jai-core-1.1.4/0000755000175000017500000000000011633360406013062 5ustar mathieumathieujai-core-1.1.4/LICENSE-JDL.txt0000644000175000017500000004357510203035544015324 0ustar mathieumathieuJAVA ADVANCED IMAGING DISTRIBUTION LICENSE (VER. 1.1.X) JAI I. LICENSE GRANTS, TERMS AND RESTRICTIONS 1.0 General License Terms. This Java Distribution License ("JDL") is between Sun Microsystems, Inc. ("Sun") and You where "You" means the individual or legal entity exercising rights under this JDL. "Technology" means the following as provided under this JDL: (i) the source code and binary code from the Reference Implementation ("RI") of the JavaTM Advanced Imaging version 1.1.x Specification (the "Specification") and related documentation, all as revised or upgraded and made available hereunder; and (ii) the associated technology compatibility kit ("TCK"). The TCK contains the TCK documentation, user's guide ("TCK User's Guide"), test tools and test suite associated with the Specification, as revised or upgraded by Sun. The TCK is provided so that You may determine if Your implementation is compliant with the Specification. "Modifications" means any (i) change or addition to or derivative of the Technology; or (ii) new source or object code implementing the Specification for the Technology. "Commercial Use" means: (i) Your use of the RI and/or Modifications as part of a Compatible Implementation within Your business or organization, but only by Your employees or agents; and/or (ii) any use or distribution, directly or indirectly by You of the RI and/or Modifications as part of a Compatible Implementation to any third party, alone or bundled with any other software or hardware, for direct or indirect commercial or strategic gain or advantage. By Your use or distribution of the Technology and Modifications, You agree to the terms of this JDL. 2.0 License Grants, Restrictions and Termination. 2.1 Commercial Use and Internal Deployment License Grants. Subject to the terms of this JDL, Sun grants to You, to the extent of Sun's licensable copyrights, patents and trade secrets in the RI, a limited, non-exclusive, non-transferable, worldwide and royalty-free license to modify, compile, reproduce, distribute, internally use and internally deploy code and related documentation from the RI and/or Modifications for Commercial Use, but only as part of Your own Compatible Implementation (as defined below), and only if You make such distributions as follows: (i) Source Code. You can only distribute source code for the RI and Modifications under all of the following conditions: (a) You make such distribution under the terms of this JDL; (b) without offering or imposing any different terms; (c) to a receipient who has accepted the terms of this JDL; and (d) You provide a copy of this JDL to each such receipient; (ii) Binary Code. You can distribute binary code for the RI and Modifications but only under license terms which (a) have been accepted by the receipient; (b) are consistent, and not conflicting, with the terms of the JDL; and (c) where such terms are no less protective of Sun than the terms of this JDL. 2.2 TCK License Grant. Subject to the terms of this JDL, Sun grants to You, to the extent of Sun's licensable copyrights, patents and trade secrets in the TCK, a limited, non-exclusive, non-transferable, worldwide and royalty-free license to internally use the TCK for the sole purpose of developing and testing Your Compatible Implementations (as defined below). 2.2.1 TCK Master Support Agreement. If you desire support for the TCK, You may execute a separate TCK master support agreement with Sun. 2.2.2 TCK Use Terms. Your TCK license grant hereunder is expressly conditioned upon your continued adherance to the following restrictions: (i) You may not sublicense or distribute the TCK to any third party except for any specific TCK code files identified as redistributables in the TCK User's Guide ("Redistributables"), but only: (a) as part of a TCK Adaptor accompanying a Compatible Implementation (where a "TCK Adaptor" means a program that effects interoperability between the TCK and the Compatible Implementation); and (b) pursuant to a license agreement that protects Sun's interests consistent with the terms of the JDL; (ii) You may not create derivative works of the TCK nor of any of its components except for the Redistributables in a TCK Adaptor; (iii) You may not disassemble or decompile binary portions of the TCK's test suites or test tools or otherwise attempt to derive the source code from such binary portions; (iv) You may not develop other test suites intended to validate compatibility with the Specification to which the TCK licensed hereunder corresponds; (v) You may not use the TCK to test a third party's product; (vi) You may not make Commercial Use of code which implements any portion of the Specification unless such code is included in a Compatible Implementation; (vii) You may disclose Test Reports (where "Test Reports" means those reports generated by the TCK which identify only configuration information and the status of individual or aggregate test executions) for an implementation which You are attempting to certify in accordance with the terms of this JDL for the sole purpose of making Your implementation a Compatible Implementation, but you may not make any claims of comparative compatibility nor disclose any other TCK testing information to any party. For example, You cannot claim that Your implementation is "nearly compatible" or "98% compatible." When You share Test Reports in any public forum, including mailing lists, marketing material or press releases, and Your implementation is not a Compatible Implementation, You must state that Your implementation is "not compatible;" and (viii) You will protect the TCK as confidential information of Sun and, except as expressly authorized herein, You may not disclose the TCK to any third party. This obligation of confidentiality with respect to the TCK will survive any termination or expiration of this JDL. 2.3 Term and Termination. This JDL shall have an initial term of three (3) years after your acceptance of this JDL and shall thereafter automatically renew for additional one year terms unless either party provides notice to the other party sixty (60) days prior to an anniversary date. The license grants of this JDL, are expressly conditioned upon Your not acting outside their scope, and Your continued compliance with the material provisions of this JDL. All license grants under this JDL will automatically and immediately be revoked without notice if You fail to so comply. Upon termination or expiration of this JDL, You must discontinue all use and distribution of the Technology and any Compatible Implementation licensed under this JDL. All sublicenses You have properly granted hereunder shall survive termination or expiration of Your rights under the JDL. Provisions which should, by their nature, remain in effect after termination or expiration shall survive, including, without limitation, the provisions of Article II (General Terms) and the TCK confidentiality obligations under this JDL. 2.4 General License Restrictions. Some portions of the Technology are provided with notices and/or open source or other licenses from other parties which govern the use of those portions. Your use or distribution of encryption technology contained within the Technlogy is subject to all applicable governmental regulations of the United States of America and the country where the Technology is deployed. You agree to comply with the U.S. export controls and trade laws of other countries that apply to the Technology and Compatible Implementations. You acknowledge that the Technology is not designed, licensed or intended for use in the design, construction, operation or maintenance of any nuclear facility. Other than the rights granted herein, Sun retains all rights, title, and interest in the Technlogy. 3.0 Compatibility and Use of the TCK. 3.1 Compatible Implementation TCK Requirements. Your use of the RI and/or Modifications as part of an implementation of the Specification is a "Compatible Implementation" if the implementation meets the following requirements: 3.1.1 A Compatible Implementation must comply with the full Specification, including all its required interfaces and functionality; 3.1.2 A Compatible Implementation must either: (i) pass the most current compatibility requirements (as defined by the TCK User's Guide) which were made available by Sun one hundred twenty (120) days before first commercial shipment ("FCS") of each version of the Compatible Implementation (eg. if Your FCS was December 31, 2003, You must have passed the most current version of the TCK as of September 2, 2003); or (ii) at Your option, pass all the compatibility requirements of a newer version of the TCK and its associated TCK User's Guide; 3.1.3 A Compatible Implementation may not modify the functional behavior of the "Java Classes" which means the specific class libraries associated with the Technology; and 3.1.4 A Compatible Implementation may not modify, subset, superset or otherwise extend the Licensor Name Space, nor include any public or protected packages, classes, Java interfaces, fields or methods within the Licensor Name Space other than those required and/or authorized by the Specification. "Licensor Name Space" means the public class or interface declarations whose names begin with "java", "javax", "com.sun" or their equivalents in any subsequent naming convention adopted by Sun through the Java Community Process, or any recognized successors or replacements thereof. 3.2 Self-Certification for Compatibility. You shall self-certify that Your Compatible Implementation passes the applicable TCK as set forth above, if and when Your Compatible Implementation in fact does so, provided that: 3.2.1 If Sun policy (as communicated to You by Sun) also requires verification of compatibility for Your Compatible Implementation then You shall, prior to the FCS of the Compatible Implementation, submit verification of compatibility to Sun or to an independent test facility designated by Sun. If such verification is by an independent test facility, then the reasonable costs of such activity (including any applicable fees) shall be at Your expense. Sun may publish or otherwise disclose Your test results. 3.2.2 Upon thirty (30) days written notice by Sun, and no more than two (2) times per calendar year, You shall permit Sun or its authorized representative to inspect and test any Compatible Implementation which has been self-certified per this subsection 3.2 to ensure that such Compatible Implementation meets the compatibility and other requirements for a Compatible Implementation as set forth herein. The reasonable costs of such inspection shall be at Sun's expense; provided, however, that You shall reimburse Sun for such costs if the inspection reveals that the Compatible Implementation does not meet such requirements and these deficiencies are not cured within thirty (30) days. 4.0 Fees and Royalties. There are no fees or royalites associated with the license grants for the Technology licensed under this JDL. II. GENERAL TERMS 5.0 No Warranty. THE TECHNOLOGY AND/OR MODIFICATIONS ARE PROVIDED "AS IS", WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS, IMPLIED OR STATUTORY INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE TECHNOLOGY AND/OR MODIFICATIONS ARE FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING OF THIRD PARTY RIGHTS. YOU AGREE THAT YOU BEAR THE ENTIRE RISK IN CONNECTION WITH YOUR USE AND DISTRIBUTION OF ANY AND ALL TECHNOLOGY AND/OR MODIFICATIONS UNDER THIS JDL. SOME JURISDICTIONS DO NOT PERMIT THE EXCLUSION OF IMPLIED WARRANTIES SO THE ABOVE EXCLUSIONS MAY NOT APPLY TO YOU 6.0 Infringement Claims. Sun may terminate this JDL, in Sun's sole discretion, upon any action initiated by You alleging that the use or distribution of the Technology and/or Modifications by Sun, or any other licensee of the Technology and/or Modifications, infringes a patent owned or controlled by You. In addition, if any portion of, or functionality implemented by, the Technology becomes the subject of a claim or threatened claim of infringement ("Affected Materials"), Sun may, at its sole option, (i) attempt to procure the rights necessary for You to continue using the Affected Materials, (ii) modify the Affected Materials so that they are no longer infringing, or (iii) immediately suspend Your rights to use and distribute the Affected Materials under this JDL by providing notice of suspension to You in a reasonable manner, and refund toYou the amount, if any, having then actually been paid by You to Sun under this JDL, on a straight line, five year basis . 7.0 Limitation of Liability. Sun will be not be liable for any claims relating to, or arising out of, this JDL, whether in tort, contract or otherwise, in an amount in excess of any annual license fees You paid to Sun for the Technology. IN NO EVENT WILL SUN BE LIABLE FOR ANY INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL OR PUNITIVE DAMAGES IN CONNECTION WITH OR ARISING OUT OF THIS JDL (INCLUDING, WITHOUT LIMITATION, LOSS OF PROFITS, USE, DATA, OR ECONOMIC ADVANTAGE OF ANY SORT), HOWEVER IT ARISES AND UNDER ANY THEORY OF LIABILITY (including negligence), REGARDLESS OF WHETHER OR NOT SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. FURTHERMORE, LIABILITY UNDER THIS JDL SHALL BE SO LIMITED AND EXCLUDED, NOTWITHSTANDING FAILURE OF THE ESSENTIAL PURPOSE OF ANY REMEDY. TO THE EXTENT ANY INDIRECT, PUNITIVE, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES YOU INCUR FOR ANY REASON WHATSOEVER CANNOT BE DISCLAIMED THEN THE ENTIRE LIABILITY OF SUN UNDER ANY PROVISION OF THIS JDL, AND YOUR EXCLUSIVE REMEDY FOR ANY SUCH DAMAGES, SHALL BE LIMITED TO THE GREATER OF THE AMOUNT ACTUALLY PAID BY YOU FOR THE TECHNOLOGY OR FIVE HUNDRED US DOLLARS, WHICHEVER IS GREATER. SOME JURISDICTIONS DO NOT PERMIT THE EXCLUSION OR LIMITATION OF INCIDENTALS OR CONSEQUENTIAL DAMAGES SO THE ABOVE EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. 8.0 Support. Sun does not provide any support for the Technology or the TCK to either You or Your customers under this JDL. Support for the TCK is provided under a separate TCK master support agreement. If You desire support for the RI, You may contact Sun. You are solely reponsible for providing all support to Your customers with respect to the Technology, TCK, Modifications and/or Compatible Implementation. 9.0 Marketing and Press Announcements. Your initial press announcement concerning execution of this JDL must be reviewed and approved by Sun prior to its release. You hereby authorizes Sun to include You in a published list of licensees of the Technology. Sun shall also be authorized to use Your name in advertising, marketing collateral, and customer success stories prepared by or on behalf of Sun for the Technology subject to Your prior approval, such approval not to be unreasonably withheld or delayed. 10.0 Notices. All notices required by this JDL must be in writing. Sun shall deliver notice to You via either e-mail or by physical mail based on the information You provided to Sun when you accepted this JDL. Notices by You to Sun will be effective only upon receipt by Sun at the following physical addresses: Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, California 95054, Attn.: Sun Software VP of Sales, cc: General Counsel, cc: Product Law Legal Department or to such different address as Sun provides on the Technology Site for such purpose. 11.0 Proprietary Rights Notices and Branding Requirements. There are no specific branding requirements associated with the Technology under this JDL. You may not remove any copyright notices, trademark notices or other proprietary legends of Sun or its suppliers contained on or in the Technology, including any notices of licenses for open source components. You agree to comply with the Sun Trademark and Logo Usage Requirements currently located at http://www.sun.com/policies/trademarks. Except as provided in this JDL, no right, title or interest to Sun's trademarks, brands or logos is granted herein. 12.0 U.S. Government End Users. The Technology and Modifications are a "commercial item," as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer software" and "commercial computer software documentation," as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Technology and/or Modifications with only those rights set forth herein. You agree to pass this notice to Your licensees. 13.0 Miscellaneous. This JDL is governed by the laws of the United States and the State of California, as applied to contracts entered into and performed in California between California residents. The relationship created under this JDL is that of licensor and licensee and does not create any other relationship such as a partnership, franchise, joint venture, agency or employment relationship between the parties. This JDL may not be assigned or transferred by either party without the prior written consent of the other party, which consent shall not be unreasonably withheld or delayed, except that Sun may assign: (i) to an affiliated company, or (ii) in the event of a merger, acquisition or sale of all or substantially all of the assets of Sun. Any express waiver or failure to exercise promptly any right under this Agreement will not create a continuing waiver nor any expectation of non-enforcement. The disclaimer of warranties and limitations of liability constitute an essential basis of the bargain in this JDL. This JDL represents the complete agreement of the parties concerning the subject matter hereof and may not be modified or amended in whole or part, except by a written instrument signed by the parties' authorized representatives. Nothing herein shall supersede or modify the terms of any separate signed written license agreement You may have executed with Sun regarding the Technology. YOU: By: ___________________________________ Name: ___________________________________ Title: __________________________________ Date: _____________________________________ jai-core-1.1.4/LICENSE.txt0000644000175000017500000000057110203035544014702 0ustar mathieumathieuThis software is licensed by Sun: i) for research use under terms of the Java Research License (JRL) as specified in the LICENSE-JRL.txt file and on the web at "http://jai.dev.java.net/jrl.html"; or ii) for commercial use under the terms of the Java Distribution License (JDL) as specified in the LICENSE-JDL.txt file and on the web at "http://jai.dev.java.net/jdl-jai.pdf". jai-core-1.1.4/build.properties0000644000175000017500000000300410503601450016264 0ustar mathieumathieu# # Base version and milestone. # String versions are as x.y[.z], file versions as x_y[_z]. # Specification milestones are edr, ..., mr. # Implementation milestones are alpha, ..., fcs # version_buildnum=03 version_betastr=alpha version_base=1.1.4 version_base_file=1_1_4 verspec=1.1-mr verspecfile=1_1-mr jai-core-1.1.4/README-FIRST.txt0000644000175000017500000000051510203035544015440 0ustar mathieumathieuThe source code for the jai-core project is copyrighted code that is licensed to individuals or companies who download or otherwise access the code. The copyright notice for this project is in COPYRIGHT.txt The source code license for this project is in LICENSE.txt Instructions for building this project are in README-build.html jai-core-1.1.4/build-tools/0000755000175000017500000000000011633360406015317 5ustar mathieumathieujai-core-1.1.4/build-tools/package-list0000644000175000017500000000520710203035544017604 0ustar mathieumathieujava.applet java.awt java.awt.color java.awt.datatransfer java.awt.dnd java.awt.event java.awt.font java.awt.geom java.awt.im java.awt.im.spi java.awt.image java.awt.image.renderable java.awt.print java.beans java.beans.beancontext java.io java.lang java.lang.ref java.lang.reflect java.math java.net java.nio java.nio.channels java.nio.channels.spi java.nio.charset java.nio.charset.spi java.rmi java.rmi.activation java.rmi.dgc java.rmi.registry java.rmi.server java.security java.security.acl java.security.cert java.security.interfaces java.security.spec java.sql java.text java.util java.util.jar java.util.logging java.util.prefs java.util.regex java.util.zip javax.accessibility javax.crypto javax.crypto.interfaces javax.crypto.spec javax.imageio javax.imageio.event javax.imageio.metadata javax.imageio.plugins.jpeg javax.imageio.spi javax.imageio.stream javax.naming javax.naming.directory javax.naming.event javax.naming.ldap javax.naming.spi javax.net javax.net.ssl javax.print javax.print.attribute javax.print.attribute.standard javax.print.event javax.rmi javax.rmi.CORBA javax.security.auth javax.security.auth.callback javax.security.auth.kerberos javax.security.auth.login javax.security.auth.spi javax.security.auth.x500 javax.security.cert javax.sound.midi javax.sound.midi.spi javax.sound.sampled javax.sound.sampled.spi javax.sql javax.swing javax.swing.border javax.swing.colorchooser javax.swing.event javax.swing.filechooser javax.swing.plaf javax.swing.plaf.basic javax.swing.plaf.metal javax.swing.plaf.multi javax.swing.table javax.swing.text javax.swing.text.html javax.swing.text.html.parser javax.swing.text.rtf javax.swing.tree javax.swing.undo javax.transaction javax.transaction.xa javax.xml.parsers javax.xml.transform javax.xml.transform.dom javax.xml.transform.sax javax.xml.transform.stream org.ietf.jgss org.omg.CORBA org.omg.CORBA.DynAnyPackage org.omg.CORBA.ORBPackage org.omg.CORBA.TypeCodePackage org.omg.CORBA.portable org.omg.CORBA_2_3 org.omg.CORBA_2_3.portable org.omg.CosNaming org.omg.CosNaming.NamingContextExtPackage org.omg.CosNaming.NamingContextPackage org.omg.Dynamic org.omg.DynamicAny org.omg.DynamicAny.DynAnyFactoryPackage org.omg.DynamicAny.DynAnyPackage org.omg.IOP org.omg.IOP.CodecFactoryPackage org.omg.IOP.CodecPackage org.omg.Messaging org.omg.PortableInterceptor org.omg.PortableInterceptor.ORBInitInfoPackage org.omg.PortableServer org.omg.PortableServer.CurrentPackage org.omg.PortableServer.POAManagerPackage org.omg.PortableServer.POAPackage org.omg.PortableServer.ServantLocatorPackage org.omg.PortableServer.portable org.omg.SendingContext org.omg.stub.java.rmi org.w3c.dom org.xml.sax org.xml.sax.ext org.xml.sax.helpers jai-core-1.1.4/src/0000755000175000017500000000000011633360232013646 5ustar mathieumathieujai-core-1.1.4/src/share/0000755000175000017500000000000011633360402014747 5ustar mathieumathieujai-core-1.1.4/src/share/mediaLib/0000755000175000017500000000000011633360406016461 5ustar mathieumathieujai-core-1.1.4/src/share/mediaLib/linux/0000755000175000017500000000000011633360235017620 5ustar mathieumathieujai-core-1.1.4/src/share/mediaLib/linux/amd64/0000755000175000017500000000000011633360241020530 5ustar mathieumathieujai-core-1.1.4/src/share/mediaLib/linux/amd64/libmlib_jai.so0000644000175000017500001566363610473721517023372 0ustar mathieumathieuELF>@@86@8@444DDxh6xhFxhFQtdy]|W }VHn{X -u^?qz&YgI:B l QON/vr.@135;_Lp`>"\kf2ae9F 7bT$,08*RSDjhGdJ!(sM=#+AxoiK)c%'~E[t4< CUmZPw6  X$h8kl@l ( @ a3 D DxhFjF8nFs< @*Y8 q  Sz e @) 0 Y'* ^i> 8f , !  " @< 6 PS t^: ")F2 9 D66  Ii.C pIB `H6D'a0 Y)  L p;D1 p9 = 0.)@ D@) `^^. C $ C4 ) p% `%  s9u nF@ C. 2. g  P~/ d ) BN m  @& #  z; `%sD; P#> 5@1 p9D 0;  Aj @) `> 9h . +    B# @ @)   T]-/ `1 01. @+ `X; 0$)D `Y Щ@ AqM qDM @ d7Y( P Pg7 @ [ 2 1fB G ! 0- ? :@D  `7 * 03%  0vi? ?r- +  @> p9 2vD1 9= P4@ ( `> 0rY/ 00 Y< & @8 )kD3 )Cw' i _A pF@ , @ @]R  U=5 5 )A> 6@/ m1 1, j& p pm4  yi+ pz.Dy! ,   , $ 3 DR, 6  Y% Z/ p ? >q `O[9 0 0H   Q7)  [YT*  # l  @S! 0{C J1 01* @6 `k" `q  `DV" p*(  pz^3 ) П P^Y  @ m 0 $ м@ `7 )]   * P[ @}  W]"D9 8 )7 ' ZA `E/ O\+ 6  0cY8' 0 Pk@Q  0 3  d6 )  U hzo2 0 1    oYbD7= `/< +Y5  O7 PKim . $ +: @! j@ 4l Ej( `= 1+ ;< (= 3@< -)  PPzj   0 f= p0  )D0 in P< f PB2 p 9E b w^2 C 6  `>*+ p)! P  M@w ez B9  )  1 {i p@9" P; D0 P? ;2 B/ B87 )  Z9 PJava_com_sun_medialib_mlib_Image_ColorTrue2IndexInitJava_com_sun_medialib_mlib_Image_initinterptableIDsJava_com_sun_medialib_mlib_Image_initcolormapIDsJava_com_sun_medialib_mlib_Image_initIDsJava_com_sun_medialib_mlib_Image_getVersionstrcpystrcatJava_com_sun_medialib_mlib_Image_IFFT_13___3D_3DJava_com_sun_medialib_mlib_Image_IFFT_13___3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_IFFT_12___3D_3DJava_com_sun_medialib_mlib_Image_IFFT_12___3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_IFFT_11___3D_3DJava_com_sun_medialib_mlib_Image_IFFT_11___3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_FFT_13___3D_3DJava_com_sun_medialib_mlib_Image_FFT_13___3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_FFT_12___3D_3DJava_com_sun_medialib_mlib_Image_FFT_12___3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_FFT_11___3D_3DJava_com_sun_medialib_mlib_Image_FFT_11___3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_BlendMulti_1FpJava_com_sun_medialib_mlib_Image_BlendMultiJava_com_sun_medialib_mlib_Image_ColorOrderedDitherMxNJava_com_sun_medialib_mlib_Image_ColorErrorDiffusionMxNJava_com_sun_medialib_mlib_Image_ColorDitherInitJava_com_sun_medialib_mlib_Image_SubsampleAverage_1FpJava_com_sun_medialib_mlib_Image_SubsampleAverageJava_com_sun_medialib_mlib_Image_FilteredSubsample_1FpJava_com_sun_medialib_mlib_Image_FilteredSubsampleJava_com_sun_medialib_mlib_Image_SubsampleBinaryToGrayJava_com_sun_medialib_mlib_Image_Clear_1FpJava_com_sun_medialib_mlib_Image_PolynomialWarpTable2_1FpsqrtJava_com_sun_medialib_mlib_Image_PolynomialWarpTable_1FpJava_com_sun_medialib_mlib_Image_ClearJava_com_sun_medialib_mlib_Image_PolynomialWarpTable2Java_com_sun_medialib_mlib_Image_PolynomialWarpTableJava_com_sun_medialib_mlib_Image_ZoomTranslateToGrayJava_com_sun_medialib_mlib_Image_GridWarpTable2_1FpJava_com_sun_medialib_mlib_Image_GridWarpTable_1FpJava_com_sun_medialib_mlib_Image_GridWarpTable2Java_com_sun_medialib_mlib_Image_GridWarpTableJava_com_sun_medialib_mlib_Image_ZoomTranslateTable_1FpJava_com_sun_medialib_mlib_Image_ZoomTranslateTableJava_com_sun_medialib_mlib_Image_AffineTable2_1FpJava_com_sun_medialib_mlib_Image_AffineTable_1FpJava_com_sun_medialib_mlib_Image_AffineTable2Java_com_sun_medialib_mlib_Image_AffineTableJava_com_sun_medialib_mlib_Image_ZoomTranslate_1FpJava_com_sun_medialib_mlib_Image_ZoomTranslateJava_com_sun_medialib_mlib_Image_Xor__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Xor__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Thresh5_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3D_3DJava_com_sun_medialib_mlib_Image_Thresh5_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3D_3DJava_com_sun_medialib_mlib_Image_Thresh5__Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3I_3IJava_com_sun_medialib_mlib_Image_Thresh5__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3I_3IJava_com_sun_medialib_mlib_Image_Thresh4_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_Thresh4_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_Thresh4__Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3I_3I_3IJava_com_sun_medialib_mlib_Image_Thresh4__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3I_3I_3IJava_com_sun_medialib_mlib_Image_Thresh3_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3DJava_com_sun_medialib_mlib_Image_Thresh3_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3DJava_com_sun_medialib_mlib_Image_Thresh3__Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3IJava_com_sun_medialib_mlib_Image_Thresh3__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3IJava_com_sun_medialib_mlib_Image_Thresh2_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3DJava_com_sun_medialib_mlib_Image_Thresh2_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3DJava_com_sun_medialib_mlib_Image_Thresh2__Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3IJava_com_sun_medialib_mlib_Image_Thresh2__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3IJava_com_sun_medialib_mlib_Image_Thresh1_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3D_3DJava_com_sun_medialib_mlib_Image_Thresh1_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3D_3DJava_com_sun_medialib_mlib_Image_Thresh1__Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3I_3IJava_com_sun_medialib_mlib_Image_Thresh1__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3I_3IJava_com_sun_medialib_mlib_Image_Sub_1FpJava_com_sun_medialib_mlib_Image_SubJava_com_sun_medialib_mlib_Image_Sobel_1FpJava_com_sun_medialib_mlib_Image_SobelJava_com_sun_medialib_mlib_Image_Scale_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3DJava_com_sun_medialib_mlib_Image_Scale_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3DJava_com_sun_medialib_mlib_Image_Scale2__Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3DJava_com_sun_medialib_mlib_Image_Scale2__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3DJava_com_sun_medialib_mlib_Image_SConvKernelConvertJava_com_sun_medialib_mlib_Image_SConv7x7_1FpJava_com_sun_medialib_mlib_Image_SConv7x7Java_com_sun_medialib_mlib_Image_SConv5x5_1FpJava_com_sun_medialib_mlib_Image_SConv5x5Java_com_sun_medialib_mlib_Image_SConv3x3_1FpJava_com_sun_medialib_mlib_Image_SConv3x3Java_com_sun_medialib_mlib_Image_Rotate90_1FpJava_com_sun_medialib_mlib_Image_Rotate90Java_com_sun_medialib_mlib_Image_Rotate270_1FpJava_com_sun_medialib_mlib_Image_Rotate270Java_com_sun_medialib_mlib_Image_Rotate180_1FpJava_com_sun_medialib_mlib_Image_Rotate180Java_com_sun_medialib_mlib_Image_ReformatJava_com_sun_medialib_mlib_Image_PolynomialWarp2_1FpJava_com_sun_medialib_mlib_Image_PolynomialWarp_1FpJava_com_sun_medialib_mlib_Image_PolynomialWarp2Java_com_sun_medialib_mlib_Image_PolynomialWarpJava_com_sun_medialib_mlib_Image_Or__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Or__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Not__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Not__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Mul_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Mul_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_MulShift__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2IJava_com_sun_medialib_mlib_Image_MulShift__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2IJava_com_sun_medialib_mlib_Image_MinFilter7x7_1FpJava_com_sun_medialib_mlib_Image_MinFilter7x7Java_com_sun_medialib_mlib_Image_MinFilter5x5_1FpJava_com_sun_medialib_mlib_Image_MinFilter5x5Java_com_sun_medialib_mlib_Image_MinFilter3x3_1FpJava_com_sun_medialib_mlib_Image_MinFilter3x3Java_com_sun_medialib_mlib_Image_Min_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Min_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Min__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Min__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_MedianFilterMxN_1FpJava_com_sun_medialib_mlib_Image_MedianFilterMxNJava_com_sun_medialib_mlib_Image_MedianFilter7x7_1FpJava_com_sun_medialib_mlib_Image_MedianFilter7x7Java_com_sun_medialib_mlib_Image_MedianFilter5x5_1FpJava_com_sun_medialib_mlib_Image_MedianFilter5x5Java_com_sun_medialib_mlib_Image_MedianFilter3x3_1FpJava_com_sun_medialib_mlib_Image_MedianFilter3x3Java_com_sun_medialib_mlib_Image_Mean_1FpJava_com_sun_medialib_mlib_Image_MeanJava_com_sun_medialib_mlib_Image_MaxFilter7x7_1FpJava_com_sun_medialib_mlib_Image_MaxFilter7x7Java_com_sun_medialib_mlib_Image_MaxFilter5x5_1FpJava_com_sun_medialib_mlib_Image_MaxFilter5x5Java_com_sun_medialib_mlib_Image_MaxFilter3x3_1FpJava_com_sun_medialib_mlib_Image_MaxFilter3x3Java_com_sun_medialib_mlib_Image_Max_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Max_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Max__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Max__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_LookUp2Java_com_sun_medialib_mlib_Image_Log_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Log_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Log__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Log__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Invert_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Invert_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Invert__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Invert__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Histogram2Java_com_sun_medialib_mlib_Image_GridWarp2_1FpJava_com_sun_medialib_mlib_Image_GridWarp_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3F_3FDDIIIIIIIIJava_com_sun_medialib_mlib_Image_GridWarp2Java_com_sun_medialib_mlib_Image_GridWarp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3F_3FDDIIIIIIIIJava_com_sun_medialib_mlib_Image_GradientMxN_1FpJava_com_sun_medialib_mlib_Image_GradientMxNJava_com_sun_medialib_mlib_Image_Gradient3x3_1FpJava_com_sun_medialib_mlib_Image_Gradient3x3Java_com_sun_medialib_mlib_Image_FourierTransformJava_com_sun_medialib_mlib_Image_FlipY_1FpJava_com_sun_medialib_mlib_Image_FlipYJava_com_sun_medialib_mlib_Image_FlipX_1FpJava_com_sun_medialib_mlib_Image_FlipXJava_com_sun_medialib_mlib_Image_FlipMainDiag_1FpJava_com_sun_medialib_mlib_Image_FlipMainDiagJava_com_sun_medialib_mlib_Image_FlipAntiDiag_1FpJava_com_sun_medialib_mlib_Image_FlipAntiDiagJava_com_sun_medialib_mlib_Image_ExtremaLocations_1FpJava_com_sun_medialib_mlib_Image_ExtremaLocationsJava_com_sun_medialib_mlib_Image_Extrema2_1FpJava_com_sun_medialib_mlib_Image_Extrema2Java_com_sun_medialib_mlib_Image_Exp_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Exp_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Exp__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Exp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Erode8_1FpJava_com_sun_medialib_mlib_Image_Erode8Java_com_sun_medialib_mlib_Image_Erode4_1FpJava_com_sun_medialib_mlib_Image_Erode4Java_com_sun_medialib_mlib_Image_Div_1FpJava_com_sun_medialib_mlib_Image_DivShiftJava_com_sun_medialib_mlib_Image_Dilate8_1FpJava_com_sun_medialib_mlib_Image_Dilate8Java_com_sun_medialib_mlib_Image_Dilate4_1FpJava_com_sun_medialib_mlib_Image_Dilate4Java_com_sun_medialib_mlib_Image_DataTypeConvertJava_com_sun_medialib_mlib_Image_CopyJava_com_sun_medialib_mlib_Image_ConvolveMxN_1FpJava_com_sun_medialib_mlib_Image_ConvolveMxNJava_com_sun_medialib_mlib_Image_ConvMxN_1FpJava_com_sun_medialib_mlib_Image_ConvMxNJava_com_sun_medialib_mlib_Image_ConvKernelConvertJava_com_sun_medialib_mlib_Image_Conv7x7_1FpJava_com_sun_medialib_mlib_Image_Conv7x7Java_com_sun_medialib_mlib_Image_Conv5x5_1FpJava_com_sun_medialib_mlib_Image_Conv5x5Java_com_sun_medialib_mlib_Image_Conv4x4_1FpJava_com_sun_medialib_mlib_Image_Conv4x4Java_com_sun_medialib_mlib_Image_Conv3x3_1FpJava_com_sun_medialib_mlib_Image_Conv3x3Java_com_sun_medialib_mlib_Image_Conv2x2_1FpJava_com_sun_medialib_mlib_Image_Conv2x2Java_com_sun_medialib_mlib_Image_ConstXor__Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_ConstXor__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_ConstSub_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_ConstSub_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_ConstSub__Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_ConstSub__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_ConstOr__Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_ConstOr__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_ConstMul_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_ConstMul_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_ConstMul__Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_ConstMul__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_ConstDiv_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_ConstDiv_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_ConstDiv__Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_ConstDiv__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_ConstAnd__Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_ConstAnd__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_ConstAdd_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_ConstAdd_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_ConstAdd__Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_ConstAdd__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_ColorTrue2IndexJava_com_sun_medialib_mlib_Image_ColorOrderedDither8x8Java_com_sun_medialib_mlib_Image_ColorErrorDiffusion3x3Java_com_sun_medialib_mlib_Image_ColorConvert2_1FpJava_com_sun_medialib_mlib_Image_ColorConvert2Java_com_sun_medialib_mlib_Image_ColorConvert1_1FpJava_com_sun_medialib_mlib_Image_ColorConvert1Java_com_sun_medialib_mlib_Image_ChannelInsertJava_com_sun_medialib_mlib_Image_ChannelExtractJava_com_sun_medialib_mlib_Image_Blend_1FpJava_com_sun_medialib_mlib_Image_Blend2_1FpJava_com_sun_medialib_mlib_Image_Blend2Java_com_sun_medialib_mlib_Image_Blend1_1FpJava_com_sun_medialib_mlib_Image_Blend1Java_com_sun_medialib_mlib_Image_BlendJava_com_sun_medialib_mlib_Image_And__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_And__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Affine2_1FpJava_com_sun_medialib_mlib_Image_Affine_1FpJava_com_sun_medialib_mlib_Image_Affine2Java_com_sun_medialib_mlib_Image_AffineJava_com_sun_medialib_mlib_Image_Add_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Add_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Add__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Add__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Abs_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Abs_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Abs__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Abs__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2strstrmemcpysqrtfceillogexpfcosfsinfmemmovememsetfreereallocmemalignlibc.so.6libm.so.6libmlib_jai.soSUNW_1.0SUNW_1.0.1SUNW_1.1SUNW_1.1.1_01SUNW_1.1.2GLIBC_2.2.5 SD'= bD!' $kDbD'= $vDkDA $DvD"' DD?D ui DIDui DDе D D D` DP ȀD ЀDY ؀DP DD`DDDPD`D DP(D0D 8D@DHD@oPDoXDo`DPphDppDqxDpqDqDpDqDpqDqDPrDrDsD`sȁD%ЁD`%؁D%D`%D%D %D%D@%D0%D%D`% D%(Db0D@c8DPd@DeHD%PD0%XD%`D%hD%pDP%xD %D%D%D %D%D%DAD0MD@TD`[D@DDD`DDD D@(D0D8DP@DHD PDXD`DhDpDxD DDPDDD DDD@DȃD ЃD؃D DD@DDD0DDDp D(D@0D8D@DHD@PDXD`D hD`pDxDDDD0DDPDDDpȄDЄD؄DD@DDDPDD DD D(D0D8D @DHDPPDXD`D0 hD pD xDP D D@ D D`DDD@DDȅDЅDp؅DPD DPDDDDPDD D0(Dp0D8D @D HD PD@ XD hD`%pD%xD%Dp$D$D$D%D 5Dp5D5D06D6ȆD7ІDp7؆D7Dp8D8Dp9D:D:D ;D;DP< DP+(D+0D,8Dp,@D,HD`-PD-XDp.`D/hD/pD@0xD1D1D2DP3D04D@&Dp&D&D&D0'ȇDp'ЇD'؇D(DP(D(D(D@)D)D*D`*D* D@(Dp0D8D@DPUHDYPD_XDf`D@hDppDxDDzDpDDГDpDDpDD0IȈDpKЈDPN؈DQD D`D@DDmD`qD tDw D&(DP&0D@&8D&@DP&HD&PD&XD&`D@H'hDC'pD9'xDP*'DФ&D&D&D&D&Dp&D&DP&DP'ȉDp'ЉD#'؉D&'D'D'D'D'D_'D['DpW'DT' D|'(Dw'0Do'8DPk'@D'HD`'PD'XD'`DfhDbpDp^xDYD@L'DM'DPO'DQ'Db'Dd'De'D@h'Dp'ȊD'ЊDp'؊D'DRDTDUDpWD'DYD`'D`W D`'(DU0D '8DS@DRHDPPD OXD@M`DIhD`JpDKxD LD0#D#D0n#DN#D@#D|#D@`#D<#D"ȋD"ЋD"؋D"D"D"D"D "D'D'D(DP( D`"(D"0D#8D#@D'HD0'PDP'XD'`DЪ'hD'pD'xD0'D#D0#D`#D#D#Dp#D"#D'#D9(ȌD`<(ЌD`?(،D C(D,#Dp/#D2#D7#D,(Dp.(D`1(D 5( D((D (0D#(8D@'(@Dp#HD`#PD#XD@#`D#hD0#pD#xD #D )D)D)D)D)D)D)D)D*ȍD*ЍD.*؍D)D*DO*D+Dv+DV+D+D0;D  D`*(D*0D`o*8D+@D6+HD`DhDpDxDDpDDDDDD@ȎDЎD0؎DDDDD`DDp D`(D0D8DHDPDXDPhDppDpxDDDPDDDpD0DȏDЏDP؏DDpDpDDD@DD@(D0D`8Dp@DHDPDXDhDpDpxDDDDDDDPD0ȐDАDpؐDDPDжDDPD0DD (D@0D8DHD0PD0XD`D@hD@pDxDDDФDDD0D0DȑDБDPؑDDD D0DDD  D(D0D@8D HDPD`XDphD@pDxDD@D DDD0DDȒD ВD ؒDDvDwD }DЃDxD@~D (D@x0D}8D`@DwHD yPD~XDhD{pDxDЉD`zD@DD`rDrD0sDsDtȓDuГDpuؓDvDDDDD Dp DP D  DD(D D0DD8D`D@DDHDDPD@DXDD`DDhD DpDDxD`DDDDDDDD D0DȔD`ДDؔDDDPDDDЅD D(Dp0D |8Dp@DpHD`DhD0pD`xD Dp D DDD@D`D@ȕDЕDؕDD DDDDD D(D@0D 8D@DHDPDpXD`DhDDD DDDpDDpDPDpȖDpDeDpgDiDkD`nDqD_DPt D0c(Dy@D@IHD KPDMXDO`DRhDTpDpCxDWDFD\D@.D0D02D4DP6ȗD8ЗD(ؗD;D+DP@DPD D@D  D`(D0D 8D @DHD`%`DphDpDpxD}D DzDDvDЁDsDqȘD0fИDoؘDdDmDaDpjD\D@hD0Z D X(DK0DU8D@H@DPSHDEPDOXDP@`DpMhD=Dp;D`.D09D+D6D`(D2D#D0șD D*D*D.*D)D*DO*D+Dv+ DV+(D+0D0;8D @D /HDP/PD /XDp /`D /hD/DbD hDmD/DtDzD`D0/DȝDНD@؝Dp/DD@DD /DDDDp/ D(Dp0D8D /@D\HDdPDiXD.`D@ohDwpD|xD.D DpDD`.DD0DРD0.DȞDpОD ؞D.D0D0DD`/D@-/D4/DDJDOD>/D@^ȟDmПD@r؟DD/DD@DЖDpK/DDD`D/ D@(D0D8D@/@DpHD(PD.XD/`D4hDGpDMxD/D RDjDoD !/DtDD D0$/D ȠDРD@ؠD0DDD8DXD0xDD`DPءDDDP8DXDxDD`u/D3D0_DDDDr/ȢD1ТD]آDDD Dk/D*D@VDp D(D@@D`/HDe/PDi/XDo/`D{/hDpD@)xDP/D /D`HDTDZDmD }DDDȣDгУD0أD`DDDDD]/Db/DpDm/ Dx/(D@0D8D-@D~/HD9PDPJXDX`DdhD@opDxDDDDDDD`DD DPrȤDrФDsؤD`sDmDnD`nDnD@&0D'0D(0Dp)0 D0+0(D+00D,08D@.0@D/HD/PD/XDp0`D 0hD0pD0xD 0DP1D1D1D1D`1D1D01D1D01ȥD1ХD`1إD 1DP1D1D`1D@1D1D1D1D1 D1(D`10D18D01@D1HD@1PD1XD1`D1hD01pD 1xD`1DP1D1D01D1Dp 2D2Dp2D02D2ȦD2ЦD2ئD 2D1D1D2D2D1DP1D1D1 D1(D10Dp18D1@D`1HD1PD1XDp1`D1hD1pD1xDP1D 1D1Dp1Dp1D1D@1D@1D1ȧDPЧDاDDPDpDDDDPDD  D(D0D8D @DHDPPDXD`DhDPpD xDD@DDDDPDDDPDȨD ШDبDDDDD0DDpD@D D2(D20D28D02@Dв2HD 2PD2XD2`D@2hD2pD2xD2D2D2D2D 2D$3D3D03D3D 3ȩD3ЩDP2ةD2D0S3DO3DK3DF3D0=3D93Dp53D03 D@3(DA30D@C38DD3@DP*3HD+3PD-3XD.3`DRhDPpD OxD@MDID`JDKD LD\DDD`DȪDzDBDDDDD`(D0D8D0@DHD`hDpDxDDDD?DCDDDpjȫD{DD0D@D@/DP{ D#(D#0D`#8D#@D#HD@#PD#XD#hD $pD@$xD$D$D$D@$D$D@$D$D$ȬD=0ЬDC0جD?0DpM0DA0DJ0D0F0DW0DH0DU0DO0 D@R0(Dg00Dp08D j0@D0HDl0PDP{0XDs0`D0hDw0pD00xDЂ0D0D0Dp0D0D0DЫ0D0D0D0ȭD0ЭD00حD0D0D@0D1D`1DD1D 1D81D!1 Dq1(D -10De18DM1@DY1HD%PD`%XD%`D`%hD%pD %xD%D@%D0%D%D`%D%DbD@cDPdDeȮD%ЮD0%خD%D%D%DP%D %D%D%D %D% D%(DA0D0M8D@T@D`[HDH0PDU0XDO0`D@R0hD50pD70xD90D;0Dw0D00DЂ0D0DpZ0D]0D`0D d0ȯD0ЯD00دD0D0D0DЗ0DЛ0D0D -1De1DM1 DY1(D 00D 08D 0@D 0hD}2pD0~2xDp~2D}2D0}2D`}2D}2Dp2D2D 2D2D2ȰD`2аD2ذD`2D2Dp2D2D2D2D 2D2D2 D~2(D20D@28D2@D2HD 2PDp2XDЀ2`D@2hD2pD2xD2D 2D2D2D2D2DP2D2D02ȱDP2бD2رD2D2D2DP2D2D02D2D@2DР2 Dp2(D20DТ28D2@D2HDP2PD02XD02`D2hD 2pD`2xD2D2D@2D2D2D`2D2D02D2D@2ȲD2вD02زDМ2jF@jFjF@.0jFi4jFjF@jF`DjF@DjF`jFpjF@q4jF 0kF+0kFDkF/kFD kFD(kFe40kF08kFD@kFHkFPkF`3XkF@D`kFs4hkFpkFxkF`4kF@l4kF3kFDkF`DkF`3kF0kFkFp)0kF,0kF@DkFDkFX4kFPkF1kF@&0kFlFplFu4lF1lF` lF D(lFp00lFD8lF+@lFHlF`~1PlFPXlFP`lFhlF plFxlF/lF 0lF1lFDlF`@4lF0+0lF3lFDlF`DlFlF DlF'0lF DlFlF/lFr4lF3mF(0mF`3mFDmF mF1(mFм0mF8mF@p4@mFDHmF01PmFk4XmF`mFhmF@pmF3xmF3mFmF`04mFmF@DmF`3mFmFmF mF mFDmF@mFmF`3mFmF DmFc4nFDnF0nF\4nFD nF(nF0nF0 jF$(jF)0jF58jFJ@jFbHjFgPjFuXjF`jFhjFpjFxjFjFjFjFjF5pE%pE%pEh%pEh%pEh%pEh%pEh%pEh%pEh%pEhp%pEh`%pEh P%pEh @%pEh 0%pEh %zpEh %rpEh%jpEhH\$Hl$Ll$Lt$L|$Ld$HXL'HEE͉AH5&g2AT$0IHHLH g2Hg2LH|$pD,$t$hL$`EEH1H|$H߉t$L$LAH\$(Hl$0Ld$8Ll$@Lt$HL|$PHXffHHQHfffffffffffHHYHfffffffffffHHOHfffffffffffS1HHĀaHH.H5f2HHHH8H[fHH\$Hl$Ld$Ll$ILt$L|$HH8IXLH ULHIU IŹufttLL!LLHTLLHTu#H\$Hl$Ld$Ll$ Lt$(L|$0H8H]H5ge2HS0H]HHe2HSpffffffffLH\$Hl$Ld$Ll$Lt$L|$HXMHL$HHT$ LL$WHt$ H"THt$HHD$TLHITHt$HIS IŹufttH|$ALLL!Ht$LHSLLHSHt$LHSHT$Ht$ HSu#H\$(Hl$0Ld$8Ll$@Lt$HL|$PHXH]H5d2HS0H]HHd2HSpffffffffffHH\$Hl$Ld$Ll$ILt$L|$HH8ImVLHRLHIR IŹufttLL'!LLHRLLHRu#H\$Hl$Ld$Ll$ Lt$(L|$0H8H]H5'c2HS0H]HHc2HSpffffffffLH\$Hl$Ld$Ll$Lt$L|$HXMHL$HHT$ LL$qUHt$ HQHt$HHD$QLHIQHt$HIQ IŹufttH|$ALLL!Ht$LHQLLHQHt$LHnQHT$Ht$ H\Qu#H\$(Hl$0Ld$8Ll$@Lt$HL|$PHXH]H5a2HS0H]HHZb2HSpffffffffffHH\$Hl$Ld$Ll$ILt$L|$HH8I-TLHPLHIP IŹufttLL!LLHwPLLHiPu#H\$Hl$Ld$Ll$ Lt$(L|$0H8H]H5`2HS0H]HHga2HSpffffffffLH\$Hl$Ld$Ll$Lt$L|$HXMHL$HHT$ LL$1SHt$ HOHt$HHD$OLHIOHt$HIrO IŹufttH|$ALLL~!Ht$LHlOLLH^OHt$LH.OHT$Ht$ HOu#H\$(Hl$0Ld$8Ll$@Lt$HL|$PHXH]H5_2HS0H]HH`2HSpffffffffffHH\$Hl$Ld$Ll$ILt$L|$HH8IQLH`NLHIRN IŹufttLL'!LLH7NLLH)Nu#H\$Hl$Ld$Ll$ Lt$(L|$0H8H]H5^2HS0H]HH'_2HSpffffffffLH\$Hl$Ld$Ll$Lt$L|$HXMHL$HHT$ LL$PHt$ HbMHt$HHD$PMLHIBMHt$HI2M IŹufttH|$ALLL!Ht$LH,MLLHMHt$LHLHT$Ht$ HLu#H\$(Hl$0Ld$8Ll$@Lt$HL|$PHXH]H5Z]2HS0H]HH]2HSpffffffffffHH\$Hl$Ld$Ll$ILt$L|$HH8IOLH LLHIL IŹufttLL!LLHKLLHKu#H\$Hl$Ld$Ll$ Lt$(L|$0H8H]H5g\2HS0H]HH\2HSpffffffffLH\$Hl$Ld$Ll$Lt$L|$HXMHL$HHT$ LL$NHt$ H"KHt$HHD$KLHIKHt$HIJ IŹufttH|$ALLL~!Ht$LHJLLHJHt$LHJHT$Ht$ HJu#H\$(Hl$0Ld$8Ll$@Lt$HL|$PHXH]H5[2HS0H]HH[2HSpffffffffffHH\$Hl$Ld$Ll$ILt$L|$HH8ImMLHILHII IŹufttLL!LLHILLHIu#H\$Hl$Ld$Ll$ Lt$(L|$0H8H]H5'Z2HS0H]HHZ2HSpffffffffLH\$Hl$Ld$Ll$Lt$L|$HXMHL$HHT$ LL$qLHt$ HHHt$HHD$HLHIHHt$HIH IŹufttH|$ALLL!Ht$LHHLLHHHt$LHnHHT$Ht$ H\Hu#H\$(Hl$0Ld$8Ll$@Lt$HL|$PHXH]H5X2HS0H]HHZY2HSpffffffffffAWHAVAUIATIULSHHL$XHT$TLL$@qHT$PLLHD$ nHT$LHLHD$unHD$D$PH$H|$`H$H$H$L$Hl$H<$Ht$8H\$0HL$(E1A9HT$Xt$TLHL$ 1x;l$PRHt$@LFDD$PH$HHt$H|$ IHt$@LLGDd$PAxHH$LT$0McLJ C4KeHD$LL$(LLD$8J KC4feAyHT$Xt$TLHL$ f|$PhH|$֢H|$̢H[]A\A]A^A_fH|$IcHL$(HT$8L$H,AH4HLLoHT$0HL$LLLd$HJL=I4oD;t$PL$I|LT$Ht$(LcLL$8LK JC4vH$LD$0LKJ C4yv;l$P|^HcHHc|$PHD$HHc|$PH$HݡHc|$PHD$8HʡHc|$PHD$0H跡Hc|$PHD$(H褡INjD$PH|$nH<$eH|$8[H|$0QH|$(GL?H|$5H|$+_ImH5XU2LU0M}HHU2LAWpH[]A\A]A^A_ffffffffAWHAVAUIATIULSHHL$XHT$TLL$@/nHT$PLLHD$ jHT$LHLHD$jHD$D$PH$H|$`H$H$H$L$Hl$H<$Ht$8H\$0HL$(E1A9HT$Xt$TLHL$ 1t;l$PRHt$@LCBDD$PH$HHt$H|$ IuHt$@LLSBDd$PAxHH$LT$0McLJ C4KaHD$LL$(LLD$8J KC4aAyHT$Xt$TLHL$ {b|$PhH|$FH|$<H[]A\A]A^A_fH|$IcHL$(HT$8L$H,AH4HLLilHT$0HL$LLLd$HJL=I4BlD;t$PL$I|LT$Ht$(LcLL$8LK JC4sH$LD$0LKJ C4r;l$P|^HcHrHc|$PHD$H_Hc|$PH$HMHc|$PHD$8H:Hc|$PHD$0H'Hc|$PHD$(HINjD$PH|$ޝH<$՝H|$8˝H|$0H|$(距L话H|$襝H|$蛝_ImH5Q2LU0M}HHHR2LAWpH[]A\A]A^A_ffffffffAWLIAVEAUATIUHSHhHT$dgHL$XHT$TLHHD$jHL$HHT$DLHIwjHL$@HT$_HL$PHT$LLHI&_H$HH5H$HI4HT$`t$\LHIeHT$Pt$LHHeT$<$Lt$$$L,$D$D$T$T$@L$ L$D|$0t$(LHމT$T$HXjHT$Pt$LHHAASHT$`t$\LHSH$LHj4H$LHW4Eu2H\$hHl$pLd$xL$L$L$HĘffLuH5D2HAV0H]HH$E2HSpffffffHl$Ld$Ll$Lt$L|$H\$HhHIHT$,HL$0L$MHD$Lt$r]HL$ HT$LHIZ]MILHS1HT$0t$,LHH,dHT$ t$LHdLt$fD$fL$HLLHT$ t$LHAQHT$0t$,HLnRHLH1EH\$8Hl$@Ld$HLl$PLt$XL|$`HhfffHT$0t$,LH|cHT$ t$LHhcLt$fD$fL$1LL:HT$ t$LHAQHT$0t$,LHQE_LeH5B2HAT$0H]HHIC2HSp3fffffffH\$Hl$Ld$Ll$Lt$H8HIHT$HL$H[LHH1HT$t$IHHbLHHT$t$HHAQLLH1Eu!H\$Hl$Ld$ Ll$(Lt$0H8ffH]H5A2HS0H]HHeB2HSpffffffAWHAVIAUATUHSHHH$D$xL$pL$L$T$h\$`d$Xl$Pt$H|$@F,H$H$HH{ZH$H$LHI]ZH$H$HIWH$HI3*YA2XA2Qf.X A2H$HY tA2D,L$/A*fD$HD$(f.RLH$H/H$HHD$8/H$$LHHD$0x`H$$LH^`H$LHk^H$LH_Ht$(LkSH$LHJH$$LHMH$$LH|NHT$0H$H.HT$8H$H.HT$(H$H.LmHH5O?2AU0LeH?2HHAT$pH[]A\A]A^A_ff#X-?2H$HY-?2D,l$.A*fd$HD$(f.H$H-H$HHD$8-H$$LHHD$0^H$$LH^H$LH\H$LH^Ht$(LH$LHHH$$LH$LH$$LHLHT$0H$HE-HT$8H$H0-HT$(H$H-FH[]A\A]A^A_Ð$f|$@Mft$Hfl$PEfd$Xf\$`LfT$hfL$p$fD$xHL$0LHT$8;OfffH]HH5"=2S0H]H=2HHSp:ffffAWHAVIAUATUHSHHH$D$PL$HLD$`LL$XT$@\$8d$0l$(t$ |$'H$H$HHUHL$xHT$tLHIUH$HT$hHISHt$`HI.*Y <2X <2Qf.pjX<2Y<2,*f.Ht$`H+Ht$XHHD$+H$$LHI[HT$xt$tLH[Ht$hLHYH$LH>[$f|$Aft$ fl$(Mfd$0f\$8LfT$@fL$H$fD$PHT$LL+MHt$hLHEHT$xt$tLH%IH$$LHIHt$XLHK*HT$Ht$`H9*sHĘ[]A\A]A^A_X^;2Y^;2,*f.tnHt$`H)Ht$XHHD$)H$$LHIZHT$xt$tLHnZHt$hLH~XH$LHYf|$ft$ Afl$(fd$0Mf\$8fT$@LfL$HfD$PL$HT$ $LKHt$hLHVDHT$xt$tLHGH$$LHXHHt$XLH(HT$Ht$`H(LmHH5@92AU0LeH92HHAT$pHĘ[]A\A]A^A_fffLeHH592AT$0LeH92HHAT$pdfH\$Hl$Ld$Ll$Lt$H8HIHT$HL$HQLHH&HT$t$IHHXLHw}HT$t$HHADGLLH&Eu!H\$Hl$Ld$ Ll$(Lt$0H8ffH]H5%82HS0H]HH82HSpffffffAWHAVIAUATUHSHHH$D$xL$pL$L$T$h\$`d$Xl$Pt$H|$@"H$H$HHPH$H$LHIPH$H$HINH$HI)*Y72X72Qf.X 72H$HY 72D,L$$A*fD$HD$(f.RLH$H%H$HHD$8%H$$LHHD$0VH$$LHVH$LHTH$LHUHt$(L'{SH$LH@H$$LHDH$$LHDHT$0H$H7%HT$8H$H"%HT$(H$H#LmHH552AU0LeH62HHAT$pH[]A\A]A^A_ffcX-62H$HY-62D,l$<#A*fd$HD$(f.H$H,$H$HHD$8$H$$LHHD$0UH$$LHTH$LHRH$LHHTHt$(LwyH$LH?H$$LHdBH$$LH CHT$0H$H#HT$8H$Hp#HT$(H$H;"FH[]A\A]A^A_Ð$f|$@Mft$Hfl$PEfd$Xf\$`LfT$hfL$p$fD$xHL$0LHT$8KfffH]HH5b32S0H]H32HHSp:ffffAWHAVIAUATUHSHHH$D$PL$HLD$`LL$XT$@\$8d$0l$(t$ |$H$H$HHLHL$xHT$tLHIKH$HT$hHIQIHt$`HI1%*Y %32X %32Qf.pjX32Y32,*f.Ht$`HT!Ht$XHHD$B!H$$LHI5RHT$xt$tLH!RHt$hLH1PH$LH~Q$f|$Aft$ fl$(Mfd$0f\$8LfT$@fL$H$fD$PHT$LLIHt$hLH <HT$xt$tLHe?H$$LH @Ht$XLH HT$Ht$`Hy sHĘ[]A\A]A^A_X12Y12,*f.tnHt$`HHt$XHHD$H$$LHIPHT$xt$tLHPHt$hLHNH$LH Pf|$ft$ Afl$(fd$0Mf\$8fT$@LfL$HfD$PL$HT$ $LHHHt$hLH:HT$xt$tLH=H$$LH>Ht$XLHHT$Ht$`HLmHH5/2AU0LeH02HHAT$pHĘ[]A\A]A^A_fffLeHH5B/2AT$0LeH/2HHAT$pdfH\$Hl$Ld$Ll$Lt$L|$HHHHT$PHED$(L$ T$\$DD$ IHL$HHT$DHHGHL$8HT$4LHIGH$HHH$HIHT$Ht$DLHINHT$8t$4HHNHt$PHHMf\$fT$MfL$ fD$(MT$ DHLGBHT$8t$4HHA<HT$Ht$DLH<H$LHH$LHEu)H\$XHl$`Ld$hLl$pLt$xL$HĈLmH5}-2HAU0LeHH-2HAT$pffffffffffAWHAVAUIATUHSHHH$LD$`LL$XD$PL$HH$H$HH5FHL$xHT$tLHIFH$HT$hHICHt$`HIHt$XHHD$@sH$HHD$8H$$LHILHT$xt$tLHLHt$hLHJH$LHKLL课Ht$hLH6HT$xt$tLH#:H$$LH:H$LHFHT$8Ht$XHHT$@Ht$`HLmH5+2HAU0LeHH&,2HAT$pHĘ[]A\A]A^A_D$$Ll$ $fL$HfD$P$$D$DD$(L$D$HL$8$HT$@|$t$LL*Ht$hLH5HT$xt$tLH9H$$LH9H$LH'HT$8Ht$XHHT$@Ht$`HHĘ[]A\A]A^A_ffffffAWHAVIAUATUHSHHH$LD$PLL$HD$@L$8 HL$xHT$tHH[CHL$hHT$dLHICCH$HT$XHH@Ht$PHIHt$HHIHT$xt$tLHIIHT$ht$dHHIHt$XLHGH$HH=ID$fL$8Ld$ fD$@$$$$D$DD$(D$|$Lt$L$HL$L3Ht$XLHD$43HT$ht$dHH 7HT$xt$tLH7Ht$HLHHt$PLHT$4uHĈ[]A\A]A^A_LmH5(2HAU0LeHH)2HAT$pHĈ[]A\A]A^A_ffAWHAVAUIATUHSHHH$LD$`LL$XD$PL$HH$H$HHEAHL$xHT$tLHI-AH$HT$hHI>Ht$`HIHt$XHHD$@H$HHD$8H$$LHIGHT$xt$tLHGHt$hLHEH$LH GLL;lHt$hLH1HT$xt$tLH35H$$LH5H$LH6HT$8Ht$XHHT$@Ht$`HLmH5&2HAU0LeHH6'2HAT$pHĘ[]A\A]A^A_D$$Ll$ $fL$HfD$P$$D$DD$(L$D$HL$8$HT$@|$t$LL躿Ht$hLH0HT$xt$tLH4H$$LH4H$LHHT$8Ht$XHHT$@Ht$`HHĘ[]A\A]A^A_ffffffAWHAVIAUATUHSHHH$LD$PLL$HD$@L$80HL$xHT$tHHk>HL$hHT$dLHIS>H$HT$XHH;Ht$PHIHt$HHIHT$xt$tLHIEHT$ht$dHHDHt$XLHCH$HHMDD$fL$8Ld$ fD$@$$$$D$DD$(D$|$Lt$L$HL$LýHt$XLHD$4.HT$ht$dHH2HT$xt$tLH2Ht$HLHHt$PLHD$4uHĈ[]A\A]A^A_LmH5#2HAU0LeHH!$2HAT$pHĈ[]A\A]A^A_ffH\$Hl$Ld$Ll$Lt$L|$HIHHT$PMEHD$ L$T$\$HHL$HHT$DLHB<HL$8HT$4HHI*<HT$(LHI9HT$Ht$DLHICHT$8t$4LHBHt$(LH@Ht$PLHLBf\$fT$DfL$fD$ LLL(Ht$(LHA-HT$8t$4LH\0HT$Ht$DLH1Eu)H\$XHl$`Ld$hLl$pLt$xL$HĈLeH5!2HAT$0H]HHm"2HSp뮐H\$Hl$Ld$Ll$Lt$L|$HIHHT$PMEHD$ L$T$\$Hg HL$HHT$DLH:HL$8HT$4HHI:HT$(LHI7HT$Ht$DLHI`AHT$8t$4LHLAHt$(LH\?Ht$PLH@f\$fT$DfL$fD$ LLL4Ht$(LHA`+HT$8t$4LH.HT$Ht$DLHh/Eu)H\$XHl$`Ld$hLl$pLt$xL$HĈLeH5O 2HAT$0H]HH 2HSp뮐AWHMAVAUIATUHSHHHT$HLD$ HL$@HT$AcffffAWHAVIAUMATUHSHHxHT$8LD$HL$0HT$,HH6HL$ HT$LHI6HT$LHI4Ht$HIt HT$0t$,LHIm=HT$ t$LHY=Ht$LHi;Ht$8LH<At'1ɐHcA T@~D$HT$@LD$LLLL#Ht$LHQ'HT$ t$LH*HT$0t$,LHY+Ht$LH uHx[]A\A]A^A_LmH5K2HAU0LeHH2HAT$pHx[]A\A]A^A_fffffffffffAWHMAVAUIATUHSHHHT$HLD$HL$@HT$2HAT$0H]HH2HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$Lt$HXHIHT$$HL$(MH-HL$HT$LHI,HL$HT$LHI,HT$(t$$LHH3HT$t$LH3HT$t$HH3HLLwHT$t$HHAT!HT$t$LH@!HT$(t$$LH!EuH\$0Hl$8Ld$@Ll$HLt$PHXLeH52HAT$0H]HH\2HSpH\$Hl$Ld$Ll$Lt$L|$HXHHHT$HL$HL$ LD$L $+Ht$HHHt$HIH4$HI|HT$ t$IHHu2LLLHdHT$ t$HHA H4$LHnHt$LH^Ht$LHNEu)H\$(Hl$0Ld$8Ll$@Lt$HL|$PHXffffH]H52HS0H]HH%2HSpffffffH\$Hl$Ld$Ll$Lt$L|$HhHIHT$,HL$0LL$HLD$|*HL$ HT$LHId*Ht$HHDHt$HH$3Ht$pHI#HT$0t$,LHI1HT$ t$HH1H$MLHLHT$ t$HHAHT$0t$,LHhHt$pLHHt$LHH$Ht$HEu#H\$8Hl$@Ld$HLl$PLt$XL|$`HhLeH5$2HAT$0H]HH2HSpffffH\$Hl$Ld$Ll$Lt$L|$HXHHHT$HL$HL$ LD$L $(Ht$HHHt$HIH4$HIHT$ t$IHH/LLLHDHT$ t$HHA-H4$LHHt$LH~Ht$LHnEu)H\$(Hl$0Ld$8Ll$@Lt$HL|$PHXffffH]H52HS0H]HHe2HSpffffffH\$Hl$Ld$Ll$Lt$L|$HhHIHT$,HL$0LL$HLD$'HL$ HT$LHI'Ht$HHdHt$HH$SHt$pHICHT$0t$,LHI\.HT$ t$HHH.H$MLHL3WHT$ t$HHAHT$0t$,LHHt$pLHHt$LHH$Ht$HEu#H\$8Hl$@Ld$HLl$PLt$XL|$`HhLeH5d 2HAT$0H]HH 2HSpffffH\$Hl$Ld$Ll$Lt$L|$HhHHHT$,HL$ HL$0LL$LD$:&Ht$ HHHt$HHD$Ht$HIHt$pHIHT$0t$,HHI,Ht$MLLHHT$0t$,HHATHt$pLHHt$LHHt$LHHT$Ht$ HEu#H\$8Hl$@Ld$HLl$PLt$XL|$`HhH]H5 2HS0H]HH 2HSpffH\$Hl$Ld$Ll$Lt$L|$HxHIHT$1HSpfH\$Hl$Ld$Ll$Lt$L|$HhHIHT$,HL$0HLD$LL$HL$ HT$LHIHt$HHDHt$HI4HT$0t$,LHIM HT$ t$HH9 $DL$xLDD$pHL$LHT$ t$HHAHT$0t$,LHHt$LHHt$LHEu$H\$8Hl$@Ld$HLl$PLt$XL|$`HhÐLeH5U1HAT$0H]HH1HSpfffffH\$Hl$Ld$Ll$Lt$L|$HhHIHT$,HL$0HLD$LL$,HL$ HT$LHIHt$HHHt$HIHT$0t$,LHI HT$ t$HH DL$xDD$pLLHLHT$ t$HHAwHT$0t$,LH#Ht$LHHt$LHEu#H\$8Hl$@Ld$HLl$PLt$XL|$`HhLeH51HAT$0H]HHn1HSpfH\$Hl$Ld$Ll$Lt$L|$HhHIHT$,HL$0HLD$LL$HL$ HT$LHIHt$HHtHt$HIdHT$0t$,LHI}HT$ t$HHi$DL$xLDD$pHL$LtHT$ t$HHA HT$0t$,LHHt$LHHt$LH Eu$H\$8Hl$@Ld$HLl$PLt$XL|$`HhÐLeH51HAT$0H]HH1HSpfffffH\$Hl$Ld$Ll$Lt$L|$HhHIHT$,HL$0HLD$LL$\HL$ HT$LHIDHt$HH$Ht$HIHT$0t$,LHI HT$ t$HHDL$xDD$pLLHLHT$ t$HHAHT$0t$,LHSHt$LHHt$LHEu#H\$8Hl$@Ld$HLl$PLt$XL|$`HhLeH5 1HAT$0H]HH1HSpfH\$Hl$Ld$Ll$Lt$L|$HhHIHT$,HL$0HLD$LL$HL$ HT$LHIHt$HHHt$HIHT$0t$,LHIHT$ t$HH$DL$xLDD$pHL$LDHT$ t$HHA=HT$0t$,LHHt$LHIHt$LH9Eu$H\$8Hl$@Ld$HLl$PLt$XL|$`HhÐLeH51HAT$0H]HH31HSpfffffH\$Hl$Ld$Ll$HHHIHT$HL$ HHL$HT$ LHIHT$ t$LHHqHT$t$ HH]HLBtHT$t$ HHAHT$ t$LHEuH\$(Hl$0Ld$8Ll$@HHLeH51HAT$0H]HH<1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$HHHIHT$HL$ HHL$HT$ LHIxHT$ t$LHHaHT$t$ HHMHLsHT$t$ HHA HT$ t$LHEuH\$(Hl$0Ld$8Ll$@HHLeH51HAT$0H]HH,1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$HHHIHT$HL$ HHL$HT$ LHIhHT$ t$LHHQHT$t$ HH=HLqHT$t$ HHAHT$ t$LHEuH\$(Hl$0Ld$8Ll$@HHLeH51HAT$0H]HH1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$HHHIHT$HL$ HpHL$HT$ LHIXHT$ t$LHHAHT$t$ HH-HLpHT$t$ HHAHT$ t$LHEuH\$(Hl$0Ld$8Ll$@HHLeH51HAT$0H]HH 1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$HHHIHT$HL$ H`HL$HT$ LHIHHT$ t$LHH1HT$t$ HHHLnHT$t$ HHAHT$ t$LHEuH\$(Hl$0Ld$8Ll$@HHLeH5~1HAT$0H]HH1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$HHHIHT$HL$ HPHL$HT$ LHI8HT$ t$LHH!HT$t$ HH HLnHT$t$ HHAHT$ t$LHwEuH\$(Hl$0Ld$8Ll$@HHLeH5n1HAT$0H]HH1HSpH\$(Hl$0Ld$8Ll$@HHffffffAWHIAVAUATE1UHS1HxHT$tDD$TDL$PD$D$pHT$pLHHD$H[H$HIH$HHD$@;\$THD$8}+AwH5k1DHc HAAvDAHT$8H$HHT$@H$HEu"Hx[]A\A]A^A_L$1EKc1HAU0H]HH1HSp뵐H\$Hl$Ld$Ll$HHHIHT$HL$ H0HL$HT$ LHIHT$ t$LHHHT$t$ HHHL2oHT$t$ HHAHT$ t$LHWEuH\$(Hl$0Ld$8Ll$@HHLeH5N1HAT$0H]HH1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$HHHIHT$HL$ H HL$HT$ LHIHT$ t$LHHHT$t$ HHHLnHT$t$ HHAHT$ t$LHGEuH\$(Hl$0Ld$8Ll$@HHLeH5>1HAT$0H]HH1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$HHHIHT$HL$ HHL$HT$ LHIHT$ t$LHHHT$t$ HHHLkHT$t$ HHAHT$ t$LH7EuH\$(Hl$0Ld$8Ll$@HHLeH5.1HAT$0H]HH1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$HHHIHT$HL$ HHL$HT$ LHIHT$ t$LHHHT$t$ HHHLkHT$t$ HHA{HT$ t$LH'EuH\$(Hl$0Ld$8Ll$@HHLeH51HAT$0H]HH1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$HHHIHT$HL$ HHL$HT$ LHIHT$ t$LHHHT$t$ HHHLgHT$t$ HHAkHT$ t$LHEuH\$(Hl$0Ld$8Ll$@HHLeH51HAT$0H]HH1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$HHHIHT$HL$ HHL$HT$ LHIHT$ t$LHHHT$t$ HHHLRgHT$t$ HHA[HT$ t$LHEuH\$(Hl$0Ld$8Ll$@HHLeH51HAT$0H]HH|1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$HHHIHT$HL$ HHL$HT$ LHIHT$ t$LHHHT$t$ HHHLbjHT$t$ HHAKHT$ t$LHEuH\$(Hl$0Ld$8Ll$@HHLeH51HAT$0H]HHl1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$Lt$HXHIHT$$HL$(MHHL$HT$LHIHL$HT$LHIHT$(t$$LHHqHT$t$LH]HT$t$HHIHLLjHT$t$HHAHT$t$LHHT$(t$$LHEuH\$0Hl$8Ld$@Ll$HLt$PHXLeH51HAT$0H]HH 1HSpH\$Hl$Ld$Ll$HHHIHT$HL$ HHL$HT$ LHIhHT$ t$LHHQHT$t$ HH=HLpHT$t$ HHAHT$ t$LHEuH\$(Hl$0Ld$8Ll$@HHLeH51HAT$0H]HH1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$Lt$HXHIHT$$HL$(MHhHL$HT$LHIPHL$HT$LHI8HT$(t$$LHH!HT$t$LH HT$t$HHHLL HT$t$HHAHT$t$LHHT$(t$$LHLEuH\$0Hl$8Ld$@Ll$HLt$PHXLeH5>1HAT$0H]HH1HSpH\$Hl$Ld$Ll$Lt$HXHHHT$$HL$(EHE%HL$HT$HHI HT$(t$$LHHHT$t$HHT$pDL$hDDD$`HL$DTHT$t$HHAHT$(t$$LH5Eu H\$0Hl$8Ld$@Ll$HLt$PHXfLeH5%1HAT$0H]HH1HSpfffffH\$Hl$Ld$Ll$Lt$HXHHHT$$HL$(EHEHL$HT$HHIHT$(t$$LHHHT$t$HHT$pDL$hDDD$`HL$D_HT$t$HHAiHT$(t$$LHEu H\$0Hl$8Ld$@Ll$HLt$PHXfLeH51HAT$0H]HH1HSpfffffH\$Hl$Ld$Ll$Lt$HHHHHT$HL$EEHHL$HT$HHIHT$t$LHHHT$t$HHDD$PDDHL<HT$t$HHAUHT$t$LHEuH\$ Hl$(Ld$0Ll$8Lt$@HHLeH51HAT$0H]HHq1HSpfffH\$Hl$Ld$Ll$Lt$HHHHHT$HL$EEHHL$HT$HHIHT$t$LHHHT$t$HHDD$PDDHLLQHT$t$HHAEHT$t$LHEuH\$ Hl$(Ld$0Ll$8Lt$@HHLeH51HAT$0H]HHa1HSpfffH\$Hl$Ld$Ll$Lt$HHHHHT$HL$EEHHL$HT$HHIHT$t$LHHHT$t$HHDD$PDDHLHT$t$HHA5HT$t$LHEuH\$ Hl$(Ld$0Ll$8Lt$@HHLeH5ӳ1HAT$0H]HHQ1HSpfffH\$Hl$Ld$Ll$Lt$HHHHHT$HL$EEHHL$HT$HHIHT$t$LHHHT$t$HHrDD$PDDHLHT$t$HHA%HT$t$LHEuH\$ Hl$(Ld$0Ll$8Lt$@HHLeH5ò1HAT$0H]HHA1HSpfffH\$Hl$Ld$Ll$Lt$HHHHHT$HL$EEHHL$HT$HHIHT$t$LHHvHT$t$HHbDD$PDDHL蜬HT$t$HHAHT$t$LHEuH\$ Hl$(Ld$0Ll$8Lt$@HHLeH51HAT$0H]HH11HSpfffH\$Hl$Ld$Ll$Lt$HHHHHT$HL$EEHHL$HT$HHI}HT$t$LHHfHT$t$HHRDD$PDDHLHT$t$HHAHT$t$LH豿EuH\$ Hl$(Ld$0Ll$8Lt$@HHLeH51HAT$0H]HH!1HSpfffH\$Hl$Ld$Ll$Lt$H8IHHT$HL$HLHHmHT$t$IHHfHL諥HT$t$HHA$LLHFEu!H\$Hl$Ld$ Ll$(Lt$0H8ffH]H5ů1HS0H]HHE1HSpffffffH\$Hl$Ld$Ll$Lt$H8IHHT$HL$HLHH荞HT$t$IHHHL+HT$t$HHADLLHfEu!H\$Hl$Ld$ Ll$(Lt$0H8ffH]H51HS0H]HHe1HSpffffffH\$Hl$Ld$Ll$HHHIHT$HL$ HHL$HT$ LHIHT$ t$LHHHT$t$ HHHLHT$t$ HHAKHT$ t$LHEuH\$(Hl$0Ld$8Ll$@HHLeH51HAT$0H]HHl1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$HHHIHT$HL$ HHL$HT$ LHIHT$ t$LHHHT$t$ HH}HL蒘HT$t$ HHA;HT$ t$LHEuH\$(Hl$0Ld$8Ll$@HHLeH5ެ1HAT$0H]HH\1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$HHHIHT$HL$ HHL$HT$ LHIHT$ t$LHHHT$t$ HHmHLRHT$t$ HHA+HT$ t$LH׺EuH\$(Hl$0Ld$8Ll$@HHLeH5Ϋ1HAT$0H]HHL1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$HHHIHT$HL$ HHL$HT$ LHIHT$ t$LHHqHT$t$ HH]HLHT$t$ HHAHT$ t$LHǹEuH\$(Hl$0Ld$8Ll$@HHLeH51HAT$0H]HH<1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$HHHIHT$HL$ HHL$HT$ LHIxHT$ t$LHHaHT$t$ HHMHL蒑HT$t$ HHA HT$ t$LH跸EuH\$(Hl$0Ld$8Ll$@HHLeH51HAT$0H]HH,1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$HHHIHT$HL$ HHL$HT$ LHIhHT$ t$LHHQHT$t$ HH=HL"HT$t$ HHAHT$ t$LH觷EuH\$(Hl$0Ld$8Ll$@HHLeH51HAT$0H]HH1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$HHHIHT$HL$ HpHL$HT$ LHIXHT$ t$LHHAHT$t$ HH-HL2HT$t$ HHAHT$ t$LH藶EuH\$(Hl$0Ld$8Ll$@HHLeH51HAT$0H]HH 1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$Lt$HXHIHT$$HL$(MHXHL$HT$LHI@HL$HT$LHI(HT$(t$$LHHHT$t$LHHT$t$HHHLL軔HT$t$HHA褴HT$t$LH萴HT$(t$$LH<EuH\$0Hl$8Ld$@Ll$HLt$PHXLeH5.1HAT$0H]HH1HSpH\$Hl$Ld$Ll$HHHIHT$HL$ H HL$HT$ LHIHT$ t$LHHHT$t$ HHHLHT$t$ HHA蛳HT$ t$LHGEuH\$(Hl$0Ld$8Ll$@HHLeH5>1HAT$0H]HH1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$Lt$HXHIHT$$HL$(MHHL$HT$LHIHL$HT$LHIؽHT$(t$$LHHHT$t$LHHT$t$HHHLLkHT$t$HHATHT$t$LH@HT$(t$$LHEuH\$0Hl$8Ld$@Ll$HLt$PHXLeH5ޣ1HAT$0H]HH\1HSpHl$Ld$Ll$Lt$L|$H\$HhILHT$4HLL$IHL$(HT$$LHI諼HL$HT$LHI蓼Ht$HISHT$(t$$LHIlHT$t$LHXAEwHH1Hc HHT$t$LH HT$(t$$HL赱Ht$LHLuHH51AV0LmH<1HHAUpH\$8Hl$@Ld$HLl$PLt$XL|$`HhËT$4LH̶DD$4LLLHH[T$4HLHD$譪HT$t$LHIHT$(t$$LHHt$LHUD$[/T$4LH蕴DD$4LLLHH[T$4HLHD$tT$4LHDD$4LLLHHZT$4HLHD$c1T$4LH/DD$4LLLHHZT$4HLHD$T$4LH DD$4LHLLHSZT$4HLHD$}ffffffH\$Hl$Ld$H(HHHL$HT$HT$t$HHHHVHT$t$AHHbEuH\$Hl$Ld$ H(H]H5^1HS0H]HHޠ1HSpH\$Hl$Ld$ H(H\$Hl$Ld$Ll$HHHIHT$HL$ H@HL$HT$ LHI(HT$ t$LHHHT$t$ HHHLOHT$t$ HHA軭HT$ t$LHgEuH\$(Hl$0Ld$8Ll$@HHLeH5^1HAT$0H]HHܟ1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$H(HHHL$HT$8HT$t$HHH!H XHT$t$AHH袭EuH\$Hl$Ld$ H(H]H51HS0H]HH1HSpH\$Hl$Ld$ H(H\$Hl$Ld$Ll$HHHIHT$HL$ H耷HL$HT$ LHIhHT$ t$LHHQHT$t$ HH=HL2THT$t$ HHAHT$ t$LH觬EuH\$(Hl$0Ld$8Ll$@HHLeH51HAT$0H]HH1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$H(HHHL$HT$xHT$t$HHHaHJHT$t$AHHEuH\$Hl$Ld$ H(H]H5ޜ1HS0H]HH^1HSpH\$Hl$Ld$ H(H\$Hl$Ld$Ll$HHHIHT$HL$ HHL$HT$ LHI訵HT$ t$LHH葼HT$t$ HH}HLJHT$t$ HHA;HT$ t$LHEuH\$(Hl$0Ld$8Ll$@HHLeH5ޛ1HAT$0H]HH\1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$H(HHHL$HT$踴HT$t$HHH衻H KHT$t$AHH"EuH\$Hl$Ld$ H(H]H51HS0H]HH1HSpH\$Hl$Ld$ H(H\$Hl$Ld$Ll$HHHIHT$HL$ HHL$HT$ LHIHT$ t$LHHѺHT$t$ HH轺HLBJHT$t$ HHA{HT$ t$LH'EuH\$(Hl$0Ld$8Ll$@HHLeH51HAT$0H]HH1HSpH\$(Hl$0Ld$8Ll$@HHffffffAWHAVAUATIUHSHxHT$\LD$@LL$8կHL$PHT$LLHIݲDl$\IAE1E9Ht$@H肇Ht$8HHD$0pT$\LHIHT$Pt$LLHHw$$HT$`$D$M $HL$0|$t$HLWHT$Pt$LLHAT$\HLHޠHt$8LHHT$0Ht$@HE8Hx[]A\A]A^A_LeH51HAT$0H]H 1HHE1SpIcDLHA&E9D`|Ht$@HPHt$8HHD$(>T$\LHIHT$Pt$LLHHED$D$HT$`D$HL$(LHDL$D$D$MDT$HT$Pt$LLHAȥT$\HLH覟Ht$8LHօHT$(Ht$@HąELuHH5]1AV0LmH1HHAUpHx[]A\A]A^A_fffH\$Hl$Ld$Ll$Lt$L|$HHHH$LD$XLL$PD$HL$@HIL$āHL$xHT$tHHHL$hHT$dLHIHt$XHIgHt$PHHD$8ULHHD$0襅HT$xt$tLHI螶HT$ht$dLH芶H$LHLL(HT$ht$dLH0HT$xt$tLHܤLLH^HT$0Ht$PHHT$8Ht$XHڄLmH51HAU0LeHH>1HAT$pH$H$L$L$L$L$HĸD$D$$$fL$@fD$H$$DL$(DD$ D$D$L$$HL$0HT$8|$t$LLdHT$ht$dLHHT$xt$tLH誣LLH,HT$0Ht$PH躃HT$8Ht$XH訃ffffffffH\$Hl$Ld$Ll$Lt$L|$HHHHT$pHL$0LD$HLL$@D$8ILl$0HL$hHT$dHHUHL$XHT$TLHI=Ht$HHH轂Ht$@HI譂HT$ht$dLHIHT$Xt$THHHt$pHHb$D$Ll$0fD$8fL$0$$$T$$D$DD$(D$|$ t$LL$$LLHHT$Xt$THHA6HT$ht$dLHHt$@LHHt$HLHEu5H\$xH$L$L$L$L$HĨLmH51HAU0LeHH1HAT$pffffffffffH\$Hl$Ld$Ll$Lt$L|$HHHH$LD$XLL$PD$HL$@HIL$}HL$xHT$tHH?HL$hHT$dLHI'Ht$XHI觀Ht$PHHD$8蕀LHHD$0HT$xt$tLHIޱHT$ht$dLHʱH$LH7LLhHT$ht$dLHpHT$xt$tLHLLH~HT$0Ht$PH,HT$8Ht$XHLmH51HAU0LeHH~1HAT$pH$H$L$L$L$L$HĸD$D$$$fL$@fD$H$$DL$(DD$ D$D$L$$HL$0HT$8|$t$LLHT$ht$dLH>HT$xt$tLHLLHL~HT$0Ht$PH~HT$8Ht$XH~ffffffffH\$Hl$Ld$Ll$Lt$L|$HHHHT$pHL$0LD$HLL$@D$8ILl$0ZzHL$hHT$dHH蕨HL$XHT$TLHI}Ht$HHH}Ht$@HI}HT$ht$dLHIFHT$Xt$THH2Ht$pHH袮$D$Ll$0fD$8fL$0$$$T$$D$DD$(D$|$ t$LL$$LLHHT$Xt$THHAvHT$ht$dLH"Ht$@LHB}Ht$HLH2}Eu5H\$xH$L$L$L$L$HĨLmH5ݍ1HAU0LeHH\1HAT$pffffffffffH\$Hl$Ld$Ll$Lt$L|$HHIHT$LHL$PHLD$0LL$(試HL$@HT$v1HS0H]HHv1HSpH\$Hl$Ld$ H(H\$Hl$Ld$Ll$HHHIHT$HL$ H HL$HT$ LHIHT$ t$LHHHT$t$ HHݕHL HT$t$ HHA蛃HT$ t$LHGEuH\$(Hl$0Ld$8Ll$@HHLeH5>u1HAT$0H]HHu1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$H(HHHL$HT$HT$t$HHHH HT$t$AHH肃EuH\$Hl$Ld$ H(H]H5~t1HS0H]HHt1HSpH\$Hl$Ld$ H(H\$Hl$Ld$Ll$HHHIHT$HL$ H`HL$HT$ LHIHHT$ t$LHH1HT$t$ HHHL HT$t$ HHAہHT$ t$LH臂EuH\$(Hl$0Ld$8Ll$@HHLeH5~s1HAT$0H]HHs1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$HHHIHT$HL$ HPHL$HT$ LHI8HT$ t$LHH!HT$t$ HH HL HT$t$ HHAˀHT$ t$LHwEuH\$(Hl$0Ld$8Ll$@HHLeH5nr1HAT$0H]HHr1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$HHHIHT$HL$ H@HL$HT$ LHI(HT$ t$LHHHT$t$ HHHL HT$t$ HHAHT$ t$LHgEuH\$(Hl$0Ld$8Ll$@HHLeH5^q1HAT$0H]HHq1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$HHHIHT$HL$ H0HL$HT$ LHIHT$ t$LHHHT$t$ HHHLB HT$t$ HHA~HT$ t$LHWEuH\$(Hl$0Ld$8Ll$@HHLeH5Np1HAT$0H]HHp1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$HHHIHT$HL$ H HL$HT$ LHIHT$ t$LHHHT$t$ HHݏHL HT$t$ HHA}HT$ t$LHG~EuH\$(Hl$0Ld$8Ll$@HHLeH5>o1HAT$0H]HHo1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$Lt$HXHIHT$$HL$(MHHL$HT$LHIHL$HT$LHI؇HT$(t$$LHHHT$t$LH譎HT$t$HH虎HLL HT$t$HHAT|HT$t$LH@|HT$(t$$LH|EuH\$0Hl$8Ld$@Ll$HLt$PHXLeH5m1HAT$0H]HH\n1HSpHl$Ld$Ll$Lt$L|$H\$HhHIHT$,HL$0MEHHL$ HT$LHI訆HL$HT$ LHI萆HT$0t$,LHIyHT$ t$LHeHT$t$ LHQDLLL HT$t$ LHA {HT$ t$LHzHT$0t$,LH{Eu#H\$8Hl$@Ld$HLl$PLt$XL|$`HhLmH5l1HAU0H]HH m1HSp뵐H\$Hl$Ld$Ll$HHHIHT$HL$ H者HL$HT$ LHIhHT$ t$LHHQHT$t$ HH=HLr HT$t$ HHAyHT$ t$LHzEuH\$(Hl$0Ld$8Ll$@HHLeH5k1HAT$0H]HHl1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$HHHIHT$HL$ HpHL$HT$ LHIXHT$ t$LHHAHT$t$ HH-HLr HT$t$ HHAxHT$ t$LHyEuH\$(Hl$0Ld$8Ll$@HHLeH5j1HAT$0H]HH k1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$HHHIHT$HL$ H`HL$HT$ LHIHHT$ t$LHH1HT$t$ HHHL HT$t$ HHAwHT$ t$LHxEuH\$(Hl$0Ld$8Ll$@HHLeH5~i1HAT$0H]HHi1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$HHHIHT$HL$ HPHL$HT$ LHI8HT$ t$LHH!HT$t$ HH HLB HT$t$ HHAvHT$ t$LHwwEuH\$(Hl$0Ld$8Ll$@HHLeH5nh1HAT$0H]HHh1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$HHHIHT$HL$ H@HL$HT$ LHI(HT$ t$LHHHT$t$ HHHL蒆HT$t$ HHAuHT$ t$LHgvEuH\$(Hl$0Ld$8Ll$@HHLeH5^g1HAT$0H]HHg1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$HHHIHT$HL$ H0HL$HT$ LHIHT$ t$LHHHT$t$ HHHLb}HT$t$ HHAtHT$ t$LHWuEuH\$(Hl$0Ld$8Ll$@HHLeH5Nf1HAT$0H]HHf1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$Lt$L|$HhHIHT$,HL$0HMEHL$ HT$LHI~LHHTHT$0t$,LHIӅHT$ t$HH迅$$HDL$xDD$pLT$$L$D$L HT$ t$HHAMsHT$0t$,LHsLLH{TEu&H\$8Hl$@Ld$HLl$PLt$XL|$`HhffLeH5d1HAT$0H]HHSe1HSpfffffH\$Hl$Ld$Ll$Lt$L|$HhHIHT$,HL$0HME}HL$ HT$LHI}LHHzSHT$0t$,LHIsHT$ t$HH_$$HDL$xDD$pLT$$L$D$LT HT$ t$HHAqHT$0t$,LHrLLHSEu&H\$8Hl$@Ld$HLl$PLt$XL|$`HhffLeH5uc1HAT$0H]HHc1HSpfffffH\$Hl$Ld$Ll$Lt$L|$HhHIHT$,HL$0HMEP|HL$ HT$LHI8|LHHRHT$0t$,LHIHT$ t$HH$$HDL$xDD$pLT$$L$D$L贘 HT$ t$HHApHT$0t$,LH9qLLHQEu&H\$8Hl$@Ld$HLl$PLt$XL|$`HhffLeH5b1HAT$0H]HHb1HSpfffffH\$Hl$Ld$Ll$Lt$L|$HxHIHT$HT$ t$LHIoHT$t$ HHoLHL HT$t$ HHA]HT$ t$LHI^LLH>Eu&H\$(Hl$0Ld$8Ll$@Lt$HL|$PHXffLeH5%O1HAT$0H]HHO1HSpfffffH\$Hl$Ld$Ll$Lt$H8HIHT$HL$H hLHH<HT$t$IHHnLH& HT$t$HHAd]LLH<Eu!H\$Hl$Ld$ Ll$(Lt$0H8ffH]H5EN1HS0H]HHN1HSpffffffH\$Hl$Ld$Ll$Lt$L|$HXHIHT$HL$ MH#gHL$HT$ LHI gLHH;HT$ t$LHImHT$t$ HHmLHL$JHT$t$ HHA[HT$ t$LH9\LLH;Eu&H\$(Hl$0Ld$8Ll$@Lt$HL|$PHXffLeH5M1HAT$0H]HHM1HSpfffffH\$Hl$Ld$Ll$Lt$H8HIHT$HL$HeLHH:HT$t$IHHlLH HT$t$HHAT[LLH:Eu!H\$Hl$Ld$ Ll$(Lt$0H8ffH]H55L1HS0H]HHL1HSpffffffH\$Hl$Ld$Ll$Lt$L|$HXHIHT$HL$ MHeHL$HT$ LHIdLHH9HT$ t$LHIkHT$t$ HHkLHL9HT$t$ HHA}YHT$ t$LH)ZLLH9Eu&H\$(Hl$0Ld$8Ll$@Lt$HL|$PHXffLeH5K1HAT$0H]HHK1HSpfffffH\$Hl$Ld$Ll$Lt$H8HIHT$HL$HcLHH9HT$t$IHHjLHk HT$t$HHADYLLH9Eu!H\$Hl$Ld$ Ll$(Lt$0H8ffH]H5%J1HS0H]HHJ1HSpffffffH\$Hl$Ld$Ll$Lt$L|$HXHIHT$HL$ MHcHL$HT$ LHIbLHH8HT$ t$LHIiHT$t$ HHiLHLt HT$t$ HHAmWHT$ t$LHXLLH8Eu&H\$(Hl$0Ld$8Ll$@Lt$HL|$PHXffLeH5H1HAT$0H]HHsI1HSpfffffH\$Hl$Ld$Ll$Lt$H8HIHT$HL$HaLHH7HT$t$IHHhLH HT$t$HHA4WLLH7Eu!H\$Hl$Ld$ Ll$(Lt$0H8ffH]H5H1HS0H]HHH1HSpffffffH\$Hl$Ld$Ll$Lt$L|$HXHIHT$HL$ MH`HL$HT$ LHI`LHH6HT$ t$LHIgHT$t$ HHgLHLd HT$t$ HHA]UHT$ t$LH VLLH6Eu&H\$(Hl$0Ld$8Ll$@Lt$HL|$PHXffLeH5F1HAT$0H]HHcG1HSpfffffH\$Hl$Ld$Ll$Lt$H8HIHT$HL$H_LHH5HT$t$IHHfLH+ HT$t$HHA$ULLH5Eu!H\$Hl$Ld$ Ll$(Lt$0H8ffH]H5F1HS0H]HHF1HSpffffffH\$Hl$Ld$Ll$Lt$L|$HXHIHT$HL$ MH^HL$HT$ LHI^LHH4HT$ t$LHIeHT$t$ HHeLHL HT$t$ HHAMSHT$ t$LHSLLH{4Eu&H\$(Hl$0Ld$8Ll$@Lt$HL|$PHXffLeH5D1HAT$0H]HHSE1HSpfffffH\$Hl$Ld$Ll$Lt$H8HIHT$HL$H]LHH3HT$t$IHHdLH HT$t$HHASLLH3Eu!H\$Hl$Ld$ Ll$(Lt$0H8ffH]H5C1HS0H]HHuD1HSpffffffH\$Hl$Ld$Ll$Lt$L|$HXHIHT$HL$ MH\HL$HT$ LHI\LHH2HT$ t$LHIcHT$t$ HHcLHL$ HT$t$ HHA=QHT$ t$LHQLLHk2Eu&H\$(Hl$0Ld$8Ll$@Lt$HL|$PHXffLeH5B1HAT$0H]HHCC1HSpfffffH\$Hl$Ld$Ll$Lt$H8HIHT$HL$H[LHHm0HT$t$IHHbLH HT$t$HHAQLLHf0Eu!H\$Hl$Ld$ Ll$(Lt$0H8ffH]H5A1HS0H]HHeB1HSpffffffH\$Hl$Ld$Ll$Lt$L|$HXHIHT$HL$ MHZHL$HT$ LHIZLHHm/HT$ t$LHIaHT$t$ HHraLHL$!HT$t$ HHA-OHT$ t$LHOLLH;/Eu&H\$(Hl$0Ld$8Ll$@Lt$HL|$PHXffLeH5@1HAT$0H]HH3A1HSpfffffH\$Hl$Ld$Ll$Lt$H8HIHT$HL$HYLHH}/HT$t$IHHv`LH HT$t$HHANLLHv/Eu!H\$Hl$Ld$ Ll$(Lt$0H8ffH]H5?1HS0H]HHU@1HSpffffffH\$Hl$Ld$Ll$Lt$L|$HXHIHT$HL$ MHXHL$HT$ LHIXLHH}.HT$ t$LHIv_HT$t$ HHb_LHL HT$t$ HHAMHT$ t$LHMLLHK.Eu&H\$(Hl$0Ld$8Ll$@Lt$HL|$PHXffLeH5>1HAT$0H]HH#?1HSpfffffH\$Hl$Ld$Ll$Lt$H8HIHT$HL$HWLHHM,HT$t$IHHf^LH HT$t$HHALLLHF,Eu!H\$Hl$Ld$ Ll$(Lt$0H8ffH]H5=1HS0H]HHE>1HSpffffffH\$Hl$Ld$Ll$Lt$L|$HXHIHT$HL$ MHVHL$HT$ LHIVLHHM+HT$ t$LHIf]HT$t$ HHR]LHLTHT$t$ HHA KHT$ t$LHKLLH+Eu&H\$(Hl$0Ld$8Ll$@Lt$HL|$PHXffLeH5<1HAT$0H]HH=1HSpfffffAVHMAUATIUHSH`HL$XHT$TUHL$HHT$DLHIqUHL$@HT$QHT$t$ HH*QDHLpHT$t$ HHA>HT$ t$LH?EuH\$(Hl$0Ld$8Ll$@HHffH]H501HS0H]HH11HSpH\$(Hl$0Ld$8Ll$@HHfH\$Hl$Ld$Ll$Lt$L|$HxHIHT$HL$ HT$LHI>Ht$HIH$HHIHT$0t$,LHH$EHT$ t$LHsEHt$8LHD1Hc T@~H4$LLd$@vHT$ t$LH2HT$0t$,LH3H$H$H"Ht$HHLmHH5$1AU0LeH%1HHAT$pHx[]A\A]A^A_H4$L߶AtdHT$ t$LHd2HT$0t$,LH3H$H$HHt$HH|EaHx[]A\A]A^A_D$L$ LLL AzfAWHMAVAUIATUHSHHhHT$(DL$HL$ HT$HH<HL$HT$ LHI<LHIHT$ t$LHICHT$t$ LH~CHt$(LHBAt-1ffffHcA T0~D$L$HT$0D$L$LLLHT$t$ LH0HT$ t$LH1LLHuHh[]A\A]A^A_LmH5"1HAU0LeHH #1HAT$pHh[]A\A]A^A_fffffffffffAWHAVAUIATUHSHHxHT$8LD$DL$  HL$0HT$,HHM;HL$ HT$LHI5;Ht$HIH$HHIHT$0t$,LHH$AHT$ t$LHAHt$8LHSA1Hc T@~H4$LLd$@bfHT$ t$LHj/HT$0t$,LH0H$H$HrHt$HHLmHH5!1AU0LeH!1HHAT$pHx[]A\A]A^A_H4$LeAtdHT$ t$LH.HT$0t$,LH/H$H$HHt$HHEaHx[]A\A]A^A_D$L$ LLLZAzfAWHMAVAUIATUHSHHhHT$(DL$ HL$ HT$HH?9HL$HT$ LHI'9LHI HT$ t$LHI@HT$t$ LH?Ht$(LH^?At-1ffffHcA T0~D$L$HT$0D$L$LLLaHT$t$ LH[-HT$ t$LH.LLHuHh[]A\A]A^A_LmH51HAU0LeHHz1HAT$pHh[]A\A]A^A_fffffffffffH\$Hl$Ld$Ll$HHHIHT$HL$ H7HL$HT$ LHI7HT$ t$LHH>HT$t$ HH>HLHT$t$ HHAK,HT$ t$LH,EuH\$(Hl$0Ld$8Ll$@HHLeH51HAT$0H]HHl1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$Lt$HXHIHT$$HL$(MH6HL$HT$LHI6HL$HT$LHI6HT$(t$$LHHq=HT$t$LH]=HT$t$HHI=HLL۔HT$t$HHA+HT$t$LH*HT$(t$$LH+EuH\$0Hl$8Ld$@Ll$HLt$PHXLeH51HAT$0H]HH 1HSpH\$Hl$Ld$Ll$HHHIHT$HL$ H5HL$HT$ LHIh5HT$ t$LHHQ<HT$t$ HH=<HLHT$t$ HHA)HT$ t$LH*EuH\$(Hl$0Ld$8Ll$@HHLeH51HAT$0H]HH1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$Ll$Lt$HXHIHT$$HL$(MHh4HL$HT$LHIP4HL$HT$LHI84HT$(t$$LHH!;HT$t$LH ;HT$t$HH:HLLK<HT$t$HHA(HT$t$LH(HT$(t$$LHL)EuH\$0Hl$8Ld$@Ll$HLt$PHXLeH5>1HAT$0H]HH1HSpH\$Hl$Ld$H(HHHL$HT$83HT$t$HHH!:HiHT$t$AHH(EuH\$Hl$Ld$ H(H]H51HS0H]HH1HSpH\$Hl$Ld$ H(H\$Hl$Ld$Ll$HHHIHT$HL$ H2HL$HT$ LHIh2HT$ t$LHHQ9HT$t$ HH=9HL袍HT$t$ HHA&HT$ t$LH'EuH\$(Hl$0Ld$8Ll$@HHLeH51HAT$0H]HH1HSpH\$(Hl$0Ld$8Ll$@HHffffffH\$Hl$Ld$H(HHHL$HT$x1HT$t$HHHa8HɎHT$t$AHH&EuH\$Hl$Ld$ H(H]H51HS0H]HH^1HSpH\$Hl$Ld$ H(H\$Hl$Ld$Ll$HHHIHT$HL$ H0HL$HT$ LHI0HT$ t$LHH7HT$t$ HH}7HLHT$t$ HHA;%HT$ t$LH%EuH\$(Hl$0Ld$8Ll$@HHLeH51HAT$0H]HH\1HSpH\$(Hl$0Ld$8Ll$@HHÐATSHHL'A$IHHLH 1H1H"DHLHH 1H1H"DHLHH 1H1H"DHLHH 1Hp1Hx"DHLHH h1HS1H\"DHLHH D1HE1H@"DHLHH 1H1H,"DHLHH 1H1H"DHLHH 1H1H!DHLHH 1H1H!DH[A\ffffffHSHH!DH1[fffATSHHL'A$IHHLH H1H&1Hl!DHLHH $1H1HP!DHLHH 1H'1H4!DHLHH 1H1H!DHLHH 1H1H DHLHH 1H1H DHLHH p1H1H DHLHH L1H1H DHLHH (1H1H DHLHH 1H1Hh DHLHH 1HY1H| DHLHH L1HH1H0 DHLHH (1H01H DHLHH 1H1HDHLHH 1H1HDH[A\fffHH1HfffffffffHL1AHffffffffHLAHffffffHH1HfffffffffHL1AHffffffffHLAHffffffHH1HfffffffffHL1AHffffffffHLAHffffffHH1HfffffffffHL1A HffffffffHLA HffffffHH1HfffffffffHL1AHffffffffHLAHffffffHH1HfffffffffHL1AHffffffffHLAHffffffHHcHH41HfffHHcLH4I1LAHffffffffffHHcLH4IȹLAHffffffffHHcHH41HfffHHcLH4I1LAHffffffffffHHcLH4IȹLAHffffffffHHcHH41HfffHHcLH4I1LAHffffffffffHHcLH4IȹLAHffffffffHHcHH41HfffHHcLH4I1LAHffffffffffHHcLH4IȹLAHffffffffHHcHH41HfffHHcLH4I1LAHffffffffffHHcLH4IȹLAHffffffffHHXHffffffffffHHXHffffffffffHHXHffffffffffHHXHffffffffffHHXHffffffffffHHXHffffffffffHHcHH4XHfffffHHXHffffffffffATSHHL'A$IHHLH H1HI1HDHLHH $1H 1HDHLHH 1H1HDHLHH 1H1HDHLHH 1H1H|DHLHH 1H1H`DHLHH p1HG1HDDHLHH L1H-1H(DHLHH > 1H1H DHLHH  1H 1HDHLHH 1H 1HDHLHH 1H 1HDHLHH 1H 1HDHLHH 1H~ 1HxDHLHH f 1H_ 1H\DHLHH B 1H@ 1H@DHLHH  1H! 1H$DH[A\fffffffffAWAVAUATIUHSHXDR(LrHL$PZ L:Lj8LZ@DT$L$Lt$0Dr\$Z$DJ,L|$8Ll$(L\$ Dz L$L$Dj0DL$A΃L$oWL$LD$ Ad$ JHDAhH]HDDLHhLMHDDLHAhLEHDDLHAhL]HD1LHAhLUHDLL$HAhH]HDLL$HhLMHnDLL$HAhLEHDLHL$PHA@AH]t$ HL]HLD$0L$ 1HHALEHDHLHA@EH]t$HLUH1HLD$ L$HALMHDHLHA@Et5AUAfffLmHwDLHL$HHA@A#A9H]H51HS0L}HQ1HHAWpHEH(DHL$@LH@LmH DLHAAHA8L}1HHAhLuHHDLHA@HE1HHhLMHHDLHA@HE1HHhLUHLHHDA@LEL$LHH{DAhHX[]A\A]A^A_fffH]t$ HLMLD$0HËL$ 1HHALJD$ HDAhL]HDDLHAhLUHDDLHAhLMHnDDLHAhLEH[DLHAhL]HIDLL$HAhLUH5DLL$HAhLMH!DLL$HAhLEHMDLHL$PHA@EAEM|DL]D$ JLHkDHAhLUH^DDLHAhLMHKDDLHAhLEH8DDLHAhL]H%DLHAhLUHDLL$HAhLMHDLL$HAhLEHDLL$HAhL]HHDHL$PLA@@ǃ gfLmHHAhL]HHDLHA@LuHHAhL}HH{DLHA@HEL}t$HAL]IHD$81ҋL$LHLAH]LHL}LAHDHA`E9I|fffffHEHDLH]t$HHL$8LUIcHHHL1ҋL$ALEDAHLHApE9|fffLmHHAhLuHHTDLHA@HEmH]t$HL]Ht$8I1ҋL$HLHAL}LHALMLAHDHA`E9IffH]t$HH|$8LUIcH1HLϋL$HALEDAHLHApE9|Aƃ0{H]t$HLmHD$HLD$(L$Ht$H1HALmt$HALUHD$HLD$(L$Ht$H1HAFSLHӹAHL[ffSLH1AHL[ffffHHLHfffffffffffH\$Ld$Ll$Lt$H(HHIAIXL MD)DLHAL`LH\$Ld$Ll$Lt$ H(fffffSLHӹAH%L[ffHl$Ld$H\$Ll$Lt$L|$HxIA8Ey0HT$8IHHt$@HL$0LD$(HD$1MHT$ IQMi@AY$Eq HT$KEHD$+ ȨH]HT$Ht$8HAt~A LuHH50AV0LmH1HHAUpI<$4KL,KH|$("KH\$HHl$PLd$XLl$`Lt$hL|$pHxEfAu$x1fffHt$ H|$(LcLMJJ4HAyH|$H51+HETHT$Ht$@H?=LEHt$0LHA$x,HD$ L|$(LcL]HJK4AyH|$H51蚖HtHT$Ht$@HH|$H51mHuH|$H51WHuH|$H51AHuYfH|$H51'HH|$H51 HH|$H5n1HEH]HT$Ht$@HffffffSLH1AHI[ffffHl$Ld$H\$Ll$Lt$L|$HxIA8Ey0HT$8IHHt$@HL$0LD$(HD$1MHT$ IQMi@AY$Eq HT$HEHD$( ȨH]1HT$Ht$8HA~ALuHH50AV0LmH 0HHAUpI<$#HLHH|$(HH\$HHl$PLd$XLl$`Lt$hL|$pHxEeAu$x-fffHt$ H|$(LcLM1JJ4HAyH|$H50HEXHT$Ht$@HBALEHt$01LHA$x.fffHD$ L|$(LcL]1HJK4AyH|$H50莓HtHT$Ht$@HH|$H50aHuH|$H50KHuH|$H505Hu]ffffH|$H50HH|$H5p0HH|$H5^0HEH]1HT$Ht$@HffffffffHHFHfffffffffffH\$Ld$Ll$Lt$H(HHIAIXL MD)DLHALEH\$Ld$Ll$Lt$ H(fffffAUIATIUHSHx*fffffLMHcHIItAyLEEL=EH[]A\A]fAUIATIUHSHx'fffffLMHcHIIt1AyLDLDH[]A\A]fffAUIATIUHSHx*fffffLMHcHIItAyLDL}DH[]A\A]fAUIATIUHSHx'fffffLMHcHIIt1AyL(DL DH[]A\A]fffAUIATIUHSHx*fffffLMHcHIItAyLCLCH[]A\A]fAUIATIUHSHx'fffffLMHcHIIt1AyLhCL`CH[]A\A]fffAUIATIUHSHx*fffffLMHcHIItAyLCLBH[]A\A]fAUIATIUHSHx'fffffLMHcHIIt1AyLBH[]A\A]fffffffffAUIATIUHSHx'fffffLMHcHIIt1AyLHBL@BH[]A\A]fffAUIATIUHSHx*fffffLMHcHIItAyLALAH[]A\A]fAUIATIUHSHx'fffffLMHcHIIt1AyLAH[]A\A]fffffffffAUIATIUHSHx'fffffLMHcHIIt1AyL(AL AH[]A\A]fffH\$Hl$Ld$Ll$Lt$L|$HhLj0LzXHLZhLBHLHrpIHJxLl$0Lr@Lj8L|$ Lz`HHL\$LD$(Ht$HL$Hu0H$HUXAL HUPHu HALHUHHuHALHU@HuHAHD$0H;E@tH @L;mHtL@L;uPtL@HD$(H;EXtH?H|$ tL?MtL?H|$t H|$?H|$t H|$?H|$t H|$?H<$t H<$?H?L~?H\$8Hl$@Ld$HLl$PLt$XL|$`HhffffffffH\$Hl$Ld$Ll$Lt$L|$HhLj0LzXHLZhLBHLHrpIHJxLl$0Lr@Lj8L|$ Lz`HHL\$LD$(Ht$HL$Hu01H$HUXAL 1HUPHu HAL1HUHHuHAL1HU@HuHAHD$0H;E@tHl>L;mHtL^>L;uPtLP>HD$(H;EXtH=>H|$ tL->MtL >H|$t H|$>H|$t H|$=H|$t H|$=H<$t H<$=H=L=H\$8Hl$@Ld$HLl$PLt$XL|$`HhfffffUIHSHHHywL0Ic LfffHH50HP0HHH'0HPpHE=H[]LHcHfffH)ǹLHHAH=H[]LHcHLHcLHcHffffffUIHSHHHywL0Ic LfffHH50HP0HHHg0HPpH<H[]LHcHfffH)1LHHAHS<H[]LHcHLHcLHcHfffffffffAWAVAUIATIUHHSHHLD$LL$Lt$P <IHEHHTCH AGHEHHACH AGHEHH.CH AG HEHHCH AG0HEHHCH AG$HEHHCH AG HEHHCH AG(HEHHCH AG,HEHHCHI$LeHHCHA$IELmHHCHAHL$LeHHoCHHA$H]HHIXAH1H<:A;HT$IH|+LuHI4$AXH|$XHL[]A\A]A^A_HELHhHcA;I|fffffffffLd$Ll$Lt$L|$H\$H(HIAIXD)Hc9M $IIDLLAPLH$Ld$Ll$Lt$L|$ H(fffLd$Ll$Lt$L|$H\$H(HIAIXD)Hct9M $IIDLLA@LH$Ld$Ll$Lt$L|$ H(ffffffLl$IHcHl$Ld$Lt$L|$H\$AH8H1I8D9I|&LH\$Hl$Ld$Ll$ Lt$(L|$0H8IEHc1I4LD9I|ffffLl$IHcHl$Ld$Lt$L|$H\$AH8H1Ig8D9I|&LH\$Hl$Ld$Ll$ Lt$(L|$0H8IEHc1I4LD9I|ffffLl$IHcHl$Ld$Lt$L|$H\$AH8H1I7D9I|&LH\$Hl$Ld$Ll$ Lt$(L|$0H8IEHc1I4LD9I|ffffLl$IHcHl$Ld$Lt$L|$H\$AH8H1IG7D9I|&LH\$Hl$Ld$Ll$ Lt$(L|$0H8IEHc1I4LD9I|ffffLl$IHcHl$Ld$Lt$L|$H\$AH8H1I6D9I|&LH\$Hl$Ld$Ll$ Lt$(L|$0H8IEHc1I4LD9I|ffffHl$Ld$Ll$Lt$H\$H(HIHIXA$H1H<6A;$I|#LH$Hl$Ld$Ll$Lt$ H(ffHELHhHcA;$ID|fffffffffHl$Ld$Ll$Lt$H\$H(HIHIXA$H1H<|5A;$I|#LH$Hl$Ld$Ll$Lt$ H(ffHELHhHcA;$ID|fffffffffAWAVIAUIATSH5`I4IL3ILHHCA L3LHA$HCA L3LHAD$HCA L3LHAD$H~CA L3LHAD$ HjCA L3LHAD$HVCA L3LHAD$HBCA L3LHAD$H.CA L3LHAD$HJCA L3LHAD$ HCA L3LHAD$$HCA L3LHAD$(HCAL3LHIHCAL3LHIGHCAL3HCLHIG AL+I7HIG0AXL3IwHAGAXL+Iw HAGAXL+AG(Iw0HAXID$XAG8ID$`AD$PAD$TID$hID$pID$xIDŽ$[LA\A]A^A_ffffffAWIAVIAUIATSH@H $D$D$D$ D$D$D$F2IHLHHC A$HLHHC AD$HLHHC AD$HLHHC AD$ HLHHpC A $LHHyCAD$H AD$0HLHHBC AD$,HLHHC AL3LHH&CAIH L[A\A]A^A_ffffffffHcHl$H\$H, Ld$Ll$IH(IHc1HI$LL1HHHHl$H\$Ld$Ll$ H(fffH\$Ld$HLl$Lt$HcH(AI0IHLH1DHLELH\$Ld$Ll$Lt$ H(ffffffLd$Ll$Lt$H\$Hl$L|$HXEy01IHt$ HT$ILD$MAY$Ai #0EHD$% ȨtwI$1Ht$LIEtlM4$LH50AV0I,$HU0HLUpH$IUH\$(Hl$0Ld$8Ll$@Lt$HL|$PHXEtAE$uHc|$`HM/l$`H$x(HD$I$Lc1LJ4H $JyH|$H50p{HE^T$hHt$ L"fIE8?fffffI$1LLIE@fffHc|$`H.l$`H$x.ffffH|$M4$Lc1J4LAH4$JyH|$H5&0zHtT$hHt$ LYH|$H50zHuH|$H50zzHuH|$H50dzHu\fffH|$H50GzHH|$H50-zHH|$H50zHEI,$1Ht$ LAUI1ATUHSHHL'H6A$L#1HE@HuHIE0A$L#1HEHHu HIE8A$L#HEP1IE@Hu0HA$E@HEXIEHu!EHuFEPuoEXH[]A\A]Hc]HHc,H}@IĉHMe0EHtHc]HHc,H}HIĉH^Me8EPtfffHc](HHcp,H}PIĉH/Me@EXlHc]8HHc@,H}XIĉHMeHH[]A\A]Ld$Ll$Lt$H\$H(HIII1D@HLP0HY/ M$HLLAH\$Ld$Ll$Lt$ H(fffffH\$Hl$Ll$Ld$Lt$L|$HHD!AHHHDqDy0AwH=v0DHc HL/H5d0AU0L+H0HHAUpDE MUHD$EDHH$+ EDx0ut$p,H\$Hl$ Ld$(Ll$0Lt$8L|$@HHH1HMcJHffDE MUfffH1HMcJH1HMcJH1HMcLHH1IcM,H؉L$돐HH\$Hl$Ld$Ll$Lt$tHtHt O;NtH\$Hl$Ld$Ll$Lt$DG D;F uDD;uDWD;Vu;JtD;B uD; uD;RuDADgH_nL^HzrsAAtRAdE~6LcMcHcD1D9}LcF,G,D9F,|MHLu1"E~LcMcHc1D9}(LcG4kBoADD D9fB k|MHLAuE~LcLcMcDLH1LH1بu{E1t7AfA3f3EJE9-CAHcB> f1f1Ё E9ȉ ~E9}McFOfGKfFKMLLr1D9}HcwfAsD9fs|ELcMcHc1D9}*LcGtAT=ADD D9AL|MHLAuH\$Hl$Ld$Ll$Lt$L|$HHIиt.Ht)Ht$tRtMtHtCҸtxfffH\$xH$L$L$L$L$HĨE11ۋO;NuDO D;N uDA9uo;nuA;HtLE;H uE;fffjA;h`GALoAALfIPEpD$\Ll$`D Ld$pDnHT$hEɉD9uD9l;D$\bFfuA@uGffu AAADEuHc|$\HL$`IcIcMcHt$0LD$(Hl$ Dd$LtH|$l$`HT$pLl$hH|$`HcH)I)H)IMDIID)D)AA +t$`tTHD$`DAAL1HDDA# IID9ՉHcT=D9~HAW9EE#IIDH9~D9}MHAEL1AAAHAEDA#B E9DLcA9D9~HT$H|$(HD$ H|$pHD$hHT$`IL$1H^LT$hD,A +t$`E)IM9uEMPtt)A~AXHD$`DMPAL1HDDDAD BA#ID9ʼnLcA;D9~HAAW91ADADIFDE#IADH9~D9Ht$hHt$0I9sAHADL1AAHDDDDADAB DA#E9D~LcA<D9~h[LT$pD,A +t$`E)IM9uEMQtt)A ~AYHD$`DMQAL1HDDDAD BA#ID9͉LcA;D9~HAAW91ADADIFLE#IADH9~D9zHT$pHT$0I9sAHADL1AAHDDDDDB A#E9DLcA8D9~9u+t$`E1E1ID$< +D$HH87HHEff'HHffHH6ffH[]AWAVAUATUSHxHID$LD$HHHD$DHAHD$@HgAHzAAuA<$Au"EA<$AD$ ǍAupGlmA<$AD$ AD$ Ǎ Ǎ ǍD$L D$Ld$L? T$HFAA<$AD$ AD$ AD$  lj lj lj NjD$@A9]D$@EA9uDd$DED$DCT4IAAD;|$DCD4HHD$0D)D$DvDnDf HD$(HD$$HHHHD$;D$u l$$D$$[HcHT$@4AD;L$$}19}L$AɍHH\$(f4C9|AD;L$$|ǃ~IDD t$4DD D$8DD T$FD$4VT$0v t$,IHlAH]H[HIID9u AAMHcA4AE9})9}DAˍHA49|AE9|ǃ~#AE9fD$4HD$HT$@HT$ t$,Ht$HD$8HD$HcL4DIcAZDAHI tcD9HH!T$@Ht$H t$@T$0HD$ PHH!t$8HD$H D$8HT$DzHD$@JDt$,t%D|$@t$4HD$ pD$0D$8D$,Ht$FLD$@H|$8D9pHcLH|9~AE9.HH[]A\A]A^A_ÐLd$H\$Hl$Ll$Lt$L|$HHIIAMĺtHt n;ot:H$H$L$L$L$L$HDn D;o uwuAJA;HtE@ E;uMۺM بuEɋ[(AD$$]E18BE1A(D)D)LcHcHcAAAHEEHA*<YD,E)EADH~~E1ADIcE1D)H<HLDIcD)AHcJ AN<H8H>B~AA~HcHH${DŽ$ D9$ L$uLcMcL$L$L$$ Hc$ DŽ$tH$Lc$tH$DŽ$L$L$NMMIL$L$xH$xH$xL$xA3H$xL$xKzH$xH$x$pEH D$ D9$$l$hDPAsKzD$dD$`$\$X$TEAAD$pADAAE!AE E^D$lDA!A A^$hA!D EF D$dDAA!кE END$`DA!A A~$\!׺ Av$X!ֺ AVI $TEE_A_EG EOAAw!¸ AW$I D$ D9$s9$}T$H$xAEIDDAE!A E$I9$|$t$tH$H$LHL$H$$ H$H$D9$ MEHA*Y,A)EAD H~~E1ADIcE1D)H<HLDIcD)AHcJ AJH8H>~AA~DIcHH$dDŽ$\D9$\wL$]IcIcH$L$H$$(Lc$\DŽ$L$PHc$H$PDŽ$@L$ L$HB~AA~HcHkHH$`D$LD9l$LH$uHcIcH$HL$8H$$HcD$LDŽ$\HD$@Lc$\LD$@D$ L\$8H$pL$`AO O?ILMO< D$D9\$ AAWAOA AwEWEGEO$X$T$P$L$HD$DD$@D$<bD H$0D$XD DE!AE DSD$TEA A EE!AE D[D$PD <A!A {$L DD!AD DC D$HD 4A!A s($D ! K0$@ S8fE$<fEVfE^fA~fEF H@! T$H$0 T$fAv(D$ fAN0$! fAV8I@9t$ 9l$ }@DL$ ;HH$0D$ AC< ! fA>I9l$ |$\$\H$hH$`LL$pL$hL$H$D$LD9l$LOPM4_L$pL$hH$`DŽ$DŽ$DŽ$DŽ$HcODMcJLGH$IrH$xIιA+L$ LIH$HL$*~fL0YȃA1AA*^LcD1IJ<BAHA*YD,E)EADH~ƃ~E1ADMc1D)J<HL HcDH9)N< HƋL8B B ~AA~HcHI9l$P|$$H$H$LL$L$L$H$D$|D9l$|OPM4_L$L$H$HDŽ$DŽ$DŽ$DŽ$MczA+L$ L$DLGMJL$MDIL$L$*~f0YȃѿAA1D*A^Hcٍ<1HH>EHE*,DYA,A)EADH~~E1ADIcE1D)H<HLDIcD)AHcJ AJH8H>~AA~HcHHH$vDŽ$D9$4H$DEIcMcH$H$L$D$Hc$DŽ$H$Lc$H$DŽ$H$H$L$N O?$ILMN< 9$AAAwEW EGE_EOAO$$$D$D$D$D$$jD H$D$D DE!AE DSD$EA A EE!AE D[D$D <A!A {$ DD!AD DC D$D 4A!A s($ ! K0$ S8fEF $fEfEVfE^fA~H@! T$H$ T$fAv($fAN0D$! fAV8I@D9$9$}ID$;HH$$AC< ! fA>I9$|$$tH$H$L$ L$L$$L$L$D9$K_H$H$PDŽ$DŽ$DŽ$DŽ$MczA+L$ L$DLGMJL$MDIL$HL$@*~f0YȃѿAA1D*A^Lcэ<1IJ>EHE*I9$|$4$4tH$@H$8L L$HL$$L$L$@D9$K_H$HH$8}A+L$ DLGMJErDL$hL$`*~f0YȃѿAA1*^Lcэ<1IJ>EHA*,Y,A)EAD H~~E1ADIc1D)H<HL HcDH;)N HƋH 8 B ~AA~TmHcHH$XDŽ$|D9$|tIcL$DuIcH$L$H$D$,Lc$|DŽ$LL$pHc$LH$pDŽ$`L$hL$XHEHE* DYE,E)EAD:H~~E1ADMc1D)J<HL HcDH9)N< HƋL8B B ~AA~tmHcHH$=DŽ$<D9$<HcH$]IcH$H$(H$$$Lc$<DŽ$L$0Hc$H$0DŽ$L$L$LIIIL$(L$L$E AKH$H$ASEB D$L$$H$Xw$D$D$$D9$DYEQ$$D$D$EAAD$ADAAE!AE E^D$DA!A A^$A!D EF D$DAA!кE EN D$DA!A A~$!׺ Av$!ֺ AVI$EE_A_EG EO AAw!¸ AW$I$$9$u9$}T$H$AEIDDAE!A E$I9$|$$ H$H$LL$H$$<H$H$D9$<MCd19fD -B0fDdB00fH\fA.CfLB9f$f$LcB*F*Tf$fD$F*\f$fD$f$AYYAYEYDY$YXEYDY$YXXd$xAXAXAXAX\Xl$pXfA.r.fAIP\fA.fD.rffD.fr fAD,AfAfD*D,AfAfE fD.r@ff.fffrVfRD,AfE(f.r"fD,AfAfD2cD,AfD2,f$1dffffffffH\$Hl$Ld$Ll$Lt$L|$H8HHӸtHt o;ntCAD\DZGHQA\f.HA\f.AHJE9Icf$JfAJDfA JDAYfAYAYAYXf$YXf$YA\XYf.AYXAYXXfD.r) fD.fr).fD.ːr*,, , M7I >ALHpAY\,ASAY\D,EEDYD\E,EDYD\A,AAY\D,EoDDYD\E,5DDYD\A,AY\D,ED*DYD\E,H$Hn$of5[60fD EfD[fkDfKfS AfDC(f{0fC8Yc@DYf60HcDYC4)F,7H<[YAYHD$YD$d$0Y\$ t$DY|$`Y$L$xT$pDD$hD$X}Hft$0Hf|$ D\$qH$DTmE1LLE9I}D9McAHE9F<|ET$E1E9fD 50DE50McDlmMcD$HD9l$HEDTmEZtfAIP\fA.%fH\fA.fD$HLBD$HDD9T$HHFTFTTFT$HcL$Hf$f$f$f\$pfl$xfD$`fD|$hD*4D*lAYAYD*dAYAYDYl$XAYXAYEYXDYXAXAX\AXfA.fD.r2fAfD.fffr0ffD.r7f,ffAD,AfAfDD,AfAfDAL$N{AJ4vA9L(Dm19fD 30fD:30MfAIP\fA.fH\fA.fLB9f$fD$HcD*$D*\f$D*TfDt$pfD$`fDl$hEYAYAYEYDY\$XAYAXEYDYXfl$xAXAY\AXAXfA.AX fD.fAH$H$f510fD+EfDcfD[AfDSfDK HcfDC(f{0C4)fC8Yc@DYH<@EDYAHG,0t$DYDYD$ADYD$d$0DYYD$YD$D$D$$$xHft$0HD\$H$DTmE1LLE9I}D9McAHE9F<|ET$E1E9fDv00D 00McDlmMcD$LD9l$LEDTmAZdfAfA.IPfHfA.fD$LLBD$LDD9T$LHFTFTTFTHcL$Lf$fD$f$f$f$*D*|YEYD*tAYAYDY$AYAXDYXf$YY$fA.XAXXAXf.r/fAf.fffr*ff.r,f,fAD,AfDD,AfDAL$N{AJ4vA9LJDm19fD.0f=.0>fAfA.IPfHfA.fLB9f$fD$Hc*D*TfD$f$fD$D*LfD$YEYEYYY$EYDY$AXEYAXDYAXAXAXfA.AXf.rfA fffku?D,AfE f.rff.rfD,AfDD,AfD*fD.r3f_D,AfAfE fD.r"fD,AfAfDD,AfAfD*HALD$L$A$FHÐH\$Hl$Ld$Ll$Lt$L|$HhHIиtHt O;NtF4?N4?IAOE9ANAOF<)E~|LHAunEfcLcLcJNE1D9}!LcEF F BDD9BD|LHAuHJHlHT$;LH\$Hl$Ld$Ll$Lt$L|$HHIиt$HttRtMtHttttH$H$L$L$L$L$Hf1E11E1E1O;NuDW D;V t1AD뗐L(댋O;N'ffqDAA9uDD;~uHFAHWAADT$|vH$H$D بAAL-A A AhEp EEHAD E 9AA9D 9$A9D!بtDD$|D$AL$|Lc$HcH$H$D$LcDL$|Ll$pHT$hIt:M|ADD$xDL$4L|$`Ht$XHt$PD$L$AIcD$L$I)MLII)D)ȅED+$EEEA EAH$H3D$`BAD\$`A)ՉADAуDE A$ H$DDAEIA!AD9HcFAD9~EAI$A9}$A#9DAA:A#qIArIA9|D;$}ID;\$xAAI!AIDD;$}!DD$`A1A!LcCD9~L\$pHD$hL$L\$PL\$XH$HD$`L$4HE1DO D;N ?o;nLfvLo9A9AE EA  EAXEPAP AADAE9'EDD}HcHcEE1MLE95D#AA#AA@D#AA@#A HA@ IE9~A9},D#AAA9}#AA@AA9} ED#IEHIIAuH$D AED+$E)݉DHI9EDDD<H$H3D$`D)DDA Dщ<$|$`AD <$AHDDL;L$PsEaDDD狌$!ꋌ$D9HcBD9~4$DAIID$AE9}EEaDADDDٍDD!AAYI!ABIE9|D;$6D;D$x,EaDADIEAA D!A ID;$DD$`AL;L$XsEaDىEDA1D!LcCTD9~AjAA0A6A7Ah EXEHAAD AD AADT$|EtLc$HcL$H$Lcϋ|$|HD$ LL$KT8IL0|$HL$HHT$@HT$8$L$L$T$0LcT$0M)M)MLID)ED+$EEED޻ EEH$H3D$H)ӉAAD DT$, |$,DEً\$HAL$0T$0D AAHDDAIA!AD9HcF AD9~T$,AAAI$ A9/A#8DA A9A#pDAqA#xI AyI A9~D;$}n$A94AAI!AIA9DAAIDA!EID;$}!DT$HA1A!LcCD9~L\$ LL$L$L\$8L\$@L$LL$HL$.H$D AED+$E)߉Dt$HDl$I98EDЉ؍4)щ|$D |$DD DщD$ H$H3D$HL$H t$ HDL;D$8sE`DDDL$0#T$L$09HcB 9~T$L$ DAIIT$L$$ A9eE`DA DD !A A@DADDAD#d$DEaE` I D#D$DAAI A9~D;$5$A9]E`DADID4D!|$A1IA9,E`DADID D!|$A ID;$DT$HAL;D$@sE`DىEDA1D!LcCD9~zAhAKAhE0EhD AAE ADAA ,A A@AEHEhAAAE A EDEAEA DAAE D AAE9E1LALDOE98ED#!AE։AD&D#yD~D#qDv#i Hn HE9~A9}$D#AP9D}#Y^AX9}D#QDV1EDDEAAhAAPDIAE9tWE~HcHcE˅MM~/AAE#8AE9E#pAEqE#xI EyI AuIIAuvAAE9uDLLA9UAD#)AAD.D#ADFD#aH DfH u#EAXEӉHA,AH\$Hl$Ld$Ll$Lt$L|$HHIиt$HttRtMtHttttH$H$L$L$L$L$Hf1E11E1E1O;NuDW D;V t1AD뗐L댋O;N'ffqDAA9uDD;~uHFAHWAADT$|vH$H$D بAAL-A A AhEp EEHAD E 9AA9D 9$A9D!بtDD$|D$AL$|Lc$HcH$H$D$LcDL$|Ll$pHT$hIt:M|ADD$xDL$4L|$`Ht$XHt$PD$L$AIcD$L$I)MLII)D)ȅED+$EEEA EAH$H3D$`BAD\$`A)ՉADAуDE A$ H$DDAEIA AD9HcFAD9~EAI$A9}$A 9DAA:A qIArIA9|D;$}ID;\$xAAI AIDD;$}!DD$`A1A LcCD9~L\$pHD$hL$L\$PL\$XH$HD$`L$4HE1DO D;N ?o;nLfvLo9A9AE EA  EAXEPAP AADAE9'EDD}HcHcEE1MLE95D AA AA@D AA@ A HA@ IE9~A9},D AAA9} AA@AA9} ED IEHIIAuH$D AED+$E)݉DHI9EDDD<H$H3D$`D)DDA Dщ<$|$`AD <$AHDDL;L$PsEaDDD狌$ ꋌ$D9HcBD9~4$DAIID$AE9}EEaDADDDٍDD AAYI ABIE9|D;$6D;D$x,EaDADIEAA D A ID;$DD$`AL;L$XsEaDىEDA1D LcCTD9~AjAA0A6A7Ah EXEHAAD AD AADT$|EtLc$HcL$H$Lcϋ|$|HD$ LL$KT8IL0|$HL$HHT$@HT$8$L$L$T$0LcT$0M)M)MLID)ED+$EEED޻ EEH$H3D$H)ӉAAD DT$, |$,DEً\$HAL$0T$0D AAHDDAIA AD9HcF AD9~T$,AAAI$ A9/A 8DA A9A pDAqA xI AyI A9~D;$}n$A94AAI AIA9DAAIDA EID;$}!DT$HA1A LcCD9~L\$ LL$L$L\$8L\$@L$LL$HL$.H$D AED+$E)߉Dt$HDl$I98EDЉ؍4)щ|$D |$DD DщD$ H$H3D$HL$H t$ HDL;D$8sE`DDDL$0 T$L$09HcB 9~T$L$ DAIIT$L$$ A9eE`DA DD  A A@DADDAD d$DEaE` I D D$DAAI A9~D;$5$A9]E`DADID4D |$A1IA9,E`DADID D |$A ID;$DT$HAL;D$@sE`DىEDA1D LcCD9~zAhAKAhE0EhD AAE ADAA ,A A@AEHEhAAAE A EDEAEA DAAE D AAE9E1LALDOE98ED !AE։AD&D yD~D qDv i Hn HE9~A9}$D AP9D} Y^AX9}D QDV1EDDEAAhAAPDIAE9tWE~HcHcE˅MM~/AAE 8AE9E pAEqE xI EyI AuIIAuvAAE9uDLLA9UAD )AAD.D ADFD aH DfH u#EAXEӉHA,AH\$Hl$Ld$Ll$Lt$L|$HhHIиtHt O;NtIcEHDAD+<D<L<H+AAGD+AAG+Q E9AW |HLAu1LL$0D$4D$0HcƒLL0~@E1Hcƒ_LL0~@fffffHc‰L0~1D9LL$0MLLD9fHcA=_AA)DHN$Ldɋ^DoAAA(EHHvA9D9!Ȩt EAA%AEH[AEpEEP LcMcK?O,E1D9}DLcADfF+F4?N4?IA+OANDA+OE9AN|LHAuZEQLcLcJNE1D9}!LcEF+ F B+DD9BD|LHAuHT$&L\vH\$Hl$Ld$Ll$Lt$L|$HHIиt$HttRtMtHttttH$H$L$L$L$L$Hf1E11E1E1O;NuDW D;V t1AD뗐Lx댋O;N'ffqDAA9uDD;~uHFAHWAADT$|vH$H$D بAAL-A A AhEp EEHAD E 9AA9D 9$A9D!بtDD$|D$AL$|Lc$HcH$H$D$LcDL$|Ll$pHT$hIt:M|ADD$xDL$4L|$`Ht$XHt$PD$L$AIcD$L$I)MLII)D)ȅED+$EEEA EAH$H3D$`BAD\$`A)ՉADAуDE A$ H$DDAEIA1AD9HcFAD9~EAI$A9}$A39DAA:A3qIArIA9|D;$}ID;\$xAAI1AIDD;$}!DD$`A1A1LcCD9~L\$pHD$hL$L\$PL\$XH$HD$`L$4HE1DO D;N ?o;nLfvLo9A9AE EA  EAXEPAP AADAE9'EDD}HcHcEE1MLE95D3AA3AA@D3AA@3A HA@ IE9~A9},D3AAA9}3AA@AA9} ED3IEHIIAuH$D AED+$E)݉DHI9EDDD<H$H3D$`D)DDA Dщ<$|$`AD <$AHDDL;L$PsEaDDD狌$1ꋌ$D9HcBD9~4$DAIID$AE9}EEaDADDDٍDD1AAYI1ABIE9|D;$6D;D$x,EaDADIEAA D1A ID;$DD$`AL;L$XsEaDىEDA1D1LcCTD9~AjAA0A6A7Ah EXEHAAD AD AADT$|EtLc$HcL$H$Lcϋ|$|HD$ LL$KT8IL0|$HL$HHT$@HT$8$L$L$T$0LcT$0M)M)MLID)ED+$EEED޻ EEH$H3D$H)ӉAAD DT$, |$,DEً\$HAL$0T$0D AAHDDAIA1AD9HcF AD9~T$,AAAI$ A9/A38DA A9A3pDAqA3xI AyI A9~D;$}n$A94AAI1AIA9DAAIDA1EID;$}!DT$HA1A1LcCD9~L\$ LL$L$L\$8L\$@L$LL$HL$.H$D AED+$E)߉Dt$HDl$I98EDЉ؍4)щ|$D |$DD DщD$ H$H3D$HL$H t$ HDL;D$8sE`DDDL$03T$L$09HcB 9~T$L$ DAIIT$L$$ A9eE`DA DD 1A A@DADDAD3d$DEaE` I D3D$DAAI A9~D;$5$A9]E`DADID4D1|$A1IA9,E`DADID D1|$A ID;$DT$HAL;D$@sE`DىEDA1D1LcCD9~zAhAKAhE0EhD AAE ADAA ,A A@AEHEhAAAE A EDEAEA DAAE D AAE9E1LALDOE98ED3!AE։AD&D3yD~D3qDv3i Hn HE9~A9}$D3AP9D}3Y^AX9}D3QDV1EDDEAAhAAPDIAE9tWE~HcHcE˅MM~/AAE38AE9E3pAEqE3xI EyI AuIIAuvAAE9uDLLA9UAD3)AAD.D3ADFD3aH DfH u#EAXEӉHA,AH\$Hl$Ld$Ll$Lt$L|$AA~-D)A9=D)D"" шH\$Hl$Ld$Ll$Lt$L|$øDGlE1"E@".AH @.HE9}*@t$fffDAHDHE9} @ufffHH1uEJ1E9LcAN$E9N$~HHHE9}DD)DHDHuD#A)AED"/AD"E Du-EJ1E9LcAFHD9fTEfLE|AEDl$(Dl$,EE9|LcnLcGDH^HoMMEEIIE9@E9!t Eù6E1E1AIcMcHt1JHD9ƉTL|AEDl$8Dl$uEt fD)M*1AXLdFfWAAAAEBf=/McDE1E1E1E11E9GD*D*AXAX߉%AG AH%AAyE*A*DYYE*E*E1E1E1E1D9EXDXAXAXpEtHD_D*/D*gAXAX܉؁Aً_ AH؁AAuE*E*DYDYE*E*LHEXEXAXAXYYYYA$A\$Ad$Al$M;11E1E19AMcAAE1A9}zf5Af5AAAHAyэAE*E*E1D*E111A9D*AXAXAXAX|t7f5DADf5DDDQEDQHEuD)D)E*D*D*E*ME1E111AAXLAXAXAXYYYYA$AL$A\$Ad$fWۉӉEf%/McEE1E1E1A9}QDw*?HXEAAEEyA*YA*AE1E1A9XX|t+DWD*HAXDAEAuE*DYE*LAHEXAXJYYA$A\$At$At$At$A4$AkLcD$Lt$D$D$AD$D$D$AD|$ELýAB IcH)LKAD#DEAEDA?AB4 :AyAADEADA%AAA DI%AD7<yADADމDL*1AE1L*M*L*AXAXAXAX4Et#DAI%AuEtA# AAEADDL*AXڃM*AXDAE)YAAXYL*AXD)AI*D)Ll$XL$YACXYCAX MA AX$A$JAt$A4$5LcD$D$L|$D$D$D$D$A|$ELDACTZB AA ADt$McT$L)#LOAA?AB4 :AyAADEADA%AAA DI%D7<yAADÉE1H*1H*XXOl$Ѕt$L$A1I%AuL$̅t+L$DA#AAEAADMAL*AXЉL*AXAALl$L$McYCXYCCX MC At$At$A4$LcG4[D$D$D$D$D$D$LT$|$ELAAE11DL$Lc|$L$E11DL$L)AD#ALR&D$?ADARAE 8Az AAA%B4EZ EEB։DAAA EAAAC< E0EBIL$FDAAADD߁L*L$A11E1L*M*сAX9AXAXDL$AD9cA DArA DǍˁFAAADFEBI D9~D$) @DA AjADHDǍAȁAAC4ED\5~3B AADA#JAʁAADӉDJl<AAˋ\$EAM*AXAM*AXDً|$Ll$YL*AXAXY̓MYAL\CX C L|IcEff ~$EDMcEBZYXBuAE9|LLu1f fff E~McMcDE1E9}@McEfBfB ~$EDMcEfBYXBuAE9|LLuW1D9}AYHYL9X s(AYPLQM9XQsAYXAXYHt$H|$L|$Lt$L$,DE;McMcDE1E9}GMcEfBfB ~+EDMcEfFf1)f1ҁ) E9ȉ ~E9}McGKfF+OfFKMLLq1D9}HcAsf+wD9fs|ELcMcHc1D9})LcGtAT=A)DD!D9AL|MHLAuHHAtHt ;AtDHDOD9NufWf.v*fD~/f.rf.vf.rt/fwtDHcIcH2BBB D H*Y,fAID9|ÐDWHOLG0LGfO(1D9}PDHcIcH2D *Y,fABADIH*Y,fAPID9|fffffffffffDWHOLG0LGfO(1D9}VDHcIcH2BD *Y,fAB BADIH*Y,fAPID9|ffffffDWHOLG0LGfO(1D9}\DHcIcH2BBD *Y,fAB BBADIH*Y,fAPID9|ffDWHOLG0LGfO(1D9}bDHcIcH2BBBD *Y,fAB BBBADIH*Y,fAPID9|fffffffffDWE1HOLG0LGfO(E9}hIcAIcH29*Y,fABAy*Y,fA@BAyH *Y,fAPIE9|ffffDWE1HOLG0LGfO(E9}qIcAIcH2B 9*Y,fABBAy*Y,fA@BBAyH *Y,fAPIE9|fffffffffDWE1HOLG0LGfO(E9}zIcAIcH2BB 9*Y,fABBBAy*Y,fA@BBB AyH *Y,fAPIE9|fffDWE1HOLG0LGfO(E9IcAIcH2BB B$9*Y,fABBBB(Ay*Y,fA@BBB B,AyH *Y,fAPIE9|fffffDWE1HOLG0LGfO(E9IcAIcH29*Y,fABAy*Y,fA@BAy*Y,fA@B A y H*Y,fAPIE9|ffffffDWE1HOLG0LGfO(E9IcAIcH2B9*Y,fABBAy*Y,fA@BBAy*Y,fA@BB A y H*Y,fAPIE9qffffffDWE1HOLG0LGfO(E9IcAIcH2B B9*Y,fABBB$Ay*Y,fA@BBB(Ay*Y,fA@BB B,A y H*Y,fAPIE9efffffffffDWE1HOLG0LGfO(E9IcAIcH2B BB09*Y,fABBB$B4Ay*Y,fA@BBB(B8Ay*Y,fA@BB B,B<A y H*Y,fAPIE9YSDWHWLG0H_DO 1D9}*DHcIc 1DDHfHD9|[fffUSD_HoHG0LGDW 1D9}1DHcHc D11DEDMHfAID9|[]fffffffffUSD_HHoH7G0LGDW 1D9}6DHcHcH AAEDDMHfAID9|[]fffUSDWHHoH7G0LGD_ 1D9}9DHcHcH AAA DEDMHfAID9|[]ÐUSD_HHoH7G0LGDW 1D9}ADHcHc HDыEDMfABEDMHfA@ID9|[]ffffffUSD_HHoH7G0LGDW 1D9}GDHcHc HDыBEDMfAB BEDMHfA@ID9|[]ffUSD_HHoH7G0LGDW 1D9}MDHcHc HDыBBEDMfAB BBEDMHfA@ID9|[]fffffffffUSD_HHoH7G0LGDW 1D9}SDHcHc HDыBBBEDMfAB BBBEDMHfA@ID9|[]fffffUE1SD_HHoH7G0LGE9DO }OIcAHc HDɋE}fABE}fA@BE}H fA@IE9|[]fffffffUE1SD_HHoH7G0LGE9DO }XIcAHc HDɋB E}fABBE}fA@BBE}H fA@IE9|[]ÐUE1SD_HHoH7G0LGE9DO }aIcAHc HDɋBB E}fABBBE}fA@BBB E}H fA@IE9|[]ffffffUE1SD_HHoH7G0LGE9DO }jIcAHc HDɋBB B$E}fABBBB(E}fA@BBB B,E}H fA@IE9|[]fffffffffffUE1SD_HHoH7G0LGE9DO }_IcAHc HDɋE}fABE}fA@BE}fA@B E } HfA@IE9|[]fffffffUE1SD_HHoH7G0LGE9DO }kIcAHc HDɋBE}fABBE}fA@BBE}fA@BB E } HfA@IE9|[]ffffffffffUE1SD_HHoH7G0LGE9DO }wIcAHc HDɋB BE}fABBB$E}fA@BBB(E}fA@BB B,E } HfA@IE9|[]fUE1SD_HHoH7G0LGE9DO fffIcAHc HDɋB BB0E}fABBB$B4E}fA@BBB(B8E}fA@BB B,B<E } HfA@IE9|[]fffffffffAWAH D@E1AVfEWAUATUSHH HWFH$nH$DODW H$H\$xOH$$$~ VLFfd/D*DYD*DYE*LcHcHIDDl$PL$p^A^XAX^^$DY$f=:d/DX]DXDXE,Dd$tD)D$tA,A,D)D9ND9D$DNDT$hHM,@1fD$f$D,E*EKfA.*YS EFA$ E,A*EBf.ʼn$ $,EFD;$DO$A9EN$$DL$li$~$XHLcJ` =T$lH$ANJfHD$@LH$AP $HM$;\$l}SD|$lA)AIc~BADHD$@LH$AAIcD苄$AHM$;\$l|H$HL$8LH$H$ &H$H$IcHT$8H$ Lf$X$D,D+d$tE9} Hc\$pHI)H$L$$$t$h9$J,OH$}!D,D\$tcEAC2Lt$xL9$t H$41HH []A\A]A^A_E1A9}McABNA9B|fE1HcAA9L F}McAB^CYE9B|ÐE1HcAA9LFL } IcAAKNAIE9Љ|ffffffHcSHH E1LL1HA9L N}'IcAAKNAJAIA9|[ffE1HcAA9L F}McAB^CYBE9B|fffffffffE1HcAA9LFL }#IcANAKAIE9Љ|fffHcSH E1LL1HA9L N}*IcANAKAJAIA9؉|[ffHWL1ɋ9}LcOcE 2D H9|ffffDGHOL1D9}HcMcET3E3DHD9|ffffffffffDGHOL1D9}'HcMcI2DPDPDDDHD9|fffDGHOL1D9}(HcMcI2DPDPDP DDHD9|fffDGHOL1D9}#LcKcL2EDARQHD9|ffffffDGHOL1D9},HcMcM2EESDES ESDQHD9|DGHOL1D9}7HcMcM2ESESDEDES ESESDQHD9|fffDGHOL1D9}DHcIcH2BBLD HD9M@H*Y,f|ÐDWHOLG0LGfO(1D9}ADHcIcH2BBB LM@D HD9H*Y,f|ffffffffffSD_HLGH7G0LOfO(1D9}VDHcHc HIIAEH*Y,fABA@IEPID9H*Y,f|[fffSD_HLGH7G0LOfO(1D9}\DHcHc HIIBAEH*Y,fAB BIA@EPID9H*Y,f|[fffffffffffSD_HLGH7G0LOfO(1D9}bDHcHc HIIBBAEH*Y,fAB BIBA@EPID9H*Y,f|[ffffffSD_HLGH7G0LOfO(1D9}hDHcHc HIIBBBAEH*Y,fAB BIBBA@EPID9H*Y,f|[ffSD_E1HLGH7G0LOE9fO(}sIcAHc H AA8BA@AxH*YH*Y,fA II,fAABA@IAxI E9H*Y,f|[fffffSD_E1HLGH7G0LOE9fO(}|IcAHc H J AA8BBA@AxH*YH*Y,fA II,fAABBIA@AxI E9H*Y,f|[ffffffffffSD_E1HLGH7G0LOE9fO(fffIcAHc HJJ A A8BBBA@AxH*YH*Y,fA II,fAABBIB A@AxI E9H*Y,fz[ffffffSD_E1HLGH7G0LOE9fO(fffIcAHc HJJ J$ AA8BBBB(A@AxH*YH*Y,fA II,fAABBIB B,A@AxI E9H*Y,fq[fffffffffffSD_E1HLGH7G0LOE9fO(fffIcAHcH AA8H*Y,fAQAPAxAA@AxH*YH*Y,fAQIQ,fAAA A@ IAx IE9H*Y,f o[fffffffffSD_E1HLGH7G0LOE9fO(fffIcAHc H JAA8H*Y,fA JJAHAxBBA@AxH*YH*Y,fAIII,fAABB IA@ Ax IE9H*Y,fc[ÐSD_E1HLGH7G0LOE9fO(fffIcAHc HJ JA A8H*Y,fA JJJ$AHAxBBB(A@AxH*YH*Y,fAIII,fAABB IB,A@ Ax IE9H*Y,fW[fffSD_E1HLGH7G0LOE9fO(fffIcAHc HJ JJ0 AA8H*Y,fA JJJ$J4AHAxBBB(B8A@AxH*YH*Y,fAIII,fAABB IB,BfHD$@LH$AP $HM$;\$l}SD|$lA)AIc~BADHD$@LH$AAIcD苄$AHM$;\$l|H$HL$8LH$H$ &H$H$IcHT$8H$ Lf$X$D,D+d$tE9} Hc\$pHI)H$L$$$t$h9$J,OH$}!D,D\$tcEAC2Lt$xL9$t H$41HH []A\A]A^A_E1A9}McAA1A9B|fE1HcAA9L 0}McAA3C E9B|ÐAE1HcE9L1HcL 0}%fffIcAB1B E9Љ|ffffffffffAHcSHH<1LcM0E1HE9L 0}&IcA91BB E9؉|[E1HcAA9L 0}McAA3C BE9B|fffffffffAE1HcE9L1HcL 0}(fffIcA1BB E9Љ|ffffffffAHcSH1LcM0E1HE9L 0})IcA1BB E9؉|[HWL1ɋ9}LcOcE 2D H9|ffffDGHOL1D9}HcMcET3E3DHD9|ffffffffffDGHOL1D9}'HcMcI2DPDPDDDHD9|fffDGHOL1D9}(HcMcI2DPDPDP DDHD9|fffDGHOL1D9}#LcKcL2EDARQHD9|ffffffDGHOL1D9},HcMcM2EESDES ESDQHD9|DGHOL1D9}7HcMcM2ESESDEDES ESESDQHD9|fffDGHOL1D9}A7~Lct$Lc|$kEfffE1A9fffMcDCh)щȉ!! fC jCThD)щȉ!! fCLjCThD)щȉ!! fCLjCThD)щȉ!! fCLjCThD)щȉ!! fCLjCTh D)щȉ!! fCLj CTh D)щȉ!!A fCLj CThD)щȉ!! A9fCLjA9})McDACh)щȉ!! A9fC j|AOpOz[]A\A]A^A_ffffffAWIIAVAUEAATUHSHLD$8Lt$@L|$HT$L$DL$D$D9L$D$D$1D9}rDL$Dd$܍6E1B< B "LcHcBDUE1A;~-CAfSA-A;E HIcMcDEC;CAfSA~D9|D$t$D$t$D$܋L$9L$e[]A\A]A^A_E A.EEHEFDt$AAE~Lc|$HcL$EuL|$D|$HL$1D9HcDAK)‰Љ!! fAJADKD)‰D!! fATJADKD)‰Љ!! fATJADKD)‰D!! fATJADKD)‰Љ!! fATJADK D)‰D!! fATJ ADK D)‰Љ!!؃ fATJ ADKD)‰D!! D9fATJD9}JHcDAK)‰Љ!! fAJADKD)‰D!! D9fATJ|HT$HD$AMSMBz[]A\A]A^A_ffffffAWAIIAVAUATEUHSHT$L$DL$E1E9D$D$E1E9}^Dt$Dl$1B47B /E1fffE A3McIcLL$8HDEC;GLL$HCAAfS~AE9|ANjT$D$T$D$D;|$|[]A\A]A^A_LL$@Hl$8LL$@G,dHL$HH\$8Dl$H|$@Ht$HD}E1D)DeAiDIL$DC_~~HcL$Hct$T$D$ HL$Ht$؉T$D$1;t$HcDAK)‰D!D! fAJADKD)‰Љ!D! fATJADKD)‰Љ!! fATJADKD)‰D!D! fATJADKD)‰Љ!D! fATJADK D)‰Љ!! fATJ ADK D)‰D!D! fATJ ADKD)‰Љ!D! fATJADKD)‰Љ!! fATJADKD)‰D!D! fATJADKD)‰Љ!D! fATJADKD)‰Љ!! ;t$fATJf;t$}mHcDAK)‰D!D! fAJADKD)‰Љ!D! fATJADKD)‰Љ!! ;t$fATJ|L$HT$HD$MSMB[]A\A]A^A_fffffffffAWAIAVAUATUDSHLt$@HL|$HIDL$T$L$LL$8D$T$9T$D$D$E1A9}WDl$Dd$BE1B4*B "ffffA3E HIcMcCC;FCAfWA~AA9|D$ԋt$L$t$L$ċD$9D$|[]A\A]A^A_ffCEFEaAE/AAYDd$DD$Ef EFD4l$ADl$܉|$؉\$D$EoAY A T$~LcL$Lc|$AnLL$DL$19&Hc΋T$AK)‰ЋT$!#D$ fAJADKT$)‰ЋT$!#D$ fATJADKT$)‰D!D! fATJADK)‰D!! fATJADKT$)‰ЋT$!#D$ fATJADK T$)‰ЋT$!#D$ fATJ ADK T$)‰D!D! fATJ ADK)‰D!! 9fATJD9Hc΋t$AK)։t$!#D$ fA4JATKt$)։t$!#D$ fAtJATKt$)։D!D! fAtJATK)։D!! fAtJHL$AO{MJ#[]A\A]A^A_fffffffffAWAӺAVEAUATUSLd$@LD$HHHt$8E,$AAD1EADNDND9McHcDL$L|$HD$l$PE1E1Ʌt|+T$PDD9N1E1A9}DlwTwEE)AA)ADAA@A TwA)DE A TwA)DEA TwA)DAA TwA)DAA TwA)DEA Twt$A)DA D!AD#t$D AALcE9C4>ODT$AE9McEމB4WFlWD$A)BtWD)FlWAA@)A BtWDD)FlW A ΉD)BtW A ΉщD)FlWA Ήщ)IcA ΉDD)A ΉA D!AD#t$D AAB>D;D$+IcEEpOA)DD;t$DlOEpD)AAA@D D;t$TOAEpA)EAA D D;t$}wDTOE)EEHAAD D;L$}UTOA)EEXAAD D;\$}4DtO D)AAXAAD ;\$}DTO D)Չ Dl$AIcE)DD$D)AA!#t$A E!AF"\=E FD=L|$L$Ld$J+L$PD$;L$OL$1ۉL$A9ΉL$Afff|$PADA)D t$HcAD)11!D1ʍO)ALD! D)11!ʍO1DD! ;l$~;l$}QHcDL$PEAA7~Lct$Lc|$kEfffE1A9fffMcDCh)щȉ!! fC jCThD)щȉ!! fCLjCThD)щȉ!! fCLjCThD)щȉ!! fCLjCThD)щȉ!! fCLjCTh D)щȉ!! fCLj CTh D)щȉ!!A fCLj CThD)щȉ!! A9fCLjA9})McDACh)щȉ!! A9fC j|AOpOz[]A\A]A^A_ffffffAWIIAVAUEAATUHSHLD$8Lt$@L|$HT$L$DL$D$D9L$D$D$1D9}rDL$Dd$܍6E1B< B "LcHcBDUE1A;~-CAfSA-A;E HIcMcDEC;CAfSA~D9|D$t$D$t$D$܋L$9L$e[]A\A]A^A_E A.EEHEFDt$AAE~Lc|$HcL$EuL|$D|$HL$1D9HcDAK)‰Љ!! fAJADKD)‰D!! fATJADKD)‰Љ!! fATJADKD)‰D!! fATJADKD)‰Љ!! fATJADK D)‰D!! fATJ ADK D)‰Љ!!؃ fATJ ADKD)‰D!! D9fATJD9}JHcDAK)‰Љ!! fAJADKD)‰D!! D9fATJ|HT$HD$AMSMBz[]A\A]A^A_ffffffAWAIIAVAUATEUHSHT$L$DL$E1E9D$D$E1E9}^Dt$Dl$1B47B /E1fffE A3McIcLL$8HDEC;GLL$HCAAfS~AE9|ANjT$D$T$D$D;|$|[]A\A]A^A_LL$@Hl$8LL$@G,dHL$HH\$8Dl$H|$@Ht$HD}E1D)DeAiDIL$DC_~~HcL$Hct$T$D$ HL$Ht$؉T$D$1;t$HcDAK)‰D!D! fAJADKD)‰Љ!D! fATJADKD)‰Љ!! fATJADKD)‰D!D! fATJADKD)‰Љ!D! fATJADK D)‰Љ!! fATJ ADK D)‰D!D! fATJ ADKD)‰Љ!D! fATJADKD)‰Љ!! fATJADKD)‰D!D! fATJADKD)‰Љ!D! fATJADKD)‰Љ!! ;t$fATJf;t$}mHcDAK)‰D!D! fAJADKD)‰Љ!D! fATJADKD)‰Љ!! ;t$fATJ|L$HT$HD$MSMB[]A\A]A^A_fffffffffAWAIAVAUATUDSHLt$@HL|$HIDL$T$L$LL$8D$T$9T$D$D$E1A9}WDl$Dd$BE1B4*B "ffffA3E HIcMcCC;FCAfWA~AA9|D$ԋt$L$t$L$ċD$9D$|[]A\A]A^A_ffCEFEaAE/AAYDd$DD$Ef EFD4l$ADl$܉|$؉\$D$EoAY A T$~LcL$Lc|$AnLL$DL$19&Hc΋T$AK)‰ЋT$!#D$ fAJADKT$)‰ЋT$!#D$ fATJADKT$)‰D!D! fATJADK)‰D!! fATJADKT$)‰ЋT$!#D$ fATJADK T$)‰ЋT$!#D$ fATJ ADK T$)‰D!D! fATJ ADK)‰D!! 9fATJD9Hc΋t$AK)։t$!#D$ fA4JATKt$)։t$!#D$ fAtJATKt$)։D!D! fAtJATK)։D!! fAtJHL$AO{MJ#[]A\A]A^A_fffffffffAWAӺAVEAUATUSLd$@LD$HHHt$8E,$AAD1EADNDND9McHcDL$L|$HD$l$PE1E1Ʌt|+T$PDD9N1E1A9}DlwTwEE)AA)ADAA@A TwA)DE A TwA)DEA TwA)DAA TwA)DAA TwA)DEA Twt$A)DA D!AD#t$D AALcE9C4>ODT$AE9McEމB4WFlWD$A)BtWD)FlWAA@)A BtWDD)FlW A ΉD)BtW A ΉщD)FlWA Ήщ)IcA ΉDD)A ΉA D!AD#t$D AAB>D;D$+IcEEpOA)DD;t$DlOEpD)AAA@D D;t$TOAEpA)EAA D D;t$}wDTOE)EEHAAD D;L$}UTOA)EEXAAD D;\$}4DtO D)AAXAAD ;\$}DTO D)Չ Dl$AIcE)DD$D)AA!#t$A E!AF"\=E FD=L|$L$Ld$JA7~Lct$Lc|$kEE1A9 McDCT)щȉ!! CLCTD)щȉ!! CLCTD)щȉ!! CLCTD)щȉ!! CLCTD)щȉ!! CLCTD)щȉ!! CLCTD)щȉ!!A CLCTD)щȉ!! A9CLA9}-ffMcDACT)щȉ!! A9CL|MMA[]A\A]A^A_fffffffffAWIIAVAUEAATUHSHLD$8Lt$@L|$HT$L$DL$D$D9L$D$D$1D9}nDL$Dd$܍6E1B< B "LcHcA*E1A;~+CAÈA+A;E HIcMc(C;CAÈA~D9|D$t$D$t$D$܋L$9L$i[]A\A]A^A_E A.EEHEFDt$AAE~Lc|$HcL$EuL|$D|$HL$1D9 fHcDB)‰Љ!! BBDD)‰D!! BTBDD)‰Љ!! BTBDD)‰D!! BTBDD)‰Љ!! BTBDD)‰D!! BTBDD)‰Љ!!؃ BTBDD)‰D!! D9BTD9}JfHcDB)‰Љ!! BBDD)‰D!! D9BT|L\$LT$A[]A\A]A^A_fffffffAWAIIAVAUATEUHSHT$L$DL$E1E9D$D$E1E9}\Dt$Dl$1B47B /E1fffE A3McIcLL$8H(C;FLL$HCAA~AE9|ANjT$D$T$D$D;|$|[]A\A]A^A_LL$@Hl$8LL$@G,dHL$HH\$8Dl$H|$@Ht$HD}E1D)DeAiDIL$DC_~~HcL$Hct$T$D$ HL$Ht$؉T$D$1;t$fHcDB)‰D!D! BBDD)‰Љ!D! BTBDD)‰Љ!! BTBDD)‰D!D! BTBDD)‰Љ!D! BTBDD)‰Љ!! BTBDD)‰D!D! BTBDD)‰Љ!D! BTBDD)‰Љ!! BTBD D)‰D!D! BT BD D)‰Љ!D! BT BD D)‰Љ!! BT ;t$r;t$}jHcDB)‰D!D! BBDD)‰Љ!D! BTBDD)‰Љ!! BT;t$|L\$LT$L$[]A\A]A^A_ffAWAIAVAUATUDSHLt$@HL|$HIDL$T$L$LL$8D$T$9T$D$D$E1A9}VDl$Dd$BE1B4*B "ffffA3E HIcMcC;FCAÈ:A~AA9|D$ԋt$L$t$L$ċD$9D$|[]A\A]A^A_fffCEFEaAE/AAYDd$DD$Ef EFD4l$ADl$܉|$؉\$D$EoAY A T$~LcL$Lc|$AnLL$DL$19"Hc΋T$B)‰T$!#D$ BBDT$)‰T$!#D$ BTBDT$)‰D!D! BTBD)‰D!! BTBDT$)‰T$!#D$ BTBDT$)‰T$!#D$ BTBDT$)‰D!D! BTBD)‰D!! 9BTD9Hc΋t$B)։t$!#D$ B4BTt$)։t$!#D$ BtBTt$)։D!D! BtBT)։D!! BtMLT$A.[]A\A]A^A_ffffffAWAӺAVEAUATUSLd$@LD$HHHt$8E,$AAD1EADNDND9McHcDL$L|$HD$l$PE1E1Ʌt|+T$PDD9N1E1A9}D<>DA)D)EDD>AA@D EE)DD|> DD)AL>AAD EA)EDD>AAD EE)DD|> DD)AL>AAD EA)AIEAD !D! IcшDD>D|> DD)EE)EDD> AA@D EE)ED|> AA D EE)EDD> AAD EE)ED|> AAD EE)EDD>AAD EE)ED|>DAAD EE)ELcAD !D! AAA9A4?DD)݃IcDDDD:D<:DD)D)D|:D@ t:D)ADAA )D DD:ADAAD D|:D)DD) t:ADAAD DD:)ADIcAAD)D  !D! AAE9}_1AIcDDD|=AD)! E9|AMcAD!D! D!AE"D A H|$H\$L$K[]A\A]A^A_fffffffAWAAVIAUATUSHl$@Ht$8L|$HHL$8DL$D$DnDM AEADl$E1EENՉT$AWEAUEOԅANDT$ԉ UOD$Et$DD$A9McLcL|$LD$؋L$PDd$E1Dl$Ћ\$P1DL$DT$AAAD+\$PD;\$DO\$1E1D\$AD9D\$iT$PADA)DL$A IcA>)ȹ)щL>!ЋT$ É)Dى! D;D$~D;D$}BIcЋt$PDL$D:DA)E)EDL$A AA!D DT$D!D! DA"D! AD\$AE9IcDE\>>D)A)DˉL>AA@A )Dщ\> A )DˉL>A )Dщ\>A )DˉL>A )ˉ\>A D)ډA ύMD!AE!D LcC7\>T> EA)D)AAD@A T> )ӉD A T> )ӉDA T> )ӉDA T> )ӉDA T>)Ӊ\>DA )މDA D!AE!D ALcE9C47sD\$E)AIcDED\>>DD)A)\>AEA@A L>A)DE\> A)A DD)\>ADуAA T>AE )DA)ADAE D\>A HcD)ډA D!AE!D AB6D;D$\$1AA9YAffIcA9HL$D)DD!D HD$L8)AKAD! A9~D;D$}#McDAE?AAE)DșD! A!AKA!AE ALcDD!AG"L5D Cl5D$H|$Lt$Dd$D9d$}HT$8L\$8As\$t$@[]A\A]A^A_ffAWEAVAUI1ATUSLd$@Ӻ$ILT$HAE4$AL$AD$EbEDNEAI$A ENEA$IEOND|$AAI$EDNARD $IDOC@D9DL$ȉD$EHcHcD\$Ht$H\$fffL|$8L$P1DL$ȋl$PE1EEWE_D|$ADL$T$ADL$AEDADt$D$T$+L$P;L$OL$E1E1L$ăA9̉L$AT$P)щL$L$DA Hc,>)Dȹ)L>!DA )͹)T>DD!D)A L$!A ;\$s;\$}ODd$PAAAL$HcDd=DA D)AAA!DEE ;\$EA|E!AD!L$D |$ EEAE"}E!E Ee+L$ċt$D|$AAt$D$DEAL$t$DL$D$l$9HcEDL>D4>DA)D)EDL>AA@D EE)DDt> DD)AL>AAD EA)EDL>AAD EE)DDt> DD)AL>AAD EA)AL$EAD !#T$ IcB*DL>Dt> DD)EE)EDL> AA@D EE)EDt> AA D EE)EDL> AAD EE)EDt> AAD EE)EDL>AAD EE)EDt>AAD EE)EDL$AD A!#T$A HcAL$F *Dt>DL>DD)EE)EDt>AA@D EE)EDL>AA D EE)EDt>AAD EE)EDL>AAD EE)EDt>AAD EE)EEAAD Dt>E)LcDDL$ A!#T$A A9G .W;\$ol$t$D$A)9t$l$}pHcDA >)DADD!Dt>D)AAIAL>DD! )AIAAAA!D T$;\$|;\$}uHcDɍSD4>E)DAD! l$;T$}GL>EA)AIDDSAD! T$D;T$}\>AIA)DEÙAD! T$D$ +L$t[DL$McAE!AD!L$D |$EAAG"<(A!E G$(H|$Ll$L$[]A\A]A^A_DD$IcAE!AD!D$D |$F|-\$!\$!\$D\$D \$L$BL-Dt$AD!t$AD!t$D$ D$DT$B"T-A!A FT-^T$McA!!T$D |$G<)l$AA!l$!l$DT$D T$Dd$G"|)A!E Gd)ffffffffffAWEI1AVAUATU͹ASLL$@LT$HE)AQAAEDNEADDAQ ENEbEA""ABENEEE*AAR EOENAADDEDNEA""END ANAD9DD$HcHcD\$HL$L$PHt$ADL$D$LD$8DT$PE1EAXAhEH E1Ev+T$P;T$OT$E1E1T$܃A9ԉT$AffffT$PD)ЉD$DA IcAD,>D)Eȹ)AL>D!AA A)͹)DEAL>D!AA A)͹)T>DEADD!)A DAL$AA!E D;D$DD;D$}QDd$PEAAL$McAE,>D$D)A)AL>AA@A A)DDDl> A D)E݉L>A A)͉DDl>A D)AL>A A)DDl>A DD)A AL$D!AD#t$D McC>Dl>T> EE)AA)ADAA@A T> A)DE A T> A)DEA T> A)DAA T> A)DAA T>A)DEA T>t$A)DA D!AD#t$D AALcE9C4>ODT$AE9McEމA4:El:D$A)At:D)El:AA@)A At:DD)El: A ΉD)At:A ΉщD)El:A Ήщ)IcA ΉDD)A ΉA D!AD#t$D AAB>D;D$+IcEEp9A)DD;t$Dl9EpD)AAA@D D;t$T9AEpA)EAA D D;t$}wDT9E)EEHAAD D;L$}UT9A)EEXAAD D;\$}4Dt9D)AAXAAD ;\$}DT9D)Չ Dl$AIcE)DD$D)AA!#t$A E!AF"\=E FD=H|$L|$L$s[]A\A]A^A_ÐAWAAAVAUATUSHL$8DH|$t$|$9HL$@EHT$Dʋ1DKE1E1fffLT$HT$McMcOrNBE1E9&IcAAKA)DA!! fAJATKA)DA!! fATJATKA)DA!! fATJATKA)DA!! fATJATKA)DA!! fATJATK A)DA!! fATJ ATK A)DA!!A fATJ ATKA)D!! E9fATJA9}-IcAAAKA)D!! A9fAJ|Dl$Et[]A\A]A^A_ffffffAWEAVAUATUSt$HT$Ht$@HT$8L$D|$|$H|$~*DJE`E1E1L$LT$HL$IcMcOjLq1D9%HcAAKA)DE!! fAJATKA)DA!! fATJATKA)DE!! fATJATKA)DA!! fATJATKA)DE!! fATJATK A)DA!! fATJ ATK A)DE!!؃ fATJ ATKA)D!! D9fATJD9}OHcAAKA)DE!! fAJATKA)D!! D9fATJ|D|$Dt$L$P[]A\A]A^A_AWAVAUG,@ATUSLT$@HT$HT$8L$Dɉt$|$|$H|$D"A*ZEJzArEuD$D$L$fffffLc\$Lc|$LD$HL$OxNYE1E9IcEAJA)DA!! fAHATJA)DA!D! fATHATJA)DE!! fATHATJA)DA!! fATHATJA)DA!D! fATHATJ A)DE!! fATH ATJ A)DA!!A fATH ATJA)DA!D! fATHATJA)DE!! fATHATJA)DA!! fATHATJA)DA!D! fATHATJA)D!! E9fATHJE9}tIcEAAJA)DA!! fAHATJA)DA!D! fATHATJA)D!! E9fATH|DT$D$DT$D$L$[]A\A]A^A_fAWAAVAUATUSt$HT$Ht$@HT$8L$D|$|$؅H|$D6DfD:DjDD$j^DJ ~ /AD$D$DD$ԉL$ffffLc\$Hct$LD$HL$OXLq1;t$*HcEAJA)DE!D! fAKATJA)DA!D! fATKATJA)DE!! fATKATJA)DE!! fATKATJA)DE!D! fATKATJ A)DA!D! fATK ATJ A)DE!!؃ fATK ATJA)D!! ;t$fATK;t$HcDEJA)։A!D!A fEKAtJEA)D!D! fAtKEDJA)։A!!A DfEDKAtJDD)AAD!AA!D fAtKL$܋D$L$D$L$[]A\A]A^A_ffffffAWAAAVAUIATIUDSDLL$8Lt$@t$|$AXE1A9}E1E119}*EډfB1HHcAA;AAD9|AD\$DA9|[]A\A]A^A_EA>E~DsD$D$HcT$E1E9MHcT$MTIcDA)!!! DAAT)!!! DATAT)!!! DATAT )!!! DAT AT)!!! DATAT)!!A! DATAT)!!! DATAT)!!! E9ATA9})IcDAA)!!! A9A|׋L$D|$L$[]A\A]A^A_ÅKDsD$D$LcL$Hct$OE1MTE9fffIcDA) !! DAAT) !! DATAT) !! DATAT ) !! DAT AT) !! DATAT) !A! DATAT) !! DATAT) !! E9ATA9})IcDAA) !! A9A|DT$D|$DT$ffffffAWAVAUATUSH\$8HT$DL$LL$@t$ԉL$|$|$AH|$D{E1T$EiE1D;l$E1E1E1E9}mEDCHL$1F"<*IcË1;Hc~+HD$ƃ/HL$BH>HcHc;AHT$ƃ~AE9|ADt$D|$D;l$s[]A\A]A^A_ËL$EDD$̅~\$DD$D$D$\$LcL$Hc|$E1D;d$LD$Ht$K,HDT$ADT$IcċT;T$Lt| DDDLDTAND9D\AN;t$LAND9AND;D$t| ENE9END;T$DDDLENE9ENAD;d$DTD\iD;d$}0McBL;L$BDAND9ANAD;d$B BD|Dd$ԋl$Dd$l$L$[]A\A]A^A_fffAWAVAUATUSLt$@Hl$8t$L$HT$AuH|$M]DL$AV|$D$AF|$At$L$\$T$؉D$E1E9{D$E11D9}RDd$EE1G+C<#1fHt$BH :HcHc;LCAHL$ƒ~AD9|ADL$|$|$D;|$|[]A\A]A^A_ffHD$ 뿋l$G @DL$Ѕ~DD$D$D$DD$D$Lcl$D$Lc\$Ld$LT$ 9D$ODnDD$DbnD$Z DN *AD$D$L$fffffLc\$H|$Hct$HL$N_1D9Lq+HcT$A r)‰D!! fAsALrD)‰D!! fATsALrD)‰Љ!! fATsALr)‰D!! fATsALrT$)‰D!! fATsALr D)‰D!! fATs ALr D)‰Љ!! fATs ALr)‰D!! D9fATs;|$Hc|$A r)׉D!! fAE~DsD$D$HcT$E1E9MHcT$MTIcDA )!Љ!!D AAL)!Љ!!D ATAL)!Љ!!D ATAL )!Љ!!D AT AL)!Љ!!D ATAL)!Љ!A!D ATAL)!Љ!!D ATAL)!Љ!! E9ATA9}+IcDAA )!Љ!! A9A|ՋL$D|$L$~[]A\A]A^A_Å9DsD$D$LcL$Hct$OE1MTE9!ffIcDA ) Љ!!D AAL) Љ!!D ATAL) Љ!!D ATAL ) Љ!!D AT AL) Љ!!D ATAL) Љ!A!D ATAL) Љ!!D ATAL) Љ!! E9ATA9}+IcDAA ) Љ!! A9A|DT$D|$DT$yffffAWAVAUATUSH\$8HT$DL$LL$@t$ԉL$|$|$AH|$D{E1T$EiE1D;l$E1E1E1E9}mEDCHL$1F"<*IcË1;Hc+HD$ƃ/HL$BH>HcHc;~AHT$ƃ~AE9|ADt$D|$D;l$s[]A\A]A^A_ËL$EDD$̅~\$DD$D$D$\$LcL$Hc|$E1D;d$LD$Ht$K,HDT$ADT$IcċT;T$Lt| DDDLDTAOD9D\AO;t$LAOD9AOD;D$t| EOE9EOD;T$DDDLEOE9EOAD;d$DTD\iD;d$}0McBL;L$BDAOD9AOAD;d$B BD|Dd$ԋl$Dd$l$L$[]A\A]A^A_fffAWAVAUATUSLt$@Hl$8t$L$HT$AuH|$M]DL$AV|$D$AF|$At$L$\$T$؉D$E1E9{D$E11D9}RDd$EE1G+C<#1fHt$BH :HcHc;L~CAHL$ƒ~AD9|ADL$|$|$D;|$|[]A\A]A^A_ffHD$ 뿋l$G @DL$Ѕ~DD$D$D$DD$D$Lcl$D$Lc\$Ld$LT$ 9D$OD7D$DjDal$_nDD$D$DL$ԉ|$LcL$Hct$LT$LD$MrOHE1D;L$McT$CD7l$D^jD$DWAD|$Dt$Dl$Dd$܉l$D\$DT$ЉD$|$|$ED~DwDj DD$Da n _ JDD$D$DL$|$ffLcL$LD$HcL$Ht$OHE1D;L$LNffffMcT$t$CD7D$DjDal$_nDD$D$DL$ԉ|$LcL$Hct$LT$LD$MrOHE1D;L$McT$CD7l$D^jD$DWAD|$Dt$Dl$Dd$܉l$D\$DT$ЉD$|$|$ED~DwDj DD$Da n _ JDD$D$DL$|$ffLcL$LD$HcL$Ht$OHE1D;L$LNffffMcT$t$CDT$E9DDDT$HDt;\$~;\$|Qfffff;D$OD$BBDD9|L;D$OD$BDBD9|G;D$OD$BD;\$}>LcBD9}D$BBDD9}D$BDBD9}Ft;\$|‹T$L$T$L$L$4[]A\A]A^A_ffffffAWAVAUATUSt$ԉL$|$|$AH|$HT$E1E9{E1E11D9}\DDD1A4*A L|$2HED< HcMcL|$@E;}=LT$PEBHHc; |A; ~1AE1E9|)AE9}|IcA4AE1E9E1E1E1A9D|$,E&H$8 H$Xǃ 0~AA9}TC DT$,1F#AH$`BH$HH HcBH;4|H$P;4~H$@AD$lD$hE9T:D$lD$hD9)fffA&AfHL$H$X$h$lH$`LL$LD$EAL$D$$틌$D$ED$D$D$4$,D$D$$0D$(XED$XD$T$Lc\$TL$`Hc\$XML$XI1;$)Lc$,C<@ljD)!щD)‰$4!! ! C4C|$(@ljD)!щD)‰$0!! ! CtC|$,@ljD)!щD)‰$4!! ! CtC|$(@ljD)!щD)‰$0!! ! CtC|$,@ljD)!щD)‰$4!! ! CtC|$(@ljD)!щD)‰$0!! ! CtC|$,@ljD)!щD)‰$4! !ǃ! CtC|$(@ljD)!щD)‰$0!! ! Ct;$9Lc$,C<@ljD)!щD)‰$4!! ! C4C|$(@ljD)!щD)‰$0!! ! 9Ctn$l$ht$TT$XA(D$l$hAD$$E1E9E1E119}DDEAH$X9}(H$`B'HHcҋ;|A;~1AAD$D$E9|L$L$L$L$Ld$ L$ L$L$E1E9/E1E1E1A9}D$D$1D$D$1AAH$`BL$ HcH 0BHA;4}JH$ H$Xǃ ~AÃA9|AD$lD$hE9]L|$ A;4~H$멍\m$$D$$E틼$D$$$ $D$$$D$D$$D$D$$D$D$D$`D$\Dl$@A D$Lcl$`Hct$\L$`L$XIME1D;$/Mc$ C<@ljD)!щ։)‰$!! ! C4C|$@ljD)!щ։)‰$!! ! CtC|$@ljD)!щD)‰$!! ! CtC|$ @ljD)!щ։)‰$!! ! CtC|$@ljD)!щ։)‰$!! ! CtC|$@ljD)!щD)‰$!! ! CtC|$ @ljD)!щ։)‰$! !! CtC|$@ljD)!щ։)‰$!! ! CtC|$@ljD)!щD)‰$!! ! CtC| $ @ljD)!щ։)‰$!! ! A Ct C| $@ljD)!щ։)‰$!! ! Ct C| $@ljD)!щD)‰$!! ! Ct D;$D;$Mc$ AC<@ljD)!щ։)‰$!! ! C4C|$@ljD)!щ։)‰$!! ! CtC|$@ljD)!щD)‰$!! ! CtD;$&D$l$hDT$\T$`L$@:EA EE &D$xD$tDl$0Lc\$tLct$xD$4E9D$4L$`H$XOdN,!$HcD$4ETAHD4D;uIcD|E;4$ AMfffff;t$Lt$A |AD\$\$E9|[]A\A]A^A_E<$DuEmE)E~D$D$DL$ЋD$Hc|$D$HcL$Ht$H\$9D$L$H,D$HcT$A AtA|ED ELETE\A\D)D9AF͉LD)D9AFD)D9tDAFDщ|DD)D9EFD)D9DD DD$EFD)D9DLAEFDTEE)E9EFE)E9D\AF݃D$D9D$ԉ\6D$9D$}+Hc\$D\$EDD)D9EFD$D9\$DD|Dd$܋l$Dd$l$L$[]A\A]A^A_fffffffAWAVAUATUSLl$8H\$@t$ȉL$|$|$ADL$H|$HT$LL$HE1D;t$D$E11D9}zl$Efff?HL$1F"D*IcË1;Ic},HD$ƃ7HL$BHBHcHc;|A;TAHT$ƃ~D9|AD|$ȋD$D$D;t$d[]A\A]A^A_Ë3KEE}EuDD$T$A)Ayt$L$A)A)΅҉l$|$~\$D$D$\$D$Lc\$E1LcL$LT$LD$A9O$K,D$fffffIcA AtA|ED ELET+D$E\A\D9FL$L+L$D9Ft$Dt+t$D9F|$+L$DD9DFD$܉|D׉DD ED+D$E9DFL$+|$D9DFT$+t$DLD9DF\$+L$DTD9F\$AD;l$D\\*D;l$}EffIcEED+\$E9DFT$DTELD+D$D9DFL$AD;l$DL|Dl$Dd$Dl$Dd$L$[]A\A]A^A_fffAWAVAUATUSLt$8Hl$@t$ĉL$|$|$ADL$H|$HT$LL$HE1D;|$D$D$1D9}YDl$Dd$E1G+C<#1HL$BH>HcHc;T|JA;DAHT$ƃ~AD9|ANjD$ċT$D$T$D;|$|[]A\A]A^A_HD$E&A^C@D]D}EvDmT$܋L$EDd$AyD)D\$E)t$)t$DT$Ea|$\$~l$D$D$l$D$HcL$1LcL$HT$LD$9HOD$HcՋ t|DD DLDT+D$;D$FL$A D);L$DFt$AtD)D9DAF+L$;L$A|DDFD$D);|$DFL$D)D9EFԃ;l$ED ETELb;l$}TLcB4+L$;L$Ft$C4FLDD);|$DFL$GLFDDD)D9EFă;l$GD|D\$ċ\$D\$\$L$[]A\A]A^A_AWAVAUATUSt$؉L$|$|$AH|$HT$E1E9E1E1E1E9}gDDB1D*<L|$BHAD<>HcIcL|$@A;|ML|$8A;PHT$HDIcDA pщ)) D!D! fA rALpщ)) D!D! fALrALpщ)) D!D! fALrALpщ)) D!D! fALrALpщ)) D!D!A fALrALp щ)) D!D! fALr ALp щ)) D!D! fALr ALpщ)) !D! E9fALrA9}/IcDAA pщ)) !D! A9fA r|ыD$,ED$dL$@ME1E9|>xfffxM0L1EAI鉼P@AE9}H$0$H$$ H$@L$PH|$H$EAHl$L4$HL$H$$$ H$LD$EAL$HL$H$$$ H$LD$EAL$rHL$H$$$ H$LD$EAL$6H\$Hl$Ld$Ll$Lt$L|$HHIиt.Ht)Ht$tRtMtHtCҸtxfffH\$xH$L$L$L$L$HĨE11ۋO;NuDO D;N uDA9uo;nuA;HtLeE;H uE;fffjA;h`GALoAALfIPEpD$\Ll$`D Ld$pDnHT$hEɉD9uD9l;D$\bFfuA@uGffu AAADEuHc|$\HL$`IcIcMcHt$0LD$(Hl$ Dd$LtH|$l$`HT$pLl$hH|$`HcH)I)H)IMDIID)D)AA +t$`tTHD$`DAAL1HDDA3 IID9ՉHcT=D9~HAW9EE3IIDH9~D9}MHAEL1AAAHAEDA3B E9DLcA9D9~HT$H|$(HD$ H|$pHD$hHT$`IL$1H^LT$hD,A +t$`E)IM9uEMPtt)A~AXHD$`DMPAL1HDDDAD BA3ID9ʼnLcA;D9~HAAW91ADADIFDE3IADH9~D9Ht$hHt$0I9sAHADL1AAHDDDDADAB DA3E9D~LcA<D9~h[LT$pD,A +t$`E)IM9uEMQtt)A ~AYHD$`DMQAL1HDDDAD BA3ID9͉LcA;D9~HAAW91ADADIFLE3IADH9~D9zHT$pHT$0I9sAHADL1AAHDDDDDB A3E9DLcA8D9~9u+t$`E1E1ID$< +D$AAL8H5i@L-IcfA f.F H>LIcЅE@A A1A!DƃoAAAf`1IA"AfE1 Ϻ@E $1҅S,u,LH5۔-Icff.v2BHڷ>LH"Hc$HF>LBH(>At A 4EAlAă}D!0IAA AALfffffFH>LIcTEff5LRfqEtSw<Lff{LffffH5@ftSH5@LH /fffffL2fH5\@^LAAfffAALff+Afffffff%AArAALLffHc$LRsLUfffaLffQL ffAL蓶ff1Lff!L,ffLRffLSffLffLsffL胫ffHHHÐHH\$Hl$Ld$Ll$Lt$L|$tHt W;Vt&H\$Hl$Ld$Ll$Lt$L|$fffffO ;N uDD;uDOD;NuAA_D^HHvt|Au~k ˍ-HcMcLHcAM:HHL9s5f(L)HLl$f\$ffT\$Ld$L"HL9rLHAu1+~HcMcLcL4M\>@IHM$>tLFHOAusL9MsEHMIDI!%I LAH H I LHL9rL9s ALLMjBL9MsAEPIAI L HHL9r룐HHHÐH\$Hl$HHtHtDGD;FtH\$Hl$HfffO ;N uDD;uD_D;^uAA بuXEFAɋWHoNH^uGuEAAAAt.AvHH/f%fffWHHGHHHHÐHH\$Hl$Ld$Ll$Lt$L|$tHtHtDGD;FtH\$Hl$Ld$Ll$Lt$L|$DO D;N uDD;uD_D;^uD;BtD;J ffuD;uD;ZuEAoLgLnDɋ~LrrtJAFfA9A9!بtA9u EٹAteAt13FÅ~HcHcLcAELML~ DfAIX H HuIIMAu롅~IcHcLcHT$HD$HcL$LLLHL\$MHMMHK4vAAXMMD$MMMVA $AHNEII9suII9D\$\$IIT$d$H X\$D\$|$T$T$D$DII XT$\$H H III9rII9sA*AX)A(MLt$IL$tAHNEII9syII:D\$DD$IIT$DL$H EXDD$D\$|$t$T$|$DII Xt$\$H H III9rI6AttHNI9#IMIID$Dd$D\$Dl$H I EXE D$DT$D\$D\$EXEPII9rH~I9II IID$T$L$D$H H XT$D\$D$Dt$L$D|$DEXDt$\$H H III9r5HHH聥HÐHHAtHtDAAAv DHÐE1ADHÐHl$Ld$Ll$L|$H\$Lt$Hx;HT$xIHADD$tMϺt:H$HH$PL$XL$`L$hL$pHxDvD;wuED$lD$hrwD$lD$hT$hH|$xL$t$tH$`DT$HLD$XAXDŽ$$T$T$T$lH|$ H|$Xt$LHDŽ$D$0$AHD$0D$(nD$pDT$HH$HL$`1Ƀt:t0t&ttAAEMt.Et)A AEE1AAA CusA΋u,AL$,E بDH|$X#1ۃ|$tE\$tEtH$HukT$p D$lD$hnAArDAw'MfffH|$XHf@HcLЉD$pDT$pEqLL$`qD$MD $ A1 $D ƉA AA!AtAD ЃAD!uAH|$XH@HThMH|$XH@EHL$`HT$xL$D\$lDT$hAXLLD$PH$H$HT$ EHD$0D$(D$DT$DT$D$}lD$phw,ːD$pH$H8L.fffuAtH|$XLLx"D$pH|$XLL##AH|$XH @AH|$XH@H|$XLL6e"oH|$XLk"]fffffffffffHHIҸt)Ht$DAAAD!بtAtHÐE1L%HÐHDAAAv DHÐADHAWAVAUIATUSHHX>Df0D~ Dv$GN4<ADAHDEHc1HtAHCk(H\$AEDDHH$At$LH蟜H @HXH[]A\A]A^A_ËL$D$ AUEEIul$H|$(~HHcMcL$L,E1M\=A9}GL @E1ҐIcMcAH2AA9A B HD2A BLHL2AC|LLuFB 1Ҩ91HXH[]A\A]A^A_fffffffffLd$Ll$Lt$L|$H\$Hl$HHIIIH$EHDŽ$tHt .;/t8H$xH$L$L$L$L$HĨËV$$;OuDN ~HFA]H$DFL$H$D$L$$H$$$D^IUD$AzEA É$*H$D$$D$$$)Ɖ$*$)$t5t+t!tt 1ۃE݋$H$$`D$$D$$L$DŽ$$$H$h$D$D$$D$$$H$`L$pDD$扬$D$$H$$$H$ͭHD$\AADE\$\AHD!ШD\$\P۹L">EHHcIH$!fAo fAgfAfAwA$DŽ$THDŽ$HDŽ$YYA$fA*fW%}-\AA|$\*^^D ȨYYD,D$,ԉ$E@A!3A@A tDŽ$DŽ$L$D$L|$ $$H$$D$LL$D$DD$0L$(E11LH\$D$D$D<$dH$Ht$$H$`$D$H$D$D$At$(T$LLD$0L|$ Dt$D\$D$gdH$hH;$fH$H$L HL$ALd$PAAH$HRH$H3H$H$H$hH;$Lc$H$1HMIL)D$ED9}LcD9J<|I$I1H$19} Hc$L$LcK H9|苄$$$4G9}L$Lc9K |닌$H$8$<ɉ$$$~ML$H$H$ H$H$L$(LL$H$L$0H$ՉH$hH;$t{H$qAN$D$E,IcH]$11IH9}Lc9B|Lc$$N,19}LcC|$9|D$$$B P9}Hc9A||$$8݋$($,ɉ$勌$$L$\$A҉$$H$L$H$H$ <L$H$H4$H$LL$IHT$LHt$P$H$HH$HML贿fff覿ffffH$H_"H$HtyH$Hu14`$tHm>HH,Ha>H$1HY"똅D$HL$H$(L$0H$ L$EL|$hH$ HD$xLD$pH$L$HT$`$8$L$H$H$MLD$hHLLT$$Ht$H$Ht$P$D$Lt8H$HH$HMtL(D$L$8$<$D$9$$XHcA;AD D+LL\$xHD$`DE E DD+TL\$pED9E~H$H$MLD$`H$LHt$HL$Ht$PL$HM$At^H$HuEH$Hu1MtL%DfD\AEDTD,fffH$H$HLt$pH$LL$xLD$hHT$HD$LL4$Ht$P$$<$,L$H$LL$`q$$$LD$H\$L $HcH$LD$hMH33)fffLffff +ffff$$M^LQL$L$$LT$`H$$Lt$L\$L$VffffffffffHDIAQAAD!بtAtHLHÐAWAVAUATUSEHXAEplEPDHL$IcH(Ht$DL$EH|ApdEhpD$DT$EPxExhHT$IDDt$L$D!Ah\DHEX@IHff`DL$܉l$D!IcD\$HHD$EX0C4f(fpfxDL$HH@HL$HHcHD$HCAXt9\$McMcLcLd$1M DL$DT$D$*B BYDD*B ZLDYD* DYAXD*B BDYAX*B ZYYHct$DX*YDL$D!DXHHD$AYXXff`DDL$D!HHD$f(fpfxDL$HH@HL$HHc9\$HHBMcMcLcLl$Hl$FB F ZDd$LDFD~A*E*YDYA*E*YDYIcAXAXYYXAX\A\MIcHcMcMcH\$0DzHD$0D:B4BB RD$A*LBYA*YD*E3A{DYGL$(Hc9fAh|!fffff$~$$Ѽ$|$tn|$$T$S $ $pfff$$$$9$Lc$H$H$hL$hH$hL$hL$hL$hJ,ދK@EL$DW\L$hH$hH$hH$L$L$$ABXAklD$DcpEHd$Awh_|L$$L$pD$L$$$oxD$$$L$hAډDD!HH$f(fDPfDXfD`#$$HH$$f0fxfD@fDHAHcMcAHtOTIH$L$4MMʅAE1AffffHc$$McCAA"<$d$DIHDEAD\$EIA9Dl$~A9D)D9~ DE" $EL;D$u+L$T$DAD99DAHHH1#HÐHHH1HfffffffffHHHHHÐH\$Hl$Ld$Ll$Lt$L|$HHtHtHtHtDGD;Ft&H\$Hl$Ld$Ll$Lt$L<$HfffDO D;N uDD;uD_D;^uD;BtD;J ffuD;uD;ZuD;AuD;I fffuD;zHFHoAHYDEċyDqHD$HBHl$H\$Z|$ԋ~DHD$ A"EDEAB D,9@9!t D9D;l$!Ȩt EľEE9A AAAAfffALc\$LcLcMc׉1D9Ll$HD$HcHL$L|$Lt$fETHfD<)fD4(M,/I.E\EYEXE4/Hl$fDbD9fDmE\EYEXEefD]fRA\AYAXAUfDMfDBE\EYEXEE>LD$LL$LT$L\$1|~Lc\$LcLcMc1D9HcL|$Lt$L,IHD$H\$IfCfGT=E\EYEXGT;|H|$LD$LL$LT$`aAA A6Lc\$LcLcMc׉1D9Lt$HD$LcHL$Hl$Ll$C,IF 9F8Nt=KT=E\DYEXFD=L|$D9zAW\YXA~AGr\YXAvAg Z \YXA^ GLD$LL$LT$L\$ A9Lc\$LcLcMc1D9HcHl$Ll$L4IHD$L|$IFt5G|5DHL$K,7E\J1EYEXG47Lt$D9DbEnE\EYEXDeEVDJE\EYEXDMYLD$LL$LT$L\$2XPLcT$HcLcMcω1D9}rL|$Lt$HcLl$L\$HD$H\$D9EHt$AA|A\YAXADt\YXt|H|$LD$LL$LT$kAA\LcLcDHc\$Mc׉A19}>Ld$Ll$LcL|$L\$9fClfC$\CY$XC$|LD$LL$LT$H\$uDAąMcLc|$HLcHD$LcLD$t$LL$LT$LD$H|$L\$MLALI܃I4IIv,AIAI\AYIXAIAAH~A II9M"III IIDd$I |$L$H DD$\$\$Dd$t$\ωL$l$Dd$d$\H AYYXXf\$Hl$fL$HD$H H IIH\$I9~L$LII9sEE E\EY EXELt$Ht$Ll$Ht$L|$L$pOAH~AII9IIII IIT$H D\$L$H D|$T$L$\$Dt$D\؉L$Dl$\$Dd$A\H EYAYDXAXfL$Hl$fD\$HD$H AI M IH\$I9~D$DIAH~I9fI)IIM#IIl$d$\$l$Dd$t$H H I \YXA l$\$\$D$Dd$T$\$\YXAXII9mEDEAB D,AH~A III9IL IH\$\$H \AY DL$I Dl$\$Dt$E\EYkXIEXfDl$Ld$fL$HD$I L IILL$I9~L$mLI@AH~AIJI9ILIH\$d$H \AY#\$l$DT$I XD$\AYkIXfl$Hl$fd$Ld$H DH IILT$I9~D$qLQAH~AII9II II+IID$DD$L$DL$\$DT$H H HE\H EYEXED$t$L$|$l$T$L$\YXApII9fIH~I9I M"II+IIL$H |$Dd$I L$l$Dd$L$DD$Dd$D\$H \l$DT$Dd$E\DL$AYEYXEXfDD$H\$f|$HD$H H III9EH~AII9II*IM#IID$H D\$l$H D|$\$\$D$L$l$Dt$E\Dd$Dl$l$Dd$A\DYAYEXAXfL$Hl$fD\$HL$H H ILIH I9BSHHH!HfffffffffHHHH!HÐD$LfA~0W.uzZHHuHHuffffffffffT$LfAfAQ~[Wfffff .u'z%Z/`.zfftHHup.uzZHHufffffffffT$LfA!fAYfAQW(.uDzBZ7h.zt<x.uKfffzEDZH DGH ufffDHD.uzDZD_DPD.ztH H uffffffffT$LfA)fAafAYfAQWfffff0.uWzUZ?p.zfftLD@D.uYfzUDZDWDH D.ztJHHufffffDXD.uzDZDoD`D.ztDp D.uzDZHD HPffffffffffD$LI ~.fWff.uzHHHuÐHHufT$LI1II~KfWff.u zH7fPf.ztHHufXf.uzHOHHuÐT$LIqMII~xfWffffff.u6z4LfPf.zt1fXf.u;z9HOHHuffff`f.uzHwfhf.zffftHHuËT$LMMAIqIIfWff.uEzCLfPf.zffft[]A\A]A^fffD$~;ffffDD HEHIA*A*YXHuffffffffSD$LIIHօ~f#,fWAAHI**YAX"A*^ ]_,!" IAIIHAu[fffffffD$~TffffDDVHD HEIA*A*A*YYXXYYHufD$~cffffDDD^EHA*A*YXA*DJHEHIA*YXYYHuffD$~XffffDDVHD HEEHIA*A*A*YYXXYYHuffffffffffUILISD$ HӅf->,fWAfffffA3DAKHID**AYAX2*AAY*ɉ^ ЉE!D]ED_A,! ˆAXz@"u^]_,! WA"AAbIIHA@[]fffffffffUILIISD$ f%>,fWۉA A1DAAQID*D*EYEX**AsI *YD^E]ED_A,!"E AXrAH*щ^ ]_,!"M @wAAAZA@IIH[]fffUILISD$ HӅf-.,fWAfffffASA3DAI*D*H*DYEX*Y D^E]ED_A,!"E @7AXrAP*҉^ ]_,!"U @wA"AAbA@IIHA+[]ffffffD$~lffffDD^DVHA*D A*A*EHIA*YYXYXIXaIaHufffffffD$fDDVDNHA*DA*EA*A*YX))DREPA*YXYYDJHEHI A*YXaaH{ffffffD$~tffffDD^DVHA*D A*A*EEHEHHI A*YYX YXQXa QaHufUILISD$ HӅf5~,fWAfffffAAKDAsID*D*D*H*DYEX"ADYDY*щD^ ЉE!E]ED_A,! ˆEXJU!D^A]_,! ЈGEXB@"uD^A]_,! WA*AAjAjIIHA[]fffffATIMIIUSD$(H_f-9,fWffA 3DASD{H*D*DYEX2*D*AqD*Ay ID*EYE^*YE]ED_A,!A"$ A EXJAHD*щE^ E]A_,!A"L$ AsAXrAH*ɉ^ ]_,!A"L$ AsA"AAbA@AbA@II I[]A\fffffffffUILISD$ HӅLf5,fWAfffffAKADAAsDI* D*D*H*DYEX*D*DY YE^E]ED_A,!"E @7EXJA@D*ЉE^ E]A_,!"E @wAXzAH*ɉ^ ]_,!"M @wA*AAjA@AjA@II HA[]fffffD$fDDND^A*A*DVD A*HEHIA*A*YYXYXYYXIXiYIiH ufD$fDNDD^A*D A*DVEA*HE*AYX9A*9DZEXA*YXYYDREPA*YXaaDJHEH IA*YXiiH TfD$fDDND^A*A*DVD A*HEEHHEHEH IA*A*YYX YXYYXQXi YQiH tfUIHISD$ \f=Q,fWASA DAsDD*UD*AKD*HI*DYEX:D*ɉADYDYDY*ىD^ ЉA!E]ED_A,! ˆEXbAQ!D^E]ED_A,! ЈGEXRAQ!D^A]_,! ЈGEXJA"q D^A]_,! WA2AArArArII H[]fffffffAUIMATMIUHSD$0HӅf5,fWA uAUD}DEHD**AYAX#**sD*E* {DCDH*DY^D*EY]A*Y_,!A"$ A EXsAJD*E^ E]ED_A,!A"L$ AqEXKAJD*щE^ E]A_,!A"L$ AqAX{AJ D*ى^ ]_,!A"L$ AqA+AAkABAkABAkAB I IIAK[]A\A]ÐUIHISD$ f=,fWAsA AASDD*u*D*AKHI*YAX*D*DY DYDY^]DD_A,!A" EXjA@D*E^ E]ED_A,!A"A OEXJA@D*ЉE^ A]_,!A"A OEXBAp *މD^ A]_,!A"q OA2AArA@ArA@ArA@ I IHm[]fffD$~=ffffD DHEHIA*A*YXHuffffffSD$LIIHօf,f,AfWAHI**YAX*AA"I*^ ]_,!f#AI fHAu[ffffffffD$~UffffD DHDVEHIA*A*YA*YXXYYHuÐD$~cffffDDD^EHA*DJA*A*YEHXHIA*YXYYHuffD$~YffffD DHDVEHEHIA*A*YA*YXXYYHufffffffffUILISD$ HӅf-.,f%,AfWA3DAKEHID**AYAX:D*EYEXBAA2ArI*ɉ^D^ !f#uAID]EA]D_A,! _f,! fWHA<[]ffffffUILIISD$ f%.,f,fWA A1DAAQID*D*EYEX**AsA*I D*AYD^AXzAjIE]ED_A,!f#E fAH*^ ]_,!f#MAA@I fwH[]fffffUILISD$ HӅf-,f%,AfWASA3DAI*D*HD*EYEXAY*AXzAPA2Ar I*҉D^^ E]E]D_A,!f#E_ f7,!f#UAA@I fwHA&[]fD$~mffffDD^DVHA*D A*A*EHIA*YYXYXIXaIaHuffffffD$fDDVDNHA*A*DDRA*DJEEPHEHI A*YA*A*YYX)XYXa)YaH{ffffffD$~uffffDD^DVHA*D A*A*EEHEHHI A*YYX YXQXa QaHuÐUILISD$ HӅ!f5^,f-N,AfWAAKDAsEDID*D*D*H*DYEX*ADYEXRDYA:EXJAzAzI*щD^ D^D^ʉ!E]EE]AD_A,! A]fU_,!_!f#uA ,!fGI fWHA[]fATIMIIUSD$(Hkf-,f% ,fWA 3DASD{H*D*DYEX:**AqD*Ay A2ID*EYD^EXR*YArAXzArIE]ED_A,!fA#$ fA AHD*E^Ӊ E]ED_A,!fA#L$ AHfAs*^ ]_,!fA#L$AA@A@I fAsI[]A\ÐUILISD$ HӅUf5,f-~,AfWAKADAAsDID* D*D*H*DYEX2D*DYA: EXRDYEXBAzAzIE^E]ED_A,!f#E A@f7AHD*E^Ӊ *D^ E]A_,!f#EA]_ fw,!f#MAA@A@I fwHA[]ffffffffffD$fDDND^A*A*DVD A*HEHIA*A*YYXYXYYXIXiYIiH uÐD$fDDND^A*A*DVD A*DZEEXHA*DRE*DJAYA*YX9XYEPHA*YEH A*YXaXiI9YaiH TfD$fDDND^A*A*DVD A*HEEHHEHEH IA*A*YYX YXYYXQXi YQiH sÐUIHISD$ hf=!,f5,fEWfASA DAsADD*U*AKD*HI*YAX D*щADYEEXjDYEBEXZDYEBEXREBI *ى^ D^D^ۉD^!D]ED_A,! E]EfAQA]D_A,!A]!__ AQfG,!!fA#q A ,!fGI fWH[]ffffffffffAUIMATMIUHSD$0HӅf5,f-t,AfWfff uAUD}DEHD*D*EYEX D**sD*E* {DCA;H*DYD^EX{A{D*EYEXSA{A]DA*DY_,!fA#$EXCA{I fA AJ*D^ E]ED_A,!fA#L$ AJfAqD*E^Ӊ E]A_,!fA#L$ AJ fAqD*D^É A]_,!fA#L$ AABABAB I fAqIA=[]A\A]ffffUIHISD$ f=,f5q,fEWfAsA AASDD*u*D*AKHI*YAX**D*DY EXrDYEXRDYEXJEEBEBEBI ^]_,!fA# A@fD*E^ E]ED_A,!fA#A A@fOAp D*E^Ӊ *D^ A]_,!fA#AA]_ fO,!fA#q AA@A@A@ I fOHg[]fffD$~=ffffD DHEHIA*A*YXHuffffffSD$LIIHօf,fWAAHI**YAX"AII*^ ]_,!f#AI fHAu[ÐD$~UffffD DHDVEHIA*A*YA*YXXYYHuÐD$~cffffDDD^EHA*DJA*A*YEHXHIA*YXYYHuffD$~YffffD DHDVEHEHIA*A*YA*YXXYYHufffffffffUILISD$ HӅf-6,fWAfffffA3DAKEHID**AYAX2*AYAXzAA"AbI*ɉ^^ !f#uAID]E]D_A,! _f,! fWHA?[]fffffffffUILIISD$ f%6,fWۉA A1DAAQID*D*EYEX**AsAI *YD^AXrAZIE]ED_A,!f#E fAH*^ ]_,!f#MAA@I fwH[]ÐUILISD$ HӅf-&,fWAfffffASA3DAI*D*H*DYEXY*AXrAPA"Ab I*҉D^^ E]E]D_A,!f#E_ f7,!f#UAA@I fwHA([]fffD$~mffffDD^DVHA*D A*A*EHIA*YYXYXIXaIaHuffffffD$fDDVDNHA*A*DDRA*DJEEPHEHI A*YA*A*YYX)XYXa)YaH{ffffffD$~uffffDD^DVHA*D A*A*EEHEHHI A*YYX YXQXa QaHuÐUILISD$ HӅf5v,fWAfffffAAKDAsEID*D*D*H*DYEX"ADYEXJDYA*EXBAjAjI*щD^ D^D^‰!E]EA]D_A,! A]fU_,!_!f#uA ,!fGI fWHA[]fffATIMIIUSD$(Hef-1,fWffA 3DASD{H*D*DYEX2*D*AqD*Ay A"ID*EYE^EXJ*YAbAXrAbIE]ED_A,!fA#$ fA AHD*E^ʉ E]A_,!fA#L$ AHfAs*^ ]_,!fA#L$AA@A@I fAsI[]A\fffffUILISD$ HӅPf5,fWAfffffAKADAAsDI* D*D*H*DYEX*D*DYA* EXJYAXzAjAjIE^E]ED_A,!f#E A@f7AHD*E^ʉ *^ E]A_,!f#E]_ fw,!f#MAA@A@I fwHA[]ffD$fDDND^A*A*DVD A*HEHIA*A*YYXYXYYXIXiYIiH uÐD$fDDND^A*A*DVD A*DZEEXHA*DRE*DJAYA*YX9XYEPHA*YEH A*YXaXiI9YaiH TfD$fDDND^A*A*DVD A*HEEHHEHEH IA*A*YYX YXYYXQXi YQiH sÐUIHISD$ `f=I,fWASA DAsADD*UD*AKD*HI*DYEX:D*ɉADYA2EXbDYArEXRDYArEXJArI *ىD^ D^D^ӉD^!E]ED_A,! E]EfAQA]D_A,!A]!__ AQfG,!!fA#q A ,!fGI fWH[]ffffAUIMATMIUHSD$0HӅf5,fWA uAUD}DEHD**AYAX#**sD*E* {DCDA+*DYH^EXsAkD*EYEXKAk]A*Y_,!fA#$AX{AkI fA AJD*E^ E]ED_A,!fA#L$ AJfAqD*E^ʉ E]A_,!fA#L$ AJ fAqD*^ ]_,!fA#L$ AABABAB I fAqIAC[]A\A]ffffffUIHISD$ f=ɣ,fWAsA AASDD*u*D*AKHI*YAX*D*DY EXjDYEXJDYEXBA2ArArArI ^]DD_A,!fA# A@fD*E^ E]ED_A,!fA#A A@fOAp D*E^ʉ *D^ A]_,!fA#AA]_ fO,!fA#q AA@A@A@ I fOHd[]fffffffff|$L~9ffff**YHHXXHHufffffffD$IӅf-,f%ɡ,AfWA**>YX9DAX1IA0HIH^D]D_A, ]_,!A# ‰HAufffffffff|$L~Lffff***fYHYHXXHXaaHufffff|$L~_ffff*2*.Y*R*fYHHXX))fHXHXaHaHufff|$L~Zffff(**Y*fHYHX(XfHXHXaHaHuffffffUILISD$ HӅf5,f-Ο,AfWD* E*EYEXAE*CAXEYDEXBDA:HA8IAzIID^D]D_D^A,̉ ыU!E]AA]_,! _,щ#u! WHA8[]fffD$I҅ f=!,f5ٞ,AfEWfE E**D*EYfAPE*RDXD*NEYDXIDEXDEIDAHE@AXIHE^E]D_A,ƉD^ ]_A]_,!A#A] ,ˉ_ ,!A#A ‰WHA fD$IӅf5,f-,AfWfE E* D*EYfAPD*FDXEYDDXADEX9IA8HyAxHAXIE^E]D_A,ʼnD^ ]_E]A_,!A#A] ,ˉ_ ,!A#A ‰WHAffffff|$L~_ffff*"**^*nYYHH YXXHXYXiYiHufff|$LfDD**>AY*Z*fY*R*nYH H EXDX99fpXpXaafHXHXiHiHwfff|$L~{ffff8*"*6Y*V*nYHH YX8X11fXXXXQQfHXHXiHiHuffffffUILISD$ HӅf=6,f5,AfEW*#E*3DYEX2E*[AXDYE*KEXZDYDEXJDEDEHEBEBI IID^D]D_D^A,ωD^ ыU!E]EE]AD_A,! _A]щU,!!_ ,!ʉG#u WH A[]fffD$I҅fD ,fD,AfEWffffffAA**6YfE`E*jX1*~AYXyA*bD*^XfAXDYDXYADAEI EXEDQ^]A_X,ʼnAEPA^ E]E_DQD^]A_EPH IH]DAAE_A,!A#]A]A_ A,Ɖ ,!A#A ,ʉWA_ ,!A#A ‰WH AffffffSD$LIхif=],f5,E1AffffffAA*!D*>DYfEXD*VDX9DYDXQD*FDYXDXADDIH DD^DX]_,fAXLMLYE^ E]XD_MXLYMXHID^]_E]ED_A,!#E]A_ A,ĉA] ,!#C ,ʉW_ ,!#C ‰WH A[f|$L~rffff****f*^*v YHHYXYYXHXaXYXqaYqH uÐ|$LfD D*D*EYD*B*~AY*Z*nY*R *v EXYHHD DXDfDHEXDHXyyf`X`XiifHXHXqH qH Ifffff|$LfD**D*DY*f*~Y*v HYHDXYDDXDfXXXXaafPXPXyyfHXHXqH qH ^fffffffffUIHISD$ ^fDȔ,f=,fEWɐ*mA*3YAX2E*sAX DYE*cEXrDYE*S EXbDYE EXREEJEJDEJH^]A_D^,ɉD^D D^AIII !]DE]AE_A,! A_E]܉AQ,!AA]!A_ A_ȉGAQ,!! ,!ʉGA#q W H[]ffffffSD$IӅfDO,fD,E1҉fAE*;AE*C*fAYXaD*6EYDX1E*kD*NAXEA*k AD*f IEYHDXIADY]A_,fAPD^DXaA AXE^]A_AA]AA_,!A# ,fAXD]fA` ELXME_A,!AXA#ALQMPLQD^]A_MPLQMPD^ D]ĉWE_A,I H A]A]A_,!A#A ,ʉWA_ ,!A#A ‰W H4[ffffffffSD$HӅfDO,f=,E1AfA*+*&YfAPD*~X!DYDDXyDD*^XDYD*N DXYHDYHXDXID^D]E_A,ĉDD^ ]A_D]fA`ADA_,!A#E]XEE_ ,fAXD^܉D]E_ A,LXM!A#ALQMPLQMPLQMPD^]A_ A,I WH E]AA_,!A#A ,A]щW A_,!A#A ‰W HAI[Ë|$L~9fffffHfHXYXHHufffffffD\$LE~NE1EffHHXYAXI^LHLHHAuff|$L~LfffffHfffHXYYXHXaaHufffff|$L~_ffff2ff.ffHXYX))fRfHHXYHXaHaHufff|$L~Zfffff(HfffHXYY(XfHXHXaHaHuffffffD\$LE~nE1EffHffHAXYYX^HXaH^gLHMLQIHAuffD\$LEE1Efff&frHfnHXYYAX!^'fHXAIXiI^oLHLLQLPHHAufffffffffD\$LE~|E1Ef*fHfffHXYYAX^fHXAIXaI^gLHLLQLPHHAufff|$L~_ffff"fHff^fnHXYYYXHXYXiYiHufff|$LfDfDf>fffnHEXAYDX99fZfpXYpXaafRfHHXYHXiHiHwfff|$L~{ffff"f8Hf6fVfnHXYYY8X11fXXXXQQfHXHXiHiHuffffffD\$LEE1EffHfffnHAXYYYX ^PXa^gPXiH^oLHMLQLQIHAtffffffffD\$LEE1EfD ff.fDBfRfvHf~HAXAYAYYAX)^/fXAXAYXq^wfHXAIXyI^LHLLQLPLQLPHHAHfffffffffffD\$LEE1EffHf6f^fnHXYYYAX1^7f`XAaXY^_fHXAIXiI^oLHLLQLPLQLPHHAXfffffffffff|$L~rffff*fHffff^fvH XYYYYXXaHXYXqaYqH uÐ|$LfDfD fDf~fnfvH EXEYD DXDfDBfDHEXAYDHXyyfZf`XY`XiifRfHH XYHXqH qH Ifffff|$Lf*fDHfDfff~fvH DXDYYYDDXYDfXXXXaafPXPXyyfHXHXqH qH ^fffffffffD\$LEE1EffHf^fnfvH AX YYYY X ^`XY^_`Xi^o`XqH ^wLH MLQLQLQIH ATffffffffDT$LEE1fD"ff6fDZfDRfZH f~fDFAXfDNH AYAYAX1EYDY^7f`AXAaXy^fPAXAQDXAD^DGfHXAIDXII D^DOLH LLAL@LAL@LAL@H H A ffDT$LEE1f"fHfDf~fVfvH XDYYYADXYD^DfhXAiXy^fXXAYXQ^WfHXAIXqI ^wLH LLAL@LAL@LAL@H H A%fffffffffff|$L~9fffHHXYXHHufffffffD\$LE~PAEHH(XYAXI^DHDHHAuÐ|$L~LfffHfHXYYXHXaaHufffff|$L~_fff2.fHXYX))RHHYXHXaHaHufff|$L~ZfffH(fHXYY(XHXHXaHaHuffffffD\$LE~pAEHfH(AXYYX^HXaH^gDHEDQIHAuÐD\$LEAEffff&rHnHXYYAX!^'HXAIXiI^oDHDDQDPHHAuffD\$LE~AE*HfHXYYAX^HXAIXaI^gDHDDQDPHHAuf|$L~_fff"H^nH XYYYXHXYXiYiH ufff|$LDD>fnH EXAYDX99ZpYXpXaaRHH YXHXiH iH wfff|$L~{fff"H86VnH XYYY8X11XXXXQQHXHXiH iH uffffffD\$LEAEffffHfn(AXYH YYX ^PXa^gPXiH ^oDH EDQDQIH AuffD\$LEAEffffD .DBRvAXAYH ~H AYAX)Y^/XAXAYXq^wHXAIXyI ^DH DDQDPDQDPH H AHffffD\$LEAEffffH6^nH XYYYAX1^7`XAaXY^_HXAIXiI ^oDH DDQDPDQDPH H AXffff|$L~rfff*Hf^v XHYYYYXXaHXYXq aYq HuÐ|$LDD D~nv EXEYHD DXDDBDHAYEXDHXyyZ`YX`XiiR H HYXH Xq Hq HIfffff|$L*HDDf~DXv DYHYYDDXYDXXXXaaPXPXyyH XH Xq Hq H^fffffffffD\$LEAEffffH^n(AX Yv YHYY X ^`XY^_`Xi^o` Xq H^w DHEDQDQDQ IHAUffDT$LEAD"6DZDRZ AXAYH~DFDN HAX1AYEYDY^7`AXAaXy^PAXAQDXAD^DGH XAI DXI ID^DO DHDDAD@DAD@DA D@ HHA DT$LEA"HD~VXv DYHYYADXYD^DhXAiXy^XXAYXQ^WH XAI Xq I^w DHDDAD@DAD@DA D@ HHA%fffffffffAWIAVIAUEAATUSHHxHHL$hHT$hHl$pDHD$0Hl$(HT$@tjHCHDW@MDOW D'HD$P DT$XDL$`T$\|$du+EA~ AwL|,DMcMAAHxD[]A\A]A^A_Hz?HD$8AuAvE1ېIcAI I4HtHtDSD;QuDK D;I u;;9AuA9Cu;>uD;VpD;N fDIHHyDLNH|LVLFLTDt ;D$X0AE9]Dt$`\$XAALcLH}HIA N19LD$H}1fffffHc9I4|11D9}LcIB LD9|tHD$8 LcL$XHD$8At$JTHT$wRDT$XAAIcH tHH '|?J<L$ Ld$0HHD$@A4E1A~AljDD9|DD$\E#Lc\$d\$\\$L\$ AE19}TAffLc1DL$`IL\$LD$HB|JTLJtDL$D<$LL$@1HAD9|Lc1|$`ILd$LL$@B|JTLJt|$D<$LD$HH|$PHAT`H|$0t%t$`D<$LL$hLD$@11H|$Pt$1T$01HT$ HT$PD9}"HcHLc\HcDL\HDD9|L$Ll$hL9l$@t H|$@)L!H;l$(tHE1HKy?HT$8AH=$w?H|$8HVy?HT$8Hx?A/Hx?qIcH¹HHAl2Hx?fHAaHfffffffffHE1DHÐAWHAVAUATUSttt4[]A\A]A^A_ËG| []A\A]A^A_ËGuu׋G L_DOH6YE1A1McD9K }HcD9H4|AAu*.fffffsfZ|$fnfvDd$ZZd$fVD|$Z\$HcWDWDl$L__ HBH9AqfL$LL$B Lt$IcHT$DHHcELt$DqHT$LD$H|$Ht$HD$E1AIc1I yHH#t$HH t$H#T$D!H T$H\$HD$H|$Ht$Ht$LD$D{HT$D`LDDlD9LD$H|$DHcڃ9LH|~AAXfD>EZDt$fDnDT$EZDd$LcODGDd$L__ C LH9[AW2C<Lt$EE1ADoLt$LcIcI41@tgHH#\$HD$DDPFdL H\$D9H|$DfffHcڃ9H<~AAIcI41@uHL$DT$Da붃G LgWHL^LVHvUD1AHcI1D9}!ffLcD9JN\NTJt|Au G H_WLHvDE1AIcH 1D9}LcD9NJt|AAuHt$LD$Dd$ND|$EhfDEZDD$LcWDGDL$H_o MIE9_AE1fDE1A9EZDT$DL$}'D1D9} D9HD |ADA9|AHE~1[]A\A]A^A_fDZDD$f~Dd$Zt$fnDl$Zd$LcODGDT$L__ G<@LHA9AHH#t$DH Ht$Ht$bG4@Dl$DT$E1AE~Ic1ItD"DjDRD9DHcك9H4HtHt~D9}HcD$DlDTAAuDûW _LLVHoHvҋGD@1A1LcD9JT}LcD9NNTJt|Au{LT$nDL$DL$E1ۉIc1H tD Ap9Lc9N~D9}fffffLcD9F |Eu DûlDŽDӻLALL9ffffffHH\$Hl$Ld$IAtHtDWD;VAtH\$Hl$DLd$Ë_ ;^ uDD;u݋G;FuՃuHAH@ AAuAVoHvHtAAuDBAfQfiENDEfD9AfA1ANfAyfEAA9fEIfEQ fEY(T$fEa0fEi8l$fEq@IHMcHcGRJ,HAE1E9IcAfAfAdfAlAYYYAYXAYXAYAYAXXXAYAYXXXT$XL$QIHE9^LD5H :ALH:E1&DBAfQfIENDEfD9AfA1ANfAyfEAA9fEIfEQ fEY(T$fEa0fEi8L$fEq@IHwMcHcGRJ,HAE1E9IcAAZAZdAZlYYAYAYXAYXAYAYAXXXAYAYZ!XXXT$XL$ZZAYH E9RLD5H :ALH.ffHXH m9?H ƈDe?HT$H8n?H $HT$8HtBh?IX9?HT$@I_8?I m9?IUy?HKJ̓?HL\$LT$LL$ LD$(HL$0HXffffffHXH0*HA @HT$H1&ʿH $HT$8H7k?IS:X߿HT$@It(%I/?IAF@#H?H,,}?HL\$LT$LL$ LD$(HL$0.HXffffffHxHMb?H!?HT$0H0@HL$(H$H`@IZd;o?HT$HT$H)\HHT$ LD$ Ijt?HL$`I)\¿IV-ҿI`"ۉ׿HL\$8LT$@LL$HLL$PLD$XHxffffffffHxHvٿHS?HT$@Hfffff`@HL$0HT$HL7A`MqH"~kHT$HT$ IZd;O?E1H $Ip= I?5^I#@HLT$ LD$(LT$8L\$HLT$PLL$XLD$`HxÐf~BffVffHYYXYXWXgWgHuffffDfzf&fnHFfv~|fAYDX'fXAYfDHAYXoXwHDYAYDXYXADXOgHuffD"fDZfDRfHF0fnfvfDNfDF f~(fffAYAfXAYEAYXoEAYf`fDhXwEYHDYXADXAYYDXAXAYAXD?AXAoDADWHCÐf fDzfDrfDjHFHfnfvfDffD^ L$fDV(ffDN0fDF8f~@fffffYL$AfYl$fXAYf`Yt$XHXoXwXAAYXAYXAAYAAYXAYXXAAYoAXAYYXAEDXADoHAEDffffffffff"~UffVf^fnH YYXYXWYX_XoW_oH uÐfDfDJf.fvHF f~fDFffffAYEX/fXAYf`fDXAYEYXwEDYXH DXGADYAYDXADXYAXED/DgXOoH TffDfDRfDJfHF@fvf~fDFfD~ fDv(fDn0fDf8fffffAYAfXAYfXAYf`fhXwAYH XEYDXGXAYXAAYXAYXAAYwAXYADXAAYwAAXYEDXADwH AD fffffffffD"fDZfDRfDJHF`ffvf~fDFL^ LV(LN0LF8HV@fD~HfDvPfDnXfffXf`AYfhfL\$fD$XHT$AYH XwAYXEYAYDXGXfL$LT$AYXAYXAAYfD$LL$AYXAYXXfL$LD$AYwAYXAAYXfL$L\$IAYXAAYAXwAYXfL$T$HT$LT$XwH ft$D|$DLT$LL$f|$Dt$DLL$LD$fDD$Dl$DLD$qfffffffffffAWIIfWAVDDAUAATAUHSEp IHfA D$8LfE{fEs|$E)HL$fEkD|$G<6AL$D$|$fD"fDZfDRE|$D4B4AAD)AAD|$EF<6Dt$C4[EC Et$A4ADt$)DD4M4DIAL$IƋD$D\$AL$ȉD$DAB D)D,1L$ĉT$ċT$DL$ԋL$DT$AT$ȺL$ЋML$D AAAE!AE AA D AE!AAAD!D\$AAL$Dl$A ЋT$E!AD ׁD!D$AL$E!E!D LT$!AL$AL$L$HL$AIcBL\$HFlF<FtFTE)D)AE)E)HD$DMDE}Dm HL$[]A\A]A^A_ffffffAWIIfWAVDDAUAATAUHSEp IHfA D$8LfE{fEs|$E)HL$fEkD|$G<6AL$D$|$fD"fDZfDR\<,AfffE1l$D$E1t$D$DDDD!D!E*DE*DDfWH|$H GIfMHD\D\L\|HEEJAzIAYD$DEYfAJ~EYfAR VAYfAZ(HA**XA*AYXAYXADAXfDD$DYD$D\$d$D$AXEEYDAXEDYAXfDD$DYD$DT$AXEAXEYEDDYAXAXXJd$D$A5D,l$D$0fffffl$D$AD,t$D$t$D$vTDDDD!D!E*DE*D*D!DHL$HcQfE@,fff[]A\A]A^A_ÐAWAVAUATI1USAH D$8DzDrD$)L$H|$IxL$L$D$d$؋ E1H|$19Ɖ\$LL$HD$D$D$L$D$ffE,$S l$DKDSD$ȍ RADl$D\B,AC D)EFlAl$GIDDEAl$A)A, C RGAAL$T$D|$кEAD$IL$Dt$EAA AAˍ )ADA DD؃AADAE!!A AD Al$؁ T$؃DT$Ll$D!HD$AAL$D!!D ΁L$A!L$L$DHcHl$E\MfDBHD(DL(T(HD$E)D))D{sH L$:[]A\A]A^A_fffffffffffAWIAVAUAATAUHSEp IHD$8LE)HL$G<6DArf%6,fWAfffff1f.E1f.ADDE!D!HD$AD*ADA*DfWAIcD HCIfDMH\\L\DHAAJABIAffVfFHf.Nf.̿A,f.=f.A/D,f.*f.vZADDD!D!*DA*DA*DD!DHL$HcQfE@fffD,[]A\A]A^A_ffffffffffAWAIAVAUATUHSEp IHD$8E)HL$C6DT$f-$5,fWAE1f. E1f.1f.2f.ADD!!A*DA*!*A*DL$DD!DHL$HcQfEHA\A\TA\LA\DHAARAJABI Aff^fNfFH f. f.AD,f.fff.AD,f.f.ͻ,f.ffEDA!!A*ADA*!*fWADAL$ADDAIcL\$AKD,}[]A\A]A^A_fffAWIIfWAVAUAATAUHSEp IHD$8LfAE)HL$G<6DAkf-2,Aff1f.E1f.DED!E!*DA*DADAfWDLL$HA AIfMH\\L\THAAJARIAYXYYXNXVHf.Ff.ſ9,f.5f.A(D,f.#f.vUADDD!D!*DA*DA*DD!DHL$HcQfE@ D,[]A\A]A^A_fffffffffAWAIfWAVAUIATAUSEp HIHD$8f#E)C6HL$DAT$f51,A1f.1f.E1f.*DDD!D!**D!A*fWDL$DDHcH|$OfAEHA\A\LA\TA\\IAAJARAZI AYXYYXNYXVX^H f.f.ƽ,f.fff.λ,f.fff.AD,f.f.v]DDD!D!**D!A**DL$DD!DHL$HcQ,fffff[]A\A]A^A_fffAWAIAVAAUMATIUHSAH \$@T$8MHf.fvA)ωȉL$HcDAHLf~HLL$D$HifDARfD.,fEWAffffffD.E1fD.E1fD.DDDD!D!A*L$A*fWL$DHL$HE|$D4B4AAD)AAD|$EF<6Dt$C4[EC Et$A4ADt$)DD4M4DIAL$IƋD$D\$AL$ȉD$DAB D)D,1L$ĉT$ċT$DL$ԋL$DT$AT$ȺL$ЋML$D AAAE!AE AA D AE!AAAD!D\$AAL$Dl$A ЋT$E!AD ׁD!D$AL$E!E!D H\$!AL$AL$L$HL$AMcBSfL\$HFlF<FtFTE)D)AE)E)HD$DMDE}Dm HL$[]A\A]A^A_ffffAWIIfWAVDDAUAATAUHSEp IHD$8fA LfE{fEs|$E)HL$G<6DfEk|$AL$fD"fDZfDR!,!,AAl$D$At$D$ADAE*E*AD!D!A*DDDHL$Hc<@}HD\D\L\|HEEJAzIAYD$DEYfAJ~EYfAR VAYfAZ(HA**XA*AYXAYXADAXfDD$DYD$D\$d$D$AXEEYDAXEDYAXfDD$DYD$DT$AXEAXEYEDDYAXAXX5d$D$A#D,l$D$l$D$A D,t$D$ffffft$D$vgADAE*E**D!D!DDT$ԋT$D!DHcHT$E@,fff[]A\A]A^A_ÐAWAVAUATIUH1SAX D$8D}DuH|$IxD$)\$D$LL$\$H|$ЉL$1L$D$D$d$19D$DmD$D$D$D$D$ffffE$DU C4D]]G RADD$[DD D|$EA)C DG[DDED$AA ADD$Dt$EDDA)CTDAD$AAI D\ L$D$Dl$A݋\4E!AE A!E)A EEɉA #D$tD#T$HT$L$! L$A⍏#L$AL$ADAHD$LcA4@0HT$DLL$HD$B4RC\Q)CtQA)ۉMD]D)}H L$a[]A\A]A^A_ffffAWIAVAUAATAUSEp IHD$8E)HL$G<6DAfI,f%9,Af.Af.DA*A*D!D!DDDHL$Hc,*Dm@/HA\A\LA\DHAAJABIAffVfFHf.@f.̻6,f.2f.A(D,f.#f.vgDA*A**D!D!DDT$T$D!DHcHT$@ff,[]A\A]A^A_fffffffffffAWAIAAVAUATUSEp IHD$8E)HL$C6DAT$ f%e,f-U,AfffAf.0Af.@f.Qf.AA$DA*A**D!D!* L$D|$L$D!d$DT$D!DHcHT$HA\A\TA\LA\DHAARAJABI Aff^fNfFH f.f.AD,f.fffff.AD,f.f.ͽ,f.ffADA*A**D!D!A$D!DL$DDHL$Hc *,Pf[]A\A]A^A_fffAWIIfWAVAUAATAUHSEp IHD$8LfAE)HL$G<6DAf%,f-,Af.Af.DDA*A*E!D!ADADDHL$AIcD *CIDMH\\L\THAAJARIAYXYYXNXVHf.1f.ſ',f.#fff.AD,f.f.vjADA*A*A*D!D!DDAT$T$D!DHcHT$E@fD,[]A\A]A^A_ffffffffffAWAIfWAVAUATIUSEp HIHD$8f#E)C6HL$DT$ f-,f5,AAf.f..Af.>ADAA**A*!!⍏!DL$DDHL$Hc *A$HA\A\LA\TA\\IAAJARAZI AYXYYXNYXVX^H f.f.AD,f.ffffff.ο,f.fff.AD,f.f.vzADA**A*!!* AL$D|$L$!d$DT$!DHcHT$f,[]A\A]A^A_fffffffffffAWAIAVAAUIATMUHSAH \$@T$8MHf.fvA)ωȉL$HcDAHLf~HLL$D$HifDAefD ?,fD.,AfffD.AfD.AfD.+ADAA*A*AD!D!A*L$L$DHL$Hc<A}HA\A\TA\dAAAAARIYYAbYI9X.XvX~}]HcLIHfD\IEADYDYFX$YF$I9AXKEX[AKE[|HA[]A\A]A^A_fA.ADAA*A**D!D!L$L$T$T$D!DHcHT$AE@fA.A1D,fD.,fA.A!D,,SffffffffAWIAAVIAUATIUSAH l$@T$8IxD$)L$f6ȉL$L$Hcf~fDFfDNH H|$D$AI ܅LifDQfD,fD%v,AfD.MfD.\fE.AfE.uL$EA***D!E!AA*⍏D!L$L$L$AL$AHL$DLcA AHA\$A\\A\TA\lAAEEʺA"AZARIYYDYAjDYI 9X6X~DXFDXN}mHcHHfElEEADYDYDX<DYYD<H9DXsXKDXkDsKDk|H AZ[]A\A]A^A_fE.AL$**A*A*D!⍏D!L$L$L$D$AT$D!AD!L$HcHT$jfE.AE,fA.,fD.fA.,E,(AWAIfEWAAVABABEEABAUAEAATIUSEp HIHD$8f fD{DL$E)C6fDsHL$DfDkAT$L$DL$DL$8 ,* , t$D$A|$D$ADD$D$DAD*E*E*D!D!AD!DL$DDHL$HcD*A$HE\E\\E\dE\LIEEZEbEJI YD$DT$fAJ EYDFfAR(EY~fAZ0^HA*EYfAb8A*EYXfD$T$*D*AYXAYXAXfDT$DYT$\$AXfDT$T$EYAXEDYAXfDT$DYT$\$AXfD\$T$AXEEYDYAXfD\$\$fD$L$D$T$D$\$D$AXEYEDYAXfDd$DYd$\$d$D$l$D$EXEXEXEX`l$D$ȻN,t$D$Jt$D$A8D,|$D$3fffff|$D$AD,DD$D$DD$D$~ADD*E*E*D!D!D* AL$D|$L$D!d$DT$D!DHcHT$A,fff[]A\A]A^A_ffffffAWIE1AVAUATUSIhAX D$8E1D$LL$)\$Hl$HՉًuH|$DM}L$L$D$1҉t$u d$1A9\$D$D$D$D$DL$|$t$cD$D$D$D$D$fffff]D\$A7DeD$DmD [CADAA**A*!!㍏!DL$DDHcH\$K*fA$HA\A\LA\TA\\IAAJARAZI AYXYYXNYXVX^H f.f.AD,f.ffff.ο,f.fff.AD,f.f.vzADA**A*!!* AL$D|$L$!d$DT$!DHcHT$Jf,[]A\A]A^A_fffffffffffAWAIAVAAUIATMUHSAH \$@T$8MHf.fvA)ωȉL$HcDAHLf~HLL$D$HifDAgfD +fD+AfffD.AfD.AfD.-ADAHD$A*A*D!D!AL$A*L$DHcD#t$L$L$AL$AAHL$A<Lt$HcA^fHT$DLd$HD$HE|$D4B4AAD)AAD|$EF<6Dt$C4[EC Et$A4ADt$)DD4M4DIAL$IƋD$D\$AL$ȉD$DAB D)D,1L$ĉT$ċT$DL$ԋL$DT$AT$ȺL$ЋML$D AAAE!AE AA D AE!AAAD!D\$AAL$Dl$A ЋT$E!AD ׁD!D$AL$E!E!D LT$!AL$AL$L$HL$AIcBfL\$HFlF<FtFTE)D)AE)E)HD$DMDE}Dm HL$[]A\A]A^A_ffffAWIIfWAVDDAUAATAUHSEp IHD$8fA LfE{fEs|$E)HL$G<6DfEk|$AL$fD"fDZfDR&++AAl$D$At$D$ADAE*E*AD!D!A*DDDHL$Hc<f}HD\D\L\|HEEJAzIAYD$DEYfAJ~EYfAR VAYfAZ(HA**XA*AYXAYXADAXfDD$DYD$D\$d$D$AXEEYDAXEDYAXfDD$DYD$DT$AXEAXEYEDDYAXAXX4d$D$A"D,l$D$l$D$A D,t$D$fffft$D$vhADAE*E**D!D!DDT$ԋT$D!DHcHT$fE@,fff[]A\A]A^A_ÐAWAVAUATIUH1SAX D$8D}DuH|$IxD$)\$D$LL$\$H|$ЉL$1L$D$D$d$19D$DmD$D$D$D$D$ffffE$DU C4D]]G RADD$[DD D|$EA)C DG[DDED$AA ADD$Dt$EDDA)CTDAD$AAI D\ L$D$Dl$A݋\4E!AE A!E)A EEɉA #D$tD#T$HT$L$! L$A⍏#L$AL$ADLL$AHD$LcA4HT$HD$Df0B4RC\Q)CtQA)ۉMD]D)}H L$`[]A\A]A^A_fffAWIAVAUAATAUSEp IHD$8E)HL$G<6DAf+f%+Af.Af.DA*A*D!D!DDDHL$Hc,*Dmf/HA\A\LA\DHAAJABIAffVfFHf.?f.̻5,f.1f.A'D,f."f.vfDA*A**D!D!DDT$T$D!DHcHT$f@,[]A\A]A^A_fffffffffffAWAIAAVAUATUSEp IHD$8E)HL$C6DAT$ f%+f-+AfffAf.0Af.@f.Qf.AA$DA*A**D!D!* L$D|$L$D!d$DT$D!DHcHT$fHA\A\TA\LA\DHAARAJABI Aff^fNfFH f.f.AD,f.ffff.AD,f.f.ͽ,f.ffADA*A**D!D!A$D!DL$DDHL$Hc *,Pf[]A\A]A^A_fffAWIIfWAVAUAATAUHSEp IHD$8LfAE)HL$G<6DAf%O+f-?+Af.Af.DDA*A*E!D!ADADDHL$AIcD *CIfDMH\\L\THAAJARIAYXYYXNXVHf./f.ſ%,f.!f.AD,f.f.vjADA*A*A*D!D!DDAT$T$D!DHcHT$fE@D,[]A\A]A^A_ffffffffffAWAIfWAVAUATIUSEp HIHD$8f#E)C6HL$DT$ f-=+f5-+AAf.f..Af.>ADAA**A*!!⍏!DL$DDHL$Hc *fA$HA\A\LA\TA\\IAAJARAZI AYXYYXNYXVX^H f.f.AD,f.ffff.ο,f.fff.AD,f.f.vzADA**A*!!* AL$D|$L$!d$DT$!DHcHT$f,[]A\A]A^A_fffffffffffAWAIAVAAUIATMUHSAH \$@T$8MHf.fvA)ωȉL$HcDAHLf~HLL$D$HifDAgfD +fD+AfffD.AfD.AfD.-ADAA*A*AD!D!A*L$L$DHL$Hc<fA}HA\A\TA\dAAAAARIYYAbYI9X.XvX~}]HcLIHfD\IEADYDYFX$YF$I9AXKEX[AKE[|HA[]A\A]A^A_fA.ADAA*A**D!D!L$L$T$T$D!DHcHT$fAE@fA.A.D,fD.)fA.AD,,SffffffAWIAAVIAUATIUSAH l$@T$8IxD$)L$f6ȉL$L$Hcf~fDFfDNH H|$D$AI ܅LifDQfD+fD%ּ+AfD.OfD.^fE.AfE.wL$EA***D!E!AA*⍏D!L$L$L$AL$AHL$DLcA fAHA\$A\\A\TA\lAAEEʺA"AZARIYYDYAjDYI 9X6X~DXFDXN}mHcHHfElEEADYDYDX<DYYD<H9DXsXKDXkDsKDk|H AX[]A\A]A^A_fE.AL$**A*A*D!⍏D!L$L$L$D$AT$D!AD!L$HcHT$hfE.AE,fA.,fD.fA.,E,(fffffffffffAWAIfEWAAVABABEEABAUAEAATIUSEp HIHD$8f fD{DL$E)C6fDsHL$DfDkAT$L$DL$DL$+z+ t$D$A|$D$ADD$D$DAD*E*E*D!D!AD!DL$DDHL$HcD*fA$HE\E\\E\dE\LIEEZEbEJI YD$DT$fAJ EYDFfAR(EY~fAZ0^HA*EYfAb8A*EYXfD$T$*D*AYXAYXAXfDT$DYT$\$AXfDT$T$EYAXEDYAXfDT$DYT$\$AXfD\$T$AXEEYDYAXfD\$\$fD$L$D$T$D$\$D$AXEYEDYAXfDd$DYd$\$d$D$l$D$EXEXEXEX^l$D$ȻL,t$D$Ht$D$A6D,|$D$1fff|$D$AD,DD$D$DD$D$~ADD*E*E*D!D!D* AL$D|$L$D!d$DT$D!DHcHT$A,fff[]A\A]A^A_ffffffAWIE1AVAUATUSIhAX D$8E1D$LL$)\$Hl$HՉًuH|$DM}L$L$D$1҉t$u d$1A9\$D$D$D$D$DL$|$t$eD$D$D$D$D$fffff]D\$A7DeD$DmD [CE|$D4B4AAD)AAD|$EF<6Dt$C4[EC Et$A4ADt$)DD4M4DIAL$IƋD$D\$AL$D$DA B D)D,1L$T$DL$̋L$DT$DT$L$ȋMDT$EAL$ٻ A!ٻA ѺAA DAA AE!DуAE A!ALl$L\$AD D!L|$؁ IcMcHT$CB)LcLcG,>EH\$DDЈLl$HB (F|(Ft(F\(AHD$A)E)D)E)DMDE}Dm HL$[]A\A]A^A_ffffAUfWIATDDUSf D$(IhfDyHfDqfDi|$|$|$LLL$D$fD"fDZfDRz+fffE1l$D$1t$D$IcE*D*DDUHcATUfWAA$AC@fDHE\E\LA\|HEEJAzIYD$DEYfAJNEYfAR VAYfAZ(HA**XA*AYXAYXADAXfDD$DYD$D\$d$D$AXEEYDAXEDYAXfDD$DYD$DT$AXEAXEYEDDYAXAXXZd$D$AED,l$D$@fffffl$D$$,t$D$ t$D$vAAIcE*D*DUHcA*ATUIcATf@fffD,[]A\A]ffffffffffAWAVAUATI1USD$8 DzDrH|$IxE1LL$D$D$D$L$HHH|$HH19H\$Hl$HD$fE,$S l$DKDSD$ȍ RADl$D\B,AC D)EFlAl$GIDDEAl$A)A, C RGAAL$D$ԉD|$E AT$L$Dt$EAIHl$Aˍ )ADA DރAAAE!D!E E AAA AAHD$HT$DLcAAIcD!FTmLl$D DHLcB,JLL$EABfE]HFBLBTHD$E)))D{sH L$N[]A\A]A^A_ffAUHATUSD$(IhHLLFf%+fWAf1f.E1f.Hc*A*D\UIcATUfWAA$AC[fDHA\A\LA\DHIAHAffVfFHf.df.̿W,f.Sffffff.A?D,f.:f.vfDn+fEWɉffffffD.kE1fD.~1fD. IcA**EDUHcAVfWAAAC@fDEHA\A\TA\dAAAAARHYYAbYID9X)XqXy}bfffffLcK4[HfDHEADYDYDX$1YD$1HD9XNDX^ND^|H[]A\A]A^A_fA.vwAIcA**ADUHcA*AVIcAWfE@fA.AD,fD.fA.t,kD,ffffffAWIAVAUATIUSMp\$@T$8f6f~fDFIHcIMfDNH|$HE|$D4B4AAD)AAD|$EF<6Dt$C4[EC Et$A4ADt$)DD4M4DIAL$IƋD$D\$AL$D$DA B D)D,1L$T$DL$̋L$DT$DT$L$ȋMDT$EAL$ٻ A!ٻA ѺAA DAA AE!DуAE A!ALl$L\$AD D!L|$؁ IcMcHT$CSADMLcLcG,wFZH\$DDfLl$HB (F|(Ft(F\(AHD$A)E)D)E)DMDE}Dm HL$[]A\A]A^A_ffAUfWIATDDUSD$(f IhHfDyfDqfDi|$|$fD"LLL$fDZfDR+w+Al$D$t$D$LHE*H0H0D*H6H6AD*B(A*AA$AC@DHcE\E\LA\|HEEJAzIYD$DEYfAJNEYfAR VAYfAZ(HA**XA*AYXAYXADAXfDD$DYD$D\$d$D$AXEEYDAXEDYAXfDD$DYD$DT$AXEAXEYEDDYAXAXX<d$D$A*D,l$D$%l$D$,t$D$t$D$vRALE*D*H0A*H6(HH0H6B(LH0H6B RD,[]A\A]fffffffffAWAVAUATIUHSIXD$8E1D:DrH|$LL$D$HHH\$HHD$H|$1HL$19D$DjD$D$D$D$D$fffA$DU C4D]]G RT$[DD D|$EA)C C[DAT$AA T$Dt$EBD)CTAAD$ DT AID$Dl$A݋\4E!AE LT$A!E)A AHD$ЉH H0D\H6LH0H6D!AB D DHD$HT$HH0H6AˉDEDLL$HD$FHD$BPG\QCtQ)E))MDE}H L$w[]A\A]A^A_fffffffffAUHATUSD$(IhLLpfa|+f%Q|+Af.Af.HL*H0H0A*H6H6D*B(*AA$AC[DHcA\A\LA\DHIAHAffVfFHf.Hf.̻>,f.:f.A0D,f.+f.vVAH*A*H0A*H6(LH0H6B(LH0H6B RfffD,[]A\A]fAWHAVAUATUSD$8M`M$M$M$ f%z+f-z+Afff.f.Af."HH*H0H0*H6H6A*F"B8LH0H6*AB0AAADAIcA\A\TA\LA\DHQIAH AffVf^fFH f.f.ͽ,f.ffff.ջ,f.fff.AD,f.f.vkAH**H0A*A*H6B HH0H6B8LH0H6B0LH0H6B(ˆHcD,[]A\A]A^A_fffffffffAUfWIATUSD$(IhHfLL}f%~x+f-nx+f.Af.HL*H0H0A*H6H6D*B(*AA$AC[DHcA\A\LA\THAAJARIYXYYXNXVHf.:f.ſ0,f.,f.A"D,f.f.vWAH*A*H0A*H6(LH0H6B(LH0H6B RffffD,[]A\A]fAWfWIAVAUATUSD$8M`Hf!M$M$M$ f-v+f5v+f.f.Af./HH*H0H0*H6H6A*F"B8LH0H6*AB0AAADAIcA\A\LA\TA\\HAAJARAZI YXYYXNYXVX^H f.f.ƽ,f.f.ο,f.ff.AD,f.f.vkAH**H0A*A*H6B HH0H6B8LH0H6B0LH0H6B(ˆHcD,[]A\A]A^A_fffffffffAWIAVAUATMUSDL$@T$8MhHf.fvf~IcHE1fE.TD\$pDT$lLLǺD$EPExA@HL$xL$薖D*T$pD*\$lAEE1E*A*DD$tD$IDHE\E\\E\dE\LDD[DcDKH D9D$tL$ffHĈ[]A\A]A^A_f.-v>+D$pD,Dt$pfD.fff.5H>+D$lD,D|$lfD.ffff.=>+AD,fE.fD.=+At$pL$lL@$AHExEpHL$xLL$0D*T$pD*\$lAEE*E*D$tIŋ$L$HE\E\\E\dE\LDD[DcDKH 9T$tfEW"fE,?ffffAWAVAUMATIUHSHHf6f~LD$0Lc$$fDFfDNJH{fDSH^ H|$ LD$L>T>HD$8E)A))DmDe]H L$THX[]A\A]A^A_fffAWAVAUIATUHSHHxf9ffRfJD$\fqfiLD$hfa$LL$`9T$\|$P\$fWT$t$Hl$@L$d$8DDD|$ |$(|$0HD$tH$E1fD.E1fD.L$$D|$tHEt$AD$HL$hLE*E*D]LT$`E1LD$`A*D$\Hŋ$DC[HE\E\LA\|DDK{H9|$\#fT$PAuYT$0fL$HYL$fDkfD|$@DYD$8*fDt$PAMDYt$(AUIfDc f\$@XEYfT$PYT$ *fD[(*DYL$8AYY|$8XfL$HAXYL$fDt$Dl$XAXfD|$@Dt$0XAXfDD$HEYDYD$AXfD.AXfDD$D\$XXDD$ AXfDL$Dd$DL$(f.%/+AD,fD.f.-/+AD,fD.ffffff.5/+AH$D|$tHDpD`HL$hH<$E*E*UL|$`A*D$\HD$fEWҍRHE\E\LA\|DDK{HD9t$\Hx[]A\A]A^A_ÐD,ZffffAWAVAUATIUHSH8f.fvLD$(LcD$xT$pLL$ f~JfKH{H^H|$L$]HL$4T$HL$fWf.E1fWf.E1fWf..L\$D|$4LEsACHL$(Lq~A*A*E$LL$ 1HT$ *CRHA\A\T\dfl$ft$]f|$UeIH;T$xYYX+YXsX{}dL|$LcO,vHfEIEEDYDYFX+DYF+I;T$xEXMEXEEMEE|HL$H8[]A\A]A^A_f.=-+ALL$D|$4LEqEiHL$(L0}A*A*E$H|$ A*C@H\\T\df.-,+A/D,fWf.*f.5|,+AD,D,UfffffffffAWfWAVAUATIUHSHH8f D$T$p9T$LD$(LL$ L$HD$4HD$E1f.E1f. LL$D|$4HEqAAHL$(L{A*A*DEH|$ 1HL$ *D$HŋT$pC@H\ \T\\ S[H9T$YL$fD$YT$AX $AXT$YAXD$If.!f. ++AD,f. f.*+AD,f.f.*+AHD$D|$4HDpDhHL$(H|$zA*A*UL\$ A*D$HDT$pfWRHA\ A\TA\\ S[HD9T$H8[]A\A]A^A_ÐD,_ffffAWAVAUATIUHSHH(D$`LD$LL$HL$$AH $fD$ fWf.E1fWf.|$ H$H@|$$Dx@HL$H<$y*T$ A*UHt$1*RHT$H\\L\DHKCHAfA $fWfAT$fAD$If.4f. (+D$ &D,DD$ fWf.fffff.(+AD,fWf.f.(+AD\$ L$HLD\$$EzEjHL$x*T$ A*DMHT$A*CIH\\L\DHKCHAH([]A\A]A^A_D,lAWIAVE1AUE1ATE1UHS1Hh R$LD$XLL$PH|$HD$8D$<L$4MD$@9D$DT$0D] L$,JHt$dD$Ht$ DL$4UBu}DED)C EDRDFADL$DE)DE DT$0DL$(ED vC EWADF ADT$@E)DE DL$,EC D ADEOADL$E1fE.TD\$pDT$lLLǺD$EPExA@HL$xL$FD*T$pD*\$lAEE1E*A*DD$tD$IDHE\E\\E\dE\LDD[DcDKH D9D$tL$fHĈ[]A\A]A^A_f.-!+D$pD,Dt$pfD.fff.5!+D$lD,D|$lfD.ffff.=h!+AD,fE.fD.C!+At$pL$lL@$AHExEpHL$xLL$D*T$pD*\$lAEE*E*D$tI$L$HE\E\\E\dE\LDD[DcDKH 9T$tfEW!E,?ffffAWAVAUMATIUHSHHf6f~LD$0Lc$$fDFfDNJH{fDSH^ H|$ LD$D$f%>*f.Tf.D$ ff.*w,؋|$t$ DL$HL$L$fDL$ fAxfApfAXLL贚*t$*L$AE*D$ *IL$HA\4A\LA\DA\luMEmH AfAf-d*fA^fANfAFI f.f.4*D$,҉T$f%*f.ffff.*D$,ˉL$f%*f.f. *v&D$ f*f.f,ى\$ ffffH([]A\A]A^A_ÐAW1AVIAUATUH1S1HhD]DU$LD$@LL$8H|$0D$$D$(D$,D}9H|$HD$PD$TD$XD$\D$H|$ffffE&} C4DEDMD,ADd$,CIDD,D\$,AA)C D$DG$@DDEfAA ADd$(DT$(EDD4PHt$0DT$DD$E!AA)CE DLD$EAFAAI fDl$HD$$D|$$EDL PHL$@E!A)E EfE` |P! LfAXUHD$0HT$8LL$8D$HD$0DDFBCtAC|AD)A)DT$)MDe]H L$DvHh[]A\A]A^A_fffffAWfWAVDDAUIATIUHSHxfJf1$fiLD$hfafYLL$`f|$(|$0L$fJt$Xl$P|$8d$H\$@T$ L$uHL$pD$HL$fAf%*f.Af-*f.wf.5o*LL$HL$hfD|$pLLfEqfAYŽE*E*E$H|$`*IC@HD\D\L\|DEDM}HL$fD|$XAuDY|$8fDt$PDYt$ fDefDl$HDYD$@*fL$XAUYL$(A]If\$PfDU(AXEYfD|$PDY|$*fD] Y\$*fT$HY|$@AXfDt$HDYL$@XAXEYfDl$ AXAYDd$ AXfDD$XDl$8DYD$0AXXf=*AXfDD$DT$f.XDD$(XAXfDL$D\$DL$0f.%*AD,f%*f.fff.-p*AD,f,fffffHx[]A\A]A^A_ÐAWAVAUIATIUSH8f.fvHcl$xT$pLD$(LL$ f~HHnfKH{H|$L${HL$0T$HL$f *f.Af*f.Af*f.:LD$HL$(fD|$0LfEpfAXLA*A*AUHt$ *fl$ft$If|$RH\\T\dA$YAT$YAd$I;T$xYXmXuX}}mfffffL\$LcO RHfEIEEDYDYFXT DYFT I;T$xEXIEXAEIEA|HL$H8[]A\A]A^A_f.=M*,f.-1*AlD,f *f.gf.5*AYD,OffffffAWfWAVAUIATIUHSH8f D$pLD$(LL$ L$HL$0AHL$D$f*f.Afs*f.T$HD$HL$(H|$LfT$0fDpfX趉*D$A*E $H|$ *ICIH\\L\TEMUHAYD$f%*YL$YT$AXEAXMAXUIf.f.*D$,؉\$f*f.f. m*AD,f\*f.ff.@*v6LD$t$HL$(ft$0LfEpfAXLfff,H8[]A\A]A^A_ffffffffAWAVAUIATIUHSH(D$`LD$LL$HL$ AH $fD$ f*f.Af *f.T$ H$HL$H<$LfT$ fDxfX͇*t$ A*E $H|$*ICIH\4\D\luEmHAfAMf%*fAUfAEIf.&f. *D$ ,ɉL$ f*f.fffff.*AD,f *f.ffff.`*v6L$t$ HL$ft$ LfExfAXLfff,H([]A\A]A^A_ffffffffAW1IAVAUE1ATUH1S1HhH|$0}$LD$@LL$8DEDMD$ D$$|$} 9D$(D$,DD$DL$|$3LT$HD$PD$TD$XD$\D$LT$fffffDEt$E7DMD$DUG$@<4D]DAEgDt$,)C IADd$(D4D$AB DDd$AD)C G$RAADEgADd$$DD$A)ԍC[DAGAAID$ )‹D$DD$DD$DL$DL$DT$D$,HD$DD$(DD$D\$D\8HT$0H|$8DHD$0HDGDtGtGD\GD)E)A)D)ۉMDmDe] HL$DHh[]A\A]A^A_ffAWfEWAVEIEEAUMATIUHSHfJf9$fqLD$xfifaDL$0ffRDL$8L$fJ|$ht$`DL$@l$Xd$PDL$H\$(T$ L$IL$D$ D$tf-x*f.D$pf5^*f.Af=F*fA.wfD.)*ffffD\$tDT$pLHL$xLǺL$fD$fEPfExfAXe6D*T$tD*\$pAE*D*IL$HE\TE\\E\dE\LDUD]DeDMH L$ *fL$hA4$YL$Hfd$`Yd$(fU f\$XDYT$P*A\$fD}(AL$AT$IDY\$PfDm8XYfL$hYL$@*fDu0*D*DYL$PDYd$PXfd$XXfL$`XAYf\$hY\$8YL$AXfDT$`DYT$ Xf\$XAXfDT$hAYDYT$0XfL$(Xfd$`Yd$EXAXfD\$XL$HfDT$T$(Dl$DXEYDT$0DXAXfD\$Dt$EXfD *AXfDd$ D\$8D|$ fD.Dd$@Uf.-*D$tG,|$tf-*f.?f.5*D$p1D,DL$pf5*f.'f.=k*AD,A,3ffHĈ[]A\A]A^A_ffffffffffAWAVMAUIATIUSHHf6f~Hc$$LD$0fDFfDNHHn H{fDSH|$ LD$8T$f *f.D$,f*f.1D$(f*fA.Af%s*fA.T$,D$(LHL$0LLD$D$fT$8fA@fExfAX2*d$,*\$(AEA**fD$ILD$HAAA\$EEA\\A\TA\lA$$YA\$YAT$DYAl$I ;$DYXuX}DXEDXM|H\$ HcHHfDEEEDYDYDXt DYDYDt H;$DXiDXaDXYDiDaDY|H L$HH[]A\A]A^A_fD. *aA,WfD.*A*E, f.5*D$,,t$,fu*f.f.=[*D$(D,DL$(ffffAWfWAVIAUIATMUHSH8f D$pLD$(L$LD$0D$D$$f%*f.VD$ f*f.lAf *f.wf.*c|$$t$ HL$(LD$f|$0fApfExfAXLL/*D$$*L$ AEA**ILD$HA\A\LA\TA\\EMU]H L$YD$f-*YL$YT$AXY\$AXNAXVAX^I f.f.*D$$,ЉT$$f%*f.f. m*D$ ,ɉL$ fW*f.f.=*AD,|,ffffH8[]A\A]A^A_ÐAWAVIAUIATMUHSH(D$`LD$LD$ AD$f%*f.>D$f%*f.Tf.D$ ff.*w,؋|$t$ DL$HL$L$fDL$ fAxfApfAXLL-*t$*L$AE*D$ *IL$HA\4A\LA\DA\luMEmH AfAf-*fA^fANfAFI f.f.*D$,҉T$f%*f.fff.p*D$,ˉL$f%Z*f.f. @*v&D$ f6*f.f,ى\$ ffffH([]A\A]A^A_ÐAW1AVIAUATUH1S1HhD]DU$LD$@LL$8H|$0D$$D$(D$,D}9H|$HD$PD$TD$XD$\D$H|$ffffE&} C4DEDMD,ADd$,CIDD,D\$,AA)C D$DG$@DDEfAA ADd$(DT$(ELD$DT$DD$A)CDAFAAI D$$D|$$EDL4Pt PHL$@E!A!A)E A Ht$0EE fDl$H|PfE`! LfAXgHD$0HT$8LD$8HD$0DT$D$DHD BAt@A|@A)D))MDe]H L$DvHh[]A\A]A^A_fffffAWfWAVDDAUIATIUHSHxfJf1$fiLD$hfafYLL$`f|$(|$0L$fJt$Xl$P|$8d$H\$@T$ L$uHL$pD$HL$fAf%*f.Af-*f.wf.5*LL$HL$hfD|$pLLfEqfAYE*E*E$H|$`*IC@HD\D\L\|DEDM}HL$fD|$XAuDY|$8fDt$PDYt$ fDefDl$HDYD$@*fL$XAUYL$(A]If\$PfDU(AXEYfD|$PDY|$*fD] Y\$*fT$HY|$@AXfDt$HDYL$@XAXEYfDl$ AXAYDd$ AXfDD$XDl$8DYD$0AXXf=c*AXfDD$DT$f.XDD$(XAXfDL$D\$DL$0f.% *AD,f%*f.ff.-*AD,f,fffffHx[]A\A]A^A_ÐAWAVAUIATIUSH8f.fvHcl$xT$pLD$(LL$ f~HHnfKH{H|$L${HL$0T$HL$f A*f.Af)*f.Af*f.:LD$HL$(fD|$0LfEpfAXLA*A*AUHt$ *fl$ft$If|$RH\\T\dA$YAT$YAd$I;T$xYXmXuX}}lffffL\$LcO RHfEIEEDYDYFXT DYFT I;T$xEXIEXAEIEA|HL$H8[]A\A]A^A_f.=*,f.-*AlD,f*f.gf.5v*AYD,OffffffAWfWAVAUIATIUHSH8f D$pLD$(LL$ L$HL$0AHL$D$f*f.Af*f.T$HD$HL$(H|$LfT$0fDpfX*D$A*E $H|$ *ICIH\\L\TEMUHAYD$f%H*YL$YT$AXEAXMAXUIf.f. *D$,؉\$f*f.f. *AD,f*f.f.*v6LD$t$HL$(ft$0LfEpfAXLfff,H8[]A\A]A^A_ffffffffAWAVAUIATIUHSH(D$`LD$LL$HL$ AH $fD$ f*f.Af *f.T$ H$HL$H<$LfT$ fDxfX*t$ A*E $H|$*ICIH\4\D\luEmHAfAMf%_*fAUfAEIf.%f. 5*D$ ,ɉL$ f*f.ffff.*AD,f *f.ffff.*v6L$t$ HL$ft$ LfExfAXLfff,H([]A\A]A^A_ÐUHH}HEUHH}HE@UHH}HE@UHH}HE@ UHH}HE@UHH}HEH@UHH}HE@UHH}HEH(UHH}HE@,UHH}HE@0UHH}uHUE#BUHH}HE@?UHH}HE@UHH}HE@UHH}HE@UHH}HE@%UHH}HE@%UHH}HE@%UHH}HE@%pUHH}HE@%0UHH}HE@%UHH}HE@%UHH}HE@%UHH}HE@% UHHH}HuUMDEDMHE}~}}u}uDž"H} tmHE H8uDž"}~+HE HH8tHE HH8uDžk"}u3HE HH8u%DžH"H}uDž2"}uEELH}tEHEHHUHHEЉE}uHEH UЉE}y]}Dž!}u E}u EDž!E;E}Džw!}xEEȃ EN}xEE= E0E}|EE=Dž !}u}uDž }u}~Dž HMHEH}uDž }SHE HEHEH}uH}Džt H}HM3)ЃuEEE.EHE8xEEEEH} u#HEHHE HU HEHHHE HEHEHEHEHHE+HEHE}~HEHHEEEHUEHUHE}~HUHEE HEHE H}HEEHEHEHEHEEHEHHxE }u Dž Dž}u u HEH E}uHo@HPDž4H@HPDž4H} HE HHHHEHHHHEHHHHE uHHHHE HHxjHEH}H}2Dž HcЋ HHHx0HEH}uH}Džt HHEHHEHHEHEHHEHEHHEHE HHEHE HU HEHHU HHEHHU HHEHHU HHEH HHEHEE0}yHEE,}yHEE(}yHEE$}yHEEEEE=~@EEEEHH<HxHxuH}DžEHHHxHpEHHHpHhEHHHhH`}E*fP^X0ufDžLL;E|LHHHx*LYXf *X,4fHLDžLL;E|_LE)HHHxHP*LYXf*X,4fHLHxfHUE}E*fP^X,ufDžLL;E|LHHHp*LYXf*X,4fHLDžLL;E|_LE)HHHpHP*LYXf*X,4fHLHpfHUHE}E*fP^X(ufDžLL;E|LHHHh*LYXf*X,4fHLDžLL;E|_LE)HHHhHP*LYXft*X,4fHLHhfHUHE P} HEHHEHHEHDž@@;E|6DžDD;E|DžHH;E|HHHHHHxHHHHDHHHpHHHH@HHHhHHHH[HD3H@ HEHHEHHEHDž@@;E|DžDD;E|DžHH;E|HHHHHHxfHHHHDHHHpfHHHH@HHHhfHHHHUHD-H@}E*fP^X$ufDžLL;E|LHHH`*LYXfi*X,4fHLDžLL;E|_LE)HHH`HP*LYXf*X,4fHLH`fHUH E}HEHHEHHEHHEHDž<<;E|Dž@@;E|DžDD;E|DžHH;E|HHHHHHxHHHHDHHHpHHHH@HHHhHHHHHH HDžHUHHH}HE@EHE@ E+EEDžTDžPDžHDžDDž@Dž<Dž8Dž4Dž0Dž,Dž(Dž$MMMEHUEB,EHc[HEH}uHDžhHHEHHHEHEHHHHEHEHHHHEMdMɸlEHhhhddtdpHE@,\\t\#GHcoHHuH}5HDžhGEEEEEEEEHEHEHE l)ЉEl)ЉEl )ЉEEUEE‹EEE}tVMɋ⋅h)EMɋ⋅h)EMɋ ⋅h)E<l)ЉEl)ЉE l)ЉEHEHEDžE`;d|DžEEE\;d|Dž EEEX ;d|7HUEXHEH HXHEH빋\HEHH\d`HEHH`Eddydd9E|'EHH HHH HEEHUE;B(|j EHHHEEHHHEEHHHE }MɋEMɋEMɋ Elh)֋MɋEll0Elh)֋MɋEll0E lh)֋MɋEll0E0EE EEEEEll)ЉEEll)ЉEEll )ЉEEUEE‹EEEEtHcЋEpHH‹EHHHHEHEEtHHHEpHH‹EHHHDžEEEE`HEHEHHDž;d|pt@EHDžDdȉ@ETDž8dA McAHIHE!D!AD!A A A9McG,E,,|GlAE)G4(E1A9ADMcAHID!D!DD HD! A9HcB A /|E1A9D6AMcD?AHIHE!D!AE!A AE A9IcF,E,,|RE1A9FD>A McAHIHE!D!ED!A A A9McG,E,,|AE)EE)EE)E1A9ADMcAHID!D!DD HD! A9HcB A /|ffffffAWAVAUATUHSA$Ӄtw[]A\A]A^A_ÃuLIE1A9IMM}HWL?McLwLoAH 2C C CLA9A ,|Da1DY(HI@A9}f!fYfQfILcET9ED9E,9E|9AE9A*A*AE*A*A\\A\\vDYIcYHEDYf$HYE)ADXfZA\DXfR\DXfJ\A,D)\͙!A!AEE9~LcG 9E([]A\A]A^A_DA LaALWHwLOD)AALL-i(EKcLLE1A9,E2McD.AAHIHID!E!AE!D AD!D A9HcF4 E4/|E1A9D7E*McA AHIHIE!E!A AD!E D!A A A9IcF, E,/|iF,D$D)D$EE)EE)E1A9McAHHIALl$`D!E!AD!D  D;T$\HcD\EfG\e|-AD+\$LE1D+\$LAD+d$LD;T$\D7D.DAMcHHIAE!E!AD!ADE A D;T$\IcLt$`DlEfG,~|AWAVAUATUSHxA$T$\Q Ht$`HL$PT$Lt-" "Hx[]A\A]A^A_LD$PD$Hl$\I0LGHHNHFHwH9l$H}H$`H$H$0H$8H$@H$PL$XH|$H|$1D$D1HH$PD$@L$XL$`L|$P Mw$ E$D$$E D$(L$DD$ AD$$$(EEAEC,E!E!D $D $D,AD! $AAD$AD$AA E D IcH$0H$8AlH$@LcAt $AL=D)؉$A|AE)D A$A)D  AD+l$DD;l$L D؉l$l)D)Ή)D,H$0HD$0fffDD)A9>DAA90C<D)A9D$xD$pADfHcH$0A|H$8HcD 14E)EEA)DH$@F D1D)B4 D9s |$lAfADD)A9DAA9nC<D)A9PD$pD$tDAffLcH$0H$8C|L$@LcA A4$ G $()A)EDAE)D4D9s|$lAAM$)A9ADE9E A)EE9D$tD$xDAjffHcA|L$8L$0HcB4D$$BD$ EA)EىA)DAL$@EAB)ADC4 D9s|$lA|$p~zADaHcL$0H$8A|HcF 4EE)EA)DAL$@EAB )ADC4D9s|$lA|$t}AD)HcL$0H$8A|HcF 4 EE)EA)DAL$@EB4 A)ADC4D9s |$lAff|$x~zADHcL$0H$8A|HcF 4EE)EA)DAL$@EAB )ADC4D9s|$lA~zADHcH$0L$8A|DLcE.DC4D)L$@)ʉG D)ADA4.D9s |$lAffAD$CD)A9rADDt$DD$DD$E1AD$L$0H$ HT$lDڗD$pD$tDA>LcK|HDT$DD$E1AD$L$0H$ HT$lDrLcK|Ht$DE)D$EΉ4$HcI|HT$DE)D$ʉ$LcK|H8\$DD)D$Aˉ$t$DAL$0H$ HT$lD$Ή4$D諔Hl$PE1ۋ]DU(HM@D;\$\fD fyfDABAAHDD8DL8t8A*AA*E9*A\޾\\kDYLcYO$vADYE)IfB!IDXfAL$\DXfAT$\A,D)\ԙ!A!AED9~IcAD;\$\L|$`BfAoHx[]A\A]A^A_HwH|$PLG1;|$\MMLvL&LcLnHH\$`C rfC `fC i;|$\fB [|HT$PB,tJj+L$LAHwLGHHjA|$L=T$LH{(Hc H+L$LLGLOHL|$Pރ|$LIoDt$LL{(KcLE1D;T$\E8AIcD7IIHALd$`A!!AA!A E D;T$\IcfD)fE\|DT$LAD+\$LC\E$E1D;T$\^E0E)DMcIIHAA!A!A!ADE A D;T$\IcLt$`fD,(fG,~|E1D;T$\EE9McIIHALl$`A!A!A!E A D;T$\Icf(fC\e|E1D;T$\E8AIcD7IIHALd$`A!!EA!A AE D;T$\IcfD)fE\|BAD+\$LE1D+\$LAD+d$LD;T$\E0E)DMcIIHAA!A!A!ADE HL$`A D;T$\IcfD,(fF,y|E1D;T$\D6AIcD?HIHALd$`E!D!AE!A E D;T$\IcD\MfE\|_DT$LAD+\$LC\E$E1D;T$\8D>E(DMcHIHAE!E!AD!ADE A D;T$\IcL|$`DlEfG,w|E1D;T$\DAMcHIHALt$`E!D!AD!A A D;T$\IcDd]fG$n|}E1D;T$\oDE8McHIHALl$`E!E!EAD!E A D;T$\Ic\EfC\e|AD+\$LE1D+\$LAD+d$LD;T$\D6E(DMcHIHAE!E!AD!ADE A D;T$\IcLt$`DlEfG,~|ffffffffffAWAVAUATUSHA$T$\Q Ht$`HL$PT$Lt-NA?@HĘ[]A\A]A^A_Ht$PD$HDT$\D9T$HLGLOL_HHHKHsHC}L$xMH$PH$XH$`H$hH$pL$L$MD$DHDŽ$0HDŽ$8D$@D7L|$P1MoD$@E"D$DED$HA8$Lffff$DL$DD$<D$@D$H$Ld$+T$<\$DD$Aщ$LcK|H+T$A,>E|>El>A*D*AA*A*A\\\\uDYHcYHADYf$HYE)DXfRA\DXfZ\DXfJ\A,D)\͙!A!AED9~IcAD;\$\F LD$`fE pHĘ[]A\A]A^A_H\$P1;t$\LCMMM HWL'LcHoLHLt$`A RfC `fA kfC y;t$\fC n|ľHT$PB,tK+L$LAHZLGLOHwA|$LyT$LH-5(HcLH+L$LL|$PLOLWLGI_ރ|$L5Dd$LL5(Kc,LE1D;\$\/A McE"AHIIIALt$`!!A! A!D D;\$\HcfD<fGHT$@EDADH$HT$|HT$@EDADH$HT$|D$$AD$HcI|HHL$@HT$|EDAH $D0D$$AD$QLcK|HHD$@HT$|EDADH$D$$AD$HL$`LcT$XH\$pDIEfF SH$H$H$D$XD\$lHHHD9\$XH$H$H$xL$ALl$0fffffDD)A9sDAA9 C<D)A9 DŽ$DŽ$AA ffHcH$A|HcD JE)EL$EA4KA)AH$AE4JAEA)EEAC4 D9s |$xAf@AE)EAE9A9 A<D)A9$$AAnfffLcH$H$C|L$LcFIFJ$D)D)D$GKDD)-4D9s|$xA@ff$D)A9 DAA9W G A)EAE9J $$AACffffHcA|H$L$Hc4K$AKD$AA)EDA)DL$AEAAAJ)AAEAC4 D9s|$xA$A+HcH$H$ELL$Ic V4WAD\$LC!D!A E>Dd$Dl$<$I~AT$DA9щT$aApAAىB :D!A@ىD!@wApIAAD#$AGHD;L$~D;L$DsT$DA9YApAAIB 8AD!EHA9+ApAAIF$:AE!D'D$$HD;L$DL$AAL$DD)9~ApEEAAD"AE!E!E D9DADD$DHL$8)L$A!HT$8D:L$A!HHH'HÐHH\$Hl$Ld$tDOD;NtH\$Hl$Ld$ffffDG D;F u;uDWD;Vu҃ttuEOFHHv~UHcLcDE1E9}:McEfB ~$EDffffIcE^uAE9|HLu1YEOFHHv~HcLcDE1E9}:IcEfZ~!EDfffIc(^EuAE9|HLufffHHHHÐHHDtDND9OtHDN D9O uD;uDVD9WuAt,Aw AȐt5AfftAu1ɐIfffff fffffffffffffHHHQHÐAWAVAUATUSHDOFDG HT$LnD_oDLw4B EOHcHcDD$Dm'HD%m'HD-m'H|$H\$E1E9E(E(E(HT$Mc19fB ZA(A(A(A)ށDL1AHLHD9MTM }|(1((HcEWAJD((HcDD9D*DXE^AE(DTUDVA(DTUDVA,ljDDfAI|D9GAE9Ll$Lt$L$H1[]A\A]A^A_ffffffffffAWAVAUATUSHDOFDG HT$LnD_oDLw4B E+l'5l'HcHcHD=l'HH|$H\$DD$E1E9YT$DT$T$Dd$T$Dl$E(HT$Mc19fB ZE(E(A(A(A)ށDL1AHLHD9MTM A(1A(((ffffHcWAJD(HcDD9*XĉD^A(D(XDATDUAVD(DATDUAV,DȋDfAI|D94AE9Ll$Lt$L$u0H1[]A\A]A^A_Ll$Lt$L$tzffffffAUATUHSO DGD_WLOFH~C`j'fD=A'E'SE'LcLcD50j'E1E9T$fDd$ET$fDL$T$fDD$E(IcEfdAA(ADDAAHcD(* DXfZD^AZDYD\ADfYXYfDfATfDUfAVffTfUfV,AqAE95LMu[]A\A]1LMtfffffffffffS1HIHPD_LD$PD9}/WHcfAZ.T$ T$ Ch'D9ىT |1D9}iL I:yh'h'Lc1BL Hc(AY,(XTUV,B~ND0ID9|HT$0HFkHP[fffffffffH(HDDOAtD^D9_At DH(fD^ D9_ uD;uD;NuAfwAA1D9I*}LcB*YD9B|At6Aw At@AAft%AuHfffE1iHHHFffffffffffffffHHHHÐH\$Hl$Ld$Ll$Lt$L|$H@HIӸtHtDOD;Nt#H\$Hl$Ld$ Ll$(Lt$0L|$8H@DG D;F uӋ;u͋_;^uŃWDVHHvuEbADэjANNE~aLcLcDJ,N$E1A9}6McEfC ~ EDMcAfBYBuAA9|HLu1AJAHHt$DNэJANT$1D9}LcfC9ZBL|ALcA)ރMcBDBD~EHHzHct$H|$IcMcHT$EL|$H|$Ht$LD$@Ht$J|t$LL$\$HEHAYLH]I$LONL9V(^ HT$DL$H DYDL$D\$T$DD$EDYDD$Dd$I M M8LqHDt$|$I Y|$Dl$Dt$t$EYt$D\$I M M`IM9YI9LrfYHHHH9rHL$LD$AHt$J D!A E>Dd$Dl$<$I~AT$DA9щT$aApAAىB :D A@ىD @wApIAAD $AGHD;L$~D;L$DsT$DA9YApAAIB 8AD EHA9+ApAAIF$:AE D'D$$HD;L$DL$AAL$DD)9~ApEEAAD"AE E!E D9DADD$DHL$8)L$A!HT$8D:L$A!HHH!HÐHHHHÐH\$Hl$Ld$Ll$Lt$L|$H@HIиtHt O;Nt#H\$Hl$Ld$ Ll$(Lt$0L|$8H@DW D;V uDD;uD_D;^uHtADwVHHvLAujAAVfADNDAANALcAfCLcAfC HcfA$؉AAEMcHcDCNHE1D9TLcDF\4DDD9F4B\tBtF\lFlF\dFd~9}BLcDAF\<9F<}'H\<<ǍA9}H\,,LHAP1WjAAVHHt$DNAADN1AAD9ډL$}HcfAD9ZL|ADALcE)݃McBDBD~EHHrLc|$H|$IcIcHt$H|$HT$L|$LL$Ht$J1D!A E>Dd$Dl$<$I~AT$DA9щT$aApAAىB :D1A@ىD1@wApIAAD3$AGHD;L$~D;L$DsT$DA9YApAAIB 8AD1EHA9+ApAAIF$:AE1D'D$$HD;L$DL$AAL$DD)9~ApEEAAD"AE1E!E D9DADD$DHL$8)L$A!HT$8D:L$A!HHH1$HÐH\$Hl$Ld$Ll$Lt$L|$H1HLd$P͹LDD$LL$HL$PH$ILD$pH$H4$H|$0HLILHT$(D$ƉMKDD1˅tjDD$p|$tT$|D$x\$LDD$HL$H|$DT$H;L$\HD||1D9}IcHTLcD9JT|1;$}&DD$\EIcB;$LtHLt|Lt$PD$xLL$9D$XILMt$PL$LHT$`D$XMӋ$9t$XIHHMDLt$PL$IcH<H)HRKH$H$.Hh:[]A\A]A^A_fff;$Lc$JLcAfCL fG fGdfCTHfG\fCfClfF$ fB fGE1D;T$t0Lc$Lc$ J<EwIcA*$AAfDD8ft0C*TAYIAYAYXAYXAYXAYXAYXAYXAXAYXAYXAYXAYXAYXAAYXAXLT$fT$T$]fT$_,AET$fD$AAT]_,CDIE9Dt$|D9t$AA9D Ш[AWE1A9gDIcflf\ EAYAEYA9EYDAYDEYEYAXAYDXAYAXEXXDXAX4EXtA4Et]E1D;T$tHc$T$Lc$ fDD$T$fD|$EwH<IcA*$f|(ft0C*TAYAAYIAYXAYXAYXAYXAYXAXAYXAYXAYXAYXAYXAXLT$fT$AAT]A_,AAE]A_,CDIE9;DT$|D9T$AA9D بVAWE1A9 McDDfBdfBlAEEYA9EYEYAYEXAYAYDXDXGXDXGXLGGL{E1D;T$ttT$Hc$fD|$T$fDT$Lc$ T$Ewft$T$H<EIcA*$f|(f\ C*TAYAIAYAYXAYXAYXAYXAXEEҺ0H|$8ADLL:EZAH|$8ADLLoaoEҺH|$8ADLL&4 FAzDDپDT$(aDT$(H|$8ADLL2EJAZDD$TL$XET$\Ht$H\$H|$@DT$L$$^EҺDD$TL$XET$\Ht$H\$H|$@DT$L$$* AzDDپDT$(`DT$(=DD$TL$XET$\Ht$H\$H|$@DT$L$$V;EҺrDD$TL$XET$\Ht$H\$H|$@DT$L$$&ErA.DDپDT$(4`DT$(DD$TL$XET$\Ht$H\$H|$@DT$L$$;ffffL$TT$XLL$`t$\H|$@E$KeH|$8ADLLT`H|$8ADLLIDD$TL$XET$\Ht$H\$H|$@DT$L$$hDD$TL$XET$\Ht$H\$H|$@DT$L$$WH\$Hl$Ld$Ll$Lt$L|$H1HLd$P͹LDD$LL$HL$PH$ILD$pH$H4$H|$0HLILHT$(D$ƉMKDD1˅tjDD$p|$tT$|D$x\$LDD$HL$H|$DT$D$E$~L$DT$LHt$8D$D$HpEDL$@DD$DDd$L$HT$LDl$Ht$8H|$0L<$AfAfDfFAYYXfL$YXAYXAY'ADHAYEYXXAYAXDXXiHD$DT$D9T$|$HHL$NMLD$M<9|$WD$L$t$9t$H`1[]A\A]A^A_L\$ȃ|$M IMDl$DD+l$T|$AAL|$܉|$AWAVAUATUSHLc^H_F$LcWN DL$|LNH\$D$nL$D$L$DXAYD>DEYEXDEYDXDXEXDX4D4HD9 f"LM9HfEcfEKfEC  DD$ 1AD9{H ffDփf(EAYf$DHEYEYEYAYAXDAYEYAYAXDEXEYXEXAYXXDXDXDX,D,HD9DM9HfAcfA[ DD$ 1AD9H DDf8f,AEYHDAYDDYDEYDAYDXDYDYDYDXEXEXEXDX>EXD>DXDHD9^L)M9fA[H DD$ 1AD9H ffDf0f<HDEYAYEYDDYAYEXYXEXDXXDX,,HD9~&Lc$$$ 1+$D$D$4H$IH|$`$McK,څDD$4=@D$4~Hc|$4HzH;$L$H$D$xL$D$4fEW$HHHD$pD$4$HHHD$hD$D+d$xD$|D;d$4\$4$AN9T$|E܉T$8D$|L$8D$D9l$|L$8$H3tHcL$|H$1HHH$9H$H$}D\$HL$Hc9H |$yLc$D$ELD$H$E1L$A9Hc$L$vIIN IIIcfA fAM1fC,(9KfE fEBfAzfArIIfffffDDff,HcEEYEYLEY9YYYEXDEYDXEXYDXDXYXXaLc$Dcfb1D9K4Lc$DT$Ll$LHMIf.fBLcʃHYDYDYYAXBX X BXlB,LD9N,Nl~9Y&YXDL$ 1AD9$LHfffff$AfD AYӃAfDAYAYLAYAXf$AYYXAYXAYXAAYXAYXAYXf$YXXAAYXAAYXX$LD9DD$ 1AD9H DƃfAAYDf$AYHEYEYAYXAXDDXEYAYAXDEYDXAYAXDEYDXAYEXDEYAXDXD4HD9+DD$ 1AD9H f8f4HAYDAYEYAYDXXDAA(DA((AYAYAYFYXL$YX(AYX(AY'A(D(HAYEYXX(AYAXD(X(X(qHD$DT$D9T$|$4HL$NMLD$M<9|$D$L$|$9|$HP1[]A\A]A^A_L\$|$M IMDt$0D+t$@Dl$AADLl$Dl$fffffffAWAVAUATUSHHGHnD$LcN$D$N LcGHD$8F^Hl$@L$\LIHD$4DD$HD$0B D$d\ t$dt$`T$4D$ T$`t$49t$ D\$\D+$LcIHcLT$t$H|$D\$AD\$D$ L$T$49T$ gL$$tHcl$ HT$@$HHHl$8IHl$(h$H@ILL$|$IHT$hD$$T$\MM9T$$kL$Lc\$`މ+$+$Hc|$HLl$AB ALc|$dL$ABABDNABLc\$4AB`ABp\$AB(ABxH|$IN4\$AB0\$AB8\$AB@\$ABH\$ABP\$ABX\$ABhL$ ZL$H$E1LE9IH|$(Hl$hٔ$LD$hٔ$DZ|$T$|L$tDZt$T$xDZl$IT 1Kt%E$B,G DE$%HcD$0H$A((Y$Y\$|A(Y$*YA(FHXT$xYL$tE(E(YDXF$A(DYD$tHAYXA(AYXL$xAYXA(AYX$YXT$|XY'A(A(AYXAYA(AYXAXXXXBHE9+$D((A(A9A(A(L$A(D)JAD$E(D(DL$|DY$EY(T$x2H(YL$tDYY(EXD((EY.D(HEXEYD(EYDXDXEXEXEXDHAZDT$HHL)L)A9J4D)D$E(D(D\$|DY$EY(DT$x1(YL$tDYDY(EX((AY(D(EXEYD(EYEXDXAXDXEXDH`DZt$H4$E1DZl$E9ٔ$Zt$D$Zl$ٔ$HT$(Dt$T$K|Dl$D$t$ٔ$Dl$D\E$It-D$uEBlG /HcD$0H\$A(D(DYD$Yd$A(Y|$.YA(FHDX\$YD$A(E(YDDXF$(YL$T$HAYDXA(AYAYDXA(AYDXd$YDXA(AYXA(DXAYDXDDD$DYAXE(EYXXXXAXBXbH 1[]A\A]A^A_D$D$Lt$x$MIMHl$x$LlIMYAD+$$$AA+$D$D$D$ffffffffffAWAVEAUATUSH HFHcoD$DF$Hc^$H$LOH$W C @IL$IoHLd$(\$pL$T$l=_D$hD$l9D$hr$Hct$pIc+$!EWH|$HHt$0D$f6fn19AZZ}?1DPD(HcMcDYA9CDD(DYEXDXD|ËT$hT$d;$ |HD$@;T$HN 8MLHL$XD$dLT$ $9t$dNLD$X-D$`L$9\$`]D$ D9|$h|(H$Dl$$l$LAHt$XEDLD4$|$H|$Pl$B2L$LD$AHt$XH|$PELD4$L$DD\$)$t$pEL$xDT$LLDt$tDL$||$t$H|$`Ht$h $DDT$ Dt$5|$tt$xET$L$DDT$pDL$||$4$H|$`Ht$hT$ Ll$DT$AffffH(HAD\$8tt'wAthDH(ÃtLAuACAwDT$HD\$DT$DT$@DT$DT$0D$ADH(EAyfAC뮐H\$Hl$Ld$Ll$Lt$L|$HHID$$tDE@Ņ u-Ex(DiDE9AA uEpE9~CffffH$H$L$L$L$L$HD9t?D9AE9D!tiA9b\$$D$\$L넃AăD uuE9uA9uE11A9}LcO<9N|p|AH?D$,A9Hl$0PfT$pA19}$fWf.uz9}LcfB\pf.zt9ʐXB:HcLct$,HfA ^LpBL019AED!fWE0z=BD-HfA,1f.DEDJ9E!ШtxHcfdpf.tBD-HfA41^tpfB.t0븃fff tsD$\$D $DD$AfD$,A9L$,AED!EtOD$HL$0HT$pAN$ALW>Lct$,H?JT0D$HL$0HT$pAO$ALH8$ALA&!$AL. $ALc5D$HL$0HT$pAH\$Hl$Ld$Ll$Lt$L|$HHHIHT$@I IDŨEHT$0HT$(M E ȨEDK$E9AA DUD9$A$9D$8=~"HcHHD$0H|$0DL$8H|$0Ht$DT$E$GL}E^D^n D ,$dE1A9$IcDDED EETDAADA‰ADADADALD;$~E1D;$wHc$0Lc$<$dHLD$XH$$lH$f\H$H$CHT$XH$CDfWHH$AD;$$McD߉EDGLEG|B)H$t$hHH$H$HT$XQH$TT$tDˋD$lAAыT$p‹D$d|$hƋD$l4 T$tDC4AAыT$pA‹D$d $CT1ɅOH$f$H$1Ht$XCCDL$OL$f wLD$D9$$H$EENA9$DDXhD $dE1A9-DMcDÉGDGL AAADDDAACCtA9~E1D;$Hc$0Hc$<$dHH|$HH$$lH$fXH$H$AHT$HH$ADfVHH$AD;$*$H$IcDT$4DEEEDE\B) H$HHD$8 H$HL$H HHD$8LDDˋD$PAʋL$4L$Tt$TDA DT$4DAʋL$PAD$AT|$4|$4D$41H$OL$4f$H$1HL$HAADH$OH$fNHE1D;$Hc$0D$dLc$<AL fH$f$H$L$AADfBDULH$AE9I$L$IcA,A|B+H$AH AA B SAL$A,A|9E1DOH$fD}$01ɅH$OL$AADfB WLH$'D$D9$$L$EA9$EEOD ب$dE1A9%IcEAAtEDEډDAAAEAAtA9~E1D;$Hc$0Hc$<$dHHD$@H$$qH$f$<HT$@H$fQAADAH$H$H$D;$/$H$IcDL$4DEED ETB) H$HHD$8 H$HL$@ HHD$8LDD͉AʋL$4AAA DщT$4DAD$|$4AT|$4t$41OD$4H$f$HL$@H$1OfNE1D;$Hc$0$dLc$<H$Ht$lH$f$H$fB~AADAH|$H$H$D;$|$H$IcDDEL EDB+H$AHH$B{\DAэ DAADA$AtH$1ۅOf$1OH$fB4zE1D;$Hc$0D$dLc$<AH,?bH$f$H$fB^AH$H$E9AAD$H$IcALDEDAB*H$HH$B[H$TAAʍ3DAAٍ4 $AtH$1ۅOf$1OH$fB4Z L$E1ɅDOfE,HcHA HH$(H$H9$t H$ fffff$LcHcIfvfnfff^ fDF(A9$tfD?fDwfDofD_fDg fDO(fDW0D ȨP $lE1ɃA9ffffIcAAYA9Af\0AYfDD8AYXAYXAYXAYXAYXAYXAXAYXAAYXAYXAYXAYXAAYXAXLAL 9$tLcJ<+D$,$9D$,D;$lHc$<fT$`f\$Xfd$Pfl$HL?1;$H$}LIcLfffH$HcH41L9}Hcf1Y4H9X|;$|\f. $f5fAMAuB MAD;$lH*A<HD;L$0}6Lc$<K $EUIBAD;L$0A*HA,|1;$}Hc$Lc;$IK|E1D;$o}*t$0$HcA1AD;$oI\HI|D$hD+$oAD9$4}Lc$PH$J4YH$$p$DŽ$pL$H$D$h9M$p$4D9$4I_H$$p$0$<9$0H$H9$xt H$x 1Hn[]A\A]A^A_ÃAL$ADL$$t) D$ff/fgD9L$,A9$tD Ш$lE1ɃA9IcDAfDDf\A9DYADYYYDXDXEXEXDEED~U$tL$L$(: D$ff/fgD9\$,A9$tD ШE1D;$Hc$8$lT$Lc$<fDD$T$f|$T$ft$T$fDL$L$DYDYAXAXL\f. fA.X f5fAf5fC^MMAD;$EED AUIcfTIYf\ACT]D*DH D*DYAT$DAX͉T$HcT$EAUIcf\IDfdYACT]DYDD*H D*DYAT$AXDʉT$HcT$AXE$ElAX A\fD.fA.˸,fA.˺,fIcH H$H$5IcwHcSLCEg H$D$E_AOMWH$HHD$$Hc$$1D$D$$L$(L$H+$N;$Cl5O$A\D$E9<YIcAYEYLYXXAXXA\D$D$fA.Ÿ,E1D;$fAfA`f)fqfy8Lc$Dt$L|$T$fDL$ET$fDT$AjN$uDYDYYAXXXLA\fD.Af5LHcA7fAD fEfA\fATHfALfEJ,L<.N >t$|L$L$I $ H$B(A)9t$tDd$4( D$Lc$I%D+$H%MDt$pALd$`Dt$lD$tT$|9T$t L$|+L$t$9tHcL$tL$$HIH$L$H$ L$L $$ L $Lt$`|$pK4MLM1D9}OL$H$A A4$DE&**A*A*HcD9A A|1;t$4}dE1L$AD5IcDD$|;t$4HcALL$$*ALE$I4A*E$N*A,$A*A |1;$ }ADD$4EIcLdB;$ LcN$MdO$LdN$MdO$|Ht$`|$pD$0$N6MM9L$0Lt$(,AHc$D+$Hc$D$IALt$AAHHD$@Dd$LDL$\HT$PfD$fD$1f$ ;t$LfD$(fD$0fD$8fD$@D$D$fD$H$LD$(L$fD fEf{fAsfDSuHcL$xLct$|L$ AH$HcfA\ f|fAtAAEfEDCpfDT Y$AYMAYۉDHAT5Hc*H D*Df$;t$LYY$Xf$AYXAYXXAYAYXAAYXAYXf$A$f$YAYXAAYXAAYXXXAYXXA\fD$PfD$X1fD$`;t$LfD$hfD$pfD$xfDDd$ D\$fD$DT$fD$fEffArfDWHcT$x$Lct$|L$fL$ fT$AYAYYfd$YAYXAAYXAAYXXAYXXXAXL\ gC%L$D$!L$D$f5fAf5fCqM9fD$AYT$ EHcf|AfEDfAtfDT fAl YAYXfD$AYXAYXXAYXAAYXAYXAX\hB%T$D$uT$D$c,Zff5fAfCqM9;$Lct$|O$6*H$fAMM;$HcE0AD5fE,fDD|$D$L$Af5fAMM;$$;t$4}2LcL$|K fffffAD5E I;t$4HE*Dt|1D9}IcfffLtLcD9Nt|1;$ },DD$4EIcffffB;$ LLHLL|LD$(Dt$\HLD$PD9t$0LLMD$(H$ILd$@D$0L׋$9t$0IHJbLD$(H$WD$tT$|9T$t*H|$8H9$t H$萆 1HĘ9[]A\A]A^A_,iIcH<H)H| H$H$A,N|$lL $LL$`|$lL$LL$`ffffffffffAWAVAUATAUSHH)f =%$<$)H$@D$8D$4H$)H$H$~fY=%YȃAHcn^AHcO*bA*^*Z*B D*"D*zHD*Z D*rD*RD*jDN DF$HVHGHD$$ D$$H$(H$ YYYDYDY$DY$DY$DYDYLcH$JD$<D$ADŽ$A$H<C 'AEMcL:A)9$D$NNO,$Hc$W<%+$4 <%$H퉴$ȃ$H$$$$$D$D9$b$$)tLc$H$($8MLL$ L$IH$$MJLM1D9};DEA A6A***fffffHcD9 A|1D9}GE1A4HcIcD$DUD9*AQ<*AV4*A |1;$<}:GHc$LL-fALM;$  Hc.A fD fTfLf,HY$fdf\fEAlEYY$fA|fAt*AAYY$AYDXAYEYAYDXDXDXDXEXDXDXD\ 5%DL$D$DL$D$f5fALM;$ D9}7Lc$O fff.A HcLHD9D*AlE |1D9}IcM LcD9O |1;$<}!GD\$GLeCL$ D$EHD$ȋ$DAA,GEFA\ED$Dt$AAADL$t$AAL$΋L$ыT$ D$1L$AHt$@1EAOfHL$LD$@1ҋD$OT$fA4Ht$DL$|$DT$A\$D\$D8l$B F A<EA4|$D$ MMMLd$@D\$D9\$ ffffD$tAEeE.Dd$ D,$AL$\$EAB#~ZL|$@fAHl$HLT$LL$LL$PL$NtULt$HD$L$T$9T$>Hp1[]A\A]A^A_L\$@1ۅOfAfffffAWAVAUATUSHHLcWHFDf$,N+$,n L$H$Lc^$ID$$D$D$$$L$HOL$0D)ID$(L$ Lt$ HDŽ$L$Ll$(D$Dȉ$$Hl$ HHHD$|$@$,$(=8$,1$(9}HcL$DL9E |DŽ$$9$|"/$D$D9$$+$$tHc$H$E1HHHt$D;l$H$H$}McAD;l$L$ C|Dd$ EfHc$Lc$Dt$ H|$HLD$@Dt$DŽ$D$(D9$H$H$$H$DŽ$$D$,D9$HH,AH$Lc$Hc$$(Dl$ALT$8Ht$0$Dl$Tff$$,$+$L$L$L$HI4D$$Hl$0L$Lt$8L$$HDnAPD$F$AKH$ H$1HL$8OLL$hBBDLD$hfIc$ D$$AAA$DL$ AATAL$1EAOfA$Ld$8H$E1DOfF4fE1D;l$TH|$0DD$HAiH$f$DLt$8L$fCtH$ AIH$E9D&D$ A$Eދ$$ EHl$8L$ D$D$EiH$ DIcAA$EG4DT H$E1DOfD9$DHl$8L$E1DOfE$jE1D;l$TfffHc|$H\ H$ H$ < AWIAVAUE1ATUSHHnFHc~D$tD$pDFDN HcsL$;$nH$x$ IWHCL$<L$3HL$hHE1L$`f %L$8LT$PD$0D$4$ $H$PH$H~ f z9%AAA*^D$tDAL$H1D9}*H$xHcH$`D9* YL$H |փ$t$4@Ã$t ب!$t19}-1H$8LcL$hLc$49JK|Ջ$t19}&L$h$tLcO9HM\|Lc$tH$hDŽ$Lc$4$4$t)$4)$0D$ NL$D$nD$$pID$4$ M$0K HcL$HEADL4‹$ D$DT$,9$L$HT$(I4GHt$0| $$ 9$d$ +$$ntLc$L$PE1Hc$ MMLL$0D;$tHD$pL$@}]HNjD$,IMDTH$hIcH<1D9}%1HcHcE S$ D9A*,|MAD;$t|E1D;T$,DŽ$\}1McAD;T$,K|L|$pDŽ$MM)L\$ D\$(D9$Hc$Ht$hL$(HT$ D$Lt$pHc$\L$h$t9$L$N rIAAEDAGH$L$8H$D$L$$M$D$D\$$$$NH$D$D$D$|$ H$$t$,EPDL$(l$0\$AAEDAGDL$8H$E1H$D$D$D$M,L$H$D|$H$(D$AlE$Dt$DT$D$GTME]ELMD;$HMDEJT$pIM$;ffffH$fKLMAD;$DDAEDH$DEAAA,@H$ T$|$t$IcDAыT$ADA։L$ $x|$ ‹D$AH$ t|$|$ |$ 1Ot$ f31ɅOH$f SLMAD;$fffffD$EtYD\$DT$EE($xDl$AFt C,L$ McC,ACT5fH$(L$L$L$8L$N$SL$($D$D9$xL$L9$ t H$ , 1H[]A\A]A^A_E1DOfDhHcH, H$ H$ qffffffAWAVAUATUSHxBf \$H$pD$H$H\$p~f$Yȃ1*^Hc*Yك~HcNHcWnDN DFHFLwHHD$D$$$H$ HcH$H4$Hl$d$L>DŽ$N $l$`NJL,N$.N<&$L$H$L<$$9$HMLT$h. ,$$$D$D9$ $+$$tHc$Lc$1H$D$dHL$MHHT$hI *D$H$LH$LH$I9H$L$E1fffffIcHcD$DtU;t$A*L$ff,;t$dwH$H,ffffH$fIH;t$d6HcfAD fEfA\fATfALfDHD$xDD$L$ILL$L$H$1D9}rE1fH$IcHcD4WD$;t$DL=72L=y7&L=7D$H$$T$8EIcHl'offfff밐SH1E11IHLT$H|$PH4$D$L u {t HĐ[ÐDD$XDL$\DT$PD\$`H|$hHt$(AL$ A1 بuAwH˥$DLcIAD?DhffDyffffDffffϋ\$ E1E9}cMcACTC ։)) !C!! E9CT|EE9gBLd$L|$Ld$L$-EfD5$fD=Rp$p$$$HcIcHT$HD$D\$E1E9T$fDT$Ld$T$fD\$ET$fDd$ET$fDl$Ht$IcDHD)AN?HDL1D9H=ow7AAAAAAHcZD9ffYffTfUfVffTfUfV,*\DAYXYXBY ZAwE1E9}]McACC ։)) !! E9C|EE9LL$L|$LL$L$t 21fffHHHÐH\$Hl$Ld$Ll$Lt$HHHH )$tHt W;Vt H\$ Hl$(Ld$0Ll$8Lt$@HHfDG D;F uDD;uDWD;VuDADDoL_DfH^AA ȨCD9D9!Ȩt EнAAAI~YMcMcA1D9}>H-$ALcDBH) t!ƁD9C4|LMAu1ABB~McAMc1D9}?H->$A LcDBkH) t!fD9fC4k|LMAuzrMcMcA1D9}3L ׳$ LcB sH)̓A D9fC s|LMAuHH $HL$HL$HL$ ffffffffHHHÐAWEAVAUATUHDSHfDUfDHD|$PEAEAAALcL$HLcHcT$@HcL$8LcN$ffffJ4IIM)L9sxIcHffffffE:fE4fAfG,IM9AAA]A]D]_A_A_A_DDE]rI9sfE"EA]D_AMI9sfA]_MI9sfE4EE]E_EAJ< +e{uDKDED[DU[]A\A]A^A_ffAWAVAUATUSDHHt$HHH|$PHD$ tcH@H|$H uHۺ@E u1xGExR.DfD^~ DVHFtW1H[]A\A]A^A_ffffxEyfffffEx.DfD^~ DVHFuED$DEIcHAD)HEED9HA)HT$0EDAEEA Ȩ$AD!ȨDD|$DÃAf$f $AHD$PHT$H AAoH[]A\A]A^A_ED$DEMcIAL\$`LT$0EEDDL\$8H|$8L$ʰH5Ht$8H|$P"Ht$8H|$H+T1҅GH|$PHl$HHHAHHEAAD,H$D HT$0H$H$F+DL$EH$Dl$$DD$Exf$f$f.v $f$f$f.v $f$f.v $f$f.v $f$f.v $f$ARAHD$PDz$EHT$0Z8E(E(E((((HcMcELHIxH9shHcHVDvDn H(]H9(]A]E(A_A]A(A_E_D(A_D(E(D(rL9sD&E(A]E_E(LnM9sDvE(A]E_E(LvM9sV(]A_D(AJ d$$D$$L|$PLT$HHt$PHL$HHT$PH\$HADD$$D$$LL$PLD$HAl$$D$$^DL$$D$$Yt$$D$$ZDT$$D$$[|$$D$$AYD\$$D$$AX1H[]A\A]A^A_HD$P-$ELT$0D Z((LcMcDJ JMcJTIM9LJ,sDNHffffD.DfLH9A(A]A(A]_(_(rH9sD6E(A]D_A(IIHuT$$D$$HT$PH\$HLL$PLD$Hd$$D$$\$$D$$AYl$$D$$AXAAH$Ht$HHT$0H|$PD$EEH$D$Dd$D$$upLT$PD2$ELD$04[AZ*A(A(((HcIcDL IDHcI9LsZH fffD&D^DVHA(H9A]E(A]A]A(_D_(A_A(D(rMLu\$$D$$Ll$PLd$HL\$PH|$HL|$PHt$HA]t$$D$$A$d$$D$$A[|$$D$$_l$$D$$A_DD$$D$$^CZE$Z$L$L$LT$04((LcHcIcJH HELcHNLLH)I9sALJ<H)DBHcH<n H4HHD$`HD$XHt$PO"AE9~7ffRAD;$ DDOAH AAE9 H\$XIc)9uAljAE9~LT$0EHDODH E EAE9IL|$XIcAAA)A9t&AD;$ 5EUA]AMA1I AÉE9~EUAMA]I |$APLD$(AAA9GHD$XIcы)9t'AD;$ EUE]AMAE1I AAÉA9~AXLD$(EUAME]I AAE9AH\$PIc)9t!AD;$ }pDD_OAE1H AAÉE9~L|$0APDOD_H AAD$yAD$AD$DAD$ffffffAWAVAUATUDSH ZB H|$xHt$pL$lDD$hD$!LrHD$`\$LDbD$HDjD$hD+D$lH$H$HT$PHL$X=Mc͋D$lDl$hD$LIIcDD$LED$DD$ HiVUUUMcHIDO^H DL$ LT$D)9$ DD$@~D$D$ D$<Dt$LD9t$<9HcL$HcH<f HIcHD$DX9DXHcB EXA EWCbEWEWCCC cL\$D*DYW(H$CBD*BDXF*G*EXE*+HcA* C*XG*$EXG*,DYEX,EYDfYH$H$H4$L$EXL$IfEXEYDXEXfEEfETfDUfEVAfEfDTfUfDVA,ʼnIcD$D9Hc$H$MH$f$0f$@IH4M)1ID9H$K4Hc$Lc$E1f%R#f#HHL$E*G*$EXD**>F*AXB*HcDH$H$EXLD$fYH|$Ht$AXA EWCbEWEWCCC cL\$D*DYW(H$CBD*BA EWBTEWEWCBLD*DYW(H$CD*|5EYA *AYD*IcDYEXADXEXEXADTUDVA(DTUDVA,<H$IcHt$L\$LT$D$E9A?L$Hc$E1H$M$ $(MM)IE9H$K42Lc$Lc$I#WLl$E1fHL$A8EWEWH$BD(MABM1IcD*>DYALD*DYEX,EXEETDUEVA(DTUDVA,<H$IcD$E9A??Hc$E1MH$E9H$$ $$H|$8Hc$Lc$E1#WBEEWEWD(A7AL5LAE*FDDYMAIcAE*DYL$EX EXEETDUEVE(DETDUEVA,AALD$8IcBD$E9A??Hc$E1MH$E9H$$ HL$8hLc$Lc$1#WffffCDEIcWD(AMAAD5LAAA*YL$AX,DATDUAV(TUV,A ALD$8HcB$E9A?fH$E1M$ $$$(IM)IE9K 3gHc$Lc$fBE"D.WT WEWA7EDl5LD*DYWAA*FdYMED)LA*McYGXLVL^Hnf#f%Ȉ#1LcDH$fC4fFlCX4L$LcFX,D9ffYCXtDYAXfDfATfDUfAVDfDfATfDUfAVD,F G ^@IcL$H$I ffXf`H9LQLYHiLa 19f-#f5͇#1LcDfFDCY#9M,XMUM]Im"%#1WLcD(F|GY4GX#1WLcD(B|CXLc|$A|$]EKAULET$ACE]Du@E ADEDD AAAE LD E AA LHl$DD$LHt$\$|$O\$9\$DT$1AD9}_DL$Dd$E1EG McLcA/A6D$A\/H0AL/BH 0El/BHD,0DD$D9|D\$D$Hl$Ht$Hl$Ht$AD9\$Hl$Ht$0DT$ET$9T$LcD$Lc|$+|$1;L$}-1fffLt$LcHD$LcGd5E$t$;L$|LD$L|$u[]A\A]A^A_ËT$Dt$RHHD$CvHl$AHHD$Dt$Ht$LcD$Ld$K| H|$D$D$\$HHD$YD)+L$L$\$Dl$IHH)D$CDmHH)D$3|$\$DD$HH)D$C@HH)D$fffffffffAWAIAVAUATUSHcnHc_O T$VHHl$؋n w9Aщ\$DNɉ9DL$N))D$E1Ʌ~DHA11~B1D$McMpLG|$LcHIŋD$ȉ|$O$nLd$HIEODAD)AL$AHE)H<(3fffffGDHD Hq96 AIȋT$ă|$OT$9|DD)AN9=EwDD) %H5 96HD1EIȅt9D9DH86%EEA<E!D!D A8Hl$Ll$A̋|$[]A\A]A^A_UDMAD L ~86DC L He86HA<A8IESAD9|wDD) E)DRA)eAWIAVAUATUS^VOv G\$_ A9ADNىD$9DN)ʋL$)1~PE11~11D$LcMPIcHwHJ,D$AHH0E1E9}vE1E1E1ED߃tIcMc (ALD9}2C4E)fffBH(L(HcƃD9߈TL|ADL$Dl$Dd$Dt$E9|[]A\A]A^A_E~IcE1D$H|$EHcT$LL$McL<*K9M L)LRH1ШE1D)ȃA9}#fffEIcAAA9IcBB|AsA92McLAL)W A9C ~E9} ffEIcAAE9McCB|DT$Dt$DT$A"HL1Ȩu_EAtJA A{A9ffMcLAL)HpAωfAfA A9fG< fCT ~UEAtzA9EcE98McLAL)Hf0fPfx fC4 E9fC|~AA)A\)dAWIAVAUATUSDN_OAPDAFt Ap o A9щ\$DNى9N)DD$щ)1҅~PE11~1 1ALcM`LGMcHIŋD$K,lHIAKPE1D;T$}yE1E1E1ED߃tIcIcDufDKD9}6C4E)fffBHTELEHcƃD9fTCfLC|ADL$EED|$D;T$|[]A\A]A^A_Ë|$~Dl$McD$L|$E1ffMcLL$LcD$JTMN CHLRL1ȨEAt DbAfE!AsA9-IcLAH ?H)DbJAA9E$yALy~E9}EIcAAE9IcAzfAq|EDT$DT$AT!EAt JAfA EcE9fIcLAH H)H6Px@ E9AIADI~cAA)A)fAWIAVAUATUSDN_OAPAiAEύsADNo ANAp \$|$9DNى9N)DD$щ)1҅~PE11~11ALcMc1M`LGHIŋD$K4HI;l$K<}uE1E1E1DEAtMcHcBDE9}6GEE)fffffCAHLIcAE9ىTL|\$EEDt$;l$|[]A\A]A^A_AA)A')/ffffffffAWIAVAUATUSHVOo Dvv D9щNA9DN)D)1҅~PJE11~1E1AHcIhHL(C HcDHwHL0IcL L L rE1E9<E11|$fffff~2DD$EHcǃBBLIcABTBLuADEE9|H[]A\A]A^A_E,D$ E1DHHD$LcL$ IcLD$N,K(OHzAu{L)H1ШLHHt DJfEK9f6IHI)EiA9F,~H9fffffED|$ dv1LO9}1fHc׍6F, BL H9B Fl|@uJDK1D9A6HHH)D9 DjfB fFl~LH[]A\A]A^A_1H9\6LcHL)fDHA 9fGL|0AA)A)fAWIAVAUATUSHDNwOAPDAFl D43o Ap AA9DNى9N)DD$ щ)1҅~PE11~11ALcIpIcLgHJ,PDLnHLfB<D9fB||A1D9}fffLLcL)iDD D9C,|XE1E9LAA1BHcB<Aƒ)HB4D)HB D)HcE9HBB<BtBLBD|A~1D9LHH)D9)DyfB,fF||1D9DLMcL)Dzr*AA A D9G<|ZE1E9NF<LAMcL)fyiffDy A E9fCfG||AA)Ak)sAWIAVAUATUSH8DN_OAPD͉F| DO Ap A9щ\$ NA9AN)DD$щD)1҅~PkE11~1f1AHcMpMcLgDT$ HH4D$ IvHN,IcH OlL L ШRE1D;d$C<E1E1|$ f~LDT$ EAEIcACLCtC|CIcAAfATCfALCfAtCfA|CuAEDt$ D;d$|H8[]A\A]A^A_DD$EDd$Hc)HT$E1HE1HD$IcMcHT$MKN SAMTA@tHD$I4It}19/fffffLcL҃J H)9H HRK KT~EDl$ AIcMcHT$MKN SAMTwAwA19}LcLJ H)9:RCDZ7DZB,DZIDZ#DZDZE1D(AYD(IcEYEY(IcAEXAYE9DX(A4AY|X((EXD(EYDXDXDE1fffDDIcAYfEYIcAEYEE9AYXf|DXfA4AYXEXDEYDXDXDE1D(IcHD(EYEAY(EYAAYIcDXAXA9(AtADZfAHDZWDZW*jDZ*"DZDZ}}D(AYD(IcEYAEY(WE9AYDXA*(tXAY$D$D$EEE$X*L$`'$);$|$);$|$`l"|$`"L$L9$t H$ L;$tLy 1$$@~HcHr HH$LcH$$D$H$L$H$H$D,$N$L$l$D|$LHcH IƸMh$H)$L)$H$D+$H*+$@*D$ )D$)HAwL#EOcMAH$H9$t H$. L;$oL b+$+$H$p$H*DB$@*DHH?AwH=#DLc,IAL$L9$s"$H)Lc$H$$P)D$)L$X)H$)H$$Lc$D$L)AL$L$H$D$D$D$D)K INH$1;$L$}+?Lc;$L$H$C B |DŽ$$9$$HcHc$+$HT$xHH$h$tDŽ$D9$<D$D$D$E)D$Hc$H$H$< E1D;$H$L$L$H,XL$^$$Lc$D$H$\A+$MHD$L$H$$1UD9}LcD9C|+$9}$fffAHcE)E9McBD]A|Hc$$9AL} Lc9C |$D$LL$H$DT$HH$$LDT$H$D;$|H$;$H<(HLAL$H$H$D;$H$L$I,\H$x1;$$E1~Y#5#IcEH$H$\"(L$x??TUVD,AfA;$fE pL$L$HcA A,YYXQ.J(cKC$D$H$$D$Lt$(D$H$Hl$ L$H$H$H$DT$D$D<$yD$H$D$D$D$Lt$(HT$ H$H$DD$H$D$H$D\$Dl$D<$D$H)Hc$H$D$)1L$)H$`$P)L$X)D$8Hc$8$L)AL$@H$ ;$8L$HD$<$4D$D)L$K,H|H$XH$P}1H?Hc;$8H$XL$PHLI |DŽ$0$49$0$Hc$<Hc+$H$HH$$DŽ$,D9$,=D$4D$$A)D$Hc$,L$`H$B<L$@E1D;$L$I,H$H$e$8$Hc$<D$L$ \A+$HID$H$L$HH$1*ED9},LcD9C|+$9} fLcD)A9LcBDC|H$9A*D},Lc9C|$D$8MH$H$PDT$HH$XD$$LDT$H$0D;$|H$;$L(ILH$AH$ID;$L$L$HK,H$1;$8E1fH$XH$PIcI?I?fD+"E;$8LLH$D]E,DH$XL$PHcf fEYEYAXQf.`FXH$)$H)1Hc$$)L$L$X)D$L)LcH$`$P)$XL$J,L$h$\;$XD$TI D$D)H$@N$H$xL$p},?Lc;$XH$xL$pBLC |DŽ$P$T9$P$Hc$\Hc+$H$H҃H$$DŽ$LD9$L$T$$)؉$(Hc$LL$H$8B< bE1D;$H$`H$L$H,^H$0aD$X$Lc$\$(L$@B\ +$MI$$L$L$$,1UD9}ffHcD9A|+$9}%fffALcE)E9IcDDEG|Hc$,$,9AL} Hc9A |$D$XLL$0H$pDT$HH$x$LDT$H$PD;$|L$;$$M(ILAL$H$H$0D;$H$hL$8NdU1;$XL$E1aY##L$xH$pIcEA??H$],;$Xf#52#L$ L$McD$E,E,H$]D,A;$fF[jH$ H$Hc YYXQ.h(4a$)Hc$1D$)H$)H$H$)$)Lcɉ$T;$TND$PH$XH$hH$`D$)O$)H$@O}?Lc;$TC C |YIcLcljt$\HT$hLD$pDŽ$L$P9$LHc$LL$hH$8B< JL$XD$L$H$IE~yH$@$HH$0$P$ID$TLLLT$HL\$@t$ $LLLl$hL$0H$0LT$HL\$@uH$`1H$8;$T;"H-\5L%5A?E1Hc(A$McA E E YYXTUV,HLEHcB ALD$P;$T|$L$P9$LxLD$pH\$hL$`H$XL$\5ufff$,D9$,$9$0|1H$@D$H$D9$0HM$@H$@H$H$0H$D$4D9$0HfD$f$fD$fD$DE;$,D$$G9E*$L$DX5xr")qH$HD)DZLH$D*EZD$D$D\$D\$$D$D$HDZ\ZtD\$\$D\$\$DY$Y$D$E$f$fD$E\DfD$DY$f$E\f$f$AYE\DY$DY$D\DY$AXfD$AYAYEYAYDXf$DXf$DXAAYAAAYAYAXAYY$DXAYXf$DXf$D|$XEf\$XY$EY|$D$AYXAXEXAXY$XAYXAXXfD.$fD$fE.1ffffT$D$T$D$zf.$kfD$fD.V\$D$18\$D$"f.$f$f.1ffffD$D$D$D$f.$f$f. E1ɉ$ D$D)DžDOD9ffL$XH$`)*HcYEDA4AYD9AYAYX\ zn"AX\mn"AXAXY$Y$Y$Y$,H$X,É,A|,A| IE%KL$D$$hL$H$x\$@L$\$0LT$(L\$ L$`LLL$H$PL$XHcLH<$8D$@D$0LT$(L\$ E;$,k$D$E1D95D-5p"D)l$p@f m"T$fD$^\L$D$Y*fDT$fDfATfDUfAVT$fDt$ffWD$pA\D$D$Y_t$D$z^f ll"f$^\L$D$Y7_\$ffWD$pD$D$Y'_fA.vHcADAD9fT$XT$)fl$fD$D*AAAAYAYAYAYAXXAXAXd$D$UOT$D$ZT$D$H\$D$>f$f.f.$D$D$QfD.FD1H[]A\A]A^A_øH[]A\A]A^A_fffffffAWAVAUATUSHL$1H|$D9Ht$DD$DL$}>H$(Hc<H$0=;L$|1HcD~+$@L$Hl$T$LLL T$$H9ʉT$'$PLcT$HcLT$D$H|$Dl$$@Dl$D$McLd$HN DЃ|$|$|$$PAƅD!L|$LK<HGI9w=I^HAAAAEDCDAAL9vH9sHDLVI9s L~CLfHVI9s HBA$X|$t$9t$|$LILL L*|$?|$H[]A\A]A^A_Ã|$|$uׅRLl$$PLK|HOI9"LcLWO$6^LAAAAEDCDAAL9vEH\$HcT$LN$LH)HI9wHLc$PLHBnL։AAADCDDACH9vL9sH>DHVL9s LnCHT$HrL9cD$PL׋$PLT$EB4!M4:Lc$PIcLcLL)H9s6LcIA:H ;A4?LDAȉAAL9rL9s L?BDI:L9sHAH;L9HAD$PLLT$$PEM,:E|$McLMcL)H9s7LcHWHA4:E<>LDADAAH9rL9sHDLwM9s LgCI:L9TH\$L׋$PNLHH9s9IZLcwHDoDLDADEACH9rL9s L'BDH_L9s LwCLM9HG$P@Dž!Dl$E1AE9[IcAB B\FtAADDADDDDAACE9~Dl$AE9IcN4BDNdCJDH[]A\A]A^A_Ë|$1\$$P9LcGlKG|DDADA9~w|$fY"E1E1H$8McH$(Ll$H$0fBLBKtBE1A92H|$HA IcAI*YډD) \,AD)! A9A|HHHÐHH\$Hl$Ld$Ll$Lt$L|$tHt V;Wt&H\$Hl$Ld$Ll$Lt$L|$fffffN ;O uDD;uDND;OuA_D^HHvtWAu~FALU"McELc1D9} f(HcfD9ffW|LLu1T~ALcMcHcL4M\>@IHM$>tLFHOAusL9MsEHMIDI!5I LAH H I LHL9rL9s ALLMj>L9MsAEPIAI L HHL9r룐HHHÐHHÐH\$Hl$Ld$Ll$Lt$L|$HPHtHt W;Vt?H$ H$(L$0L$8L$@L$HHPfffffO ;N uDD;uDOD;NuH_ADWEAAˋ~H\$H^H\$ApB9@D9!t DAAA3E [T"E>#Lc>#>#McLl$Ld$D\$>#>#D$D9|$2T$fD\$H$T$fDd$L$T$fDl$Hl$T$fDt$T$fD|$Hc|$D+L$LT$Ld$AHIIDL1D9}:LcGLCDABÁA?D9BtG |1D9T$fl$H=Z5H5W5EEEAAffffLcfBC*$AYD,A*AYAD9X\YYBX$^YXYXAYBXXYXXBDnE1E9}cAMcDAC| )BL%!!B#T C#4!! E9CLC|D\$D9|$3BDLt$LT$Lt$LT$L$]ESfD5Q"fD=;#W;#Y;#[;#HcIcHT$D\$HD$E1E9T$fD\$L$T$fDd$L$T$fDl$Ld$EEH\$Hl$IcDHAD)HHDL1D9}FLcG\BZ DAAC ?AD9CTG\|1D9H=xW5H59U5EAAAALcfCC*\YAYD,A*YAD9BXX\^YXBXYYXYXXZCxE1E9}GAfIcDA4) !A# ΁! E9ȉD|EE9qLL$Hl$LL$Hl$L$t1kffffffffffHHHÐH\$Hl$Ld$Ll$Lt$L|$HxHH d?#tHt W;Vt#H\$HHl$PLd$XLl$`Lt$hL|$pHxDG D;F uDD;uDWD;VuDADDoH_DfL^(AA ȨCD9D9!Ȩt EнAApAFIcIcAH|$HD$E1E9L-c=#L5<#L=8#H-7#McACэr AL=L$ L$ | D L$ A<A< E9B<|L\$H\$AV1zAkB~McMcAE1E9}TL-<#L%<#H-7#IcAA{эr ALɉҋDA E9f{|MLAud\McMcA1D9}EH-;#L%7#L 6#LcKt{C{A 1ΉATD9fB{|MLAuHT$ HL$ HL$(HL$0HL$8sUHHHÐATE1UHSH Ht&Ht!DN_AAtE9t H []A\ËwL+E#McMAAt~wjfffE1E9}-McMcfIcMAHcI)LIHDE9H|At:HrH []A\fffD9_tDuE1A댹9}ffL$Hc9L|AɸZAfffKAfff"AfffAfffAWAVAUATUSDH@Dt$xHT$0H|$8DD$,DAރKA E_HcC6HcHHD$DL$H\$T$(E1E9Hc|$(McDd$,D|$,LC 6HAHt$L$H|$ AH\$8LD$0McL$Hl$ 1LD9KL"NtL1H:M LILN$LaL2HKLT$EE AsAA AH E _EHDYAA A A E D$@D$DD$HL$@D$LD$PH$HD$TD$XH$PD$\H$XL$L$`L$H$hL$H$pL$H$xH$L$H$H$H$H$H$H$H$ L$H$(H$H$0H$H$8H$H$@L$H$HH$H$PH$H$XH$$Hc\$,Lc\$(H\$L$$ALID݅LEL$E1HE9ODA HDAHHHL`L^JHNJ`HVH E9~E9}l?AL$A9HHIAHHA9H`1AHHHA9HHH`HAE9}L9tDLH7Lt$L,$L;|$tLzm 1Hh[]A\A]A^A_Ë$T$$LHt$E1cH|$AWG<@AVAUATUSHhAHT$(HT$@H|$8t$4L$$DD$ DL$H$HT$H$~4DDƒHB:Hcl HD$H|$McL\$LULuLeL\$A EBAEnA$EL$AEAAAA AE E A E1A E DDAAAAA A DDAAA DDL;#H?#HH C#ADIcHA4DE!ЉD!D!D AD!A! E!A A`Ddul${Lct$4Lcl$$Dd$fD$D$(Hl$HDl$(HD$8EmEOE1HHE9SDAHDALcJHBBdH H HNN`LVHE9~E9?AWA9H}oAHA9Ћ`}YAHA9Ћd}CAHHA9Ћ}'AHA9Ћ`}dHAE1E)Ay9ffLcA 09~H;l$(tHt$(DH 4Lt$8Ll$(AL<$L9|$t H|$i 1Hh[]A\A]A^A_Ë$T$ E1Ht$H|$80`HD$lffffAWIAVAUEATIUSDHHT$@t$HcHA AL~AA~ELcD$DKE9HAAt}IqAHACCt{A9} AHA1E)A{9ffLcA9~L9t LH~1L|$Ld$AH\$H9\$(t H|$(eg 1H[]A\A]A^A_Ht$ $(E1T$4L]Ht$ fffffffffAWIAVAAUAATIUDSH8 AHT$0DD$,H$0HT$ HT$~0DDƒHHcf HD$ H|$ EH$ LE EBL#DDD D AAAAA A 1Hc׉E1DE!D!D ލ ƒH4~ffffLcڃBtyǃ~IcMcAHL$Ll$Ll$,MLD$x tdA)9DNE1E9}EA7H$ DD)AMcHA CL%~HAAE9|McO, E$E1@HALD!ȨtLHOHAN0MU}A98fffAHDN0HLL0LnHA9~̍}A9LAHJ0HHA9}DDDmE9CAB }zAH D)1U9HcLjL59~L|$Ld$AH\$H9\$ t H|$ hd 1H8 []A\A]A^A_Ht$ E1DɉZH|$ SHE111IHLT$H|$PH4$D$Lu {tHĐ[fffDT$PDD$XDL$\T$`H|$hHt$(AL$ t Au~fffff밐SH1E11IHLT$H|$PH4$D$L]u {t HĐ[ÐDD$XDL$\DT$PD\$`H|$hHt$(AL$ A1 بuAwH#DLcIAD_DcwffDvffffD&ffffϋ\$*EEAA)E)DEAD!E!A)EE)Ll$E)DD!)AD)!D))AD)ۙA!AA)DD!EAE)DD!AA)D)ؙ!A)E)EAE!EEUD$L$T$9T$?[]A\A]A^A_ffffffffffAWAVAUATUSBXHrH|$z0D$D$L$Ht$|$D$9D$D9DqDiMAAD$AAAAŋT$9T$'D$DtHcl$L\$I1;l$}LcD$IcMcMcHL$LL$LD$fHcHt$H|$HTLHDG DA 4A<DD)ʉ!Љ)AA)!Љ))ʉ!))!)ljD)D)Ǚ!Љ)!׉AD)AAA!ED)D)ř!))Ή!A L\$;l$8D$AAAAŋT$9T$[]A\A]A^A_fffffffffffAWAVAUATUSJ0Ht$D$H|$L$t$J9t$L$苊eHcD$Yt$ĉL$HD$D$\$HHD$4l$؋D$l$܋L$)D$uED$DD$D9D$t$9t$|DL$ԋD$+D$ADL$܋L$)D$tLl$Lc\$D$L|$DT$MeMuImAMDT$MMLD9T$HcD$HcT$ċL$HD$D$HT$LcHHD$ȋD$D$HHD$ffLD$HD$HL$H|$EE(C 4(B1F'D /B< D)ى!ȉ)A)É!ȉ))!)lj)!)‰)D)ډ!ȉ)!ʉB)щ!)‰)Й!)D)!DD)Ɖ!AA)D)!A)ADDD))؉Ι!!A)A)DAE)DA!ED)؉!DAA)MD)ML!A)E)EAE!EEHt$B<>D$M\$9\$DL$D9L$HT$LT$B "G"E$*D,*EDDD)E)AAA!!EE)A)E)EAE!E)EDD)!DA))AE)D!A)DA!DED)!AA)D)!A)E)EAE!EED$DD$D9D$[]A\A]A^A_AWIAVAUATUSrZXH|$z0D$L$Ht$\$|$DyDqL$9L$D$GAAD$AD$AAƋT$9T$ D$DtHcl$LT$1I;\$}LcD$Lcd$McIcLL$LD$АLcH|$K KDKtDD E  7A4|DD)ʉ!Љ)AA)!Љ))ʉ!))!)ljD)D)Ǚ!Љ)!׉AD)AAA!ED)D)Ù!))Ή!A LT$;\$HILfHl$HnL$IH9L$Lcl$D$HN4HD$ffffF\m]H|$C4lBTE $EDDT}CHILfHl$HnL$IH9L$Lcl$D$HN4HD$ffffF\m]H|$C4lBTE $EDDT}CCA9G B\FD~DAD9~DAD9~DAE9D9DMA9AN9A9ɉAAM99ωHD$M9N։;fffE9DىAM9ND\$D9\$Ll$DMLd$AEFTBLE9~ DEAD9~ ADE9~A׉DA9~DAȉD9DALD9N9Љ~ЉA9AM9NHD$ЉD$L$T$9T$[]A\A]A^A_ffffffAWAVAUATUSJ0HHBD$DH|$L$Dl$D9l$ND$RXD>DvL$"DHcH|$D$AAD$ċT$9T$D$tLcL$LD$1D9K<}HcD$LT$McMcLl$IHD$LT$ffffH|$D9}HcLL$Ll$HTLHDGFB F BE9~ EEED9~ ADE9~A͉DA9~ EADA9~DA9~AA9~DAȉE9~ED9_DY[]A\A]A^A_fffffffffAWAVAUATUSJZ0D$H|$Ht$\$L$苊T$9T$?HcD$qL$HD$D$t$HHD$:DD$ЋD$ADD$ԋL$)D$uHDL$D$ED9L$l$9l$|DT$̋D$+D$ADT$ԋL$)D$tLcl$Ld$D$L\$L$IM{MLIkLd$M#MM9L$Lcl$D$L$OtmHHD$ILt$MID9ADM9END9 A9ʉEAMD9DNA9~ EADA9~DAۉ9AA9AEMÉA9AND99HT$MD9DNDD$F MLML\$9\$HT$CBtHL$LD$AG 9E|FTC ~AЉDD9~ ADED9~ ADEE99ADMA9AND9E9DAAM9D9EHT$DMA9ANˉ D$F MLML\$9\$HT$5D$9D$HT$Ht$EED\A DLE9~ EEED9~DAD9~DAE9~ EEED9DALE9AN9A~DA9AMLL$9NA DL$D$ED9L$-[]A\A]A^A_ffffA9AЉEM9NfffffffAWAVAUATUSJ0ZDD$H|$HL$H\$RXDyDqD$D$9D$AHcAHt$D$AD$AAƋT$9T$D$DtLcD$Hl$E1E9Jt}LT$Hcl$McIcLL$ILT$AHt$E9}IcL HDHLEELL$E9BD ~ DEAD9~DA9~ЉʉA9~DAɉA9~DA9~AA9~DAЉE9~ED9hDb[]A\A]A^A_fffffffffAWAVIAUE1ATUSZ0JH|$A9݉L$T$IHcAԍkHD$؉AHHD$AA9D$tL\$MIcMNH1HINIIID9wAHcAIcL(((_(.vl$(L$(.]D(vd$\$(A.vl$DD$(A.(A((D(_(.A(A].(vL$|$(A.((_(A].L(D_A.A]!(_A.((A(A(].(v\$d$(A.A(](_(.\$D_D$Dd$D|$D.AFǃA3MAMLMD9AA,A!A4 .EE4D$E E,DvT$(l$.vd$(t$.v\$(L$._(L$D_D$Dd$D|$D.AFǃA3MAMLMD9(ffff;|$7H|$AHt$AA$A4 A,E.B M<8CL7CM;C;L$Lct$D|$C.LcD)CAA .A)HcD)B A)C &HcD)B A)A HcD)B A)A >HcD)B A)A }WfffffIcAB AA |HT$AJ B.HcD)B B _H[]A\A]A^A_fffffIcABT A)A }IcAB B LD$ARAF.IcE)B ADD.MLIcE)B ADF&MIcE)B ADDLIcE)B ADD>LIcE)B ADL\$A|#AWAVIAUATUSHHDJ0DBH|$ЋzXD$C SDL$̋3KDD$ȉl$ĉ|$LL$؉D$D$9D$T$St$L$T$,fWD$,D$L$D$D$D$D$D$DT$D9T$L$D$tLc\$I61L\$MFInM~D$HL$L\$LcƒJL~Lcl$D$E1Md5C Lcd$I4B Hc\$H<3BHc|$L7C Lc\$I 3B KtBKB J B J4BKB IL-B M,CH4+BH/B I +B OD=CK,B<8A3F0HD$F;B 3C,D)F$(!ЉD)A)lj!ЉD)A)Ɖ!DD)A)!AA)DD)ș!A))AAA!D)D)!DD)щA)AA!ɉEE)E!E))DɉD!AA)D)Й!)DD)AAAA!‰E)EA)E)DDA!D!DA))IIAIAA!DD)ۉ)D)AڙA!D!)DAA)EAE!DD))!HT$A)HA)DA!D@2HHl$Hl$L$HT$7D$L$T$9T$[]A\A]A^A_fffAWAVAUATUHSHDR0DJDH|$zXD$VDT$^F DL$DD$|$L$L$9L$ЉT$܋V\$D$؉T$gɉL$.fD$L$D$D$D$D$D$D\$D9\$1L$D$tLcd$Ll$E1MD;t$}Lc|$HcT$Hc\$HcD$Hct$L|$Lc|$HT$H\$HD$Ht$fffffIcLT$H|$LDHtL\LdH\$HT$G FA<7HD D3B #B4G'HT$Ld$AD$D)ʉ!ЉD)A)lj!ЉD)A)Ɖ!DD)A)!AA)DD)ș!A))AAA!D)D)!DD)щA)AA!ɉEE)E!E))DɉD!AA)D)Й!)DD)AAAA!‰E)EA)E)DDA!D!DA))AAAA!DD)ۉ)D)AڙA!D!)DAA)EAE!DD))!A)A)DA!DAuLl$D;t$8[]A\A]A^A_ffffffAWAVAUATUSHZ0H$rDŽ$<H$$$ۉ$$D$ۉ$9$<$H$@| $<$9$<$+$<$tL$Lc$<L$$M(M`MXMPMML$hIh MMH$hMMʅL$L$L$L$xL$pDHc$@Hc$HHc$DD$H$Hc$H$H$D|$HH$H$fffffL$L$L$L$L$H$AH$C,CG,zAE!DED)L$E)DDEDDl$ADl$DD$E!D$DD)L$))!ЉO Z)NjD$|$+D$!D41Dd$L$Dt$!A)AE)DEAE!ED)!$xA)D9$xfE"#H[]A\A]A^A_fffffffffAWAVAUATUSZ0Ht$rD$\$T$ۉ\$H|$t$D$\$ډT$T$9T$ʉT$D$L$T$9T$rL$D$tH|$LcD$L\$L$HoLMLOMMLw LML\$Hl$HoMLT$LL$LŅ~Lc|$Lcl$HcD$Lcd$L|$D|$Ll$L,Ld$HD$fffffLT$LD$H|$Ht$LEH\$E EDD}|uuD\]Ld$D)A$E&!ЉD)A)lj!ЉD)A)Ɖ!DD)A)!AA)DD)ș!A))AAA!D)D)!DD)щA)AA!ɉEE)E!E))DɉD!AA)D)Aڙ!)DD)AAA!E)EA)D)DA!DAAA!ED)A)ELl$Ll$ALLl$E!MDD)ˉ)D)AؙA!D!)H\$؉)!D))!A)HD$A)DA!Df3LAH\$>D$L$T$9T$[]A\A]A^A_fffffffffAWAVAUIATUSHDJ0DBH|$zXD$SDL$Dd$D9d$Ћ3DD$KC l$T$܋S|$t$L$D$؉T$_ADd$,D$L$D$D$D$D$D$DT$D9T$)L$D$tLcd$E1D;|$L\$O4c}Hct$HcL$HcT$HcD$Hc\$Hcl$Ht$HL$HT$HD$H\$ffffMcLd$Ht$KTK|ODKD K\L\$DoBE!DED)L$E)DDEDDl$ADl$DD$E!D$DD)L$))!ЉO Z)NjD$|$+D$!D41Dd$L$Dt$!A)AE)DEAE!ED)!$xA)D9$xfE"#H[]A\A]A^A_fffffffffAWAVAUATUSZ0Ht$rD$\$T$ۉ\$H|$t$D$\$ډT$T$9T$ʉT$D$L$T$9T$rL$D$tH|$LcD$L\$L$HoLMLOMMLw LML\$Hl$HoMLT$LL$LŅ~Lc|$Lcl$HcD$Lcd$L|$D|$Ll$L,Ld$HD$fffffLT$LD$H|$Ht$LEH\$E EDD}|uuD\]Ld$D)A$E&!ЉD)A)lj!ЉD)A)Ɖ!DD)A)!AA)DD)ș!A))AAA!D)D)!DD)щA)AA!ɉEE)E!E))DɉD!AA)D)Aڙ!)DD)AAA!E)EA)D)DA!DAAA!ED)A)ELl$Ll$ALLl$E!MDD)ˉ)D)AؙA!D!)H\$؉)!D))!A)HD$A)DA!Df3LAH\$>D$L$T$9T$[]A\A]A^A_fffffffffAWAVAUIATUSHDJ0DBH|$zXD$SDL$Dd$D9d$Ћ3DD$KC l$T$܋S|$t$L$D$؉T$_ADd$,D$L$D$D$D$D$D$DT$D9T$)L$D$tLcd$E1D;|$L\$O4c}Hct$HcL$HcT$HcD$Hc\$Hcl$Ht$HL$HT$HD$H\$ffffMcLd$Ht$KTK|ODKD K\L\$DoBfffffD;L$ffffD9\$ffffDD\$ȉD$g;l$=ffffDl$Ћt$E9Dl$t$ffffD$fffff9L${fffffȋL$ԉD$FD;l$fffft$؋D$Dd$9D$AfffffD;T$Tffff|$9t$D9d$fffDDd$ĉD$MHP[]A\A]A^A_AWAVAUATUSH8HDR0DJDD$jXH|$0Ht$(QDT$$9qYDL$ A DD$l$T$T$$9T$|$t$\$D$|0< ffD$D$D$D$D$D$D\$$D9\$ L$$+L$D$ tLct$L|$0D$Dd$D9d$O,Ll$}Hc|$Hct$Hc\$HcD$HcT$HcL$H|$Ht$H\$HD$HT$HL$EADD\$D9\$~D$D\$؉D$D\$;t$~ At$D\$;t$~ At$D\$;|$|$ЉD$9D$~D\$ԋD$D\$ЉD$9|$~ D\$ԉ|$DD\$D9\$~ D$D\$̉D$D$D9\$~ D$D\$$D9L$~ D\$DL$EE9~ EEED;l$~ EDl$D\$E9~ EEED9D$ ~ D\$ DD$ ED9d$ ~ D\$ Dd$ EE9~ EEED9T$~ D\$DT$ED9t$~ D\$Dt$E9~AӉDA9~ EADA9~ EAD;\$;L$~ AˋL$D\$9~AˉDۋD$9D$D$D\$9D\$ԉD$~ At$D\$;t$~ At$D\$;|$D$9D$GD$D\$9D$D\$ D$9D$~D$D\$ЉD$D\$9t$~t$D;|$ED|$E9D\$~ EDl$D\$E9~E;L$D$9D$ mDl$D\$ A9Dl$ D\$~ Dl$ L$ DD;t$-ED9l$Dt$Dl$D9t$~Dt$9~AՉD9|$~ D\$|$D9T$~ D\$T$D;\$A݋\$E9Dl$~Dd$A9~ EADD;T$`D$9$CDl$D$E9D,$D\$~ D,$D$E;l$ Al$E9Dl$~DL$A9~ EADD;D$~DD$D;D$~DD$9~;l$~l$A9~AA9~AD9|$ ~D|$ D9t$~Dt$94$~4$A9~DA9~DA9~ EADA9~DAщE9~ DEAE9~ EEEE9~ED9~ ADED9~DE9~ DEA9~AʉDA9~ EADA9~D9~AщDA9~AA9~DAȉ;T$~ ֋T$܉t$A9~D;L$~L$;T$;L$~L$9{HD$LT$D$T$9T$J HL$@LcD$H|$(HL$Hl$L|$L\$HLL$N4NTNl HD$JtJ|AE$HL$CLl$GG,L\$EM RfELL$H []A\A]A^A_fAWAVAUATUSDb0Ht$HT$rHT$C$L$D$\$DD9d$䋊t$H\$HcL4E|$fffD$AD9d$D$DtLl$Lc\$DL$MUEKZHcD$Hct$IcH l$HT$E1HL$HD$D$Ht$HHD$ЋD$HHD$DHHD$LT$LD$Ht$L\$HD$ F SFCDs4SB<[DCDD)ʉ!ЉD)AA)‰!Љ)A)!DD))lj!AA)D)ș!)ƉD)AAA!D)E)ω!DD))H\$AAA!EE)!)D)D)Й!Љ)D!AAE)EAE!ED)D)!IcEA)D)ى!AEHT$ID$AD9d$;H|$A ~Hl$DAD[]A\A]A^A_Ld$A$[]A\A]A^A_ffAWAVAUATUSH HDb0DZDD$DJXH|$Ht$QDd$ DiyD\$q YDT$T$؋T$ 9T$ԋAD $DD$l$|$t$\$D$|7q fD$D$D$D$D$D$D$D$Dd$ D9d$? L$ +L$ԋD$tHl$HcL$Hc\$Lcl$Lct$LcT$LeLc|$HL$Hc|$HD$H\$Ll$Lc\$A,LA \Lt$ClG,tLT$G4TG|LxL|$L\$G<\ALD$DA))DDD)AAAD!A!AA!D)E)AEA)DD)EE)AE!!DD))A)E)A!DA)A!DEA)LT$D)EAAAE!LD$A!DD)EE)L\$DA)Ld$)DHL$!A!Hl$A)DDELt$A)DD!A))IZ!FCF[A)FcE)D$K kB,sDHD$EA!E)CT=EDAD4CT$D!AE)A)DDA)D!D!A)D؉A)D)D)AAAAA!A!EE)D)D)D)EEݙE)!D)!AAD!H|$Hl$AE)A)ELl$H\$AE!LT$ED)DD)D)LD$!ЉA)!DAHL$D)؉!DAA)D)L_!A)AM RfELL$H []A\A]A^A_fAWAVAUATUSDb0Ht$HT$rHT$C$L$D$\$DD9d$䋊t$H\$HcL4E|$fffD$AD9d$D$DtLl$Lc\$DL$MUEKZHcD$Hct$IcH l$HT$E1HL$HD$D$Ht$HHD$ЋD$HHD$DHHD$LT$LD$Ht$L\$HD$ F SFCDs4SB<[DCDD)ʉ!ЉD)AA)‰!Љ)A)!DD))lj!AA)D)ș!)ƉD)AAA!D)E)ω!DD))H\$AAA!EE)!)D)D)Й!Љ)D!AAE)EAE!ED)D)!IcEA)D)ى!AEHT$ID$AD9d$;H|$A ~Hl$DAD[]A\A]A^A_Ld$A$[]A\A]A^A_ffAWAVAUATUSHHDb0D$DZDDJXH|$Ht$EDd$}DEuD$] MDT$D$ԋD$9D$̋UDL$|$DD$t$\$܉L$؉T$|6D$D$D$D$D$D$D$D$Dl$D9l$L$+L$̋$tH|$LcD$HcL$LcT$L\$HcT$L7Hc\$HcD$Hcl$LD$O Lc|$HL$GEHT$LL$A H\$AHD$E E9Hl$EL|$C4~ EEE9~A͉DE9~ EEEA9~ EADE9EOE9~ DEAD9AOA9~DAˉD9~ ADEA9AOD9AO9~ωщLl$9L\$OHL$LT$9LL$HD$MeOHT$t$Ht$GEA CGE E9A4~ EEE9~AωDE9~ DEAA9~DAȉE9EOE9~ EEED9AOA9~DAˉD9~ ADEA9AOD9AO9~AɉDHl$9L\$OHL$LT$9L|$HD$LmOHT$AHt$GDE\GLALCTETE9At~ DEA9~ωщE9~ EEEA9~ EADE9EOE9~ EEED9AOA9~ EADD9~ ADEA9AOD9AO9~AʉDH|$9L\$OHL$Ld$9H\$HD$HoOHT$AHt$FDD\DTLDLBTE9؋t~ EEE9~͉щE9~ DEAA9~ EADE9EOE9~ EEED9AOA9~ EADD9~DAA9AOD9AO9~AʉDLD$9L\$OHL$H\$9Hl$HD$Mx OHT$AHt$GEEA E AE9A4~ DEA9~AωDE9~ DEAA9~DAȉE9EOE9~ EEED9AOA9~ EADD9~DAA9AOD9AO9~AʉDLD$9L\$OHL$LL$9H\$HD$Mx(OHT$Ht$GEEA A4CE9E ~ DEA9~AωDE9~ DEAA9~DAȉE9EOE9~ EEED9AOA9~ EADD9~ ADEA9AOD9AO9~AʉD9AO9Ot$A9FHcT$HT$:HL$HD$Dt$EEA݉L9L\$L$AǃA9Ht$McLT$HT$HL$L\$LL$JH|$FDB FD H|$E9Ћ4~ DEA9~׉ʉE9~ DEAA9~DAЉE9~EE9~ EEED9~DA9~ EADD9~ ADEA9~DD9~D9~A҉D9~9~DD$EDDAAىE9~DT$EE9~DD9~AAA9~DAЉE9~EE9~ EEED9~DA9~ EADD9~ ADEA9~DD9~D9~AӉD9~9?LT$Dt$EEA݉Ht$A IHT$9H[]A\A]A^A_fffffffffAWE1AVAUATUSj0Ht$HT$rHT$L$\-t$\$닊HA9\$HcL,DuAAA9D$DtL\$DD$McM EOVD$Lcd$HcHcT$\$HL$HHL$HLd$E1HD$؋D$HT$HHD$ЉHHD$ffIcA|L\$ALL$HD$H|$LD$Ht$C E HD$AGA;D9EA4~DA9~ЉE9~ DEA9~ȉщD9~AE9~ DEAD9~DA9~ EADD9~DAA9~DD9~D9~A҉D9~9IcAtL\$AfffIAAA9mH|$A ~AHl$[]A\A]A^A_ÐAWAVAUATUHSHDR0DJDBXDD$H|$QDT$9qYDL$A DD$DqT$؋T$9T$ԉ|$t$\$D$Dy|7$fffD$D$AD$D$AD$D$D\$D9\$L$+L$ԋD$tHc|$Lc\$McLeHcT$HL$Hct$LcL$LcD$A$C,HIcA C.CAC4vd$(l$.vL$(D$.v\$(T$.vd$(L$._vl$(T$.](vl$(L$.vD$(\$]_.(v (\$(Le_]D(AC,A CC$A.C4vT$(l$.vL$(D$.vd$(\$.vT$(L$._vl$(\$.](vl$(L$.vD$(d$]_.(v (d$(HU_]D(B, BB$.B4vT$(l$.vL$(D$.vd$(\$.vT$(L$._vl$(\$.](vl$(L$.vD$(d$]_.(v (d$(Le_]D(AC,A CC$A.C4vT$(l$.vL$(D$.vd$(\$.vT$(L$._vl$(\$.](vl$(L$.vD$(d$]_.(v (d$(HU _]D(B, BB$.B4vT$(l$.vL$(D$.vd$(\$.vT$(L$._vl$(\$.](vl$(L$.vD$(d$]_.(v (d$(Le(_]D(AC,A CC$A.C4vT$(l$.vL$(D$.vd$(\$.vT$(L$._vl$(\$.](vl$(L$.vD$(d$]_.(v (d$(Ee_]D(D9Lcd$.;AEJE(9E(E(E(E(D(HcHDB$B B,.B4vT$(d$.vD$(L$.vl$(\$.vT$(D$.v(.vd$(\$.v(.vd$(D$.vL$(l$.v(.v(.vD$(L$.v(.v(E.A(A(A(A(A(A((vA(A(E.vA(A(E.vA(A(.vT$(D$.v(.vd$(\$.v(.vd$(D$.vL$(l$.v(.v(.vD$(L$.v(. []A\A]A^A_fffAWE1AVAUATUSz0DBHT$HL$HD$?Ht$DD$l$苑\$\$HA9HcT$L DWfffAAA9D$DtL|$Dd$McM7EK\L$l$Lc|$Lct$HcE1LcL$LcHcH4IcAHAB B$,B.B4vL$(d$.vD$(T$.vl$(\$.vL$(D$.v(.vd$(\$.v(.vd$(D$.vT$(l$.v(.v(.vD$(T$.v(.IcA4HAIAAA9gD$ ~D$܃H|$[]A\A]A^A_AWAVAUATUHSHDR0DJDBXDD$H|$QDT$9qYDL$A DD$DqT$؋T$9T$ԉ|$t$\$D$Dy|7fffD$D$AD$D$AD$D$D\$D9\$L$+L$ԋD$tHc|$Lc\$McLeHcT$HL$Hct$LcL$LcD$fA$fC,HIcfCfA fAfCfC4f.vd$fl$f.vL$fD$f.v\$fT$f.vd$fL$f._vl$fT$f.]vl$fL$f.vD$f\$]_f.v\$Le_]DfAfC,fA fCfC$fAfC4f.vT$fl$f.vL$fD$f.vd$f\$f.vT$fL$f._vl$f\$f.]vl$fL$f.vD$fd$]_f.vd$HU_]DffB,f fBfB$ffB4f.vT$fl$f.vL$fD$f.vd$f\$f.vT$fL$f._vl$f\$f.]vl$fL$f.vD$fd$]_f.vd$Le_]DfAfC,fA fCfC$fAfC4f.vT$fl$f.vL$fD$f.vd$f\$f.vT$fL$f._vl$f\$f.]vl$fL$f.vD$fd$]_f.vd$HU _]DffB,f fBfB$ffB4f.vT$fl$f.vL$fD$f.vd$f\$f.vT$fL$f._vl$f\$f.]vl$fL$f.vD$fd$]_f.vd$Le(_]DfAfC,fA fCfC$fAfC4f.vT$fl$f.vL$fD$f.vd$f\$f.vT$fL$f._vl$f\$f.]vl$fL$f.vD$fd$]_f.vd$Ee_]DD9Lcd$4;AEEJEE9EEDDHcHDffB$ffB fB,ffB4f.vT$fd$f.vD$fL$f.vl$f\$f.vT$fD$f.vf.vd$f\$f.vf.vd$fD$f.vL$fl$f.vf.vf.vD$fL$f.vf.vfE.AAAAAAv AAfE.v AAfE.v AAf.vT$fD$f.vf.vd$f\$f.vf.vd$fD$f.vL$fl$f.vf.vf.vD$fL$f.vf. []A\A]A^A_ffffAWE1AVAUATUSz0DBHT$HL$HD$?Ht$DD$l$苑\$\$HA9HcT$L DWfffAAA9D$DtL|$Dd$McM7EKwL$l$Lc|$Lct$HcE1LcL$LcHcH4IcAHA)fB fB$fff,fBfB4f.vL$fd$f.vD$fT$f.vl$f\$f.vL$fD$f.vf.vd$f\$f.vf.vd$fD$f.vT$fl$f.vf.vf.vD$fT$f.vf.IcA4HAIAAA9LD$ ~D$܃H|$[]A\A]A^A_fffHtGEt!D$AAйHD$AAй HH1L$H ;1D$D$ D$H$H1DD$DL$4D$8H$H$HT$XYHHE14HÐHDT$ D$D$D$HÐATAUHSAkAwHc!DHc HAL$DM1ۉD1AIc4/D /@uHc@:4/sffHc@:4/r9oD/LcHcE*@4/@:4*v fHc@:4*w9?D *D /@4*DMuMU}DEA8v EAD8vˉщD8v ADEA8v EADA8v EADD8vDAD8v ADE@8vAD@8vAԉD@8v։@8vAʉD8vAˉDDM@uMU@}DE[]A\DEMUu}A8vDAȉ@8vA҉D@8vAӉD@8vADA8v EAD@8vˉ8vAʉD@8vAˉD@8vAщDDEMU@u@}[]A\}UMu@8vAD@8vAˉD@8vAщD@8vAD8vAԉD@}UM@u[]A\uMU@8vAD@8v։8vωщ@uMU[]A\UM8vAЉDUM[]A\D9}'DHc)Hl*DaAt$9|AHA)LcAt$I|(fffffffAWAVAUATIUSHJDj0H$H$$H$$YqADA Dq \$ht$tD$pDD$lQD$`AHHD9l$`A}D$|$D$`L$Dt$D9l$`L$$tH$Hc\$D$TLcL$lLcT$`L$D$HD|$`LL$HLT$XHL)D9D$T}Hct$pIcMcHD$ L\$@Ht$L$hE1D+|$hE9B49}1McHcLcOADD)E9BA)OCA|DH,HD!AV1HH9}V3H@8qv HH79|DA8v>HD$Mc׉M C:4 rL_I)L;\$@DHT$X 93DA8wHT$McDMG:rHyH)H;|$@|rLD$XA8D$THL$ EHL$XH+l$HH\$H$9t$TD$`L$Dt$D9l$`AH$ǂHĨ[]A\A]A^A_DAD8DFwDD8F4L$Hc\$lDŽ$ID9$H\$8|$D9$}D+$$tD$H$Hc$L$$+t$hEIIcHLL$xHH+|$8H$1D9}+ffffLcHcL$K DD9D<G<|H$D $$E1T$hD$lA$E94:HH<}%ffffIcLcAMLD)E9G D;|DHct$t$D$h$+\$hIL$L$LD$8I|DLcHHt$0A4?McK @8A8LtH8u@:qHP+H9sDDRHH9r@qAv1H$L9AH8Jv HH9|2@8HcL$pIHL$:TrHoL)L9H|$x8QHAtH8uA:0HPaL9rDDRHL9sA0Av1H$L9WA2@8nHc|$pM H|$B:t rHjH+$L95HT$x@2Hl$xD$IcHD$8H$D$dH+|$8Ht$(HEND$D9D$dD+\$hL$hMD$F D)IcHB HD$0ITDI4?D88Ht ffH8uD:FHPkH9sffff JHH9rDFHT$0CD MD$LcHItB A1K48_A8 LtfffffH8u:VHHaH9sfffffYHH9rVAV1HM9bA2fffffH@8qv IHA09|D A8BHD$IcۉI@:4rIHL)L9DɈM?8NHFt fH8uD:HPsH9rfffff2@rHH9sD@@8vBH@8w@r@8sBH@8r@rDZD8DFDWA8AFfff8NHFtfffffH8uA:HHL9rfffffDDAHL9sAAV1HM9A2D A8HD$EMcIE:rLAI)M9ADED$dHl$(EH+|$8LT$8$9L$d<8@D8sfffffBHD8rDB@D8vBHD8wDBk@8vAH8wQ@8sffAH8rQQ@8ADF8ApDD8FfffATAUHSAAwHV!DHc HAL$DM1ۉD1AIct}fDL}fuHcf;t}} Hcf;t}|9D\}LcHcfF\Uft}f;tU~ Hcf;tU9^DLUfDL}ftUDMuMU}DE fA9~ EADf9~ˉщfD9~ ADEfA9~ EADfA9~ EADfD9~DAfD9~ ADEf9~ADf9~AԉDf9~։f9~AʉDf9~AˉDfDMfufMfUf}fDE []A\DEMUu}fA9~DAȉf9~A҉Df9~AӉDf9~ADfA9~ EADf9~ˉf9~AʉDf9~AˉDf9~AщDfDEfMfUfuf}[]A\}UMuf9~ADf9~AˉDf9~AщDf9~ADf9~AԉDf}fUfMfu[]A\uMUf9~ADf9~։f9~ωщfufMfU[]A\UMf9~AЉDfUfM[]A\D9}'DHc)HlUDaAt$9|AHA)HcAt$H|EfffAWAVAUATUSHDB0H$H$H$H$rD$$$H$D DoGWCL=$$$;DŽ$D$D9$HH|'fff$D$D9$$+$$tLc$Hc$H$L$MLd$xLc$HMHt$pHMJ H)I< 1D9}"1LcLcBW$D9fBDE|HDH$D$$D$D9\$$$HL$xN 牔$M$IHc$McIcLD$8HD$0Ht$@1D9}(Hc$H$LcD9N EqfFS|DHE9ts$Ht$81HL$0IHE$LTu9L Ku "fIM9 A0$9}\f9~HL9 EG1HHD9MHD9f9~Hu f9EfD9މ|IxL9ȘfffA$LL$@LT$0LD$8D$D$MHMJSADHD$pMJLEA4Df9f9UHt Hf9uf;qHPH9sff:fzHH9rfqD$$$9D$$<$D$D9$'L$AǃH[]A\A]A^A_f9QHAt Hf9uf;uHPmH9r fJHH9sfujfD9ىHWL9Gf9OMf93fA; $HwH)HH;t$8|Gf9NmfA;$`LFI)IL;D$8IFf9O:A@f9N&DŽ$D$D9$L$I$I$|"$D$D9$$+$$tLc$Hc$H$Lc$H$N HH|$`HMLD$hMI)1LD9H$K< }!1LcLcBW$D9fBDu|DHH$D$ LL$hD$ND$AD9d$ K KHc$L$IcIcHT$HHt$HHT$PIxHt$XHD$$H|$(HHD$HffffHT$L$McLD$(LT$XMdC4XI<G\fD9f93Ht Hf90ufD;GHP"H9sff2frHH9rfDGE9Ee1IID9}3AGIA99f9~IA0f9wHGtfffHf90ufD;HPH9rfff:fzHH9sfDE9qHD$PH|$XID!IL(L$19uIM9YA19f9~IM9iAL$IcHt$IBHD$(IcH$Lt$L\$(NdA4{A|JH9HL9r$D$(LD$HD$HLI9t$(M4}L\$FdE&~HF.ztffffHDD.uz HP.H9r2rHH9s{DIHAD.ztHDD.uzDHPD.rjH9rDDBHH9sJ@.vBH.wR@.vBH.wR@.vBH.wHRLL) HI<+M HM.H.vHH.w HL9L9s/H.vHH.wHL9rH9 fffffD HD HH9r@.vBH.wRfAWIE1AVAUATUSHhZ0Jj DrHt$XLH|$`\$TL$PHJXA։D$L‰DAD;L$T|$THcHt$ ω|$AL$D;L$TL$D$PtL\$`McLl$@O.rHJL9(D$ @.vBH.wRRM.r,. rHWMcH)HL9|[(H$u.rLvMcI)IM9|:(H$I(]B_N]O](]F@.vBH.wR$9}H9|.O$9}H9|.rFD$ B$9}H9|.dNZ9}H9|.rAD$ 7$A9}H9|.N9}H9|. BfffffffAWIE1AVAUATUSHxj0Db AHHH|$pHt$hzl$dAhRXL$HH\$PEx|$`t$\IBD%T$LDAD;L$dVD\$dAD\$$AL$$D;L$d5L$$D$`tMcHct$HLl$PJLt$pALIE1D;l$\}Hc|$LCDMcDL\$(HT$@D$8L$HH9HL9r틴$D$(LD$HD$HLI9t$(M4lL\$NdM&f~HFf.ztfHfDfD.uzf HPf.H9rH2HrHH9sifDIHAfD.ztHfDfD.uzfDHPfD.rnH9rLLBHH9s;f@f.vBHff.wRf@f.vBHff.wRf@f.vBHff.wHRf L)LHfI<+M HMf.fHf.vHHff.w HL9L9s2ffHf.vHHff.wHL9rH9ffffL HL HH9rf@f.vBHff.wRAWIE1AVAUATUSHhZ0Jj DrHt$XLH|$`\$TL$PHJXA։D$L‰DAD;L$T|$THcHt$ ω|$AL$D;L$TL$D$PtL\$`McLl$@OAHNEII9syII:D\$DD$IIT$DL$H EYDD$D\$|$t$T$|$DII Yt$\$H H III9rI)AttHNI9IMIID$Dd$D\$Dl$H I EYE D$DT$D\$D\$EYEPII9rH~I9II IID$T$L$D$H H YT$D\$D$Dt$L$D|$DEYDt$\$H H III9r(HHHoHÐH\$Hl$Ld$Ll$Lt$L|$W;VAt$H\$Hl$DLd$Ll$Lt$L|$fO ;N uԋ.9/u΋G;FuDNn,ALgL~DWBl$ċo,9 B<9~A9AA.HEL$xIcIc҉D$Ht$HT$DD$D\$D$DT$AAHHJ< +D$H|$LDHAHMHEI)D)AAtDAAL;d$u+L$A!AEIAD"/AD!A D/HAVA9$A1A@7AIIшOHA9~E9}yH;|$ȹu+L$E)AEPAD"HWAA!E E9D}3H;T$ȹu +L$AAAAD"G!A DGL|$Ld$L$E1AAE1AA)M9EMQtfD)D~EYMQL;d$u +L$!ljDADD A"$AA!I|$D A$DAVA9+EADIEAA DшHA9~E9DD)D9~EH;|$Au+L$AAADADA@"7EAE!D @79>AAD/AHH蔜HÐH\$Hl$Ld$Ll$Lt$L|$H(O;Nt"H\$H,$Ld$Ll$Lt$L|$ H(DG D;F uDD;uDWD;Vu;JtD;B ffuD; uD;RuF,LOHZHnDZDw,D$ԋB,LL$DNH\$Hl$_D$ЉAB9B 9<9{9%BD5HEL$xIcIcHcH|$Ht$HT$D$DD$DD$ȋD$D|$Dl$AAHLD$HHD$HD$D+D$DXAHL\$HI)A4D+D$D)ADPAHLT$HA\I)D)A DtFDAAL;D$u+L$A!ADIA A"IÃD! AIU9E IE IEI9~9}3L;D$u+L$̺AA A"! AH|$HL$H|$HL$Ht$Ht$L$1*DA1A)L;T$u E"Dd$ąMbtrAA)AA~ArDMbAAL;D$u+L$A!E;ىd$DED$AE"IÃA E!E EIt$čU93A4$d$DIAADL$E IEI9׉t$~9)D9~A4$L;D$u+L$D$ĉAD ‰!A"9DA1A)L;\$uA L$MctuAA)AA~A[DMcAAL;D$u+L$A!Ad$DAEAD|$AE"IƒA E!E EI\$U93A$d$DIAADL$E IEI9׉\$~9)D9~A$L;D$u+L$D\$ADAAE"(AAA A!E E(^9EAA)D)A)E1D$T$Dl$A;|$D)ʃ E"Dd$EMcMj~E{L~ EjDl$DMcMjL;D$u +L$!ljd$L$ED$AAAAd$L$DT$E"D$A A!E EDT$ID|$DT$}A9aAME<$AIIʼnL$d$L$EDT$Ad$L$D\$ADT$E ET$IA9D|$T$~A9D);D$~E<$;D$~ EMDL$L;D$u+L$T$D$L$AL$D|$D$EAE1AA)L;\$uA:A ;@t$EMcIrtpD)~ ARA SDDMcIrL;D$u +L$!ljDd$DAAD$AE"!A EIDl$UA99>AA <$d$DIHDEAD\$EIA9Dl$~A9D)D9~ DE $EL;D$u+L$T$DAD99DAHHHQHÐH\$Hl$Ld$Ll$HHAHI~DV BAPfA EY\ff-9H?AA*l$YHD$\HEHD$\fd$HL$D$l$D$\*\\T$Af.1H\$(Hl$0Ld$8Ll$@HHfffffA 1ҨufA EY\+ffW*t$\D$\Y\\A*\AdDN(HN(A*f.vDaEA*|$D$DiQD)A)D*E*D]D]AAf.f.T$D$f.AAoAA,D*A\D%:,D*A\Ћ~D^DDуfDYDXDDfAWfEADYYXYDfEWDYYDDYYE\ADDYEXDYEXED\f 6DXEXDE\DDXD\ADAXD\DXE\X  fiADF~?$u,A,LcL^1H9J ILcA 9BT|1,,D**DNDFA\\9AAHHFIcHcH|L >1D9BLcE;G$ D9HD,8BA*E*E*D*D\E\DYDYDXEXE\DYEXA,BL|OD,E,McLF1H9I @HcD i9DL|:AAHHFIcHcH1D9BLcF_G$YD9HD,GAAA*E*E**D\A\DYYDXDXE\DYEXA,BL|S,D*A\f3 fD 3 ,*\f=8fD=4fD3 DDf%3DYDً~EYXnDYDYDDYDYYEYDYDYEYDYDXYDXDYDYDYE\YEXDYDYE\DYYEDYDYDXA\DYE\DXDYXDYYDYYDYDXDXEXE\DYEDYE\E1fEW5LcLfA9HN Dd-LcFl%McA)C!C *HcC)B4AYA*HcAYB<X*HcAYBX*KAYAX*AYAY*AY*XAYL*AAYXX*AY*AYY*LXAYA9X*AAYX*AY 4XT$*AY*AY*AYAYXXXf\$XfAYXA_ffTfUfV,B|wHt$HuAAAHHFIcHcH1D9}BLcF_G$YD9HD,GAAA*E*A*D*\E\YDYXEXD\DYAX,BL|[,ALc,Ln19HIL4LcF9FT|YE1fEW2LcLFA9HM@D\-LcFdMcA)CC B*HcC!A4JAYA*HcAYAEEDYDYYiEDYDYDYEDXDYDYDYDYDYDYDX DXDYf-o" DYADYAYEXD\ADYAYD\$\ffTf.fffTfTf.DY! AAYf !\^AA-$AAՋYYYYЃ\$0AYDAYAYT$AYfEf\\fDWfWDD$|$8d$ L$(v#HT$D蟡HHf:!^KHT$D蜜HHffffff.%! uzHD$fDl$Ef HD$fD|$DXEYAAYAWAVIAUATAUHSH}H+}HJ0Hr8LB@HBHLjPHHE zpfDD%h#L^fD>Lc8LHIc0fD-D HyMPAT$AEAAJDfEYAXLcAYH AfAWJ4LAAX1AAY9AYA*hA*`AYAYAY\DDDXAEXfAXE\fAWEXDYAYE\AYAYAYD\D\DX*qAYDXA*H AYEXJ XE\AXXXAY#fD="fD5O"Ifff* *AAYYIf'fA+IcHfEIc2IIX*AAYIX*A AYHLXH4XHNAYD*EYYX*HYDX*HAYDX*H AYLDXDY*AYD*HEYDAXD*PDY*x AYDXHcIcTAXDDYDYAXAXYYDfA\XfAWE\fYAXYfAWfYYDYEXDEXYD\DXXDX\D\X\AfEEXfTfAUfVfAfTfAUfV,A*i*vY*IAYAT$AYX*i AYL9XXYffD=#!HyfD%^ fLHHcMc8L^fD-cMPDDY ! DDDYDHDY%8EYDXJ4DLc1DYLFDYDXDYA*HYDYDYDYEXfD5 AYD\AYYYD\AYEXYYAXXYDYDYY@ D\Y%oYYXAY\YY\AYAXYYAXXYDDYA*PAYD\*fAYJ4J XA*P AYED$D9XXY#Ifffff* *AAYAYIffAIcHIc2IIIX*AYX*A AYHLXH4HNY*(AYX*HAYX*HYX*H AYLX*HYAYY*0AYD*H EYDYX*hAYDT$fD$DXT$HcDYIcTXDYYXY AXDDY 9 AYDDYfDYXDYAYfEXDYEYE\DYDYD\AYEXYYAXAXDYEDYY E\DYDXYDYXAY\YY\AYAXYYAXXYDDYf\$D\ffTffUfVffTfUfV,A*I*fAY*YAYAT$YX*I AYL9XXY;OfD=fD5,*!D*aAYDY*I*i AYN AAYI, AXD*mDYD*eEYfEX*M AYfEXAYD*ULEY*mAYX*e AYEXD*mEYEXDXDY*UYAXDXDXDXDYAXffTfAUfVDfEfATfEUfAVHcD,Ic\E$[]A\A]A^A_D*!*YEYAYD*q*Q DYNAYM DXE*jEYE*zDYA*J AYEXDXDYA**MAYA*A*ZAYE*r YEYAXAXAXXYA*r\$AYfD$\$fDl$XXDfEfEXAXAYXffTfAUfVDfEfATfEUfAVffffAWAVE1AUAATIUHSHFH+LBXHJ@HzHLJ0LR8LzPHELHL$H|$DZpE=fDDfE)fE IIEEAEfEEXAffDWAEYEYAXEYfWAYEAYEYAYEYAAYEXAXEE\E\A\XEPXEXA0EXAYA\AXEpE`AP \A`0\Ap8AXAH(I@fDfD HHD$LcD HqffcL_fkfsAUCDNf{(fDC0ODfDK8LK@HcIH$H5<0H$$d$xl$pt$hL|$ET$XAH$pix'H۸tMtD DL$4DD$4E;Et8H$XH$`L$hL$pL$xL$HĈDSE;UDT$0u{IUL[HT$HL\$@S AE\HcHHD$(HHtIcDADA[]A\A]A^A_fffffffffAWfDE1E1A9AVAUAXATAUSHW H_(f_fWHOPHG0fDJ fbfr(fD[ fDcAXLo@AXLwHHL$DYHD$DXJYDXrYADXDYL8DX[Y,AXYX2AXYYXAYXfc(YAXXcEEXYX#,XAYfAXAXD,fOAXAXD,fjfDDIcE\E\AXAXA,*D\A,D*E\AAtHt$,,XDYXDYAXAXA,fAWA,fVHt$DΉD))D))!A!!AE9>IcDADA[]A\A]A^A_fffffffffAWfDME1E1AVAUAXATAE9USHW HO(f_fWHw@H_HfDJ fbfr(fDY fDaAXHt$AXH\$LPDYLw0DXJYDXrYADXDY,DXYYLo8,D$AXYX2AXYYXAYXfa(YAXXaEEXYX!XAYfAXAXD,fOAXAXD,ffffDH\$DIcE\E\AXAXA,׉H\$A,D**,D\ ,D)ى+|$)D)!E\!EA!E|XXAE9E,AXAXS[]A\A]A^A_DfffAWfD=AE1AVAUAXATUSHW Hw(fWL_@LWHLGPfDJ fbfr(fDn L\$fDvAXHHLT$DYLD$DXJYDXrYADXDYHl$DXnYf_HL$fDgxfD\$DLo0Lg8,E1AXYE9X2AXAXY,YXAYXff(YAXXfEEXYX&D$XAYfAXAXD,fOAXAXD,DH\$HL$IcDA\E\EXAX,*\؉A,H\$؉XDXXfgDYXAAXAXDXYDY,AXDXXDYDXfWAX,‰D$fAXD,fOAXD,AH\$HL$IcAXA\AXAX,At$,*\A\H\$,EXDXAYEX,<L$*\H|$DX,AXHAYH ILD,HH Nj|$HD$)l$I D)D#|$+t$D)A!!AE9 []A\A]A^A_DfffffffffffAWfD=Mf-=E1AVAUAXATAU1D9SHW H_(HOPHw8Lo@LwHfz@fZ frXfDBhHL$fDRpL0Ht$YDXzYDXr8DYDDXBPDYDYDXR`YDXYXrDYDXDXB0DYDXRHDYDXDYDXBDXDYXDXR(DYEXEXfD2DYDXDYAYEXfD XEYEEDYDXDXDXDYEXDYEXEEYDXXAEXEAXYEXXAD\$fc@DXf[XfShYYYXcYDXfs YDXf{pX[8XSPDYDYX{`YX[YXS0DYDXYYX{HDYYXSDXYX{(DYDXYX;DYAXDDXAYEYDXXXDXAXAYXAYXYXYDXAXXXfWAXAXXD,YXAEXXfoAYAXXf,ʼnD$AXD,fOAXD,f-'ADHcAXE\AXAX,AA,*D\A\AH\$,EXDXDYEX,*\AtHt$EXXDXt$A,AXYfSDA,fAW+L$D)ӉD)D)!!!E9)HcADA[]A\A]A^A_fffffAWfD=f-E1AVAUAXATAU1D9SHW H_(HOPHw8Lo@LwHfz@fZ frXfDBhHL$fDRpL0Ht$YDXzYDXr8DYDDXBPDYDYDXR`YDXYXrDYDXDXB0DYDXRHDYDXDYDXBDXDYXDXR(DYEXEXfDDYDXDYAYEXfD XEYEEDYDXDXDXDYEXDYEXEEYDXXAEXEAXYEXXAD\$fc@DXf[XfShYYYXcYDXfs YDXf{pX[8XSPDYDYX{`YX[YXS0DYDXYYX{HDYYXSDXYX{(DYDXYX;DYAXDDXAYEYDXXXDXAXAYXAYXYXYDXAXXXfWAXAXXD,YXAEXXfoAYAXXf,ʼnD$AXD,fOAXD,f-ADHcAXE\AXAX,AA,*D\A\AH\$,EXDXDYEX,*\AtHt$EXXDXt$A,AXYfSDA,fAW+L$D)ӉD)D)!!!E9)HcADA[]A\A]A^A_fffffAWfD=f-E1AVAUAXATAU1D9SHW HO(Hw@H_HLPLo0fz@fZ frXfDBhHt$fDRpH\$Lw8YDXzYDXr8DYDDXBPDYDYDXR`YDXYXrDYDXDXB0DYDXRHDYDXDYDXBDXDYXDXR(DYEXEXfDDYDXDYAYEXfD XEYEEDYDXDXDXDYEXDYEXEEYDXXAEXEAXYEXXAD\$fa@DXfYXfQhYYYXaYDXfq YDXfypXY8XQPDYDYXy`YXYYXQ0DYDXYYXyHDYYXQDXYXy(DYDXYX9DYAXDDXAYEYDXXXDXAXAYXAYXYXYDXAXXXfWAXAXXD,YXAEXXfoAYAXXf,ʼnD$AXD,fOAXD,fffffEH\$AHcA\A\EXAX,։H\$,**A,\ +t$,D)щD)D)\E!AA4!EX!EXEXAlE9AXDXt$DXXAX@[]A\A]A^A_ffAWfD=f-}AVAUAXATAU1SHW fDfDoxHw(L_@LWHfz@fZ frXfDBhDT$fDRpDl$LOPLHL\$YDˋXzYDXr8DYDDXBPDYLT$DYLL$LD$DXR`YHL$؉\$DLw0DXL8E1YD9XrDYDXDXB0DYDXRHDYDXDYDXBDXDYXDXR(DYEXEXfD'DYDXDYAYEXfD XEYEEDYDXDXDXDYEXDYEXEEYDXXAEXEAXYEXXAD\$ff@DXf^XfVhYYYXfYDXfv YDXf~pX^8XVPDYDYX~`YX^YXV0DYDXYYX~HDYYXVDXYX~(DYDXYX>DYAXDDXAYEYDXXXDXAXAYXAYXYXYDXAXXXfWAXAXXD,YXAEXXfoAYAXXf,ʼnD$AXD,fOAXD,AH\$HL$HcEA\A\EXAX,*\Yt$ȉ,H\$؉AXDDXAYDXY-ִAXDXDXXXDXfT$YT$fT$XXfl$YXAXYXYXYDXfl$HD$f\$DYL$HL$Y\$AXDY?\$YXEXXAXXfWAXXXfd$Yd$YD,Xd$YXf\$AXEXXfwDXYX5ijAX,։T$YXfd$AXfDL$EXAYXfX D,fOX qD,f5~ffff\ LEDX>HcX/AX,*\A,ED\ AH\$,EXYEXDXA,AtEX,*D\Ht$EXfSXDX\$XDYDAAXA,fAW+L$D)ӉD)D)!!!E9HcADA[]A\A]A^A_ÐAWX >H@H@AVAUATAU1SLG LWPLO8H@Hw(Lo@LwHfAxPfEX(LT$fExpfELL$fEfEL0YEAXx DYEXxHDYEXHhDYDYEXDYEXDDXDYEXxDYEXH@DYEXP`DYDYEXpxDYEXEXHDYEXP8DYEXpXDYDYEXEXPDYEXp0DYEXDYEX0H\$f\$DYѰfv(H\$fE1D9DYAYYEXEXDXDAXDYEXDY=cYXV`EXAXEXYXV8EXfDLAXfD .EYYXVDYAYAXEXDDYAXEXEADYDYDXDYDXDYEXDAYEXDYDXXAXAXDXfnpDXXYXnHDYDXYXnDYEXEDYMDXXEAXDYDXffPDXYXf DYEXEEXDXfADD$DYYX^hDYDXfDXYX^@YXDYDXYX^YX~xDYDXYX~XDYYDXX~0DYYYkX>AXDDXAYDXY-6AXDXDXXXDXfT$YT$fT$XXfl$YXAXYXYXYDXfl$HL$f\$DYL$HD$Y\$AXDY\$YXEXXAXXfWAXXyXfd$Yd$YD,Xd$YXf\$AXEXXfwDXYX5$AX,։T$YXfd$AXfDL$EXAYXfX D,fOX ѬD,f5ޯffff\ EDXHcXAX,*\A,ED\kAH\$,EXYEXDXA,AtEX,*D\Ht$EXfSXDX\$XDYDAAXA,fAW+L$D)ӉD)D)!!!E9HcADA[]A\A]A^A_ÐAWX H@H@AVAUATAU1SLG LW@LOHH@Hw(LwPL0fAxPfEX(LT$fExpfELL$fEfELo8YEAXx DYEXxHDYEXPhDYDYEXDYEXDDXDYEXxDYEXP@DYEXH`DYDYEXpxDYEXEXPDYEXH8DYEXpXDYDYEXEXHDYEXp0DYEXDYEX0H\$f\$DY1fv(H\$fE1D9DYAYYEXEXDXDAXDYEXDY=éYXV`EXAXEXYXV8EXfD AXfDAYYXVEYDYAXEXDDYfAXEXEADYDYDXDYDXDYEXDAYEXDYDXXAXAXDXfnpDXXYXnHDYDXYXnDYEXEDYDXXEAXDYDXffPDXYXf DYEXEEXDXfADD$DYYX^hDYDXfDXYX^@YXDYDXYX^YX~xDYDXYX~XDYYDXX~0DYYY˧X>AXDDXAYDXY-AXDXDXXXDXfT$YT$fT$XXfl$YXAXYXYXYDXfl$HL$f\$DYL$HD$Y\$AXDY\$YXEXXAXXfWAXX٦Xfd$Yd$YD,Xd$YXf\$AXEXXft$DXYXAXYYXfd$AXfDODX \XfX LA,щT$D,fOX 1D,EDXDD\ H\$HcXA,ED\A,щH\$,*‰D)D)D\EXA,ʉ *ɉ+t$D\D)EAELEXAX!EXE!EXDX\$!DXXE9XX$[]A\A]A^A_fffAWX .H@H@AVAUATAU1SLG fDfwxL@H@LwHLoPfAxPfEX(fExpfEDt$fEfEL|$LLLt$YEDAXx DYHw(EXxHDYLl$EXPhDYL\$DYEXLT$DYt$DL$EXDLw0L8DDXDYEXxDYEXP@DYEXH`DYDYEXpxDYEXEXPDYEXH8DYEXpXDYDYEXEXHDYEXp0DYEXDYEX0H\$f\$DYvfv(H\$fE1D9DYAYYEXEXDXDAXDYEXDY=YXV`EXAXEXYXV8EXfD AXfDAYYXVEYDYAXEXDDYAXEXEADYDYDXDYDXDYEXDAYEXDYLDXXAXAXDXfnpDXXYXnHDYDXYXnDYEXEDYDXXEAXDYDXffPDXYXf DYEXEEXDXfADD$DYYX^hDYDXfDXYX^@YXDYDXYX^YX~xDYDXYX~XDYYDXX~0DYYYX>AXDDXAYDXY-۠AXDXDXXXDXfT$YT$fT$XXfl$YXAXYXYXYDXfl$HL$ft$DYL$HD$Yt$AXDYDt$f\$XXEXAXYXfWX!Xfd$Yd$AXD,Yd$XYXf\$AXEXXft$DXYXAXYYXfd$AXfDODX XfX A,щT$D,fOX tD,#EDDXUD\ LH|$HcH\$DX0AXA,ED\A,*D\DYL$ȉt$,EXDXA,D$DHx[]A\A]A^A_Hc$H7HIIMDf$ffffff.T$PsfDD$XfD.wf.L$Hs\Lct$DLd$0\HD$8D$DHL$D,E*A\,GLT$(Ld$ B,YA2ILT$(,*\fBHDt$ Ld$LE4$HĘ[]A\A]A^A_H1HĘ[]A\A]A^A_HHڐAWIAVAUATUSHhYE` HL$HT$H|$8H~1EhMp@A@A*YEH|$0>A*AhLt$(AEA*IpHAR \D*\f A@$Ht$ \$AXAE*ExXD$XAZAAJAE*Er\D\*\*A\af.s1Hh[]A\A]A^A_Ðf.vf.rf.vLD$ \\D,D,*D)\D)A*\T$Ld$(AAYAY,LcDLl$0L$HM,Ll$HM,CE1A9|fWLL$IcME1A9}RMc1LD9}(HcH4HcfEDYHD9AX|IcAMAYLA9X|AT@A9|H|$8Ht$@ AB(MB(DA*f.vEHEA*f.vEPApD))D*D*D]D]AA9E1A9zfWH|$E1McA9N}]Icffff1LD9}-HcH4fffffZ2HcAY4HD9X|IcAIAYLA9X|ABT@A9xAWIAVAUEATUSHHhDHVH|$HE`A8EH AhAHT$@AIH@IPHAAA HL$8AHAHT$0AEAADt$$AEAAHE*AE*f A*AP$AY*E*ExNAT$,DFV DvYfe*DD$(\\*XXD\\\*A\f.s1Hh[]A\A]A^A_f.vf.rf.vDHt$0\\D,EA*,*\AE)\A)Hl$8Dl$(EAYDD$$AY,L$,HHT,EMcLL$@HAH LL$AAUf/F(LF(DA*f.vEhA*f.vAp)D*AHD]A)D*D]AIHHMx@ED$$HL$0AHL|$8EAAHE*AE* AGA@1D9ff%zfWL|$E1LcE9Oo}THc\$(E1LA9}(McO ?D>LA*McABY4A9X|McAIBY E9X|f.BDPD9uH|$HHt$P 1D9}ff%E1L\$LcL|$fT$OkE1E9}^Hc\$(E1L|$LA9fL$}+IcL ffLD*IcADYA9AX|IcAIY E9X|f.vBDPD9h5f.w,1f.,1D9f%߇fWLT$LcE1ME9}PHc\$(E1LA9}$McD>LA*McABY|D$HH$@E苼$(D$0Dt$ DL$L$ Ht$<$HLD\$<)D$H$0Dt$ D$(L$@HL$ LDD$T$ELt$D$v;$HH$@Dt$ D$0D$(L$ t$H|$HDD$LED4$,D$H$0D$(L$@HDt$ DL$L$ |$D$LEL\$010H$@$0Dt$ D$(D$HEL$ LHT$t$HDt$D$AD$0$HEL$@D$(HLDt$ DL$L$ D$L|$D$ @$HL$@Dt$ $(D$0HL$ |$LD$L$EDt$i>9$HL$@ED$0$(L$ LDt$ t$HLT$D|$$.H$@$0Dt$ D$(D$HEL$ LHT$t$HDt$D$-D$0$(ED$HL$@LDt$ DL$L$ 4$HDT$L|$IB$HD$0Dt$ D$(L$@HL$ LT$DD$ELt$D$L$HH$@ED$0D$(L$ HDt$ |$LHD$DT$D<$lKD$HH$@Dt$ $0D$(ELDL$L$ Ht$T$HD4$JID$0$(ED$HL$@HLDt$ DL$L$ $DT$L|$PL$@$(Dt$ D$0D$HL$ HLD$<$ELDt$D\$cOH$@$HED$0D$(L$ LDt$ Ht$HD$DT$D<$NPH$@$0ED$H$(L$ Dt$ H|$t$LHD\$$KD$H$0L$@$(HL$ Dt$ DD$|$ELL\$$~G$HD$0Dt$ D$(L$@HL$ LT$DD$ELt$D$;E[$H$0Dt$ $(L$@EL$ |$T$L4$HLt$0 D$0D$HH$@D$(HL$ LDt$ DD$EDT$HD$D<$G@D$H$0Dt$ L$@D$(EHDL$L$ |$LL\$D4$>d$HH$@ED$0$(LL$ Dt$ T$Ht$HDT$$3=D$0D$HL$@D$(HLDt$ DL$L$ DD$EL|$D$-D$0D$HH$@D$(HL$ LDt$ DD$EDT$HD$D<$Y,iD$HH$@Dt$ D$(D$0HLDL$L$ HT$D$EDt$IH$@$0ED$H$(L$ Dt$ H|$t$LHD\$$M$HD$(L$@$0HL$ Dt$ |$D$LELT$D$0rL$@$0E苔$(D$HLDt$ LL$L$ t$$HD|$q@!$0Dt$ L$@D$HD$(EL$ H|$LL\$Dt$D$>D$(L$@ڋ$HD$0HLDt$ D $L$ LD$ED$D|$ 1{$HH$@Dt$ D$(D$0EL$ Lt$HT$HDt$D$.(L$@$0Dt$ D$HD$(ELLL$L$ t$HD|$D4$U-UHSHHHt"DN DFH11AHHcHH[]ÐUHSHHHt"DN DFH11A覾HH蛘HH[]ÐHHtHtvHfffff1蔔‰HÐHHtHtwHfffff1D‰HÐHHtHtvHfffff1ҹ‰HÐHHtHtwHfffff1ҹ褓‰HÐAUIATUSDDfDWo_ DoALGHvtAAFA A Af A[]A\A]1D9}0f |offfLcKD9JDfBXBT̸|ܸfDd$fD\$ALcʙAfFTfFL̸AEHcAAfDDf|HcfDtfDl̸[qMcIcAfffffAA19T$ft$T$fd$T$HcDD$^T^9ffDl^A*AY*AYDd^A*AYAXAXA*AYXffAXDffATfDUffAVDfDfATfDUffAVDfD,BfATfDUfDVAfEfDTfUffDVE,DFlfDfATfDUfDVAfEfDTfUffDVfE,FdfTfUfVffTfUfV,BTyD9ZHcAD,~T$fl$T$D9A*AYfAXffTfUfVfl$fffTfUfV,BLcABMcMc11D9;Hl$fd$ZLcDF,fB\fD9BTfffA*AYD*EYD*EYAXEXDXffEfEDAfATfDUffDTfAVfUfAVDffEfTfETfUfDUfffEVfVfTEfUD,G,ffEfVDfET,C\fDUfEVA,CTLMfDfD AHcAfA4fHl$fd$ZHcDD,^Dd^D9T^ffA*AYE*EYD*EYAXEXDXffEfEDAfATfDUffDTfAVfUfAVDffEfTfETfUfDUfffEVfVfTEfUD,fE,XffEfVDfETD,fEdXfDUfEVA,fATXLMf:fD AHcAfEf4HcfA,f$ALcfGAfFEAA{McIcA19tLcBfB\f9Flf*BTfY*E*AYDYD*EYAXXDXEXCCDG|Gl~D9}cLcAF,fD9A*YAXC}?Hc؍A^D9D*EYDXE$}HcD$NA*YXA LIA-;McMc11D9EHl$fd$ZfffffLcDF,fB\fD9BTfffA*AYD*EYD*EYAXEXDXffEfEDAfATfDUffDTfAVfUfAVDffEfTfETfUfDUfffEVfVfTEfUD,fG,`ffEfVDfET,fC\`fDUfEVA,fCT`LM McMcHAI1D9<Ld$fd$Hl$f\$HcD,^T^D9ffA*AYD*EYDl^AXEXE*DYffEEXAfTfUffDTfVfUDfEfffAVEfTfETfUfDUfffEVfVfTEfU,AffEfVDfETD,ElfDUfEVA,ATLMMcMcՉ1D9}jHcnDlnD9TnD*E*EYDYD*DYEXDXDXAZAZEZA ADET|LMuMcMcՉ1D9}[Hcn\nD9DlnD*D*DYEYE*DYEXDXDXE4E\ET|LMufffffffffffAUIATUSDDfDWo_ DoALGHvtAAFA A Af A[]A\A]1D9}0f |UfffLcKD9JDfBXBT̸|ܸfDd$fD\$ALcʙAfFTfFL̸AEHcAAfDDf|HcfDtfDl̸[WMcIcAfffffAA19T$ft$T$fd$T$HcDD$^T^9ffDl^A*AY*AYDd^A*AYAXAXA*AYXffAXDffATfDUffAVDfDfATfDUffAVDfD,BfATfDUfDVAfEfDTfUffDVE,DFlfDfATfDUfDVAfEfDTfUffDVfE,FdfTfUfVffTfUfV,BTyD9ZHcAD,~T$fl$T$D9A*AYfAXffTfUfVfl$fffTfUfV,BLcABnMcMc11D9;Hl$fd$ZLcDF,fB\fD9BTfffA*AYD*EYD*EYAXEXDXffEfEDAfATfDUffDTfAVfUfAVDffEfTfETfUfDUfffEVfVfTEfUD,G,ffEfVDfET,C\fDUfEVA,CTLMfDfD AHcAfA4fHl$fd$ZHcDD,^Dd^D9T^ffA*AYE*EYD*EYAXEXDXffEfEDAfATfDUffDTfAVfUfAVDffEfTfETfUfDUfffEVfVfTEfUD,fE,XffEfVDfETD,fEdXfDUfEVA,fATXLMf:fD AHcAfEf4HcfA,f$ALcfGAfFEAA{McIcA19tLcBfB\f9Flf*BTfY*E*AYDYD*EYAXXDXEXCCDG|Gl~D9}cLcAF,fD9A*YAXC}?Hc؍A^D9D*EYDXE$}HcD$NA*YXA LIA-;hMcMc11D9EHl$fd$ZfffffLcDF,fB\fD9BTfffA*AYD*EYD*EYAXEXDXffEfEDAfATfDUffDTfAVfUfAVDffEfTfETfUfDUfffEVfVfTEfUD,fG,`ffEfVDfET,fC\`fDUfEVA,fCT`LM McMcHAI1D9<Ld$fd$Hl$f\$HcD,^T^D9ffA*AYD*EYDl^AXEXE*DYffEEXAfTfUffDTfVfUDfEfffAVEfTfETfUfDUfffEVfVfTEfU,AffEfVDfETD,ElfDUfEVA,ATLMMcMcՉ1D9}jHcnDlnD9TnD*E*EYDYD*DYEXDXDXAZAZEZA ADET|LMuMcMcՉ1D9}[Hcn\nD9DlnD*D*DYEYE*DYEXDXDXE4E\ET|LMufffffffffffAWE1AVAUATUSL\$@Ht$DD$t$E9}IcAD;T$IHT|DT$A(fffffED+D$IcAAIcHDHD~߅LT$H(LcHct$8fDv>fD >Ld$McLl$L\$ALd$Ht$ffffHL$@H4HL$tgL|$AHGfEfEA*YT$MHIffTfAUfVfAfTfAUfVD,D7|$I AcLFf9fqfifaL9AAAI*DffD*DYH D*DYffEfEAEfDTfUfETfDUfDVffEVEfEDEfEfETDfDUffEVDfETE,EfDUffEVE,I M M+IRID*DYH D*DYfEfEAEfDTfUffETfDVfDUEfEDfEVfETfDUEffEfEVDfETE,DfDUfEVA,H H IkIM9uffI9LsfAAA*"Y!ffHIffTfUfVffTfUfVD,DHH9rLT$HL$AHt$MHHD*,DYDXAZA@9}H*YXDZD$D9}IcHcILH9|$D$D$DA4G9sHc9IL|`H\$Hl$Ld$Ll$Lt$L|$H1HHl$`I̹HHT$XDL$THL$H$L$PH$EL$H|$8H4$HILLHT$0D$kH|$XM AAKDDD1Dt|D$D$$$\$TD\$Pt$PDD$LT$D|$HDt$DADك$~t$~jAJAt?fffH$H$L$L$L$L$HHT$XELLL<1҃uDL$DDD$HD4$L$LT$PHt$8H|$0x1tL$HT$PDT$(Ht$8D$D$H?DT$(AA&LL$XDD$HDt$L$LT$PLd$Ht$8H|$0L $DL$DB'rDD$DL$HIT$Lt$PD4$H|$0tKHT$XELLL iHl$XDL$DDt$DD$HL$LLd$T$PHt$8H|$0H,$SH\$Hl$Ld$Ll$Lt$L|$HHIL$L$0H|$pH$ELD$`H|$8H4$ILHDLIHT$0LT$(L\$ HD$PHD$XD$AhLT$(L\$ MM ΃KDDT$LE1υDL$`D|$dDD$hD|$DDl$DD|$lDL$HDD$@DDʼnE$t|$`$R|$L|$L|$LeH$xH$L$L$L$L$HĨD$ED$EL$@T$HHt$8DL$|DD$xH=|$La|$L|$LYEEAKDD$@L$DET$HHt$8\$H|$0Dl$Ld$L4$u1|$L>|$LAuAELLLL/"1҃DD$@L$DET$HHt$8$H|$0)A}AELLLL螱띃|$L|$LVAmIDD$@L$DET$HHt$8\$H|$0Dl$Ld$L4$#EMADD$@L$DET$HHt$8\$H|$0Dl$Ld$L4$蚕EAELLLLAMAELLLLEdDD$@L$DET$HHt$8\$H|$0Dl$Ld$L4$nL$@T$DLL$Pt$HH|$0E$(H\$Hl$Ld$Ll$Lt$L|$H1HHl$`I̹HDL$\IHH$L$PL$EL$H4$HT$@H$ILLLT$0HT$8D$HL$@cMM AAKDDD1Dtz$D$D$$\$\L$XD\$Tt$Xt$TDD$L|$PDADك$~r$~hACAt=ffH$H$L$L$L$L$HH|$0ELLL-1҃uDL$LDD$PD,$L$TT$XHt$@H|$8q1tL$PT$XDT$(Ht$@D$D$H8DT$(AA&DL$LDD$PDl$L$TT$XLd$Ht$@H|$8L<$wDD$LL$PIT$Tt$XD,$H|$8mPH|$0ELLLYDL$LDD$PDl$L$TT$XLd$Ht$@H|$8L<$QAH\$Hl$Ld$Ll$Lt$L|$HHIH|$pL$0L$H$ELD$`H|$8H4$IDHILLHT$0HD$PHD$XD$L\$(`LT$(MM ΃KADDÃEA1AAt}L$`|$dT$lDL$hL$Ll$L|$HT$@DL$Dl$@D͉E$ty$c$UAAApH$xH$L$L$L$L$HĨD$ED$EL$DT$LD\$ Ht$8DL$|DD$xH5D\$ AaAAREMAEDL$@DD$D\$L$HT$LDl$Ht$8H|$0Ld$L4$1 AAAAuAELLLL1҃DL$@DD$D$L$HT$LHt$8H|$0֧EEAAELLLLAANAmBDL$@DD$D\$L$HT$LDl$Ht$8H|$0Ld$L4$A}DL$@DD$D\$L$HT$LDl$Ht$8H|$0Ld$L4$uEAELLLL E]AAELLLLEYDL$@DD$D\$L$HT$LDl$Ht$8H|$0Ld$L4$DD$@L$DLL$PT$Ht$L$H|$0ΠH\$Hl$Ld$Ll$Lt$L|$H1HHl$`I̹HDL$\IHH$L$PL$EL$H4$HT$@H$ILLLT$0HT$8D$HL$@A\MM AAKDDD1Dtz$D$D$$\$\L$XD\$Tt$Xt$TDD$L|$PDADك$~r$~hACAt=ffH$H$L$L$L$L$HH|$0ELLL`1҃uDL$LDD$PD,$L$TT$XHt$@H|$8;j1tL$PT$XDT$(Ht$@D$D$H$1DT$(AA&DL$LDD$PDl$L$TT$XLd$Ht$@H|$8L<$wDD$LL$PIT$Tt$XD,$H|$8@fPH|$0ELLLEKDL$LDD$PDl$L$TT$XLd$Ht$@H|$8L<$A1H\$Hl$Ld$Ll$Lt$L|$HHIH|$pL$0L$H$ELD$`H|$8H4$IDHILLHT$0HD$PHD$XD$L\$(\YLT$(MM ΃KADDÃEA1AAt}L$`|$dT$lDL$hL$Ll$L|$HT$@DL$Dl$@D͉E$ty$c$UAAApH$xH$L$L$L$L$HĨD$ED$EL$DT$LD\$ Ht$8DL$|DD$xHj.D\$ AaAAREMAEDL$@DD$D\$L$HT$LDl$Ht$8H|$0Ld$L4$ҩ1 AAAAuAELLLL1҃DL$@DD$D$L$HT$LHt$8H|$0vEEAAELLLLZAANAmBDL$@DD$D\$L$HT$LDl$Ht$8H|$0Ld$L4$oA}DL$@DD$D\$L$HT$LDl$Ht$8H|$0Ld$L4$nEAELLLLE]AAELLLLEYDL$@DD$D\$L$HT$LDl$Ht$8H|$0Ld$L4$薹DD$@L$DLL$PT$Ht$L$H|$0nAWAVAUATUSH(1HcoDn$VD$LcF$D$H^DN L$PIAmHOT$|D$xH$@IIL$`1=L$h1L$D$D$H$H$HT$`HT$ D$TcH$1HD$`LcJLA9|IM)A9}oH fA#IcAAYfA4fEAYAYAYXEYXAXL$HD$8AHL$`I4M$HLH$MMIH$L$,D$D9$#H|$@H9$t H$C1H)[]A\A]A^A_IcH1ۋ$1HcHc\$lAD;t$AAAAAA|1;$}I\$$HcffffEl;$HcE,EdE$ElE,EdE$|1D9Hc$D$AY4EYLD$EYD$AY| ADD$$D9$AXAY$D$D$D$D$AXD$X$XA4EY4AYlATAYd$AY\ DXAY$DXDXDXE4EY EYlETEYdEY\ EXEYEXEXEXE AY,EYDADAY|AYt AXAYXXXA,[HL$@|$LMD$TJ49$LM9L$TD+$Lcd$lHc$+$Hc$D{H\$,D|$LA9|IM)A9}nH ff(Ic(AA"AY(A4EAY(AYAYXEYXAXMD=HI 6fC$ID5fCM MDfA4fA XXAX,XAXXCX4CX AX,AXCX4CX \\YYXQf.!LT$D$L$D$ALT$AE9 H[]A\A]A^A_Ã$D $ D$`ED$4D9\$4$ E{D|$Hˉ\$`fL$H$x]HcL$4Lc$McHc|$I,HLH+$K0M 0LH΃|$\ILdK,MLDD$\DN,LcA%fffAMLLMAfFJ,?IL$J/IM$I|$XI,KO 2ELc$HAL<H$McDD$XA(ffAL$MLLAfFfGfFfG EAEXEXFXDAXEX$$EXX+FXLEX$$X+FXDFXLA\E\YEYAXQf.ODD$LL$LT$D$MXLL$DD$LLT$D$LAL$A&D$(L$TD9\$(ND$`D $P)E1E9Lc|$It=IK fG,KD fE$MfEfEAEAXEXAX,EXGX4EXGXEXAX,GX4GXEXA\E\YEYAXQf.LT$D$jLT$D$D$`D $XE1E9xLc$EAkAMcL$Lc$%H$J<#AE9,$xtLc$Hc$IcL$LMIHKI ?O I4>fEfEfE4ID=fG,ILM fEfE$AEAXEXCX,EXEXfE fC4fEfAHcH$H$@E1DŽ$ω$$$|H$K,L$L$$H] LMDx8EsMA A(Xm(Xu(Lh XmMc Xu\AXE\XD$$#AI H I I AEE $$$D(EXDXXD\DXXA(AXE(D\(YA(EYAXQ.k(d$pl$`t$PLD$HLL$@DT$8d$pLD$Hl$`LL$@t$PDT$8"$I$SL$H$AL$@L$L$MD;$JH$HcHHIH$H)D;$L,H$(DŽ$4L$ LcH$L$`E1DŽ$Ή$$$oH$(K,L$ L$$H]LMD`D(E[ME3A(XmA(XuA(LhXmMcXuA\AXE\AXD$$#AIHIIAEA<$$$(XX X\X X(XD(D\(Y(EYAXQ.s(d$pl$`t$PLD$HLL$@d$pLD$Hl$`LL$@t$P4$I$`H$L$L$`$4H$(H$ Iɋ$9$4O4L$AD+$PD$H+$T$P$TD$TDADDAJAD+$X+$L$4AD$(ED$4$P$0D$DD$PA@Ń$H$LD$4$,D$H!9E1E9*Hc$,Lc$0EULc$4D$H$ &H$A$E9$$tHc$(L$ IcM .M/IHK OI>I4?DIMM4:HJ,L׃$pN H$L4)$pELc$0AHIcL$H$ȉ$;H$H$MMMH$H$G$C4EA A(D(AXDXAX.AXEX(AXCX4AX AX.EX(CX4AX A\\YYXQ.C(LD$HLL$@LT$8D\$0LD$HLL$@LT$8D\$0A$0E9RD$PD $X-E1E9!Lc$PLc$4E}McL$H$AAE9$DtHc$(L$IcM ,IHK OI$BD4M.D<(A(XAXBX,DXXDXFX4FXLAXAXBX4AXBX,EXXI 4I6B$BD$M .D,(A(XAXX,DXXDXFX$FX,X,XFX$FX,\E\YEYAXQ.(DD$HDD$H D$XD $LE1E9Hc$HLc$4EuLc$,Hc$PHc$0Lc$DL$IH$IHH$L$I)L$0fH$HH$DAAE9G$DtL$L$IcH$H$L$IIN J K<K4DG4E$uT$DDl$DWɋL$HWF GFjA)ED+T$LAA*YۉL$LDL$HAA*YXQ._(T$D\$II,Y0$T$H(]D,AfED\$BHĈ[]A\A]A^A_Ë$D_ffffffAWHcHAVAUATUSHH)H#H\$hHD$tHD9L$tDD$|DL$xHt$`H\$XH|$PHcHD$8HL$0fffE1D$(ȉD$,L$,$uHl$hH|$`H\$XL|$Pt$|LLLDEUDoMDDcF jE4HEEEE)A)ԅDt$HDd$DLuDL$LLgHk\$|@ffffY"(IIH]D,AfEIA>E$uL$DDl$DWDL$HWBGFi)AD+T$LAA*YDL$LT$HAA*YXQ._(T$L\$IIY!T$L\$H(],fA7IIL$,IL$(cHD$PH\$0D$tHl$8D\$xHl$hHl$`Hl$XL (IIH]D,AfEIA>E$uL$DDl$DWDL$HWBGFi)AD+T$LAA*YDL$LT$HAA*YXQ._(T$L\$IIYT$L\$H(],fA7IIfffffL$,IL$(dHD$PH\$0D$tHl$8D\$xHl$hHl$`Hl$XLE$uL$DDl$DWDL$HWBGFi)AD+T$LAA*YDL$LT$HAA*YXQ._(T$L\$IIYT$L\$H(],fA7IIL$,IL$(kHD$PH\$0D$tHl$8D\$xHl$hHl$`Hl$XLE$uT$DDl$DWɋL$HWF GFjA)ED+T$LAA*YۉL$LDL$HAA*YXQ.G(T$D\$II4Y8T$H\0(TUVD,AfAfED\$*HĈ[]A\A]A^A_Ë$D/ffffffAWHcHAVAUATUSHH)HH\$hHD$tHD9L$tDD$|DL$xHt$`H\$XH|$P&HcHD$8HL$0fffE1D$(ȉD$,L$,$Ht$hH|$`H\$XL|$PT$|LLLDfDDoMDkG lE4HEEAE)D)Dt$Hl$DDL$LLvLgHk&\$|RY(IIH\?/TUVD,AfAfEIA>E$uL$DDl$DWDL$HWBGFi)AD+T$LAA*YDL$LT$HAA*YXQ.G(T$L\$IIYT$L\$H\q.(TUV,ffA7I2L$,IL$(E$uL$DDl$DWDL$HWBGFi)AD+T$LAA*YDL$LT$HAA*YXQ.G(T$L\$II,Y0T$L\$H\+(TUV,ffA7I2L$,IL$(E$uL$DDl$DWDL$HWBGFi)AD+T$LAA*YDL$LT$HAA*YXQ.G(T$L\$II萻YT$L\$H\5)(TUV,ffA7I2L$,IL$(D$ L$O$;ML$O IMM؃|$XK,bKBO$JOT}>Hc$$pL,6D|$XHD$HLcAEYvjH$LML]D,AfE MzAHt$HF sEWEWDsC fffD$L$0$p9\$WD$`D $PCE1D;$p2$pLc$Lc$pLc$CY5L$]D,AfEYAD;$p$xtIcL$N;WWOKTC CG SESHc$C4 HDNI<ItA {A(LT$D$LM蟣YLL$LDL$D$OAD]A,fA9&AL$4D;$pJ$P $`CE1D;$p2$pLc|$,Lc$Lc$CYL$]D,AfEYAD;$p$xtIcL$N;WEWOKTC CG SESHc$C4 HDNI<ItA {A(DT$L$IIYD L$DT$H\ E(DATEUAV,ffA3IA$D$$L$8D$pD9\$$%D$hE_D$ D$pD9\$ DLcl$,Lc$AD$D\$O$;ML$O IMM؃|$XK,bKBO$JOT}Hc$$pL,6D|$XHD$HLcAfYD= H$LML\A(DATDUAVD,AfAAfE MzHt$HF sEWEWDsC MD=E* HI4C*,IT G*M A*<A* AXMDA*XC*XC*AXAXXXXAXXXX\\YYXQf.LT$D\$IvfD%L$ D]E,ED\$LT$AE9H([]A\A]A^A_Ã$$yD$pED$<D9\$<$E{D|$PȉD$|fL$P$Hc|$K9ML$KIMMȃ|$pI,KM$OD[Hc$D|$pDL,LcAH|$`:fDH$LMLAD]A,AMHt$`F*B*L*E*C**,*|DE* AXDXXAXXDXAXXXDXAXXA\\YYXQf.1LD$ LL$LT$D\$LmfD-L\$ ML$LL$LAD]E,EOLT$D\$D$4L$\D9\$4)D$pD $`GE1E9;Lc|$DLc$AkMc/f-)H$ ],ՉAE9$tHc$IcN ;K<OD G*HI 6A*G*,IT=AG*M E*EA* AXMDA*DXG*$EXC*,AXXDXEXXXDXEXXA\D\YEYAXQf.LT$D\$dLT$D\$$p $hE1E9Lc$Lc$EAAkL$Mc7fD-kN#H$ D]E,F$AE9a$tLc$Lc$IcH$MLHII4K<IO E*J #E*A*E*HcI4JI<nlDى$V4IcӃH:HcLcI4JI<.=Dى$fjDى$M놃~IDى$l%Dى$Xfffffz\$(D$$LD$DD$Hl$$$D\$H$H$Dl$ \$D<$AD)A)$$$D$E)D+$L$ $HD$DΉ$$$D$,D$($@$DDу$HD$8$<A$DD!=1;$GLc$$L$Lc$ Lc$(Lc$,ML$MD$AˉDLcWK0K8W(E ET2C F,$XD~D^LFnDn $T@AD$`D$\L$  A9@A  D*$`E*DYDYEWD,$\ED*$|DYWDX=@WA*L$EZEZEZEZE,D$dDXD+$dD,DXA*$dA^D$A^HH$HD$hDZZA]DXDXA,+$|E,A9ANЉT$tAW9N.D$pAFE{.D$EF;$`O$`D;$DO$$D$AD$ANL$HE1@AL$$4$0 L$@L$L$( A9AAD Ȩ Hl$hA9D$AN9D$dNރ$ $H$ L$DK\$|D$,B= $ Hc$H< HI Hct$|H$@1;L$|L,IDM|H$8}Hf D$|WLcL$@E ;L$|A*Y$0ZX,C<|L$|~7L$@$D$,ALE D9~L$@yA|1;$}8f $WHc*Y$4;$ZXD,EL|D$E~#ATD$A ;$`~ DZE\1;$}:Lc拼$TH$8C|WO׃;$CTB<|1A9}3AD19}Lcэ1G :HA9F 8|9|DŽ$l$|9$SHc$XH$1;$}Hc;$A|ꋌ$dLc$\D$DD$xL$1;$E$L$ D$D$OA)HI M\$1Aʉ\$L$AD9A\$}TAED+L$HcH$8Mcd CCTEA)DD !HB8AD9|D$EtRHcL$8IcDAA BA)D;T$D} FdAD +L$!HcB<9A<;$H$H$ L$xH$H 1;$}+HcH$HL$Mc AB;$|Ջ$H$H$;L$|tKLc$L$@D$GCDD)t$\HH$ |$|$9L$L9$@t H$@MLLLEWD$D|$tD*D$tDYAZXx{,D9$ L$pL$DL$tAFIACD=Hct$pL$p1Hc|$t;L$pM4IMLt$HL9$H$ffffLL$`H$HM$uHcffffffH$H$]1;$| eH$*;$JHcA<EH$D2L$L9$@H$@<ffffffWHf.vfrf.r f.vf.s Hfqf.uzf.z fffffft)ff.fuzf.zfffftHufff軀dffffffffffffffWHf.vfrqf.r f.vf.s Hf^qf.uzf.z fffffft.ff.fuzf.zfffftHH1H1H1躆땐H\$Hl$Ld$Ll$Lt$HHHHMøtHtDFD;GtH\$ Hl$(Ld$0Ll$8Lt$@HHÐDN D;O uDVD;WuD&/A9tuADnLvWHwtUAuAE‰EЅt2H$CDH-HL$L\$LD1UA봋G,ClH$H{-HL$LDL\$D$HH\$Hl$Ld$Ll$Lt$L|$tHtDFD;Gt$H\$Hl$Ld$Ll$Lt$L|$fffDV D;W uDD;uD^D;_uA^oLnLwt*AuAA Aff|1Af$AAufD!fDމEEZAZD\$L$~E`E1D$DLcT$E1IcE9OLMAXIcÉA.LfD.ITHfD.IT Dt$HT$fD.IT(T$HT$fA.IT0vpLAD;\$IT8 Icf fTf\fdfl ft(f|0fDD8fD. L$HT$fD.HBDD$D$HT$IBt|$HT$T$Hl$fD.HB?t$HT$fD.IB l$HT$fD.HBd$D$HT$IB\$HT$T$Hl$fD.HBmT$HT$fD.IB9A9|` DD.ډDl$T$D.TT$T$Td$D$vfDAT A9IcA ATA\Ad D.wL$T$T$l$L$D$BVd$D$ʋT$AB\$T$T$ˋt$D.BHT$T$Dd$l$D.Bt$Et$L$DAIM!fDfD G,fDZfAM@MI7D$D$l$HcL$Lc\$AEMJ E1A9Lf.HvLfD.HTJLf.HTLfD.HTLf.HT LfD.HT(LfA.HT0vsLAHT8A9IcfA fATfA\fAdfAl fAt(fA|0fED8fD.)L$HT$fD.HBDD$HT$fE.IBv|$HT$fD.HBNt$HT$fD.IB"l$HT$fD.HBd$HT$fD.IB\$HT$fD.HBT$HT$fD.IBtE9| aLf.Hv>LAHTE9}CIcfA fATfD.wL$HT$fD.HBT$HT$fD.IBt$Dt$L$fAADl$G,[DZDl$fA8fDD$ZEZt$fA\$DZD\$fDRDd$EZDt$fAhfDyD$ZEZd$fEaD$EZDL$fJD$ZT$fAxfDAD$ZEZt$fAYDD$DZD\$DL$9AMD$D$L$Lc\$Hct$KE1D;\$H  ED.DT$DT$D.DTED.DT_ED.DT T$DT$D.DTED.DTEE.DTsT$DT$E.DT<EE.DT  EE.DT$T$DT$E.DT(EA D;\$DT,Ic T\d lD.t|DDDL DT$D\(Dd,L$D$DT$DBDd$D$DT$EBTD\$DT$T$t$D\$D$DBDT$D$DT$DBDL$D$DT$EBDD$DT$T$t$DD$D$DBd|$D$DT$DB.t$D$DT$EBl$DT$T$t$l$D$DBd$D$DT$DB\$D$DT$EBVT$DT$T$t$T$D$DBE9|;ED.DvyT$DT$D.DTvJEADTE9Ic T\D.wL$D$DT$DB\$D$DT$EBT$DT$T$t$T$D$DB`D$Dt$D$D;\$|^HfD.IDt$HT$fD.ITT$HT$f.ITvQLAD;\$ITIcf fTf\fdfD.wL$HT$fD.HBpd$D$HT$IB\$HT$T$Hl$fD.HBcT$HT$fD.IB1Dt$Dl$L$t DEf:AfMM zE1E1l$IcACIcL1I 9Lf.IdLf.IT:Lf.ITLf.ITLf.IT LfA.IT(LfA.IT0viLʃIT89Hcf fTf\fdfl ft(fDD0fDL8f.4L$HT$f.IBDL$HT$fA.IBDD$HT$fA.IBZt$HT$f.IB/l$HT$f.IBd$HT$f.IB\$HT$f.IBT$HT$f.IBD9|1LID9}#Hcf f.wL$HD$f.IBAEL$sAIM!Dl$EA@AAAfD:BBG4[fD)fDqfAM@MI|AvD$D$t$LcT$HcT$E1D;\$K LLfD.IT$HT$f.ITLfD.IT[LfD.ITT$HT$f.IT LfD.IT(LfE.IT0oT$HT$fA.IT87LfE.IT@ LfE.ITHT$HT$fA.ITPLA D;\$ITXIcf fTf\fdfl ft(f|0fDD8fD.fDL@fDTHfD\PfDdXL$HT$fD.HBDd$D$HT$IBXD\$HT$T$Ht$D\$D$HBDT$HT$fE.HBDL$D$HT$IBDD$HT$T$Ht$DD$D$HBg|$HT$fD.HB5t$D$HT$IBl$HT$T$Ht$l$D$HBd$HT$fD.HB\$D$HT$IB^T$HT$T$Ht$T$D$HBE9|8LfD.IvqT$HT$f.ITvBLAITE9}}Icf fTf\fD.wL$HT$fD.HB\$D$HT$IBT$HT$T$Ht$T$D$HBgD$Dl$D$jHH\$Hl$Ld$Ll$Lt$L|$AtHtDVD;WAt"H\$Hl$DLd$Ll$Lt$L|$Ë^ ;_ uDD;u΋n;ouDfLv19DoH}LcAfB fB. w9|At4AAuJ sE1ffffY:ufA(f:Df1DDZDZD|$ZDD$~E1E1AMcMcABK N E1A9.Ic Tdl t.|DDDLw A.Dv L$T$.Aw A.Dv T$T$.ATw A.Dv d$T$.ATw A.Dv l$T$.AT w A.Dv t$T$.ATw A.Dv |$T$A.ATw E.Dv DD$T$A.ATw E.Dv DL$T$AATA9E9},Ic .w A.Dv L$D$AAE9|AAAt W ffffffAfD Df!EAF,ZEZT$fJD$DZDZDT$fEhfDqD$EZEZD\$fAxfDbfDAD\$ZEZt$fAXfjfal$EZZDZT$DZDD$L E1D$\$Hc\$AEE1IcA9L I mL$T$D.AwT$D$Dv T$T$D.ATwA.ۉv \$T$D.ATw A.Dv d$T$D.AT wA.T$T$l$T$D.ATwt$D$Dv t$T$D.ATwA.v |$T$E.ATw E.Dv DD$T$AATA9_Ic T\d lD.t|DDA.T$T$1E9|L$T$D.AwT$D$Dv T$T$D.ATwA.ۉv \$T$D.ATw A.Dv d$T$AAT E9}?Ic T\d D.dA.T$T$NSt$EL$ fAfDDfAC,DZEZD\$fEhfJfDqD\$DZEZDZDD$EZDD$DE1D$AHcL$Mc׍EM J E1A91IcA ATA\Ad AlD.AtA|EDw A.Dv L$T$D.ʉw A.Dv T$T$D.ӉTw A.Dv \$T$D.̉Tw A.Dv d$T$D.ՉT w A.Dv l$T$D.ΉTw A.Dv t$T$D.׉Tw A.Dv |$T$E.ȉTw E.Dv DD$T$ATA9A9}RIcA ATD.w A.Dv L$T$D.ʉw A.Dv T$T$ATA9|t$EAIzfffffaAAI0MXDd$IhfD"Dl$fD fDjF<fDQfDrfDYfDzfAM@E1E1\$AGE1IcIcI A9L IIcf fTf\fdfl ft(f|0fDD8fD.w fA.Hv L$HT$fD.Iw fA.Lv T$HT$fD.ITw fA.Hv \$HT$f.ITw fA.Lv d$HT$fD.ITw fA.Hv l$HT$fD.IT w fA.Lv t$HT$fD.IT(w fA.Hv |$HT$fA.IT0w fE.Lv DD$HT$AIT8A9E9Icf fTf\fdfD.w fA.Hv L$HT$fD.Iw fA.Lv T$HT$fD.ITw fA.Hv \$HT$f.ITw fA.Lv d$HT$AITE9WDl$Dd$L$DEMAfDfD C,fDZfAM@E1E1\$IcMcՍEM J E1A9NIcfA fATfA\fAdfAl fAt(fA|0fED8fD.w fA.Lv L$HT$f.Hw fA.Lv T$HT$fD.HTw fA.Lv \$HT$f.HTw fA.Lv d$HT$fD.HTw fA.Lv l$HT$f.HT w fA.Lv t$HT$fD.HT(w fA.Lv |$HT$fA.HT0w fE.Lv DD$HT$AHT8A9A9}YIcfA fATfD.w fA.Lv L$HT$f.Hw fA.Lv T$HT$AHTA9|AEL$'fDDAG$REZDl$fAfD$DZDZD\$fJD\$DZDt$fAxfDAD$ZEZt$fjl$DZD|$fA`fDID$DZEZDd$DD$E|$D$D$LcT$LcL$KE1J E99Ic T\d lD.t|DDDL DT$D\(Dd,wL$D$Ev L$DL$D.D wT$D$Av T$DL$D.DLw\$D$Ev \$DL$D.DLwd$D$Ev d$DL$D.DL wl$D$Av l$DL$D.DLwt$D$Ev t$DL$D.DLw|$D$Ev |$DL$E.DLwDD$D$Av DD$DL$E.DLwDL$D$Ev DL$DL$E.DL wDT$D$Ev DT$DL$E.DL$wD\$D$Av D\$DL$E.DL(wDd$D$Ev Dd$DL$A DL,E9E9Ic T\D.wL$D$Ev L$DL$D.D wT$D$Av T$DL$D.DLw\$D$Ev \$DL$ADLE9kt$Dl$DEMAIhfD:BfD)BG,RfDqfAM@~E}D$D$LcL$HcT$E1E9K L &Icf fTf\fdfl ft(f|0fDD8fD.fDL@fDTHfD\PfDdXw fA.Lv L$HT$fD.IwT$D$Hv T$HT$f.ITw\$D$Lv \$HT$fD.ITw fA.Lv d$HT$fD.ITwl$D$Hv l$HT$f.IT wt$D$Lv t$HT$fD.IT(w fA.Lv |$HT$fE.IT0wDD$D$Hv DD$HT$fA.IT8wDL$D$Lv DL$HT$fE.IT@w fE.Lv DT$HT$fE.ITHwD\$D$Hv D\$HT$fA.ITPwDd$D$Lv Dd$HT$A ITXE9E9Icf fTf\fD.w fA.Lv L$HT$fD.IwT$D$Hv T$HT$f.ITw\$D$Lv \$HT$AITE9pt$Dd$DDf2fMnE1E1AMcMcABK N E1A9@Icf fTf\fdfl f|(fDD0fDL8f.w f.Lv L$HT$f.Iw f.Lv T$HT$f.ITw f.Lv \$HT$f.ITw f.Lv d$HT$f.ITw f.Lv l$HT$f.IT w f.Lv |$HT$fA.IT(w fD.Lv DD$HT$fA.IT0w fD.Lv DL$HT$AIT8A9E9}.Icf f.w f.Lv L$HD$AIE9|AAA`HIHHHHffffHMIHHHHffHHHHHffffffHHHH>HffffffHIHHHHÐHIHHHKaHffffHMIHHH!HffHHHHHffffffHHHH~HffffffHIHHHHÐH\$Hl$Ld$Ll$Lt$L|$H(O;Nt"H\$H,$Ld$Ll$Lt$L|$ H(DG D;F uDD;uDWD;Vu;JtD;B ffuD; uD;RuF,LOHZHnDZDw,D$ԋB,LL$DNH\$Hl$_D$ЉAB9B 9<9{9%BD5HEL$xIcIcHcH|$Ht$HT$D$DD$DD$ȋD$D|$Dl$AAHLD$HHD$HD$D+D$DXAHL\$HI)A4D+D$D)ADPAHLT$HA\I)D)A DtFDAAL;D$u+L$A!ADIA2A"IÃD! AIU9E IE2 IEI9~9}3L;D$u+L$̺AA2A"! AH|$HL$H|$HL$Ht$Ht$L$1*DA1A)L;T$u E"Dd$ąMbtrAA)AA~ArDMbAAL;D$u+L$A!E;ىd$DED$AE"IÃA1E!E EIt$čU93A4$d$DIAADL$E2 IEI9׉t$~9)D9~A4$L;D$u+L$D$ĉAD1‰!A"9DA1A)L;\$uA L$MctuAA)AA~A[DMcAAL;D$u+L$A!Ad$DAEAD|$AE"IƒA1E!E EI\$U93A$d$DIAADL$E2 IEI9׉\$~9)D9~A$L;D$u+L$D\$ADAAE"(AAA1A!E E(^9EAA)D)A)E1D$T$Dl$A;|$D)ʃ E"Dd$EMcMj~E{L~ EjDl$DMcMjL;D$u +L$!ljd$L$ED$AAAAd$L$DT$E"D$A1A!E EDT$ID|$DT$}A9aAME<$AIIʼnL$d$L$EDT$Ad$L$D\$ADT$E1ET$IA9D|$T$~A9D);D$~E<$;D$~ EMDL$L;D$u+L$T$D$L$AL$D|$D$EAE1AA)L;\$uA:A2;@t$EMcIrtpD)~ ARA2SDDMcIrL;D$u +L$!ljDd$DAAD$AE"!A EIDl$UA99>AA2<$d$DIHDEAD\$EIA9Dl$~A9D)D9~ DE2 $EL;D$u+L$T$DAD99DAHHHasHÐUSHWfDGxfDO`fDgh_DGJHjLJ DA,ЅHfDMcfD_pJ<AffffA*AE1A\E9\,D*EHcA\* *DHIcA*$*\E9AXD\DYDYEDYYDYDYDYDYEXEXDXA,AkEXIA,ЉAHHt"[]1fffffffffATUSHWfDOxfDW`fDohowJLbLR A,хMHfD2LcfDgpJ<AffffA*EE1A\A9D\,D*AIcA\HcA*,A*LHcA*AAXA9\DYEYDYEYAYYAYXA*LYXX,AA*\A*L DYA*lDYA*\ YYEXDXDXA,AT$EXIA,щAHM[]A\1fffSHWfDWxfD_`fDwhD_DWrHZLB D A,҅HfD% IcfDopH<AffffE*E1AD\D9E\,D*ARA\HcDAX* *T H*4\AYAYDAYEYYYAYX*T YX*tYX*TAYX*tYX*tYX*tDY*tYHcDX*lYDX*dYHR,HDXALD9,҉PA,׉PEXIA,҉AAHH [1fffffffATUSHWfDOxfDW`fDoh_OBHjLR DA,хIfD fDgpLcA*EE1A\E9D\kffff,D*EIcA\HcA*$A*lHcA* HAAXD\YAYEDYEYYAYAYXA*lYXX,A2A*dA*LAYA*lYLE9AYXA*LYXX,ĉFA*dA*lAYA*LYAYXA*lYXX,ĉFA*L A*lDYA*d YDYDXA*\YEXDXA,։V EXOA,щHLDK[]A\1fAWAVIAUATUSH HOfW`HT$@fOhGfDwxDD!HYHT$8T$(D$DiL$ BH\$0HY ,T$'H|$8IcHIIELcD$fD=eLD$EE,A*EfEnpD\E\B419}.HcAAY$AY9XD,D|fffl$ AX,D)L\$ADJo|$,1HĨ []A\A]A^A_fffDHL$0E1AE9HH4}AA,*ADXl$(\HcD*Y,A^ $ AF,D$D,AŋU$D$$D$HH$AMcL$IcAHIFAHcIE9M^ |DJDM$EFM($ADEF A9|y}(AA  ȨtAnAn A EoE AAAD ب/AGA+FH$EGE+FEE.ENMv EgGDA)Hc΋$D)J<1$ DjLAHzLcI :D$HJ0EIcL>1EDHD)HޅLRHHE)B D]AIDB Db$DZDMZ8DbY,AAA D Z.=82D$DUp]x+fZ.5YZT$DH\$ft$*$\ D*$A\l$PDL$X_5\$0\$(^$@]^$8A]fffD;$#$D$D $D $D $$ @!$ DA$ Ѽ$D$ AD!Ȩu$$w&f UI?L$AY$@D*$D$D\*$fD51$\d$X$0EDY-\?DYDt$HDl$ fD\$ fD%DY$@DX$0DXD\yD$A~fL$fY$8X$(X\ $~fDfT$HDXD$$$ADG$D$D$AA9ȉtO$pAʅ~lD$Z$WA*ٹA*XUpZ~ Xu.r'f $H$AAL$~ $DZD$yAWʅzD$Z$WA*A*XUxZ~fffXu.r&f $L,$DH4$~ $DZD$yf $A$f$@*$f5$\D*$D$D\DY|YX$0YDT$ t$8\u|fDL$ f-DY$@DX$0XD\ $A2|f JXf$8Y5X$($\{fD= fT$8DXD$/HV(N(DBDJzD*A_$E*E*A_A_D*$A_$$DŽ$H\$xf\$PfL$xfD$X\$pH\$hfd$PfL$hfD$Xd$`DŽ$ DŽ$ DŽ$ EFEN AGI~Mn EGEO IMo !AvEVAM^I^ AwEWM_I_ AWHAVAUATUSDa8DGDY DQTi(q@DIAXDd$E1D\$DT$E9ĉl$t$YDy DL$Dq$D$HLYLi0LQHw,1;|$}(E1Ʌ~C HcD@4(uDL$;|$|1D9}]E1E~C DHcDB4uDL$D9|1D9}-E1ɋT$~T$C HcDB4(uDL$D9|1;|$}3E1fff~C ffffHcDB4uDL$;|$|AE9%[]A\A]A^A_ffffffffffAWHAVAUATUSDa8DGDY DQTi(q@DIAXDd$E1D\$DT$E9ĉl$t$YDy DL$Dq$D$HLYLi0LQHw,1;|$})E1Ʌ~C HcDftEuDL$;|$|1D9}`E1E~C DHcDfA4CuDL$D9|1D9}/E1ɋL$~T$C HcDfAtEuDL$D9|1;|$}0E1ɐ~C ffffHcDfA4BuDL$;|$|AE9$[]A\A]A^A_fffffffffAWHAVAUATUSDa8DGDY DQTi(q@DIAXDd$E1D\$DT$E9ĉl$t$YDy DL$Dq$D$HLYLi0LQHw,1;|$}(E1Ʌ~C HcDʉtuDL$;|$|1D9}^E1E~C DHcDA4uDL$D9|1D9}.E1ɋT$~T$C HcDAtuDL$D9|1;|$}3E1fff~C ffffHcDA4uDL$;|$|AE9$[]A\A]A^A_ffffffffffAWHAVAUATUSi(wDQ8DITY@DAAXl$19DT$DL$\$DYDq Dy Di$DD$D$HLQLa0LIH1D9}4E1fffE~BLDffHcuDD$D9|1D9}kE1E~BLDHcAuDD$D9|1D9}7E1ffffT$~T$BLHcAuDD$D9|1;|$}8E1fffffE~BLDffHcAuDD$;|$|9 []A\A]A^A_fffffffffAWHAVAUATUSi(wDQ8DITY@DAAXl$19DT$DL$\$DYDq Dy Di$DD$D$HLQLa0LIH1D9}5E1fffE~BLDffHcHuDD$D9|1D9}jE1E~BLDHcIuDD$D9|1D9}6E1fffT$~T$BLHcIuDD$D9|1;|$}8E1fffffE~BLDffHcIuDD$;|$|9 []A\A]A^A_fffffffffAWLAVAUATUSEZEJ Aj8AR Ar$AZTABAJ(D\$Ez@ErXDL$MZ0Mbl$LMJHT$DGt$\$D$L$D|$Dt$L\$Ll$Ld$LL$t$+t$HD_$Dw(L$oD{C 1ALW0AΉl$D$B>T$T$L$AT$D9ÉT$E1D;d$l$}xD$Dl$DE~IDL$t$L$DfffffAHcDADECD Ll$HBB*uDAċ|$|$D;d$|E1D;d$l$}uD$ffffL$D~BDL$t$L$DAHcDADECD Ll$HBB*uDAċt$t$D;d$|E1D;d$l$}uD$ffT$|$~BDL$t$L$DAHcDADECD Ll$HBB*uDADL$DL$D;d$|E1D;d$l$}oD$L$D~BDL$t$L$DAHcDADECD Ll$HBB*uDAċ|$|$D;d$|D9[]A\A]A^A_ffAWLAVAUATUSEZEJ Aj8AR Ar$AZTABAJ(D\$Ez@ErXDL$MZ0Mbl$LMJHT$DGt$\$D$L$D|$Dt$L\$Ll$Ld$LL$t$+t$HD_$Dw(L$oD{C 1ALW0AΉl$D$B>T$T$L$AT$D9ÉT$E1D;d$l$}zD$Dl$DE~KDL$t$L$DfffffAHcDADECD Ll$HABfADUuDAċ|$|$D;d$|E1D;d$l$}uD$ffft$D~DDL$t$L$DAHcDADECD Ll$HABfADUuDAċL$L$D;d$|E1D;d$l$}uD$DL$|$E~DDL$t$L$DAHcDADECD Ll$HABfADUuDAċT$T$D;d$|E1D;d$l$}qD$t$D~DDL$t$L$DAHcDADECD Ll$HABfADUuDAċ|$|$D;d$|D9[]A\A]A^A_fffffffffffAWLAVAUATUSEZEJ Aj8AR Ar$AZTABAJ(D\$Ez@ErXDL$MZ0Mbl$LMJHT$DGt$\$D$L$D|$Dt$L\$Ll$Ld$LL$t$+t$HD_$Dw(L$oD{C 1ALW0AΉl$D$B>T$T$L$AT$D9ÉT$E1D;d$l$}xD$Dl$DE~IDL$t$L$DfffffAHcDADECD Ll$HAADuDAċ|$|$D;d$|E1D;d$l$}uD$ffffL$D~BDL$t$L$DAHcDADECD Ll$HAADuDAċt$t$D;d$|E1D;d$l$}uD$ffT$|$~BDL$t$L$DAHcDADECD Ll$HAADuDADL$DL$D;d$|E1D;d$l$}oD$L$D~BDL$t$L$DAHcDADECD Ll$HAADuDAċ|$|$D;d$|D9[]A\A]A^A_ffAWLAVAUATUSEZEJ Aj8AR Ar$AZTABAJ(D\$Ez@ErXDL$MZ0Mbl$LMJHT$DGt$\$D$L$D|$Dt$L\$Ll$Ld$LL$t$+t$HD_$Dw(L$oD{C 1ALW0AΉl$D$B>T$T$L$AT$D9ÉT$E1D;d$l$}xD$Dl$DE~IDL$t$L$DfffffAHcDADECD Ll$HAADuDAċ|$|$D;d$|E1D;d$l$}uD$ffffL$D~BDL$t$L$DAHcDADECD Ll$HAADuDAċt$t$D;d$|E1D;d$l$}uD$ffT$|$~BDL$t$L$DAHcDADECD Ll$HAADuDADL$DL$D;d$|E1D;d$l$}oD$L$D~BDL$t$L$DAHcDADECD Ll$HAADuDAċ|$|$D;d$|D9[]A\A]A^A_ffAWLAVAUATUSEZEJ Aj8AR Ar$AZTABAJ(D\$Ez@ErXDL$MZ0Mbl$LMJHT$DGt$\$D$L$D|$Dt$L\$Ll$Ld$LL$t$+t$HD_$Dw(L$oD{C 1ALW0AΉl$D$B>T$T$L$AT$D9ÉT$E1D;d$l$}oD$L$D~BDL$t$L$DAHcDADECD Ll$HIIDuDAċ|$|$D;d$|E1D;d$l$}oD$t$D~BDL$t$L$DAHcDADECD Ll$HIIDuDAċT$T$D;d$|E1D;d$l$}tD$fDL$|$E~BDL$t$L$DAHcDADECD Ll$HIIDuDAċL$L$D;d$|E1D;d$l$}oD$t$D~BDL$t$L$DAHcDADECD Ll$HIIDuDAċ|$|$D;d$|D9[]A\A]A^A_ffAWAVAULATUSHEuEU Am8AE Ee$EMTA]EE(Dt$AU@AMXD$HM}l$Mu0D_D$Dd$DL$\$DD$T$L$Ht$L|$Lt$ȋG(HwoD$$MUHDw D$L$V L$l$o$T$DFLT$DWADE)ʋL$ANE)E1ADL$DD$T$L$E9H_0L$KE1D;|$Dl$D$fffDDEA|$)DD$A D!AA!A DL$~\|$t$DDDAT$D+D$ D!!T$Hc D$DߋL$ADB HL$H uDl$ADD$DD$D;|$PE1D;|$Dl$D$fffDDEA<$)DD$A D!AA!A DL$~[|$4$DDDADЉL$+D$ D!!T$Hc D$DߋL$ADB HL$H uDl$ADL$DL$D;|$RE1D;|$Dl$D$fffffDDEAT$)DD$A D!AA!A DL$~\|$t$DDDADЉL$+D$ D!!T$Hc D$DߋL$ADB HL$H uDl$ADD$DD$D;|$PE1D;|$Dl$D$fffDDEAT$)DD$A D!AA!A DL$~\|$t$DDDADЉL$+D$ D!!T$Hc D$DߋL$ADB HL$H uDl$ADL$DL$D;|$PAE9H[]A\A]A^A_fffAWAVAAULATUSHHEEE} E]8Am AE$EeTEUA](DD$@EM@AMXD|$l$A8D$D|$Dd$0D|$Dd$l$<9l$D$Dl$l$A8D$D|$Dd$,D|$Dd$l$<9l$D$Dl$8D9l$|$|$D$fffffDT$T$D$D|$L$Ll$AEBT$EAD!AE!;T$OT$D;L$EN$D<$ɉT$D$Dl$Dl$PDd$L D$DVҁ!D$DD!9O9OAT$PAL$PF:HF 9B4D$IcF HFIcF|$ DD))T$DD)D)IcEʍ H|$ >l$A8D$D|$Dd$(D|$Dd$l$89l$D$PD9t$PuHX[]A\A]A^A_ÐAWLAVAUAATUSHXAoAw EG8Ew Eg$AGTEWA_(l$LEO@AOXt$HHIWDD$DIw0D_Dt$@Dd$D;l$@Dt$D$PT$fDT$D$T$Dl$T$Dt$T$D|$DDEA*DQDd$!AYʋl$DDL$D!;T$ZDN;\$T$LND$$,$ \$DT$LE(E(E(DfffAD*DəAYҍq!WWZW!D9AOD9AOWAADB"DH48B!H*48*4*)(Hc48Hc\*48(Y\Y*XY\X\XYXAXD.vA\HT$ Hc,Dۈ DL$AD$PDt$l$4DL$@l$D9L$PqD$\$<9\$Dt$[ D$\$<9\$Dt$T$fDT$D$T$Dl$T$Dt$T$D|$fffDAD*DaAY!ʋl$DT$DDDL$ZD!;T$Dd$DN;t$ND$$,$E\$DT$DE(E(E(DffffAD*DəAYq!WWWW!D9AOD9ZAOAADB"DH48B!H*48*4*)(Hc48Hc\*48(Y\Y*XY\X\XYXAXD.vA\HT$Hc,Dۈ DL$AD$Dt$l$,DL$<l$D9L$nD$\$89\$Dt$S AAE9'HX[]A\A]A^A_D$Dt$@D9t$DT$DT$T$fD\$D$T$Dl$T$Dt$T$D|$fffl$D$t$Dt$\$LDT$De!*AYEAAE!;T$Dd$DZDND;L$END4$D$$fl$L .\$LE(E(A(DAD*D֙AYӍN!Z!D9AOD9AOAADB2DB"HcDHH 9H8AB6AYHcDHH 9H8YA$HcDHcAY$H 8H:B&AAYHcDYH 8HcH8(YA AY \(Y\AYYAYXXX\\YXAXD.vA\HT$ Hc,D݈ DT$D$DL$DT$4DL$DT$Dt$@D9t$D$Dd$D$l$9l$\$ȉ\$T$fDd$ET$fDl$D$T$Dt$ffffL$D$DDd$|$DY!A*EDAYD!D9DNT$ D9NDd$l$܅\$D\$ EEEZDω*щAYҍq!ʉ!D9AOD9AOAADB"DHA* B!HA*,*)HcHcA*A*$\\YYYXXX\\YXAXfD.vA\Ht$HcDDA,։)D$D\$؋l$D\$l$|$9|$D$Dd$D9d$\$ĉ\$>AE9H0[]A\A]A^A_D$D\$D9\$\$ȉ\$T$fDd$ET$fDl$D$T$Dt$L$D$l$|$Da!*DDAYD!D9EDNT$$D9NDd$l$܅\$D\$$EEEZDffffω*щAYҍq!ʉ!D9AOD9AOAADB"DHA* B!HA*,*)HcHcA*A*$\\YYYXXX\\YXAXfD.vA\Ht$HcDDA,։)D$Dd$D\$ Dd$D\$|$9|$D$l$9l$\$ȉ\$D$Dd$D9d$\$ĉ\$T$fDd$ET$fDl$D$T$Dt$L$D$ED\$|$i!A*AY!D9DDNT$(D9NDd$l$܅\$D\$(EEEZDffffω*щAYҍq!ʉ!D9AOD9AOAADB"DHA* B!HA*,*)HcHcA*A*$\\YYYXXX\\YXAXfD.vA\Ht$HcDDA,։)D$l$Dd$l$Dd$|$9|$AWAVAULATUSH0EuEM Ee8AE E]$EETA]Am(Dt$(AU@AMXDL$$M}HDd$ Mu0DWD$D\$DD$\$l$ T$L$Ht$L|$Lt$G(HwMMHDw$E1D\$$D D$؋L$؋V L$nLL$T$DODgAEAAADd$)ʋL$NE)݉T$DD$ԉl$LG0L$E9ՉL$NfD5fD=BgD$,|$9|$,t$̉t$T$fDl$ET$D\$ED$D\$T$ED$L$(|$AAk*AYD!!D9DDND9NDd$l$܅\$D\$(EAEZDfffω*щAYэq!ʉ!D9AOD9AOAADB"D4*HfA B!HfA$Hcƍ4)fAHc\\YYYXAX X\\YXAXf.vA\HL$HcDDA,0D$,\$؋|$\$|$Dd$D9d$,D$l$9l$D\$D\$T$fDl$ET$D\$ED$L$D$D\$|$Da!*AYDD!D9EDNT$$D9NDd$l$܅ \$D\$$EAEZD!A\Ht$Hc,DDAω*щAYэq!ʉ!D9AOD9AOAADB"DHfA B!HfA$*)HcHcfA\\YYYXAX X\\YXAXf.,HT$Hc,DDA1D$D\$؋l$ D\$l$|$9|$tD$Dd$D9d$\$ȉ\$T$fDl$ET$D\$ED$L$T$ED$|$i*ҋD$ AY!!D9DDND9NDd$l$܅\$D\$ EAEZDfffω*щAYэq!ʉ!D9AOD9AOAADB"D4*HfA B!HfA$Hcƍ4)fAHc\\YYYXAX X\\YXAXf.vA\Ht$HcDDA,0D$Dd$D\$Dd$D\$|$9|$D$\$9\$l$ĉl$T$fDl$ET$D\$ED$L$D$DDd$|$DY!A*EDAYD!D9DNT$(D9NDd$l$܅\$D\$(EAEZD'ffffA\Ht$Hc,DDAω*щAYэq!ʉ!D9AOD9AOAADB"DHfA B!HfA$*)HcHcfA\\YYYXAX X\\YXAXf.,Ht$Hc,DDA1D$\$؋l$\$l$|$9|$nAE9H0[]A\A]A^A_fffffffffffAWAVAULATUSH`EuEe E]8EU AE$EMTEEA](Dt$TAm@AMXDd$PIuHD\$LMu0DDT$HD$DDL$@DD$<\$8l$4L$0HT$(Ht$ Lt$G(HwD$XDO$MeHD_ Do$ $V L$HDL$\$nDL$PT$AAADWLd$)ʋ $D\$ NE)DT$T$DD$LW0L$D9|$Xl$L$babD$\|$H9|$\t$t$; T$fD|$D$ffffDd$DL$DD$ Dt$T$TAAAL%Jh.E)DAYAHAAEXADt$E$AEd$AEl$AET$ Dd$AE D#\$ AAA #l$ A!AE!AE Dt$A  #L$ !DD D#D$ D!Dd$D l$\$ɋt$t$XT$ɋ|$TfDL$T$fDD$L3h.Et$|$ffffDL$|$DfEAAL "g.)qAAA!AiAqAy DODș D!A!эQA t$EDL$XD E!!D DGD D!A!C1A ЉH D!!J At$XEDD$XA B6A|$XYHJ C0HJB7AHJ C!YAHJB&YXAHJ C YXHAXAYJB'YHJ A)AYHXAJ.YHXAJ A(YHAXAYJ/YHJ AD A4AYLcD$XAHcIcXAYfEXAJ JYAY$HcN AY,XHcJ4CY4LL$(AYXAY<XXXAYXZA\ffTfAUfVfAfTfAUfV,C T$D|$T$L$/D$\D$\$$D|$L$pL$$D$D$Dl$lDd$D$D9$rD$$9l$|$x|$d"D$$9l$܋|$x|$\T$fD|$D$T$\D$|$\$DjA)DZE`DEHDAAAl$D d$D#$T$AD!l$Dl$\D d$EH=<.AD$؉L$`AE D#$A #$$AAA!ՃE!܉E A $D D#$D!DgD$D DoD$$DW D$2DD$D$T$ɋ$fDL$T$fDD$L<.EDD$D$DL$`|$`DfEAAL ;.)qAAA!AiAqAy DODș D!A!эQA t$`ED$D E!!D DGD D!A!D$A Љ D!! AD$HJD$ED$A$HA J D$YDHAJD$YHAJ C)XYHJB.AHJ XC(YHAXAJB/YYHJ C!AYHXAJB&YHXAJ C YHAXAYJB'YHJ AYXAA)XYHc.A4(D/LcH$XAJ HcKLcL$YAY$J IcAY4XAY,J AYAY<XXXXAYXAfEZA\ffTfAUfVfAfTfAUfV,A<1$D|$T$`L$D$D$D$Dl$\Dd$D$D9\$D$$9l$Ћ|$t|$TT$fD|$D$fT$TD$|$T$DjA)DZE`DEHDAAAl$D d$D#$T$AD!l$Dl$TD d$EH=7.AD$̉L$XAE D#$A #$$AAA!ՃE!܉E A $D D#$D!DgD$D DoD$$DW D$2DD$D$T$ɋ$fDL$T$fDD$L7.EDD$ D$DL$X|$XDfEAAL 6.)qAAA!AiAqAy DODș D!A!эQA t$XED$D E!!D DGD D!A!D$A Љ D!! AD$HJD$ED$A$HA J D$YDHAJD$YHAJ C)XYHJB.AHJ XC(YHAXAJB/YYHJ C!AYHXAJB&YHXAJ C YHAXAYJB'YHJ AYXAA)XYHc.A4(D/LcH$XAJ HcKLcL$ YAY$J IcAY4XAY,J AYAY<XXXXAfEAYXZA\ffTfAUfVfAfTfAUfV,A<1$D|$ T$XL$D$D$D$Dl$TDd$D$D9\$$D9$˅y$|$||$L$D$$L$DL$LD$$L$L$AEiE)AiE`DEXDAAA|$D d$D#$T$AD$PD!l$Dl$LD d$AAD$H 2.AE D#$A A#$AA!E!ADE A $D D#$D)D!DqD$D DyD$$Da D$ʋT$$$T$D$fDT$T$fD\$T$L3.DL$DD$T$DD$P|$PDfEAAL2.)qAAA(ApE@ AxDGD D!A!ЍQA t$PED$D E!!D DOD D!A!D$A щ D!! AD$HcHJ JED$A$A$D$AY$HcHJ JAYAD$AYYDHcHJ JAYA D$AY YHcHJ XJAYAAYC(YHcHJ JXAYAYXAB.AYHcHJ JAYAYAC)AYHcYHJ JAYA B/AY HcHJ YJAYXAAYC YHcHJ JXAYAYXAB&AYHcHJ JAYAYA AY C!XYHcHJ JAYYXA AY B'HcHJ A(JD.A4)D /H$AYYXA AY HcJ HcJAYAYXA AY IcAIcJ LcJAYAYXYA,AY,HcK NIcAIcJAYYC4NAY4Hct$XA,CY,AYAYYAYXXAYXDZD\o'AfEfEAfDTfAUfDVAfEAfDTfAUfDVA, >D$D|$DL$PL$$D$l$LD\$$L$D$Dl$xEDl$D $D$(|$DD$DD$$|$D$AE`E)EXAiDAIEAADl$Dl$D l$T$#$AD!d$E܉T$HA l$AA T$A A#$E!A#$DŁH=,.A$D D#$E!A A D!D/D$D DwD$$DDg T$YDL$(D$T$ɋ$fDT$T$fD\$T$L,.DL$D$DL$fffDD$H|$HDAfEAAL+.)qAAA(ApAxE@ DGfED D!A!ЍQA t$HED$D E!!D DOD D!A!D$A щ D!! AD$HcHJ JED$A$A$D$AY$HcHJ JAYAD$AYYDHcHJ JAYA D$AY YHcHJ XJAYAAYYC(HcHJ JXAYAYXAB.AYHcHJ JAYAYAC)AYHcYHJ JAYA B/AY HcHJ YJAYXAAYC YHcHJ JXAYAYXAB&AYHcHJ JAYAYA AY C!XYHcHJ JAYYXA AY B'HcHJ A(JD.AYYXA AY HcJ HcJAYAYXA AY IcAIcJ E)JIcAIcD/AYH$AYXYA,AY,JJ IcAIcN Hct$AYYXA,JAY,A4CY4AYAYYAYXXAYXZ\=7!ffTfAUfVDfEfATfEUfAV, >D$D|$DD$HL$$D$l$DD\$(L$D$Dl$xEDl$<$D$,|$ffffDD$D$D|$DD$@L$$D$l$D$D<$DD$8L$$D$l$4D\$0L$$D9$9H[]A\A]A^A_ɋ$D$l$LD\$$L$$&D$$9l$|$x|$d8T$fD|$D$ fffffT$dD$|$d$DjA)DZE`DEHDAAAl$D d$D#$T$AD!l$Dl$dD d$EH=.AD$L$hAE D#$A #$$AAA!ՃE!܉E A $D D#$D!DgD$D DoD$$DW D$2DD$ D$T$ɋ$fDL$T$fDD$L.EDD$D$DL$h|$hDfEAAL .)qAAA!AiAqAy DODș D!A!эQA t$hED$D E!!D DGD D!A!D$A Љ D!! AD$HJD$ED$A$HA J D$YDHAJD$YHAJ C)XYHJB.AHJ XC(YHAXAJB/YYHJ C!AYHXAJB&YHXAJ C YHAXAYJB'YHJ AYXAA)XYHc.A4(D/LcH$XAJ HcKLcL$YAY$J IcAY4XAY,J AYAY<XXXAfEXAYXZA\ffTfAUfVfAfTfAUfV,A<1$D|$T$hL$D$D$D$Dl$dDd$ D$D9\$ɋ$D$l$DD\$(L$OVɋ$D$l$$D|$L$pL$$D$D$Dl$lDd$D$D9$rD$$9l$|$x|$d"D$$9l$܋|$x|$\T$fD|$D$T$\D$|$\$DjA)DZE`DEHDAAAl$D d$D#$T$AD!l$Dl$\D d$EH=.AD$؉L$`AE D#$A #$$AAA!ՃE!܉E A $D D#$D!DgD$D DoD$$DW D$2DD$D$T$ɋ$fDL$T$fDD$L .EDD$D$DL$`|$`DfEAAL .)qAAA!AiAqAy DODș D!A!эQA t$`ED$D E!!D DGD D!A!D$A Љ D!! AD$HJD$ED$A$HA J D$YDHAJD$YHAJ C)XYHJB.AHJ XC(YHAXAJB/YYHJ C!AYHXAJB&YHXAJ C YHAXAYJB'YHJ AYXAA)XYHc.A4(D/LcH$XAJ HcKLcL$YAY$J IcAY4XAY,J AYAY<XXXXAYXAfEZA\ffTfAUfVfAfTfAUfV,A<1$D|$T$`L$D$D$D$Dl$\Dd$D$D9\$D$$9l$Ћ|$t|$TT$fD|$D$fT$TD$|$T$DjA)DZE`DEHDAAAl$D d$D#$T$AD!l$Dl$TD d$EH=8 .AD$̉L$XAE D#$A #$$AAA!ՃE!܉E A $D D#$D!DgD$D DoD$$DW D$2DD$D$T$ɋ$fDL$T$fDD$LN .EDD$ D$DL$X|$XDfEAAL 2 .)qAAA!AiAqAy DODș D!A!эQA t$XED$D E!!D DGD D!A!D$A Љ D!! AD$HJD$ED$A$HA J D$YDHAJD$YHAJ C)XYHJB.AHJ XC(YHAXAJB/YYHJ C!AYHXAJB&YHXAJ C YHAXAYJB'YHJ AYXAA)XYHc.A4(D/LcH$XAJ HcKLcL$ YAY$J IcAY4XAY,J AYAY<XXXXAfEAYXZA\ffTfAUfVfAfTfAUfV,A<1$D|$ T$XL$D$D$D$Dl$TDd$D$D9\$$D9$˅y$l$|l$L$D$$L$T$LD$L$L$DjA)эzEaDEYDAAAl$D d$D#$T$AD!l$Dl$LD d$AH d.ADD$ĉD$PAE D#$A #$$AAA!ՃA!E A D$D D#$D)!DqD$D DyD$$Da DD$DD$$D$T$D$fDT$T$fD\$T$Lp.DL$DL$DD$DD$P|$PDfEAALQ.)qAAA(ApE@ AxDGD D!A!ЍQA t$PED$D E!!D DOD D!A!D$A щ D!! AD$HcHJ JED$A$A$D$AY$HcHJ JAYAD$AYYDHcHJ JAYA D$AY YHcHJ XJAYAAYC(YHcHJ JXAYAYXAB.AYHcHJ JAYAYAC)AYHcYHJ JAYA B/AY HcHJ YJAYXAAYC YHcHJ JXAYAYXAB&AYHcHJ JAYAYA AY C!XYHcHJ JAYYXA AY B'HcHJ A(JAYYXA AY HcJ HcJAYAYXA AY .LcHcK A)NAYHcAYHc/Hc|$XYC,AY,NJ HcHcN AYYC4NAY4H$XC,CY,AYAYYAYXXAYXDZD\AfEfEAfDTfAUfDVAfEAfDTfAUfDVA, 7$D|$T$PL$D$D$Dd$LD\$$L$D$l$xEl$D$D$(|$fffDD$DD$$|$D$AEhE)EXEaDAIDAAl$D d$D#$T$AT$HD!l$Dl$DD d$E܁AT$H=$-AA #$A #$AAE!E!ADA A $D D#$D/D!DwD$D DD$$Dg T$^DL$(D$T$ɋ$fDT$T$fD\$T$LE-DL$D$DL$fffDD$H|$HDAfEAAL-)qAAA(ApAxE@ DGfED D!A!ЍQA t$HED$D E!!D DOD D!A!D$A щ D!! AD$HcHJ JED$A$A$D$AY$HcHJ JAYAD$AYYDHcHJ JAYA D$AY YHcHJ XJAYAAYYC(HcHJ JXAYAYXAB.AYHcHJ JAYAYAC)AYHcYHJ JAYA B/AY HcHJ YJAYXAAYC YHcHJ JXAYAYXAB&AYHcHJ JAYAYA AY C!XYHcHJ JAYYXA AY B'HcHJ A(JD.AYYXA AY HcJ HcJAYAYXA AY IcAIcJ E)JIcAIcD/AYH$AYXYA,AY,JJ IcAIcN Hct$AYYXA,JAY,A4CY4AYAYYAYXXAYXZ\=ffTfAUfVDfEfATfEUfAV, >D$D|$DD$HL$D$D$Dd$DD\$(L$D$l$xEl$<$D$,|$ffffDD$D$D|$DD$@L$D$D$Dd$D$D<$DD$8L$D$D$Dd$4D\$0L$$D9$/H[]A\A]A^A_D$D$Dd$LD\$$L$D$$9l$|$x|$d,T$fD|$D$ T$dD$|$d$DjA)DZE`DEHDAAAl$D d$D#$T$AD!l$Dl$dD d$EH=C-AD$L$hAE D#$A #$$AAA!ՃE!܉E A $D D#$D!DgD$D DoD$$DW D$2DD$ D$T$ɋ$fDL$T$fDD$LY-EDD$D$DL$h|$hDfEAAL =-)qAAA!AiAqAy DODș D!A!эQA t$hED$D E!!D DGD D!A!D$A Љ D!! AD$HJD$ED$A$HA J D$YDHAJD$YHAJ C)XYHJB.AHJ XC(YHAXAJB/YYHJ C!AYHXAJB&YHXAJ C YHAXAYJB'YHJ AYXAA)XYHc.A4(D/LcH$XAJ HcKLcL$YAY$J IcAY4XAY,J AYAY<XXXAfEXAYXZA\ffTfAUfVfAfTfAUfV,A<1$D|$T$hL$D$D$D$Dl$dDd$ D$D9\$D$D$Dd$DD\$(L$JMD$D$Dd$HX[]A\A]A^A_D$l$89l$Dd$Dd$T$fDl$D$T$fDt$T$fD|$D\$ԋT$ԋ|$l$ԋL$DD|$A*AY)׉DJBDG #\$wDDDDY ?mDEY!DYDX ݃EAYADYADYADYAE A DYD#D$#t$#|$AAE!!A!EXDDYoqE A Dd$l$D\\$DYD\$DYE\DDY ulDXlDYDYEXDXYDDYD\L$L$L|$DL$|$ffEEDA*AYAD)EAqDY-kDAYYDX D!A!AYߍQYA DYDY D!փYD! AyAEDD$LXY5#p D!\!C DYHAt$L D!A!CYA A|$LHcEDL$LD|$\Y-kX5kYYAXXYDDYD\AZB&HAZB'YHAZ C!YHAZ$A(YXHAYXXAZ$.HAZ/YHAYAZ A)YHYXAZAGHXAYXAZHAZ YHAYYXXAZ AHYXAZ BB4HcIcAYXAZ HcHt$YAZX D!XC!At$pA!HA \XC\A*AB&|$pEDD$pHcHD|$A*YAYB'fEHA* YC XHA*$YA)XHXA*$.YHA*AY؍/AYHA* YA(XHA*YAG XHXA*YHA* AYȍAYHXA* YAXHXA* YBHcIcXA* YA*0d)L$D$p|$\9|$pt$t$ T$fD(l$fD|$T$fDt$D$fDd$DL$DD$T$hD|$ AA*AYE)EYAhDAAAHEAAA #l$fAYXE!AfAWA l$YYY #\$YD!AEDD ݉AE D#D$DD #L$D\DXA!DXE!E D\ EXDd$l$\$D\$D\DXt$t$l|$ht$Љ|$EEDAA*AYD)EHqD D!A!Y܍QA DXf D!fAWD!Y AxAYYD D!\YEDL$l! DD\At$l C!A!D!XHXA AZB&EXA|$lD|$HXAZ\EB'DD$lHAYAZ YC HAZ$XYA)HYXXAZ$.HAZ/YHAYAZ A(AYHYXAZAHYXXAZHAZ YHAYAYXXAZ AHYXAZ CF B4HcIcHcYXAZ C LD$8LcYAZ,D$L|$89|$Lt$t$T$fDl$D$T$fDt$fE(fffffDd$DL$DD$T$DD|$AA*AYE)EYAhDAAAHEAAA #l$fAYXE!AfAWA l$YYY #\$YD!AEDD ݉AE D#D$DD #L$D\DXA!DXE!E D\ EXDd$l$\$D\$D\DXtt$t$H|$Dt$|$EEDAA*AYD)EHqD D!A!YڍQA DXf D!fAWD!Y AxAYY D!\YEDL$H! D\At$H C!A!D!AXXHA fA B&XA|$HD|$H\fAEDD$HXB'HYfAYC HYXfAA)HYXXfA.HfA/YHAYYXfAA(HYXfAAHYXfAHfAYHAYYXXfAAHYXfACF B4HcIcAY$HcAY @>HƋ|$41A9}LcG9E4|D$4t&DT$,LcT$,C"AE"1A E1fffft$LD$HLD$LD$8B 6fE1AE)AAAHHA;L;l$tLXK1HX[]A\A]A^A_fff<$L\$8L\$ DL$@tj|$D$$EDT$0A%AAffrgAtAfffuLfffLO+댋L\fLNYAtIAr#At-Afff9Lbf*LIffL裁ff LffLcffL'ffL3ffHDA $L$$AAv DHffL$ADHÐLd$Ll$Lt$L|$H\$Hl$H(IIL$HHt$HH$1M\$H$H|$H$HT$HAfL$HAHt$HHDŽ$fD$HtMtA,$A;mt:H$H$L$L$L$L$ H(fAL$$$A;]uf.$sf.rET$EL$H$H$ED$ H$AEI|$H$`D$H$L$D$L$f$f$A]H$D^H$D$IUEBH$$Ay $$XD$H$D$$$$$$*A$$)Ɖ$*jA)Ãt6fffAED$$$H$$D$H$$$$$D$H$H$H$D$D$H$D$$H$H$≄$ 1$D$D$D$D$DŽ$) H5?+IcHHL4Lt$PKD$$L$p$D$H$L$H$ADL$(|$D$H$T$LLD4$DŽ$HDŽ$HDŽ$\$0H$HD$ D|$Ah$$AL$$D$L$$$$ L$$H$D$L$L$$~ZL$H$HcADH$LH$L4$HD$ՅAtH$HDE@A!tAHDŽ$ @A tDŽ$L$A L$(tk$H$\$0D$D$H$$D$1ɉT$(Ht$ LDD$LE1D$DT$$?AEu#AAAtAH$ H?AffN>ffffKAAD$bAAAAt A wD$ADD$ AD$A$$DŽ$L$$$H$L$Hc,E$D9 ;$~kA܉+$D$D9$$D$EIcH=HH$HQ$119}Lc9B|Lc$II 1HL$x$9}LT$xLcC<$9|狄$$$ 9}L\$xLc9C<|Hc$IU@AIuMU(IM8HT$pHt$XLT$hDHL$`U+$A$H$ff-*$^$+$*$1X\$XU9Y\.$D$AD$AAAA*/A*YH$LcX9,BDf.wDE*AfEfEfDTfAUfAVD$X$\D$`L$8H$@D$Hc$D$$D$ L$A$$H \8+H,H$H$v $LT$h$H$L$LD$pA Ht$XLAzI} HT$LL$L$LL$`L$HT$xT$PAƉD)$1AxH$f%f=E|$^$D)*$A*׍C1X9\$XY\!$D$A$*$AA0E*AYH$LcX9D,Ff.wDE*AfEfEfDTfAUfAVH$H$1vD$AIcH9HH$H$e9H$ HtS9H$HtA9D+$j$LD$hAT$LcL$H$Ld$pHL$hA8I} L$APHt$XOHl$L$$LL$`Ll$HT$xT$PAH$8H$H$8o$HcH8HH$Lc$+$LT$`1ɋ$ C+$9SL$LcC9~7D$AIcH(8HH$WfHDI$L$$L$APA!ШtAtHL$f$L$f$LcHÐH\$Hl$Ld$Ll$HH\$HL\$AML͸tHt v;wt(H$H$L$L$HfWf.sf.r ff{/{ &A?D u@ƅ t |L\$f\$L$H$Ll$HLD$HT$MD$13s,HLLAWIAVAUATUHSHH`L$H$LD$PL$H$Ht$XL$D@HEg0AO,AWdwDE1DD$@Dd$8EGTEg`EyL$IcAE9AA*HcDYAA*HcDYrAAX*HcD YjA AX*YbHL$PDXuE1ۋt$LE9IcHHL$fffD!HcJ9DHT$A0IcAE9HcHcAA*HcDYAA*HcDYjAAX*HcD YbA AX*YZHT$PXXtgE1ۋt$LE9IcHHT$ffffD!HcJ9DHT$A0IcAE9HcHcAA*HcDY2AAD*HcDDYBA AAX*YzHL$PXX44t{&E1E9ЋD$LffffDD!HcHcHcJ>IcAE9AA*HcDY:AAD*HcDDYJA AAXD*DYBHL$PDAX<|E1ۋT$LE9IcHHt$DIcDA!A HJ8HD$E9HcHcAQD*HcTDYA QD*DYPHL$PTEXDX D |E1E9ЋD$LfffffDD!HcHcHcJ>IcAE9AAD*HcDDYA AD*DYZHL$PDEXD|EkD;l$ LL$D$McLd$1O4DD9HMMLItILfA.fAffA^fAVLcLt$fC4fGD9fBfvffHc4LuD*HcLHcTDYDM*DUY*DXAsYDX*AKY*ASYEYX*ArYX*AJY*ARYAY*AqA4IXA QYDX*Y*YX*YHcD9ATXAYXDXAYDXEX$E$H$1$D9fDfDOfDGfULcd$tJ<L$l#D$hHJ 0Hf1fQL$dT$tLcJcTJc DdUDMA*E$KYD*DYA*E$SYDXE*E$JDYEYA*E$RAXYA*E$IYAYHcD9E*E$QXDYDXATA*YAYDXDXDYEXEXEDE1D;$&L$x$$H$(H$xEYpEQtEL$HV`D\$PDT$LL$L$L\LTLLT$HfffH$xD$TD$D)MD$TD$TxH$$f7fofg1D9IcH4fffL$PL$H#D$LHJ,0ADHfD.Hc,A kAjD*D*A iEYHcEYD9AT*AYDYDYYEXDXEXErfDl$TD;$H$1틔$D9fDfDNfDF}IcHH$fL$P#D$LL$HHJ<0H$DHcf_f/fwfOHcE* HcDDYNHL$8E*DYFBDAXAXB<|E1ҋT$4E9IcL<DIcDA!A HH$HcHcLE9E*HcTDYHL$8A* YHTDXDXD|^E1E9؋T$4NDMcDA!HcHH$Hc4E9E*HctDYHL$8A* YHBTDXF|ECD;D$H$LL$McD$Ld$1DO,DHfAmfAeD9MMLItILfA]fAUdLcL|$fC4fCD9fB<YYYXfBXYXCX4C4|ECD;D$DLl$L$IcLt$1MdD$EfA$$fA\$DD9fAT$HM ItILffffLcL|$fC,fBfAvfAnfAfIcAAL MHcH$8fADfA fYYXfA YXfA YXXfffTfUfVffTfUfV,9H$8LD9L4aL$$$HcH$@H$`EEA0HL4H$0LLdLL1fA6D9fAnfAfpIcAALMHcL$8fA,HH|$ DDD!HHD$pHD$ HcH8HpIcH|$l$IcAJ IcDAY,J IcDH|$ |$AY{H$`$E1L$H$McH$DDK0HBLIDA9A(LcA(IA(ICH?AIcL$HIT$4DD$4H D$4D|$4DYT$4L!|$4EXYfDD$(L|$(DH IǃI D|$4t$4Xf|$(HL$(H H ȉD$4D$4.r L!H CD$4D$4IM!.LBLD|$4L!|$4H ?HH ,ψHT$(~l$(I :.rL II L|$(D~|$(IAA.LBALAH I E9؉T$4DD$4A,@9H$I< L<AL$IcY ITT$4d$4XfL$(Ht$(AHH!L ‰T$4T$4A.r H!HCT$4|$4HH!D.HB‰D$4L$4D,DH$H!H ?HDDt$@D;$D$AED+$D;$EMD$L$`$IcH$H$L$E$DLHAL$0HlHtL,L|DAA[D$tAcH$Ak E1A9H$x,E(HcA(HA(D$McL$L$xKLK4H$KIcAE9fAHcDYfAHcDYZfEHcD DYZH$XfEIcAE9fA,HcDY*fE HcDDYJH$fA DYJAXXA,|vE1ɋ$E9cHcHHt$xfffffDIcDA! HJ8HD$xE9HcHcfA,HcTY(H$fA ЋTYHXAX,A,|E1D9׋$fffDIcDA!E9HcHHcJ8fEHcTDYH$fA$ЋTY`DXE|sEaD;$L$$IcL$1M4D9HM\MTMDILfA.fAffA^fAV LcL$fG fG49fGfBDYDYDYYEXEXDXGX G |AI;$L$IcH$1M,Ë$fAefA]D9fAUHLLDHLOLcL$fC,fC9fF YYDYXAXCX,C,|AI;$L$IcL$1Mċ$fAfASD9HMDILLcL$fC$fF,9YDYAXCX$C$|v$H$1L$McH$D9HfC L;L$t$L\$ILcL$fCYCXTL9O\|$DAD+$;$AM퉬$H$$McL$H$ND1H9M,MdM\MTfA(fA`fAXfAPjL$t$Lt$IHcH$fEdfEfEffffLcL$`fFfG<9fG4fFDYDYDYDYEXEXEXGXG|AR;$vL$$LMcH$h1OD9HLLLHLfA fAXfAP~ffffLcL$`fC,fG9fBYDYYAXXCXlCl|,EZD;$~H$$LMcH$hND1H9L HLfAfAPHcL$`fA$fD$9YDYAXAXdAd|$LH$hMcH$H$XDHfB L19`N LLcL$`fCYCXL9K|$H$IcL$h1L $LMH$XfA)fAaD9fAYHfAQM$I\MDM\N MHcH$`fE$fD fEfE,DYDYDYDYEXEXEXDX$D!L9L,|TH$IcL$h1L$$LH$XfA$$fA\$D9fAT$HI\M\MLNMHcL$`fDfEvnf IcH4L$L#D$HHJ 0D L$DHcADD9HHcDEEEYEYEYEY ATDYDYDYDYEXEXEXEXElffffDl$PD;$H$$D]DUDMDE 1D9}IcHH$L$L#D$HL$DHJ40H$ADHHcLHcTD>v~V Hc4HcD $A,D4YY YEYYDXAYDXA$YDXA AYEYXA,YAY4HcXAD9YAYvnRIcH4Љ#D$v}IcHHL$pЉD!HJ 0HL$p DYiDy L$0AHHc4HcLHcTHcD E4E EADYAY IcEYEYADYAY,A9ATEXAYEYL$E1D$E9E $ED$A|$At$ Hc$Lc$H,$DDD$$#$#$HH$#$HD`HcHJJ :H4:N$2DF<F4EYBDYIcAAYE9DYAYEY$YEXCY$AXXEYAYDXYDXDYDXEXDXDDŽ$D$D9$L$`L$`L$ AB`AytEAhEYpED$l$$|$pDD$hAylEALD\$tHMtH$pL$h$$D$xH$pH$ L$h$L$|L4L|HHIIIŋ$)ȃMD$xD$xH$D$.f^1D9LcN L$tD#D$pL$lHH$E D#D$hHcE‰!HHD9F0B<8EYAYFY (DYYDYDXEXDXDufffDt$xD$D$D9$Dd$|$L$1D$$D9A E EAAy}Hc$Lc$LcLcHH$`fffL$tDL$l#D$p#T$hHH$H$`!DhHcpHP J2J :J*DB$BF4EYBYF<YDYBY,YBY4DXB$DYDXYBYAYDYHcEXD9AXEYDXXDXXDXAYDYDXEXDXDG$H$E1ۋ$E9DxpHc$Lc$LcH,L$tDL$l#D$p#T$hHH$H!PHcDXHJ2J :J*DFIcAE9AA*HcDYAA*HcDYrAAX*HcD YjA AX*YbHL$PDXuE1ۋt$LE9IcHHL$fffD!HcJ9DHT$A0IcAE9HcHcAA*HcDYAA*HcDYjAAX*HcD YbA AX*YZHT$PXXtgE1ۋt$LE9IcHHT$ffffD!HcJ9DHT$A0IcAE9HcHcAA*HcDY2AAD*HcDDYBA AAX*YzHL$PXX44t{&E1E9ЋD$LffffDD!HcHcHcJ>IcAE9AA*HcDY:AAD*HcDDYJA AAXD*DYBHL$PDAX<|E1ۋT$LE9IcHHt$DIcDA!A HJ8HD$E9HcHcAQD*HcTDYA QD*DYPHL$PTEXDX D |E1E9ЋD$LfffffDD!HcHcHcJ>IcAE9AAD*HcDDYA AD*DYZHL$PDEXD|EkD;l$ LL$D$McLd$1O4DD9HMMLItILfA.fAffA^fAVLcLt$fC4fGD9fBfvffHc4LuD*HcLHcTDYDM*DUY*DXAsYDX*AKY*ASYEYX*ArYX*AJY*ARYAY*AqA4IXA QYDX*Y*YX*YHcD9ATXAYXDXAYDXEX$E$H$1$D9fDfDOfDGfULcd$tJ<L$l#D$hHJ 0Hf1fQL$dT$tLcJcTJc DdUDMA*E$KYD*DYA*E$SYDXE*E$JDYEYA*E$RAXYA*E$IYAYHcD9E*E$QXDYDXATA*YAYDXDXDYEXEXEDE1D;$&L$x$$H$(H$xEYpEQtEL$HV`D\$PDT$LL$L$L\LTLLT$HfffH$xD$TD$D)MD$TD$TxH$$f7fofg1D9IcH4fffL$PL$H#D$LHJ,0ADHfD.Hc,A kAjD*D*A iEYHcEYD9AT*AYDYDYYEXDXEXErfDl$TD;$H$1틔$D9fDfDNfDF}IcHH$fL$P#D$LL$HHJ<0H$DHcf_f/fwfOHcAfEfDWd$'fD$DXD$E(\D$Efl$\fT$YT$E\AXDD$fDd$D\GE@EDYd$DXf\$EXJE\DhEyDXfD|$f4$YY\$D8fDD$EfD\$DY$DY\$EEJAYDY|$l$fd$AX\A\\$EXd$t$D|$H[]A\A]A^fAWJAVHcAAAUG6FATAG<A*McDUII fEAfEWE\|$AXfE\$D)l$fAifA fEWfDoffE!fEHf|$fT$X|$EDXT$D\ADXfA(EX;A\DA\\XfL$\L$EXft$fAsf|$fWl$SD!AeXA\EAXD\T$AEXD$fd$fD$\D$E\?fAXfWt$XFDIE]D\gED\t$X\AEpAAIEkAE9MILfffMcfD$fDl$IfDT$fl$IDYT$fD|$Y,$ft$MILfD0I4Yt$f$$H|fDfyIfDY<$IDT$fDD$Xl$fDMDM LTADYY|$E9AYDY\$YD\Y\$l$fwfDd$A\fD(AXfDXDYAYD\DXDt$ED\DXf~DYY<$A\EDYL$EYXAXE\XAXD\\T$fEfDWD$L$fEfA`fD$fDT$f$ft$fAJfDd$fDYYd$AYDYL$DYfW|$A\fDAYAXfDJAD\AXXD\fA"l$fA)\$fAYYYl$YY$\Yd$AYXfl$XDA\DA\DXDX\fL$\fT$A\EXAXD\t$AXD\l$ffWt$Ha)fD.fW\$DvfT$X|$fDD$fl$D_fD\$ Efd$fD$Yd$EAXYD$ fL$EX?|$A\f|$fD|$0D\Rf$DXDf\$fDD$DYD$(D\DYY\$0A\Y|$DYDXYT$(E0EhAiD\A EE\EJXXD\$D<$|$T$H@[]A\A]A^A_fffffffAWAAEfWAVAAUATU͍ESDˉDHH$DHcH *f-f%,)L$pL$`L$Pf\d$xd$hd$XYYlX$Y\$$ HĘ[]A\A]A^A_E1E9}IcLcHcLD$L,Hl$L$HL$McfD|$hfd$`fL$x9fDt$pfDT$POOLfAPfEfAfAiOT O\fE"fEZYYl$hfE+DYY\$`fEKAYDYd$pD\fd$XEYDY\$xEYXDYL$XA\AYDYl$Pl$HEXEA\fDT$HAEXEEXDX\T$HXAX\E\|$8E\T$@t$0d$ Dd$(HL$HfffHcfL$Xft$PfA$fElfAfA|fDD$hfDt$`fADfDd$0AYfA,DYl$XYYd$PDYY|$hD$EXfD$8fD|$fT$\ft$(DY|$pDYY\$`AXDd$EfEYT$xAXAXD\d$8E\EfE ED\t$(fETD\l$0Xf|$xEXYE\fEDYl$pHDXIA\fD|$@H)D\DX|$ XfT$@\T$ DD$@DDX\X\DD$8L$ fL$\$0l$(HIH)0DxILH)H)9D DhD2RfDt$8f|$0K fD|$(fT$@K4!XT$ K"H)H)fDd$xfD$EXD\\$8H)DYD$pfD$AAXDYl$pft$@D\T$0EXD1D\L$(\t$ yVK #D>DfD$H)DRE9D qEYDY$E\EXD\$xf\$xEYfl$xADd$pfd$pAYAYXD\\$`Yd$`AYD\$h\l$XfDL$pfDT$xDYT$`EYEXDL$P\HĘ[]A\A]A^A_H$DzH$DAUA͉DDATIHDYDUHSH8H<H H *fD-*fD5AD$L8L$NN J4H,0HcCT-fD|EYDYlC 9AAXD$fDl$DYE\fD|$D$DDYDYEXE\fDd$EYEEYEEYAEYAEYAEXEE\EEYEYAEYAAYE\AYEXAAAAYA\AYt$EAYAYAY|$AY\Xl$EY\L$A\$EXAYl$XL$LcƒfBfBl9fB\fBtfCLfGTfBDfC|$AY$fCtAYDYfC\$AYfCAXD$fG \fB$X\f$AYDY$DX\$$AYFYD\DG@D\Df@D_ f D`f^0fDVPDn`fDPfDwfvpfDO0fDgpfAfW\$ADffWl$E\fE\fDWL$AXfDvE\A\DXEADE\DAXfD E\EXDXAE\fD%EYEYEYEYEXAXA\A\A\DGA\~AXEXO0n0gPf-fDVP_pfDF(DvpfDo(fgfD_fOhAEYEEYAYEYEXfDnD\fwHAE\AXfDFHY=AXYE\fD~hEDYD$d$DYAYߘD\$EYA\DY DXEYAD\DXAXD\f8AXE\T$fV8AYd$AYDDYfWxfEEYfDNfDWt$D\fXY 0AXfD_fEfDWD$EYDXD\AEXA\fDfXAY5ȗDYY\fvxDYD\AXD\AEXA\L$Y=fL$Y5\L$Ȁt$DXf|$\ft$X|$Xt$AD\Xfl$vft$f|$\|$A\DXD$ffWT$o(AXf(OHAAD\|$EX~HA\DGhDD$XDnhwA\\EXng8N8DX^XD_xDNxffffffAA1D9DaHcуHD9L 2L:fEfAYfEyfErfAafAfABED\EDXfAZDDXA\DXAAA\\fAXEffAWAXfAWDXD\E\XXE A9\EBD\EaAjAIAREYffAA1D9@ B)L$HcуHD9L:L 2fAbfE fAqfAZfE:fEAfAB(fEi0DXfAi fEq(fEZ8fAR0D\fAaEDL$fE f|$DXD|$EA\AD\XfArffWd$DL$\DXfAZ fDd$\|$D\DDXfAA8EXfWt$A\D|$f|$DXA\A\fDl$AEXfD\$DXl$A\ff|$f|$EXfWD$EfWT$AXDXXE\D\|$f|$DT$Dft$Dd$\DX\AD\XfL$E|$AfD$E\AXD\DXfT$fEfDWD$EXE\DY=DDY5ffW\$D\DY~AXAXAX\AD\YQXf|$A\A*A!fl$Ej fd$EY fDl$ARfD\$fT$AIAAXA\E\E\A\AXXEj(D\EY(AzAqAj0Aa0ARAIAB8EI8ffffffAWDQAVMcAUATAA*UASADAH`L5#*fD\-H|$PHt$HH|$HDL$DLcfCLfGlD\EHcLT$HLL$HJ,HMcHL$PHt$PYNIDYCY$Lt$HIfAL|$PfEALd$PMHDl$0fDd$0EXMIEXd$ d$fA&fA7D$$fD'fA$YADEYXED\DXD\fA\Dt$8fT$8A\AD\T$fDXfl$(l$XfDfAW\D)D$DXHct$DLl$HfAWD\AHD$PHT$PDEHE\HLEXH<M3DXfD5pX\D\D:A}D)EE $Ld5A*HLAL3M E&fA$$fDf/f1fE:fAfE)ffWd$EDffEfWt$A\D\fDWD$AXfDEAD\XE\A\DXAED\AXDA\EXY-fEYAYEYEX\\AXE\DA\XDX>A $DAA!ED$T$D9T$IMfffffLcl$HT$PHL$HHt$PH|$HfD|$HfDl$f\$IHY$L)LL)IH\5LL\=fD\$fHD$I H\$L\$DY\$f/HD$HD$\$fDD$fL$M41DX\$Y $M<9fDHD$MDYD$I2M:LD$LlLd fD M EYDYL$D\$EYDYd$D\E\fD.EXEE\EXAXA\DT$D$l$fE6fA/fd$f<$f fEft$fEMfE$$fD|$YYl$fDT$AYDY4$HD$AY\YL$EYAXAYfDt$EYDYd$EYDYL$DXf $\ft$EXfDAXX\f HD$E\fD \$fAA\T$fEYHD$EXE\EYd$YEXY$E\fD ffW|$\YT$EYXfd$Xd$AYfEfDWD$A\DffWl$EYDDX\ADXD\fEfDWt$\EXA\fEfDWd$&fT$fDD$DXD$fd$fD$\d$\D$Ht$fl$(\X|$DH|$EAHt$A&AD+fT$EXA;|$ADXYT$ E\f\$H|$E\DHT$AXD\YE\DX A}E,$l$fd$DE8DD \d$fD|$ft$8fDd$fD$fD$Y\$ DYd$(D$AYDY\$0L$DDY|$09L$DYD$8AXA\EX\$t$D<$dH`[]A\A]A^A_fffAWJAA*DAVHcAUATUSHL*f5ryDŽ$Ӥ$H$Lc$H$D$fA\fAdD$L$EY EH$IMcYMAB,AL|$pYL$L$IcILcDHLJ,MMDXD$D$D$$DXLt$hLl$`L$L$DYDYIIE\A\D$f$$$$H\$XL$H|$hHt$`fE9fE2IfAL$LLJD-H|$PH$Ht$HHt$pHD$@H$DL\$hIf'D-zND%f>H|$HMLD$0Ht$@L\$8LD$8HT$`DD)l$\DXfA(N"HT$PD\$(fDfD"AE\AXfD9EL$ fE\EXfEDD$fEfEWEXD\fA]DT$fEDA\fEWfE,$EXDA\ED\DXf/EX\Xft$HD$0fE f|$(fT$ fL$(X|$f0XT$A\L$H$L$H|$pfWl$Ht$HX:f|$A\AE E\A$fD$ EX\D$AXfD\T$fWt$XAD'E A"f$$X\EA}D\AXD\4$A$$DE3HD$@HL$PH\$8D$|HT$0 $9D$|D*.HL$XLt$hL|$`HL$HcD$|H$f$fD$L\$fD$DY$f$HY$fD$HH$f$H|MfDMY$f$H|$fE0DY$I<HtM DT$fA2XT$f?Ht$I4AYDY$fD$H\5I M,6YD\H\$H\$M$DYY$T$fDL$Ld$Ll$fD#H\$M$M,7L\ A\fD*f+AXD\DYDXAYD\$fDED\DXfA9DYA\EEYE\AXD\\$H\$f$fD$Y$Y$fD H\$DYd$fEf#H\$fDWD$AYDY$Xf$AXfDd$DYY$\XfA,$A\fDAXfDL$f AfAfW|$XDYXAYD\fA#D\T$f$\$fA]YY$YY$\Yd$AYXfl$A\DXDAXDX\fT$\*DXAEXAXfffWt$A\fW\$fd$D\l$ED\t$f$fl$HD$H\$\X|$D\A\A LD$E*E1LL$f$|$fDl$A8Y$f|$EfD\$EDXLT$D\AXEXE\DXDfD$>ED0E,$AmED AY$f$\$fD$fD$f$f$fD$DY$Y$D$|EYY$$DY$9|$|DY$DXD\EXD$D$D$H[]A\A]A^A_AWJ AVLcAUATUSH0L ]z*f-oH$ DŽ$D*Ӥ$AfGlfCdH$DCY4D$Lc$$$D$DEYAYI$$$D$A,EELcA$$EXIHcXH,McMIEYYE\\D$fD$$$D$L$H$ L$ H$ L$L$ H$L$LH$L$ H$H$ML$f#IL$L$H$LL$HMH$H$DMLfE$fA;H$H$L$JT=L$L$L$H$pEXfD)NTMML$xL$hA\L$`O,D$XfD0AD\Xf?$PA\AXf$HH$fDD%.pL$L$hL$xfDH$pAfE L$`D)d$fA.fAWf AXfEUfD$XE\fD$PDX$HH$ H$H$$@f$XDX$@fEA\$HfEWfE'EE\EXD$8fE8AD\XfA*\Xf$0fA4$D(fWl$DXA\ED\DX$(ffWt$fD$PAXfD$(D\$@H$A\f$8fD$8X$0Hc$D\$0L$DX\$(L$L$xL$DEAEH$pDXL$hH$`XA&H$ D\E1AD\E(EA]E $H$AD#D>HH$ HHH$HHTH$H$fD8L$Lc$H$LD5L$L$HL$L$H$H$EIL$L$L$H$Mf9L$ IM47L$H$fD2ML$DXMNdL$L$ML$L$D\J|MLD$fDfA]L$H$H$EE\EXfE7AD$D\XfE$L$fAL$fE.L$H$fEH$DfA$$H$fA+AH$fDf$EXfD f$XX$L$A\fDWt$A\ED$fE A$f$X$\$fEfWd$DXD\f/DXD\A\AXf$f28fWl$XA\EE\EX$ffWt$Af$f$AfD$f$D\\$XH$DX$L$DŽ$AX\$L$D\$L$L$H$H$H$H$H$AXE]E $\E;EEA8'D. D $9$ L$L$fffLc$H$ f$f$f$f$Y$L$IY$fD$LL$fD$I<ffM,HLIH|$xH\$xDY$K<fA;Ll$pXO,HL$hfD3fD?I KNTK4.M$DY$O/$HD$`LT$XAYDY$Ld$PHt$HMLd AYDY$Jt-E\\EXfE3X\A\EXT$@D$8|$0H\$pfD$fD$fD$f$f$fH\$`f;H\$hDYY$f H\$XDYY$f+H\$PE\DfD;DYH\$HY$EYXfA$AYEAYDY$E\fDDXfA AYDY$EXDX\f$E\fD$DL$(fD$\D\$ fDf1EXfE}YY$fEEYAfDWD$E\EYAXfEX\fd$@Xd$(EYDY$D$fT$fW|$A\DY$DXEYDXAE\DE\AXEXD\fEfDWT$fEfDWL$"fT$8fl$0AfL$ fD$@\D$(H\$xLL$pfD\$\X|$8AXD\t$ A\DXD$0A H\$`|$8fL$/ALL$hD3H|$XDDD$0DXH\$HA9f|$E\DfDD$A\A\AXAXD\EX9HL$PE]D1#A EL$ A4$D>f$f$f$fD$IfD$MK4L$O,JDI<6fDOI7Ll$HD$H|$HD$H|$L$H\$Ld5fAfAIf8fD'Ld$H$H|$LM,LTMO$M AYDY$fYY$HD$Ll$LT$O,NTfD8Y\DXf>X\E\AXT$D$D\$fDH|$fDEYEYA\EEYEYDE\EXD\DL$HD$fE4$f$f$Y$f$fD DY$DY$fD$H|$fEfDWT$AYDY$AYDY$DXEXfE9f$A\AYf2EXfE#DY$AXE\fEAE\L$fAXfEuAYXEYfEfDW\$\EYDY$D$T$fT$A\DY$XEYEXfDl$DXl$EDXD\E(EE\AXD\EXfEfDWt$fEfDWL$fl$fd$fL$\|$\L$HD$LD$H<$HL$fDD$H\$fT$A\DX\$Ht$A\DXT$DX(fl$HD$A 9f|$DD\$DDD\DT$E\AXAXE\A\A;L\$DEXE,$AAmA3fD$fD$f$f$f$E"f$fD$fD$Y$EYY$$YDY$D$DY$D9$Y$DY$D$f$DX\A\AXD$$$$jH0[]A\A]A^A_ffffffAWAfWAVAUATUSˍCHf-Jf%\H$H$AADAHcAH f*d$xfD$x)؉$T$pf|YLT$`T$PD$hD$XY$XY\$$%HĨ[]A\A]A^A_E1D;$}Hc$DD)L4HcL$H$McIfDt$hf|$`fd$xfL$pDMLfDT$POMffAE9fE;fA(K4O I6fD&K<fA1EYDY|$`fD+YYl$hfDAYDYd$pD\EYDYL$XAXfD|$XYYt$xl$H\EYDYl$PDXDE\DA\EXfDL$HEEXEXD\\$@DX\T$HEXE\D\$0D|$DD$(T$8Dd$ ffHcfDD$hfL$`fAfA4f,f$fD$fDl$PfEfAfE[I fE|fA\fA LcIcIHM$LI4A\$fDl$fAl$L$AXL$fA $XfE"H,Dl$fDkAYDYA\EXD\\fARD4$fD$EE\t$t$EXXDT$fD\AAXED\DXXD\AEX{EXEt$E\D\D\D E<$AAsEffDEjfDYf)fEfvfD}fD5hDEAXE\fDZD\AXAED\EXDAXD\AD\EXA\AXY-*hEYEYAYEXAX\\E\D A\zX!DXYD.NUD]D9 D'AMMID)D$Lcf\$fDl$If|$fD\$I DY\$fD|$Y|$fd$II4 I| DY|$fDT$M L)fD&fvIfD7fA(MLMA\fD\$D9DYYt$AYDYd$DY\$AYDYt$A\fD)AXfDaEXfExED\AD\EXfwDYEYDXDYYt$L$DL$fDL$A\DAYDYAXfD0DXE\fDD$AXAXEYD\f`A\D$T$DYEXfD AEXA\\$DYt$fDBYd$fAfL$fl$fARfE:YY\$A\EAAYD\DXfAaYYd$\AYfD\$YDX\$XffW|$\DfD\$AYD\\$EDXX\AEXA\D$D\X\f|$X|$\fL$D>\L$ED\yVD_E`fDd$ADXd$E(XADX\zDXD"D8fD$\D$fD\$D\ft$f|$PfD$$fL$fDl$fl$YL$EqEBAYDYl$AE fDL$DY\$Yl$DYDY $t$Y|$fDT$EXD\D\AXD\$DT$Dd$|$H[]A\A]A^ffffffAWJIAVHcAUATAEA*UAMcGT-SIBI AE\AXfDT$fAVEE\EXX\D$Dl$fDofAYf3fE`fDT$fL$DXT$A\L$fD$D\EXfAEEAEX\Xft$E\Xl$fkX\d$D\\D$Asft$Xt$DXA\EfDQEvDXD\AAXXD!A\E7D\AgAAxAIDkD D9MILՐLcf,$fDl$IfD$$fDL$KDYd$fD<$DYL$fT$MI fDI4YT$fd$H|fD1fyIfDY|$IfDT$f7EXfDgMM LTD9DYY<$AYDYt$YD\DL$fDD$fDL$Y\$A\fD(AXEYD\EYDXf,$D\$fDXED\DXf~DYY|$A\DYt$EYXE\fEDXAXAYD\fA`AXA\D$DYL$A\fD AD\XT$DYD$fA)Y$$fT$ft$fAfDd$fL$YYl$AXfDBAXD\t$AD\XfAaYD$fABYd$H\Y\$DYAYAYfD|$XXf\$A\DD\X|$AXD\l$DX\AEXD9|$A\D.?Xf|$fDl$\ADXQDvD_EAXDXD\DX:\DjEfL$fD$fD<$fDl$fDt$f|$ AXfDT$fT$A\DYT$\YT$EYDY|$A E fDL$AAEBfDD$AYDYD$DYl$DYL$ EX\E\D|$EX|$D4$Dl$"H([]A\A]A^A_ffAWAAEfWAVAAUATU͍ESDˉDHH$DHcH d=*f2)D$pD$`D$PfLf\T$xT$hT$X$XY\$$ HĘ[]A\A]A^A_E1E9}IcLcHcLD$L,Hl$L$HL$McfD|$hf\$`fDt$p9f|$XfDT$POOLfAPfA fA fAiOT O\fE*fEZYYl$hfE#DYYL$`fEKEYDY\$xD\f\$xEYDYL$XAYXDYd$PAYDYl$pA\l$HfDT$HEXDA\DX\T$HEXDE\D\ADXT$@AXE\\$ EXDD$8t$0Dd$(HL$HHcfDD$hfL$`fAfEtfA$fElft$PfADfDd$0fA,AYDYt$hf|$xDYY\$`AYD$fD$8EXfD|$fT$DY|$pD\fL$XYAXYT$xAXDd$Yl$pEEfEDYl$XD\d$8A\fD|$@D\t$(YYd$PDX|$ \ft$(EXXfT$@AX\T$ EAXfE fETD\l$0E\fEDHIDXH)D\X\DD$@D\DX\$0d$(|$ f|$DD$8xIH)0DxILH)H)9D DhD2RfDt$0fL$8K fD|$(fT$@K4!XT$ K"H)H)f\$xfD$EXD\T$0H)DYl$pfD$AAXDYd$pD\\$8ft$@EXDqD\L$(\t$  VK #D>DRfD$DH)E9D qDYY$E\AXDT$xf|$xEYfD\$xD\$pfl$pYDYXE\|$`Yl$`EYDT$hD\D\$Xfd$pfDL$xDYL$`AYAXd$P_HĘ[]A\A]A^A_H$DH$DdfffffffffffAUA͉DDATIHDYDUHSH8H<HH {7*f% -D$L$L8N$fD$N J4H,0HcCT-f\fDlC 9DD$fDl$DXDYE\fD|$D$DDYDYEXE\fDd$EYEEYEEYAEYAEYAEXEE\EEYEYAEYAAYE\AYEXAAAAYA\AYt$EAYAYAY|$AY\Xl$EY\L$A\$EXAYl$XL$fffLcƒfBfBt9fFTfBfBDfC|fBl$f$AYD$fGTAYDY$$fCfCtAYfG\\D$fCdAXfG \DXXD\AY$D$fD$AY$ADYT$DY$D$AYGYEYfGf/fDn fDv@fG fDg@fDG`fV`DEADXAEX\E\AXAXEA\E\AXDXD\D\X?AXD\DW@E\D^@w f o`ffPfFpD~`fOPfWfDn0fDWpfDw0DDAXD\E\AXfDnXD\EAADD\AXDAXY-KE\fD5KEXA\DEYAEYDXAY\AXEXD\E\A\XD^DO0^0DPvPWpDnpfDg(f~(fDofgHfD1f1fGhEYAYAYEAYD\AXfDfAXED\DXf~HE\fDVhL$DAYD|$EDYDYEYXA\DAYDYDXE\AXAXD\fw8A\fDWl$fnX\$f_XAYDYv8EYDAYYEXAYD\\ffxE\EXfDNXf\$AYX\$D|$EYE\EXfDwxffW#AAYfDD$AYfDt$DXD$D\t$\\DG^AXX\A\A\A\D$DXEXW(AN(DwHfDD$f|$fD$AD\D$XX|$\\D$\D\DFHDohDXDfhDX^W8N8GXD~XDWxDNxffffAA1D9HcуHD9L:L 2fA*fEfABfEyfAJfEqfEjfAQDE\DXEXEE\DXAXEA\AA\AAXDXD\E\XXE A9\EBD\EYAbAYAjEQfffffffffffAA1D9! )L$HcуHD9L 2L:fAifE9fAbfArfAfAfAQfE1DXfEB(fEa(fEZ8fAI0X\fAqDD|$fEzD\fDl$fAi8D$fD$DX\$EDt$fEr0D\DXfAb \fAQ EXA\fDL$A\AXDl$fDl$D\D$X\D|$A\AXEE\EXfD\$E\DX\EXfDT$DXDT$fDT$DL$ft$DfD$\fL$fl$DXAEXA\fD|$ffW|$A\t$ft$E\AXDD|$A\E\l$EXEXE\EXDDY58ED\DY%*Eft$DXAf\$\fT$DY EY%DXt$AXAXEEXE\DXAEZ fT$fD\$D\AA AXAJEyXE\EZ(EQ(EJAqEj0Ai0AZAQfD$fL$A\\AB8AI8fffffAWDQAVMcAUATAA*DEUADDAS\-LcHpL5$*H|$`Ht$XHt$`D\Ld$`HcfG|fC\J,fC HL$`HMcL|$`INfA$H|$XAH\$@L$0fDl$@AXL$ f XMDL$TfE'LT$XLL$XDl$Lt$XAYfD/DYIHT$`IfEEXfA)MA\D\\fADt$HfDT$HEE\t$8t$(EXXDT$fD\AAXED\DXXD\AEXEXE\D\D\D Hct$TLl$XHD$`D$ T$TA}D9E1HA$A2HLELd5E.fH<HLL3fE$M3M fDf)DfA2fAfE9fD5AE\9T$ AXfDED\AXEAD\XDEAXD\AD\EXA\AXY-fAEYEYAYEXAX\\E\DA\>X!DXA$D/A AEuDlIMD)D$Lcl$ HT$`HL$XHt$`H|$XfDt$(HfDl$ fDL$(IHfD|$ L)LL)IH\5LL\=DYL$M41HD$I H\$L\$DY|$M<9HD$HD$f|$(f\$ fEI2Y\$fl$M:fD HD$MY|$fDT$LlEXffE f0AYLD$Ld M EYDYd$ D<$f$$DYYt$(\AYE\fD.DYAXfD'EE\AD\XfA7EXDYL$DD$A\DDYD\AXD\T$DY\$fEuYt$fA$$Y$fDD$(fD|$ HD$fL$fl$EYDYt$ AXDYYd$(DXA\AXfD EXA\E\$fEXHD$E\fDfHD$YY\$EDXfDD\fA D\$DY$YYd$\Y$YXffW|$Y|$A\DDX\fL$\fT$XL$XT$X\AfL$fD$\L$A\D$\Ht$A\H|$DX>ft$8AEXAfL$ AYL$0AHt$D+EH|$E\E#EEXfD\$(DX\\AYD*fDl$ DY\$0DXHT$DYl$8DXD!D\A}A$t$(fDT$(D>E0DDD\EXDT$(D\$ f|$fDd$Hfl$fDL$Yl$@D$ DYL$HL$TDYY|$@9L$ D\AXDd$|$Hp[]A\A]A^A_ÐAWJAVHcAUATUSHL2*H$H$A*fE\fE|AD$$EL$fEMcL$IAAB,:ALcL$AXLcAAXJ,L$IIcMIHMAYLL$@L$AYMD$D$DT$xD|$hMLl$PLD$X\Ld$HL$\I$f$$$d$pH\$8L$H|$PHt$HL$L$IL\$PfE1LJL%HT$HH|$0J<&Ht$XfE8HL$(H$IfA>fDMJDL\$ NHT$@EHD$HD$0LD$ Ht$(EXfD fDE\fAf6Dd$fD!AE\AXfE7AE\AXfE $ET$fAUE\EXX\Dl$fE*$AD\Xf\Xl$HL$fA fD$fDT$ft$DXT$f)\t$DH$H$LD$XHt$@H\$0HL$ DX\$$DXDE\AHT$(XDA7fDD$D\AA&fd$DXD$\d$AAAXEXE\A\XXED>D\EeE$D\A2'A; D D$d$9T$dHD$D(HL$8Lt$PL|$HHL$HcD$dH$f$fDl$xL\$fD$DYL$hfT$xHYT$pfD$HH$fDd$xH|MfDMDYd$hfd$pH|$fE0DY|$pI<HtM DL$fAXT$f?Ht$I4AYDYt$xfDT$hH\5I M,6YE\H\$H\$M$DYY$T$fDD$Ld$Ll$f3H\$M$M,7L\ A\fD*fD#AXD\EYDXEYD\$fDED\DXfA9DYA\DEYE\AXD\D$H\$f$fDL$xY\$hYt$fDH\$Y|$pf#H\$AYDYD$xDXft$hDYY$XfAA\fD A\AXfDAXfDd$AD\XAfA,$D\L$fL$XfAeT$fT$pAXYD$fYd$pYYl$h\Y\$DYAYXAYfD|$A\DXf\$D\DX\AAXA\EXXE8X|$HT$D\l$LD$\D\t$fL$AfDT$fD$H\$fD$xY$|$A\E*fDl$DXE1:fD$f|$EE\LL$LT$DXDXEYDY$AXfDd$xD\DY$D\D$9D.EAA $EUE DfD$DXE\D$Dt$xfDl$pf$fT$hfDL$hY$DY$D$d$9|$dAYDY$\EX|$pDl$hYH[]A\A]A^A_ÐAWJ AVAUATAA*DEUASHL=j*H$HcD$D$Lc$EfE|fEDH$fA D$McH$H$IDIL$AAC,1LLEAXAD$AX$$D$D$D$L$H$HcAYH,AAYMcMID\\D$f$$$$H$H$H$L$L$xH$H$LL$L$fDIH$L$L$HfE $MH$H$LJT=EMH$pfAH$H$`EXH$fDL$L$E\fD!L$L$D$HfD0ANTMML$hL$XO,L$PAD\XfDA\EXD$@fD*AXE\$8L$pfE'L$XL$hL$`H$PfEfAMEfAfD$8fE $f$HX$@H$EXH$E\EH$H$L$DX\$8D$0fE8EXE\AD\XfA$(f$H\$@A\AX$ f+8D1&DAAD\X\AXX$D\XD\A EfD$0f$(fD$0f$(DX$ X$Hc$D\$ L$\$L$hL$pL$XH$`H$PE1H$EH$A8H$L$EAEA4$A#D#D>HH$HHH$HTHHH$H$H$ILD5L$H$fD8Lc$H$L$L$L$fDL$EfD2L$L$ML$IM47L$EXH$MfAL$MNdML$L$L$D$fD.L$L$L$DfEMJ|MfA)LL$AH$H$E\AXfE7E\AE\AXfE $fA$ED\DXDX\AD$D$L$fE H$XH$H$D\fAfD$AH$DX$Af3fDfD$L$f*L$XL$L$D\fL$DfD$DX\$$ADX\f$X\$A\E$DX$E\EXA7A&AMAE$E;E)EfD$f$f$XH$\$XH$D\H$H$DXH$H$DŽ$\D\E7& D D $9$4 L$L$Lc$H$f$f$fD$f$DY$L$xIY$fD$LL$f$I<f fD*I LlIH|$hH\$hY$K<&fE$D\$pXl$pHL$`Ll$Xf;fD7I O,#DY$O'JD%MK4.LD$PO/HD$HLT$@MAYDY$l$pYY$Ht$8D\LT Jt-\DXfA<$XD\E\AXL$0Dl$(DT$ H\$`fD$fD$fDL$pf$f$fH\$PfDH\$XDYY$fH\$HEYDY$f+H\$@E\DfD+DYH\$8YD$pEYDXfAAYAAYDY$E\fD EXfAAYDY$AXDXE\fD$D\f$\f1T$fT$pD\$fD\$0EXfEmDX\$YY$EE\EXfE X\f&EYDY$D4$L$YA\DYD$pAYDfDt$EYDXf\$0\\$H\$hDLL$`DX\|$D\fT$ XE4$f$EAXD\AD\Xfd$(A\DXd$ E\E\A\DXT$(\Dd$ AX#H\$PAH|$HLL$Xfd$EX;H\$8ED'fDd$fD$\DXDXD!HL$@EUDD3A AA2L$D.f$f$f$fD$IfDL$pMK4!L$O,JDI<6fDOI7Ll$HD$H|$HD$H|$LD$H\$L\5fAfA If0fD'L\$HD$H|$LM,LDMO&M AYDY$fDYY$HD$Ll$LD$O,'ND%f(AY\DXX\f>E\\$L$AXDT$fDH|$fD/EYEYA\DEYEYDE\EXD\Dt$HD$fE f$f$DY$fA!fD DY$Yl$pfD$H|$HL$AYDY$AYDY$EXDXf$fDD$pAYY$\EXfE$$f2AXE\fE(AE\D$fAXfEMEYXEY\f\$X\$EYDY$L$fL$T$A\DYD$pXAADXfd$EYA\E\DDX!fT$\|$EXDXT$HD$D\ALT$H|$H\$Ht$fL$E\fD|$fd$AXfDl$A\D\l$DX\$A\EXHD$A\E*?DD\$DfD\$fD$DLT$AXE\EXfD$DX\EYDE<$DAA AeA2E D$fD$fD$fD$f$f$f$DY$fD$EYDY$$Y$$Y$9$DY$Y$E\DXD\D$DXD$D$D$H[]A\A]A^A_fAWAfWAVAUATUSˍCHfH$H$AADAHcAH %*)؉$T$xD$pT$hfLf\D$`T$XD$P$XY\$$/HĨ[]A\A]A^A_E1D;$}Hc$DD)L4HcfffffL$H$McIfDt$hfD|$`fd$pf|$XDMLfDT$POMffEE9fA fA(K4O I6fD.K<fADYfD#Yl$hDYfDYL$`E\fD|$xEYYDYL$XAYY\$xXDYd$Pl$HA\EYDYl$pEE\EXfDL$HEXD\D\$@DXDX\T$HED\ADXAXE\T$8D|$DD$0t$(Dd$ fHcfDD$hfL$`fAfE4f,fA$f|$xfD$fDl$PfAAYfD\$8DYt$hDY,$f,d$ft$f$Yt$pfD$EYDYd$XDX\$D\fL$XYYD$xfD|$(D)Y\$`DHYl$pD9\ft$0D\$fD\$8EXD\\$YYd$PAXAXfDt$@XfD$@D\t$ A\XD$ EAXED\l$(DD\d$0fD $A\EXDXT$@fD$A4DDd$fDd$D\IXfffW%DE\fW-E\D\DXDd$fDd$fDl$EX|$AD|$A\fL$DXD\fD$XfDd$DfDt$Dl$EDXfW5zDX\fd$D\fl$ED\f\$EXE\DDY= ffW,E\XDY- D\$A\DY% \AA\fDL$ifl$AXY` AXa@AHE\EXfDD$XYA\IDqPD\D fDL$EXy(E\DA DIXi`fd$f\$fL$DQhfD$AXXA\\a0Y8IpAxfffLA[DӉ19}fD(\$LcڃI9I ;fa fY8fDa0fDi(ffDIfDqfyDX\a0DX\Y(DEXD\IDd$DXA\ffEXED\|$fAWfAWAXE\D!XDXiDy \D\Dq(IDQQ0DI8ѺS) AAHL )L$ff ~ DӉ19LcڃI9I ;fDa fDi0f9fDyfY8fi(fDIfDqEXfQ@D\a0faxfAHX\Y(fDQ`EDl$AXfL$fDiPl$EXfiXA\fDypX\t$E\fDqhDX\QPL$fL$EXD\QpDX\ahX\AXDAXD\D$DX\fd$L$\XL$AE\AXEE\EXEL$A\Dl$AXDA\EXAXfDfW-#E\DXDY=EXDY\DY5}Yu!f\$fd$fD\$fD$X\fL$E\fDd$A\Yf\$AXaHADY@DAXAPAXIAE\DXa(Y\E\DaXAXDY XA\q`D\DAhY0I8ypDIxeLA1[DӉ19}fLcڃI9I ;f1fDYfyfDyfQ fDq(fDi0fDa8DA\DXAXAE\AXDAXAE\DA\ADXXD\\EXDIX)A\DQ D\I(DAyq0DY8 ffffffffAW)AVAUATIUSHH`T$TL$T Dt$TT$L$L$TA)L$HHHD$D$T ~ f L$TAAD)D$HD$@t$LDd$Hl$Pd$@AA1;l$P|$DT$fEEDIfA^fAUYYDYYEA}AVA]\$T$D D HD AHHcfA fE|HE9YDYA fE4E|fElDYDYE4Eln[]A\A]A^A_9f%{_A)EfAfWAA*AE^DAAE)D9YYW}H=g)fffffDD|=1H48AD D9DHD,8DA? HHA ADAJ A Mcf9fQI9O$fA4$fA\$YYYA<$YAT$1Yu\$T$ HcHD9fE +fED+DYDYE +ED+[]A\A]A^A_ÉAAAAiA)E9l$LL-f)DD1DDHG)B,(AD L$D9DDC4(HB<(|$ t$ĉl$L$ d$LcD D d$IHcLcL$K D t$HH9LAIKL LRH:LBHcT$LQNL HID9J<IRLLOHHWMMJLAE9 IAAAAADE)AE9H-e)DDDH.D (A 1ADAD9}gffffD.H<(D HH N<DIwID LcID9KH:LBH HrI?MG|AE9]pffffffffffAWAVAUATIUHS&9f%[)ˉfA,$AAA*^AA)DyIcE9YA,$f]Y]fAYAfLYLL-"d)fD1HF (DB)DA ADAD9DDC4(HB( DD HcD D HHcHcD HHN'H /J< H(fE0fD)fD'fDDYDYDYDYD7D(HcHE D9DJ< L(H .J&fDfEfDf9DYDYDYYDD DA8DD E HE AfAIcE9YAfTYTfA YA fD|DYD||[]A\A]A^A_9nAf%YAA)Ef7DAAAAAA*^EDE)AE9Y7f.Y.}L !b)D1HB<DB D DD9DDCHF4 HHA H4(ADAfDE IcHD9NB8 IcAЉ։HcD D HHD fDLf4Hf,fDdHcHcHHHfLH )EYfTfD,AYE9AYEYD$fD fD$$ fD$ AYAYfD$ f$EYD$\$EYEXD\$fD$$fD$DX$DXD\$AD$f$D$f$f$$AXAAXA\AXXD\yE\q(!Dq Qf$fD$f$AXE\A\IDY0i8AE ~AEHcALiVUUUAI E)D)AAMAEEE1AAMDŽ$DŽ$ )$Ӥ$ DŽ$DŽ$Ӥ$Ӥ$AADŽ$DŽ$Ӥ$Ӥ$E9D$DLD)T$`LrR)DD $DH$GB$ $D$D$AD IcA A A AAHHcHHE ffDtHcfDf|HcHHE f4fDDIcfD IcIcHHHfD$;f$fTfD3D$fL3D$fDLIcD$$H$D$D$$fDd;$D$$f,fD$Hf$f$H )fD$f$fD$f$fD$f$$\$fD|EXD\$AXf$X$D\$X$D$fD$D\$D$h$PX$$D\$D$xX$$pDX$D$`D\$$XD$H$@f$f$pX$fD$hf$fD$fD$f$fD$xf$xf$XfD$PXf$XD\$EX$8\$AXDX$`E\\$`d$HX$@Dl$8DX$8\$0\$@D$0DT$\$p$(D\$hDt$(|$t$PDD$@D$ fD$Pf$HD\$8f$HX$0fD-n\$0fnfDt$HfDL$8fDT$Hf|$8DXt$PD$fD$(DXL$@L$pD\T$P$T$Xfd$X\|$@D1EXD\$(DIDQ@A\DX\$pA\DYEnDYYD$ fDl$X$EXDY-nyHf$f\$ fDD$(fDd$(fd$fDt$ft$0fD$0fT$XfDL$AXX$ \$ EXD;$\$E\D\|$ I X$Y(D\$DAqDaXAPi`AXDyhE\Q8DIxa0DqpAMAAAAAAMAE1A)AAAE9L='L)fffffDD HDB<>B8 IcAЉ׉D D HcHHD HfD flfD,fdHcHHcHfDDHffD<fDADH )E9AXD$f$fD$DXEE\\DDXXAD\ED$D\$EEXXA\D\EXD1AXyE\q A\Da(DYIDi0a8"DDfufD]Mc1IIcH}Ar1t$xfDD$xfDd$xEXE\DEBtH|C<Dd9 fffLc)HLTNTf|19ffAWB||H[]A\A]A^A_fDsfDCDAfD{fD AE1E*DY%OLMEXLM(EXEXEXEEYE\EEYE\EAEYE\EYAXADM DU8D]}D]0ffW5rCu1H[]A\A]A^A_Am f=?@AD)fCfDc`Afk fK(E*fDC@fcA^fD fsfDkPfD[XfDs0fS8f[pYDYYY$fCHDYD$fDchDY$fD$f$$f$YYEDYE\YEXDY$DYDXDY\f$YAXX$YY{x$D$D$D$D$D$x$p$$$hf$fD$f$f$f$fD$fD$fD$f$XfD$fD$xDXf$x\$\X$DX$$0D\$D$D\$$XX$D\$$`ADX$h$X\$hD$PD\$D$H\$D$@$8D$(D$p$HfD$Xf$XDX$8fD$HAX\$8f$@E\\$(f$P\$0f$pD?fD$p$hfD$PfD$HfD$X$`D$D\$`$Pf$@D\DX$0f-eDX$D)$X$(D$ $'D$DX$f$$`D$@DXD$xDYeDef$\$f$p\X$xf$HA\$Pf$hfD$`fD$hYfD$`fD$pfD$XD\$x\u@f$@AXfD$XMDX$ AEhD\$ E\f$@YAXYf$HDmHX$PXDMpXDU M0De`DXD\u8D\em(D]DuPD}X\fD%JfDJfD=:fD-G:AExEE?DE)ƍPHD^Lcf\fFTLc҃IcfB|flA\ffW$AXffW$EEY\XDYAY$AAYAYEYAYA\EYXE\XX\\AYDXAYAYAYtBTBL\fDEfD}1ɀuOHMH !EE\EXDDufffffLc)HLd)ȃHNdfD\fEfDW$F\~PAfD58AD)f fCAfDk fk(A*fD{fDc0D^fsEYAYAYAYAYEYEYDYs8DDEXDADXA\\AAXAEXA\A\DDAXEXE\D\X}XDE\DM DU(]eD\M0fUfm0D :E1fufMLeHfDz7fD%17LeDm8fe8D\fD-`u/Dֹ DXfffAWEfAW\DXfeDAD\XX\EYAY]e@EXXE\A\EYAYEYAYDu}D}0U8Lc)HLt)ȃHNtfLffAWBL~H;1HMH}f86D$LD$LE36LD$fSffl$1H]H]XXY\]YeufsfD+fD[HfDc@fK(f[`fcfDs$fD$f$D$EDX$\$fD{P$fCXfk0EXfS8fDCp$fD[ f[h$xD$pD$hE\$`$XD$@$PD$HD$8fDCx$0f$f$f$Pf$xfD$pfD$xf$pAXf$XfD$XXfD$@fD$8f$@X$hAXDX$`$(D\$h$ \$`AX$H$D\$HDX$($DX$ D$\$(D$\$$D\$$D\$PD$D$D$$f$fD$\$XfD$DX$fD$D\f$f$8\$f$0fD$0f$$\$ EXf$$f$\$X$E\f$D%5D$ X$ED$8X$ D$0$f5[D)$DDX$0$f$0AX〴$E\$DY%[D\X$8$\D$Mf$\$ f$DYX$f$YfD$fD$YfD$fD$fD$f$A\f$f$]@f$D\$eDX$AX$EX\$EPf$\$D]HDuDM DX}(D\u`AXMhXE\D}DUXe0]8Dmpf fDk f[f{fS8fDs(fc0fsDDAXDA\DDXDE\DXDD\EXE\AEXA\EXAXE\D]Xm A\D}UDE(]&fffffffffffA@HDT$~~:%D$HffffD$^HffD$vqHfD$VHffffffffffffAWAVAUIATEUSHDHAw:AFAAfffMAAffff1HĘ[]A\A]A^A_Ã$.E9>fD=.DD)DA D*E^~ AD$LcAMiVUUUAI E)D)AD1EEAAAL$DŽ$DŽ$)$Ӥ$DŽ$DŽ$Ӥ$Ӥ$ADŽ$DŽ$Ӥ$Ӥ$AD9D$ 0)$ L6) $DH$GB$$D$D$AD Hc̓ A A AAHHcHHE fD fDDHcf,f\HcHHE f4fdIcfD$f|IcIcHHHf ;fDl;fDfDEYEYIcHAYHJ )AYAYD$AYD$xEY$pAY$hf3AY$`EY$Xfd3EYD$PfDdEY$H$@AYD$8AYD$0EYD$(fTf$@f$@fD$D\$pfD$xf$HfD$`AYXD\$h\$XfD$\D\$PfD$`DX$pf$0$ f$ $f$8f$xD$$f$8fD$XDX$P$D$A\DX$HD$X$(X쀴$X$h$\f$ $fD$0fD$D\$(f$DX$A\ր$AXE\$fD$f$DX$\$$EXD$fD$D$fD$$$AXfD$D$fD$D\DXf$DX$\$E\D$\$$D$DXD$AD$$XXD$$fD$f$DX$\$fD$\$fD \Rf%TRfD$fD5IRf9R$fEfDW$ D$$DY$D\Y$fD$$f$D\D$DY$EXD$fD$A\$f$D$Y$DEDXD$I@\D\$DIaHDfD$fD$f$A fD$fD$DY`f$f$f$DX$DX$;$\$D\$X$D\$DX$DiX$Dq\$qP\$DaXY(DIhDA0y8Qpax$DLBfE9#$fD=&DD)DA *D^D$~ AD$HcALiVUUUAI E)D)؃DAA1AL$DŽ$DŽ$)$Ӥ$DŽ$DŽ$Ӥ$Ӥ$ADŽ$DŽ$Ӥ$Ӥ$A9$D5(D)$LR.) $DH$G B$$D$fD$fD$fD$Af$fD$D D$f$f$fD$ A H E HA A DY,AHcDY\HcHcIcHHHHfD$DY$E McY|IcIDYHD$xYDIcD$pY3HHcDY|3HDY ;D$J )$D$h$`$XD$PD$HfD$f$f$f$f$f$fD$fD$f$fD$fD$fD$hDYD;DX$xBY,X$pBY\D\$xY D\$pDX$XYtD$@D$$8$Y$$0D$DYt$(D$$ $D$D$f$`X$PfD$(D\$f$PfD$hfD$HfD$@f$Hf$@f$(f$ f$fD$$\$`D\$XD$fD$DX$8DX$0\$8\$0X$$X$D$\$ D$DX$D$DX$$$$$$D$pf$fD$fD$fD$f$f$fD$fD$fD$f$f$f$\$D\$DX$DX$\$\$DX$$XDX$D$HD\$D$h\$D$`X$$@X$$8D$D$xD$P$x$$f$f$X$fD-WI\$fD$D\$f$\$f$fDIfD% If$pfD$p$DY$X$x$AXD\$xD\$$$DY$D$fD$ADq@D$EXD\$DY-dHEYD$pDyHf$@fD$@fD$hfD$`f$hf$`f$Xf$HfD$XAXf$HfD$8E\f$8DX$DX$\$y0\$DQpX$xX$PD\$xDI\$PDADX$piP\$pYXq a(Da`IhDY8Qx;$fffDDT$HL$HE yHL$fDCfDKfT$D$DXf$D$f$f$XcXD$f$Xf$$f$\\YY$f$$GYY$O$WfADEEAAAL$AE1)AAAE9L=%)DD HB<>B8 IcAЉ։D HcD HHD HffdHcfD,f|HHcHHffDLfDfDtAYAYJ )EYAYEY$f$AY$f$EYf$f$EY$fD$D$fD$D$$X$D$X$D$x\$\$DX$DX$x$p$h$`$XD$PD$HfD$xf$D\$f$p\$fD$hf$pf$hfD$`fD$XfD$`f$XX$PD$@$GDX$H$8$?\$P\$HE9DX$@DX$8D\$@!\$8Diy Q(DIDADq0q8BA D~AD$HcȉLiVUUUI A)D)yADAEE1AAL$DŽ$0DŽ$,)$0Ӥ$,DŽ$(DŽ$$Ӥ$(Ӥ$$ADŽ$ DŽ$Ӥ$ Ӥ$AD9D$45)$ffLi!) $(DH$0GB$$$,D$ D$AD Hc̓ A A AAHcHHHE f$fD|HcHf<fDlffDLHcIcIcIcHHHHfD3fl3f4;fDt;E fDfD$fDD$$D$$D$D$$$D$D$$D$f$fD$f$fD$Mcf$fD$IfD$f$Hf$X$J )DX$fDd\$fB D\$fB\X$DX$D$D\$$$\$$D$X$$D$x$pD$hD$`$g$X$_$PfD$fD$f$fD$fD$f$f$f$fD$f$DX$D\$\$DX$DX$\$\$D$HX$pD$@DX$h$8\$pD$0D$($ $'$$d$hD|$X|$8fD$f$fD$xfD$f$xf$PfD$HfD$Pf$HfD$@fD$8f$@D\$hX$`DX$XD\$`\$XX$0DX$(Dl$0D\$0T$P\$(DL$HDX$ DD$(DX$l$ \$ t$pDt$`D$D$@D$D$L$xfD$8fDD$xD\$fDl$xf$fD$X$f-<D\$f%<fEfDW$f=<f<ft$hE\fDt$XfD\$h$Xt$pY$D$DXt$`Y$E\D\\$p$D$Y$$1AY$Dq$$DY@fDT$XfD$PfDd$Hf\$Pfd$HfD|$8f|$0fDl$8fL$0fT$(fDL$ fDD$(D\T$`X$DX$\$\$DX|$@X$DQHD\l$@A\$DaX$YPDX$aXD\$Dy y(Di`IhQ0DI8DApfl$ ;$4\$ix$A< fDE)̾DfS fDsfD{fDc*fDk0f{8D^ffDS(5AYAYEYEYEYEY\AYD$XfD$f$D$D$EYfD$D$DX$f$\$DfD$D\$DE\D$f$XfEfEXD$fD$fDWXfW\$EXAXDXD\o DwA\ODgD(_0fD$D\D_8$AfD=IE)̿Df3fk@fDcfD[HD*fK f[(E^fDK`fDShfDCfcfDkPfCXf{0fS8fDspAYAYEYEY$H f$H AY$8 AYX$8 EYD$@ EYD$0 EY$( AY$ EYD$ AYD$ AYD$ AY$ EYDY{xD$ $ $ $ D$ D$ $ fD$@ f$H fD$@ Af$( f$ fD$ fD$( f$ fD$ f$ DX$0 \$8 D\$0 X$ X$ D\$ D\$ D$ X$ $ DX$ D$ \$ $ D$ D$ $ D$x $ $p D)$ D$h $` fD$ f$ f$ fD$ f$ fD$ f$ fD$ f$ fD$ EX\$ X$ \$ D\$ D$H X$ DX$ \$ $X D\$ $P X$ $@ $G DX$x D$8 $? $D$$D$$D$fD$` fD$X DX$@ fD4DX$8 fD$ f$ fD$p f$h f$p f$h f$` fD$X D\$ D$f$\$x D$X$DX$P X$H \$P \$H D$\$@ $D\$8 DYD$$$( $$D$D$0 f$\$f$\$fD$fD$fD$Yf3f$YM3fD$fD$f$f$DX$X$$/ D\$$ f$D\$X$0 \$0 EMAMEU@ffW$ DXD\EEHAe\$A}PEmY2EuXf$X$fD$fD$f$fD$f$fD$DX$( \$D\$( DXX$ AE f$D\$ E}(E]8Au`EehAm0EMp\AExAADEEAAAL$AE1)AAAE99L=)DDf$ f$HB<>B8fD$fD$f$fD$fD$f$ IcAЉ։HcD D HHD Y,HHYtHcJ )HY\DY4DY$HcDY\HE9DY<DYd\D$hf$hXEXD$`fD$`f$`A\$@DAX$Pf$PD$XfD$Xf$XDXD\A\$h$8\$8$HDXDDAXD)DXDXD\IQ fD$PD\DqDYDy0E\DQ8DA(#ADEAEAAL$AE1)AAAE9L=^))\$DD HB<>B8 IcAЉ։HcD D HHD flHf4fDfDdHcHHcHfDHfD<$fD$f<fdf$A\J )$AXf$EXDfD$A\EX$$Xf$D$fD$fD$f$D\$A\DAXDXE\D$$\$$E9DIDq A(fD$fD$DX$DX$\$D\$DYDQI0Da8A D~AD$LcMiVUUUI A)D) ADADE1AL$DŽ$ DŽ$ )$ Ӥ$ DŽ$ AӤ$ ADŽ$ DŽ$ Ӥ$ Ӥ$ DŽ$ Ӥ$ 9Չ$  D5D)$ffLy ) $ DH$ G B$ D$ D$ AD D$ Hc̓ Љ HD A HA A f flAHcHcHcHHHE fD fDDfftIcf$3fDt3IcIcHHHfD$f|fDfDD$ fD,3D$ $ $ $ $ $ D$ D$ $ D$ $ D$ fD$ fD$ f$ f$ Icf$ f$ Hf$ fD$ HfD$ f$ J )DX$ fDT3DX$ f;\$ fD|;\$ X$ D$ X$ $ \$ D$ D$x D\$ D$p DX$ $h X$ $` $X $P $H D$@ D$8 $0 fD$ f$ fD$ fD$ f$ fD$ fD$x fD$p f$x f$p f$h f$` D\$ \$ DX$ DX$ \$ D\$ DX$X D$( DX$P $ \$X D$ \$P D$ X$H $ X$@ D$ D$D$$$$$fD$ fD$( DX$ fD'DX$ f$h fD$` fD$8 f$0 fD$8 f$0 f$( fD$ \$H D$0fD$0D\$@ D$(DX$(fD$(DX$ X$ D\$ \$ $\$ D$D\$ EYD\$0D$$D$DY-&$$D$D$f$f$X$\$f-&f5~&fD$fD$$f$fD$fD$f$f$f$YfD$X$YDX$EXD\$E\\$$X$$\$DYDX$!DQPDqDa@yHAQXDy fD$fD$f$f$f$f$f$DX$D\$;$ \$X$X$\$\$DI(DA`ihI0Y8qpaxAJf-.E)fKDӤ$fs*$fc(fCfD3fD{ ^f[0fDS8YYYY$Hf$HfD$HDY$8fD$8DYY$@f$@DYXD\f$@EE\$0\$0EXX$0EXA\$8AEEEDXDXD\A\XD_AXD/\DO D\(GOo0Eu8ADEAEAAL$AE1)AAAE9L=W)fffffDD HB<>B8 IcAЉ։D D HcHHD HfDdffD,fDLHcHHcHf|fHfDflD$f$f$DA\J )E9$f$$f$EXfD$AXD$DA\D\$XD\$AXED$DX\$DXD\EXYEXD1A\DY a(A\DQDyy0Q8e'AfDE)f{DӤ$fD*$fcHfDk fS(fD{`D^fDshfsfDcfkPfKXfD[0f[8fDKpAYEYAYEY$ f{@AYEYD$ EY$ AYD$x AY$p EYD$h AYD$` AY$X EYAYD$P EYDYSx$H $@ D$8 $0 D$( D$ fD$ f$ f$ fD$ f$x fD$p fD$` f$x fD$X f$P DXf$X fD$P \X$ D\$ X$h D$ EDX$` $ D\$p \$h DX$H $ X$@ D$ \$H $ D\$@ D$ D$ $ D$ $ $ D$ fD$0 f$8 f$8 f$ f$ fD$ f$ fD$ fD$ f$ EXfD$ X$( D\$0 \$( X$ D$ X$ D\$ \$ $ DX$ D$ DX$ $ \$ $XD\$ $HD$0$ D$@D$8$D$fD$ fD$ DX$ f$ DX$ fD$ \$ f$ D\$ fD$ f$ f$ X$ D$xf$xDX$ D$f$\$$hX$xfD$hD$pDX$pf=\$ \$ $`DY-hD$PYY$($ $ $ $ D$ f$p\$hfD$XfD$Hf$XfD$Hf$@fD$8f$@f$8fD$0fD$ fD$0DX$`YDX$P\$`D\$PX$ $x DX$ \$ D?\$ DwDX$ w@DX$(DgHD\$ oD_OP_XDO DW(DG`f$fD$f$ fD$f$\$(DX$ \$ XD\ghG8Do0WpDxf[fDSfKfS0fDC fc(fD D5$D$f[8fD$$fD$$D\$fD$fD$f$AE\DX\$D\DXEfEAXAfXfEWAAfAWEXXA\A\DX?EXGD\w A\o(D_DGDO0g8fD3fDc@fsf[HfDS fk(fC`fD[hD$p fD$p D$` DX$` fDK$h fDCf{P$X fSXfc0D$P fDk8fD{p$H fKx$@ D$8 D$0 D$( $ $ $ D$ D$ $ D$ f$h fD$p f$h fD$P f$H f$8 fD$P fD$0 f$( f$0 X$X D\$` \$X DX$@ X$8 \$H D\$@ $ DX$ D$ X$ $ \$ D$ D$ $ $ D$ $ D$ D)$ $ $ f$( fD$ fD$ f$ fD$ f$ fD$ f$ fD$ f$ \$ DX$ DX$ \$ D\$ X$ DX$ $ \$ D$ D\$ D$x X$ $p $w D$h $o $hD$X$8D$0$PfD$ fD$ DX$p fD%DX$h f$ fD$ fD$ fD$ f$ f$ f$ f$ fD$ D$f$X$ D$X$D\$ D\$ DX$ X$x \$ $H\$x DY\$p D$(D\$h D$ D$p$`$X $@$xD$D$` f$x\$f$\$fD$Hf$HfD$hYf$XYf$hfD$XfD$Pf$PDX$p\$p$_ X$`$P f$xD\$`DX$` \$` DG@offW$ DX\D_HDO\$PDGY:gXfD$ fD$8fD$0fD$8f$0f$(fD$(DX$@f$ DX$X D\$@DX\$X X$P D\$P Do Dg8D(Dw`Ohw0DWpfDkfSfKfDs fD f{(fs0D$x$hfDk8$pf$xfD$pfD$hf$xEf$pE\$`\$`EX\EXD\$hEDXDXADAEXAXXE\A\D_DXE\oD\Dg g(DGDO0f+fD[f[HfC fDK(fDC`f{hfS$D$fD$fD[@f$$X$fc$fDkPfD{XD$fDs0fK8D$fspfDcx$EX$x$pD$hD$`D$X$P$HD$@D$8$0f$fD$f$fD$f$f$f$xfD$pfD$xA\fD$pf$Xf$PD\$X$DX$$(\$\$X$hDX$`D$ D\$h$D\$`D$X$H$X$@$$ D$ D$ D$ $ $ fD$@fD$Xf$8fD$0f$8fD$0f$(fD$ f$(f$ f$ fD$ D\$PD\$HX$DX$\$D\$X$D$ DX$D$ \$$\$D$X$ $DX$ D$$D$$$$D$f$ f$ fD$ X$ X$ fD$ D\$ fD$ D\$ fD$ D\$ D\$ $f$$fD$\$D$fD$DX$D$D\$f$X$fD D$D$ $ Y AYEYEY$ D$ $ D$ f$fD$f$f$f$fD$fD$fD$f$fD$f$f$X$DX$\$\$X$ DX$ D\$ D\$ DGX$ @DX$WH\$ g\$DoDPDwXw Dg(o`Ohf$f$fD$fD$DX$ D\$ AXA\D_0_8DOpfffEWHHHSHWDfffA@HDT$~~:%D$[HffffD$'6HffD$JHfD$膱HffffffffffffAWIAVEAUATIUHDSHAH$w5AGA=ALA2Afff1H[]A\A]A^A_ffff$E9fD=DAD)DAA A*D^~AFHcALiVUUUAI E)D)؃DE1DŽ$DŽ$DŽ$ANDŽ$)$Ӥ$Ӥ$Ӥ$$DŽ$Ӥ$Ӥ$DŽ$DŽ$Ӥ$Ӥ$A9Չ$D D)$@ffL(DD $DH$GBMc͋$$ID$D$K9L$AAD D$ꋌ$ HA A A fTHcfEHcfDLfE,HcfDdfEHcIcIcfEfDfA IcfDtfA4EYEYf|EYEYAYD$fDLAYD$xfE,EYD$pfDdAYD$hfEEY$`$XAYD$PEY$HfD$hf$hf$f$EYf$xf$xAYEYEXEYA\fD$XEYX\f$pAXA\fD$p$0E\ƀ$7\$@f$`DXf$H$8D$fD$H$(f$`AX逴$/A\EXfD$PA\D$ AXf$XEX$$E\A\AX$fD$PA\D$$D$f$f$@$X$0fD$EXDX$fD$8$fD$@f$ fD$ DX$(D\$0fD$8Xf$D\$($\$D$8\$$0f$0D$D$fD%}fDtAXD$E\fD$8$ D\$0AX$(A\AX$D$DY$EY$D$D$f$ fD$ D\$(fDDff$fD$f$DXfD$f$ffW$@f$fD$fD$EY\$($X$DX$\$D$DD\$X$X$YD\$ D\$E{ $EI cAAD\Dc(Ei(fD$f$f$f$D[fD$f$DX$D;$\$X$X$D\$\$EqDS0AY0SAIDC8Ay8dfff$H$DLE9"D$fD=.DD)DAA A*D^D$~AFHcȉHiVUUUH ))ADE1DDŽ$DŽ$DŽ$ANDŽ$)$Ӥ$Ӥ$Ӥ$D$DŽ$Ӥ$Ӥ$DŽ$DŽ$Ӥ$Ӥ$A9Չ$0)$H=(DD $DH$E;8Mcŋ$$ID$D$K8f$f$AAfD$f$D D$f$ꋌ$f$ A A A HHcYtfD$AYf$HcDY\L$IcAY,HcMcY\HcAY$IcDYT$AY<$D$$$x$pD$h$`fD$fD$fD$f$fD$f$fD$fD$f$f$fD$f$DY|X$EY X$DYlD\$AY \$FYdD$X$CYD$P$DYDD$HD$EY4$@D$8$0D$(D$ $f$xf$pfD$`f$xfD$XfD$PfD$Xf$PfD$8f$0fD$ fD$8X$hX$`D\$p\$hDX$HDX$@D\$H$\$@$DX$(D$X$ $D\$0D$D\$(D$D$$D$$D$D$f$f$fD$f$f$f$fD$f$fD$fD$fD$fD$X$X$D\$\$X$X$D\$$\$$pDX$D$XDX$$HD\$$hD\$$`D$@$8D$D$xD$PD$f$f$X$fD$X$f$D\$fD \$f5f%f$fD$pf$$fD$DX$xX$$D$f$\$$\$D\X$EYAYDY$$D$fD$DX$DY%{ f$@f$@fD$pfD$hfD$`fD$hfD$`f$Xf$HfD$Xf$Hf$8D\$xAXDX$A\DX$D\$D\$X$X$PEx D\$DK\$PEhX$Dc(E@(KA@Ds0Ap0SAhc8fD$8D\$D;$EP8ffD/H$DD$HD$HE!HD$fufafT$$f$Xu$f$X!X$8Xf$8$0f$0\\YY$(f$($ YY$ O$ SffDAAһAAANE1D$AA)AAD;$ffffH5(DD DHDA<00 IcAЉ։ D D HHcHfDfAYfd$ AXXd$(E\EXD_ v E\oDnDO(D~(gDF_0f|$fL$fD\$fD$\$fl$AXXE\F0ND_8\Am8ZffNf/fDWfDfDFffvDDDXA\fNEXA\EXEAXA\ED\DDXDXD\D\DXD7AXD.\DgA\DND_fo0fWfD(ffDo fDWfF0fDw0fDfDXf.fDfgfDFf^8Xn A\D\N fD^AXXg8EXD$PDXF8fD~(\^$pD\W0$\W($xD$`D$hD$HD$@DX\FEXD\^(D$X$8f\8fD$h\f$hf$f$xX$`DXX$XfD$PEXDX$HfD$d$xfD$xD\$`$0f$0f$@D\$XD$$D$f-1A\fD$8$f$D$AXD\$0D$AXX$DX\$8D$D\$@\$p$f$PE\$HD\AXEfEDXfDW=\A\DYo7fD$fD$DYf$DX$D\$fD$\$YfD$Yf$f$fD$f$f$D.EXfD$D_ A\fD\$xN XfL$pDXfDd$x\DwEXA\DND\G(EX^(XDGnE\fl$pw0Dn0D_NDg8FffffffA@HDT$~~:%D${HffffD$cHffD$|HfD$HffffffffffffAWIDʸAVEAUATUSHHAw-A9AkA~AA7 1HH[]A\A]A^A_A}E9tf%WDD)ɉfA fESDD*AA^A)DaIcHAE9YDYA fA< ESfAT YYA< AT L_(H$ H$0fffDDȉD1HG,B<AD DD9DCAHA F,A ADAE IcE HJ4IcE HJ fDfDvf1fDiDYDYYDY6D DnIcMcHIDqJKTD9ff@ffJYYYYHB"T$Ht$\$HT$$($8D D HD AHHcfE<fA\HE9DYYE<fA,A\fE\YDYA,E\\H$0H$ $DLqeE9f%,UDAD)EfAfDGAA*ED^AAE)D9YDYDG}H=](H$ H$0ffffE1D<9DH48AD DA9DDH,8DDAT=D AHH D LcJ,IA9O,fUfEfA]fAMYYYYAUAE]Mt\$Hl$T$HT$$($8 LcID9fC #fGT#YDYC #GT#H$0H$ 4f-Sl$HT$HE{SHT$fWf_fOfDf|$XDXXXYADY\\_DYYgw$$ Ae fD=RE)λDfDG f(fWfDO*fDg0fO8D^ffgD5UEYAYAYEYEYD$fD$AY$fD$AYAYD$D$DX\$DX\$DX\$DXE\$ffAfAWEXfAWAXE\E\D/wD_ DW(\XX\A[0AsAkAc8EDŽ$AADӤ$E);$H-Z(L$(H$ H$8H$0DDE)D$HD(AE ADA9\$}xLD$D$DA(H4(D$D$D HDHJ  HcLHqH9\$N<MIMIwL Hy|ILHL;$CH$0H$8H$ L$(DAADŽ$)$ADAE9D$L-X(L$(L$ H$8H$0D$1HB,(DF/AD $D9fffD$C4)HF(DT$ t$l$$ d$HcD D d$HHcD$J D |$LAH9HJLRLBLcD$L H:HcHLQIL N OD9I9IAMIRI:IBMIQCIHHAE9H$0H$8L$ L$(M$A fNE)ξDfOHfDf0fDW@*fDo(fg^f_fW`fDXfDgPfDO8YDYYDY$fOhDYD$fD$Y$f$YYfDGpYE\$DYAXE$fg f$$DX\$fWA\fD$D$fD$f$DYYJPYX$EXDYDY$YGxDX\$D$fD$A\AXfD$DfE\fD$E\EXfWA\fD$D$ED$EXfD$DXfD$$A\DD$hfDXD$@E\f$fD\f$fWA\D$`fWEXDD$PfD$\DX$fD$DXD$8$H\D$xfD$x$0EXAEXfD5uAXE\DfA\fD$D\$XAYfWf$hEXEY$$$XAXE\DDYtA\_f$xf$hf$`fD$`AYf$XfD$Xf$8fD$@A\fD$@DA\DX$PDXDX$D\f$8D\$Pg@f$0D\$GHf$0wDGPDo AXDw(E\Dg`AXDOhXA\o\DX_0g8OpGxA9 fDIE)fDDӤ$fW*$fw fDO(fDofDgD^f0EYAYAYEYEYEYAYDYG8$D$AXD\$DXD$\$fD$D$DXA\$EXAD\EEDXEXA\E\XD?AXDwD\w A\DO(o_DW0AS8ffgfWfDW fDG(fDof0fO8DD5KKDEXAAXDXXEA\\DD$DX$A\D\$DXff\fAWA\D'DOfAWD_ o(A fD%GE)fGHDӤ$fDD*$fDWfDo@fW`fDhE^f_0fDO(fPfgXfDG8AYEYEYEY$(f$(AYEYAfG AYEYAXAXD\$(AYAYE\$EYD$fWfD$f_pAYD$ $$$$AYD$D$AYEYDYgxD$fD$f$ fD$X$D\$ f$fD$f$fD$f$fD$fD$DXf$\$AX$DX$D$\$DXD\$D$fD$X$D\$$f$DX$D$D$$AX$D\$D\D$X\$$D$D$fD$D\$Ef$f%=nE\fD$EXfD$fD$DX$f$Xf$f$D\f$D$fD$D$(DX\$$f$fD$A\D$fD$D\$DX\$AXo@E\DX$(A\_DDYEXD\f$DY%$mDYGHfAXfW=yFAXDYEXA\wDGOPf$f$f$fD$f$fD$fD$f$A\fD$XAXD\A\gXEXW EX_(E\DW`A\ohDo0Dw8DOpffD@fg fDWhfWf0fo`fD_(fwPfDwpDfGf_A\fDofDOEXfDg8XGHX\_HEXDXoXX$hD\OXD$xDXgx$XD\W(D$P\g`$p$HDD$`\WPD$@D$8DXfEfD$0D$(fGxfD$p\G8DDX$PfD$`fD$`fEWfAWfD$xf$xf$pfD$HDX$XAX$ f$@\$XDD$HfD$@\$PDX$(f$HD\$0l$@$'\$(\pD|$XX$0\$(t$ DD$PD$DXE\D\fDl$8Dt$DL$D$0fD$ fD$8fAWfD$8f\$Xf%ifl$@DX\$ fDl$8DXfDL$@$D\f|$Hft$H\t$PDT$pEXDfEXD\d$pE\f\$(fAWfD\$X\\$0DY iwHDYAXfDD$ A\fD|$8DX$DYD\Yfd$(AXXd$0E\EXD_@DG(E\oDOPDoDXg _`f|$fL$fD\$fD$ \$fl$AXXE\Gh0O8D_p\AkxfWfO(f/fDW fDfDGf0fw8DDDXA\fO8EXA\EXEAXA\ED\DDXDXD\D\DXD7AXDo\Dg A\DO(D_go06fWfDPffDo@fDW fGhfDw`fDg(DXfofDOfg0fDG8f_xXoHA\D\OHfD_AXXgpEXD$PDXGxfDX\_8$pD\W`$\WP$xD$`D$hD$HD$@DX\G(EXD\_XD$X$8f0\pfD$h\f$hf$f$xX$`DXX$XfD$PEXDX$HfD$$fD$xD\$`$0f$0f$@D\$XD$$D$f-neA\fD$8$f$D$AXD\$0D$AXX$DX\$8D$D\$@\$x$f$PE\$HD\AXEfEDXfDW=*>A\DYd7fD$fD$DYf$DX$D\$fD$\$Yf$Yf$fD$f$fD$f$EXDofD$A\D_@fD$XOHfD$fL$xDX\DwEXGPo(A\fl$xD\DOEX_XXDG E\w`DohD_0O8Dgp8ffffffffffA@HDT$~~:%D$+HffffD$aHffD$v}HfD$FHfÐAWDAVAUATIUSHHA$8D$@A9fD=9D) *D^~FHcAHiVUUUAH D))NAADŽ$DŽ$)$Ӥ$DŽ$DŽ$Ӥ$Ӥ$DŽ$DŽ$Ӥ$Ӥ$AAD9 C;)$L A($$$H$D$BD$Hc̓ A A AAHE HE HfDfDTHcHHcIcfD4f\HcHHHfD,fDdIcf f|IcHHf43fD fdEYf,EYIcEYHJ !AYEYD$fD4;EYD$AYD$AY$EYD$fDl;AYD$fDd3AY$AY$D$EY$EY$xEY$pf$fD$\$fD$f$D\$fdf$X$fD$D\$f$EXf$x\$$`f$AYX$D$PE\$pf$A\D$HfD$DX$D$hfD$$X$WDXDX$$_$@f$A\DXEXA\fD$xDX$p\ffW$$8f$`$?D$X\$PfD$@AXf$8A\fD$HfD$hf$`$0fD$hDX$XDX$`X$PXD\$X$8$ A\$8D$xf$x$X$D$PAX$Hf$HE\fD$@D$($pE\AXD\f%L]D$@D$ YD$h$0fD$hfD$hfD$fD-\D\$xfD\fD\f$`D\f$`f$X$'D9fEfDW$XEYfD$P\EYfD$PDX$0D\$0\!D$(fD$HAHD$fD$HDX$(D\f$XD\$(I@DqDaPEYDXDiEXDYXA\D$DYDI f$(f$ f$(f$ y`DX$ \$ X$X$\$\$DA(qha0Y8QpAxtH[]A\A]A^A_A9fD=1D) *ЉD^D$~FLcAMiVUUUAI E)D)ЃNAADŽ$DŽ$)$Ӥ$DŽ$DŽ$Ӥ$Ӥ$DŽ$DŽ$Ӥ$Ӥ$AAD9D3D)$fffH i9(苔$$$HD$D$$f$fD$f$f$f$fD$fD$fD$ A A AAHf$HE E Y4Hc̓DYLHcHcHHIcY$HHYTHcIcY,HHDY\IcMcHDY<IDYD$D$Y<$xJ !$p$hD$`D$XD$P$HfD$fD$f$f$fD$fD$f$f$fD$f$f$hDY;DYt;X$xY3X$pYL3D\$xFY,\$pX$Xf$Y|D$@FYdD$8$$0$$(D$D$ $D$$f$`X$PfD$8fD$PfD$hfD$HfD$Hf$0fD$(f$fD$0DXf$f$$D\$`D\$XDX$@D$D\$@X$ DX$\$(D$D\$ D$X$D$X$D$\$8$D$$D$$$fD$fD$f$f$f$fD$fD$fD$fD$fD$DXf$fD$D\$\$X$X$D\$D\$DX$D$DX$$D\$$\$$DX$D$D$D$D$D$$D$D$D\$f$\$X$fD$D\$f Tf%|TftTf$fD$D$Y$f$fD$fD$$Y$X$$D9AXDX$D\\$Y5S$D\$DX$$AY)DYQ@DyH$DAfD$f$fD$fD$f$fD$fD$f$f$DXf$f$\DX$D\$\$DI0DX$ipDX$\$\$DQX$DqP\$YXDi Da(I`yha8AxcONAAAA)AAAD9L51(ffffDHB0IcA‰ƉHD D HD Hf$fDTHcHcHcHHHfD,fDfLNfD>f>f$HfD$ f$fD$ $EXD\$$f$\$(L$pf|$pf\$pDX$$D9X$A\DX$D\$Y%>>t$DL$hAXT$XA\EYD\$0DYYD$D$$fT$Hfl$HfDl$`fDd$Pft$`fDL$PfD\$@fD|$@fDD$8f|$(XfDT$8fDt$(\DXl$hDXd$X\t$hQD\L$XiPDX$D\$DX$D)X|$0DaD\$q@D\t$0DIHDYDyXDA y(DQ`Dqhf\$ fL$fDl$ fD$X$X$D\$\$Y0I8DipAxvoNAAAA)AAAD9>L5(ffffDHB0IcA‰ƉHD D HD HfDfLHcHcHHcHf,Hf$f|NDD)T$DH 'HDIcA‰Ɖ D HfEflHcHcD fE4fD|HcfTfDDfA HfA4ADE\N1J 9A\AXE\D$fD$\AX$fD$DXf$f$fEfDWD$X$$DEDX$D;$\$D\DXDX\EEXA\D)A`DIE`DqAPiA D~A@LcAMiVUUUAI E)D)ЃAHDŽ$dDŽ$`)$dӤ$`DŽ$\DŽ$XӤ$\Ӥ$XDŽ$TDŽ$PӤ$TӤ$PDŽ$LDŽ$HӤ$LӤ$HED;$=)|$xfffLI'D$d$`$XH$TD$PBD$LMcD$HIAK0O8苌$\ A A A HfEAVAuA]|\$T$D AHcHA9fE 3fED3DYDYE 3ED3&[]A\A]A^A_ÉAAAA)DyD9LL'LcD1G$ADA9L''HcDBD LcD IDK H8L@LcD IK L LQH9LAHcHLLPHcHJ<9N LLWIIIHHOMMQlD9DD AHcD HHcDH HcD HN'H /J< H(fE0fD)fD'fDDYDYDYDYD7D(HcHE A9DJ< L(H .J&fDfEfDf9DYDYDYYDD DA8"D E HA fAMcD9YAfTYTfC YC fF|DYF|[]A\A]A^A_9@Af%AA)Ef7DAAAAAA*^EDE)AE9Y7f.Y.}HY'IcDE1<DE9zIcDDD D AHcHHHE9N< L4(J "H4*fEfEfD fDDYDYDYDYDDEE|D ALcE9fG,DYG,fFdDYFd3[]A\A]A^A_AD$AAADd$E)D;l$~HK'McDAE1DE9}`H5)'McDAE AA2IcHH/DL J 'D HLHE9N H(I2H8H1H:ML|AD;l$qD$D$d$)L$AD;t$DIDL$L-'McL$G<+E1AL$AE9L V'IcL$BDD ALc t$IL$I+D Hc T$HJ'H /K<#LLL LLLHcLLHcHHE9N H(L)J!H;I LMHI8MLZAD;t$!fffAWAVAUATEUHDSHHA)AAH5DHc HE9 fD=fDAD$D)ɿ D*E^~ AD$LcAMiVUUUAI E)D)؃eAAL$AEEE1AAL$DŽ$DŽ$)$Ӥ$DŽ$DŽ$Ӥ$Ӥ$ADŽ$AӤ$DŽ$Ӥ$E9D$=u)$L6'D$$$H$D$BD$IcA A A AAHE HE HfD,fdHcHcHIcHf fTfD4HcIcHf\Hf<f4;EYAYIcHEYHH )AYfAYD$fDlAY$fd3D$fDtIcHAY$f,fDDAYf\;$$f 3EY$EYAY$EYAYAY$AYD$fD$fD$DX$fD$DD\$fD$A\D\$EXfD$f$f$AYX$f$X$EXD$xfD$D$hfD$D\$f$xD$`EDX\$D$pfD$DXD\f$D\f$pX\$xEXX\$D$XD$PX$\$pE\$H$D$fD$XfD$hEDX$Pf$XD\\$PDXf$`frD$D$DX\$hD$fD$HA\$fD$f$D$fD$`DX\$H$8f$?$EXfD$E\DX$\DYDXfD$@Y$@D$0Y$0Y$@$0$(f$f$fD$f$f$fD$f$AXDX$@D;$A\fD$\$@EX!f$8E\Q@DDqEADX$(AXDiXX$0YPDXDIH\$8ED\$0Ia Dq(DQ8A\DD\$(A\q`Q0DIpyhAXDax%fAt$HMcL5'fDcADAfG fCtEAD9EEXEYDE\A2 fD%D fDfff?EDA)ōpHEeMcfDtfBLLcD9IcfFlfB\flAXF\tf$f$f$fAWfED\fEWXD$f$EYDY$f$DYAYYAYE\AYAYDX\AXA\AXD\$XAYAYAYEYdBTBLDt AL$AAAAAAL$AE1A)AAAE9L=D'fffDDHB8IcA‰ƉHD D HD HfDLf4HcHHcHcf,fDdHHfLH )EYfT3fD,3AYE9AYEYD$fD fD$$ fD$ AYAYfD$ f$EYD$\$EYEXD\$fD$$fD$DX$DXD\$AD$f$D$f$f$$AXAAXA\AXXD\yE\q(!Dq Qf$fD$f$AXE\A\IDY0i8AD$ ~ AD$LcAMiVUUUAI E)D)AAL$E1EEAAAL$DŽ$DŽ$ )$Ӥ$ DŽ$DŽ$Ӥ$Ӥ$ADŽ$AӤ$DŽ$Ӥ$E9D$DD)T$`H Q'D苔$ $$HD$D$$IcA A A AAHE HE HffDtHcHHcIcfDf|McHcIcHHHHIf4fDDfD fD$;Icf3fT3HfFfBL$D$D$$$D$D$D$fDLfDd;$$D$$f,fD$f$f$H )fD$f$fD$f$fD$f$$\$fD|EXD\$AXf$X$D\$X$D$fD$D\$D$h$PX$$D\$D$xX$$pDX$D$`D\$$XD$H$@f$f$pX$fD$hf$fD$fD$f$fD$xf$xf$XfD$PXf$XD\$EX$8\$AXDX$`E\\$`d$HX$@Dl$8DX$8\$0\$@D$0DT$\$p$(D\$hDt$(|$t$PDD$@D$ fD$Pf$HD\$8f$HX$0fD-\$0ffDt$HfDL$8fDT$Hf|$8DXt$PD$fD$(DXL$@L$pD\T$P$T$Xfd$X\|$@D1EXD\$(DIDQ@A\DX\$pA\DY:DYYD$ fDl$X$EXDY-yHf$f\$ fDD$(fDd$(fd$fDt$ft$0fD$0fT$XfDL$AXX$ \$ EXD;$\$E\D\|$ I X$Y(D\$DAqDaXAPi`AXDyhE\Q8DIxa0DqpAAL$AEEAAAL$AE1A)AAAE9L='DDHB8IcA‰ƉHD D HD HfD flHcHcHcHHHfD,fDD3fdH )fD<fDAf3DE9AXD$f$fD$DXEE\\DDXXAD\ED$D\$EEXXA\D\EXD1AXyE\q A\Da(DYIDi0a8]DfufD]Ic1HIcH}Ar1t$xfDD$xfDd$xEXE\DEtH|C<Dd9fLc)HLTNTf|19ffAWB||H[]A\A]A^A_fDsfDCDAfD{fD AE1E*DY%LMEXLM(EXEXEXEEYE\EEYE\EAEYE\EYAXADM DU8D]}D]0ffW5u1H[]A\A]A^A_Am f=OAD)fCfDc`Afk fK(E*fDC@fcA^fD fsfDkPfD[XfDs0fS8f[pYDYYY$fCHDYD$fDchDY$fD$f$$f$YYEDYE\YEXDY$DYDXDY\f$YAXX$YY{x$D$D$D$D$D$x$p$$$hf$fD$f$f$f$fD$fD$fD$f$XfD$fD$xDXf$x\$\X$DX$$0D\$D$D\$$XX$D\$$`ADX$h$X\$hD$PD\$D$H\$D$@$8D$(D$p$HfD$Xf$XDX$8fD$HAX\$8f$@E\\$(f$P\$0f$pDfD$p$hfD$PfD$HfD$X$`D$D\$`$Pf$@D\DX$0f- DX$D)$X$(D$ $'D$DX$f$$`D$@DXD$xDYDef$\$f$p\X$xf$HA\$Pf$hfD$`fD$hYfD$`fD$pfD$XD\$x\u@f$@AXfD$XMDX$ AEhD\$ E\f$@YAXYf$HDmHX$PXDMpXDU M0De`DXD\u8D\em(D]DuPD}X\fD%fDfD=fD-WAExEE?DE)ƍPHD^Lcf\fFTLc҃IcfB|flA\ffW$AXffW$EEY\XDYAY$AAYAYEYAYA\EYXE\XX\\AYDXAYAYAYtBTBL\fDEfD}1ɀuOHMH !EE\EXDDufffffLc)HLd)ȃHNdfD\fEfDW$F\~PAfD5AD)f fCAfDk fk(A*fD{fDc0D^fsEYAYAYAYAYEYEYDYs8DDEXDADXA\\AAXAEXA\A\DDAXEXE\D\X}XDE\DM DU(]eD\M0fUfm0D E1fufMLeHfDfD%ALeDm8fe8D\fD-u/Dֹ DXfffAWEfAW\DXfeDAD\XX\EYAY]e@EXXE\A\EYAYEYAYDu}D}0U8Lc)HLt)ȃHNtfLffAWBL~H;1HMH}fHD$LD$LECLD$fSffl$1H]H]XXY\]YeufsfD+fD[HfDc@fK(f[`fcfDs$fD$f$D$EDX$\$fD{P$fCXfk0EXfS8fDCp$fD[ f[h$xD$pD$hE\$`$XD$@$PD$HD$8fDCx$0f$f$f$Pf$xfD$pfD$xf$pAXf$XfD$XXfD$@fD$8f$@X$hAXDX$`$(D\$h$ \$`AX$H$D\$HDX$($DX$ D$\$(D$\$$D\$$D\$PD$D$D$$f$fD$\$XfD$DX$fD$D\f$f$8\$f$0fD$0f$$\$ EXf$$f$\$X$E\f$D%D$ X$ED$8X$ D$0$f5D)$DDX$0$f$0AX〴$E\$DY%D\X$8$\D$Mf$\$ f$DYX$f$YfD$fD$YfD$fD$fD$f$A\f$f$]@f$D\$eDX$AX$EX\$EPf$\$D]HDuDM DX}(D\u`AXMhXE\D}DUXe0]8Dmpf fDk f[f{fS8fDs(fc0fsDDAXDA\DDXDE\DXDD\EXE\AEXA\EXAXE\D]Xm A\D}UDE(]&fffffffffffAWAVAUIATUDSHDHAw:AFAiAfffMAtA7ffff1HĘ[]A\A]A^A_Ã$E9 fD=4DAD)ɉA E*E^~ELcAMiVUUUAI E)D)ȃAADEE1MDŽ$DŽ$)$Ӥ$DŽ$DŽ$Ӥ$Ӥ$ADŽ$DŽ$Ӥ$Ӥ$AA9$ M)$ fffH 'D$$$HD$D$$IcA A A AAHE HE HfD fDDHcHHcIcf,f\HcHHHf4fdIcfD$f|IcHHf 3fDl3fDEYfDEYIcAYHJ )AYAYD$AYD$xEY$pAY$hf;AY$`EY$Xfd;EYD$PfDdEY$H$@AYD$8AYD$0EYD$(fTf$@f$@fD$D\$pfD$xf$HfD$`AYXD\$h\$XfD$\D\$PfD$`DX$pf$0$ f$ $f$8f$xD$$f$8fD$XDX$P$D$A\DX$HD$X$(X쀴$X$h$\f$ $fD$0fD$D\$(f$DX$A\ր$AXE\$fD$f$DX$\$$EXD$fD$D$fD$$$AXfD$D$fD$D\DXf$DX$\$E\D$\$$D$DXD$AD$$XXD$$fD$f$DX$\$fD$\$fD f%fD$fD5f$fEfDW$ D$$DY$D\Y$fD$$f$D\D$DY$EXD$fD$A\$f$D$Y$DEDXD$I@\D\$DIaHDfD$fD$f$A fD$fD$DY`f$f$f$DX$DX$D;$\$D\$X$D\$DX$DiX$Dq\$qP\$DaXY(DIhDA0y8Qpax $L\fffffE9"D$fD=DD)DAA A*D^D$~ELcAMiVUUUAI E)D)AAEEE1AMDŽ$DŽ$)$Ӥ$DŽ$DŽ$Ӥ$Ӥ$ADŽ$DŽ$Ӥ$Ӥ$AE9D$D5D)$fffH ٩'D$$$HD$D$$fD$f$fD$fD$fD$f$f$fD$ A A AAHE HfD$E DY$IcAY|HcHcHHcHDY,HIcDY\IcHHDYHYDIcD$Y3H$DY|3IcJ )DY HD$xD$pD$h$`$XD$PD$HfD$f$f$f$f$f$fD$fD$f$fD$fD$fD$hDYDDX$xY,X$pY\D\$xY 3D\$pDX$XYt3D$@D$$8$Y$;$0D$DYt;$(D$$ $D$D$f$`X$PfD$(D\$f$PfD$hfD$HfD$@f$Hf$@f$(f$ f$fD$$\$`D\$XD$fD$DX$8DX$0\$8\$0X$$X$D$\$ D$DX$D$DX$$$$$$D$pf$fD$fD$fD$f$f$fD$fD$fD$f$f$f$\$D\$DX$DX$\$\$DX$$XDX$D$HD\$D$h\$D$`X$$@X$$8D$D$xD$P$x$$f$f$X$fD-\$fD$D\$f$\$f$fDfD%f$pfD$p$DY$X$x$AXD\$xD\$$$DY$D$fD$ADq@D$EXD\$DY-EYD$pDyHf$@fD$@fD$hfD$`f$hf$`f$Xf$HfD$XAXf$HfD$8E\f$8DX$DX$\$y0\$DQpX$xX$PD\$xDI\$PDADX$piP\$pYXq a(Da`IhDY8QxD;$$fDDT$HL$HE HL$fDCfDKfT$D$DXf$D$f$f$XcXD$f$Xf$$f$\\YY$f$$GYY$O$WAAEEAAMAE1A)AAAE9L='DDHB8IcA‰ƉHD D HD HffdHcHcHcHHHfD,ffDLJ )f|fD3AYfDt3AYEYAYEY$f$AY$f$EYf$f$EY$fD$D$fD$D$$X$D$X$D$x\$\$DX$DX$x$p$h$`$XD$PD$HfD$xf$D\$f$p\$fD$hf$pf$hfD$`fD$XfD$`f$XX$PD$@$GDX$H$8$?\$P\$HE9DX$@DX$8D\$@!\$8Diy Q(DIDADq0q8TA D~EHcALiVUUUAI E)D)KAAEEE1AMDŽ$0DŽ$,)$0Ӥ$,DŽ$(DŽ$$Ӥ$(Ӥ$$ADŽ$ DŽ$Ӥ$ Ӥ$AE9D$45`)$L!'D$0$,$(H$$D$ BD$IcA A A AAHE HE Hf$fD|HcHcHIcHf<fDlffDLHcIcMcHHHIfDfl$f4fDtD$fD3fD3$fFD$$D$D$$$D$IcD$$HD$f$fD$f$fD$J )f$fD$fD$f$f$X$DX$fFd\$f ;D\$f\;X$DX$D$D\$$$\$$D$X$$D$x$pD$hD$`$g$X$_$PfD$fD$f$fD$fD$f$f$f$fD$f$DX$D\$\$DX$DX$\$\$D$HX$pD$@DX$h$8\$pD$0D$($ $'$$d$hD|$X|$8fD$f$fD$xfD$f$xf$PfD$HfD$Pf$HfD$@fD$8f$@D\$hX$`DX$XD\$`\$XX$0DX$(Dl$0D\$0T$P\$(DL$HDX$ DD$(DX$l$ \$ t$pDt$`D$D$@D$D$L$xfD$8fDD$xD\$fDl$xf$fD$X$f-D\$f%~fEfDW$f=gfgft$hE\fDt$XfD\$h$Xt$pY$D$DXt$`Y$E\D\\$p$D$Y$$1AY$Dq$$DY@fDT$XfD$PfDd$Hf\$Pfd$HfD|$8f|$0fDl$8fL$0fT$(fDL$ fDD$(D\T$`X$DX$\$\$DX|$@X$DQHD\l$@A\$DaX$YPDX$aXD\$Dy y(Di`IhQ0DI8DApfl$ D;$4\$ix$AfDD);fS fDsfD{fDc*fDk0f{8D^ffDS(5AYAYEYEYEYEY\AYD$XfD$f$D$D$EYfD$D$DX$f$\$DfD$D\$DE\D$f$XfEfEXD$fD$fDWXfW\$EXAXDXD\o DwA\ODgD(_0fD$D\D_8$3A[fD=D)Ϳf3fk@fDcfD[HD*fK f[(E^fDK`fDShfDCfcfDkPfCXf{0fS8fDspAYAYEYEY$H f$H AY$8 AYX$8 EYD$@ EYD$0 EY$( AY$ EYD$ AYD$ AYD$ AY$ EYDY{xD$ $ $ $ D$ D$ $ fD$@ f$H fD$@ Af$( f$ fD$ fD$( f$ fD$ f$ DX$0 \$8 D\$0 X$ X$ D\$ D\$ D$ X$ $ DX$ D$ \$ $ D$ D$ $ D$x $ $p D)$ D$h $` fD$ f$ f$ fD$ f$ fD$ f$ fD$ f$ fD$ EX\$ X$ \$ D\$ D$H X$ DX$ \$ $X D\$ $P X$ $@ $G DX$x D$8 $? $D$$D$$D$fD$` fD$X DX$@ fDDX$8 fD$ f$ fD$p f$h f$p f$h f$` fD$X D\$ D$f$\$x D$X$DX$P X$H \$P \$H D$\$@ $D\$8 DYD$$$( $$D$D$0 f$\$f$\$fD$fD$fD$Y4f$YfD$fD$f$f$DX$X$$/ D\$$ f$D\$X$0 \$0 EMAMEU@ffW$ DXD\EEHAe\$A}PEmYPEuXf$X$fD$fD$f$fD$f$fD$DX$( \$D\$( DXX$ AE f$D\$ E}(E]8Au`EehAm0EMp\AExAAEEAAMAE1A)AAAE9tL='DDf$f$fD$HfD$f$B8fD$fD$f$IcA‰ƉHD D HD HY,J )YtHcHcHcHHHY\E9DY43DY$DY\3DDY<YdXD$hf$h\EXD$`fD$`f$`$Pf$PA\AX$@DD$XfD$Xf$XDXD\A\$h$8\$8$HDXDDAXD)DXDXD\IQ fD$PD\DqDYDy0E\DQ8DA(pAAEAEAMAE1A)AAAE92wL=@')\$DDHB8IcA‰ƉHD D HD Hf4flHcHcHcHHfDfDdfD3HfD<3$fD$fdf<f$A\J )$fD$AXEXf$DA\$D\$f$D$EXfD$XfD$f$D$D$$A\DX$AXDXE\$$E9\DIDq A(DYfD$fD$DX$\$D\$DQI0Da8$=A D~ELcAMiVUUUAI E)D) AAEEE1AMDŽ$ DŽ$ )$ Ӥ$ DŽ$ DŽ$ Ӥ$ Ӥ$ ADŽ$ AӤ$ DŽ$ Ӥ$ E9D$ kD5D)$ffffLi'D$ $ $ H$ D$ BD$ IcA A A AAHE HE HfD fDDHcHcHcHHHf flfIcftf$3IcfDt3IcHHHfD$f|fDD$ fDfD,3D$ $ $ $ $ $ D$ D$ $ D$ $ D$ fD$ fD$ f$ Icf$ f$ Hf$ f$ J )fD$ fD$ f$ DX$ DX$ fDT3\$ f;\$ fD|;X$ X$ D$ \$ $ D$x D\$ D$ D$p DX$ $h X$ $` $X $P $H D$@ D$8 $0 fD$ f$ fD$ fD$ f$ fD$ fD$x fD$p f$x f$p f$h f$` D\$ \$ DX$ DX$ \$ D\$ DX$X D$( DX$P $ \$X D$ \$P D$ X$H $ X$@ D$ D$D$$$$$fD$ fD$( DX$ fDDX$ f$h fD$` fD$8 f$0 fD$8 f$0 f$( fD$ \$H D$0fD$0D\$@ D$(DX$(fD$(DX$ X$ D\$ \$ $\$ D$D\$ EYD\$0D$$D$DY-ܢ$$D$D$f$f$X$\$f-f5fD$fD$$f$fD$fD$f$f$f$YfD$X$YDX$EXD\$E\\$$X$$\$DYDX$!DQPDqDa@yHAQXDy fD$fD$f$f$f$f$f$DX$D\$D;$ \$X$X$\$\$DI(DA`ihI0Y8qpaxA.f-1wD)fKӤ$fs*$fc(fCfD3fD{ ^f[0fDS8YYYY$Hf$HfD$HDY$8fD$8DYY$@f$@DYXD\f$@EE\$0\$0EXX$0EXA\$8AEEEDXDXD\A\XD_AXD/\DO D\(GOo0Eu8aAAEAEAMAE1A)AAAE9FL=\~'DDHB8IcA‰ƉHD D HD HffDdHcHcHHcHfD,Hf|fJ )fDLfD3D$fl3f$Df$A\E9$f$$f$EXfD$AXD$DA\D\$XD\$AXED$DX\$DXD\EXYEXD1A\DY a(A\DQDyy0Q8wAfDsD)f{Ӥ$fD*$fcHfDk fS(fD{`D^fDshfsfDcfkPfKXfD[0f[8fDKpAYEYAYEY$ f{@AYEYD$ EY$ AYD$x AY$p EYD$h AYD$` AY$X EYAYD$P EYDYSx$H $@ D$8 $0 D$( D$ fD$ f$ f$ fD$ f$x fD$p fD$` f$x fD$X f$P DXf$X fD$P \X$ D\$ X$h D$ EDX$` $ D\$p \$h DX$H $ X$@ D$ \$H $ D\$@ D$ D$ $ D$ $ $ D$ fD$0 f$8 f$8 f$ f$ fD$ f$ fD$ fD$ f$ EXfD$ X$( D\$0 \$( X$ D$ X$ D\$ \$ $ DX$ D$ DX$ $ \$ $XD\$ $HD$0$ D$@D$8$D$fD$ fD$ DX$ f$ DX$ fD$ \$ f$ D\$ fD$ f$ f$ X$ D$xf$xDX$ D$f$\$$hX$xfD$hD$pDX$pf=\$ \$ $`DY-D$PYY$($ $ $ $ D$ f$p\$hfD$XfD$Hf$XfD$Hf$@fD$8f$@f$8fD$0fD$ fD$0DX$`YDX$P\$`D\$PX$ $x DX$ \$ D?\$ DwDX$ w@DX$(DgHD\$ oD_OP_XDO DW(DG`f$fD$f$ fD$f$\$(DX$ \$ XD\ghG8Do0WpDxf[fDSfKfS0fDC fc(fD D5o$D$f[8fD$$fD$$D\$fD$fD$f$AE\DX\$D\DXEfEAXAfXfEWAAfAWEXXA\A\DX?EXGD\w A\o(D_DGDO0g8\fD3fDc@fsf[HfDS fk(fC`fD[hD$p fD$p D$` DX$` fDK$h fDCf{P$X fSXfc0D$P fDk8fD{p$H fKx$@ D$8 D$0 D$( $ $ $ D$ D$ $ D$ f$h fD$p f$h fD$P f$H f$8 fD$P fD$0 f$( f$0 X$X D\$` \$X DX$@ X$8 \$H D\$@ $ DX$ D$ X$ $ \$ D$ Dl$ $ $ D$ $ D$ D)$ $ $ f$( fD$ fD$ f$ fD$ f$ fD$ f$ fD$ f$ \$ DX$ DX$ \$ D\$ X$ DX$ $ \$ D$ D\$ D$x X$ $p $w D$h $o $hD$X$8D$0$PfD$ fD$ DX$p fD%DX$h f$ fD$ fD$ fD$ f$ f$ f$ f$ fD$ D$f$X$ D$X$D\$ D\$ DX$ X$x \$ $H\$x DY\$p D$(D\$h D$ D$p$`$X $@$xD$D$` f$x\$f$\$fD$Hf$HfD$hY8f$XYf$hfD$XfD$Pf$PDX$p\$p$_ X$`$P f$xD\$`DX$` \$` DG@offW$ DX\D_HDO\$PDGYYgXfD$ fD$8fD$0fD$8f$0f$(fD$(DX$@f$ DX$X D\$@DX\$X X$P D\$P Do Dg8D(Dw`Ohw0DWpfDkfSfKfDs fD f{(fs0D$x$hfDk8$pf$xfD$pfD$hf$xEf$pE\$`\$`EX\EXD\$hEDXDXADAEXAXXE\A\D_DXE\oD\Dg g(DGDO0f+fD[f[HfC fDK(fDC`f{hfS$D$fD$fD[@f$$X$fc$fDkPfD{XD$fDs0fK8D$fspfDcx$EX$x$pD$hD$`D$X$P$HD$@D$8$0f$fD$f$fD$f$f$f$xfD$pfD$xA\fD$pf$Xf$PD\$X$DX$$(\$\$X$hDX$`D$ D\$h$D\$`D$X$H$X$@$$ D$ D$ D$ $ $ fD$@fD$Xf$8fD$0f$8fD$0f$(fD$ f$(f$ f$ fD$ D\$PD\$HX$DX$\$D\$X$D$ DX$D$ \$$\$D$X$ $DX$ D$$D$$$$D$f$ f$ fD$ X$ X$ fD$ D\$ fD$ D\$ fD$ D\$ D\$ $f$$fD$\$D$fD$DX$D$D\$f$X$fDЈD$D$ $ YAYEYEY$ D$ $ D$ f$fD$f$f$f$fD$fD$fD$f$fD$f$f$X$DX$\$\$X$ DX$ D\$ D\$ DGX$ @DX$WH\$ g\$DoDPDwXw Dg(o`Ohf$f$fD$fD$DX$ D\$ AXA\D_0_8DOpiffEHHHSHWfffAWIAVAUEATIUHDSHAH$w5AGAA#LAAfff1H[]A\A]A^A_ffff$E9fD=\DAD)DAA A*D^~AEHcALiVUUUAI E)D)؃TDE1DŽ$DŽ$DŽ$AMDŽ$)$Ӥ$Ӥ$Ӥ$$DŽ$Ӥ$Ӥ$DŽ$DŽ$Ӥ$Ӥ$A9މ$D ^D)$@ffLid'D$$$H$D$BD$McD$IAK9L$苌$ A A A HfDLfE,HcfTfEHcfDdfEHcHcIcIcfEfDEYfA fDtEYfA4EYIcEYf|EYD$fDLAYD$xfE,AYD$pfDdEYAYD$hfE$`AY$XEYD$PEY$HfD$hf$hf$f$AYf$xf$xEYEYEXEYA\fD$XX\f$pAXA\fD$p$0E\ƀ$7\$@f$`DXf$H$8D$fD$H$(f$`AX逴$/A\EXfD$PA\D$ AXf$XEX$$E\A\AX$fD$PA\D$$D$f$f$@$X$0fD$EXDX$fD$8$fD$@f$ fD$ DX$(D\$0fD$8Xf$D\$($\$D$8\$$0f$0D$D$fD%#fDAXD$E\fD$8$ D\$0AX$(A\AX$D$DY$EY$D$D$f$ fD$ D\$(fDpDf[f$fD$f$DXfD$f$ffW$@f$fD$fD$EY\$($X$DX$\$D$DD\$X$X$YD\$ D\$E{ $EI cAAD\Dc(Ei(fD$f$f$f$D[fD$f$DX$D;$\$X$X$D\$\$EqDS0AY0SAIDC8Ay8z$H$DLqE9"D$fD=TDD)DAA A*D^D$~AEHcALiVUUUAI E)D)؃DE1DŽ$DŽ$DŽ$AMDŽ$)$Ӥ$Ӥ$Ӥ$$DŽ$Ӥ$Ӥ$DŽ$DŽ$Ӥ$Ӥ$A9$V)$fL\'D$$$H$D$BD$McD$f$If$fD$K8f$f$A苌$f$ A A A HfD$Ytf$AYHcHcHcDY\L$AY,HcIcY\AY$IcDYT$AY<$IcD$$$x$pD$h$`fD$fD$fD$f$fD$f$fD$fD$f$f$fD$f$DY|X$EY X$DYlD\$AY \$DYdD$X$AYD$P$DYDD$HD$EY4$@D$8$0D$(D$ $f$xf$pfD$`f$xfD$XfD$PfD$Xf$PfD$8f$0fD$ fD$8X$hX$`D\$p\$hDX$HDX$@D\$H$\$@$DX$(D$X$ $D\$0D$D\$(D$D$$D$$D$D$f$f$fD$f$f$f$fD$f$fD$fD$fD$fD$X$X$D\$\$X$X$D\$$\$$pDX$D$XDX$$HD\$$hD\$$`D$@$8D$D$xD$PD$f$f$X$fD$X$f$D\$fDw\$f5wf%wf$fD$pf$$fD$DX$xX$$D$f$\$$\$D\X$EYAYDY$$D$fD$DX$DYv{ f$@f$@fD$pfD$hfD$`fD$hfD$`f$Xf$HfD$Xf$Hf$8D\$xAXDX$A\DX$D\$D\$X$X$PEx D\$DK\$PEhX$Dc(E@(KA@Ds0Ap0SAhc8fD$8D\$D;$EP84fDKH$DD$HD$HEKHD$fufafT$$f$Xu$f$X!X$8Xf$8$0f$0\\YY$(f$($ YY$ O$ S(fADAEEڻAAMAE1D$A)AAE9H=S'DDH8IcA‰Ɖ D Hf\fE4D HcHcHcfDTfDHfAE)ADfefDe AfA$fEl$ A*fufAl$D^fDu0fET$0fEfAT$fD}(fA|$(fD]fED$fM8AYEYAYEY$p f$p AYD$` AYX$` EY$h EYD$X AY$P AY$H EYD$@ AYD$8 EY$0 EY$( AYEYL$8D$ $ D$ D$ $ D$ $ f$h fD$p fD$h Af$P f$H fD$8 f$P f$0 fD$( f$0 X$X D\$` D\$X X$@ X$8 D\$H \$@ $ X$ D$ DX$ D$ \$ $ D>$ D$ $ $ $ $ D)$ D$ $ f$ fD$( fD$ f$ fD$ fD$ f$ f$ fD$ fD$ AXD\$ DX$ \$ \$ $x DX$ DX$ \$ D$ \$ D$ DX$ $p $w DX$ $h $o D$D$$$D$D$f$ fD$ X$p fD$ DX$h fD%cD\$p f$ f$ fD$ f$ fD$ fD$ f$ $f$\$ \$ DX$ D$EX$x D\$ D\$x \$h $D\$$D$$DY bD$X AXD$$DYD$` $_ f$f$fD5Zb\$fD$f$fD$ff$f$fW$ fD$EXf$fD$A\\$f$Y5aDX$X$H$\$D\$X$` D\$` DYX$D$P W D$H Dx DXDG(H(gf$fD$fD$f$f$fD$f$X$X D\$D\$X X$P X$H D\$P \$H XDg0Dh0whDw8H8"ADADEڻAMAE1ɉ$|A)AAA9L?'DDfD$f$HfD$fD$Bf$fD$f$fD$IcA‰ƉH D EYDD AHcD HHcDH HcD HL/J 'H<(J fE8ff/fDDYYYDYD?HcHA(A9DL(J< J &H.fA0fD/fDfD1YDYDYDY0D)ED7'D E HA fDTIcD9DYDTfA<YADEXAAXDXXEA\\DD$DX$A\D\$DXff\fAWA\D'DfAWD_nA fD%D)$$$$fF Ӥ$`fDD*$`fDfDo E^fW0fD~0f_fDNf(ff(fDFAYEYEYEY$(AYf$(EYAfGAYE\EYAXAYAX$AYD$fVEYfD$f_8AYD$ $$AY$EYD$AYDYf8$D\$(fD$D$f$ f$X$fD$f$fD$DXf$fD$fD$f$AXD$fD$D\$ DXD$$X$DX$AXD\$$f$\$D$\$D\$D$$D\$$D$D$D\X\$D$$fD$D\$fD$DX$f$f%7 fD$fD$fD$DX$f$Xf$D$fD$D\D$Ef$E\f$EX$fD$DX\$D$DX\$fD$A\D\$D$(E\DX$(DAXEXDY%: DYD\f$A\fDYfW=AXDYAXEXo F wDFf$f$f$f$fD$f$fD$f$A\fD$A\fD$XAXD\O(A\f(EXWEX^E\DW0A\n0DoDvDO8F8ffD fgfDV0fWffo0fD^fw(fDw8DffA\fDnfDNEXfDfXF X\^ EXDXn(X$hD\N(D$xDXf8$XD\VD$P\g0$p$HD$`\W(D$@D$8DXfEfD$0D$(fF8fD$p\FDDX$PfD$`fD$`fEWfAWfD$xf$xf$pfD$HDX$XAX$ f$@\$XDD$HfD$@\$PDX$(f$HD\$0l$@$'\$(\8D|$XX$0\$(t$ DD$PD$DXE\D\fDl$8Dt$DL$D$0fD$ fD$8fAWfD$8f\$Xf%fl$@DX\$ fDl$8DXfDL$@$D\f|$Hft$H\t$PDT$pEXDfEXD\d$pE\f\$(fAWfD\$X\\$0DYDYAXfDD$ A\fD|$8DX$DYD\>Yfd$(AXXd$0E\EXD_ v E\oDnDO(D~(gDF_0f|$fL$fD\$fD$ \$fl$AXXE\F0ND_8\Al$8ffNf/fDWfDfDFffvDDDXA\fNEXA\EXEAXA\ED\DDXDXD\D\DXD7AXD.\DgA\DND_fo.fWfD(ffDo fDWfF0fDw0fDfDXf.fDfgfDFf^8Xn A\D\N fD^AXXg8EXD$PDXF8fD~(\^$pD\W0$\W($xD$`D$hD$HD$@DX\FEXD\^(D$X$8f\8fD$h\f$hf$f$xX$`DXX$XfD$PEXDX$HfD$$fD$xD\$`$0f$0f$@D\$XD$$D$f-[A\fD$8$f$D$AXD\$0D$AXX$DX\$8D$D\$@\$x$f$PE\$HD\AXEfEDXfDW=A\DY7fD$fD$DYf$DX$D\$fD$\$YfD$Yf$f$fD$f$f$D.EXfD$D_ A\fD$N XfL$xDXfD$\DwEXA\DND\G(EX^(XDGnE\fl$xw0Dn0D_NDg8L5&D)$D$<$0$,D$ DHHC2F 0F3$8D$$f$@f$@f$@fD$@AfD$@fD$@D D$(fD$@D f$@Mcꋌ$4I HA A AYA YLHcHcAY$K8IcDYDHcHcEY,L$pIcEY$ADYL$IcAY,$$D$D$fD$@DYlD$D$$f$@fD$@fD$@f$@fD$@f$@fD$f$f$fD$f$AYDYtDX$EYX$YT\$EYAWA^\$T$D D HD AHHcfA fE|HE9YDYA fE4E|fElDYDYE4El7[]A\A]A^A_9f%$WA)EfAfWAA*AE^DAAE)D9YYW}H=_&HH49H8DD|=1 D D9HHDHHE<>D$89Dɉ AHAHE N$A ADfA<$AfAT$A McIY9OtYfA6fA^A>AVYYA4$A\$V\$T$ HcHD9fE +fED+DYDYE +ED+[]A\A]A^A_ÉAAAAiA)E9l$"L-2^&DLMIHB,/AF(G)D1AD D L$D9ffIHB)IB<(ADC4( L$ LcD IKD H8L@LcD IK L LQH9LAHcHLLPHcHN D9J<IIILLWHHOMMQIAE9AAAAADE)AE9H-\&LDLHHD /D(.DAAE A 1ADAD9}|DHHE/H (D/ HAHN<A DA ADAIE IcHD9JIwH:LBH HrI?MG|AE9.AWAVAUATIUHSU9!f% S)ˉfA,$AAA*^AA)DyIcE9YA,$f]Y]fAYAfLYLL-b[&fDLLHHF .F(B)D1AAE A ADAD9IHB/IB (AC4( D DD HcD D HHcHcD HHN'H /J< H(fE0fD)fD'fDDYDYDYDYD7D(HcHE D9DN H<(H .J&fEfDfDf9DYDYDYYDD E?DD E HE AfAIcE9YAfTYTfA YA fD|DYD|M[]A\A]A^A_9f%PA)EfA4$AAAAAA*^EDE)AE9YA4$f.Y.}L 1Y&DLLHHB< B B4 1 D DD9fffffIHF4 IB4ADC AHHA H4(A ADfDAE IcHDYD9NEYD$pAf$@fD$pfD$hfD$`f$hf$`f$Xf$HfD$XAXf$HfD$8D\$D\$xDX$y0DX$\$\$X$xDq@X$PDyHD\$xDI\$PDADX$piPYXq a(Da`IhDY8fD$@f$8\$pD;$E\QxDQpfffffDDT$HL$HE HL$fDCfDKfT$D$DXf$D$f$f$XcXD$f$Xf$$f$\\YY$f$$GYY$O$WRfAAEAEAMAE1A)AAAE9rL +&DHHBB4B< D IcAЉ։D HcD HHD HffdHcfD,f|HHcHHffDLfDAYfDtAYJ )EYAYAY$f$EY$f$f$f$D$$fD$X$$\$EYD$X$EY\$DX$D$D$x$p$`$h$XD$PfD$xf$fD$D\$\$f$pDX$xfD$hf$pf$hfD$`fD$XfD$`X$PD$@$G\$P$8$?DX$@D$HDX$8E9DX$H\$HD\$@!y DIDADiQ(Dq0f$X\$8q80A D~EHcALiVUUUAI E)D)ADŽ$0EEE1AMDŽ$,)$0Ӥ$,DŽ$(DŽ$$Ӥ$(Ӥ$$ADŽ$ DŽ$Ӥ$ Ӥ$DŽ$Ӥ$E9D$45L5l&)$fffDዴ$(D$ DHHC2B<0F1$0$,D$ $$D D$IcA A A A AHcHHHE f$fD|HcHf<fDlffDLIcIcHcHHHf4fD3fl3$fDtfDD$fD$D$$D$$D$$McD$D$Mc$f$fD$f$fD$If$fD$IfD$f$HX$fF J )DX$fFd \$fB D\$fB\X$D$DX$D$D\$$$\$$D$$D$x$pD$hD$`$g$Xf$fD$fD$f$fD$fD$f$f$f$fD$X$$_DX$D\$\$DX$DX$\$$P\$D$HX$pD$@DX$h$8D$0D$($ $'$$d$hD|$Xf$fD$f$fD$xfD$f$xf$PfD$HfD$Pf$HfD$@fD$8\$pD\$hX$`DX$XD\$`\$XX$0|$8DX$(Dl$0D\$0T$P\$(DL$HDX$ DD$(DX$l$ t$pDt$`D$D$@D$D$f$@fD$8\$ f$D\$fD$X$f-2D\$f%2f=2f2ft$hfDt$XL$xfDD$xfDl$xXt$p$$DXt$`Y$D$Y$fEfDW$E\$1$D$Y$E\AY$$DqfD\$hfDT$XfD$PfDd$Hf\$Pfd$HfD|$8f|$0fDl$8fL$0fT$(fDL$ D\\$pD\T$`X$DX$\$\$DX|$@DY@X$DQHD\l$@A\$DaX$YPDX$aXDy y(Di`IhQ0DI8fDD$(fl$ D\$D;$4\$DApix.$+A fDD);fS fDsfD{fDc*fDk0f{8D^ffDS(5 AYAYEYEYEYEY\AYD$XfD$f$D$D$EYfD$D$DX$f$\$DfD$D\$DE\D$f$XfEfEXD$fD$fDWXfW\$EXAXDXD\o DwA\ODgD(_0fD$D\D_8$AfD=(D)Ϳf3fk@fDcfD[HD*fK f[(E^fDK`fDShfDCfcfDkPfCXf{0fS8fDspAYAYEYEY$H f$H AY$8 AYX$8 EYD$@ EYD$0 EY$( AY$ EYD$ AYD$ AYD$ AY$ EYDY{xD$ $ $ $ D$ D$ $ fD$@ f$H fD$@ Af$( f$ fD$ fD$( f$ fD$ f$ DX$0 \$8 D\$0 X$ X$ D\$ D\$ D$ X$ $ DX$ D$ \$ $ D$ D$ $ D$x $ $p D)$ D$h $` fD$ f$ f$ fD$ f$ fD$ f$ fD$ f$ fD$ EX\$ X$ \$ D\$ D$H X$ DX$ \$ $X D\$ $P X$ $@ $G DX$x D$8 $? $D$$D$$D$fD$` fD$X DX$@ fD*DX$8 fD$ f$ fD$p f$h f$p f$h f$` fD$X D\$ D$f$\$x D$X$DX$P X$H \$P \$H D$\$@ $D\$8 DYD$$$( $$D$D$0 f$\$f$\$fD$fD$fD$YF)f$Y-)fD$fD$f$f$DX$X$$/ D\$$ f$D\$X$0 \$0 EMAMEU@ffW$ DXD\EEHAe\$A}PEmYb(EuXf$X$fD$fD$f$fD$f$fD$DX$( \$D\$( DXX$ AE f$D\$ E}(E]8Au`EehAm0EMp\AExAAEAEAMAE1A)AAAE9L &Df$f$fD$f$HHBB<B4 DfD$fD$fD$f$ IcAЉ։HcD D HHD Y,HHYtHcJ )HY\DY$HcDY4HE9DY\DY<DYd\EXD$hXf$hD$`fD$`f$`A\$@DD$XfD$XDAX$Pf$PDX\A\$h$HDDDX$8AXDXD)DXD\IfD$Pf$X\$8D\DqDYDy0E\DQ8Q DA(AAEEAAMAE1A)AAAE9}eL .&)\$DHHBB<B4 D IcAЉ։HcD D HHD flHf4fDfDdHcHHcHfDHfD<$fD$f<fdf$A\J )$AXf$EXDfD$A\EX$$Xf$D$fD$fD$f$D\$A\DAXDXE\D$$\$$E9DIDq A(fD$fD$DX$DX$\$D\$DYDQI0Da8dA D~ELcAMiVUUUAI E)D) AE1DEDŽ$ MDŽ$ )$ Ӥ$ DŽ$ DŽ$ Ӥ$ Ӥ$ ADŽ$ DŽ$ Ӥ$ Ӥ$ DŽ$ Ӥ$ A9$ D5rL5;&D)$fDዴ$ $ DHHC3F0F 1$ $ D$ AD D$ D D$ IcA A A A AHHcHHE fD fDDIcf flHcHcIcHHHHfftD$ f$3fDt3D$ fD$;f|;$ fDfD$ $ $ Ic$ D$ IcD$ $ HD$ $ fD$ fD$ f$ Hf$ f$ Hf$ f$ J )fD$ fD$ DX$ fD,DX$ fDT\$ f3\$ fD|3X$ D$ X$ D$ \$ $ D$x D\$ D$ D$p DX$ $h $` $X $P $H D$@ D$8 f$ fD$ f$ fD$ fD$ f$ fD$ fD$x fD$p f$x f$p f$h X$ D\$ \$ DX$ DX$ \$ D\$ $0 DX$X D$( DX$P $ \$X D$ \$P D$ X$H $ D$ D$D$$$$fD$ fD$( DX$ fDDX$ f$` f$h fD$` fD$8 f$0 fD$8 f$0 f$( fD$ D$0fD$0X$@ D$(DX$(fD$(\$H D\$@ DX$ X$ D\$ $\$ EY\$ $D\$ D$D\$0D$$D$DY-v$$D$D$f$f$X$\$f-,f5fD$fD$$f$fD$fD$f$f$f$YfD$X$YDX$EXD\$E\\$$X$$\$DYDX$!DQPDqDa@yHAQXDy fD$fD$f$f$f$f$f$DX$D\$D;$ \$X$X$\$\$DI(DA`ihI0Y8qpaxAXf-D)fKӤ$fs*$fc(fCfD3fD{ ^f[0fDS8YYYY$Hf$HfD$HDY$8fD$8DYY$@f$@DYXD\f$@EE\$0\$0EXX$0EXA\$8AEEEDXDXD\A\XD_AXD/\DO D\(GOo0Eu8AAEEAAMAE1A)AAAE9=L %ffffDHHBB4B< D IcAЉ։D D HcHHD HfDdffD,fDLHcHHcHf|fHfDflD$f$f$DA\J )E9$f$$f$EXfD$AXD$DA\D\$XD\$AXED$DX\$DXD\EXYEXD1A\DY a(A\DQDyy0Q8S~AfDSD)f{Ӥ$fD*$fcHfDk fS(fD{`D^fDshfsfDcfkPfKXfD[0f[8fDKpAYEYAYEY$ f{@AYEYD$ EY$ AYD$x AY$p EYD$h AYD$` AY$X EYAYD$P EYDYSx$H $@ D$8 $0 D$( D$ fD$ f$ f$ fD$ f$x fD$p fD$` f$x fD$X f$P DXf$X fD$P \X$ D\$ X$h D$ EDX$` $ D\$p \$h DX$H $ X$@ D$ \$H $ D\$@ D$ D$ $ D$ $ $ D$ fD$0 f$8 f$8 f$ f$ fD$ f$ fD$ fD$ f$ EXfD$ X$( D\$0 \$( X$ D$ X$ D\$ \$ $ DX$ D$ DX$ $ \$ $XD\$ $HD$0$ D$@D$8$D$fD$ fD$ DX$ f$ DX$ fD$ \$ f$ D\$ fD$ f$ f$ X$ D$xf$xDX$ D$f$\$$hX$xfD$hD$pDX$pf=\$ \$ $`DY-D$PYY$($ $ $ $ D$ f$p\$hfD$XfD$Hf$XfD$Hf$@fD$8f$@f$8fD$0fD$ fD$0DX$`YDX$P\$`D\$PX$ $x DX$ \$ D?\$ DwDX$ w@DX$(DgHD\$ oD_OP_XDO DW(DG`f$fD$f$ fD$f$\$(DX$ \$ XD\ghG8Do0WpDx+f[fDSfKfS0fDC fc(fD D5M$D$f[8fD$$fD$$D\$fD$fD$f$AE\DX\$D\DXEfEAXAfXfEWAAfAWEXXA\A\DX?EXGD\w A\o(D_DGDO0g8fD3fDc@fsf[HfDS fk(fC`fD[hD$p fD$p D$` DX$` fDK$h fDCf{P$X fSXfc0D$P fDk8fD{p$H fKx$@ D$8 D$0 D$( $ $ $ D$ D$ $ D$ f$h fD$p f$h fD$P f$H f$8 fD$P fD$0 f$( f$0 X$X D\$` \$X DX$@ X$8 \$H D\$@ $ DX$ D$ X$ $ \$ D$ DD$ $ $ D$ $ D$ D)$ $ $ f$( fD$ fD$ f$ fD$ f$ fD$ f$ fD$ f$ \$ DX$ DX$ \$ D\$ X$ DX$ $ \$ D$ D\$ D$x X$ $p $w D$h $o $hD$X$8D$0$PfD$ fD$ DX$p fD%' DX$h f$ fD$ fD$ fD$ f$ f$ f$ f$ fD$ D$f$X$ D$X$D\$ D\$ DX$ X$x \$ $H\$x DY\$p D$(D\$h D$ D$p$`$X $@$xD$D$` f$x\$f$\$fD$Hf$HfD$hY f$XY f$hfD$XfD$Pf$PDX$p\$p$_ X$`$P f$xD\$`DX$` \$` DG@offW$ DX\D_HDO\$PDGYgXfD$ fD$8fD$0fD$8f$0f$(fD$(DX$@f$ DX$X D\$@DX\$X X$P D\$P Do Dg8D(Dw`Ohw0DWpcfDkfSfKfDs fD f{(fs0D$x$hfDk8$pf$xfD$pfD$hf$xEf$pE\$`\$`EX\EXD\$hEDXDXADAEXAXXE\A\D_DXE\oD\Dg g(DGDO0f+fD[f[HfC fDK(fDC`f{hfS$D$fD$fD[@f$$X$fc$fDkPfD{XD$fDs0fK8D$fspfDcx$EX$x$pD$hD$`D$X$P$HD$@D$8$0f$fD$f$fD$f$f$f$xfD$pfD$xA\fD$pf$Xf$PD\$X$DX$$(\$\$X$hDX$`D$ D\$h$D\$`D$X$H$X$@$$ D$ D$ D$ $ $ fD$@fD$Xf$8fD$0f$8fD$0f$(fD$ f$(f$ f$ fD$ D\$PD\$HX$DX$\$D\$X$D$ DX$D$ \$$\$D$X$ $DX$ D$$D$$$$D$f$ f$ fD$ X$ X$ fD$ D\$ fD$ D\$ fD$ D\$ D\$ $f$$fD$\$D$fD$DX$D$D\$f$X$fD@D$D$ $ Y$AYEYEY$ D$ $ D$ f$fD$f$f$f$fD$fD$fD$f$fD$f$f$X$DX$\$\$X$ DX$ D\$ D\$ DGX$ @DX$WH\$ g\$DoDPDwXw Dg(o`Ohf$f$fD$fD$DX$ D\$ AXA\D_0_8DOpffEHHHSHWtfffAWIAVAUEATIUHDSHAH$w5AGA]AMAAfffD1H[]A\A]A^A_ffff$E91fD=DAD)DAA A*D^~AEHcALiVUUUAI E)D)؃DE1DŽ$DŽ$DŽ$AMDŽ$)$Ӥ$Ӥ$Ӥ$$DŽ$Ӥ$Ӥ$DŽ$DŽ$Ӥ$Ӥ$A9։$D D)$@ffDL %$$DHHC FF Mc$$ID$K9L$AAD D$D D$ꋌ$ HA A A fTHcfEHcfDLfE,HcfDdfEHcIcIcfEfDfA IcfDtfA4EYEYf|EYEYAYD$fDLAYD$xfE,EYD$pfDdAYD$hfEEY$`$XAYD$PEY$HfD$hf$hf$f$EYf$xf$xAYEYEXEYA\fD$XEYX\f$pAXA\fD$p$0E\ƀ$7\$@f$`DXf$H$8D$fD$H$(f$`AX逴$/A\EXfD$PA\D$ AXf$XEX$$E\A\AX$fD$PA\D$$D$f$f$@$X$0fD$EXDX$fD$8$fD$@f$ fD$ DX$(D\$0fD$8Xf$D\$($\$D$8\$$0f$0D$D$fD%kfDbAXD$E\fD$8$ D\$0AX$(A\AX$D$DY$EY$D$D$f$ fD$ D\$(fDDff$fD$f$DXfD$f$ffW$@f$fD$fD$EY\$($X$DX$\$D$DD\$X$X$YD\$ D\$E{ $EI cAAD\Dc(Ei(fD$f$f$f$D[fD$f$DX$D;$\$X$X$D\$\$EqDS0AY0SAIDC8Ay8Rf$H$DLE9#$fD=DD)DA *D^D$~AEHcȉHiVUUUH ))ADE1DDŽ$DŽ$DŽ$AMDŽ$)$Ӥ$Ӥ$Ӥ$D$DŽ$Ӥ$Ӥ$DŽ$DŽ$Ӥ$Ӥ$A9։$#)$DL%$D$f$HHBBFMc$$I$D$Af$fD$f$f$ f$fD$D D$K8ꋌ$L$ A A A HHcYtAYHcIcDY\McAY,HcHcY\AY$IcDYT$$D$$$x$pD$hf$fD$fD$fD$f$fD$f$fD$fD$f$f$fD$AY<X$DY|X$EY D\$DYlAY $`$FYdD$X$CYD$PDYDD$HEY4$@D$8$0D$(D$ D$f$f$xf$pfD$`f$xfD$XfD$PfD$Xf$PfD$8f$0fD$ \$X$hX$`D\$p\$hDX$HDX$@$D\$H$\$@$DX$(D$X$ $D\$0D$D$D$$D$$D$fD$8f$f$fD$f$f$f$fD$f$fD$fD$fD$D\$(X$X$D\$\$X$X$D$D\$$\$$pDX$D$XDX$$HD\$$h$`D$@$8D$D$xD$Pf$f$X$fD$X$f$D\$fD \$f5f%fD$f$fD$p$fD$D\$X$D$f$DX$x$\$D$$D\X$YAYDY$$D$fD$DX$DYEf$@f$fD$pfD$hfD$`fD$hfD$`f$Xf$HfD$Xf$Hf$8\$AXD\$xDX$DX$D\$D\$X${ X$PEx D\$DK\$PEhX$Dc(E@(KA@Ds0Ap0SAhf$@fD$8D\$D;$A\c8EP8ffffDH$DD$HD$HEHD$fufafT$$f$Xu$f$X!X$8Xf$8$0f$0\\YY$(f$($ YY$ O$ SffADAEӻDŽ$AAME1D$E)$AAD;$|L%fffDDHHCB<B4$ IcAЉ։D HcD f\fE4H HcfDfAAY$$fL$fDd$f=>D9f5>fT$fDD$fDL$ffWL$E\fl$fDT$X\L$fD|$D\fD\$fD$fDl$DXL$AYXl$QD\T$DAXD\|$DX$Y$\$DXl$D i$DQ@DyHDYAPDi f\$fDt$fDd$f|$fL$ft$fDL$X$D\t$D\$X$X$\$D\$Y(Dq`Dahy0I8qpDIx@NA AAAA)AAD9hL %DfD$HfD$HfD$Hf$HDHHCHB4F<fD$Hf$Hf$Hf$H AB<D D IcAЉ։ HcD HHD HDY HDY<N<)DY\HcHYlHcDYTHE9YYdEYE\EXD$ fD$ fD$ AD$A\AXEXDXD\$D\DEXEXD\A\AXEXEoE\Ew \A(AOAwE_0AW8{NA AAAA)AAD9JD5L t%D)t$DDHHCHB4F< AB<D D IcAЉ։ D HcHHD HfD<fDDfD\fD$HcHHcHfDHfD4fdfAEN<)E\AD\X\ED$EXE\D$$AXDX$E9ffWd$XAD$EEA\$EoXX\D\A?DXAwA_8D\AO EO(EWE_0a ~FHcALiVUUUAI E)D)NADŽ$t DŽ$p)$tӤ$pDŽ$lDŽ$hӤ$lӤ$hDŽ$dDŽ$`Ӥ$dӤ$`DŽ$\AӤ$\D9D%,L%%D)d$xIHIB&B< AG A$tG "$p$lD$dD$\ A$hD D D$`Hc̓ A A A AHcHHHE fD4fDLHcHf,fDdf fdHcIcIcIcHHHHffD,3f4;fDD;D$PfD<D$H$@D$8$0$($ D$fDfDl3$D$D$fD$(f$fD$Pf$HIcfD$PfD$HHf$0f$0Hf$fD$DXDX$@fD\AXX$8f<J )D\$@f\D\$8D$X$ D$$\$ $X$$D$D\$$\$(D$D$$$$D$f$f$fD$fD$f$f$fD$f$fD$fD$XfD$f$\DX$DX$\$l$H\$d$ DX$X$D\$D$D\$D$DX$$\$$xD\$DT$`T$PDt$8DL$(Dd$@L$fD$f$D\$fD$\$f$DX$f$X$fD93X$fD53fD$fDL$`D|$0fD$xfl$P\$pfT$pD\$DD$hDXL$ht$XXl$XEXD\$xD$pD $wD9A\DXY 2AXDY=z2A\DYDYD$hD$`ifd$HfDD$@ft$HfD\$@fDd$`fD$Pf|$8f\$(fDT$8XfDl$(fT$ EXfDt$fDL$ \D\d$hE\\D$XaX$pDAX\$0qPD\$pDYXD\l$0X$hDa@DX$`AHD\$hy Y(DQ`DihQ0Dq8DIpfl$\$`ixHNA AAAA)AAD9L J%DDHHCHB4F< AB<D D IcAЉ։Hc D HHD ffDdHcHHHfDfDtHcfDHfDf|fD AN<)E9A\D$Xf$XDXf$XAXDAXE\EAXD\DA\DAADXDX\\EXE/XEE\Ao \Aw(E_AWEG0A_8}fAWIAVAUATIDUHSH$H$pD$D9zfDD)ѺA *^$~A@HcȉHiVUUUH ))؃AHDŽ$ DŽ$)$Ӥ$DŽ$DŽ$Ӥ$Ӥ$DŽ$DŽ$Ӥ$Ӥ$DŽ$DŽ$Ӥ$Ӥ$ED;$L5r %)$xD$fD$fD$fD$IHIB6B<0AG3Aً$G1$McD$fD$f$f$ A$D D$ID D$fD$ꋌ$fD$ HA A DYDA EYHcHcEYDIfEEfA^fAUYYDYYEA}AVA]\$T$D D HD AHHcfA fE|HE9YDYA fE4E|fElDYDYE4El[]A\A]A^A_9Tf%A)EfAfWAA*AE^DA AE)D9YYW}H=]$IDIHAt=AD48A $\$HL$ljމHHF49F8B<>AL$G\=E1AAE A E AL$AA9ffffLDMDIHC;AB<8DG8B49L$A D DL$D Lc t$AIK<#I+ T$LLLc T$IK!I )LL LLHcHcHLHL/D;l$N H(J'LMI H;LHI8MLD$Dt$D9t$-ffAWAVAUATEUHDSHHAKAA9H55DHc HE9 fDDAD$D)ɿ D*E^~ AD$LcAMiVUUUAI E)D)AAL$DŽ$ EEE1AAL$DŽ$)$Ӥ$DŽ$DŽ$Ӥ$Ӥ$ADŽ$DŽ$Ӥ$Ӥ$DŽ$Ӥ$E9D$5=L5e$)$DHHHB6F 0F7A$G3$$$AD$D AD$D D D$IcA A A A AHcHHHE f fDdHcHfD4fDfDHcf\HIcIcfD<HMcIf,AYEYfBt HEYHEYH )AY$f 3D$fDDIcHAYD$fD,AYD$$flfDt3AY$fB EYEY$EYAYDEYD$f|f$f$fD$EXD\$DfD$DX$A\AYAYAXf$D$hAYA\fD$h$f$EXX$fD$$pD\D$xfD$p$`f$xf$\$f$D$XDX\$fD$\D\$fD$DX$$P$HEX$@D\$pDX\$hAXD$D$X$fD$Pf$`DX$HfD$PD\$Hf$xf$XAXD\$`D$DA\D$fD$fD=&DX$f$\f AX$f$XA\fD$D$E\DX$XAXDY$D\f$D$8A\fD%$(DY$8Y$(EYX$0$7D$8D$ D$()fD$f$fD$DXfD$0f$\f$f$D\f$X$(X\$8D;$D\f$Dy(X$8yhDA@fD$EXQD\$0DaHAD\$(X$ EaPD\$ i0qDq Di`AXDApA\Q8DaxIAXAt$HIczL=$fDBADAfA$fAtEAD9DDDXDYE\A fD%DfDff?EA)PHEXMcfDlfBlLcD9McfFtfB\fF|AXF\lfE$f$f$fEWfEE\fEWAXDD$f$AYDY$f$DYAYYAYD\EYAYDX\AXA\AXD\$AXAYAYAYEYLBTBlFl AL$AA EAEAAL$AE1A)AAAE9L $DHBHB<H B<D B< IcAЉ։HcD D HHD fD|f4HHcffDLHHcHfDlHH )EYfTf,AYE9EYEYD$fD<f$$AYfD$AYfD$f$AYD$fD$EYAXD$DX\$$DX$D\fD$E$f$f$AD\$DXD$D\$AXAXE\A\y!Da q(fD$f$f$f$EXAXA\A\DiQY0I8AD$ ~ AD$HcALiVUUUAI E)D)AAL$DŽ$ EEE1AAL$DŽ$ )$Ӥ$ DŽ$DŽ$Ӥ$Ӥ$ADŽ$DŽ$Ӥ$Ӥ$DŽ$Ӥ$E9D$D%.L5$D)d$`DIHIB6B<0AG 0A$G2$ $D$ A$D D$D D$IcA A A A AHHcHHE f fDtIcfDfDdHcHcIcMcHHHHIfDftfD,3Icf,;f<HfTfF$D$D$D$D$$D$$fDl3fl;$$D$ffD$HfD$f$H )f$f$fD$fD$f$f$$\$fD|EX\$fFLDXX$f$D\$DX$D$fD$\$D$D$hX$$D$PD\$$xX$$pDX$D$`\$D$X$H$@f$f$pX$f$hf$fD$fD$fD$fD$xfD$xfD$Xf$PXf$XD\$AX$8D\$AXDX$`E\D\$`d$HDX$@D$8X$8L$0\$@D$0DT$\$pD$(D\$hDt$(Dd$DD$Pt$@|$ fD$Pf$HD\$8fD$H\$0fEDX$0f ;fD-*fDT$HfDd$8fDD$Hft$8DXT$PD$fD$(DXd$@T$Xfd$XfDt$XDL$pD\D$P$\t$@DX\$(DDaE\DX\$pDA@XDYD\AYYEY$ $D$qHf$fL$ fDL$(fl$(f|$0fD$0fT$fDt$fDl$XfDT$AXX$ \$ EXD;$\$A\D\|$ a X$I(DX$DID\$yiXD\$APY`DyhQ0Dq8DipDQxhAL$A AAAAAL$AE1A)AAAE9&L ^$DHBHB<H B<D B< IcAЉ։D HcD HHD HfD f\f,fDHcHHcHfD\HfD4fD<EfdD\DH )DXE9D$f$f$\EDXAD$EXEXA\D$E\AEXXE\D\XDEXy\DA A\Di(QDYA0a8vDfufDMIc1HIcH}1t$xf|$xfD\$xAXE\}t5H|ArC<D\9 ffffLc)HLLNLfDD19fEfEWFD|H[]A\A]A^A_fDsfDCDAfD{fD AE1E*DY%ֶLMEXLM(EXEXEXEEYE\EEYE\EAEYE\EYAXADM DU8D]}D]0ffW5u1H[]A\A]A^A_Ak f=]AD)fCfDc`Afk fK(E*fDC@fcA^fD fsfDkPfD[XfDs0fS8f[pYDYYY$fCHDYD$fDchDY$fD$f$$f$YYEDYE\YEXDY$DYDXDY\f$YAXX$YY{x$D$D$D$D$D$x$p$$$hf$fD$f$f$f$fD$fD$fD$f$XfD$fD$xDXf$x\$\X$DX$$0D\$D$D\$$XX$D\$$`ADX$h$X\$hD$PD\$D$H\$D$@$8D$(D$p$HfD$Xf$XDX$8fD$HAX\$8f$@E\\$(f$P\$0f$pD fD$p$hfD$PfD$HfD$X$`D$D\$`$Pf$@D\DX$0f-DX$D)$X$(D$ $'D$DX$f$$`D$@DXD$xDYDef$\$f$p\X$xf$HA\$Pf$hfD$`fD$hYfD$`fD$pfD$XD\$x\u@f$@AXfD$XMDX$ AEhD\$ E\f$@YAXYf$HDmHX$PXDMpXDU M0De`DXD\u8D\em(D]DuPD}X\fD%fDfD=fD-eAExEE?DE)ƍPHD^Lcf\fFTLc҃IcfB|flA\ffW$AXffW$EEY\XDYAY$AAYAYEYAYA\EYXE\XX\\AYDXAYAYAYtBTBL\fDEfD}1ɀuOHMH !EE\EXDDufffLc)HLd)ȃHNdfD\fEfDW$F\~RAfD5ŸAD)f fCAfDk fk(A*fD{fDc0D^fsEYAYAYAYAYEYEYDYs8DDEXDADXA\\AAXAEXA\A\DDAXEXE\D\X}XDE\DM DU(]eD\M0fUfm0D E1fufMLeHfDfD%QLeDm8fe8D\fD-u/Dֹ DXfffAWEfAW\DXfeDAD\XX\EYAY]e@EXXE\A\EYAYEYAYDu}D}0U8Lc)HLt)ȃHNtfLffAWBL~H;1HMH}fXD$LD$LESLD$fSffl$1H]H]XXY\]YewfsfD+fD[HfDc@fK(f[`fcfDs$fD$f$D$EDX$\$fD{P$fCXfk0EXfS8fDCp$fD[ f[h$xD$pD$hE\$`$XD$@$PD$HD$8fDCx$0f$f$f$Pf$xfD$pfD$xf$pAXf$XfD$XXfD$@fD$8f$@X$hAXDX$`$(D\$h$ \$`AX$H$D\$HDX$($DX$ D$\$(D$\$$D\$$D\$PD$D$D$$f$fD$\$XfD$DX$fD$D\f$f$8\$f$0fD$0f$$\$ EXf$$f$\$X$E\f$D%D$ X$ED$8X$ D$0$f5D)$DDX$0$f$0AX〴$E\$DY%D\X$8$\D$Mf$\$ f$DYX$f$YfD$fD$YfD$fD$fD$f$A\f$f$]@f$D\$eDX$AX$EX\$EPf$\$D]HDuDM DX}(D\u`AXMhXE\D}DUXe0]8Dmpf fDk f[f{fS8fDs(fc0fsDDAXDA\DDXDE\DXDD\EXE\AEXA\EXAXE\D]Xm A\D}UDE(]&fffffffffffAWAVAUIATUDSHDHAw:AFAAfffOAAK ffff1HĘ[]A\A]A^A_Ã$dE9fD=DDAD)ɉA E*E^~ELcAMiVUUUAI E)D)ȃSADŽ$ EEE1AMDŽ$)$Ӥ$DŽ$DŽ$Ӥ$Ӥ$ADŽ$DŽ$Ӥ$Ӥ$DŽ$Ӥ$E9D$J RL5$)$ ffDIHIB7B40AG3A$G1$$D$D$ A$D D$D IcA A A A AHcHHHE fD fDDHcHf\f,f4fdHcIcIcHHHfD$f|f ;AYfDl;fDIcHEYIcEYHHAY$hf3AYJ )AYD$EYD$xAY$pAY$`EY$Xfd3EYD$P$HAY$@D$8AYD$0fDfTfD$f$@f$@D\$pfD$xf$HfD$`D\$hEY\$XAYfDdD\$PXfD$`DX$PD$(D$fD$$ fD$Xf$xDX$HEYD$\DX$p$X$hD$$$$f$8$f$ f$8fD$0f$0D\$(XfD$X$(fD$A\DX$f$DX$\$$f$D$\$fD$\f$ $A\D$fD$$D$$AXfD$AXDX$ED\$E\$$\EXD$D$DXAX$XD$D$fD$f$fD$\$fD f%fD5DX\$$D$$fD$DX$fD$f$f$\$fEfDW$ D\D$fD$DY$$Y$D$DY$D\EXD$$D$Y$D$$DfD$f$f$fD$fD$Ef$fD$A DXDX$\f$DX$A\fD$\$D\$DIDf$aHf$X$D\$I@DiDX$D\DqX$qP\$DaX\$Y(DY`DIhDA0y8QpaxD;$ff$LzE9#D$fD=؍DD)DAA A*D^D$~EHcALiVUUUAI E)D)E1AADŽ$ AMDŽ$)$Ӥ$DŽ$DŽ$Ӥ$Ӥ$ADŽ$DŽ$Ӥ$Ӥ$DŽ$Ӥ$E9D$D5L5$D)$DfD$f$fD$fD$IHIB7B40AG1A$G3$$D$D$fD$ A$D f$f$D D$fD$IcA A A A AHHcHHE DY$HY|HcIcDY,HHDY\HcIcHDYHYDIcJ )YHD$DY|Ic$HD$xD$pD$h$`$XD$PfD$fD$f$f$f$f$f$fD$fD$f$fD$fD$DY DX$xDYDX$pY,;D\$xY\;D\$pY 3D$HD$Yt3D$@$Y$$8D$DYt$0$($ $D$D$fD$hf$`f$PfD$hfD$HfD$@f$Hf$@f$(f$ f$fD$(DX$XX$P\$`D\$XDX$8DX$0\$8D$\$0$X$$X$D$\$ D$D\$D$$$$$$D$fD$f$fD$fD$fD$f$f$fD$fD$fD$f$DX$\$fD$D\$DX$DX$\$\$D$pDX$$XDX$D$HD\$D$h\$D$`DX$$@$8D$D$xD$P$xf$f$X$f$X$fD-\$fD$D\$fDxf$fD%ff$p$$f$X$x$X$f$$\$AX$DY$$DY$D$fD$D$EXD\$DY-EYD$pAf$@fD$pfD$hfD$`f$hf$`f$Xf$HfD$XAXf$HfD$8D\$D\$xDX$y0DX$\$\$X$xDq@X$PDyHD\$xDI\$PDADX$piPYXq a(Da`IhDY8fD$@f$8\$pD;$E\QxDQpffffDDT$HL$HE HL$fDCfDKfT$D$DXf$D$f$f$XcXD$f$Xf$$f$\\YY$f$$GYY$O$W2fAA EEAAMAE1A)AAAE9bL ;$DHBHB<H B<D B< IcAЉ։D HcD HHD HffdHcfD,f|HHcHHffDLfDAYfDtAYJ )EYAYAY$f$EY$f$f$f$D$$fD$X$$\$EYD$X$EY\$DX$D$D$x$p$`$h$XD$PfD$xf$fD$D\$\$f$pDX$xfD$hf$pf$hfD$`fD$XfD$`X$PD$@$G\$P$8$?DX$@D$HDX$8E9DX$H\$HD\$@!y DIDADiQ(Dq0f$X\$8q8tA D~EHcALiVUUUAI E)D)ADŽ$0 EEE1AMDŽ$,)$0Ӥ$,DŽ$(DŽ$$Ӥ$(Ӥ$$ADŽ$ DŽ$Ӥ$ Ӥ$DŽ$Ӥ$E9D$45L5j$)$fDIHIB6F0AG 2$0F7$,$($$AD$D AD D$ D D$IcA A H A HA Af<fDlHcHcHE Hf$fD|ffDLIcHcIcHHHf4fD3fl3$fDtfDD$fD$D$$D$$D$$McD$D$Mc$f$fD$f$fD$If$fD$IfD$f$HX$fFJ )DX$fFd\$fB D\$fB\X$D$DX$D$D\$$$\$$D$$D$x$pD$hD$`$g$Xf$fD$fD$f$fD$fD$f$f$f$fD$X$$_DX$D\$\$DX$DX$\$$P\$D$HX$pD$@DX$h$8D$0D$($ $'$$d$hD|$Xf$fD$f$fD$xfD$f$xf$PfD$HfD$Pf$HfD$@fD$8\$pD\$hX$`DX$XD\$`\$XX$0|$8DX$(Dl$0D\$0T$P\$(DL$HDX$ DD$(DX$l$ t$pDt$`D$D$@D$D$f$@fD$8\$ f$D\$fD$X$f-D\$f%f=fft$hfDt$XL$xfDD$xfDl$xXt$p$$DXt$`Y$D$Y$fEfDW$E\$1$D$Y$E\AY$$DqfD\$hfDT$XfD$PfDd$Hf\$Pfd$HfD|$8f|$0fDl$8fL$0fT$(fDL$ D\\$pD\T$`X$DX$\$\$DX|$@DY@X$DQHD\l$@A\$DaX$YPDX$aXDy y(Di`IhQ0DI8fDD$(fl$ D\$D;$4\$DApix}$hA fDwD);fS fDsfD{fDc*fDk0f{8D^ffDS(5zAYAYEYEYEYEY\AYD$XfD$f$D$D$EYfD$D$DX$f$\$DfD$D\$DE\D$f$XfEfEXD$fD$fDWXfW\$EXAXDXD\o DwA\ODgD(_0fD$D\D_8$!AI fD=vD)Ϳf3fk@fDcfD[HD*fK f[(E^fDK`fDShfDCfcfDkPfCXf{0fS8fDspAYAYEYEY$H f$H AY$8 AYX$8 EYD$@ EYD$0 EY$( AY$ EYD$ AYD$ AYD$ AY$ EYDY{xD$ $ $ $ D$ D$ $ fD$@ f$H fD$@ Af$( f$ fD$ fD$( f$ fD$ f$ DX$0 \$8 D\$0 X$ X$ D\$ D\$ D$ X$ $ DX$ D$ \$ $ Dv$ D$ $ D$x $ $p D)$ D$h $` fD$ f$ f$ fD$ f$ fD$ f$ fD$ f$ fD$ EX\$ X$ \$ D\$ D$H X$ DX$ \$ $X D\$ $P X$ $@ $G DX$x D$8 $? $D$$D$$D$fD$` fD$X DX$@ fDDX$8 fD$ f$ fD$p f$h f$p f$h f$` fD$X D\$ D$f$\$x D$X$DX$P X$H \$P \$H D$\$@ $D\$8 DYD$$$( $$D$D$0 f$\$f$\$fD$fD$fD$Y0f$YfD$fD$f$f$DX$X$$/ D\$$ f$D\$X$0 \$0 EMAMEU@ffW$ DXD\EEHAe\$A}PEmYLEuXf$X$fD$fD$f$fD$f$fD$DX$( \$D\$( DXX$ AE f$D\$ E}(E]8Au`EehAm0EMp\AEx{AA EEAAMAE1A)AAAE9L w$ffffDf$f$fD$f$fD$HBHB4HfD$fD$f$ B4D B4 IcAЉ։D HcD HHD HY,HY\J )YtHcDY$HcHHDY<E9DY4DY\DYdEXXD$hf$h\D$`fD$`f$`D$XfD$XA\$Pf$PAX$@DDXD\A\$h$HDDDX$8AXDXD)DXD\IfD$Pf$X\$8D\DqDYDy0E\DQ8Q DA(mA AAAAMAE1A)AAAE908oL u$)\$DHBHB<H B<D B< IcAЉ։HcD D HHD flHf4fDfDdHcHHcHfDHfD<$fD$f<fdf$A\J )$AXf$EXDfD$A\EX$$Xf$D$fD$fD$D\$DA\AXDXE\D$$$$E9DIDq f$fD$fD$DX$DX$\$D\$DY\DQI0Da8A(A D~ELcAMiVUUUAI E)D) E1AADŽ$ AMDŽ$ )$ Ӥ$ DŽ$ DŽ$ Ӥ$ Ӥ$ ADŽ$ DŽ$ Ӥ$ Ӥ$ DŽ$ Ӥ$ E9D$ +D52lL5q$D)$fDIHIB7B40AG0A$ G1$ $ D$  A$ D D$ D D$ IcA A A A AHHcHHE fD fDDHcf flIcHcIcHHHHfftD$ f$fDtD$ fD$;f|;$ fD3fD3$ $ $ Mc$ D$ IcD$ $ ID$ $ fD$ fD$ f$ Hf$ f$ Hf$ f$ J )fD$ fD$ DX$ fF,DX$ fFT\$ f\$ fD|X$ D$ X$ D$ \$ $ D$x D\$ D$ D$p DX$ $h $` $X $P $H D$@ D$8 f$ fD$ f$ fD$ fD$ f$ fD$ fD$x fD$p f$x f$p f$h X$ D\$ \$ DX$ DX$ \$ D\$ $0 DX$X D$( DX$P $ \$X D$ \$P D$ X$H $ D$ D$D$$$$fD$ fD$( DX$ fDZDX$ f$` f$h fD$` fD$8 f$0 fD$8 f$0 f$( fD$ D$0fD$0X$@ D$(DX$(fD$(\$H D\$@ DX$ X$ D\$ $\$ EY\$ $D\$ D$D\$0D$$D$DY-#$$D$D$f$f$X$\$f-ٌf5ɌfD$fD$$f$fD$fD$f$f$f$YfD$X$YDX$EXD\$E\\$$X$$\$DYDX$!DQPDqDa@yHAQXDy fD$fD$f$f$f$f$f$DX$D\$D;$ \$X$X$\$\$DI(DA`ihI0Y8qpaxwAgf-xaD)fKӤ$fs*$fc(fCfD3fD{ ^f[0fDS8YYYY$Hf$HfD$HDY$8fD$8DYY$@f$@DYXD\f$@EE\$0\$0EXX$0EXA\$8AEEEDXDXD\A\XD_AXD/\DO D\(GOo0Eu8A AAAAMAE1A)AAAE9L h$fffDHBHB4H B4D B4 IcAЉ։HcD D HHD ffDdHcHHHf|fD,J )fDLfHcHD$f$fDflD$f$f$A\$f$EXfD$E9AXD$DA\D\$XD\$AXED$DX\$DXD\EXYEXD1A\DY a(A\DQDyy0Q8AAfD]D)f{Ӥ$fD*$fcHfDk fS(fD{`D^fDshfsfDcfkPfKXfD[0f[8fDKpAYEYAYEY$ f{@AYEYD$ EY$ AYD$x AY$p EYD$h AYD$` AY$X EYAYD$P EYDYSx$H $@ D$8 $0 D$( D$ fD$ f$ f$ fD$ f$x fD$p fD$` f$x fD$X f$P DXf$X fD$P \X$ D\$ X$h D$ EDX$` $ D\$p \$h DX$H $ X$@ D$ \$H $ D\$@ D$ D$ $ D$ $ $ D$ fD$0 f$8 f$8 f$ f$ fD$ f$ fD$ fD$ f$ EXfD$ X$( D\$0 \$( X$ D$ X$ D\$ \$ $ DX$ D$ DX$ $ \$ $XD\$ $HD$0$ D$@D$8$D$fD$ fD$ DX$ f$ DX$ fD$ \$ f$ D\$ fD$ f$ f$ X$ D$xf$xDX$ D$f$\$$hX$xfD$hD$pDX$pf=\$ \$ $`DY-D$PYY$($ $ $ $ D$ f$p\$hfD$XfD$Hf$XfD$Hf$@fD$8f$@f$8fD$0fD$ fD$0DX$`YDX$P\$`D\$PX$ $x DX$ \$ D?\$ DwDX$ w@DX$(DgHD\$ oD_OP_XDO DW(DG`f$fD$f$ fD$f$\$(DX$ \$ XD\ghG8Do0WpDxf[fDSfKfS0fDC fc(fD D5Y$D$f[8fD$$fD$$D\$fD$fD$f$AE\DX\$D\DXEfEAXAfXfEWAAfAWEXXA\A\DX?EXGD\w A\o(D_DGDO0g8ZfD3fDc@fsf[HfDS fk(fC`fD[hD$p fD$p D$` DX$` fDK$h fDCf{P$X fSXfc0D$P fDk8fD{p$H fKx$@ D$8 D$0 D$( $ $ $ D$ D$ $ D$ f$h fD$p f$h fD$P f$H f$8 fD$P fD$0 f$( f$0 X$X D\$` \$X DX$@ X$8 \$H D\$@ $ DX$ D$ X$ $ \$ D$ DV$ $ $ D$ $ D$ D)$ $ $ f$( fD$ fD$ f$ fD$ f$ fD$ f$ fD$ f$ \$ DX$ DX$ \$ D\$ X$ DX$ $ \$ D$ D\$ D$x X$ $p $w D$h $o $hD$X$8D$0$PfD$ fD$ DX$p fD%{DX$h f$ fD$ fD$ fD$ f$ f$ f$ f$ fD$ D$f$X$ D$X$D\$ D\$ DX$ X$x \$ $H\$x DY\$p D$(D\$h D$ D$p$`$X $@$xD$D$` f$x\$f$\$fD$Hf$HfD$hYFzf$XY-zf$hfD$XfD$Pf$PDX$p\$p$_ X$`$P f$xD\$`DX$` \$` DG@offW$ DX\D_HDO\$PDGYgygXfD$ fD$8fD$0fD$8f$0f$(fD$(DX$@f$ DX$X D\$@DX\$X X$P D\$P Do Dg8D(Dw`Ohw0DWpfDkfSfKfDs fD f{(fs0D$x$hfDk8$pf$xfD$pfD$hf$xEf$pE\$`\$`EX\EXD\$hEDXDXADAEXAXXE\A\D_DXE\oD\Dg g(DGDO0f+fD[f[HfC fDK(fDC`f{hfS$D$fD$fD[@f$$X$fc$fDkPfD{XD$fDs0fK8D$fspfDcx$EX$x$pD$hD$`D$X$P$HD$@D$8$0f$fD$f$fD$f$f$f$xfD$pfD$xA\fD$pf$Xf$PD\$X$DX$$(\$\$X$hDX$`D$ D\$h$D\$`D$X$H$X$@$$ D$ D$ D$ $ $ fD$@fD$Xf$8fD$0f$8fD$0f$(fD$ f$(f$ f$ fD$ D\$PD\$HX$DX$\$D\$X$D$ DX$D$ \$$\$D$X$ $DX$ D$$D$$$$D$f$ f$ fD$ X$ X$ fD$ D\$ fD$ D\$ fD$ D\$ D\$ $f$$fD$\$D$fD$DX$D$D\$f$X$fDrD$D$ $ YrAYEYEY$ D$ $ D$ f$fD$f$f$f$fD$fD$fD$f$fD$f$f$X$DX$\$\$X$ DX$ D\$ D\$ DGX$ @DX$WH\$ g\$DoDPDwXw Dg(o`Ohf$f$fD$fD$DX$ D\$ AXA\D_0_8DOp{EHHHSHWfffAWAVAUEATIUHDSHAH$H$w:AFAAfffNAAffff1H[]A\A]A^A_Ã$E9fD=FDD)DA *D^~AELcAMiVUUUAI E)D)ADDŽ$ ED$E1ADŽ$AMDŽ$)$Ӥ$Ӥ$DŽ$Ӥ$Ӥ$DŽ$DŽ$Ӥ$Ӥ$DŽ$Ӥ$E9D$D HL=N$D)$@ffffD$$IDHIC;F8AG9ߋ$B?D$Mc$IAAD AD$D D$ H$ꋌ$ A A A HcHfDLfE,HcfTfEIcHcfDdfEHcfEfDtfDfA EYEYf|IcEYIcLEYL$AYD$fDLAYD$xfE,EYD$pD$hEY$`AY$XEYD$PfA4fD$hf$hf$EYf$xf$xAYfEEYfDdAYEXA\fD$X\AXEY$Hf$A\E\fD$pEY$@f$`$0$7Xf$p$8DXD$fD$HAX\f$HEX$ f$XAXA\A\fD$P$(f$`$/AXfD$PE\$$A\DA\EXD$f$EXf$ fD$ $$f$@$D$fD$8fD$@fD$8fD$Xf$DX$X$0A\$DX$(AXD\$0$0D\$(\$$\$$D$8$D$D$AXD$E\$ $D$$(f$0fD$8f$ D\$0fD%ujfDljfD$ AXD\$(fDRjfBjf$ffD$f$fW$@DYfD$EYf$f$\$(EYր$X$D$DX$D$\$DD\$D$DX$X$Y E{ $EI DXcD\AAfD$fD$fD$f$f$f$fD$f$D\$D;$D\$DX$\$X$X$D\$Dc(\$Ei(D[EqDS0AY0SAIDC8Ay8>$H$DH$[E9#D$fD=>DD)DAA A*D^D$~AEHcȉHiVUUUH ))|DE1DŽ$ DŽ$DŽ$AMDŽ$)$Ӥ$Ӥ$Ӥ$$DŽ$Ӥ$Ӥ$DŽ$DŽ$Ӥ$Ӥ$A9މ$@L=fF$)$D$f$f$fD$HHHB>F 8F?Aڋ$G:$$D$Af$f$D AD$D D$f$D fD$H$ꋌ$Mc A A A HHcYtIAAYHcLDY\L$IcAY,HcMcY\HcAY$IcDYT$$D$$$x$pD$hf$fD$fD$fD$f$fD$f$fD$fD$f$f$fD$AY<X$DY|X$EY D\$DYlAY $`$FYdD$X$CYD$PDYDD$HEY4$@D$8$0D$(D$ D$f$f$xf$pfD$`f$xfD$XfD$PfD$Xf$PfD$8f$0fD$ \$X$hX$`D\$p\$hDX$HDX$@$D\$H$\$@$DX$(D$X$ $D\$0D$D$D$$D$$D$fD$8f$f$fD$f$f$f$fD$f$fD$fD$fD$D\$(X$X$D\$\$X$X$D$D\$$\$$pDX$D$XDX$$HD\$$h$`D$@$8D$D$xD$Pf$f$X$fD$X$f$D\$fDia\$f5Xaf%PafD$f$fD$p$fD$D\$X$D$f$DX$x$\$D$$D\X$YAYDY$$D$fD$DX$DY{`Ef$@f$fD$pfD$hfD$`fD$hfD$`f$Xf$HfD$Xf$Hf$8\$AXD\$xDX$DX$D\$D\$X${ X$PEx D\$DK\$PEhX$Dc(E@(KA@Ds0Ap0SAhf$@fD$8D\$D;$A\c8EP8fDo5H$H$H$DD$LD$LEQ5LD$fufafT$$f$Xu$f$X!H$X$8Xf$8$0f$0\\YY$(f$($ EYY$ N$ PDA AAAAAME1D$A)AAE9WL<$DIHBIB<AC4 F DD L$IcAЉ։D HcD f\fE4HfDfA< HcHcHfDTfA$IAYfLfEEYH$AYAY$f$fD$D$fD$fD$$X$EY$D\$AYDX$AYD\$EYD$$$$D$D$D$D$xf$f$fD$f$\$fD$\$f$DX$f$X$fD$f$f$x$`$g$X$_X$`D$pX$X$hDX$pX$h\$pD\$hE0AxDQA`IfD$f$xD\$`D;$\$XE@qcA D~AEHcALiVUUUAI E)D)ADE1EDŽ$@ DŽ$<ADŽ$8AMDŽ$4)$@Ӥ$<Ӥ$8Ӥ$4D$0DŽ$,Ӥ$0Ӥ$,DŽ$(DŽ$$Ӥ$(Ӥ$$E9D$D|%R3L=9$)$ffD$@$4IDHIC:F 8AC<;A؋$<C8D$(D$,D$$AD Mc $0I H$Aꋌ$8 A A A HcHfAfD$DX$D\$f 2>f">f$f$fD$fD$f$A\DX$(f$Y-=fD$AYfD$DX$AYDX$$ \$\$$ EY$ DXE0D$ DX$ \D\$ s AP [EXc(EP(f$fD$fD$fD$f$fD$f$fD$X$ D;$D DX$D\$ D\$X$ DX$ \$ {D\$ ExDK0Eh0KE`C8E@8AfD-E)fA $DӤ$fEd$*$fE\$fmfufDuD^fEL$feL$L$AYEYEYAYAY$hf$hEYD$XfD$hEYEXAYED\$XD\D$`f$`D$Hf$HfD$`$PDXX$PAXD\$PAX\$HXD\D$@AXE\AEA$EEA|$A\AMfD$@A\$@EXEt$AuAl$ DA AAAME1ɉ$A)AAA9L$fffDIHBIB<AC4 F DD L$IcAЉ։D HcD H fDlflfA1HĈ$[]A\A]A^A_BH$pH$ptH$pH$xLDHH$xBffUfeAH?ݜ$hHD$hXXH$hY$h\Y$hA&A^+E9fD DDD)A D*E^D$ ~AEHcHiVUUUH ))؃ ADE1EDŽ$ DŽ$ADŽ$AMDŽ$ )$Ӥ$Ӥ$Ӥ$ D$DŽ$Ӥ$Ӥ$DŽ$DŽ$Ӥ$Ӥ$E9D$^%L=Y#)$D㋴$ fD$ fD$ f$ IHIB9F 8AC<8A$G;$McD$f$ AfD$ f$ D D$ $f$ D D$f$ ꋌ$fD$  A A A HcHHHIDY|f$ K3DY\HcIcY|HHYTHcHcYdHHDYLIcfD$ Y\HfD$ YLL$xADYtIcD\YD HXDYT AD$fD$ DYl$f$ DYL5Y\5$f$ YlfD$ ADYdfD$D\DX$X$f$f$D$AD$$f$$\$D$Df$fD$D$EX\$X$\$AX$D$DX$EX$$\f$$AE\fD$DX$D\AXD\$D$EXA\D$$f$fD$f$D\$D$Df$\fD$X\$$X$f$DXf D\$$$AfD$D$fDAXE\fD$$A\D|$xAXD\X$EEX$p$wY=eDXD$xDY$xA\Y?D$hY$hD$x$hD+f$fD$f$fD$fD$fD$f$XfD$f$E\fD$fD$\fD$f$DX$xDXAfT$x\$xDX$pD\DS D\$pAc DX$h\$hD{EKK(E[(DCDX\XEsDk0AC0DcASs8fDT$xD;$D\ES8H$xDL H$pH$pA7fDE)fD}DfDU fDmfDufDe0*fm8D^EYEYEYEYEYEAYAEXA\EXA\EEXE\X\EEFA~AvAf% E)fDm@DfuhfDEfDe(fDM`D*fEfUPA^f}Xfm0f]8fMpDYYDYDYD$fDm DY$fufD$D$fD$fD$D$YDX$YD\$D$YD\$YY$Y$YD$DYYex$DX$$D$$$$D$f$fD$\$fD$fD$D\$fD$DX$f$DXDX$f$\$f$$p\$fD$pX$fD$D$f$fD$D$xfD$D$D$`f$`X$DX\$p\$xXDX$$D\AX$`E\fD$ED\$D\DXDY=$gDY f$ED\$`DX$`EX\$EXEf0E\EvEXAE\E^Af EV(EFEn8:DA AAAAAME1D$dA)AAE99L<#DfD$ fD$ f$ f$ IHBIB<AC4fD$ fD$ fD$ f$  F DIcAHD N7H$xЉщD D HcHH HYTDYDDYdHcHDY\HcYdHDYt$XfD$Xf$XDY|D$Pf$PYl$@f$@EXA\AXD\$PD$8AX$0D$($ $f$8fD$0D\$8fD$@X$0fD$f$ f$ f$(fD$(D;$dE\DXA\$$X$D\$AXE(AXE\AXA\DOAPgEXoA D~AEHcALiVUUUAI E)D)DE1ADŽ$X DŽ$TADŽ$PAMDŽ$L)$XӤ$TӤ$PӤ$L$HDŽ$DӤ$HӤ$DDŽ$@DŽ$<Ӥ$@Ӥ$<E9D$\D-L=#D)l$`fDዴ$LIHIB?F8AG9$XF;$T$HD$@AD AD$<D D$DD McAꋌ$PI A A A HHcHHK3fDTfDtHcfDlf\HcIcHHHfDLfdfD|fL HcfDd IcHHIcL$xHD$0D$(D$ $D$$fDLfd5D$$fD|5D$fT=fl=ftfD$f$fD$f$0fD$($$f$0fD$(fD$$f$f$EXX$ fDDAXDX$DX\$ D\$D$DX$D$$\$D$X$$D\$D$D\$$\$D$D$$$fD$f$f$f$f$fD$fD$f$fD$fD$EXf$fD$A\X$\$\$Dt$8DX$\$DX$\$D\$$xDX$$p\$$hDX$DD$PD\$@D$(DT$Dl$0L$Dd$Xf$f$pX$xfD+ f+ f$f$fDT$PfDt$@fDl$PAXD\$pfDL$@\$l$Hf$h\$xDXT$X|$pDXt$HD\l$XD\L$Ht$ $X$_DX\$hE3Dk \Xl$pDY-X EXDYD\DY=@ AYD$`$PEK fL$0ft$0fd$fDt$f\$8fDd$8f|$(fT$fD\$(XfD$fDD$\fDT$X$`AXD\$`E\D;$\X$XXT$ D\$X\D$ DX$P[AKD\$PDc(As({ASD[0AC0DCAcDS8Es8bDA AAAME1ɉ$LA)AAA9 L##ffDIHBIB<AC4 F DIcAHD N7H$xЉщD HcD HH HfD|flfDdftHcHcHHfDf|fDT fDt ED;$LEXE\D$@f$@fD$@\XAXEXD\D$0A\DXEX\D\EXDXE(E\\EHgE@D_Exqfef} fu0fM8Xu\M\XXX\\A^A.AVAff}fmXfM0fD]8fefDMfDEpfDuxfDe@fuP$`$X$PD$Hf$`f$`Df$Pf$HD$@D$8X$XA\$XA\fDU`X$@EXfD}hX$8DXU D\}($(DD$0$$ $XD\E\$$f$8\$HfD$PD\$@f$ D$fD$fD$0AXDX$fD$f$0D\$$f$D$X$fD$ E\D\$AXD$f$D$$AXD\$D$f$X\$X$A\Y=}EXAv DYqX\A^An(ANfD$fD$f$\$EXE\Af0E~EV8EHUIffffffffAWDʸAVAUIATIUSHXAD$Lw,A8AA~A A 1HX[]A\A]A^A_$LD;$L$Lf%fA $AA D)勌$LD*A^AA)DyIcD9YA $fDDYDfA<YA$F\=1AAE A E A$A;$ffffHIDIHC;AB<8G8B49$A D D$ Lc $IK<#K+ $LLLc $IK!K )LL LLHcHcHLHLN/N N(J';$II I;LHI8MI$D$D9$v$A D)$Lf$LfDfAefAM fW0ffDW fEmfA]fDg(*^fE}(fEMDYYYYYD$0fD$0DY$(f$($ fAM0\$ $fWDY$f$0YfgfDG8DYE\Yf$(YX$ AXE$DYDYD$DYDXYAYE8$fD$A\fD$fD$lA\AXfD$E\EXfD$DX\$E\D$ED$DDXfA\D$fD$EXDπ$A\fD$EXfD$fWA\ffE\$Hf$DXfWD\f$D$pfWEXDD$XfD$DX$DD$hDXfD$D$@\D$$P\X$8AA\EXfD$AXD\$$`E\Df$EXfD5.$fWfD$f$pf$E\f$hfD$hAYA\DYEXf$pEYAXf$`fD$`A\AYXfD$HDD\f$@A]f$@fD$Hg f$8A\DX$DDDX$XAXD\$XE\D\$AE wAmDG(E}(AXDoEuXDg0A\EM0_AeO8f$8\AE8!AI fDD)$L$LfӤ$fDN*$fDffDD^fwfDofEYAYAYEYEYEYAYDYF$@D$8AXD\$@DXD$0\$8fD$0D$(DXA\$(EXAD\EEDXEXA\E\XD?AXD6D\wA\DNo^DWAUff&fWfDWfDFfDnffNDD5DEXAAXDXXEA\\DD$DX$A\D\$DXff\fAWA\D'DfAWD_ngA fD%D)$L$LfF Ӥ$fDD*$fDfDo E^fW0fD~0f_fDNf(ff(fDFAYEYEYEY$PAYf$PEYAfGAYE\EYAXAYAX$@AYD$8fVEYfD$ f_8AYD$H$0$AY$(EYD$AYDYf8$D\$PfD$@D$f$Hf$0X$8fD$ f$fD$8DXf$fD$fD$f$AXD$fD$ D\$HDXD$$X$DX$AXD\$$f$(\$D$\$@D\$D$$D\$0$ D$D$D\X\$(D$$fD$D\$fD$DX$f$f%fD$fD$fD$DX$f$Xf$ D$fD$D\D$Ef$E\f$EX$fD$DX\$D$DX\$fD$A\D\$D$0E\DX$0DAXEXDY%DYD\f$ A\fDYfW=AXDYAXEXo F wDFf$f$f$f$fD$f$fD$f$A\fD$A\fD$XAXD\O(A\f(EXWEX^E\DW0A\n0DoDvDO8F8ffD fgfDV0fWffo0fD^fw(fDw8DffA\fDnfDNEXfDfXF X\^ EXDXn(X$D\N(D$DXf8$D\VD$x\g0$$pD$\W(D$hD$`DXfEfD$XD$PfF8fD$\FDDX$xfD$fD$fEWfAWfD$f$f$fD$pDX$AX$Hf$h\$DD$PfD$h\$xDX$Pf$pD\$Xl$H$O\$P\8D|$`X$X\$0t$(DD$XD$@DXE\D\fDl$@Dt$ DL$D$8fD$HfD$`fAWfD$`f\$`f%fl$HDX\$HfDl$@DXfDL$H$GD\f|$Pft$P\t$XDT$xEXDfEXD\d$xE\f\$0fAWfD\$`\\$8DYLDYAXfDD$(A\fD|$@DX$@DYD\>Yfd$0AXXd$8E\EXD_ v E\oDnDO(D~(gDF_0f|$ fL$fD\$ fD$(\$@fl$AXXE\F0ND_8\Am8ffNf/fDWfDfDFffvDDDXA\fNEXA\EXEAXA\ED\DDXDXD\D\DXD7AXD.\DgA\DND_fo0fWfD(ffDo fDWfF0fDw0fDfDXf.fDfgfDFf^8Xn A\D\N fD^AXXg8EXD$xDXF8fD~(\^$D\W0$\W($D$D$D$pD$hDX\FEXD\^(D$$`f\8fD$\f$f$f$X$DXX$fD$xEXDX$pfD$$fD$D\$$Xf$Xf$hD\$D$$D$f-A\fD$`$f$D$AXD\$XD$AXX$DX\$`D$D\$h$$f$xE\$pD\AXEfEDXfDW=kA\DY7fD$fD$DYf$DX$D\$fD$\$YfD$Yf$f$fD$f$f$D.EXfD$D_ A\fD$N Xf$DXfD$\DwEXA\DND\G(EX^(XDGnE\f$w0Dn0D_NDg84fffffffAWIDʸAVEAUATUSHXAw-A9AA~AA 1HX[]A\A]A^A_AE9f%DD)ɉfA fESDD*AA^A )DaIcHAE9YDYA fA< ESfAT YYA< AT *L#H$0H$@fffDLMIHB<AF<DCLAD A1G, ωD DD95HHF<HBF,AA ׉AB4E A AD AE IcE HN,HcD HJ fEMfEuIcLcf1fDiHID9DYKDYYDYAuD fEmN,DqfJfAUfAEYYYYA]AMBT$Ht$\$HT$$8$HD D HD AHHcfE<fA\HE9DYYE<fA,A\fE\YDYA,E\H$@H$0$DL? E9 f%DAD)EfAfDGAA*ED^AA E)D9YDYDG}H=+#H$0H$@DIHA4HffffffffffVHHtAA111$U]HÉHHtAA111$&]HÐJHHtHtAA1H1$HfffffffEHHAHt HtHtHt$fffHffffEHAHtHtHtHt $HfffffffffJHHtHtAA1H1$ϴHfffffffHHtHtAA1H1$萴HffffffffHHtHtAA1H1$eHÐAWIAVIAUATUHDƃSHDD$AOt$Eɉt$%D$HcHcHHEH|$Ht$D$T$Dd$UMu}DEDM DU D]H1)‰f1)fK1)Ɖfs1)Df{A1A)DfDCA1A)DfDK A1A)DfDS A1A)fD[HATD$9D$}!+L$fEH1f)fHuL|$Lt$ALL[]A\A]A^A_1ÐAWIAVIAUATUHDƃSHDD$AOt$Eɉt$D$HcHcHHEH|$Ht$D$L$Dd$UMu} DEDMDUD]H 1)‰1)K1)Ɖs1)D{ A1A)DDCA1A)DDKA1A)DDSA1A)D[H AdD$9D$}+L$EH1)ЉHuL|$Lt$ALL[]A\A]A^A_1ÐAWAVAUATUSOTpHG HWL_(LW0LO8LG@L$ċoLwPHD$H_hO\HT$L\$LT$LL$LD$W`GHl$̉t$H\$L$9H=#H|$Dd$̉D$D9LcLl$ffD$T$9T$HcT$HL$HL$H|$H\$Ht$L|$Lt$ E,E$tLT$LD$E AlDL$ȉl$9EL|$EAAHALL$ALD$ILcAAAyAqAAhEPEXEAD$AD|$AH|$Љt$IcLD$HT$H4KLLct$I9~DNFHL$vKDDA7LL$HHcI4IL;|$~DNFEDADl$Dd$EADDAI 6A DAq9QLAEAqQAyA AA1LQq AADDDYADցHT$|$ t$DD$DL$jDRDZDHt$EA9DFDNNDD$D$DL$L$xDA?DLL$HHcI4IL;|$~DNFfffffDDELA~EDANDEDFED.LAVA ADNDEDDDnLADv EEDVAVDDAADEA ADEEA |$Dl$Dt$Dd$EAC,t'AD$T$9T$[]A\A]A^A_1A?L;#L\$AWAVAUATUSH _TpHW HGL_(LW0LO8LG@\$܋oLwPHT$HOh_\HD$L\$LT$LL$L$W`GHl$t$HL$Љ\$XH=!#H|$Dd$D$D9$LcLl$D$؋T$9T$HcT$Hl$L<$HL$HL$H|$H\$LD$tE4A t$Dt$tH|$LT$DELD\$DL$9Lt$Lc|$HHD$D$HD$ȍ HNl0Ll$Dd$Dl$Hcl$Lt$DEDAHt$ALL$Hl$IDFAAiEQEYEAV~DD$LD$L$HcCD Ht$HIL;t$T$|$H qDIAvZADEHt$ALL$IHcCDHHL;t$I qDIANDE@ELDl$EqDd$EDQDA9EDALAA EAqQAyA AA1LQq AADDDYDD$DL$AځHT$D|$ t$EEAjDRALD$DZA0A9A@EHt$ApDL$D$t$fA>hDDEI4EENDEDFDD.A~LEVA ADDDFADDnLA DEAVDDFAA ADEDVDADEEA |$Dl$DD$Dd$EAC,t:AD$|$D$؋T$9T$H 1[]A\A]A^A_A>L%Y#Ld$fffffffffffAWAVAUATUSH _TpHW HGL_(LW0LO8LG@\$܋oLwPHT$HOh_\HD$L\$LT$LL$L$W`GHl$t$HL$Љ\$XH=1#H|$Dd$D$D9$LcLl$D$؋T$9T$HcD$Hl$L<$HL$HL$H|$H\$LD$tE4A t$Dt$tH|$LT$DELD\$DL$9RLt$Lc|$D$HHD$HD$ȍIHNl0Ll$Dd$Dl$Hcl$Lt$DEDAHt$ALL$Hl$IDFAAiEQEYEAV~DD$LD$L$HcCDIHt$HIL;t$T$|$H qDIA vYADEHt$ALL$IHcCD@HHL;t$I qDIA NDE@ELDl$Eq Dd$EDQDA9EDALAA EAq QAyA AA1LQ q AADDDYDD$DL$AځHT$D|$ t$EEAjDRALD$DZA0A9A@EHt$ApDL$D$t$fA>hDDEI4EENDEDF DD.A~LEV A ADDDFADDnLA DEAV DDFAA ADEDVDADEEA |$Dl$DD$Dd$EAC,t:AD$|$D$؋T$9T$H 1[]A\A]A^A_A>L%i#Ld$fffffffffffAWAVAUATUSH _TpHW HGL_(LW0LO8LG@\$܋oLwPHT$HOh_\HD$L\$LT$LL$L$W`GHl$t$HL$Љ\$kH=A#H|$Dd$D$D97LcLl$D$؋T$9T$HcT$Hl$L<$HL$HL$H|$H\$LD$tE4A t$Dt$tH|$LT$DELD\$DL$9Lt$Lc|$HHD$D$HD$ȍHNl0Ll$fffffDd$Dl$Hcl$Lt$DEDAHt$ALL$Hl$IDFAAiEQEYEAV~DD$LD$L$HcBHt$HIL;t$T$|$H qDIA v^fADEHt$ALL$IHcBHHL;t$I qDIA NDE@ELDl$Eq Dd$EDQDA9EDALAA EAq QAyA AA1LQ q AADDDYDD$DL$AځHT$D|$ t$EEAjDRALD$DZA0A9A@EHt$ApDL$D$t$cA>eDDEI4EENDEDF DD.A~LEV A ADDDFADDnLA DEAV DDFAA ADEDVDADEEA |$Dl$DD$Dd$EAC,t:AD$|$D$؋T$9T$H 1[]A\A]A^A_A>L%f#Ld$AWAVAUATUSH_TpHW HGL_(LW0LO8LG@\$ԋoLwPHT$HOh_\HD$L\$LT$L $LD$W`GHl$܉t$HL$ȉ\$iH=#H|$Dd$܉D$D9LcLl$D$ЋT$9T$HcT$H,$L|$HL$HL$H|$H\$LD$tE4A t$Dt$tH|$LT$DELD\$DL$9D\$Lt$HT$LL$Hcɋ\$AM,FAL\$HT$Ll$HcACA3A{ESD2DzDbDjD$D$t$Hct$|$DT$HHt$MHD$IhHLHDRDBzH*DZDJrHL$H9L$vvLD$fA|$L\$D$LL$HD$HL$LcH9L$K4HH,FI)]DUDE}DZ*DJraAދL$L$E׋D$D$A LT$AEAA+I,ED]A}AMAAEEAAA]LAuUDD]AAEDeA׋|$ˋl$AEAAEADL$\$DD$D\$Hl$H|$EDUDMDMD7A@DEDDgDoDT$DL$DD$L$LLD$ADOfE9AEAAHt$EAA LEAZARAAEAA AA<+EZIAjAAAAAEbEEE*DADEEr\$EEDl$DD$DL$EA@~.L|$fAD$ЋT$9T$H1[]A\A]A^A_H\$Of+L%(#Ld$ffffffffffAWAVAUATUSH0_TpHW HGL_(LW0LO8LG@\$oLwPHT$HOh_\H$L\$(LT$ LL$LD$W`GHl$t$HL$\$H=#H|$Dd$D$D9lLcLl$D$T$9T$PHcT$Hl$L|$HL$HL$H|$H\$(LD$ tE4A t$Dt$tH|$LT$DELD\$DL$9L|$HLcD$I4HcD$OlHt$Ll$HD$D|$l$Lct$Ll$L$EAl$AADALL$ALT$OduLt$HT$Ld$AAEAYE*ErAjEbLcAqD$D$K DD$LL$\$t$HLBHD$H9D$JAID{DCK DRDJr vLD$fAD$EL$AHT$LL$AHD$McK4HHfD.r-fE,fWVD,AfD_D,AfDLcKDY%̷I|HHHH4HH HPHGfvfD^f9fDifDQfD6HHfD~HH<t$(|$8Dl$0,f"f?DT$ fjfDOfrfDWD\$fDifDZfAp,,fAHfAPDl$fDo,fA8IA fD$8\fL$0D\D+t$dH$D\Ht$HH|$@E\A\AYA\EYAVEYAYXAYAXAXAXfDt$ D\t$(AX\\EYDXt$(AYAYXfd$\d$A\XD,fEAYD,fEXAYEYXd$DXEXE,fEhD\EYAXD,fEH#,AD;|$`hL|$L9|$@t H|$@1HĨ[]A\A]A^A_HcHHHD$@fffffffAWAVAUATUSHWLH_hH$Lg(HoL_0LW8H$HL$HHL$HcNT$pH\$`WXH^@LO@L$LG G\Hl$xHL$L$H)ˁL$L$Dw`oHDoPDgTD$\,t$pl$l9IcH|$fffHD$H$HcD$lH$H|$`H$H$L$D|4A D|$tDX)X!DY%I<HHHH4HH HPHGfvfD6fDfDifDQfD~HHHH<t$0DD$@Dl$8f"fDfjfDOfrDT$(fD^fDifDW,,D\$ fDZ,Dl$fDoA0AHAP,AxIA9fD$@D\fL$8D\D\E\A\DYA\DYDYYAXYAXAXAXfDt$(D\t$0AX\\DYDXt$0AYAYXfd$\d$ A\XXX AYYAYXd$ D,DXED,DX5WEXDXD\E,EpEYAXX%.,AhD+|$tH$Ht$PH|$HAWnD$lD|$pD9|$lLl$L9l$Ht H|$H1Hĸ[]A\A]A^A_HcHHD$HH|$HfffffffffAWAVAUATUSH Lg(HoWLH_hH$L_0LW8Ht$xLO@LG Ld$pG\Hl$HDw`DoHT$@DgPWXoTH\$8Hc~HL$ HL$H^@L\$hLT$`LL$XLD$PD$4H HH)ˁD;l$@EIcHt$fffHT$pHD$HD$PIcH|$8HL$hH|$`D4Ll$X4Dt$DD4ALtLD$8E$AlLcL$DLL$PLL$(ED+L$DEQELl$HD E*ʃE4HcHDJ.Lcl$4A*D\ ELD$ DY Y5ܭHI|H RH4HfDfDnfDvHIH HPfIfD!fDyHRHHGL$ffbfDBH@HDY HI|H RH4HfDfDnfDv,HIH HPfDQfD!fDy,HRHHGfA0fAHDT$ffbfDB,fAPH@IAHXDY ΧHI<H RH4HfDfDnfDv,HIH HPfDQfD!fDy,HRHHGDT$ffbfDB,H@HLsDDHL9s DZDHHL9rtL9s ULSHU!HqI9wffDODoDH DDAAA fEfASfEkfEKII9vaLI7M9Rw DoDWfAH DAAA fESfASfEkIM9v LiDM9ODDO DWH fAKA fESfA3DfAsIM9vLIM9O7DoH AAA fEfAKfAsfEkIM9v[ffAWEDIAVAUATUSHD|$8IH~|LcLcLcAM4DكA@ǃ tXIPI9wA;A:AKIAJII9vM9s AAffffLLAIIu[]A\A]A^A_fffLHcLHI)t5~ltIL9JDsDDL9s +LV@.L[DI9sАA If HH9rI9sffEIfDHH9r뙅uI9ffsESIfDHH9rrffESI~EHcLcHJ4@u*E1E9}fIcAHE9A|HIAu[E1HOE9}fffIcAHcDE9A|fffffffAUEDIIATUSHHHcHcL$L,H,MD@I4t LW L^M9LwIBIIH AACII9vI9sI A LHIAIIu[]A\A]ffffffffAWEDIAVAUATUHHISD|$8HHcLcL,L$N4DMLAMD=At#M9sAAIM9s AJA II M9Lw/IIRIJIAH H ACASAK II9vM9s,AIsL9AsArIKL9Ass EB ECfffLLMIII[]A\A]A^A_L9s DUL_DLU4fffESI~RHcLcHJ4@u:E1E9}"fIcAHHE9HA|ffffHIAu[ffE1HOE9}fffIcAHHE9HcTA|ffAVEDIAUATUSHDt$0IHHHcHcL$L,H,I|fffAM4AtQII9HwIcCISIARAII9vM9sIcCALHHAIIu[]A\A]A^I9LIwH HA H AJIM9vM9sH밋L[ LVhfffffffAVEDIIAUATUHSHDt$0HHcHcH@L,LL$I|fEI4~DfffI9sA IA IuICurI9IHw)ffffIISIAH ARABI I9vI9sIMZI9As H ARLLLAIIh[]A\A]A^I9LXHw'ffICIcSIABH AABI I9vI9sIcKIRH9A sIsArfffffffffAWMcAVAUATUDSH1Dt$8DT$HD$D$D$D$D$x&BD LcBDxDtyAFDE9}IcBLA+LE9BL|HcT$IHAItAttA[]A\A]A^A_DL$@E~Lct$@ANMcHcE1A9}McAMA9CTA|McLHCIIu[]A\A]A^A_Dl$D|$DD$@EE{Lcl$@HcE~'IcMcEfABMAASIAuHLIIu[]A\A]A^A_D|$Dd$@D|$D|$D|$D|$ED|$Dd$@LcLcE~8Hcl$Hct$ELcL$ABTB MAASAKIAuLLAIIufffffffffffAWMcAVAUATADAUH1SDl$8DT$HD$D$D$D$D$x'BD fHcDxDtyAFDE9}ffffIcBLA+LE9BL|HcT$AIHWItAtpA[]A\A]A^A_Ë|$@~LcIcMcH<6t$@O$ E1E9}K E:McAIE9fGE"MD9|HHAHIu[]A\A]A^A_Dt$DL$DD$@EEDL$~l$@McIcE~&LcL$McEDHAG<MAuLHHIu[]A\A]A^A_ËL$D\$@L$L$L$L$EۉL$ IcDd$@McHD$E~^ G,H$D_LWDNL$$H$DFÃ~HVD$t$L$L|$HD$L$H$D$D$ بD+$D)Ήt$8AA|$< Ȩf HI~fLYȃDl$8ADd$DL$$AE1EE)D9}CT%B<:D)HcDEuA;t$4|E1D;L$0}EDd$$AߍuEE)ffD9}DB<:D)HcDEuAD;L$0|19}0E1fffff~C| fffLcGuE9|E1E9t$$΅~!AEC|%ffffLcGuAE9|AHwDGAD$ ysfffff҉T$ A]T$ T$td$ E1D;L$4H|$(McF}=Dl$$A1EE)D9}A H\$(Hl$0Ld$8Ll$@Lt$HL|$PHXËT$`Ht$EH|$ A؉Dt$Dd$$L`n ffffffffffH臨 HfH\$؉Hl$Ld$Ll$Lt$IH(IIԹھDu0ALLL H$Hl$Ld$Ll$Lt$ H(ALLL ffffH HfH\$؉Hl$Ld$Ll$Lt$IH(IIԹھD_u0ALLLX H$Hl$Ld$Ll$Lt$ H(ALLLh ffffHwHÐf 0AAAAA*^tKAA1 Ȩt,*YY25Y*5f.JC6ffff*YY4f.E0ѐAUATUSHN_ oDNwLGWIMMcLcAff1yefffAIQQQQQڃYQH9|t31AyljӁHAAD)9|MMAML@[]A\A]AIQQQQQڃYQHV9|CAЁADHDFAD)9|MfAUATUSDVLFo LOO_DMBMOHLcL$fffff1yyFfffA)IffAfAPfAPfAPfAPfAP fAhfAP I9|t;1Ay!fffƉՁfA(IAɉAD)9|MMMM%[]A\A]A)IffAfAPfAPfAPfAPfAP fAhfAP IQ9|(AсAfEIDIAD)9|8AUATUSFLFo LOO_pMMNOHLcL$fff1yp1fffA)IAAPAPAP APAPAhAPI 9|t81AyffƉՁA(IAɉAD)9|MMMM1[]A\A]A)IAAPAPAP APAPAhAPI Q9|4fffAсAEIDIAD)9|=fL =E1TfMcAE:DDKEAAHKEAAAHVKHVOLVH QOA9|ffffffffAUEAIIATHH 4=US1D9}zMcHcE1HIcA8HIHIBЃHIBHIBI APAAOA9|MHD9LI|[]A\A]fffffffATHHcHHL <USHDH)HHHZHH9EWAA*LaHL)HH#HHHH~uAD]A)ffDDDHDA ‰I I $I IL$I IL$I IL$I uI9wjL)DULL[DMIE9~D)AD E@~)DfffWA%AA*Hu[]A\H9tDDK4H1DWLbDA*ZffffffffffHl$Ld$L|$H\$Ll$Lt$H8HEEIH~0HcLcL,DfffffDMLLHuH\$Hl$Ld$Ll$ Lt$(L|$0H8fffAWAVAUATUSHO DOV_HoLfDGAمHcIcAL<HD$E1A9Lt$ABAAIcAOD)MLDLH1H4(EHafffHƉAAA AAЃQAH APAAO9|1D9}fffffHcD9*LA |EA9Hl$MAH[]A\A]A^A_ffffffffAWAVAUATUSGGO WuFuL_HnڅɋvMI*HcAHD$HcHD$E1AASAAKAsA{ECEKESI%@ Ё ȁA A AAD AD AD EHōSOA9^كtM19}fA+I! uDKAADOAIcF")A F)L|$Ll$AML[]A\A]A^A_ffAUATUSGGO D_uFuVLGLNDWLLAHcMcH,6HL$fffffAO tfAIfAIALudMSM9w4fffAIA%AAA D HL9vffffL9HsfAf LHIIu[]A\A]MSM9wAPEIA D HL9vfffffffffffUSGDOW ouFu DʺFLGHNMI~>DHHcH<Aх~A(I)HuIIAMLu[]ffAWAVAUATUSDG WO_HnL_vEMI-HcHcEHHD$HT$E1AASAAKAsA{EC EK ESI%@ Ё ȁA A AAD AD AD EHōKOA9^كtM19}A+I! uDSAADOAMcG" (A G (L|$Ll$AML[]A\A]A^A_fUSGDWW _uFu DҺGLFLONML~KDHHcHAff~!AI AIuHIAIMu[]fffffATUSGDWO D_uFu DѹGVLOLFLL~VDHHcAL$H,Aʅ~*AADI) f%fAIuLHAIIu[]A\fffAUATUSGGDW WuFu AA‹NWHvHEIIHHcHcL$Ll-H,DIL4@M4t LOLFI9Iw0AAA@AAIAPA@ IM9vM9LsD HEIM9rLHHIIu[]A\A]ffffffffAWAVAUATUSHGWDoO T$uFu DWFLgHnHcHAH\$HD$D$1A9?Lt$Hc)ؿLT=ILLLփJ 6HcLtDHDHMKIL9s+AAA@IQA HL9rL9sEAADIAMLtWH*L$A IMKILM9wXII0WWI*\$T$L!։H HH *T$D$H H H1HL9vL9s WA* !D$A9H|$HT$AM$|HlH[]A\A]A^A_AWAVAUATUSHGDgO DwuFu DGVH_HnEHHcEHHHD$H|$ADD$D$19L|$Hc)A=HsL\DLMIcI4tD IHDL$LNHM9s0ffAA@AHAPA@ IM9rI9s1AAp1D9}LcD9B*LC |DDA9CH\$Hl$AH[]A\A]A^A_fffffffAWAVAUATUSDG WO_HnL_vEMI-HcHcEHHD$HT$E1AASAAKAsA{EC EK ESI%@ Ё ȁA A AAD AD AD EHōKOA9^كtM19}A+I! uDSAADOAMcG" (A G (L|$Ll$AML[]A\A]A^A_fUSGDWW _uFu DҺGLFLONML~RDHHcHAff~(AI !AIuHIAIMu[]USGDWO _uFu DѹVGLFLOLM~QDHHcH,HAʅ~&fffffAI!fAIuHIAIMu[]fffffffffffAUATUSGGDW WuFu AA‹NWHvHEIIHHcHcL$Ll-H,DIL4@M4t LOLFI9Iw0AAA@AAIAPA@ IM9vM9LsD HEIM9rLHHIIu[]A\A]ffffffffAWAVAUATUSHGWDoO T$uFu DWFLgHnHcHAH\$HD$D$1A9?Lt$Hc)ؿLT=ILLLփJ 6HcLtDHDHMKIL9s+AAA@IQA HL9rL9sEAADIAMLtWH*L$A IMKILM9wXII0WWI*\$T$L!։H HH *T$D$H H H1HL9vL9s WA* !D$A9H|$HT$AM$|HlH[]A\A]A^A_AWAVAUATUSHGDgO DwuFu DGVH_HnEHHcEHHHD$H|$ADD$D$19L|$Hc)A=HsL\DLMIcI4tD IHDL$LNHM9s0ffAA@AHAPA@ IM9rI9s1AAp1D9}LcD9B*LC |DDA9CH\$Hl$AH[]A\A]A^A_fffffffAWAVAUATUSDG WOoLfH_vEH\$MHcHcEHT$HD$E1DCDSDKDC {sKD D[S%E DS AA@D E DKAA D E DCAAD A {AAD s߁ Kށ ف A$IH AōMOA9%t[A1A9}&fffffDcD+HE AA!D uEDOHcB" 3 B 3H|$HT$Lt$AH4WMHt$H[]A\A]A^A_ffffffffffUHSGDWO _uFu DѹGLZHwRMI~WDHHcHAʅ~,fffAI !AIuHIAIMu[]fffffATHHUSGDWO D_uFu DѹHxHr@RII~^DHHcAL$H,Aʅ~.AfADI) !fAIuLHAIIu[]A\ffffAUHATUSGDGO D_uFu DHXV@H~II~xHHcDL,L$6AA˅~HAffA I)ȉ !f!f fAIAuLLAIIu[]A\A]ffffffffAWHAVAUATUSGWDG ouFu AALQGILOLELHcHL,L<HcL4DK|fff@M$6tWLKLV* AMt~I9HwBfWWA*\$T$A*QT$D\$AII M MII9vM9s WA*!A LLLIIg[]A\A]A^A_I9HwIMWWIA*t$T$M!ӉI LAH *l$D$H I MII9vefffSGDWO _uFu DѹVGLFLOLM~IDHHcHHAʅ~ffffA* IAIuHIAIMu[fffffAWHHAVAUATUSG_O DguFuٹHx@HrRAHLcE-7L,WE1DE9}{((fffffD)AIc=LL2DL1D9}<((fffBD9}$HcA.s.r8BD9|fffEAA9|LLl[]A\A]A^A_,BfffffffffffUHSGDGO DOuFu DLZGRHw~pDf%HAHcHfWHAʉIM~3ˉAZ)I]_,fAIuHIAu[]ÐUHSGDGO DOuFu DLZGRHwDHHcA%05,5HHAʉfffIM~=(̉(fAIIt!A.s.r fAIIuHIAu[],fA뻐AWAVAUATUSGGDG DOuFu AAHOHVvHHEtA%LcHcL\$EH(H(H|$HD$Ht$H<tE.hHSHMITLOL9(I(AII>L!HHH D$DL$D.s\A.L IIM9wRHHT$DD$D.sA.L!L HH D$DL$D.rL IIM9vI9s.bAfHL$HT$AHlHHH[]A\A]A^A_ffD$DT$A,H H 8T$|$L!,ljH fffL_L9h(H(AIIKfffffLH!HALI.sU.r`IAM ffffMIIIM9.s.rBLH!L ALI.rfffffIAM fffI,H AI ,ALH!L d.rA.ffr,A8wD,DrfffffffSGDWO _uFu DѹVGLFLOLM~IDHHcHHAʅ~ffffAZ IAIuHIAIMu[fffffAWAVAUATUSDG WOoLvLvELMHcHcDD$HT$HD$E123KDKD[S{ DS DC sC8D DKD D[فA SA {E DSAE DCA s E DKAA S"A {$E DC&AA s(A S*A {.A s, S0 s6 S2 S4 S> C: C< A@A D AAD сD ɁH@D A A $IčMOA9tfA1A9}.DcDCDkHE A E AA!D uD]DDOAIcB" 3 B 3LD$Lt$L$ODT.D\.Dd.BlDt$\$DL$D+L$Dt>D|>D>)|$+|$AAEAAD$D!ÉAD++\$E!DL$!HT$|$DL$Dl!HL$\$Dl$TщT$D)AAAD!DL$D+L$L$EAAE!AD+l$DL$DA!HT$Dl$DD$HT$TT$)!ՋT$+T$!T$T$D)D)҉!ADE)D)ډ!AӉ+L$D)!Aԉ!DADA)@(A!AEAAAD!LT$ADD!DD$E)D(B<DAAE)AAD(D!ADDEA)AA(B\AD!E!DADFLD+t$@*l$H|$AAD!l$T>@,>D+D$*L$AAD!L$L>D$\$9\$s$9\$LcT$L\$Hl$|$+|$Ll$HD$CtGdE\*Cl*AEDElAAD!ED+L$|$EAAD)AA)E!AD+t$DDL$DA!DAD)A!ΉDt$E(!A!AH|$EDE)AD(E<:AHL$D!DA t$9AHD$LL$E)DEtGD D+t$D+D$A!C,DDD)D)AA!Dt$A!AF #A!DD$D!Ht$EE)B|-AE(AщD)E!D(EE|2Ld$!DC\"ffffDT$D$Ll$Ll$Ld$AD9T$Ld$Ll$DD$AD9D$tH1[]A\A]A^A_Lct$$E1LT$MA9K,}McHD$ACtEt/G GdElA)DE)EAA!AH|$DE!D)E!ADE)AD(AD!DA9A?|H1[]A\A]A^A_ÐAWHcHcHHAVHD:AUATUSDH DD$DL$L$Ht$HD$D$HcDHT$HۃH\$L$Ll$Ll$D$Lt$T$Lt$L,$Ll$L$$L$Lt$E$Ll$LD$H|$Ll$EJ9T$A(wD\$Ll$DL$؉l$ԉt$Hct$Lt$Hl$L<$LL$HL$ElvDTuD\uDduAlwA|qEtqE|qDDl$)|$ȋ|$A+|$AAD!D++\$܉A!|$AAD!ED+L$L,$\$DADuA!TqDL$ԉD$H$+L$DL$ĉT$AAAD!DL$D+L$L$EAAE!Dl$D+l$DL$DA!TpDl$ЉT$HD$)DpD$Љ!ՋT$+T$̉!T$̉T$D)D)҉!ADE)D)ډ!AӉ+L$D)!Aԉ!DAA!!|$ELD$DDA)Af)AD!EE)fE)AAfA pE!+|$E)EEA)AfA)f+L$AAfD)E!AADD+t$f+l$D!׃D$D!D|$L$fETpAH\$fELpAD!l$D9|$fTsfLsf,sv\$9\$LcT$L\$H,$|$+|$Ll$HD$CtSGdSF\UClUAFDPFlPAAD!ED+|$|$EAAD)AA)E!AD+t$DD|$DA!DAD)A!ΉDt$fE)!A!t$AHL$H|$DE)EAfD)AfF WD!D9fBQAL|$H$E)DGDWFtPD+t$D+D$A!C,DDD)D)AAA!A!Dt$DD$A!Ht$D!F<#Ld$B|-EE)D)AfE)AfD)E!!EDfFLVfC\TDT$D$Ll$Ll$Ld$AD9T$Ld$Ll$DD$AD9D$tH 1[]A\A]A^A_Lc\$Lt$\$O^K,E1A9}McHT$ACtzFt}G zGdzFlzA)DE)EAA!AH|$DE!D)E!ADE)AfD)AD!DA9fB{H 1[]A\A]A^A_ÐAVAUATUSHDbLJ1ҋ_nLVDw HT$DoL_1_HT$fL$L3!|HcAZYZ\$|$~E~wfHt$|HcMcMcND19}DL>!HcN4J lCY,Zf.,r9B|IMMuHĨ1[]A\A]A^ÐAWHcHAVHD:AUATUSHcDHH DD$DL$\$Ht$HD$D$Hct$DHT$L$HHt$Lt$Lt$D$L|$T$L|$L4$Lt$L,$L$L|$EeLt$LL$Hl$Lt$ES9T$E}Dd$Lt$DT$DD$ԉ|$PLcD$L|$H,$LT$HT$Ht$Ll$C\GGdGGtGF|EClBBLBBDFt$+t$H$GLEAL$HL$E)D$DAADL$A!D!F\BC<:ED+T$BTAt$EAE!AD+l$D)DT$D\$EAE!ADl$AT$D!DL$D+L$L$EAE!AD+\$DL$DA!H$D\$BDBD$HT$D)BTBT$Й!ANjD$)!ʼn+t$)ؙ!É+|$D)!Aĉ+L$D)!ЉAƉ!֋T$D)fD)!DDEE)!t$fE)!H|$L$AE)fD)E!AD)EAD)fBGAfE)fE)A!D!fFTGDA!LT$Ht$fB\GC )Ef)fG$BD$fBTFD$H\$!9D$L=fBLC\$9\$LcT$L\$LD$H,$Ll$GtSC|PGdSF\UClUGlPDD$D+D$EAE!ED+L$DD$EADA)E!AD+|$D)DL$DƉA!D!DADD$A!D|$DE)fD)ʉAHL$AD!Lt$D)fD)DfBQ!DA9fC4VL4$L|$D)މGLVGtW!D+L$D+t$B,Ht$D)D)DDAA!DL$A!ADt$!D!F#B|-Ld$EljE)D)fE)AfD)E!!EDfF|VfC\TffffDT$D$Ll$Ll$Ld$AD9T$Ld$Ll$VDL$AD9L$tH 1[]A\A]A^A_Lc\$LD$\$OXK,E1A9}McHL$AC|zFD}G4zGdzFlyHL$A)DE)EA!ADE!D)E!ADE)fD)AD!DA9fB4y|H 1[]A\A]A^A_ÐAWAVAUATUSHL$HcDHD9D$DL$Ht$HL$HD$D$Hc\$DƒT$H\$ffffLt$Lt$L|$L|$t$Lt$Lt$Ll$L|$L\$Lt$Lt$LL$Hl$Lt$EeDd$ESDT$EDD$}D$9t$|$NLcD$Ll$L|$Hl$LT$HD$LL$CT(C\8Gd8Gt8E|(ClT$ALHD$AE)L$CtDHL$A!C<:ED+T$ĉt$t$+t$EAAAE!AD!E\DT$D+l$HD$t$D\$ELDA!Dl$DDL$D)AAD!ED+L$DEAE!D\$D+\$DL$DA!ATD\$T$HD$ADD$D)!ANjD$)!ʼn+t$)ؙ!É+|$D)!Aĉ+L$D)!ЉAƉ!֋T$D)D(!DDEE)!t$E(!L$AE)D(H|$AE!AD!AD)E(EDA!A8ET8A\8ED)Ll$E(܉EΙ)@(G4(A!H\$ȉG EL!HL$ȍT=ATD$D$9D$$9\$LcT$L\$LD$Hl$Ll$CtC|GdE\*Cl*GlDD$D+D$EAE!ED+t$DD$EAEA)E!AD+|$A)Dt$DDA!DA!DDLL$A!D|$D)D(!DD)D(C Lt$!DC42DD$AA9LL$L|$D)މGt GL:D+t$!D+L$B,Ht$ЉD)D)DDAA!Dt$!AF#A!DL$D!EE)B|-E(AE!ljD)ED(E|2Ld$!DC\"fffDT$D$Ll$Ll$Ld$AD9T$Ld$Ll$^Dt$AD9t$tH1[]A\A]A^A_LcD$$E1LT$MA9K,}McHL$AC|ED/G GdElHL$A)DE)EA!ADE!D)E!ADE)D(AD!DA9A4|H1[]A\A]A^A_ÐAWHcHAVHD:AUATUSHcDHH DD$DL$\$Ht$HD$D$Hct$DHT$L$HHt$Lt$Lt$D$L|$T$L|$L4$Lt$L,$L$L|$EeLt$LL$Hl$Lt$ES9T$E}Dd$Lt$DT$DD$ԉ|$PLcD$L|$H,$LT$HT$Ht$Ll$C\GGdGGtGF|EClBBLBBDFt$+t$H$GLEAL$HL$E)D$DAADL$A!D!F\BC<:ED+T$BTAt$EAE!AD+l$D)DT$D\$EAE!ADl$AT$D!DL$D+L$L$EAE!AD+\$DL$DA!H$D\$BDBD$HT$D)BTBT$Й!ANjD$)!ʼn+t$)ؙ!É+|$D)!Aĉ+L$D)!ЉAƉ!֋T$D)fD)!DDEE)!t$fE)!H|$L$AE)fD)E!AD)EAD)fBGAfE)fE)A!D!fFTGDA!LT$Ht$fB\GC )Ef)fG$BD$fBTFD$H\$!9D$L=fBLC\$9\$LcT$L\$LD$H,$Ll$GtSC|PGdSF\UClUGlPDD$D+D$EAE!ED+L$DD$EADA)E!AD+|$D)DL$DƉA!D!DADD$A!D|$DE)fD)ʉAHL$AD!Lt$D)fD)DfBQ!DA9fC4VL4$L|$D)މGLVGtW!D+L$D+t$B,Ht$D)D)DDAA!DL$A!ADt$!D!F#B|-Ld$EljE)D)fE)AfD)E!!EDfF|VfC\TffffDT$D$Ll$Ll$Ld$AD9T$Ld$Ll$VDL$AD9L$tH 1[]A\A]A^A_Lc\$LD$\$OXK,E1A9}McHL$AC|zFD}G4zGdzFlyHL$A)DE)EA!ADE!D)E!ADE)fD)AD!DA9fB4y|H 1[]A\A]A^A_ÐÐH\$Hl$Ld$Ll$Lt$L|$HHIA˺tHt ;t:H$H$L$L$L$L$HD~D;unLnEDgLwuAu 땃wH5Hc HtEE A E DA AA!At DAD AD!uADw4L:McMA듾1fDىLѢ1DىL=DىL辟DىL DىL DىL葜DىLDىL!DىL葔zW AIHcDWMcEa III9NDE9AN)DD$D)E1ɉD$l$ ~D@A 11~B 1AMcLcK,ROdHHIDmHD$x$HIEO,VLl$pg 4 D$E D H\$xl$ HL$pHT$xH\$`+l$`HL$PLcl$`L$+L$PLcL$PH\$pAAOD-D)ALωMADt$H+L$HŋD$PHLDT$Ll$DLL$0AAADHID$`$NBL\$8LT$8HH9L,{MIcAHt$ HHt$(|$DLc$Ld$ \$DMK, KAArAzI D@DHA DfE1AC AKJLAAD D ˉ1yDIL\ uI LD$(LD$8ALT$8MRDl$PE\$ A+\$LD;\$PH|$xLD$pbE~3Lc$O $DwoHfAfEpfAhMuC[McAED;\$PH|$pHt$xHLGJHcl$JffHcHc΃BBC4KA IACID9|HcHcB,B4A*C4D$tIFLD$GAE9XLt$LL$LL$L$[]A\A]A^A_E~LcHcK4E1E9}HD$Ld$IcN\5MlLd~"IcT$H\-MIC< A;IuAE9|IHt$Au[]A\A]A^A_fffffffAWE1IAVIAUATUSD;T$8DD$LD$@}IcAIHD;T$8HT||$0EHcL$8HcHHD$DL$H\$ɉL$E1D;l$8LcL$8l$Hc|$t$8Dd$ԃNl$AH|$t$LD$McH\$KHD9|rEL$LcLcL|$Lt$MMyADT$8Dd$,1LMHT$0HL$8AEAAEZD9}/H9AE4xfD2HYHA<_fzHD9|AtH)AHMEhLfDHTEt$1IDHYD9}eCALALЁ%A4*B) (L%DHB(AACID9|G4ALALAAA .F4*.LցF.IsAA΍OAE3AHESET$D9}-L2F\ufDHzHA\}f^HD9|D9}L"FlefD.LT$0HL$HD$HD$8AI,JHl$0#H9At$A DDYLqt$\}1D9fHZ}iALALALG%B4*)AB (A*EEIȉCHD9|ALALցAB *M.C4AG,AD$.ʉLAALAEԁMځADcD$)B4*OG*DT$LAAAD9fD[ DcHs}-LB|]f>HZHEt]fDvHD9|;L$BEkL$L$LcL$DL$,HcLt$HL$I$IjM}GIAHD$Hl$DD$ L|$D$8H\$Hl$Ll$D|$ HL$0HT$8؃F_EwE1IDHzE9}vCAALAMALЁA%4)C (DDHAHAAHECIE9LI|GALAMAALցAA D*C.D4ET$AAAAE9EEs}BHDTMAl$D9fES})LwCtuEl$E9fAs }LB<{fA{ HD$0Ld$H\$H\$8AN`L\$0XjH:EGA LaDDZD4{HzfD1E1E9}yAALALALG%4*B)A A*EIHLMDHA$AD$IE9|AMALAALցA /F<*C4AD.LAAAE<$F<.AALAET$LD4)AEE~fAt$ ET$E9LG\UEnE9fE\$cHw sA^ D9fAL$GLwFDufED$1HT$8HL$0ALHFSHLLl$fDHHT$8AH D4KHL$0fD1HrHDlufDiIHH\$HHT$8HL$0AH2DsfDLBFdEfDaLZHC|]fyH9EHcL$H$H$H$LcDl$,H|$Ld$M L[T$HuLqAL $L\$Dl$Ht$Lt$HT$8L$$Lt$L|$Ll$DD$HL$0؃JfffKAh1IDHz9}jCALAMALЁA%B4"B1C 9F (DHAAAEKI9|G ALALAALցC !F 2BT=B,.DkAAE9E Ak}>HDSE9E,LfEk}'HwE$vDsE9fEc }H_E_fEC L\$0H|$HD$HD$8L$M<{L|$0fLEH1A HYHzD9GTDfDDZ}jALALALG%B42B9AB (C"EDHȉCHD9|AMALAALցC 0F:B.C4AF&LAAADF6AALADSLF:B4)AEDEfs DSE9LG,\DeE9fDkwHOENDu E9fDS[HoE ofDKGH*AHE lMMML,$fD HLAGTfDHzHE$~MLl$fDaML<$HH:AA,|f)LJC4NfqHZHE,_fDiMMMLd$HnELcHcK $E1E9}YLT$8D|$,IcHD$0L$IELxM$~'IcT$,H\-fffMIGF&AB .AB,6DD~AACEHc\$Dt$McDL$$Ll$AA AH\$HD$؃HT$DD$E1HL$HA:HE9DR}.ADAlFd>DVHD!iHE9|҉DEDDD\DDYt H.DTDQHL$HD$HL$HD$L$HD$؃TEA 1A)BA)D9}.LD$H$0 HcLT$D9MH A< B||Hl$HcHt$E1HUIL1EuIl$Ћ|A9}QATtDD|$AL$ApARIA IA9|D\lDD؉ٍ48ADDD)F,9A2EjHcB9~DD)EHJLI(DdEEEAAADADYDQD!]EnHc|$Ic1D9}JL\$Dd$LcL$0 LT$MEK~T$IcM"IEEIuD9|H|$Ht$AuH []A\A]A^A_ffffffffffAWAVI1AUATUSH0Dl$hDD$H$LD$pD9}HcIHD9HT||$A ؃&|$E"HcHcCL-HHD$DL$H|$L$E1E9Hct$\$MND|$GD-IcLL$DD$AHt$H<$Mc1JtE^LD$L9E}1LcL$IcMcӃ0A2EXEID=L9|McIcEA 1D2D\=tMLD$E3E8AE9rHl$Lt$H,$L$H0[]A\A]A^A_AtA;H0[]A\A]A^A_D9}LcHcDL$Ld$HT$D$H $LLd$Hl$H\$At3DDHMcG\%DE|-DyELDIHuD|$IWLWE1E)AA9|$LcHcAA B"A4(G <D*I DDAIBB4!)D F  (ARAA{AJIDDACI D;l$[HcHc B"4/F '<D*I DDAIBB4!)D F  (AuAA{IzIK DDD9AC}2HLcGT%DE\-DYELDIHD9|H\$H$L$Ht$M4vH0[]A\A]A^A_D$D9L$HcLcH\$Ll$H$H\$Hl$Ld$Ll$ D$L$MFAȅ~?xHcAIB F(4(D<AAAEEIuLcEC<+A+C,#DA:D$HD$H$D9L$LT$O4V?D$)D$D$ D|$A1MVAD)|$;L$Hc}LcL$Jt7C;L$|HcL$B 'B/D/4IӋT$AȃDDAI~OzH4 (B B(DAL$B4AAIA3IuHDDB(AB <(L$+D$DAADC,9A+LcC9~ELcHcK4$1D9}AL$D|$HcLL\IE~T$McL'HC,A(MuD9|H $IAuffffffAWAVI1AUATUSH0Dl$hDD$H$LD$pD9}HcID9HT||$A ؃-|$E)HcHcCL-HHD$DL$H|$L$E1E9Hct$\$MND|$GD-IcLL$DD$AHt$fffffH<$Mc1JtE^LD$L9E}1LcL$IcMcӃ0A2EXEID=L9|McIcEA 1D2D\=tMLD$E3E8AE9rHl$Lt$H,$L$H0[]A\A]A^A_AtA;H0[]A\A]A^A_D9}LcHcDL$Ld$HT$D$H $LLd$Hl$H\$At3DDHMcG\%DE|-DyELDIHuD|$IWLWE1E)AA9|$LcHcAA B"A4(G <D*I DDAIBB4!)D F  (ARAA{AJIDDACI D;l$[HcHc B"4/F '<D*I DDAIBB4!)D F  (AuAA{IzIK DDD9AC}2HLcGT%DE\-DYELDIHD9|H\$H$L$Ht$M4vH0[]A\A]A^A_D$D9L$HcLcH\$Ll$H$H\$Hl$Ld$Ll$ D$L$MFAȅ~?xHcAIB F(4(D<AAAEEIuLcEC<+A+C,#DA:D$HD$H$D9L$LT$O4V?D$)D$D$ D|$A1MVAD)|$;L$Hc}LcL$Jt7C;L$|HcL$B 'B/D/4IӋT$AȃDDAI~OzH4 (B B(DAL$B4AAIA3IuHDDB(AB <(L$+D$DAADC,9A+LcC9~ELcHcK4$1D9}AL$D|$HcLL\IE~T$McL'HC,A(MuD9|H $IAuffffffAWE1AVIAUIATUSD|$8DD$LD$@E9}IcAIHE9HT||$EHcHcC ?HHD$DL$H\$L$E1E9DD$Hc|$MM\$C4?IcLL$At$ЃDD$H|$1McAM9O3NDAuL\$}7LcL$fffHcHc΃BBAsA IABDM9|HcLcB<C4A:BtT$̅tIc FLD$GAE9mLt$Ll$L$[]A\A]A^A_E~HcLcH41D9}=Dd$HcMN2H\E~T$IcMc"IA,A+IuD9|MIAu[]A\A]A^A_fffffffAWAVAUIATUSHAHT$ЉL$DD$~DAс)|$E;HcT$HcDL$HD$HHT$E1D;$l$Dt$M}Hc$AHt$L$ IcEUE]HDFHD9YHE9|ADÅFDDDQtLBqH|$MAJgHD$D$TIUHL$E1ۋ\$MUIȋIA9f9AEAu}N‰DADA8AAArAExIIA9|‰DIHEAA48DAXfEXA0I:DEfDQAfDAL$ AM&InI^E$DU;McDA:AAG|FT}B<{BB~DEA:dLcd$HcL$$$Hl$O<$HD$LDd$Ȩt7MEL\$IUALK4HDVfENfAK^fA[*z1ILZDAl$49}aDAA3BBDACAxAIAPI 94|DABBAhэSAIH D9Ax}%I+MAsfAp A[fYEcfDaL|$Ll$AH$ AL8LpL`HhEA>A$DEAIcA ;BAAEGAA4XD)ω!)f)!f|]A|X )f)!E9ft]~E9}JMcGLXCXA)DA!A49AD)։!B )fA)A!AfFL]HD$XE1E9JHL$hMcH4$G \BA4D)ω!)@(!C<A|)@(!E9Ct~E9}GMcAL)!э49A<D)AAAD!AB )A(A!AGH\$E1LT$HLL$PE9};ffIcAF\BL A)D(AA!EE9F\=|DT$ D$AD$DD)މ!AD)D(!DC<"A|D)D(!DCt"D;D$~E9}HMcEDA)DA!A 8A<D)ɉA!F D)E(A!EG"L\$ME]EHAXAxD)AAAD!F E1E9qffIcDABt)AAAD!D>B4D)ω!)@(!@<+B|)@(!E9@t+~E9}GMcGLCA)DA!A49AD)։!B )A(A!AE +HD$E1E9JHL$(McH4$G #A< E3C4;A +A)DA!DAA)DA!DΉAD)E(A!EG 3T$A4XD)ω!)f)!f|]A|X )f)!E9ft]~E9}JMcGLXCXA)DA!A49AD)։!B )fA)A!AfFL]HD$XE1E9JHL$hMcH4$G \BLnLfL|$(Ll$ L~(Ld$DK{KkLn0Lf8DCD))AAAD!!FE1D;L$LDT fffIcDE|DA)D)AAAD!FA|AAD!EA)DA!DA)A(A!AFD-DDDD)@(!B|-D;L$LiE9}UIcDTD)DLD)‰!B4!B<)!D>DE)AD(AD!DBL-HD$8L AiEQAQAyEAA)E)AAE!E*AnE1!A9IcDBtAD)AAAD!FAB4A)DA!DEA)DA!DA)A(A!AF'FDDD)@(!A9Bt'lE9}RIcDBT )FT D)‰!B4!DD)މ!B<D)D(!DBL%HD$8E1E9JIcLL$ HT$FD=BL-H|$(Bt DLD)DT=B|%A)!ADA!DEA)DA!DΉAD)E(A!ELT$`FLT$lB ALcɉ)@(!E9C FD=FDl$hDd$4D$hL|$PL|$`AADMl$hD$0AD9d$0Dl$h>t$49t$0Lcl$hNDpM IhMxMh(Ld$Hl$KDKSEf{Ih0DCA)E)AAAAE!AE E1D!E9DIcDEtAD)AAAD!FE4A)DA!DEA)DA!DA)A(A!AD/DDDD)@(!E9@t/mE9}QMcDATD)E D)‰!B4!FD)։!B<D)D(!DA,E1E9}~H|$McHD$C+E+AC4;E ;AA)ED)AAE!DLD$`!AD)ʉ!AEA)AA(AE!AE9G$|H|$Xs_1H[]A\A]A^A_ÐAWHcAHAVEpAUATUSHcIHHcH vHIED$0D$|HTOEHt$`DL$, HT$p1ҨtH[]A\A]A^A_AnHcH^HHD$ht11L\$hLcLcO SNNĀ~McEfE1SKMcDSDKJĀDC A)A)DDA!A!EE E1E9IcDEt{ AD)AAAD!FE4{A)DA!DEA)DA!DA)fA)A!AfDD}DD{DD)f)!E9ft}jE9}XIcDDL{ D)ؙ!E)FDA!C4D {D)։!BAD)fE)A!EfD\}AJ{AE1L$L$E9}2IcAE$iAE1A9H\$XA.AvEeA]L$$kIcFT7F\/FD7FL/D)!ʍ 2D)ډ!4ˉ)@(!H$D)D(D!)DDLd$`@(!B'H$DE)A(@lHl$`A!ADd/Bl7Fd/Bt7B\/D)!ADD)D!DAD)D(A!H$DEE)ڈTDAD(ىD!D)ADL\$`E(A!BLH$EAE)DTAAE(LD$`E!EFTD;|$$D$D$<A9D$ADl$ fHt$0HL$xLL$XLL$(Dt$ HqH\$pH\$0Hl$pLL$PLT$pH\$hDeLD$hH|$hDd$EZD\$AD$DD$D9t$D|$ fHcL$L$H$LT$`L$H$H\$`H|$pF$LD$hDd$HDlF$Dl$DFt FlDt$@D|)DtD|$Ll9DL9D|B|B\AD+D$DDA!+t$ DD$AAD)D!ED+\$t$ EAE!AD+T$D\$DA!ҙDT$!A)!HD$pTT$HT$hDD$HD$pTT$HT$hDD$ D$D)ș!AD$)!D)!ʼn)ؙ!ËD$D)ș!AD$ )!DD)!ADD)!AʼnD)!ADD)!H$ADL\$`FDDBtE@lB\DLB|D+D$HED*L$HHl$XDEAE!DL$HD )+T$DD*\$DLD$XEDA!D\$DDF\D+L$@@*l$@L\$XEAD!l$@Bl+T$LD*D$LDE)AAD(D)D!AD)E(A!DD$LLL$XA!E(EA!ADEE(D)FD LD$PA!EB,FTF\FlD$T$ 9T$D$AD9T$yLcL$Ll$`Lt$pL$H\$hH$G<)Gl1C D\$Et\$ D+D$!AD+D$ ED+D$D!A!DD$+|$DD)AA!|$D!E1HD$`ADE)A<A(Hl$XAA!D)E(AE$)Ht$PA!EE1L$D9L$Hl$`Ld$pHD$hGD9E|)Cl!EdDD))މ!!B<L$ӉD)CT!HD$`ADDD(D)D(A|D)!DLD$X!DC\HL$PAt D|$AE9H\$pLD$hL$HD$`ETG|C|E\A)E)DDA!A!A*G'L$D)E)!B,+EAACl!E!E)HD$`G,0EAEE!ElLD$XEDDE))@(E(A!E!EC\LT$PGtD$D$<Lt$PLt$(H|$pAD9L$DZnDZFDZN Hct$HH LJfAfCHJLJLYfEYfC H LJXDYfG4LJYAXfEHJDYfCE1fADYXYNjL$EXYAYA9DXDXEYAXfD- B9H AfAHJfE4HJYDYfEDZnDZFDZN Hct$HH LJfAfCHJLJ LYfEYfC H LJXDYfG4LJ YAXfEHJDYfCE1fADYXYNjL$EXYAYA9DXDXEYAX#fD-B fffB)H AfAHJfE4HJYDYfE I`fA A*JYD*JDYD*AXJ DYL*YAMAXA9XY AI*JYf%H*JYD*XDYJ LAXD* DYAXD*JDYD*JAYDY*J LXYEXD* DYDX*JYEXD*JDYEYAX*J YZpAXH*YZx XZhHXZ AHcIAYf%HXZDZ@DZHDZP HcIFHDfA.HPIcIc 4fD.rfA%,fff DJDBDr *E*YDYE*LL$DYA*YMAXLDzrJ LDDrAXD*DYz D*DYD*DYIcXAYE*DRDYIcXA*YEXE*DY*YEXEXEYE*DYAXDXEXDXEYAXfA.r5D\$fAEQD$LT$ILL$LD$L\$f= f.s,D\$fAE~fffff[]A\A]A^A_fffAWAVAUAATIUHSHFH+LR@LJHLB0LZ8D$HZ`LzPHLT$LL$LD$L\$tfD& HHD$E0A IqAIzMPfAHcAM E3AZ!AZifAAZqAAZy MKMLcMAZEZ@EZHEZP OvLct$MKC DZDB *E*YDYJE*DYEEE1AX*YHL$AXHHE9XY fD%j I_fD  A*JYD*J DYD*AXJDYL*YAMAXA9XY AI*JYf%H*J YD*XDYJLAXD* DYAXD*JDYD*J AYDY*JLXYEXD* DYDX*JYEXD*J DYEYAX*JYZpAXH*YZx XZhHXZ AHcIfAY%HZXDZ@DZHDZP HcH@ILHPIcfA.Ic HIH *fD.rfD ,fffff rDr DJ*D*YDYE*Ht$DYA*YHAXHDDRz JHD2DJAXE*DYDRD*DYD*DYIcXAYE*DB DYIcXA*YEXE*DYA*YH4RH6EXEXEYE*DYAXDXEXDXEYAXfA.r2|$fDGD$LT$HLL$LD$L\$f= f.s,|$fD~[]A\A]A^A_ffffffAWAVAUAATIUHSHFH+LR@LJHLB0LZ8D$HZ`LzPHLT$LL$LD$L\$ZfDF HHD$fffffAE2IzA IqMPEAfɁHcL fAAAZ!IAZiAZqAZy MKMcLct$ZDZBDZJDZR OMKC DZDB*E*YDYJE*DYEEE1AX*YHL$AXHHE9XYfD% I_fD A*JYD*JDYD*AXJDYL*YAMAXA9XY AI*JYf%H*JYD*XDYJLAXD* DYAXD*JDYD*JAYDY*JLXYEXD* DYDX*JYEXD*JDYEYAX*JYZpAXH*YZx XZhHXZ AHcIAYf%HXZDZ@DZHDZP HcIIfA.HPIcIc 5fD.rfD&, DJDBDr*E*YDYE*LL$DYA*YMAXLDzrJLDDrAXD*DYzD*DYD*DYIcXAYE*DRDYIcXA*YEXE*DY*YEXEXEYE*DYAXDXEXDXEYAXfA.r2|$fDGD$LT$HLL$LD$L\$f=S f.s,|$fD~[]A\A]A^A_fAWAVAUIATAUHSHNH+HZ`HB@LBHLzPLJ0Hz8IIEAMXEMQHpMcfAHcAM DAZ!AZifAAZqAAZy LOI<LDZDZGDZODZW HcIx DBz*E*YDYJD*DYED$1AX*YK 6HD9AXXYfDIcffffADM *JYD*JDYD*AXJDYL*YAL$AX9XY AI*JYf%HD*JDYD*JLDYAXD* DYAXD*JDYAX*JYAYD*JLXDYDXD* DYEX*JYEXD*JDYEYAX*JYZpAAXI*YZx XZhHHcXZ AHcIAYf%HXZDZ@DZHDZP HDHHHcfA.Ic 4fWf.r 1fADM%H,fDDjO$6rZL DBA*D*YDYD*D*DYDJDrDYE*LDYDDRAXjrA*YE*DYHcIcAXAXAYD*DYX*YEX*YDXEXEYE*DYA*YAXDXDXDXEYAXfA.rfADU[]A\A]A^A_fW1f.sH,ffffffAWAVAUAATIUHSHFH+LR@LJHLB0LZ8D$HZ`LzPHLT$LL$LD$L\$ZfD^HHD$AE2IzA IqMPEAfɁHcL fAAAZ!IAZiAZqAZy MKMcLct$ZDZBDZJDZR O^L\KC DZDB*E*YDYJ E*DYEEE1AX*YHL$AXHHE9XYfEWIdffffA A*JYD*JDYD*AXJ DYL*YAMAXA9XY AI*JYf%H*JYD*XDYJ LAXD* DYAXD*JDYD*JAYDY*J LXYEXD* DYDX*JYEXD*JDYEYAX*J YZpAXH*YZx XZhHXZ AHcIAYf%HXZDZ@DZHDZP HcIFHDfA.HPIcIc 4fD.r 1fA(H,ffff DJDBDr *E*YDYE*LL$DYA*YMAXLDzrJ LDDrAXD*DYz D*DYD*DYIcXAYE*DRDYIcXA*YEXE*DY*YEXEXEYE*DYAXDXEXDXEYAXfA.r5D\$fAEDD$LT$ILL$LD$L\$fW1f.sH,D\$fAE~[]A\A]A^A_fAWAVAUAATIUHSHFH+LR@LJHLB0LZ8D$HZ`LzPHLT$LL$LD$L\$nfDHHD$E0A IqAIzMPfAHcAM E3AZ!AZifAAZqAAZy MKMLcMAZEZ@EZHEZP OvLct$MKC DZDB *E*YDYJE*DYEEE1AX*YHL$AXHHE9XY fEWI`fD  A*JYD*J DYD*AXJDYL*YAMAXA9XY AI*JYf%H*J YD*XDYJLAXD* DYAXD*JDYD*J AYDY*JLXYEXD* DYDX*JYEXD*J DYEYAX*JYZpAXH*YZx XZhHXZ AHcIfAY%HZXDZ@DZHDZP HcH@ILHPIcfA.Ic HIH *fD.r1fD fffffH,ffff rDr DJ*D*YDYE*Ht$DYA*YHAXHDDRz JHD2DJAXE*DYDRD*DYD*DYIcXAYE*DB DYIcXA*YEXE*DYA*YH4RH6EXEXEYE*DYAXDXEXDXEYAXfA.r2|$fDAD$LT$HLL$LD$L\$fW1f.sH,|$fD~[]A\A]A^A_ffffffffffAWAVAUAATIUHSHFH+LR@LJHLB0LZ8D$HZ`LzPHLT$LL$LD$L\$TfDHHD$fffffAE2IzA IqMPEAfɁHcL fAAAZ!IAZiAZqAZy MKMcLct$ZDZBDZJDZR OMKC DZDB*E*YDYJE*DYEEE1AX*YHL$AXHHE9XYfEWI_fD A*JYD*JDYD*AXJDYL*YAMAXA9XY AI*JYf%H*JYD*XDYJLAXD* DYAXD*JDYD*JAYDY*JLXYEXD* DYDX*JYEXD*JDYEYAX*JYZpAXH*YZx XZhHXZ AHcIAYf%HXZDZ@DZHDZP HcIIfA.HPIcIc 5fD.r 1fD)H,ffff DJDBDr*E*YDYE*LL$DYA*YMAXLDzrJLDDrAXD*DYzD*DYD*DYIcXAYE*DRDYIcXA*YEXE*DY*YEXEXEYE*DYAXDXEXDXEYAXfA.r2|$fDAD$LT$HLL$LD$L\$fW1f.sH,|$fD~[]A\A]A^A_ÐAVIAUATIUSF+HZ0Hz8LZ@LRHLJPH-ҏ LcAfffIc2IcIff IIc HIHIAHItfdHBHVfDflHft\\YYXX\YX,B1s[]A\A]A^fffffffAVIAUATIUSF+Hj0HZ8Hz@LZHLRPL LcAfffIc3IA f+HfUIHHHcIHcAJ 0HItfA$HBfEHFfA HfAHBA\fA4HB\HVYfAYfE HVDXXfED\D\D\DYDYDYAXAXDX,ЈA\YDXA,҈Q[]A\A]A^ffffffAVIAUATIUSF+Hj0HZ8Hz@LZHLRPsL LcAfffIc3HA fDIf]IHRH IHcIAHcJ 0HItfA4HBfE$HFfA HfA$HBA\fAL$L$L$Lc;$HEdGdAlC,EtG4E\G|1;$8L$L$LcL$$D$D$G| CLGD$$GDCtH$H$DD$D$D$$D$EL$DL$$C H$D$Fd FtFlCTG|$$ED$AEL$EAG4Bt ClBGLGdDDClL$L$$D$L$D$CLGt $$DGCTCtAD;$8C 8LT$h|$xDŽ$$<I<HM9$H\$D$8D+$HLc$+$@Hc$ Hc$DL$dl$dALd$(MHHD$DL$`Ld$XT$@l$4H\$8ffHT$$LH$DD$$Lȉ$$LDD$|$`~HT$XD$Dt$4D9t$Ll$X$M|Lc$$8+$HM҃LT$H|$TBffffffE H\$(fD]D$L|$HHl$Ht$T9t$$$Hct$L$L$L$$AH$$EA\EtE$$HT$(EELDlD$$AW$$$$$$‹$$$ʋ$$$ʋ$$$$Nj$$<H$$|D$D$D$D$$D$D$E$EEAAED$$DABAAAEOfDmL\$(ADOfFd]D$L|$HHl$Ht$T9t$DD$dD9D$D$D$$D$BD$8D+$HLt$(DT$DMD$fEML|$D9|$Hc|$L$DH$L$EH$AD$EA4DD $$E$AD$D$Ћ$D$ADȋ$EDAH$$$0|$DOD$MfML9|$L+|$XD$8D9t$8Lt$(M&ffffD$fED$8LD9D$Hc|$L$DH$L$EH$$ADEEDA4D $$$AD$D$Ћ$D$ADH$ȋ$EDA$$0ADOD$$8fDML9T$HL$D$@HL$89$HML$L$HT$$D$<D9$L$H$L$L$ID$D$,CMD9)HcfALfAfEDfATf\fdfAlftAYf|AYXfAYXfAYXfAAYXfAYXfAYX\ 2 L$D$L$D$bAHcHL$L$L$Lc;$EtG4A\CAlClETG|1;$ffffL$L$LcD$D$D$$GtGT GCL$H$CDD$D$EL$$$$AH$ED$AƋ$AG4D$D$FlGd C,CTCt$EL$AAAG,B\GD G4CDG|DADDCL$H$$D$D$$BLGl $DGCTCtAD;$C XLL$X|$hD$pK<LML\$D$ D9\$pCD$D+$$ +$Lc$Hc$Dd$Tl$TADd$PL|$0HT$8t$Hl$DHT$$H$D2D$W$DD$i$ȉL$||$P~HT$0DD$ D$D9D$ LT$0Dl$xM<RD$D+$Hc$AH|$(D\$L6EHt$0.D$ L|$(Hl$(\$L9\$ Hct$ L$L$L$H$D$|T$x$EA\$E$EtEDELDDl$$A$D$|HD$0B8$$T$x$$‹$$D$|ʋL$|$$ʋ$$$$NjD$x$<H$L$t|D$D$D$D$$D$D$E$EEAAED$$DABAE1EEOD]LD$0E1DOE,(D$ L|$(Hl$(\$L9\$  DD$TD9D$ D$D$\$|D\$x7D$D+$"fffED$ L|$0Hl$0D9t$ Hc|$ L$DL$H$EH$AD$E4E$D $EA$D$Ћ$D$D$ADȋ$EDAH$L$t$0 E1ɅDODMD$ L|$0Hl$0D9t$ ffffMD$L+t$0D9|$ |)'fffED$ Hl$0D$D9D$ LcL$ L$D$EADEL$$CD$H$H$G$EFB4H$$Aϋ$D$D$AL$tA$EDAAAFD$D$<CI;T$X)HcfA fADfEDfATfA\fdfAlftAYf|AYXfAAYXfDAYXfAAYXfAYXfAYX\  L$D$L$D$nAHcHHL$OO$EuL$ItHt$ ~ f fT$pYT$pfD\$pAAH$(E*E*E*OE*E*G E*wD$|E^*}*u*m*e *ED$D9|$|AEYEYEYAYAYD$AYD$AYD$Y$EY$EY$$$$  AD|$,HHD$XD$|L$,$9D$|AL$,$$tLcl$|H$Hc$MLLl$ IMH$HT$`L$I1MK,)M<)END9L<$}cE1L$IcHcD$D9AWE|UA*D|U*A*L<$A AWD*E,|1D9Hcf$fD$Y,fDDDYLf$D9Y|ft fD$f$f$fD$fD$EYAXAYAXfD$Xf$X,DY,fdYDfT Y\f$AYDXAYDXf$DXDXD,EYfE\EYdfEL EYTEYEXEYEXEXEXEAY$fAtAY|fAD AYlAYXAYXXXA$wl$LL$`L$O,iL|$XLc$HAHcL$lHc$HD$t$Hc$D$ML|$PE~LD$@HL$8HT$Ht$ATmHD$8EMH$EDED*HT$@E*LL$PALUE*E1E9D*K )@IMff$Y$Y$DY$DY$Y$XAXAXX\ D$D$D$D$fnALLE9Icf$AfLEf4fY$fAYY$fA$$AY$ED*Y$Y$iY$f|fELfE\XD*AXAYXXf$XAYXAAYXf$AYA Y$XAAYX\5 Xf$AYXAAYXAlt$D$ t$D$fffnALLE9nE9f$T$fDL$T$f|$LL-T$fD\$AYfALLE94AAY$Y$EIcf,EfY$Y$fAY$fA$D*Y$XAAYXXAXAYXXXA Y$XA\f.,fA.r]f,f5,f5McKH)E^ HH\$HfD5ܒ fD=  HcljHD$ffEMANAAEI<(DDWDODG LD$T$LT$EEDMWWHcWHFDW%H(fE hpx *BY*BY*XBYWL*YXWXW*BYAY*YBXW*YBLXW*YXW*BYAY*YBXXW*YBLXW*YXW*BYWAY*YBXXW*BYWLT$X*YXAYXAZfT$fffTfUfVfAfTfAUfV,fAI'D\$Ht$HL$M$tDD$HH4A[]A\A]A^A_1fffAWH j $ AVAUATUHSDGLDO(GDo$HD$D$DD$HcT$IOMg A7DL$A_D$HT$H҉t$H)щ\$Ld$LqLt$L$LE_ Hc|$T$fDt$T$fD|$\$H|$EDT$MAAOAI(EDDRDJDB LD$T$|$LT$EEMȉD%H(WW hpx HcHFWWfE*BY*BY*XB YWL*YXWXW*BYAY*YBXW*YB LXW*YXW*BYAY*YBXXW*YB LXW*YXW*BYWAY*YBXXW*B YWLT$X*YXAYXAZfT$fffTfUfVfAfTfAUfV,fAI%D\$Ht$M$tDD$HI4FLt$Ld$Lt$Ld$HD$L$y[]A\A]A^A_1ffffffAW،   AVAUATUHSDGLDO(GDo$HD$D$DD$HcT$IOMg A7DL$A_D$HT$H҉t$H)щ\$Ld$LqLt$L$LE_ Hc|$T$fDt$T$fD|$\$H|$EDT$MAAOAI(EDDRDJDB LD$T$|$LT$EEMWWۍ@DW%WH(fE hpx HcHF*BY*B Y*XBYWL*YXWXW*BYAY*YB XW*YBLXW*YXW*BYAY*YB XXW*YBLXW*YXW*BYWAY*YB XXW*BYWLT$X*YXAYXAZfT$fffTfUfVfAfTfAUfV,fAI$D\$Ht$M$tDD$HI4FLt$Ld$Lt$Ld$HD$L$x 0[]A\A]A^A_1fffffAWh  D AVAUATUHSDGLDO(GDo$HD$D$DD$HcT$IOMg A7DL$A_D$HT$H҉t$H)щ\$Ld$LqLt$L$LE_ Hc|$T$fDt$T$fD|$\$H|$E؋T$MAAOAM(EEREJEB LD$T$|$LT$EEMȉD%H(WW hpx HcHFWWfE*BY*BY*XBYWL*YXWXW*BYAY*YBXW*YBLXW*YXW*BYAY*YBXXW*YBLXW*YXW*BYWAY*YBXXW*BYWLT$X*YXAYXAZfT$fffTfUfVfAfTfAUfV,fAI$D\$Ht$M$tDD$HI4FLt$Ld$Lt$Ld$HD$L$x 2[]A\A]A^A_1ffffffAWIEWAVAE(E(AUATUSHLG_H$f DODŽ$DŽ$$AA@AxIhD$M` $$$H$^IXlfYxpQ$$D,$DZZ$D$| tPL$EW% H$hE*E(D(DxDY䉄$D\$|EYD9$$DT$PD\$@Dd$0Dl$ ALAD LcHJ|HiH$H$DT$PD\$@Dd$0Dl$ r 11H$LcLc$NNN~Hc$Lc$IIcHT$pHIH)MHL]$H$MDLE߅ Hc$$ ǃ  ϛ  D$d? D5 D=) HL$hA(AY,HNL=D)A9EAEIAq D|$Dt$A;DD9A8}$fffLc΃C\ fD=\ D,*LcD\CIXt$hEAADYAAYAXAYEEEYAYAYDYEYAYEYEYDXAXYEYEYD\XAYE\G4 M9DYAYEXD\EaEyC 8iADM AYYXAYAYYYYAYYYYXAYX\YXYY\YAXYHl$H\$HHH_vtH\$Hl$HffHCHC HNuHCHC H8uHCHC H"Hk Hk ffffAWE1AVAUATE1USHf%4Z HO_D$(f_pfWxLA w^gX\$|^GP$L$Li$Dq\$0T$HcH|Hd$`D$hߤHD$pH|$pft$0DD$(fDD$11H|$pLcLcރJJ̐J̰~HIHIʃ$N19fD 1Y =1\ ,D*tmHcA\Xt$hA4HDDfDYDXfWEDYYDYYAE\A\DAXEXAXXD\E,:L9WoDWSLc$LL$XIM)I ELl$HLcl$|fDHX b[ fD X [ fD%:X fD-\ fD5W fD=W D [ Ll$P$AE,A*\pfYXfAWYYYY\XXAXX\A\$HT$HAAHL$AE)AHl$XHC919HLĐ}VHcIcHH9I I* *H AY$ YJ*hYjX*H$YJXXA$|AIA~IcH$L̐L̘H̠H̨19T$fl$T$fd$HcfAfA fYYXf YXf YXfffTfUfVffTfUfV,*H 9|f|$`H$LL$PAXJH)E^ HH\$HI 1I HcH HD$D=4a EMANAAEI<(DDWDODG LD$T$fDl$T$fDd$DT$LT$E(MfWWHcWHFDW%H(fE hpx *BY*BY*XBYWL*YXWXW*BYAY*YBXW*YBLXW*YXW*BYAY*YBXXW*YBLXW*YXW*BYWAY*YBXXW*BYWLT$fT$X*YfXAYXAA\ZffTfUfVfAfTfAUfV,fAID\$Ht$HL$M$tDD$HH4At t[]A\A]A^A_1ÐAW^ BF dF F AVAUATUHSDGLDO(GDo$HD$D$DD$HcT$IOMg A7DL$A_D$HT$H҉t$H)щ\$Ld$LqLt$L$LE_ Hc|$T$\$D|$H|$EDT$MAAOAI(EDDRDJDB LD$T$ɋ|$fDl$T$fDd$T$LT$E(MȉD%H(WW hpx HcHFWWfE*BY*BY*XB YWL*YXWXW*BYAY*YBXW*YB LXW*YXW*BYAY*YBXXW*YB LXW*YXW*BYWAY*YBXXW*B YWLT$fT$X*YfXAYXAA\ZffTfUfVfAfTfAUfV,fAID\$Ht$M$tDD$HI4FtIsLt$Ld$Lt$Ld$HD$L$y[]A\A]A^A_1fffffffffAWZ B B ~B AVAUATUHSDGLDO(GDo$HD$D$DD$HcT$IOMg A7DL$A_D$HT$H҉t$H)щ\$Ld$LqLt$L$LE_ Hc|$T$\$D|$H|$EDT$MAAOAI(EDDRDJDB LD$T$ɋ|$fDt$T$fDd$T$LT$E(MWWۍ@DW%WH(fE hpx HcHF*BY*B Y*XBYWL*YXWXW*BYAY*YB XW*YBLXW*YXW*BYAY*YB XXW*YBLXW*YXW*BYWAY*YB XXW*BYWLT$fT$X*YfXAYXAA\ZffTfUfVfAfTfAUfV,fAID\$Ht$M$tDD$HI4FtIrLt$Ld$Lt$Ld$HD$L$x[]A\A]A^A_1ffffffffAWLW ? $? > AVAUATUHSDGLDO(GDo$HD$D$DD$HcT$IOMg A7DL$A_D$HT$H҉t$H)щ\$Ld$LqLt$L$LE_ Hc|$T$\$D|$H|$E؋T$MAAOAM(EEREJEB LD$T$ɋ|$fDl$T$fDd$T$LT$E(MȉD%H(WW hpx HcHFWWfE*BY*BY*XBYWL*YXWXW*BYAY*YBXW*YBLXW*YXW*BYAY*YBXXW*YBLXW*YXW*BYWAY*YBXXW*BYWLT$fT$X*YfXAYXAA\ZffTfUfVfAfTfAUfV,fAID\$Ht$M$tDD$HI4Ft4tLt$Ld$Lt$Ld$HD$L$x[]A\A]A^A_1fffffffffAWIEWAVAE(E(AUATUSHLG_H$f 7 DODŽ$DŽ$$AA@AxIhD$M` $$$H$^IXlfYxpQ$$D,$DZZ$D$| tPL$EW%-F H$hE*E(D(DxDY䉄$D\$|EYD9$$DT$PD\$@Dd$0Dl$ ALAD LcHJ|HفH$H$DT$PD\$@Dd$0Dl$  11H$LcLc$NNN~Hc$Lc$IIcHT$pHIH)MHL]$H$MDLE߅ Hc$$YQ 9 19 8 9Q D$d ^ D5&Q D=D HL$hA(AY,HNL=D)A1AyEAEI D|$Dt$A;DD9A8}&fffLc΃CK%D9CCT|ދ$$1$9$HHfffffAWAWIcHJ$%* *BN 8AYAAaAi LcYIYXW*B YXW*B0YXW*BYB WI9*JYXW*B$YXW*B4YXW*BYAIW*JYXW*B(YXW*B8YXAIW*J YW*JYW*J<YXW*Z,YXXAQ fffffHL$pŃMKHc$$HLLH1ɋ$9$T$f\$T$d$T$fT$T$Ht$Hc((YlAYfXA(AYXA(YHt$fL$fX\ZffTfUfVffTfUfV,Af5;$fAT\$H$1DpD9}yDt$$\$WHcHcWA*$(HA*WIYǃYAWLD9*H*PYYJRA$|$C$1$9HHAAIcHJ$%N 8AYAAaAi Lc(YJ(YIX(YJ X(YJ0X(B(I9YJYBX(YJ$X(YJ4X(AA(YJYBX(YJ(X(YJ8XAAYR YZYb,Yj<XXXAQ II II1;$LD$T$\$T$d$T$T$Hc((YlAYXA(AYXA(YT$D$XY(TUVl$t$.B;$A|$LL$MpMD΃|$| Y$(19Ho@T$Ht$fd$$l$T$t$T$Dt$fWHc(A.^v Dt$L$HcDHʃHE L9DYAZD]EDBD_A,DDYYJfAAZD]EDZD_A,DfADE]A_,fAT,fA|9ʃ$LL$MpMD΃|$| $z|$| 19Ho@T$L58HL$ft$$|$T$DD$E(fWHcA(AY(AtW*(AY\Y((X.^vA(Hc(IHHA(AY LɃ9AY XYZ](_,fAIDA(YAAY XYZ]_,A(YAILfADAYXYZ]_,fAL,fA t?19(Ho@T$H-_7HL$fl$$t$fWHcA(AYC CT>C\>Dl$؃D$Dl$L$D\$D\$܋D$Hl$Hl$Hl$D$DL$D$HHD9L$}Lt$IF*[]A\A]A^A_1fffffffffAWAVAUATUSHGWw$_(D$H|$h T$LHL@ t$ԉ\$ЋWL$ȋH9L$LL$l$LD$Hl$L$rHcH|$DT$DXD$D$AA9D$D\$DT$D$Dt$AAHcD$Dt$DD*D *Dt*D|*H (t(|(DD$D\*DD(D+D$D)D)D\$DT*D\(L$t$D)D+\$DT$\*Dd(|$D+d$DD$\$DT(Dl*D+T$DDDT$ D|$Dl$Dl(D$DDd$D\$ |$ t$DDT$A \$ EDL$ DDA D+l$Dl$+L$A))L$D|$|$AA EAA A)A DT$C/ F B' ADT$D$Lt$HF0BT0Bt0L$D AF|0D$Dl$Dd$Dl$D9d$D|$D|$؋D$Hl$Hl$Hl$D$|$D$HH9|$}LL$IA[]A\A]A^A_1ffffffAWAVAUATUSHHOGw$DG(LT$P_H|$HD$QDI D$$Hi1LT$@t$0DD$,\$(T$ T$ D)Hl$8DL$4Hi  B<,|$L\$@McB I1MM9}Lc9C|D|$ D$ AD9|$ \$4T$AA)܅1FD9D$})LcDAC D9A*|׋L$A\$4\$,|$4Hct$$Dd$4A؉\$4AHE)DD$D|$ D$ AD9|$ T|$,%1H[]A\A]A^A_LT$8L\$LAfHD$HLHEAE1E9}vAfffDDDH)B4BDB4IcAHHcDA4Ӊ @4)DD$0E9|HIHIA]HcT$(LL$L_HT$8HL$D$Hc|$HoVHD$@H|$@H|$@*VfffAWAVAUATUSHHDO$H$H$H$L$D$`H$HL$HOH$BDL$pAp(DQ Hi_D$d1t$lD!Hl$xHi \$hYDT$tlDAA=,McL$C dO,v1Lt$P9O }Lc9C|Ht$P\$ H A<2H KdHf!> L$*Y5f C4L@\DHcL9J*\DJLcD$DYDYfHf@(YfD(IfDpfDP fZL$D$YfHf:fDJfBfDB fDz(\$tffffff\$YDYYt$AY։AYD*DY5e DYHf= X*ʉ AXY e AYH@EYDY\$XHcA\DJ*AYAXEYAXHEYfD=< AXDIfDh(fDHXfDP DYD\f:fBAXfDBAXEDl$fDpfD(DL$DYfDJfHAYDD$fDB AYfDz()aYHL9DYYDYYT$DYYt$AYDXEYDY\$EXAYXEYDXEXEYAXDXEXAXD)DqQAE9[]A\A]A^A_1fffffffffffAWAVAUATUSHO8DwLDOHHG@HW(L0HL$O\H_ E9HD$LoHT$G`D_PDWTHohL$PHHD$ AE9;H|$Ht$IcLd$H\$HDA$$D$A9Ή$Lc$L$Lc$H$L$H$L$L$L$F4CAƉ$D$D$D)$BD$CL$`E$fD=  Lc-L$X$$$Tf$t$D$D$D$#$pAED+$AED+$EOAPAHH$xEAzAAAD#$A#$ADZE!AZxAZpZh $#$pAKAAE$ E:AHH$xDZ0DZXDZ`DZh $$A!҉E)A҃AAA!ʉIcǃL$A!IcA)!I4E1A9H$ $9$ 1$9$ GLc$L$ Lc$ L$H$H$H$L$ L$H$GF4L$ BF L$B AAD$ ;$ D$ D$x C-A+$ L$`҉$ fD=T  Hc-H$ $| $$$t$D$D$D$ #$pAED+$AED+$EOAPAHH$xEAzAAAD#$ A#$ AZ8E!ADZ@AZhZp $#$pAKAAE$ E:AHH$xDZDZ`DZhDZp $$A!҉փE)AAA!IcA!ʉA)H$A!IcE1L$D$A9Lc$fD5F L$Lc$L$L$H$H$L$H$H$G$C C4FAD)HL<LcAEL$$D$$D$D$A鋼$Aً$D$E*EYE*EYD$AD+$WAEAEAEFDEY+$AAYEEEAAAAAEEYэsAE!EXAуXAAA\!D#$A!D#$$AYA\E!AD$3A\EA!ӉAXE)E\AA\DDC\AAՃA!ADD\DH$DD\!A)HcD!D\L,DE\E1EXDA9AXIcEXD\I|IcIcMcH$HcIcH$C LcL$H$E,HA4H$H$XC A EL$McHLcH$PC E AHIcH$HLcHcIcH$XL$L$PL$HH$ZH$BZL$BZ L$BZ$L$YYYBZAYXZYXYZ IcAXZ$YYAYXBZXAYBZ XBZYYAYXBZ XYXZ AYXBZ YAYXZXYXZHA9AYXAYXZA$D$$D$$H$M<$D$D9$_D$D9$(Lc$fD5v L$Lc$H$H$H$L$L$L$L$B B4L$@G#Hc݁EH$X$T$$D$D$A鋼$AD$$E*EYE*EYD$_AD+$AEEAAEFDEY+$AAYEEEAAAAAEYADzE!ʃAAXAAAAA!!XE!F$:$\AD#$A\YD#$A\EEA!ӉE)E\AA\DAXDډC\ED\DD\H$AA!AE!HcD\D)D!DLIcMcIcH$XHc$H$0C IcfD$$$fD,$HLcL$PH$C E,H$HH$@A4HA EH$C E AL$8McHLcIcH$LcLcIcffL$H$XH$L$H$HB**YY* B*$YL$PH$@AYH$8L$0fEX*YB*YX* YIcAXB*$YX*AYAYX* YXB*YXB* YAYX* AYXAfEXB* YXB*YAYX*AYXHA9XAYXffTfAUfVfAfTfAUfVD,E$qD$D$D$D$$$H$(M<$pD$D9$pD$D9$prLc$' a L$Lc$pH$L$L$L$L$L$H$B4C L$CXfC$L$8YH$ AYAYXfCL$8H$ XYfC L$8XfCYM,:fCLcAYYAYXfA I9XAfEYXfC L9HYXfC AYAYXfAXYXfCYXAYX\$hffTfAUfVfAfTfAUfVD,AA9G,4$D$$D$L$ $  D$ D$$$$+$ L$H$$L$$L$B $$CHIt$+$$H$Lc݁EL$$$$$ D$ D$$A鋼$$AD$t$ E*EYD$ A*AY_AD+$AAEEFD+$EEYEYAEGAADjE!ʃEEYAAAEXA!!AAE!F$*$$YA\AAAD#$tD#$tXA\EAA!EĉE)AәACA\E!EA\AXDL$E\DDA!AD)HcD!D\ItDD\DD\E1A9AXMcD\EXJXfC$L$8YH$` AYAYXfCL$8H$X XYfC L$8XfCYM,:fCLcAYYAYXfA I9XAfEYXfC L9HYXfC AYAYXfAXYXfCYXAYX\$hffTfAUfVfAfTfAUfVD,AA9G,4$D$$D$L$P $L D$ D$ D$ $ D+$ H$L$$ŋ$L$AAL$HIƋ$H +$ AA x$fD<$LcŁL$ $ $$$ $t$ A鋜$ $$AD$#$pD{+$AE+$HH$xDgADBAE!EADZAD~ZxA!!ZpAZh $$E!AF,D#$#$E#$pF$>HH$xDZ DZhDZpDZX $ $$A!ӉуE)AәAӉE!EA!H$HcE)D!Hc1H<9IcIcIcH$@ IcLcH$ C $fD $H$8 H$0 HA4C #H$`C L$ G\%LVCMcHEL$( H$XCD ELLcHIcLcH$PAC"IcHLcH$HH$`L$@ HfEL,8M$;H$XL$PfCfCL,8M$;H$8 L$0 YfC AYfC$L,8M$;H$( L$HXYfAYI?XYfC M$;L>XfC$L,8YH$ AYAYXfCL,8H$ XYfC L,8XfCYM$:fCLcAYYAYXfA I9XAfEYXfC L9HYXfC AYAYXfAXYXfCYXAYX\$hffTfAUfVfAfTfAUfVD,AA9Gd5$D$$ D$$L$ $ $ D$D9$ $9$ mLc$: : L$ Lc$ H$H$H$ H$L$L$L$FHEL$ H$8CD ELLcHIcLcH$0AC"IcHLcH$(ffffH$@L$ HfEL$8M,;H$8L$0fCfCL$8M,;H$ L$ YfC AYfC$L$8M,;H$ L$(XYfAYI?XYfC M,;L>XfC$L$8YH$ AYAYXfCL$8H$ XYfC L$8XfCYM,:fCLcAYYAYXfA I9XAfEYXfC L9HYXfC AYAYXfAXYXfCYXAYX\$hffTfAUfVfAfTfAUfVD,AA9G,4$D$$(D$,L$ $ $ D$D9$ D)D$D9$@ Lc$f5 5 L$@Lc$L$L$H$H$L$@L$H$C4C L$FH$Ɖ$8B A‰$<;$<B4D$B$8L$HM,A+$8o$fD<$LcL$$$\$X$t$XAD$X$\AD$#$pAEGD+$+$EWAHH$xAADsE!DFAуDZAAZxA!Zp!AZh $\D#$#$AE!EF$3#$pBHH$xDZDZ`DZhDZp $X$\A!ӉE)AӃAAωE!H$EA!HcD!E1DH4D)A9McJ2(H$$H$2H$$,H$1D$D$E9Lc$L$Lc$H$L$H$L$L$L$L$BC<‰$$$)$BD$CDED$f= LcL$DDD$EDAAED+$AD+$EA*DA*EAAEQADAD#$L$AAIcLc1E!Y A!MD9Y- C4 McMc\Lc\CACTLcCHcfBLfDDLcHcYfTDYH9fDYYDXXDYYDXA,CuL$D$D$$Lc$H$LރH$$H$u*$D$D9$D$D9$Hc$H$Lc$L$H$H$L$L$L$L$C4B L$BGL$$;$$D$C C+$L$҉$f= LcDL$D$EEDAAD+$ED+$DEA*EAEQAH$AD#$AA*McAMcN1A!A!9Y+ Y-# C4 McMc\Lc\CACTLcCHcfFTfDLHcDYfDdDYHcH9fD\DYDYEXEXDYDYEXA,BnL$D$D$$Lc$H$LރH$$H$'$+$$$+$L$L$L$L$$CT$Cf= LcDDL$$ffDEEAA+$ED+$DEEA*AAEEQAL$AD#$*AAHcE!McM1A!9Y Y- C4 McMc\Lc\ffCACTLcCHcfFtfDlHcDYfDDDYHcH9fD|DYDYEXEXDYDYEXA,BnL$D$D$$Lc$H$LރH$$H$q%$D$D9$"D$D9$Lc$L$Lc$H$L$H$H$L$L$BC BL$$D$$D)$BD$ED$f=% LcL$DD$|DEDDAED+$AD+$DA*AAEDEQAL$AD#$AA*AMcLc1A!ODA!9YH Y-@ C4 McMc\Lc\CACTLcCHcfBLfDLHcYfTDYHcH9fDYYDXXDYYDXA,BuL$D$D$$|Lc$H$LރH$$H$"$D$D9$rD$D$H$$H$R H$$H$1H$$H$fH$$H$iH$$XH$SH$$DH$;qH$$pH$謾nD$D$E9Lc$L$Lc$L$L$H$L$H$H$CC ‰$$$t)$tB D$tCL$ED$tf= LcMDDL$hD$EEEAAAD+$ED+$DEDA*AAEQAH$AD#$A*IcAAMcL1E!A!9Y Y- C McMc\Lc\CACTLcCHcfFDfD|HcHcDYfDTDYH9fDLDYDYEXEXDYDYEXA,fAsmL$hD$D$$Lc$H$LރOH$$tH$c$D$D9$D$D9$Lc$L$Lc$H$L$L$H$L$L$H$FCL$`C L$A‰$BD$;$$CB  +$L$$D$f= LcMDL$XD$DEDEAEED+$AD+$DAA*DAAăEQEL$AD#$A*AIcALc1E!MA!9Y Y- C4 McMc\Lc\CACTLcCHcfBLfD\HcYfTDYHcH9fDYYDXXDYYDXE,fEKtL$XD$D$$Lc$H$LރH$$H$a$$+$+$$L$`L$$L$`L$L$$C Cf=p LcDMD$L$PfffEDAAED+$AD+$EA*DA*DAƒAEQAAD#$AH$EAIcLc1E!Y A!L9Y- C4 McMc\Lc\CACTLcCHcfFlfDdLcHcDYfD|DYH9fDtDYDYEXEXDYDYEXA,fCSmL$PD$D$$Lc$H$LރH$$H$^$D$D9$ D$D9$Hc$H$Lc$H$L$H$L$L$L$B4C CL$Ɖ$D$$D)$B D$ED$Lcf= ML$HDD$DEEDAAD+$ED+$DEA*EAEQAH$AD#$AA*McAMcN1A!A!9Y Y- C4 McMc\Lc\CACTLcCHcfFLfDDHcDYfD\DYHcH9fDTDYDYEXEXDYDYEXA,fAKmL$HD$D$$Lc$H$LރttH$$H$\$D$D9$ D$D$H$$H$lTH$$H$KTH$$tH$-TH$$H$ TXH$$DH$UH$$|H$jfffAH- EJc\HD$AD;$D$ELc$fD L$H$H$McDH$H$H$L$F$B韬AWAVAUATUSHO0DHH_(HG@T$HW8t$HL$OLLw H\$HD$HoHT$G`_PA9ωL$D_THHD$AD;|$rLD$Ld$Mc׋L$T$LL$H|$CC DD$Lt$AG9F AʉT$ʃΉDd$);t$HN,0Ll$A}DND9}dAfffDDD Aƒ)EHH(D!DHDA)у E9|HD$@8Dd$AE9DC4 B< HH (DHD,D DHH(AHADD ɃHL (AA@@HBE DރHL( HA F  DރHH(AAHE D DރHL (AAHBE DރHL(HA F  HH(AAHE DDЃAHE 3AAE D;DA E9F,0,D;T$DAHN$0Ld$A<$ADDD Eك)AHH(DD!DHDA)у D;T$|LT$A:AD;|$[]A\A]A^A_AWAVAUATUSHw0D_HH_8HG@Ho(HO Ht$wLHWhH\$HD$H_Hl$HL$؋G`A9t$DPDwTHT$YHHD$fffAD;\$>Ld$Ld$IcH|$LD$H|$LT$LL$A ADAD A H4H HcCEH H H I IE9~D$ AIIcAI,|A9AD;\$[]A\A]A^A_1fffffAWAVAUATUSHW(DgLDOHHG0L@L_ HT$HW8LoE9HD$oPG`_THT$LWhLc AE9LD$H|$IcHt$MMAD|$Lc$H$(H$H$DElD}DL$ L$J<uXH$D$$H$}|]dD$D}p$L$$L$$ux$UhD!D]\H$H|$ DU@fB0$#$$D$LcDE0D$HH$GGffXf`fh$HH @LcJ<ˋ]tLHO19iMcMcMcE1$L$  D$ $*B JYD*B RDY*AXB ZYIcD$*Y$XH$XD!HYAXTATf$T$ #$$HH$ffXf`fh$HH@H$LHc9HHBa$H1ҋ$P$P9*L$@Lc9fCc| fffffA~ DAA|$i|$ A!A< A A!D$D$<D9$T$$D$8D9$1;$Hfv f v 1LcLcfCd$Pff;$HL$KDffTfUfVffTfUfV,fC W|$$P9$ $$X9$$`L;l$(tL~1HH[]A\A]A^A_H$H$DM@D]lLc$H$(H$ H$D$D$$DM|NDFD~A*E*YDYA*E*YDYIcAXAXYYXAX\A\KIcHcMcMcH\$0DzHD$0D:B4BB RD$A*LBYA*YD*E3A{DYGA9ȾD}Hc;$@L$I|E+OLA+PDŽ$A$@DL$EOxD$HD9$AwtA|Lc$L$8DŽ$ $X$0$K,AH$HL$9$ H$a$0+$  $4 D$DŽ$D$4D9$Hc$ HH$$DD$D$4D+$ADD$AD$EGx |$DŽ$|$ Hc$EgDEGdH$ AL$L$L$D$L$D$EglEG|H AopEw@$AXH$DL$DL$H$EOx$!D$EwhHAw\L$fDIcA_tD!$DHH$$Hˉ$ffXf`fhDHI Hх{IE1fffffHcD$D$IctD*4IctYD*IctDY*E1Hc$x$|McAʼnK<EIc|DYJ4EIctDYJ EXEIcL DYJ EXE H$NLDYMKNEXN H$CY$AYMcAYJACY4H$D$\AY,XXXYXBXB$d#$`$lHH$x$t#$h$pHH$AMcN `hp MD;l$0LHcMc,Ic\Mc\Ic| D$Md L I, L<CYL$McCY AYCY$XXXYCXCL$H$L$H$pH$H$Hc$Eh@E1_DE|$XH$L$IL$Hl$HhlxxD|$E,IcLDYH9K|Lc$$HA4H>H49H$DYAYDAY$DXXAYYXBXB$,#$($4HH$HhD$<#$0$8HH$PLcK|`HE9.H8|LcL$Oc OcdDD$dM I4 IcBYY X L{LcH$KcA AA AH$H$DmXE\U|L$L$udH$(MlD$Dmp$Lc$H$$L$0uxD!艌$ $HH$0T$ Jߋ}hLcE(DU@L$(H$8L$8\$ ffX$D$$!M‰$HDuDIfBLc$HD$ DH<@LHcIcMDMtIA1D9LE1f $ IcL$ $ $$EYAYD$ $YD!HH$0YXAX A ffXT$ #$H$8Hf$$HH@H$(LHcD9HH;f(AYYLcYYXCX C D$MD$D$LcMcHcHcL4$Ll$(L$ff fD$H\$(D$fF,D$,YYHEYHDYfBfDA&AA AH$H$DmXE\U|L$ L$udH$0MlD$$Dmp$ Lc$H$($L$8uxD!艌$$HH$8T$ J}hLcE(DU@L$0H$@L$@\$ $X$$D$,!MDuDHIBLc$ D$(HDH<@LHcIcMDMtIA1D9LE1 $(L$ $Ic$,EYAYD$ $YD!HH$8YXAX A X#$H$@T$ H$$$ HH@H$0LHcD9HH;(AYYLcYYXCX C D$MD$D$LcMcHcHcL4$Ll$8L$ H\$8D$D$F,YYHD$<EYHDYBDDT$E$GL}E5 D$D9$D$H$D9$D Ȩ $dE1A9+IcAA|E AA|EE A9~H$L$L$H$A.E^B$9$AMA8L$D$d$^ EVEN L$pA9$|$hAx\$lE~AX|$tD $dE1A9$QIcE؋T$tEELDED$lDE|AAt$hыT$pA‹D$dADD$hDA L$tT$lAA΋L$pAыT$dADAtD;$_$9$$H$H$H$EENA9$D{1] EFE^ D|$PD}mt$TD $dE1A9$ DDEEDIcDEDE\AAL$TЋT$Pt$TDDADAD$PADALD;$~E1D;$Hc$0Hc$<D$dHAHD$xH$D$lH$fH$HT$xCCDL$L$fWLH$AD;$$Mcʼn߉DDEEG|B)H$GL$HH$H$HT$xQH$T$p$l$tAAы$x‹$|$Ƌ$tы$hAAэ4 $p$lC4AAы$x‹$|$hA$CToOH$f$[H$Ht$xCCDL$OL$f wJ$9$$H$EEFEVA9$D>^D^n D T$dE1A9$IcDDED EETDAADA‰ADADADALD;$~zE1D;$iHc$0Lc$<$dHLD$XH$$lH$fcH$H$CHT$XH$CDfWHH$AD;$$McD߉EDGLEG|B)H$t$hHH$H$HT$XQH$TT$tDˋD$lAAыT$p‹D$d|$hƋD$l4 T$tDC4AAыT$pA‹D$d $CTOH$f$H$Ht$XCCDL$OL$f wLD$D9$$H$EENA9$DDXhD E$dE1A9DMcDÉGDGL AAADDDAACCtA9~E1D;$Hc$0Hc$<$dHH|$HH$$lH$f]H$H$AHT$HH$ADfVHH$AD;$$H$IcDT$4DEEEDE\B) H$HHD$8 H$HL$H HHD$8LDDˋD$PAʋL$4L$Tt$TDA DT$4DAʋL$PAD$AT|$4|$4|$4H$OL$4f$H$HL$HAADH$OH$fNHE1D;$Hc$0D$dLc$<AL fH$f$H$L$AADfBDULH$AE9!$L$IcA,A|B+H$AH AA B SAL$A,A|9ADOH$fD}$)H$OL$AADfB WLH$D$D9$$L$EA9$EEOD ب$dE1A9IcEAAtEDEډDAAAEAAtA9~E1D;$Hc$0Hc$<$dHHD$@H$$qH$f$AHT$@H$fQAADAH$H$H$D;$$H$IcDL$4DEED ETB) H$HHD$8 H$HL$@ HHD$8LDD͉AʋL$4AAA DщT$4DAD$|$4AT|$4|$4H$OL$4f$HL$@H$OfNE1D;$Hc$0$dLc$<H$Ht$lH$f$H$fB~AADAH|$H$H$D;$:$H$IcDDEL EDB+H$AHH$B{\DAэ DAADA$AtH$Of$OH$fB4zE1D;$HHc$0D$dLc$<AH,?bH$f$H$fB^AH$H$E9AAD$H$IcALDEDAB*H$HH$B[H$TAAʍ3DAAٍ4 $AtH$Of$ OH$fB4ZL$ADOfEHcH蜯HH$(`H$H9$t H$Lf$LcHcIfvfnfff^ fDF(A9$dfD7fDfDgfDofDW fD_(fDO0D Ȩw $\E1ɃA9ffffIcAAYA9Af\0AYfDD8AYXAYXAYXAYXAYXAYXAXAYXAAYXAYXAYXAYXAAYXAXLAL 9$dLcJ<+D$,$9D$,D;$\Hc$,fT$Xf\$Pfd$HL?1;$H$p}JIcLH$HcH41L9}fHcf)Y,H9X|;$|f.Q$fAMEUMBAD;$\HA*A4UD;L$0}5Lc$,K $A}IB AD;L$0*HA<|1;$}Hc$Lc;$IK|E1D;$n}*t$0$HcA1AD;$nMTHM|D$XD+$oAD9$$}Lc$@H$J ^H$$`$DŽ$`L$L$$X9M$`$$9$$KzH$$`;$ $,9$ )Hl$xH9$ht H$h菥1Hĸn[]A\A]A^A_ÃAL$ADL$$dW D$ff/fgD9L$,A9$dD Ш$\E1ɃA9IcDAfD\f\A9DYADYYYDXDXEXEX\EE\~m$dL$L$g D$ff/fgD9\$,A9$dD ШE1D;$Hc$($\T$Lc$,f|$T$ft$T$fDD$L $qDYDYAXAXLf.fA. fAfC^MMAD;$AJ,L4.N 6t$|L$L$I $ H$B(A)9t$tDd$4 D$Lc$D+$8MD|$pALd$`D|$lD$tT$|9T$t L$|+L$t$9tHcL$tL$$HIH$L$H$ L$L $$ L $L|$`|$pK4MLM1D9}OL$H$A A4$DE'**A*A*HcD9A A|1;t$4}dE1L$AD5IcDD$|;t$4HcALL$$*ALE$I4A*E$O*A,$A*A |1;$ }ADD$4EIcLdB;$ LcN$MdO$LdN$MdO$|Ht$`|$pD$0$N>MM9L$0L|$(,AHc$D+$Hc$D$AAHHD$@DL$LD|$\HT$PfD$fD$1f$ ;t$LfD$(fD$0fD$8fD$@D$D$fD$H$LD$(L$fD fEf{fAsfDStHcL$xLc|$|L$ AH$HcfA\ f|fAtAAEfEDCxfDT Y$AYMAYۉDHAT5Hc*H D*Df$;t$LYY$Xf$AYXAYXXAYAYXAAYXAYXf$A$f$YAYXAAYXAAYXXXAYXXA\fD$PfD$X1fD$`;t$LfD$hfD$pfD$xfDDd$ D\$fD$DT$fD$fEffArfDWHcD$xD$Lc|$|AHfL$ fT$AYAYYfd$YAYXAAYXAAYXXAYXXXAXLL$D$L$D$jfA fCyID9fD$AYT$ EHcf|AfEDfAtfDT fAl YAYXfD$AYXAYXXAYXAAYXAYXAXT$D$T$D$|,sfA fCyID9 ;$ HcT$|L$*H$fAMM;$HcE8AD5fE,fDD\$GLeCL$ D$EHD$ȋ$DAA,GEFA\ED$Dt$AAADL$t$AAL$΋L$ыT$ D$1L$AHt$@AAOfHL$LD$@D$OT$fA4Ht$DL$|$DT$A\$D\$D8l$B F A<EA4|$D$ MMMLd$@D\$D9\$ D$tAEeE.Dd$ D,$AL$\$EAB#~ZL|$@fAHl$HLT$LL$LL$PL$NtULt$HD$L$T$9T$6Hp1[]A\A]A^A_L\$@OfAffffffAWAVAUATUSHHLcWHFDf$,N+$,n L$H$Lc^$ID$$D$D$$$L$HOL$0D)ID$(L$ Lt$ HDŽ$L$Ll$(D$Dȉ$$Hl$ HHHD$|$@$,$(=$,1$(9}HcL$DL9E |DŽ$$9$|"/$D$D9$$+$$tHc$H$E1HHHt$D;l$H$H$}McAD;l$L$ C|Dd$ EfHc$Lc$Dt$ H|$HLD$@Dt$DŽ$D$(D9$H$H$$H$DŽ$$D$,D9$HH,AH$Lc$Hc$$(Dl$ALT$8Ht$0$Dl$Tff$$,$+$L$L$L$HI4D$$Hl$0L$Lt$8L$$HDnAPD$F$AKH$ H$HL$8OLL$hBBDLD$hfIc$ D$$AAA$DL$ AATAL$AAOfA$Ld$8H$ADOfF4fE1D;l$TvH|$0DD$HAiH$f$DLt$8L$fCtH$ AIH$E9DD$ A$Eދ$$ EHl$8L$ D$D$EiH$ DIcAA$EG4DT H$ADOfD9$DHl$8L$ADOfE$jE1D;l$TH|$0DD$HAkH$f$DHl$8L$fAjL$ AIH$E9AADE1ڋ$ $HL$8L$ D$ H$ AAI$D$IcADG3DT%L$ADOfE3$DL$OHT$8fA WL$ADOfEHcH.HH$\HT$ H9$ t H$ P.ffffHc|$HR.H$ H$ AWIAVAUE1ATUSHHnFHc~D$tD$pDFDN HcsL$;$nH$x$ IWHCL$<L$3HL$hHE1L$`f L$8LT$XD$0D$4$ $H$PH$H~ f AAA*^D$tDAL$81D9}*H$xHcH$`D9* YL$8 |փ$t$4@Ã$t ب' $t19}-1H$8LcL$hLc$49JK|Ջ$t19}&L$h$tLcO9HM\|Lc$tH$hDŽ$Lc$4$4$t)$4)$0D$ NL$D$nD$$pID$4$ M$0K HcL$HEADL4‹$ D$DT$$9$L$HT$ I4GHt$(| C$$ 9$($ +$$ntHc$L$PE1Hc$ HIHt$(D;$tHT$PH$@}ZL$$IMDT H$h1IcD9H<}%1HcLcA[$ D9*B|MAD;$t|E1D;T$$DŽ$\}1McAD;T$$K|H|$PDŽ$HI)L\$D\$ D9$Lc$L|$HDt$ HT$D$HD$PHc$\H$h$tAD9$H$L BLH$`LD9t$L$ILL$$Hct$L$E1A9MAAEDAGH$L$8H$D$L$$M$D$D\$$$$NH$D$D$D$|$ H$$t$,EPDL$(l$0\$AAEDAGDL$8H$E1H$D$D$D$M,L$H$D|$H$(D$AlE$Dt$DT$D$GTME]ELMD;$HMDEOT$pIM$;ffffH$fKLMAD;$DDAEDH$DEAAA,@H$ T$|$t$IcDAыT$ADA։L$ $x|$ ‹D$AH$ t|$|$ |$ O|$ f;OH$f SLMAD;$$t`D\$DT$EE($xDl$AFt C,L$ McC,ACT5ffffffH$(L$L$L$8L$N$SL$(z$D$D9$nL$L9$ t H$ 1H[]A\A]A^A_ADOfDaHcHH$ H$ `fffffAWAVAUATUSHxBf H$pD$H$H\$p~f-Yȃ1*^Hc*Yك~HcNHcWnDN DFHFLwHHD$D$$$H$ HcH$H4$Hl$d$L>DŽ$N $l$`NJL,N$.N<&$L$H$L<$$9$HMLT$h |6$D$D9$ $+$$tHc$Lc$1H$D$dHL$MHHT$hI *D$H$LH$LH$I9H$L$E1fffffIcHcD$DtU;t$A*L$$$D$D9$H$H9$t H$=1H()[]A\A]A^A_,IcDT$`D\$PHfL >@IL9!Шt ffffA0IAM9!ШuM9LwA0ApII9vM9s A0IM9rHIAu`ffffffATUSD~TLcA1D9}?AHft(LcօI:~E1AfffffIcEÀ4AuD9|LAu[]A\fffffffAWAVAUATUSH f $$!L$D$H$!Ll$`~fYȃAHFDvAD*D*JA*^D*BH$HG*z n NEfWD~H$AF$$$=DYDYDYYHc苴$L D+$DŽ$$K<)9$M9$Dd$XK,$LcT$XO+$j$McL$T$\L$$!$L$\D$D9$EL$\$ !tHc$L$L$IH$$K MLM1;t$XH$}-E1IcLcD$B ;t$XBTF<G||֋$tL$FdFdGDGDH$$DŽ$$N<MM9$LT$Pʉ+$Lc$Hct$XT$Lc$fDt$T$fD|$ƒLL$hHt$xT$tffffLd$PAFE1L$A $LLA9M*wA*kT$Lc$fDl$EEAfffHH!H AYAYAYYXXXA\fD.fA.HH H ATADAI\LACME9IcHIAYAY**H H AY**XЉTA2XˉTYXA\fD.fA.r=HfffffHH%,fff,E9T$ft$AAADAALME9|IcD>*\D*,AYEYE*dE*EYDYD|AXAXAX\f.sf.5ADAALME9|fD$EtHt$xDLDLHT$PD|$tIH$D9$LHMT$P$IHL$h$LH$9$HT$P$L$\D$D9$$$ !!9D$ !D$D$$H$NL;l$`tL1H []A\A]A^A_,Hc|$@DD$0HDL$ DT$HIf|$@MfDD$0fDL$ fDT$+D$$$H$DPffffffffffAWAVAUATUSHf L$D$Ll$h~fYȃAHFDvAD*D*JA*^D*BH$HG*z nNWD~ H$AF$$$=DYDYDYYHcD$LAADŽ$$K<*D|$`E9$I4:D$M$2#$mIʉT$dHHD$x!$L$dD$D9$L$d$tHc$L$E^Lc$E1IH$E9L$K,H$}3E1fffffIcMcAB D$E9BL *BL|LT$xDŽ$D$`9$LT$X1Hc$T$Lc$fDt$T$fD|$Hl$pLT$XAFL$EED$ME1MA9*w*nT$Hc$fDl$EEAfffHH!H AYAYAYYXXXA\fD.fA.HH H ATADAI\IAC IE9IcHHAYAY**H H AY*A*XACXATYXA\fD.fA.r;HfffHH%,fff,E9T$ft$AAADAAMME9|IcE8*\D*,AYEYD*dD*EYDYEHHHffff,fffff,E9AAK $"ADfAAHIE9Ic>A*\E*,AYEYE*\A*4EYY|AXAXXA\f.sf.ADf5fAAHIE9|f|$hHT$`LH$H$ML$IH̓9$LA*\E*$AYEYE*\A*4EYY|AXAXXf.sf.ADfAAHIE9|L$Ht$hL$DL$XMD9$IHMD$DT$E$BT-t$DAT$F|5DAL$D|$F|%T$T$Dt$D$ AL$AAA΋ $ыT$T$D$1L$ADTfffDL$D9L$ Et$D\$DD$4D+D$lED+|$ DD$A}ED$T$E $AȋL$AL$tЈT$$DT$D\$AIIt$IHAB FD$ D0EEDAEjD|$D|$ MMI)I)I)D$49D$ AD+d$ A?ED$T$EMAȋL$AL$tЈT$$DT$D\$AHt$AB FD$ D0EEDAEtLl$Dt$Ll$D9t$LMl$D$Ld$\$0Ld$89\$Ll$D$L$T$9T$ Hp1[]A\A]A^A_Ht$Ѓ|$H,HMDT$4D+T$lDL$AADLL$$DL$$kfffffffffAWAVAUIATUSHXnH_$D$Dv DfD^$DWLNH$PH$PL$$4H$xH$H$pH$D$HDŽ$HDŽ$L$ L$D$@D$DD$<D$8L$$`$($D$lD́@A ب$19}(1L$xLcL$pLc9KK|ڋ$19}(H$pD$LcAJ9HH\|Hc$H$pLcH4ȋ$H$$N$=$1$9}LcL$ CT9C|D$D$DŽ$,D$4D)D9$,$|#ff$,$49$,f$4+$,$tHc$,H$E1HH$D;$H$H$ $@$+$Lc$<DR1;$L$pIcI|}H$Lc;$D>F<|1;$}51$H$Lc$4 ED;$HD|1;$}+D$D$IcBlH,;$|D;$|H$LE9HM$H$AD;$E1D;$DDŽ$d}McAD;$DC|DŽ$0D$@D9$01Lc$8L$Hc$dH$pDŽ$H$$9$L$ LL$M$`tH$H$A2CCDL$L$LD;$H$F$Mcʼn߉DDEEG|B)H$GL$HH$H$H$H$T$P$L$TAAы$X‹$\$Ƌ$Tы$HAAэ4 $P$LC4AAы$X‹$\$HA$`CT]H$@0_$9$$H$EEFEVA9$lD~nD^ D 2$DE1A9$IcDDED EETDADA‰ADADADAALD;$~8E1D;$'Lc$4Hc$($DLD$pHD$h$uH$@>$`tH$HT$pA2CCDLD$hL$LD;$H$$McD߉EDGLEG|B)H$$HH$HL$pH$H$T$Dˋ$AAы$‹D$|$Ƌ$4 $DC4AAы$A‹D$| $`CTH$@0D$D9$$H$EENA9$lDjDRD -$DE1A9McDDGDGL AADADAC DACtA9~E1D;$Hc$4Hc$($DHt$XHT$P$vL$4H$ш$`tH$HT$XA2AADH|$PH$HD;$H$$H$DT$4IcDEEEDE\B)HHD$8 H$ HD$XH$ HD$8LDD͋D$`ʋL$4L$dt$dDAA DT$4DʋL$`ADA$`AT|$4D$4D$4H$D$D9$$L$EA9$lEEWD ب&$DE1A9IcEAAtEDEىDAAAEAAtA9~E1D;$}Hc$4Hc$($DH|$HHt$@$sL$4H$ш$`tH$Ht$HAAADH|$@H$H$D;$$DL$4H$IcDEED ETB*H$HHD$8HT$8HT$HH$HT$8DDDAˉʋL$4AA DщT$4DADA$`|$4ATD$4D$4H$E1D;$Hc$($DLc$4HT$$mH$ӈ$`tH$AAHt$H$H$D;$AAD^$H$IcDDEL EDB+H$AHH$A\DAэ DDAAA$`AtH$E1D;$D$DLc$4Hc$(AgH$ӈ$`H$A3AH$H$E9AAD)$H$IcALDEDAB*H$HH$AH$TAAɍ3DAAڍ4 $`AtH$$`H$AL$A[HcH谂HH$ L$L9$xt H$x`ffff$LcHcIfvfnfff^ fDF(A9$DfD?fDwfDgfDofDO fDW(fD_0D + $<E1ɃA9ffIcAAYA9Af\0AYfDD8AYXAYXAYXAYXAYXAYXAXAYXAAYXAYXAYXAYXAAYXAXLAL 9$DIcH<*D$,$9D$,D;$<fT$hf\$`fd$XLc$,fl$P1;$H$P}KIcLH$HcH41L9}HcfDDYH9AX|;$|\f.AD$A}MMC AD;$<D*HE ID;L$0}2Hc$,$EeIBAD;L$0A*HA |1;$}Hc$Lc;$IK|E1D;$n}*t$0$HcA1AD;$nI\HI|D$8D+$nAD9$$}Hc$4H$p$@D$DŽ$@L$L$`$8AD9M$@$$9$$$@*$ D$,D9$ H$H9$Ht H$Hx1HĘn[]A\A]A^A_AEL$pAEL$`AD$DAAAAAfff $ff/fg9T$,9$D Ȩ$<E1ɃA9IcDAfDlf\A9DYADDYYDYDXEXEXEXlEEl~hD$DAL$pL$`AAAA D$ff/fgD9L$,9$D Ȩ E1D;$ˋ$<T$Lc$,fDD$Hc$(T$ft$T$f|$T$fDL$$xYYXAXL\f. fA.? AЃC4IIAD;$EEDAUIcfTIYf\AC,D*DH D*DYAT$DAXΉT$HcT$E$E|AX \f. fA.ɸ,$9L$,f6fnfff^f~ fDA9$DfDgfDofDGfDO fDW(D Ȩ$<E1ɃA9DDMcfB\(AEYAfB|0AYA9AYEYAYDXAYDXAYDXAYDXAYDXGXfnfvfff^ fDF(U$E1ɃA9rIcAAYA9Af\0AYfDD8AYXAYXAYXAYXAYXAYXXAYXAYXAYXAYXAYXAAYXXLL D$0|$0tD;$Hc$f$f\$xfd$pLd$Ld$fL$IcH$L1fffHcIDLf)HY(HXyƃ~\f.ACEIIAD;$HA* aD;L$l})Hc$E*CIAD;L$lHA*|1D9}IcL LcD9N |1;$,}%DD$lEIcB;$,LTHL|拴$ HL$(DŽ$ H$D$D9\$4H$HML$(H$M$ D$4$9\$4HL$($ $$9$L$L9$t H$P1HHS[]A\A]A^A_IcHLc$,L$EED$ED$D$$H$D$H$D$D$MD+$D$$$$$$$D/$$$D$McA$AD$΋$Aы$A‹$AD$AH$@DD$ы$΋$$D$B<$$$AAA$$H$@$$DD$EEADAFDH$H$D;$*D$<D+$D$D$D$D$D$D$D$D$E9D$$$DD$D$EEEڋ$$$D]$$McD;$A$AH$H$A$D$$AA΋$Aы$$‹$$ADL$@D;$C<H+$H$HH+$D;$<ffffDD$$D$D$$$$McD$EEEL$$AAAE;D$D$A$A΋$Aщꋬ$$$ꋬ$$AH$@DD;$<B|$$$$D$D$ $D$t$H$D$l$DL$hD$PDD$XDL$TT$`Dd$\\$h|$LD|$dE2H$pL$H$hH$H$hD>Dt$|E, E43Dl$x|Dl5|$tH$L$pL$hD$H$hH$F DL$pB"E1D$\$lH$pH$E9lLc$,L\$8EEDt$tEDl$lDT$xT$|H$Dt$|H$Dt$pDl$xMD+T$TDD$HL$tt$tt$dL$|L$\D$pD/|$x|$hD$LD\$lMcAt$`ADT$T΋L$XAыT$PA‹D$DADL$HAH$@DDD$tыT$D΋L$PB<|$lDD$h|$dt$xt$\AA|$||$`t$pt$XDDT$LEDL$@AAADAGDH\$8Hl$8D;$D$<D+$D|$4Dt$0Dl$,D|$pDt$|Dl$xDT$tD\$lE9D$fffD$0|$,DD|$,DD$4EEEڋt$dD$4|$0D]|$hL$\McD;D$,AT$TAH$H$AD$LDD$Ht$`AA΋L$XAыT$0T$P‹D$4D$DADL$@CfffL$HA$H$IL$HAD;$<EADA\$DA8IcAADd$H$@AAD$A$D+kH$HEH$HH$HAD;$<qL$D$L$D9$LM$$L$D$8L$PD9$L$$$09$H$H9$@t H$@4;1HH[]A\A]A^A_Ë$ $@Hc$<H;H$@H$@t$0ۉ$,D$<D+$D$0AD$$AuD$<D+$AAbD$$D$0D$ SL$$M IMу$H$pYH$$L<IM$tH$HL$ A1HT$LL$H$HD;l$PBBDH$OHL$ DD$EEDADF4 $DEA)A$ADD$ADA$$AЋ$DD$DA4H$McB4$Aы$A‹$$A$H$BTH$@0Dl$TD9$D$AD9$D DD$E1AE9EE݋$$D$HD$ McADAFLL$AD$D׋$H$AB|$AADB|E9lfE1D;l$HXDt$AuH$@9$tH$HL$ A1H$LL$BBDLD$LE9H$HT$ DEE!D$DADB, $DAE$ADD$ADA$$AH$A4Mcŋ$B4$Aы$A‹$H$ $BTH$@0DD$TD9$D$AD9$D Ш|$E1A9L|$ D$D݉A$$G$McDՋ$L$$ALL$DAC4ClA9~+E1D;l$HDD$ArL$A6$tH$Lt$ AAH$LL$DDH|$H$E9݋$$$EDEEDڋ$HD$ AAD$͋$F$AH$,7IcD,$AAA$$DL$AATH$@(L$T9$D$AD9$D ب|$E1A9A1LT$ A؋$D$McD$C H$AL$LL$$DAFDDCtA9~&E1D;l$HDD$ArL$A $$tH$Hl$ AD5H$LL$Lt$L$E9DEEAދ$$DD$HD$ H$DAFAD$AD$A,>IcL$,$AAA$DATH$@(E1D;l$H|$wAL$AAE4$$DtL$Hl$ ABDH$LL$HD$H$A9DZD$A$$DL|$ DH$D$$GD$L$DIc$DEDE.EDL$AE1D;l$H|$rL$A.$DtL$LT$ ACH$LL$LD$L$A9D3E9D$$Ht$ H$D$H$EBIcD$D$DG;DDH$ HcHHHD$hHD$8H9$t H$Hc|$HH$H$rfffffffffffAWIAVAUE1ATUSHHhnC~$nL$`;H$XD$TD$PDFDN $vIWHCL$`<L$`3L$HE1f L$L$@LT$PD$D$$$ H$0H$(~ f rAAA*^D$TDAL$Hg1D9}*H$XHcH$@D9* YL$H |փ\$T$@Ã$T ب!$T19}-1L$LcL$HLc$9KK|Ջ$T19}&L$H$THc M49HMt|Lc$TL$HDŽ$Hc$D$TD)$$$)$L$H$D$H$LcMD$PD$(D$E‹$ID$T$,J<$n$ND$PL$$PHH$(9$HD$0|'dfffff$$9$B$+$$ntLc$L$0E1Lc$ ML|$0D;$TLT$pL$ }VD$,DTH$HIcH<1D9}%1LcHcG $D9A*,|L\$pAD;$T|E1D;T$,DŽ$<}1IcAD;T$,I|L+\$pDŽ$L\$ D\$(D9$Hc$H|$hT$(Hc$<D$HL$ H$HHL$pD$T9$L$HDL$ D9t$L H$@L$KHL$ LLc\$L$K4E1A9H$$TL$,IAIIALT$D$Ll$D$Ld$D$ʃ$L$|ffL$McD)K4`fD/fDgAfDwfD_fDO fD(fDW0f&H8fnf^f~fv fDF(Ld$ L$ $9T$A9 Ȩ.T$,E1҃A9ffffMcAAYA9AfBt0AYfFD8AYXAYXAYXAYXAYXAYXCXAYXCAYXAYXAYXAYXAAYXCXLCLA9OD$$T9D$D;T$,ffϩE1Lc$f%|1;$TL|$H$@fL$Mc}HNH$HcH41L9}ffHcf1Y4H9X|;$T|\f.)AƒAEMA<$MD;T$,*B<S}19}-1LcD|$,$G4!AMc9A*B |Ջ$<$TDŽ$<HD$hH$ t$(9M$<$9$$<$$9$Hl$PH9$t H$1Hhn[]A\A]A^A_Ã1AfD/fDgAfDwfD_DfDO fD(fDW0Ld$ ALc҃L$ f&Jfnfvfff^ fDF(T$4E1ɃA9McAAYA9AfB\0AYfFD8AYXAYXAYXAYXAYXAYXBXAYXBAYXAYXAYXAYXAAYXBXLBLD$|$yD;L$4Hc$fT$Xf\$Pfd$HL<$L|$fL$IcH$L1ffffHcIԺLfDHDYHAXyƃ~\f.dAAIA2ID;L$4D*E\d11LcD$4$C,LcȃD*Gd~Ջ$L$DŽ$L$Dl$0ÃM$D$,D9l$,$$L$D$9$L$L9$t H$C1HS[]A\A]A^A_H|HAH$H$*ffD$4E1ɃA9Hc$Lc$AAYAYXAYXAYXAYXAYXAAYXXL\ L$D$9L$D$kAȃBAMME9TA IcAfDD8f\0AYA B ALI *H AD*ADAYXAYXAYXAYXAYXAYXX \ {L$D$fL$D$T,KABAMME9TD$|$ ,f.ĸ,AWIAVAUATUSH$H$HH$D$L1H$H$HcAP~^nDFOHFDf $HoD$$H$ $$$ADŽ$$\$xDd$|$$HHʼn$$9$H$$|)P fffff$D$D9$, $+$$LtLc$L$D\$|ML$EL$L$~Hc$Lc$Hc$Hc$Hc$Hc$\$xD|$xDt$|H$L$H$H$AH$H$$D$Dt$D$\$PD$T$X$d$hD$8D$lL$D$pD$t$D$`L$D$@$<$0$,D$(D$$D$ $4L$CL$L$H$E:G4C, E)F$B $H$L$$F L$DŽ$9$L$D$LL$pH$L$EHt$pEDAEAA<D H$D$$$($$$D0$ $8A$0D$DD$Lc$$,AAD$0΋$4Aы$<A‹$@AD$DADD$ы$8H$$(D$ B<$$$<ADD$,AEA$$4DD$@EEDADL$C|$H$D$H$HD$pD9$FD$E$,$(H$HL$pD$0D$<DDD$44+$$$8D$DD$@$$D$ ELc$Fl%L$C>AAEDAG$L$$xD$|D$$$D$HH$D$D$$DD$($DT$4t$,l$8\$DDd$@M L$|$0L$AAEDAGDD$$E1D$D$L$L$AH$IcH$D;$HDt$$DT$ H$D$D$D$E1DE 0,7L0ID$x$PfffL$ ш H$@<H$L$AD;$DDAEDH$DEAAB,H$|$$AAIcDAыT$ ADAՉL$ $H|$ ‹D$$AH$tt$ D$  D$ H$H$L$AD;$$tRDT$$AA0McE$Ht$ EFl C,L$C,AB.tЈH$L$H$L$L$$D$D9$L$L9$t H$(1H[]A\A]A^A_HcH(H$H$fffffffAWAVAUATUSHHBf wH$@D$lH$H\$p~fmvYȃ1*^Hc*Yكp~DfDFNWHFD~ AD$Lw$$H$- IcH$H4DAAHDŽ$Dd$dL>D|$`N NJL,J,.H.$H$H$L$L$$$$HI9$Lt$h u}u$D$D9$ $+$$ltLc$Lc$1L$D$dML$L|$hML$L$L$D$L$ML$L$M9L$}|E1IcHcD$F4";t$A*L$H$$H$DwVD$H$Hl$ n$H^LGO L$ADm$$$H$D$)Ll$$L$_$=$$A~(f_@f$AAY$f$D19*^})L$LcH$9C*$YB$|Hc$Lc19N$HL$pODK4}H?Hc9I H |DŽ$$9$N$Ic+$)Lc$<??HD$(fEWIt$ESDOAD9|L\$0EAE E$ D$EH$A D+$A\$tD$$E1HDDDσH$A)A)H ?HD$LD4DD$$H$$ADAD)D!E!HAAH$E)D f$L$AH-F?H|$0McH$ABtA$FD!AAD$E!ADH >D D DDD!!DHL$0 H$ B,Bl$A$D!E!$ADDD !D!L$D CtD[DOAAE9HT$0Dt$tIcD$AH$DE)DD$ D$D$$D$L$AAHI$H$)HL$$ L$I $AL==A D+$AL5=$AAAHAD)Dd$ A)AE ,H$EAD#fff,f.Av~,AEE9A_DD}YIc*T*DA*\D*\_A_A__f.w,f.Aw,ALMD E9 T$A9t []A\A]A^A_1Hc\$؋D$1L 9H A*!A*yA',f.Avp,ԃATD9pHcA*t*T_*L_D*DA*l *\_f._A_w,f.Aw,ӃATD9~l$9Hc*\A*T*L___f.v,A[]A\A]A^A_1,鐐AWHcHcDHHAVAUEATE1USDD$D$LD:4HcL$؋\$ID|$H,ALT51O 9N\fE*fEzJ|fE#fEsfffHcAfADfAt9A_DDE_E_fElfEd_EEAE_fE| E_fEt A_AA___D_d_t_l_L_TA_A_AAT_A_AtA__A$AlALD9HcEfAlfAdE_Af|D_Dft_\rDD9A_E__E_AE}HfEdfE\DD_AD_E_E__E_A_AdETAUALOA9A]A9t []A\A]A^A_1Hc|$؋D$HH7H 19}AHcfDlflD9_lD_,_lA_A,|[]A\A]A^A_1ÐAWHcHcDHHAVAUEATE1USDD$D$LD: HcL$؋\$ID|$H,ALT51O 9N\E*EzJ|E#EsfffHcA(ADAtA_9D(D(E_El (E_Ed _E(E(E_E|A(E_EtA_A(A(_(_d_(A_(_DA_(_lA_(_L __TA__t AAT_AtA$AlALD9E(HcAlAdE_A(|D_D_\r(D(D9tA_E__E_AE}FD(Ed E\ D_A(D_E_E__E_A_AdETAUALOA9A]A9t []A\A]A^A_1Hc|$؋D$HH 19}AHcDllD9_lD_,_lA_A,|[]A\A]A^A_1ÐAWAVAUIATUSHcHHHA$AP$H$@H$H$AOAAH$HL$xJDŽ$DŽ$DŽ$DŽ$tDŽ$pDD$tDL$p؃ÀHcH$H<1HcHH~Hc$L$$H$L$MH$hL$hL$0J\-I$HH$ I,N(L$(L$8M$)L$T$t1LD$L$ELt$XGT$tH$1HD|$pH$DŽ$xAHT$hD$t$LH$H$DŽ$TD$$DŽ$PH$`$`AF΃D$\ $Xt2L$EAAD$T3$P$\D$XL$H$`D$LE$0EHE$8EL$H$Hc$t$L1HHX$HHD$`H$L1L#Ld$hHL$`1HDŽ$DŽ$|E4$D9H|$P$Hc$|LD$hHD$`DDE瀀$ATDDE!$ED$Ll$`AAEAE!A䀀HD$hD$EAAG 6AD ց怀 A|D EAA!C,?EAAD $EA怀D $EAE AA D#$$׋lAAAAAҋ$E AAE AA!A䀀%E牄$HAA DA ׁDE!AAAE AEAA щE $E LD$`E!L|$hA ׉$HEt E| AAAD EAAE D$A Ћ$D!D$AAD EAA䀀AAD $HD LD$XA A#LD$XD!Ld$P$A#TEDE$D!$Ld$PE!EdD!$A!Ld$PD#$!D#$$|D!E#lL$ETL$AD!A|EELAtDD$tA|$tDOD$tAAD9$|$p-$pHc$|HD$hDLd$`D瀀EEADlEDAAAAAEDEA ԁA E EAA DA DDE ˁ瀀LL$XE!D$ADAD AD!DD$LA Hl$PA A4A!!#L$p%L$$L$#$#$H$ED!D!LA!A H$D$OL$#<$p 1ɉ$J|fffffLcC,C,C A<$pO9|҃$p L$EAD $TE$$\A D+$\L$L5A\$t$\IEEeDAL$@D)D$\HAE)ADD$,AAD)艴$8D$(HE)H$ D!A!DD$\ALɃD E1H$0Aff$8L$0AH-H$McH$ ABtA$(FD!AAD$,E!ADH D D DDD!!DH$ H$@ H$@B,Bl$8A$(D!E!$,ADD AADD!E!DAD BtDSDOAAE9L$\$tMc$\AL$GtD)Dt$H$<$D$D$DL$@ADHOAD$L$E)DHd$D$< Dd$Dq$<A D+$\AL5[L"D$\ȉAEAAAD)HAADE ,LH$@AAA4D)Dd$HEAF#4A!D)A!DH㋌$\AAAD$PL$E4$AD $PE4$$RD$XEH$`A D+$XD$XA\$t$XAE1HH$H$EAEAADD$DE)D)D$XLHD$AH H$D$AE)ESDOAD9|L\$0A+D A+$ D$EH$ +$A\$t$AE1HDAAH$E)AD)H MHD$D$DDD$H$DH$AD!E!DD$AD AD)E)HH$fffff$L$AH-H|$0McH$ABtA$FD!AAD$E!ADH dD D DDD!!DHL$0 H$ B,Bl$A$D!E!L$$ADDD !D!D CtDKDOAAE9HT$0DD$tIcD$AH$DE)DT$ D$D$$D$L$AAHI$H$)HL$$ L$H $AL5JA D+$ALD$ȾAAHAAD)Dd$ D)AE fffff,f.Avq,AAE9]D}qDIc]*D*T]D]fD.A*\*L]wA,f.Aw,AAE9]D|ALMD E9T$A9t []A\A]A^A_1Hc\$؋D$1L 9H A*A*yA',f.Avv,уATD9vHc*TA*tA*l *DD]]]f.*dD*\E]A]w,f.Aw,ԃATD9~l$9Hc*TA*d*L]]]f.v,A[]A\A]A^A_1,鐐AWHcHcDHHAVAUEATE1USDD$D$LD:HcL$؋\$ID|$H,AH|51O 9LT=fDN\AAB~ HcAfDfALfDDfDTfDdfE\fElfEtT$f\$\$ft$\$ADDfDL$]A]]D]T$fT$D]D]D]D]A]E]]A]Ã9\$AD f|$fD| T$fT$A]\$A]]fT$A4A\D]D]A]A]ADE A|ALD9|CA}ALOA9cA]A9%[]A\A]A^A_1D9}HcEfdfLftfEDqfA\fAlT$fD$d$D$\$D]A]E]D$L$D$ADD$D$D9A ADD]|]]]D]E|l$D$A\A}ALOA9>A]A9Hcl$؋D$1H9HT5HLALcfBlfB$D9fBtB]dB]t]]C$|[]A\A]A^A_1ÐAWHcHcDHHAVAUEATE1USDD$D$LD:HcL$؋\$ID|$H,AH|51O 9LT=DN\AAB~ HcA(DALDDDTDd E\ElEt T$\$\$t$\$AD D DL$]A(D](]D]D]A]E]]D]T$D]T$]A]Ã9\$AD|$D|T$T$A]\$A]]T$A4A\D]D]A]A]ADE A|ALD9|CA}ALOA9fA]A9#[]A\A]A^A_1D9}HcE(dLtEDqA\AlT$D$d$D$\$D]A]E]D$L$D$ADD$D$D9A!AD (D]| ]]]D]E|l$D$A\A}ALOA9CA]A9D$Hcl$19HH ALcBlB$BtD9B]dB]t]]C$|[]A\A]A^A_1ÐAWAIAVAUATUSH DNVO oD$LDN w9A҉$HDNDD9DT$N)A)D$1D~pv E1E1Ʌ~DJAO 1Ap,D$LMhD$HFOO,EDDaHLDOEAHD$t$L$ EMcLGE  D\$|$EÅ1 ب2t$ 9t$ADžD!*D$ D$T$DŽ$4HL$L$89$4H$@$HAD$HD$H$HLc$L|$AAHc$HED|$EAD$HHD$HD$H$D+$H$$LT$(LMAIcMcHt$ D|$Hc$HfffLcDD$ T$JPH$1H$D9|$|$|9$Lt$0L9$t H$*1HĘ []A\A]A^A_ËD$D$ADEE1D;l$ JD9}Hc$HLcˋT$H$JP1H$D9|NH5 H5H5H5H5H5D$$H$HHIHD$$L$LHHD$D)AAA)A|AWAAIAVAUATUSHcnHc_VO HHAщl$n w9щ\$DNɉ9DL$N))D$E1Ʌ~DHA11~B1D$IcLcMHLgHLLc$TH$@AL$8$|A$|A怀$xEGDFdAA$xDA $|DD $| A $|Aց|-ADA 倀A DD $|AAD $xED $xA倀D $xAEC?L$8AA A D $xB\CtDA$| $xDA߁AAA A A DE AEE EA E AL$@$|D D$xD AEA䀀DD D$|D L$8 Cl AG| AA ֋$xAD $|AD$xAA ؋$|A сAD $xA H$@D EAAE D$xF4$TH$8FLBtAEFB|E D BLD$H9$TDT$8DŽ$PA D$<$H$H$DŽ$$DŽ$H$(D$($AD<˃FD$$D$ tCH$L$(D/A$AD$A+$Lc$LD$$$ L$H$($ELE$@HE$HN N(L$L$H$L$n $H$1H$D$Ev $H$1H$H$H$D$0D$,D$4D3)CHcL$4L$EL$H$AAD AtADEl$|O9|VHc$L$E1$H$H \9T$E6~L$H$L$$1J|LcCAT?YO9|D$|ALH5`AD$T$!AAHAAD)AAE A D+$LEAAE)ŋ,DDT$LADEAA!AADE!ADE D$AD)D)HDHD$XDD!!DL$L H$A D$L|$A|EAD#|T$D!D!DD !D!DL$ D!A D$ Ht$PH$LcA,;A,3~HD$XL$L$L$|1I<LcCAT?DaDOAD9|>$ALοAH5AT$$!AAHAD)D$$AE A D+$$AD)D$ALAE)E,DD)HDH$AA!AADE!DAE D$$ADىD!!D$ H$A D$L$A|EAD#|T$$D!D!DD !D!DL$ D!A D$#H$H$LcE4;E43~H$L$L$$1I|LcCAT:YO9|T$H@DŽ$lC$HL !HE)D ߉$h$HL%$lAAAHE<$dD)Ӭ$dD $d$h~AWHcIAVIIAUATUHSHcIHIH$D$D$H$Ht$8Dƃ6AHcL$$IcIcE1IITHL$XH|$`MH\A9H$DUL$UDKAIcL$DDDLt | A9AMA9AMD9DMD9EDML$9M9MEATALDTD\TLA9AMA9AMD9DMD9EDL$DM9M9ELL$MAE9At A| :$D$hLd$XL\$`D$IIALd$PL\$HDT$DHt$HH$D$4H|$PM|uDl$DL$H$ID9l$4H$H|$xAEwL$kD$(Dt$0T$,HcD$4L$H$H$L$E\D DTAL Dd E4D\$pD\|E|L$L$tDd$ Ed9T Dl$,MD;d$0At DD$0l$(EM;L$,DMD;D$(AM9ʉl$lH$MD9DMƋ\\$,H$ll$(H$9T$,MT$,9t$(Mt$(lH$9\MD9DM9M9\$0T$ME9͋\$ EMD9DMD9DM9MH$E9EME9EMD;\$pD,T$lL$ATL$H$ALDDA||$DdH$A| t T$pD4Dt$tD|AM;\$tTDMDt D9L$lLl$xDML$lE9EME9DD$DEM9ETMރD$4E\D9D$4ELA\ D$AD9T$4Hc|$4H$AL$L$D|$,H$T$(Dt$4DdALElDL$0DH$A9Ht$xH$EMD;l$0EMD;D$,EMD;L$(Df.ADIcvh,ADA 9}hHcfA fAf$fAA_ DA_$D_fA.AwA,E>f.ADIcw,ADA 9|D\$LDT$D$LLL$8AADM\$LD$AD9T$OlD\$LDt$D9t$ILc|$LE1*C*cD*c 9*S*[_A_JtPLLNLVLF(H~0sDLcB*T_DB*,l$HT$E_A_t$HD$f.B*\DHFЃD_9JFt~9}AHc_D*TD_*D_\$HL$DT$H\$fA.HFH 19|@,AT9}1HcfAf fEA_ E_A_f.w,H|$@w1H[]A\A]A^A_ÐAWHcAIAVIAUATUAhSHcIcIHIHHIHL$8ELl1 D$LDL$tH[]A\A]A^A_DeAIcHHHD$@t11fffL\$@LcLcDO NĐNLP~IcD]E1Lf[fS IcH|P1fK(D9_[_SxDELcDfBT0fBDE__D$HT$A_L$HD$f.HFЃJfBL8D9_Bt~9}BLc_fFT0D_D_fBT$Ht$DT$Lt$fA.IFJ4ALA 1HT$XH|$`9}fLcfB9B_B|1HT$hH|$p9} fffLcfF9F_F||$D$L\$PDUHHL$0L\$ DT$,fffHcL$LHD$ 1f[fS ;t$,fK(H_[_SL2LbHJLR(LJ0LB8w}fffffHcfT0fD<__D|$HD$_l$L\$fD.IFÃIfL89DD_El~9}AHc_fDD0fD_D$D_Ht$DD$HT$fA.HFI4L\$01;|$,I4f^fV fN(_^_V{D]HcDfT0fD$DE__Dd$HD$A_L$H\$fD.DHFÃIfL8D9DE_ET~9}HD$0L (Mc4MAI$LvDÍDA9"Lc1DD$KtLA9DD$|I$Lw(I$Ls I$LqI$LrM$M2넋L$TDD$L$LHcHVHHD$DD$~E|$8D|$,LL$0H=:(JcHH=&HLO(HC(L= H MO HA H5HLNHBH=HLOHCLLM IffffffAWIIIAVAUATUSLHE@ D{H|$XEkEsDAGDD$T͋t$TDD$/,AAD))ʉt$PAqT$L1҃DsLDk0t$HsPw%H5.'Hc HC4fffff|$HDEHT$XAH$C8L$TDCT{XLD$`H$D$Dl$ Dd$L$LD|$L$}AL$dD\$`t$lDT$hF L$@t$8DT$H[]A\A]A^A_ffffffffAWAVAUATUSHZJDDB0jH|$Ht$Ht$ȉ\$D$L$DzXDD$l$|$1\$D1AfffffLcƒ?JL~D$T$9T$DT$AAADDT$AHHD$D$A̋T$9T$D$DtLcl$LT$E1Hl$AMLD;D$}&Hc|$L$E2IIcAƀAf^E)uL$~k|$Lct$1Lc|$E9}xIcANAE9|AQHcˈ)CLHcD)f VAA DMHcD)fVA)uE9McAF,vEE9|fffffIcALVA)E9}IcANB AQLcA(CTLcD)fB FD A DMHcD)fVA)E9rMcAB|nA)E9}ALD$AYA(l$XHcL$T$fffffI)M fBNuD$A̋T$9T$6H[]A\A]A^A_fffffAWAVAUIATUSB jDz0LH|$D$rAD$JDŋB Mt$ML$AD$DB<D9|$EgΉt$Dd$D$L$D9|$L$D$tHcL$Lt$1H L$IA9Lt$L$}eLcIO4fHcˋT$ItL)M4~HcHcG HfA@D9HfAAHcfAB|D9|D$L$ЋT$9T$[]A\A]A^A_fAWAVAUIATUSB jDz0LH|$D$rAD$JDŋB Mt$ML$AD$DB<D9|$EgΉt$Dd$D$L$D9|$L$D$tHcL$Lt$1H L$IA9Lt$L$}`LcIO4fHcˋT$ItL)M4~7Ic׋L$H4AI HcfAQHHcfARfACu9|1Dt$1Dt$9}LcCa9|DBIcDAq)9}fLcCb9|DBIcAr)D9}ffffLcCc9|QL$LD$fAD$IcDd$HHt$؉D$E1H\$EH\$A9}fHct$IcH IcAILHq HfA AHfA BHcfA C HfAAHfABHcfACA9|119}HcEqD9|ZLcÉCA)9}fffHcA4Z9|DBIcAr)D9}ffffLcCC9|QHL$Af19IcL$6HcDt$MDME~=Ic׋L$H4AI) LcHfCqLcfCrfACu9|D$L$D9|$=[]A\A]A^A_fffAWAVIAUATUSBDZ r0LH|$D$ZʼnD$BXAt$􋊠\$MMD$D$HʼnL$4/9D$zDd$ȉD$ADd$D$L$ЋT$9T$OL$ЋD$tLcl$1LLl$Ll$HI;l$}UDd$IAHcI<1LD9}2HcHcG HfA@HfAAHcfABD9|D9|E1D;d$3Hl$Lc|$Hffff|$1B'HI|HD9}9fffffHcHcG HfA@HfAAHcfABD9|119}ffffHcA<@9|zHcAP)9}HcALc$TH$@AL$8$|A$|A怀$xEGDFdAA$xDA $|DD $| A!$|Aց|-ADA 倀A DD#$|AAD $xED $xA倀D!$xAEC?L$8AA A D#$xB\CtDA$| $xDA߁AAA A A DE!AEE EA E!AL$@$|D D$xD!AEA䀀DD D$|D L$8!Cl AG| AA ֋$xAD #$|AD$xAA ؋$|A сAD #$xA H$@D!EAAE D$xF4$TH$8FLBtAEFB|E D!BLD$H9$TDT$8DŽ$PA D$<$H$H$DŽ$$DŽ$H$(D$($AD<˃FD$$D$ tCH$L$(D/A$AD$A+$Lc$LD$$$ L$H$($ELE$@HE$HN N(L$L$H$L$n $H$1H$D$Ev $H$1H$H$H$D$0D$,D$4D3)CHcL$4L$EL$H$AAD AtADEl$|O9|VHc$L$E1$H$H l9T$E6~L$H$L$$1J|LcCAT?YO9|D$|ALH5PAD$T$!AAHAAD)AAE A D+$LEAAE)ŋ,DDT$LADEAA!AADE!ADE D$AD)D)HDHD$XDD!!DL$L H$A D$L|$A|EAD#|T$D!D!DD !D!DL$ D!A D$ Ht$PH$LcA,;A,3~HD$XL$L$L$|1I<LcCAT?DaDOAD9|>$ALAH5AT$$!AAHAD)D$$AE A D+$$AD)D$ALAE)E,DD)HDH$AA!AADE!DAE D$$ADىD!!D$ H$A D$L$A|EAD#|T$$D!D!DD !D!DL$ D!A D$#H$H$LcE4;E43~H$L$L$$1I|LcCAT:YO9|T$H@DŽ$lC$HL !HE)D ߉$h$HL%$lAAAHE<$dD)Ӭ$dD $d$h~AWHcIAVIIAUATUHSHcIHIH$D$D$H$Ht$8Dƃ6AHcL$$IcIcE1IITHL$XH|$`MH\A9H$DUL$UDKAIcL$DDDLt | A9ANA9AND9DND9EDNL$9N9NEATALDTD\TLA9ANA9AND9DND9EDL$DN9N9ELL$NAE9At A| :$D$hLd$XL\$`D$IIALd$PL\$HDT$DHt$HH$D$4H|$PM|uDl$DL$H$ID9l$4H$H|$xAEwL$kD$(Dt$0T$,HcD$4L$H$H$L$E\D DTAL Dd E4D\$pD\|E|L$L$tDd$ Ed9T Dl$,ND;d$0At DD$0l$(EN;L$,DND;D$(AN9ʉl$lH$ND9DNƋ\\$,H$ll$(H$9T$,NT$,9t$(Nt$(lH$9\ND9DN9N9\$0T$NE9͋\$ END9DND9DN9NH$E9ENE9END;\$pD,T$lL$ATL$H$ALDDA||$DdH$A| t T$pD4Dt$tD|AN;\$tTDNDt D9L$lLl$xDNL$lE9ENE9DD$DEN9ETNރD$4E\D9D$4ELA\ D$AD9T$4Hc|$4H$AL$L$D|$,H$T$(Dt$4DdALElDL$0DH$A9Ht$xH$END;l$0END;D$,END;L$(DDU(Hc(уD9]T](]Hc]D*D* A]L$]LL$\$HD$f.LFM H|$0H719|0,E >f.ADIcv[,ADA 9}^HcfAfA ffAA] A]]f.w,E >f.ADIcw,ADA 9|ffDT$LDD$D$LL\$8AADMT$LD$AD9D$OlDT$L=Dt$D9t$GHct$LE1*[D*S*c 9D*K*SA]A]LdPM$M\$MD$ML$(I|$0mDLcF*lF*$D]Dd$HT$A]D]D\$HD$fE.B*TD]F\HFЃ9J~9}?Lc]F*tB*$A]d$]H\$\$Ld$f.IFJ19|?,AT9}0HcfAfAfA ]A] ]f.w,H|$@|1H[]A\A]A^A_ÐAWHcAIAVIAUATUAhSHcIcIHIHHIHL$8ELl1 D$LDL$tH[]A\A]A^A_DeAIcHHHD$@t11fffL\$@LcLcDO NĐNLP~IcD]E1LfSf[IcH|P1fK(D9]S][ VDELc]B]\0]d$fB.$Lt$NG4D9N4fBL8]Bd~9},Lc]B]L0]T$fB.HD$JGJALAB1H|$XHt$`9}LcfB 9B] B |1H|$hHt$p9}LcfB9B]B||$D$RL\$PUHHL$0L\$ T$,fffHcL$LLd$ fSf[fK(I4]S][ HLvLfL^(LF0LN81;t$,[}fffffLc]B]\0]l$fB.,HD$JGӃ9KfBL8]Cl~9}*Hc]]L0]T$f.Ht$HG4I4HT$01;|$,H4fVf^fN(]V]^ VDUffHc]]\0]t$f.4HD$HGރD9IfL8]At~9},Lc]B]L0]T$fB.H|$JGNDVDDɃfDYDXDDfAWfEADYYXYDfEWDYYDDYYE\ADDYEXDYEXD\f rDEXDD\E\EDXDXADAXDXD\E\XWfffA#N~u,Lc,LN19HI }LcN$9Nd|Ht$Hd놋,,D*D*DF~A\A\BAIcHHFLcJ4L119}9LcfBDDًNDYD^DYDEYXEYDYDYDYYDYDYDYEXDDYYE\DYYDYYE\fD |A\EYDXDYXDYXDYDYDYYDXDXEXDYEXEDYE\DDYYDYDYDXE\DYEDYE\.9ALcLFHM E1E9C,LcE$+C IcEAEYf5=f=>fDBfD =f-=DD$fE9fEIIEAAEDYEYAXEYAEYAEYAYAYYEYAYEYAYEXEXAEYAYAXD\EYXAYD\AYD)EAaADYDYYEXAYD\AEYAYDaEYEYDyEYAYAYEYEXXEYD\XAYD\Di DYa0EXD\Da(DY8H@A\[]A\A]A^A_ÐATIUSHHz0C+Hr8LZ@LRHLJPHcAIcAIffIA HIHHHAHcHTZ Z@ZjZ2\\YYXX\YXZB !u[]A\fffffffffATIUSHHz0C+Hr8LZ@LRHLJPHcAIcAIf.fIA HIHHHAHcJ !HTZ DZ@DZZZ ZpZP DZJ DZRE\\D\D\DYYDYDYEXXAXDXD\A\DYYAXDXZAZA([]A\fATIUSHHz0C+Hr8LZ@LRHLJP*HcAAIcIfDf'IA HIk HHHk AHTZxHcDZhJ !DZzZRZ0DZ` DZHZhZB ZDZZDZrE\\A\D\\EYE\AYAYEYAYEXEYXDXAXXEXD\D\A\DYDYYAXAXDXZZAAZ A[]A\ffffATIUSHHz0C+Hr8LZ@LRHLJPHcAIcAIffDIA HIHHHT$AHcJ !HTZp DZhZXDZZZbt$DZ0Z@DZxZHZpDZBZ:DZRZjDZbZR \T$D\A\D\AYD\XT$D\EYA\AYEYA\EYEYAXAYAXAXAYAXAXAX\AXY\$\\Yt$YD$\YL$XXXZXAZZZIA []A\fffffffffATIUSHHz0C+Hr8LZ@LRHLJPHcAIcAIffIA HIHHHAHcHTf f@fjf2\\YYXX\YXB4!u[]A\ATIUSHHz0C+Hr8LZ@LRHLJPHcAIcAIf.fIA HIHHHAHcJ !HTf fD@fpfPfBf fDJfDRA\D\\D\YDYYDYDXAXXDXD\A\DYYAXDX DQ2[]A\fffffffffATIUSHHz0C+Hr8LZ@LRHLJPHcAAIcIfDf'IA HIkHHHkAHTf0fD`fxfDh HcfDHfh(J !fBffJ fRfDZ(fDrA\A\D\\\AYE\AYEYAYAYDXEYDXAXXXEXD\D\A\DYDYYAXAXDXQDq[]A\ffATIUSHHz0C+Hr8LZ@LRHLJPsHcAIcAIffDIA HIHHHL$AHcJ !fpHTfD0f@ fDxfH(fDhfX0fDB f:fDR(t$fjfp8fDZ0fbfDb8fRD\\T$D\A\D\EYD\EYA\AYA\EYAYAXEYXT$AXAYAXAYAXAXAXAX\\YD$\Yt$\YL$Y\$XXXX9iaQ[]A\ÐAWAVIAUATUSHHX2/D~H$LoVL$D[ L$CL$DK$D$HnD~ H5~$$LǺD$D$D$L$$$$$H <L;H=;H5<H:H;HL$ LT$(H 9L:H|$0Ht$8H=9H59HD$@HT$HH;H:;HL$PLT$XH :L*;H|$`Ht$hH=<H5::HD$pHT$xH$LA9L$L :L#;H$H$AL$H59HcHH$IʋK$YQEACEAXDEHD$ Ht$@L$H\$HLD$8L$Lt$PLL$0L$L\$(Ld$`H$L$HD$hH$L$H$ L$(L$HT$XL\$pLL$xL$L$8H$H$H$@L$H$0L$HL$PL$XH$`H$hL$p$L$L$x}H$HHH)H)1D9"Hc$H$LcJTHD9~拔$Lcf$$f$E$D$T$H$H$H$<$L\$CHĨ[]A\A]A^A_f1҃D9}!Hc$L$LcK,HD9|1;$}Hl$ HD$(L$HL$0LD$HL$L$LL$@L\$8L|$PLd$XH$H$Hl$pH$L$L$L$ HT$`LT$hL$(HD$xH$L$0L$L$H$HL$H$8L$@H$PH$XLcL$`L$hL$pL$Hc$L$xAf$Lcf$$$LBLLH$IBf$;$X -$|HĨ[]A\A]A^A_ÍwAWf ,IAVAUXIATEaUAhSD$@HDt$8fD,Lc|$HAXf[ fDk(E1fkfDcE9fE[(fASYX[DYAYX+YDYDYYDYDYDYAXYAXEEXXAYAXAXXfA[YAXAXfE[ DYEX[EDYAXDDXXAYAXXAX}wLD$,ʼn‰),!ʉX!ʉXD)AX!AX!!HcHIDHcH0BAHH8H!HcL!H E9H0|LAAX~[]A\A]A^A_ffffffffffAWf *IAVIAUXEiATE`UHSD$@HD|$8HcL$HfD*AXHL$fff] fDu(E1fmfDmE9fD[ fDc(fSYX]DYAYXmYDYDYDX[DYDYYDYAXDYEYAXEDYEXXAYAXAXXf[YXAXAXDDXXAYAXXAX}LD$,ʼn‰D),!X!XD)AX!AX!!HcHcHIH0HxDAHJH!HcL!H E9H0Hx|L\$AAXt[]A\A]A^A_ffffffAWf (AVIAUXEiATE`UHSD$@H˅fD(Lc|$HAXf] fDu(fmfDmfD[ fDc(fSD$8YX]DYAYXmYDYDYDX[DYDYYDYAXDYEYAXEDYEXXAYAXAXXf[YXAXAXDDXXAYAXXAXDT$8LD$E1fffff,D),!ЉXX!ЉAXD)AX!к!Hc!HcI4IcH9HcHAH!L!HH AH HNHJHNHJ{LAAXZ[]A\A]A^A_fffffffffffAWf &IEyAVEpAUXATUHSD$@HHt$HcL$HfD&AXHL$fffff] fDu(E1fefDmfD[ fDc(fSD;T$8YX]DYAYXeYDYDYDX[DYDYYDYAXDYEYAXEDYEXXAYAXAXXf[YXAXAXDDXXAYAXXAXLd$,ĉAD,E)DD!DD)!H|$X!XAXA!!HcMcHJAXH0HxL@LHDAHJH!HcL!H D;T$8H0HxL@LHgL\$AAXQ[]A\A]A^A_ÐAWf $AVIAUXIHATEaUAhSD$@HӅfDq$Lc|$HAXf[ fDs(MfkfDkfD_ fDg(fWT$8YX[DYAYX+YDYDYDX_DYDYYDYAXDYEYAXEDYEXXAYAXAXXf_YXAXAXDDXXAYAXXAX~iDT$8LD$,ՉЉ),!ȉXX!ȉAXD)AX!!!HcHcHcIL!HIL!H A uMAAX[]A\A]A^A_ffffffffffAWf "IIAVIAUXEiATGd@USD$@HӅHcL$HfD"XHL$f[ fDs(MfkfDkfE[ fEc(fASD$8YX[DYAYX+YDYDYEX[DYDYYDYAXDYEYAXEDYEXXAYAXAXXfA[YAXAXAXDDXXAYAXXAXDD$8LL$,NYEXYEXDXEXfD$YEXDXfT$xAEYYDYDXfL$hYDYYEXfD$DX$YEXEXDX$EXfD$ADYYDYDXfl$DYEXfD$YDYEXfD$YDYEXYEDYEXfDL$DYDXL$PDXfT$DXYfd$HDXYf\$YEXfD$YYYDYDXD$8YYYDXfL$(DXYDXf$YYAXT$YDXXXf$YX\$YYXf$DXYXL$YYYYYDXfl$YYYYDXf$YYYDXfl$DX KXXYKXXAXDDYKY KAXXXfT$YKXAXXYDDYXYDYYAXfD$YDYXDYDYDYAXfD$DXYKDXXDXf$XXf$XYDXf$YYYYDXf$YYDXf$YXXX$XX$YXXf$XYXH$8EA,D)A,!ЉEX!ЉEX)EX!EXDX!!AXHcHcHcI4L!HIDXH!XH AEXXfyLAX5IvHH[]A\A]A^fffffffffffAWIDAVAUATIUHSHH8AD$A+D$$DL$pt$x A $ f9IDDyEpDXHcAHD$ XffffDsXfKxE1fDfDE9fDk0f[`fDfshEYffDAYfD[pEYEYEYDX+EYAYEYEYAYAYEXEYEYAYEYDXEYEYAYEYEXAYEYEYEYAYEXfDS8AYEYEYEYDXSAYEYEYEYDXf[@AYEXfDKHX[EYDXKEXXAXfkPYEXfD[(AYXk EEYDXDYDYYDYDYDDY=UGYDXDYYDYEXYDYYDYDXEXEDX-FDXY-FEXDY FDXXEXEDYdDYFAEAXAXDYEXAXAXDYAYXDYYYDYYYYDXDXDXEXEEXDY8FDXXDXAY -FDYFDEXAXDYEXDYfAkXDYDYDYYAYDXfA[xAYEXfEAYEXDY EEYEXAYEXEYAYDXDXfAYEYADXAYXEYYYAYDL$fAK`DXfADYAYAYAYDXfAchEXfEAYAYAYD\$(fE[0EYAYAYAYEYEXEYAYAYEYDXfADXfA[pAYEXAYAYDXfAs8AYAYAYAXsXAXXfAS@AYDAXSDYEXXfAcHAYXfAk(AXcYXfA[PYAYDAX[ DYDXYDYDDYDYYDYEXDDYCYDYYDYYEXDDXYtCDXY 3aY-kCDXJCDXY%=CDXXXAXDXAXXDDYXYXDYYDYYDXYDXYEXDYDXY-BYDXXDXYBDXY BXXXYYYYYDXYDXDXY>BXXXXYXYXYXYXXLD$0A,D)A,!fd$(Xd$!EXD)EX!EXDX|$(!!EXHcC HcI4AHcEXH *HcHDX~H!d$(L!XH E9X@yXHl$ ADX:AfDQhfafD|$`f>EYl$fl$`XDT$fDp>$f%g>DL$pfD G>fApEYfDyPfDq DYLc$D$(AYEY$D$DY$D|$DYD$Y$D$YDYD$$DYDYD$0$f=YD$hf$(fn=YY$ f$Y$YYYYY$$fL$fD$EfDl$XfD\$HfDD$fl$fD$fd$8YfDL$f\$DYfDd$xDYDYDX\$YDYDYDYYDYDXDYDYYDYEXDYDYYDYEXfDl$(DYDYYYDYDXl$DYDYDYDYEXfDD$YYDXfl$DYDYDX$YYEXX$DYDXDXAXfDL$EXEDYDX$DYAYYEXfD$(YEEDY5;YDYYDXf$ DYEXDXDYAXfD$DYDDYDYEXfDT$hDYDX$0DYDX:EXDY :DYDX$ DXY-:EXEXDXDXEXEXfD$AYAYYDXf$DXf\$PYEXfD$(YYDX$YDXY@:YEXYEXDXEXfD$YEXDXfT$AEYYDYDXfL$YDYYEXfD$DX$YEXEXDX$EXfDD$ADYYDYDXf$DYEXfD$YDYEXfD$YDYEXYEDYEXfDL$@DYDXL$DXfT$0DXYfd$DXYf\$pYEXfDD$ YYYDYDXD$YYYDXf$DXYDXf$YYAX$YDXXXf\$YX$YYXfL$DXYX$YYYYYDXfl$`YYYYDXf$YYYDXfl$`DX 7XXY7XXAXDDY7Y 7AXXXfT$`Y7XAXXYDDYXYDYYAXfD$YDYXDYDYDYAXfD$DXY 7DXXDXf$XXf$XYDXf$YYYYDXf$YYDXf$YXXX$XX$YXXf$XYXH$8E1EA,D)A,!ЉEXEX!ЉEX)EX!кDXAX!DXHcX!EXHcI4IcHHcHAXH!H!H Af NfJNfJ\LAX55YHH[]A\A]A^fAVAUATIUAiEHSHHXf K5$D$X fj0f"AfDzXfDrxffDfZ8fDBfDb`fDl$fDfDJ@d$`fjfBhD$ffDzHDt$fDr|$D$$DD$@Dd$pD\$DT$D$l$ D$Xd$D$D4$fzpfDjPfDa0fDfDB fZ(fDQXfDIxf|$0ffA8D$fDyfDq`Dd$ffDD\$PfDa@fDYDD$\$fDkQDT$xDL$l$$$D|$8Dt$h|$Dl$D$D\$fy(fDl$fD\$fiHfD%03fDQhfafD|$f3EY$fl$XDT$HfD2d$f%2DL$fD 2fApEYfDyPfDq DYLc$D$(AYEYD$(D$DY$D$DYDt$Y$D$YDYD$$DYDYD$0$f 2YD$f$(f1YY$ f$Y$YYYYY$$f$fDT$EfDl$fD\$IfD$fl$pfDt$fd$YfDL$0f\$XDYfDd$DYDYDX\$`YDYDYDYYDYDXDYDYYDYEXDYDYYDYEXfD$DYDYYYDYDXl$@DYDYDYDYEXfD$YYDXf$DYDYDXD$ YYEXX,$DYDXDXAXfD$EXEDYDXL$DYAYYEXfD$(YEEDY5/YDYYDXf$ DYEXDXDYAXfD$DYDDYDYEXfDT$DYDX$0DYDXb/EXDY \/DYDX$ DXY-8/EXEXDXDXEXEXfD$AYAYYDXfd$DXf\$YEXfD$(YYDX$YDXY.YEXYEXDXEXfD$YEXDXfT$xAEYYDYDXfL$hYDYYEXfD$DX$YEXEXDX$EXfD$ADYYDYDXfl$DYEXfD$YDYEXfD$YDYEXYEDYEXfDL$DYDXL$PDXfT$DXYfd$HDXYf\$YEXfD$YYYDYDXD$8YYYDXfL$(DXYDXf$YYAXT$YDXXXf$YX\$YYXf$DXYXL$YYYYYDXfl$YYYYDXf$YYYDXfl$DX A,XXY1,XXAXDDY&,Y ,AXXXfT$Y ,XAXXYDDYXYDYYAXfD$YDYXDYDYDYAXfD$DXYx+DXXDXf$XXf$XYDXf$YYYYDXf$YYDXf$YXXX$XX$YXXf$XYXH$8EA,D)A,!ЉEX!ЉEX)EX!EXDX!!AXHcHcHcI4L!HIDXH!XH AEXXˉzLAX5*wHX[]A\A]A^AVAUATUHSHHC+C$ D$$  f)EaEHX fj0f"LcfDzXfDrxAffDfJ8fDBfDb`fD$fDfDJ@$XfjfBhD$hffDzHD$fDr$Dl$hL$`D$ D$HD$D$DL$H$$8$D|$8D$fzpfDjPfDa0fDfDB fJ(fDQXfDIxf$(ffA8Dl$(fDyfDq`Dd$pffDD$PfDa@fDYD$$fDED$`D$$d$XD$PD$D$@$Dl$xDd$@D$fy(fD$fD$fafDn'fiHfDQhfD$f='EY$f$Xl$0f-'D$0D$fD&fD &fApD$DY$AYDYDY$fDyPfDq $$DYD|$ D$DY$YYD$D$DY$D$DYDYD$$xf :&Y$f$f&Y$Y$f$Y$pYYYYY$$f$hfD$EfD$fD$IfDD$hf$HfD$f$YfD$(f$8DYfD$DYDYDX$XYDYDYDYYDYDXDYDYYDYEXDYDYYDYEXfDl$`DYDYYYDYDX$ DYDYDYDYEXfDD$HYYDXfl$8DYDYDX$YYEXX$DYDXDXAXfDL$(EXEDYDX$DYAYYEXfD$YEEDY5#YDYYDXf$DYEXDXDYAXfD$DYDDYDYEXfD$DYDX$DYDXh#EXDY b#DYDX$DXY->#EXEXDXDXEXEXfD$AYAYYDXf$DXf$YEXfD$YYDX$YDXY"YEXYEXDXEXfD$YEXDXf$`AEYYDYDXf$@YDYYEXfD$DX$YEXEXDX$EXfDD$XADYYDYDXf$DYEXfD$YDYEXfD$YDYEXYEDYEXfDL$pDYDX$PDXfT$xDXYf$0DXYf$YEXfDD$PYYYDYDX$YYYDXf$DXYDXfT$@YYAX$YDXXXf\$0YX$YYXfL$ DXYX$YYYYYDXf$YYYYDXf$YYYDXf$DX ) XXY XXAXDDY Y AXXXf$YXAXXYDDYXYDYYAXfD$YDYXDYDYDYAXfD$DXY]DXXDXf$XXf$xXYDXf$YYYYDXf$pYYDXf$YXXX$pXX$pYXXf$pXYXH$EA,D)A,!ЉEX!ЉEXD)EX!EXDX!!AXHcHcDXH4HcHL!IH!XH AEXNXffJoLAX5,H[]A\A]A^Ét$|$HHD,$SH[]A\A]A^ffAWIDAVAUIATIUSHHHAD$A+D$$$ A $(fkDL$$ExDX$HcAHD$0XffffDsXfKxMfDfDfDk0f[`fDfshEYffDAYfD[p$EYEYEYDX+EYAYEYEYAYAYEXEYEYAYEYDXEYEYAYEYEXAYEYEYEYAYEXfDS8AYEYEYEYDXSAYEYEYEYDXf[@AYEXfDKHX[EYDXKEXXAXfkPYEXfD[(AYXk EEYDXDYDYYDYDYDDY=YDXDYYDYEXYDYYDYDXEXEDX-)DXY-$EXDY DXXEXEDY8DYAEAXAXDYEXAXAXDYAYXDYYYDYYYYDXDXDXEXEEXDYbDXXDXAY WDYFDEXAXDYEXDYfAkXDYDYDYYAYDXfA[xAYEXfEAYEXDY EYEXAYEXEYAYDXDXfAYEYADXAYXEYYYAYDL$(fAK`DXfADYAYAYAYDXfAchEXfEAYAYAYD\$8fE[0EYAYAYAYEYEXEYAYAYEYDXfADXfA[pAYEXAYAYDXfAs8AYAYAYAXsXAXXfAS@AYDAXSDYEXXfAcHAYXfAk(AXcYXfA[PYAYDAX[ DYDXYDYDDYDYYDYEXDDYYDYYDYYEXDDXYDXY ]5Y-DXtDXY%gDXXXAXDXAXXDDYXYXDYYDYYDXYDXYEXDYDXY-YDXXDXYDXY XXXYYYYYDXYDXDXYhXXXXYXYXYXYXXD$Hl$@A,D)A,!+t$$Afd$8AXd$(EXD!EX!EXDX|$8!!EXHcHcIEXDXXX rzDBHcL!IHd$8H!H Aʈ @r@zDBLLl$0ADXP"HH[]A\A]A^A_Ë$T$t$LHL,$#HH[]A\A]A^A_fffAVAUATIUAiEHSHHXf $D$X0 fj0f"AfDzXfDrxffDfZ8fDBfDb`fDl$HfDfDJ@d$fjfBhD|$ffDzHD$fDr|$XDl$\$(DD$Dd$D$DT$8DL$$D$d$xD|$D$fzpfDjPfDa0fDfDB fZ(fDQXfDIxf|$ffA8Dl$fDyfDq`Dd$@ffDD\$fDa@fDYD$\$hfD 1DT$D$l$Pd$D$ D|$Dt$$Dl$0D$$D$fy(fDl$hfD\$hfiHfD%fDQhfafD|$`fEYl$fl$`XDT$fD$f%wDL$pfD WfApEYfDyPfDq DYLc$D$(AYEY$D$DY$D|$DYD$Y$D$YDYD$$DYDYD$0$fYD$hf$(f~YY$ f$Y$YYYYY$$fL$fD$EfDl$XfD\$HfDD$fl$fD$fd$8YfDL$f\$DYfDd$xDYDYDX\$YDYDYDYYDYDXDYDYYDYEXDYDYYDYEXfDl$(DYDYYYDYDXl$DYDYDYDYEXfDD$YYDXfl$DYDYDX$YYEXX$DYDXDXAXfDL$EXEDYDX$DYAYYEXfD$(YEEDY5YDYYDXf$ DYEXDXDYAXfD$DYDDYDYEXfDT$hDYDX$0DYDXEXDY DYDX$ DXY-EXEXDXDXEXEXfD$AYAYYDXf$DXf\$PYEXfD$(YYDX$YDXYPYEXYEXDXEXfD$YEXDXfT$AEYYDYDXfL$YDYYEXfD$DX$YEXEXDX$EXfDD$ADYYDYDXf$DYEXfD$YDYEXfD$YDYEXYEDYEXfDL$@DYDXL$DXfT$0DXYfd$DXYf\$pYEXfDD$ YYYDYDXD$YYYDXf$DXYDXf$YYAX$YDXXXf\$YX$YYXfL$DXYX$YYYYYDXfl$`YYYYDXf$YYYDXfl$`DX XXY XXAXDDY Y AXXXfT$`Y XAXXYDDYXYDYYAXfD$YDYXDYDYDYAXfD$DXY DXXDXf$XXf$XYDXf$YYYYDXf$YYDXf$YXXX$XX$YXXf$XYXH$8E1EA,D)A,!ЉEXEX!ЉEX)EX!к DXAX!DXHcX!EXHcI4IcHHcHA XH!H!H Aʉ NJNJbLAX5 _HX[]A\A]A^ffffffAWAVAUATUHSHHE+E$PD$@$H E f: EyEpX fr0f"HcfDzXfDrxAffDfJ8fDBfDb`fD$fDfDJ@$frfBhD|$HffDzHD$HfDr$D$$D$XDd$8D$(D$DL$x$8D$($D|$hD$fzpfDjPfDa0fDfDB fJ(fDQXfDIxf$hffA8Dl$XfDyfDq`D$ffDD$fDa@fDYD$$fD8%DT$@D$@$$$D$PDt$0$ D$Dd$pD$0fy(fD$fD$faH$pfDfqHfDQhfD$fApEY$f$Xt$`f5D$xD$fDlfD [fDyP$`D$DY$AYDYDY$fDq D|$P$DYD$D$DY$YD$DYD$DYDYD$$f Y$f$ffY$Y$f$YY$Y$YYYY$$fL$HfD$HE1fD$fD$E9fD$ft$8fD$(f$YfD$hf\$(DYfD$DYDYDX$YDYDYDYYDYDXDYDYYDYEXDYDYYDYEXfD$DYDYYYDYDX$XDYDYDYDYEXfDD$xYYDXft$hDYDYDX$8YYEXX$DYDXDXAXfDL$XEXEDYDX$DYAYYEXfD$YEEDY5kYDYYDXf$DYEXDXDYAXfD$DYDDYDYEXfD$DYDX$DYDXEXDY DYDX$DXY5EXEXDXDXEXEXfD$AYAYYDXf$@DXf$YEXfD$YYDX$YDXY#YEXYEXDXEXfD$YEXDXfT$@AEYYDYDXfL$0YDYYEXfD$DX$YEXEXDX$EXfD$ADYYDYDXf$ DYEXfD$YDYEXfD$YDYEXYEDYEXfD$DYDX$DXf$DXYf$xDXYf$YEXfD$YYYDYDX$PYYYDXf$`DXYDXfT$pYYAX$0YDXXXf\$`YX$YYXfL$PDXYX$YYYYYDXf$YYYYDXf$YYYDXf$DX XXYXXAXDDY}Y mAXXXf$Y_XAXXYDDYXYDYYAXfD$YDYXDYDYDYAXfD$DXYDXXDXf$XXf$XYDXf$YYYYDXf$YYDXf$YXXX$XX$YXXf$XYXL$A,ɉE,D)E!DAD)D!EX!EXEXA!!HcIcHHDEXDXEXAX0xDXD@DH DAXHXHH!HcL!H E90xD@DH RH$pAX-7 H[]A\A]A^A_Ét$|$HHD,$蠅H[]A\A]A^A_ffffffffffAVAUATUHSHHC+C$0D$ $(  fEaEHX fj0f"LcfDzXfDrxAffDfJ8fDBfDb`fD$fDfDJ@$XfjfBhD$hffDzHD$fDr$Dl$hL$`D$ D$HD$D$DL$H$$8$D|$8D$fzpfDjPfDa0fDfDB fJ(fDQXfDIxf$(ffA8Dl$(fDyfDq`Dd$pffDD$PfDa@fDYD$$fDD$`D$$d$XD$PD$D$@$Dl$xDd$@D$fy(fD$fD$fafD^fiHfDQhfD$f-EY$f$Xl$0f- D$0D$fDfD fApD$DY$AYDYDY$fDyPfDq $$DYD|$ D$DY$YYD$D$DY$D$DYDYD$$xf *Y$f$fY$Y$f$Y$pYYYYY$$f$hfD$EfD$fD$IfDD$hf$HfD$f$YfD$(f$8DYfD$DYDYDX$XYDYDYDYYDYDXDYDYYDYEXDYDYYDYEXfDl$`DYDYYYDYDX$ DYDYDYDYEXfDD$HYYDXfl$8DYDYDX$YYEXX$DYDXDXAXfDL$(EXEDYDX$DYAYYEXfD$YEEDY5YDYYDXf$DYEXDXDYAXfD$DYDDYDYEXfD$DYDX$DYDXXEXDY RDYDX$DXY-.EXEXDXDXEXEXfD$AYAYYDXf$DXf$YEXfD$YYDX$YDXYYEXYEXDXEXfD$YEXDXf$`AEYYDYDXf$@YDYYEXfD$DX$YEXEXDX$EXfDD$XADYYDYDXf$DYEXfD$YDYEXfD$YDYEXYEDYEXfDL$pDYDX$PDXfT$xDXYf$0DXYf$YEXfDD$PYYYDYDX$YYYDXf$DXYDXfT$@YYAX$YDXXXf\$0YX$YYXfL$ DXYX$YYYYYDXf$YYYYDXf$YYYDXf$DX XXY XXAXDDYY AXXXf$YXAXXYDDYXYDYYAXfD$YDYXDYDYDYAXfD$DXYMDXXDXf$XXf$xXYDXf$YYYYDXf$pYYDXf$YXXX$pXX$pYXXf$pXYXH$EA,D)A,!ЉEX!ЉEXD)EX!EXDX!!AXHcHcDXH4HcHL!IH!XH AEXNXˉJsLAX50H[]A\A]A^Ét$|$HHD,$GsH[]A\A]A^fffffAWAVAUATUHSHHC+C$0D$ $(  f[EiE`X fr0f"LcfDzXfDrxAffDfJ8fDBfDb`fD$fDfDJ@$frfBhD|$@ffDzHD$@fDr$D$$D$PDd$0D$ D$DL$p$0D$ $D|$`D$fzpfDjPfDa0fDfDB fJ(fDQXfDIxf$`ffA8Dl$PfDyfDq`D$ffDD$fDa@fDYD$$fDY DT$8D$8$$D$xD$HDt$($D$Dd$hD$(fy(fD$fD$fafD fqHfDQhfD$fEY$f$Xt$Xf5D$hD$fDfD fApD$DY$AYDYDY$fDyPfDq $X$DYD|$HD$DY$YYD$D$DY$D$DYDYD$$xf Y$f$fY$Y$f$Y$pYYYYY$$fL$@fD$@EfD$fD$IfD$ft$0fD$ f$YfD$`f\$ DYfD$DYDYDX$YDYDYDYYDYDXDYDYYDYEXDYDYYDYEXfD$DYDYYYDYDX$PDYDYDYDYEXfDD$pYYDXft$`DYDYDX$0YYEXX$DYDXDXAXfDL$PEXEDYDX$DYAYYEXfD$YEEDY5YDYYDXf$DYEXDXDYAXfD$DYDDYDYEXfD$DYDX$DYDX EXDY DYDX$DXY5EXEXDXDXEXEXfD$AYAYYDXf$8DXf$YEXfD$YYDX$YDXYOYEXYEXDXEXfD$YEXDXfT$8AEYYDYDXfL$(YDYYEXfD$DX$YEXEXDX$EXfD$ADYYDYDXf$DYEXfD$YDYEXfD$YDYEXYEDYEXfD$DYDX$DXf$DXYf$hDXYf$YEXfDD$xYYYDYDX$HYYYDXf$XDXYDXfT$hYYAX$(YDXXXf\$XYX$YYXfL$HDXYX$YYYYYDXf$YYYYDXf$YYYDXf$DX XXYXXAXDDYY AXXXf$YXAXXYDDYXYDYYAXfD$YDYXDYDYDYAXfD$DXYDXXDXf$XXf$xXYDXf$YYYYDXf$pYYDXf$YXXX$pXX$pYXXf$pXYXL$EA,D)A,!EX!EXD)EX!EXDX!!AXHcHcDXH HcHL!IL!XH AEXqXyIffrfzfJ_LAX-vH[]A\A]A^A_Ét$|$HHD4$_H[]A\A]A^A_ÐATAf%kfkUHSDHH~P1D9f- 5MMcHMXHcL1D9}^NZffffHMD9}6fAHcыYA\f.sf.r1HMD9|D9|[]A\,fATHf{USDHH~P1D9f%HHhXMc-E1HcA9LL}`NZHfCAMA9};fAIcȋYAf.sf.r*Hf CAMA9|fffD9|[]A\fff,fffffATHAf%USDHH~P1D9f=f-mMcHhX5ffff1HcD9LD}iNZHcfCMD9}@fAHcыXAYčf.sf.r&Hcf-fSMD9|D9|[]A\,ffATHUSDHH~P1D9f%HhXMcHcLD19}[NZHM9}7LcfABAf.sf.r)AHD M9|fffD9|[]A\fffD,ffffffUHE1SDHvPE9}NHXXIcMcJ E1A9}/L IcfALAZDA9H |AE9|[]fffffATHE1ۉUSDHHxPE9}JHhXMcE1IcA9HL}+N fIcAHLADA9HH|AE9|[]A\fLd$Ll$H\$Hl$Lt$L|$HHIH|$Ht$ALrXHD~<19}1fffffHc9I4|1D9|XXYVXAX;A;L\$;|$D$9\$[]A\A]A^A_AWAVAUATUSHj8HB@H|$HzHL$Hl$؋DD$LB0HD$D$HcHt$DL$D-H|$LD$D$LމD$)9l$ĉL$LcL$Hct$LL$Ht$Lcl$E1D;L$LT$OL|$Lt$LcLcD$Hc|$N$Hc\$IILd$L|$Lt$MffffHT$IcDt$HL$Ld$AHt$D4H4HL$I Dd$Df)fDQIcLt$fDILfDAE$DLd$HIL$CZAZ<$GZYvXXYVXXAX;A;L\$D;L$D$9l$?[]A\A]A^A_fffAWAVAUATUSHj8HB@H|$HzHL$Hl$؋DD$LB0HD$D$HcHt$DL$D-H|$LD$D$LމD$)9l$ĉL$LcL$Hct$LL$Ht$Lcl$1;\$LT$OL|$Lt$LcHc|$Lc\$N$LcL$IILd$L|$Lt$MHT$HcDt$HL$Ld$Ht$D4H4HL$I Dd$Df)fDQIcLt$LE$DLd$HIL$GZ AZ<$EZCZMEYCZ YEZ4$AZGZXDYvAXYvAYAXXXXYVXAX8A8LD$;\$fD$9l$[]A\A]A^A_fffAWAAVAUATUSHB@HZ0HJHDD$DD$D9D$Ht$Hr8HD$H|$DL$CH\$HL$Ht$؉D$Hc|$HcT$H|$HT$fffLcl$Hl$1D9NTLL$Ld$McLct$L\$IILL$N HT$HcNjl$HL$H\$,H4HL$H Ë\$ALHcHl$L\H\$HHHBZL\$ZYYYBZ YIXXYAXAMD9hD$D9D$[]A\A]A^A_ffffffAWAVAUATUSHB@Hj8H|$HzHL$DD$DL$LB0DD$HD$HcHt$Hl$LD$H|$C LމD$)D9L$ĉL$QLcT$Hct$LT$Ht$fffLcl$1;|$Ld$OLct$McL|$LcD$J MIHL$Lt$Lt$IfHT$HcNj\$HL$Hl$H4HLl$ALLf!fDAfyHcH\$L,H\$HH,H\FZ Z3BZ,H\$EYYBZYMZBZ AYYAXYXXY6XYVXAX3A3L\$;|$&D$D9L$[]A\A]A^A_fffffffffAWAVAUATUSHjHHZ8HB@H|$L$DD$Hc}LB0H\$؋D$HD$Ht$DL$Hl$LD$LD$)9\$ĉL$LcL$Hct$LL$Ht$ffffLcd$E1D;D$LT$ORLt$LcL|$Hc|$LcL$N,IILl$Lt$MHT$Icl$HL$Ll$AHt$,H4ILDl$Lf!fDAfyHcHl$LDlHl$DHLlIlFZdZuDZ\LFZTZmEYDZLLYBZ\DYMEYZUYZLDYAYYAXYAXXAXAXY6YnXYVXXAX3A3L\$D;D$D$9\$[]A\A]A^A_ffffffAWAVAUATUSHjHHZ8HB@H|$L$DD$Hc}LB0H\$؋D$HD$Ht$DL$Hl$LD$LD$)9\$ĉL$LcL$Hct$LL$Ht$ffffLcd$E1D;L$LT$K<Lt$LcL|$Lc\$LcD$N,IILl$Lt$MHT$Icl$HL$Ll$AHt$,H4ILDl$Lf!fDAfyHcHl$LDlHl$DHLlIlFZ|ZuFZtLFZlDZ]EYFZdLYFZTDYZmEYDYFZLLBZ\DYMZUEYYBZLDYAYYAXYEXAXXAXEXY6DY^AXYnXYVAXXXX77H|$D;L$D$9\$J[]A\A]A^A_fffffffffffAWAVAAUATUSHJ8HZ0HB@DD$D$DD9D$HL$HJHH|$Ht$DL$H\$HD$HL$Hct$HcT$Ht$HT$Lcl$H|$N1D9Ld$Hl$McN L\$IHfHT$HcNj\$HL$L|$H4I D|$AHLHcH\$LDXYvXYNXXAX;A;L\$D;L$D$9l$W[]A\A]A^A_fffffffffAWAVAUATUSHj8HB@H|$HzHL$Hl$؋DD$LB0HD$D$HcHt$DL$D-H|$LD$D$LމD$)9l$ĉL$LcL$Hct$LL$Ht$Lcl$1;\$LT$OL|$Lt$LcHc|$Lc\$N$LcL$IILd$L|$Lt$MHT$HcDt$HL$Ld$Ht$D4H4HL$I Dd$DffiIcLt$faLfYE$DLd$HIL$fGEXAXDYVXXYvAXXYVXXAX8A8LD$;\$D$9l$[]A\A]A^A_ffffffffffAWAAVAUATUSHB@HZ0HJHDD$DD$D9D$Ht$Hr8HD$H|$DL$CH\$HL$Ht$؉D$Hc|$HcT$H|$HT$fffLcl$Hl$1D9NTLL$Ld$McLct$L\$IILL$N HT$HcNjl$HL$H\$,H4HL$H Ë\$ALffYHcHl$fIL\H\$HHHBYL\$YBY XXYAXAMD9hD$D9D$[]A\A]A^A_ffffffAWAVAUATUSHB@Hj8H|$HzHL$DD$DL$LB0DD$HD$HcHt$Hl$LD$H|$C LމD$)D9L$ĉL$ALcT$Hct$LT$Ht$fffLct$1;|$Ld$OLcl$McL|$LcD$J MIHL$Ll$Ll$IfHT$HcNj\$HL$Hl$H4HLl$ALLffYfIHcH\$L,H\$HH,H\fB4f+fB$H\$YYYBYYMBY XXXY.XYFXAX+A+L\$;|$6D$D9L$[]A\A]A^A_fffffffffAWAVAUATUSHjHHZ8HB@H|$L$DD$Hc}LB0H\$؋D$HD$Ht$DL$Hl$LD$LD$)9\$ĉL$~LcL$Hct$LL$Ht$ffffLct$E1D;D$LT$O:Ll$LcL|$Hc|$LcL$N$IILd$Ll$MHT$Icl$HL$Ld$AHt$,H4I Dd$LffafYHcHl$LDdHl$DHLdI,fFDfufLLfB|fmfDLYDYYYUYBYdMYAXYXY\XXY6XYnXYVXXAX3A3L\$D;D$D$9\$[]A\A]A^A_ÐAWAVAUATUSHjHHZ8HB@H|$L$DD$Hc}LB0H\$؋D$HD$Ht$DL$Hl$LD$LD$)9\$ĉL$LcL$Hct$LL$Ht$ffffLcd$E1D;L$LT$K<xLt$LcL|$Lc\$LcD$N,IILl$Lt$MHT$Icl$HL$Ll$AHt$,H4ILDl$LffafYHcHl$LDlHl$DHLlIlfF\fufBLLfFTfDEfFLLfB|fmYDYfBDLDYDYYDYAXYYUYEXBYdYBY\MXY6EXXDYFXXYnXAXYVXXX77H|$D;L$D$9\$^[]A\A]A^A_ffAWAVAAUATUSHJ8HZ0HB@DD$D$DD9D$HL$HJHH|$Ht$DL$H\$HD$HL$Hct$HcT$Ht$HT$Lcl$H|$N1D9Ld$Hl$McN L\$IHfHT$HcNj\$HL$L|$H4I D|$AHLffAHcH\$LDX*AYXXYVXAX;A;L\$;|$D$D9L$f[]A\A]A^A_ffffffAWAVAUATUSHjHHZ8HB@H|$L$DD$Hc}H\$؋LB0HD$D$Ht$DL$Hl$ȍLLD$D$؉D$)9\$ĉL$LcL$Hct$LL$Ht$Lcl$E1D;L$LT$OL|$Lt$LcLcD$Hc|$N$Hcl$IILd$L|$Lt$MffffHT$IcDt$HL$Ld$AHt$D4H4HL$I Dd$Df)fDQIcLt$fDILfDAE$DLd$HIL$PE4$A*G4TYA*G4DAYE*E4|IA $XEYE*G4TEY*C DYAXE*E4|ID*EYA $EYAXY>E*G4TGD*AXYA |EYA*AYA*AXAYM*AYXAXYvXXYVXXAX;A;L\$D;L$D$9\$[]A\A]A^A_fffffAWAVAUATUSHj8HB@H|$HzHL$Hl$؋DD$LB0HD$D$HcHt$DL$D-H|$LD$D$LމD$)9l$ĉL$PLcL$Hct$LL$Ht$Lcl$1;\$LT$OL|$Lt$LcHc|$Lc\$N$LcL$IILd$L|$Lt$MHT$HcDt$HL$Ld$Ht$D4H4HL$I Dd$Df)fDQIcLt$LE$DLd$HIL$PE4$A*G4TYE*EYE4|AXfDIE*G4\MEYA*G4TAXfDAA $A*G4\AYD*A |ME*G4TDYAYEY*A $AYE*G4\DXEYXY>*A |ME*YG4TDXE|EYD*A $EYAXA*AYA*EXAYDYvM*YC \AXX*AYAXYvAXXXYVXXAX8A8LD$;\$*D$9l$[]A\A]A^A_ffffffAWAAVAUATUSHB@HZ0HJHDD$DD$D9D$Ht$Hr8HD$H|$DL$CH\$HL$Ht$؉D$Hc|$HcT$H|$HT$fffLcd$Hl$1D9NTLL$Ll$McLct$L\$IILL$N HT$HcNjl$HL$H\$,H4HL$H Ë\$ALHcHl$L\H\$HHHP+*B,[YFs*YYA*YIL\$XXYAXAMD9^D$D9D$ []A\A]A^A_ffffffffffAWAVAUATUSHB@Hj8H|$HzHL$DD$DL$LB0DD$HD$HcHt$Hl$LD$H|$C LމD$)D9L$ĉL$jLcT$Hct$LT$Ht$fffLcd$1;|$Ll$O\(Lct$McL|$LcD$J MIHL$Lt$Lt$IHT$HcNj\$HL$Hl$H4HLl$ALLf!fDAfyHcH\$L,Hl$HHDH,P]*B\UYD*B\EHl$EY*BLU]AXYM**YAYB\EXY6X*YXYVXAX3A3L\$;|$ D$D9L$[]A\A]A^A_ffAWAVAUATUSHjHHZ8HB@H|$L$DD$Hc}LB0H\$؋D$HD$Ht$DL$Hl$LD$LD$)9\$ĉL$LcL$Hct$LL$Ht$ffffLcd$E1D;D$LT$OvLt$LcL|$Hc|$LcL$N,IILl$Lt$MHT$Icl$HL$Ll$AHt$,H4ILDl$Lf!fDAfyHcHl$LDlHl$DHLlIlUDmA*FlUYE*Dl}LBLUEYE*DmDYD*EYAXA*Dl}LMYAXY6E*FlUDY*AXL}YMA*AYAX*YYnXXYVXXAX3A3L\$D;D$D$9\$`[]A\A]A^A_fffAWAVAUATUSHjHHZ8HB@H|$L$DD$Hc}LB0H\$؋D$HD$Ht$DL$Hl$LD$LD$)9\$ĉL$LcL$Hct$LL$Ht$ffffLcd$E1D;L$LT$K<Lt$LcL|$Lc\$LcD$N,IILl$Lt$MHT$Icl$HL$Ll$AHt$,H4ILDl$Lf!fDAfyHcHl$LDlHl$DHLlIlUDmA*FlUYE*Fl]LMEYE*FlUDYD*BL]LDYAXE*DmEYD*BLUDYAXY6A*Fl]LD*EXMYE*EYFlU*EXBL]DYDY^MYAXA*AY*YAXXYnAXXYVXXX77H|$D;L$yD$9\$[]A\A]A^A_AWAVAAUATUSHJ8HZ0HB@DD$D$DD9D$HL$HJHH|$Ht$DL$H\$HD$HL$Hct$HcT$Ht$HT$Lcd$H|$N1D9Ll$Hl$McN L\$IHfHT$HcNj\$HL$L|$H4I D|$AHLHcH\$LDX*AYXXYVXAX;A;L\$;|$D$D9L$f[]A\A]A^A_ffffffAWAVAUATUSHjHHZ8HB@H|$L$DD$Hc}H\$؋LB0HD$D$Ht$DL$Hl$ȍLLD$D$؉D$)9\$ĉL$LcL$Hct$LL$Ht$Lcl$E1D;L$LT$OL|$Lt$LcLcD$Hc|$N$Hcl$IILd$L|$Lt$MffffHT$IcDt$HL$Ld$AHt$D4H4HL$I Dd$Df)fDQIcLt$fDILfDAE$DLd$HIL$PE4$A*G4TYA*G4DAYE*E4|IA $XEYE*G4TEY*C DYAXE*E4|ID*EYA $EYAXY>E*G4TGD*AXYA |EYA*AYA*AXAYM*AYXAXYvXXYVXXAX;A;L\$D;L$D$9\$[]A\A]A^A_fffffAWAVAUATUSHj8HB@H|$HzHL$Hl$؋DD$LB0HD$D$HcHt$DL$D-H|$LD$D$LމD$)9l$ĉL$PLcL$Hct$LL$Ht$Lcl$1;\$LT$OL|$Lt$LcHc|$Lc\$N$LcL$IILd$L|$Lt$MHT$HcDt$HL$Ld$Ht$D4H4HL$I Dd$Df)fDQIcLt$LE$DLd$HIL$PE4$A*G4TYE*EYE4|AXfDIE*G4\MEYA*G4TAXfDAA $A*G4\AYD*A |ME*G4TDYAYEY*A $AYE*G4\DXEYXY>*A |ME*YG4TDXE|EYD*A $EYAXA*AYA*EXAYDYvM*YC \AXX*AYAXYvAXXXYVXXAX8A8LD$;\$*D$9l$[]A\A]A^A_ffffffAWAAVAUATUSHB@HZ0HJHDD$DD$D9D$Ht$Hr8HD$H|$DL$CH\$HL$Ht$؉D$Hc|$HcT$H|$HT$fffLcd$Hl$1D9NTLL$Ll$McLct$L\$IILL$N HT$HcNjl$HL$H\$,H4HL$H Ë\$ALHcHl$L\H\$HHHP+*B,[YFs*YYA*YIL\$XXYAXAMD9^D$D9D$ []A\A]A^A_ffffffffffAWAVAUATUSHB@Hj8H|$HzHL$DD$DL$LB0DD$HD$HcHt$Hl$LD$H|$C LމD$)D9L$ĉL$jLcT$Hct$LT$Ht$fffLcd$1;|$Ll$O\(Lct$McL|$LcD$J MIHL$Lt$Lt$IHT$HcNj\$HL$Hl$H4HLl$ALLf!fDAfyHcH\$L,Hl$HHDH,P]*B\UYD*B\EHl$EY*BLU]AXYM**YAYB\EXY6X*YXYVXAX3A3L\$;|$ D$D9L$[]A\A]A^A_ffAWAVAUATUSHjHHZ8HB@H|$L$DD$Hc}LB0H\$؋D$HD$Ht$DL$Hl$LD$LD$)9\$ĉL$LcL$Hct$LL$Ht$ffffLcd$E1D;D$LT$OvLt$LcL|$Hc|$LcL$N,IILl$Lt$MHT$Icl$HL$Ll$AHt$,H4ILDl$Lf!fDAfyHcHl$LDlHl$DHLlIlUDmA*FlUYE*Dl}LBLUEYE*DmDYD*EYAXA*Dl}LMYAXY6E*FlUDY*AXL}YMA*AYAX*YYnXXYVXXAX3A3L\$D;D$D$9\$`[]A\A]A^A_fffAWAVAUATUSHjHHZ8HB@H|$L$DD$Hc}LB0H\$؋D$HD$Ht$DL$Hl$LD$LD$)9\$ĉL$LcL$Hct$LL$Ht$ffffLcd$E1D;L$LT$K<Lt$LcL|$Lc\$LcD$N,IILl$Lt$MHT$Icl$HL$Ll$AHt$,H4ILDl$Lf!fDAfyHcHl$LDlHl$DHLlIlUDmA*FlUYE*Fl]LMEYE*FlUDYD*BL]LDYAXE*DmEYD*BLUDYAXY6A*Fl]LD*EXMYE*EYFlU*EXBL]DYDY^MYAXA*AY*YAXXYnAXXYVXXX77H|$D;L$yD$9\$[]A\A]A^A_AWAVAAUATUSHJ8HZ0HB@DD$D$DD9D$HL$HJHH|$Ht$DL$H\$HD$HL$Hct$HcT$Ht$HT$Lcd$H|$N1D9Ll$Hl$McN L\$IHfHT$HcNj\$HL$L|$H4I D|$AHLHcH\$LDXYVXAX;A;L\$;|$D$9\$[]A\A]A^A_AWAVAUATUSHj8HB@H|$HzHL$Hl$؋DD$LB0HD$D$HcHt$DL$D-H|$LD$D$LމD$)9l$ĉL$LcL$Hct$LL$Ht$Lcl$E1D;L$LT$OL|$Lt$LcLcD$Hc|$N$Hc\$IILd$L|$Lt$MffffHT$IcDt$HL$Ld$AHt$D4H4HL$I Dd$Df)fDQIcLt$fDILfDAE$DLd$HIL$C*A*<$G*AXXYvXYVXXAX;A;L\$D;L$D$9l$?[]A\A]A^A_fffAWAVAUATUSHj8HB@H|$HzHL$Hl$؋DD$LB0HD$D$HcHt$DL$D-H|$LD$D$LމD$)9l$ĉL$LcL$Hct$LL$Ht$Lcl$1;\$LT$OL|$Lt$LcHc|$Lc\$N$LcL$IILd$L|$Lt$MHT$HcDt$HL$Ld$Ht$D4H4HL$I Dd$Df)fDQIcLt$LE$DLd$HIL$G* A*<$EYYE*C*MC* E*4$AYDYA*G*DYvAXYvXAXXYVXXAX8A8LD$;\$fD$9l$[]A\A]A^A_fffAWAAVAUATUSHB@HZ0HJHDD$DD$D9D$Ht$Hr8HD$H|$DL$CH\$HL$Ht$؉D$Hc|$HcT$H|$HT$fffLcl$Hl$1D9NTLL$Ld$McLct$L\$IILL$N HT$HcNjl$HL$H\$,H4HL$H Ë\$ALHcHl$L\H\$HHHB**YYYB* L\$YIXXYAXAMD9hD$D9D$[]A\A]A^A_ffffffAWAVAUATUSHB@Hj8H|$HzHL$DD$DL$LB0DD$HD$HcHt$Hl$LD$H|$C LމD$)D9L$ĉL$QLcT$Hct$LT$Ht$fffLcl$1;|$Ld$OLct$McL|$LcD$J MIHL$Lt$Lt$IfHT$HcNj\$HL$Hl$H4HLl$ALLf!fDAfyHcH\$L,H\$HH,H\F* *3B*,H\$YEYYB**AYYAXB* YMXXY6XYVXAX3A3L\$;|$&D$D9L$[]A\A]A^A_fffffffffAWAVAUATUSHjHHZ8HB@H|$L$DD$Hc}LB0H\$؋D$HD$Ht$DL$Hl$LD$LD$)9\$ĉL$LcL$Hct$LL$Ht$ffffLcd$E1D;D$LT$ORLt$LcL|$Hc|$LcL$N,IILl$Lt$MHT$Icl$HL$Ll$AHt$,H4ILDl$Lf!fDAfyHcHl$LDlHl$DHLlIlF*d*uD*\LYF*T*mEYYD*LLEYB*\*UDY*LMAXDYYAXAYYAXY6AXXYnXYVXXAX3A3L\$D;D$D$9\$[]A\A]A^A_ffffffAWAVAUATUSHjHHZ8HB@H|$L$DD$Hc}LB0H\$؋D$HD$Ht$DL$Hl$LD$LD$)9\$ĉL$LcL$Hct$LL$Ht$ffffLcd$E1D;L$LT$K<Lt$LcL|$Lc\$LcD$N,IILl$Lt$MHT$Icl$HL$Ll$AHt$,H4ILDl$Lf!fDAfyHcHl$LDlHl$DHLlIlF*|*uF*tLYF*lD*]EYDYF*dLEYF*T*mDYF*LLAXDYB*\Y*UB*LEXEYMDYAXYAYY6EXYAXDY^XAXYnAXXYVXXX77H|$D;L$D$9\$J[]A\A]A^A_fffffffffffAWAVAAUATUSHJ8HZ0HB@DD$D$DD9D$HL$HJHH|$Ht$DL$H\$HD$HL$Hct$HcT$Ht$HT$Lcl$H|$N1D9Ld$Hl$McN L\$IHfHT$HcNj\$HL$L|$H4I D|$AHLHcH\$LDLc|$PLct$8EM\$ffE1E9Hc|$XLc\$@L?ffff)lj L҈L)lj L҈L)lj L҈L)lj L҈L)lj L҈L)lj L҈L)lj LAL)lj L҈LE9CE9}2LcT$XHc|$@OEE)AA)D LڈHAuO$|LHLD$DL$D9L$o[]A\A]A^A_ffffffAWAVE1A9AUAATUSD|$XH|$Ht$T$DD$LL$HcT$8HT$Hl$HL$HIcHT$LD$D$HctLcHMLsMM~IH\$Hcl$PLcd$@|$HE~K $IcDfAIfAIuIIMMuAD;t$o[]A\A]A^A_fffAWAAVAUATUSDd$@l$XD$9T$H|$Ht$T$DD$LL$HcT$PHcD$8HT$HD$LcT$H\$HL\$H|$HL$D$Nc,Oc J4NхJHT$L|$A^l$HMHT$fffE1A9Lc\$XHcL$@AK<L ADA)J E ffLHA)DE ffLHA)DE ffLA)DL ffLA9jE9}GHcL$@HcT$XAL H<DD)ffAEIA)D f%fLuLd$MMLD$Dd$D9d$k[]A\A]A^A_fffAWAVE1A9AUAATUSD|$XH|$Ht$T$DD$LL$HcT$8HT$Hl$HL$HIcHD$LD$T$HctLcHMLsMM~IH\$Hcl$PLcd$@|$HE~K $IcDfAIfAIuIIMMuAD;t$o[]A\A]A^A_fffAWAVAUAATUSD$9T$H|$Ht$T$DD$LL$`HcT$PHcD$8HT$HD$Hct$L\$HH\$LT$HD$L$Mc Hc,MHfffffffAWAVAUATUSHn Lc^$LcWNf D$D$$nLNLG$H$IIDeL$L$$ D$AD$L$L$L$|D$xHT$pHT$ L$`yMcLL$p$J$N A)J<Dd$~ ffT$`YT$`fd$`AAD$l\$|A*9\$lE*&E*^E*VE*?^E*wE*oDYDYDYDYDYDYD$Lc$tD+$Dl$|ID|$\AAD|$XLt$PDl$D$lL$L$|9L$l(L$$(tLc\$lL$L$$IMMޅUM1;$}$A* A*Hc;$A A|1;L$}=1ffffD$Hct$|A*4A*,A ;L$HA4A,|1;$}3DT$D$IcBM|;$HMOHL$O O$EuL$ItHt$~ f9fT$hYT$hfDl$hٽH$(E*'D*E*_E*WE*O E*GD$tE^*;*s*k*c *C$9D$tAEYEYEYEYEYD$AYD$AYD$AYD$AYD$Y$$$$$\D$$$HHD$PD$tL$$D$D9|$txL$$$$tLcD$tHc$1L$IHHD$XMLD$HJ,:H*L$EFL,D9}E1HcHc$D9A**T* AE*|A E<|1D9Hcf$f$Y$f$Y|f$D9Ytf$YlfD$YD f$f$fD$fD$fD$XfD$fD$fD$fD$f$Xf$Xf$Xf$$EY,f$AY\AYTEY|EYt DXDXEXEXE,EYEYdEY\EYTEYL EXEXEXEXEAY$AY|AYtAYlAYD XXXXA$THL$XDl$HEJ,9pL|$PLc$HcD$dHc$Hc$IL|$HD|$LD$8HD$0HT$@fffHD$8Ht$HD*eLl$0H$D*lD*\AFH.1F*t9T$LD$8fD|$AIf$AAY$EED*HcD*4f fA,ÃY$fA4LYY$fdY$fEDY$fAE*v^E*nDYDYDYDYDYDYlA_\$,D$\L$,D9|$\?L$,$tHcL$\L\$xDELt$ Lc$HII1D9Ll$HK}/1ffffLcHcDD9A*4*,C4C,|19}qffHcfE fA\9fATEYAYAYDXDXE fAL$(N4 J0XHT$HDD$Lt$hA11Lt$$HjT$Ht$HH|$`JT$T1LHH\$0T$T1HHH\$0T$T1LHH\$0T$T1LHHc0HD$`H|$`E1A9}McAJA9J|ffHc19L}ffLcfC 9BX B |ffHc1L H9L0}!LcfC 9BX CX B |ffffffLc1MK@NI9M 3L}'LcfC 9BX CX CX B |fffffHc19L}$ffLcfB 9BX CX B |fffffffffHc1L H9L0}'LcfB 9BX CX CX B |fLc1MK@NI9M 3L}-LcfB 9BX CX CX CX B |ÐHWL1ɋ9}LcOc fA 1X  H9|fffffffffffHWL1ɋ9}(LcOcM 2fA X AXI H9|ffffHWL1ɋ9}.LcOcM 2fA X AXIAXI H9|HWL1ɋ9}4LcOcM 2fA X AXIAXIAXI H9|fffffffHWL1ɋ9}2LcOcM 2fA X  fAIXJJH9|fffffffffHWL1ɋ9}>LcOcM 2fA X AXI fAIXJAXIJH9|HWL1ɋ9}JLcOcM 2fA X AXIAXI  fAIXJAXIAXI(JH9|fffHWL1ɋ9}VLcOcM 2fA X AXIAXI AXI0 fAIXJAXIAXI(AXI8JH9|ffffffHWL1ɋ9}BLcOcM 2fAXfAIXJJfAIXJJH9|fffffffffHWL1ɋ9}TLcOcM 2fAXAXQfAIXJAXI JfAIXJAXI(JH9|fffffffHWL1ɋ9}fLcOcM 2fAXAXQAXQ0fAIXJAXI AXI8JfAIXJAXI(AXI@JH9|ffffffHWL1ɋ9}xLcOcM 2fAXAXQAXQ0AXQHfAIXJAXI AXI8AXIPJfAIXJAXI(AXI@AXIXJH9|ffffHWL1ɋ9}RLcOcM 2fAXfAQXRRfAIXJJfAIXJJH 9|fffffffffHWL1ɋ9}jLcOcM 2fAXAXY fAQXRAXQ(RfAIXJAXI0JfAIXJAXI8JH 9|fffHWL1ɋ9LcOcM 2fAXAXY AXY@fAQXRAXQ(AXQHRfAIXJAXI0AXIPJfAIXJAXI8AXIXJH 9zfffHWL1ɋ9LcOcM 2fAXAXY AXY@AXY`fAQXRAXQ(AXQHAXQhRfAIXJAXI0AXIPAXIpJfAIXJAXI8AXIXAXIxJH 9bfffffffffDGHWLLO0HOfO(1D9},HcMcfA3XYHL HD9|ffDGHWLLO0HOfO(1D9}5LcKcL0fAXAXSYHL HD9|fffffffDGHWLLO0HOfO(1D9};LcKcL0fAXAXSAXSYHL HD9|fffDGHWLLO0HOfO(1D9}ALcKcL0fAXAXSAXSAXSYHL HD9|ffffffffffDOHWLLG0HOfO(1D9}GLcKcL0fAXYLfASXRYQHLBHD9|ffffffDOHWLLG0HOfO(1D9}SLcKcL0fAXAXSYLfASXRAXSYQHLBHD9|fffffffffDOHWLLG0HOfO(1D9}_LcKcL0fAXAXSAXS YLfASXRAXSAXS(YQHLBHD9|DOHWLLG0HOfO(1D9}kLcKcL0fAXAXSAXS AXS0YLfASXRAXSAXS(AXS8YQHLBHD9|fffDOHWLLG0HOfO(1D9}_LcKcL0fAXYLfASXRYQLBfASXRYQHLBHD9|DOHWLLG0HOfO(1D9}qLcKcL0fAXAX[YLfASXRAXS YQLBfASXRAXS(YQHLBHD9|ffffffffffDOHWLLG0HOfO(1D9LcKcL0fAXAX[AX[0YLfASXRAXS AXS8YQLBfASXRAXS(AXS@YQHLBHD9zfffDOHWLLG0HOfO(1D9LcKcL0fAXAX[AX[0AX[HYLfASXRAXS AXS8AXSPYQLBfASXRAXS(AXS@AXSXYQHLBHD9hffDOHWLLG0HOfO(1D9}wLcKcL0fA#X"Y!LfA[XZYYLBfASXRYQLBfASXRYQH LBH D9|ffffffDOHWLLG0HOfO(1D9LcKcL0fA#X"AXc Y!LfA[XZAX[(YYLBfASXRAXS0YQLBfASXRAXS8YQH LBH D9nffffffDOHWLLG0HOfO(1D9LcKcL0fA#X"AXc AXc@Y!LfA[XZAX[(AX[HYYLBfASXRAXS0AXSPYQLBfASXRAXS8AXSXYQH LBH D9VÐDOHWLLG0HOfO(1D9LcKcL0fA#X"AXc AXc@AXc`Y!LfA[XZAX[(AX[HAX[hYYLBfASXRAXS0AXSPAXSpYQLBfASXRAXS8AXSXAXSxYQH LBH D9>ffffffAWE1AVAUATUSH( DHWFnH$DWD_ EH$HL$pH$O$$~ VLFWf-gD*DYD*$DYD*LcHcHIEDL$@A^AɉL$XDX^XA^^DY$f=DXD$f$]DXDXD,Dd$\E)D$\A,A,E)D9DND9DND\$PHM,1EdfD$fD$D,E*ECfA.A*YEEFD$$E,A*AZfD.$,AFD;$DO$D9DNAl$ $DD$T$~$HcH [D =T$TH$E1Lc$1AH4E9H$HLcOcM 2A X AXI AIXJAXI JH9|HWL1ɋ9}JLcOcM 2A X AXIAXI AIXJAXI AXIJH9|fffHWL1ɋ9}VLcOcM 2A X AXIAXIAXI AIXJAXI AXIAXIJH9|ffffffHWL1ɋ9}BLcOcM 2AXAIXJJAIXJJH 9|fffffffffHWL1ɋ9}TLcOcM 2AXAXQ AIXJAXIJAIXJAXIJH 9|fffffffHWL1ɋ9}fLcOcM 2AXAXQ AXQAIXJAXIAXIJAIXJAXIAXI JH 9|ffffffHWL1ɋ9}xLcOcM 2AXAXQ AXQAXQ$AIXJAXIAXIAXI(JAIXJAXIAXI AXI,JH 9|ffffHWL1ɋ9}RLcOcM 2AXAQXRRAIXJJAI XJ J H9|fffffffffHWL1ɋ9}jLcOcM 2AXAXYAQXRAXQRAIXJAXIJAI XJ AXIJ H9|fffHWL1ɋ9LcOcM 2AXAXYAXY AQXRAXQAXQ$RAIXJAXIAXI(JAI XJ AXIAXI,J H9zfffHWL1ɋ9LcOcM 2AXAXYAXY AXY0AQXRAXQAXQ$AXQ4RAIXJAXIAXI(AXI8JAI XJ AXIAXI,AXI<J H9bfffffffffDGHWLDO0HOfO(1D9}4HcMcA3XZYZHD HD9|ffffffffDGHWLDO0HOfO(1D9}=LcKcL0AXAXSZYZHD HD9|fDGHWLDO0HOfO(1D9}CLcKcL0AXAXSAXSZYZHD HD9|fffffffffDGHWLDO0HOfO(1D9}ILcKcL0AXAXSAXSAXS ZYZHD HD9|ffffDOHWLDG0HOfO(1D9}WLcKcL0A3X2ZYZ!DASXRZYZQHDBHD9|ffffffDOHWLDG0HOfO(1D9}cLcKcL0A3X2AXsZYZ!DASXRAXS ZYZQHDBHD9|fffffffffDOHWLDG0HOfO(1D9}oLcKcL0A3X2AXsAXsZYZ!DASXRAXS AXSZYZQHDBHD9|DOHWLDG0HOfO(1D9}{LcKcL0A3X2AXsAXsAXsZYZ!DASXRAXS AXSAXSZYZQHDBHD9|fffDOHWLDG0HOfO(1D9}{LcKcL0E DX EZDYAZ9DAsXrZYZaDBASXRZYZQH DBH D9|fffDOHWLDG0HOfO(1D9LcKcL0E DX EXK EZDYAZ9DAsXrAXsZYZaDBASXRAXSZYZQH DBH D9pffffffffDOHWLDG0HOfO(1D9LcKcL0E DX EXK EXKEZDYAZ9DAsXrAXsAXsZYZaDBASXRAXSAXS ZYZQH DBH D9^ffffffDOHWLDG0HOfO(1D9LcKcL0E DX EXK EXKEXK$EZDYAZ9DAsXrAXsAXsAXs(ZYZaDBASXRAXSAXS AXS,ZYZQH DBH D9LfffffDOHWLDG0HOfO(1D9LcKcL0E#DX"EZDYEZDDEKDXJEZDYAZyDBAsXrZYZaDBAS XR ZYZQ HDB HD9]ffffffDOHWLDG0HOfO(1D9LcKcL0E#DX"EXcEZDYEZDDEKDXJEXKEZDYAZyDBAsXrAXsZYZaDBAS XR AXSZYZQ HDB HD9EDOHWLDG0HOfO(1D9LcKcL0E#DX"EXcEXc EZDYEZDDEKDXJEXKEXK$EZDYAZyDBAsXrAXsAXs(ZYZaDBAS XR AXSAXS,ZYZQ HDB HD9-ffffffDOHWLDG0HOfO(1D9LcKcL0E#DX"EXcEXc EXc0EZDYEZDDEKDXJEXKEXK$EXK4EZDYAZyDBAsXrAXsAXs(AXs8ZYZaDBAS XR AXSAXS,AXS<ZYZQ HDB HD9AWAE1AVAUATUSH HWFnH$DODW H$HL$`HT$xO$$~ VLF!f-˹D*DYD*DYE*LcHcHIEDd$0A^AɉL$HDX^XA^^DY$f=dDXD$f$]DXDX,؉\$LA)D$LA,A,DD)D9ND9DNDT$@HM,16fD$fD$D,E*EKfA.*YލS EFE扜$E,A*EBfD.䉌$$,EFA9DOE9EN$$DL$D$~$HHcHh =yL$DH$Lc$AHDT$PIcfDADED)AD E fD.DEGADAD!AAE"4$A!D !D A $AmA9fD=fD5LH@fD-)fD%8I@fD5I?AAMcfEfB\ADAALcfTAfAUBfEffVAfEf\$AAfTB|B\fAUfEfTAfAUffVAfVAfTfEfAUBd fHl$fL$fVAfTfAU|$XD$ffVAXD$fBl(L\$f|$fTfUfffVAXABt0LT$fL$fEfTfUXAAffVAB|8fXAfEfTfUAXAffVffEXAfffE,!!A ʍNC&EB\PBD@fTBtHfAUfTfAUfVfTfVA\$AfAUB\XffTfAUfEfVAfVAfBd`Hl$fT$t$XD$AfTfAUXD$ffVABlhL\$f|$BtpLT$fTXfUAfffVfL$XfTfUAfB|xLcfVAXffTXfUfVX,A!!A AUG4'A9EE)AMcٹfBfBdfBLfF\fF| fFt(fFl0fFd8fA.FAA@fA.DFEA fA.EFDfE.AFAAfE.DFDDуfE.AFLcAAfE.DFE1fE.AAE D!AA!D C,"E9AEE1AA9JAIcffA.v EDAE f|fA.v AJEAE AAA9~E9}"IcfD fE.vADAE AE!AAJD!AA AHcDD!AF"#D B4#D$Ld$DD$D9D$HD$H<}L\$8fA fASZ[]A\A]A^A_fffffAWfEWEAVAUE1ATAUSL|$@H$ILt$HfA7fAofAgfAfAVfANfA.AFAAI$fA.DFC@EA$IfA.D$EGfA.DT$AFI$fA.FΉʁ$IfA.GE9݉L$ Х LcIcLD$Hl$D\$ fffLd$8L$PE1l$D\$PE1fE$fET$fE\$Dd$AAAEDADl$AT$AEj+L$PDl$;L$DN1E1AEA9AAD$fffT$PIcAfA G$>D$L|$Dl$D9l$H|$H}LD$8fA fAH[]A\A]A^A_fffffffffffAWA$IDEAVAUIATAUSL|$@H1Lt$HfA7t$D$fAol$D$fAgFljI$d$fAD$F\$fAVAC@A$ID$T$fAND$AGщT$D$L$DFDсI$D$AFʉ΁$IGD9߉L$_f-6f5nLcf=SfDbIcfD f{fDu{LD$fDGifD%N{H|$D\$ċL$PDT$E1Hl$8Dt$D\$PAfD}fDufDmDT$Dd$AA1DDDT$AEAZ׉L$AZމt$Dd$AZj+T$PDT$;T$DN1E1ABA9AAAD$T$PDHc E)׉AE .vDE)AD DlD.v)D DtD.v Dʉ փ;l$~D9}LDL$PAAAIHcADD$D\$E1ҿA)9}gAAHcD‰DffffAWE1AVAUATUSHHw_fWpL$fw`fx\$XfOhl$\GVZLnLf HcZHZ׉D$THZT$PHl$L\$ Hd$H$HD$`H|$`t$ DD$D$11HcLcLH|$`ރNNTpN̐~LtmIHIM|$\Jl 19Dq=3gD(D,CRWHcTHA*D\Xt$LE(E(DWEYE(EYEXE(EYEYEYA(E(AXE\EXE\A\EXEXA1L9DvDN DfSD$XHcL$XE,AHL$@HI)HM\Dl$PEqHc|$T[Dl$PD%pD-fD5gpD=fD$J?DDY| \DDYD(Yl8Yt0DDY YTYdY\EXAXXXF >EYUAYeAY]fzfDzfDBBY ?AYAYEYEYAXXDXDXB ;IE9AEEGHc|$D,$H~xHD$8H|$8fD,$H|$83xffffffffffAWAVAUIATUSH HOfW`fOhwHT$PfD_xDwHYD!HT$HDyT$8t$,L$0H\$@HY Dȁ,L$(tMcH|$HEOIN Lc\$,fD%t,GdDT$$L\$EEE,A*EԅfEEpD\E\uzL$$19})HcAAY$AY9X$|ffl$0L|$JAX,D)ADu|$(,1Hĸ []A\A]A^A_oDHL$@E1AE9HH4}fffffAE,E*CIcAA\EDXD$8HcDHD\DDY|DDYl YdYl(AYFY$>DYAYEXAXF$;Bl;THc|$D $HpHD$8H|$8fD $H|$8ppffffffffAWAVIAUATAUSH HOGfW`fOhHt$@W(fxHYD$Ht$8Hi DoT$(L$ DyH\$0T$=,D$LL$8HcEMfD$Lc\$E,|$*AfAfp\\Ld$01A9HI }|D,D*ALcDE\HLcXd$(9EFY\A\DYTDYFY AXEXYDYDXFL|fffffd$ JlXD,A)A|$,1HĨ []A\A]A^A_Eu41A9}HcDDEY,EY49EXDl|AHt$0AHH 19],*ELcD\HHcXd$(9BY\D\YLEDY FYEDT$WE,ӅE(A*E(D\EZE\uwL$19}*HcA(A(Y|mediaLib_jnimediaLibwrapperException0 @??!#g####g#!9#"&###""v$$]$D$+$$"######[]E\R]]\E\channelswidthheightstride[BpaddingsbitoffsetformatLjava/lang/Object;dataleftPaddingtopPaddingsubsampleBitsHsubsampleBitsVprecisionBitsvis_width_bitsvis_height_bits[FdataH_floatdataV_float[DdataH_doubledataV_doubleintypeouttypemethodlutlengthindexsizenormal_table[Idimdouble_lutrmapgmapbmapmaplengthv8plusav9av8plusbv9b  b b xb 44K C>`=>->>>]>->t?@@[@[@@@AALBB"C"CLB>o@@?Ap?@A@ApASP%Pe9) ~nueUE }m]jNii.$$$#########O?`@@???????jjjjKiKii??UUUUUU???UUUUUU?$I$I??qq??F]tE?UUUUUU?;;?$I$I????qq?(??aa?F]tE?d! YB?UUUUUU?{Gz?;;?h/?$I$I?{a??B!??|??AA?qq?к?(?AA???aa?}A_З?F]tE?ll?d! YB?W+ɕ?UUUUUU?9/?{Gz??;;?x+R?h/?)A?$I$I?p}?{a? 'u_[??;ڼOqɐ?B!?AA?? ?|?g1??ہv`?AA? V،?qq??к?O贁N?(?cj`?AA?3??H??k?aa??}A_Ї?ȤxL?F]tE?p \?ll?hh?d! YB?X`?W+Ʌ?X0Ҏ?UUUUUU??9/?[R֯?{Gz?beF??,?;;?88?x+R?+J#?h/?,Mɂ?)A?5'Ps?$I$I?x!?p}?ρ?{a?? 'u_[?55??k?;ڼOqɀ?h ?B!?Mb?AA?@ ??? ?qBJeD?|?~?g1~?t:W~?~?}?ہv`}?4,Tw}?AA}?tn }? V|?^|?qq|??|?|?+{?к{?3=l}{?O贁N{?ـl@6 {?(z?ppz?cj`z?mЦmz?AAz?Kzz?3y?Ny?y?r py?Hy?)I y?x?0x?kx?Jx?aax?ݾzek?кk?#+k?3=l}k?ek?O贁Nk?JH7k?ـl@6 k?"1K k?(j?^j?ppj?/j?cj`j?Y0Qj?mЦmj?JhAWj?AAj?Ň*,j?Kzj?j?3i?-hki?Ni?U$i?i? /i?r pi?w \i?Hi?,4i?)I i?ՐO i?h??7zRh?0h?:bοh?kh?h?Jh?'th?aah?xOh?ݾzd?[R֯d?Jvd?gв9d?H"d?{Gzd?f`Y4md?`d?vSd?beFd?M0':d?%f-d?QY^& d?d?feтd??c?Bc?,c?uc?{c?U)#`c?;;c?"z8$c?c,c?f"c?88c?E[uc?Hic?*_]c?x+Rc?FyFc?W[:c?j\/c?+J#c?Xwc?0 c?`*c?h/b?KNb?K%b?P- b?,Mb?7Zb?@+b?b?)Ab?[rb?b?MΡ8}b?5'Psb?'|hb?p"^b?w~Sb?$I$Ib?[`>b?߼xV4b?*"*b?x!b?UHyb?g G b?  b?p}a?Le[? [?к[?y[?#+[?@@[?3=l}[?4kq[?e[?[M-Z[?O贁N[?p B[?JH7[?#j_+[?ـl@6 [?`߻[?"1K [?,*Z?(Z? *2Z?^Z?vZ?ppZ?AZ?/Z?@Z?cj`Z?FZ?Y0QZ?V uwZ?mЦmZ?ߏObZ?JhAWZ?7& nLZ?AAZ?-C6Z?Ň*,Z?/uz!Z?KzZ?.5 Z?Z?rY?3Y? _}Y?-hkY?@Ӝ4Y?NY?-&vY?U$Y?t$ۣY?Y?p?`Y? /Y?Rf{Y?r pY?lfY?w \Y?s RY?HY?Ygз>Y?,4Y?td*Y?)I Y?AR!Y?ՐO Y?Ρ~Y?X?<X??7zRX?CX?0X?OdX?:bοX?@@X?kX?4\9X?X?KtOX?JX?n@}X?'tX?\jX?aaX??XX?xOX?)QΠEX?ݾzW?}A_W?p!W?|.W?37iW?"W?^cW?9kW?ÉCW?ȤxLW?W? ƚyW?VopW?4gW?|"P_W?muVW?tKNW?F]tEW?X ^=W?A4W?p,W?F($W? .sW? |mxW?fh) W?p \W?C$CV?``V?t(V?7CkV?&V?aȁ&V?);V?llV?xNWV?= IV?@@V?rST?T?[R֯T?\[(T?JvT?ڛT?gв9T?cT?H"T?%qT?{GzT?&UtT?f`Y4mT?GJgT?`T?pPZT?vST?CgMT?beFT?"[Í@T?M0':T?R3T?%f-T?M 'T?QY^& T?!sbT?T?:z T?feтT?@@T??S?trPS?BS?Q֩\S?,S?=S?uS?S?{S?:U\yS?U)#`S?\iLS?;;S?-S?"z8$S?n"ZS?c,S?!S?f"S?~+S?88S?D'$H{S?E[uS?soS?HiS?(bcS?*_]S?[WS?x+RS?ؒILS?FyFS?vF@S?W[:S?ƿ}5S?j\/S?2d)S?+J#S?}Ӝ+S?XwS?sa{S?0 S?,\RnS?`*S?D$R?h/R?WR?KNR?1^R?K%R?<=R?P- R?gYR?,MR?xR?7ZR?T]}R?@+R?wR?R? IR?)AR?70ؘR?[rR?5ݧR?R?~SR?MΡ8}R?PxR?5'PsR?pnR?'|hR?ricR?p"^R?!QuXR?w~SR?_NR?$I$IR?ԗCR?[`>R?9R?߼xV4R?ŏU*/R?*"*R?$R?x!R?[ R?UHyR?6^R?g G R?ml;!2R?  R?OQ?p}Q?dkQ?L?-$"$#F$F$ $#D%%$4%4%$%$D'7'd&Z'Z' 'd&Q)C)(+)+))(,{,+,,,+.......;f?;f濒EEEDFDFFE[IJIIHHFKeRPNCMKPIWVV|VpVWUWWWWWWWZrZbZUZBZBZIZ?@@@@@@ @"@$@&@(@*@,@.@0@1@2@3@4@5@6@7@8@9@:@;@<@=@>@?@@@@@A@A@B@B@C@C@D@D@E@E@F@F@G@G@H@H@I@I@J@J@K@K@L@L@M@M@N@N@O@O@P@@P@P@P@Q@@Q@Q@Q@R@@R@R@R@S@@S@S@S@T@@T@T@T@U@@U@U@U@V@@V@V@V@W@@W@W@W@X@@X@X@X@Y@@Y@Y@Y@Z@@Z@Z@Z@[@@[@[@[@\@@\@\@\@]@@]@]@]@^@@^@^@^@_@@_@_@_@`@ `@@`@``@`@`@`@`@a@ a@@a@`a@a@a@a@a@b@ b@@b@`b@b@b@b@b@c@ c@@c@`c@c@c@c@c@d@ d@@d@`d@d@d@d@d@e@ e@@e@`e@e@e@e@e@f@ f@@f@`f@f@f@f@f@g@ g@@g@`g@g@g@g@g@h@ h@@h@`h@h@h@h@h@i@ i@@i@`i@i@i@i@i@j@ j@@j@`j@j@j@j@j@k@ k@@k@`k@k@k@k@k@l@ l@@l@`l@l@l@l@l@m@ m@@m@`m@m@m@m@m@n@ n@@n@`n@n@n@n@n@o@ o@@o@`o@o@o@o@o@p@?@@@@@@@AA A0A@APA`ApAAAAAAAAAAAAAAAAABBB BBBBB B$B(B,B0B4B8BC?C@CACBCCCDCECFCGCHCICJCKCLCMCNCOCPCQCRCSCTCUCVCWCXCYCZC[C\C]C^C_C`CaCbCcCdCeCfCgChCiCjCkClCmCnCoCpCqCrCsCtCuCvCwCxCyCzC{C|C}C~CCCth\PD8, ȾP ==>0>`>>>>>>? ??$?0?>>>>p>@>>=@=@@pؾ(4@LXdp|xl`TH<0$ о`0<== >P>>>>>>>?? ?,?8?D?P?\?h?t??t?h?\?P?D?8?,? ???>>>>>>P> >==<0`о $0<HT`lx|pdXL@4(ؾp@@@==>@>p>>>>>>???(?4?@?L?X?d?p?|?x?l?`?T?H?>>>>`>0>>== PȾ ,8DP\ht;<@<<<<<=q= =l0=@=hP=`=op=s==q==o===========+=s>>r> >q>>>> >$>(>,>0>д4>8>ϼ<>@>D>H>L>P>T>X>\>`>d>h> l>p> t>x> |>s>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> >> >>> >> >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>s> |>x> t>p> l>h>d>`>\>X>T>P>L>H>D>@>ϼ<>8>д4>0>,>(>$> >>>>q> >r>>s>+===========o==q==s=op=`=hP=@=l0= =q==<<<<@<<;U?3*n$U/@7+l %  3 r  :I`8mP4yfR@. ~rf[PE:0& ztnhb\WQLGB=83.)%  ~|ywusqomkigeca_][YXVTRPOMKJHFECB@>=;:8754210.-+*)'&%#"!     *@+ P@E \= wk_TJ@7.& }|zyxvutsqponmlkjihgfedcba`__^]\[[ZYXXWVVUTTSRRQQPOONNMMLLKKJJIIHHGGFFEEDDDCCBBBAA@@@???>>===<<<;;;::::9998888777666655554444333322221111100000////......-----,,,,,,+++++CCCCCCC+eGw@P">bRC.f?7I  V{-Y1_IpA}l N7q`͸)a#LwiK<K<@~dG<3u㚼4Y 3alX2e>D8`<]D뚽ݪbIZZy-ә<>495{< 5y.aSH< sgyyetb,?̻.7?nC?Tr_N?tӰY?< e?)kp?6r+{?țuE?f?>?9ǩ?lX?lE?Gr+?]9#;?2?ũ_?f?>#(0?Q[?*^?1l*?6?-B?.1ON?QȘZ?qTf?{Q}?kJ?1 ?2?1Lp!?2;.?duR? u?Ev?/eT'?ѭ?L%?AL6?#FG?![W?Dh?61y?fNJ?XxЛ?۠*B?xֺ?D2?f^j?6w?,(? [?/S%?Ͱ77?_+H?RDZ?pvk?PNޟ}?"z3?p? SM?𣂑?ոKt?##c?^?e]{f ?z?3-J0? FB?]%>U?R`Jg?X0y?Y.S?yUk?n5!?zӿk?t'<:?Z?"L?f)?Rl ?O3?IF?:YrY?*)nl?G^v?I'?J0?o?K?i? [?=Aa(?Rݛ?,e-?z_@?"xGT?KW.g?3Ɉ{?m?QW@?i ?um.?]U ?t[[?|J-?h+S? /?֡C?X?bvl??u`q]?2?:|?`!?Xd?_{3?}kg?)F&?;3;??.P?vmg$9e?LQz?@wy?ڐ?n|?g-H??'Za?*A?k7+%?trH:?@En[vP?Dy2f?{?<$ؑ?ؐ?Ւ6?q+?#*k?GآDɨHޯ4V]"IaV+:=;5aWfmC{eeA 9@ #"`"!1)j2211(g8'A@c@?7 `b##dj.)k{A3v[#A}GRy"rng?a UH9;s-B T~}~+ uP~s m!~ e~ ]~K U~ Ljv~ C=c~& :O~o 1;~ (&~ ~L d} :} }* }u } } nr}UFZ}A}(}7}||b|gy=|m|`k|MSN|F1|9|4,j{H{'{{j{{[{;{T{gzHz?*z zrz,tOz{e+zUzF|yj6ay'Fy +uyYOy)yyJxxx<zfxb>xKx.r3wawOw">pws-Eww vhvv smv^`@vMv:uU'uxuf]uM S.u @u .tE!t!qt!At?"t"s"qs8#c}s#UKs#Gs3$m9r$Y,r$Fr.%2Nr%r% q*&q~&}q&Hq&'q{'p'p#(}qpx(h;p(Tp!)?ou)+o)^o*z&os*rn*kn+c}nq+]Cn+V n,Pmp,pJm,[D\m-F>"mo-19l-4l.0pln.+5l.'k/#kn/ k/Dk0km0sj0]j1HNjm13j1i2 im2 Ti2 i3 hm3 h3 Wh4 hm4s g4^ g5H Ugm53 g5f6fm6Of6 f7em7e7Ee8em8s!d8^$|d9I(8dl94,c90c: 4lck:9'c:>b;Cbj;HXb;Mby'`f>_>_?Q_d? _?^ @y^a@1^@k] AW]^ABW]A.]B\ZB{\B1\C[VC[C S[C [QD"ZD|,tZDh7)ZKEUBYEBNYE.YGYEFeXFpXF|dX>GXGWGW6H2WHVHV.IrLVI`UINU$JGdX|FXpFXeEFGYY.EYNBEYBUKE)Z7hDtZ,|DZ"QD [CS[ C[VC[C1\B{\ZB\B].AW]B^A]W A]k@1^a@y^ @^? _d?Q_?_>_f>'`y>n`r#=`l8h=`eM=Aa_b:'c9k:lc4 :c09c,4l98d(I9|d$^8d!sm8e8Ee7em7e7 f6Ofm6f6f5g 3m5Ug H5g ^4g sm4h 4Wh 3h m3h 3i 2Ti m2i 2i1j3m1NjH1j]0jsm0k0Dk/k n/k#/k'.5l+n.pl0.l4-l91o-"m>F-\mD[,mJpp,mP, nV+Cn]q+}nc+nk*nrs*&oz*^o)o+u)o?!)pT(;phx(qp}#(p'p{'q&'Hq&}q~&q*&q %r%Nr2.%rF$r,Y$r9m3$sG#KsU#}sc8#sq"s"t?"At!qt!tE!t. u@ .uSM ]ufuxu'Uu:vM@v`^mvs vvhv wEw-spw>"wOwaw3r.xK>xbfxz<xxxJy)yOyYuy+ yF'ya6jy|FzU+ze{Ozt,rzz z*?zHzg{T;{[{{{j{{'{H{j,4|91|FN|SMk|`|m|=yg|b||}7(}A}Z}FUr}n } }u }* } }: }dL ~ &~( ;~1o O~:& c~=C v~jL ~UK ~] ~e ~!ms ~Pu+ ~}~T B-s;9HU a?gnr"yRG}A#[v3A{k).jd##b` 1RssT5"FkfJ.+R{s nfJSXu9J;+% S~~~p~ X~;@~k(~q~\~F~./~_~~}r}+]}_G}2}f}K}2{/}hv}p|k| e|C `|{ Z||| Ti\| OV<|% IC|^ C1{ <{ 6 { 0{G *r{ #N{ *{ {6 zr z z soz* bHzh S!z Cy 3y$$ycyVy+y#ydxxx(Rxj&xww2{{wusnnwkb?wcUw@[IvR=vJ1vA%NvT9v0u(u%ulSu utAttMttassus;>ssyrprch^r`%rWqCzPqqHxqg@=q%]9qqT1pJ*p @#PpV7p-o#o> ]oon'nudn%nmamfm%mOllbl? !l k k0!vZk!lk!bj#"Wju"MLj"Bj#8ij#-i##:i$ha$h$ih%#hY%g%g%OgS&g&f&xfN'0f'e'eJ(Ve( e(ydH)oyd)d/d)YcG*Oc*DPc*9cG+/b+$nb+"bI,a,a,>aK-`-`-W`N. `._.n_R/ _/^0^W05^0]1z]]1pH]1e\ 2[\c2PY\2F \3;[k31h[3'[4Zr4uZ4$Z"5Y{5Y5/Y+6X6X69X47W7W7AW>8V8V8GVH9U9yU9oMUR:eT:[T;R QT\;HS;>S<4SSg<*!R>UQ|>DP>JP.?QTP?WO?]O8@dRO@jN@qNCAwONA~MAMMBKMBLBzLWCqFLCiKD`KaDWAKDOJEFJjE>;JE5IF-IsF%4IFH$GH|G ,HGG,H |GH$GHF4I%sFI-FI5E;J>jEJFEJODAKWaDK`DKiCFLqWCLzBLBKMMBMAM~AONwCANq@Nj@ROd8@O]?OW?TPQ.?PJ>PD|>UQ>#>Q8=Q2q=UR-=R'!;SH\;QT R;T[:TeR:MUo9Uy9UH9GV8V8V>8AW7W7W479X6X6X+6/Y5Y{5Y"5$Z4uZr4Z4['3h[1k3[;3 \F2Y\Pc2\[ 2\e1H]p]1]z1]05^W0^0^/ _R/n_._. `N.W`-`-`K->a,a,aI,"b+nb$+b/G+c9*PcD*cOG*cY)/dd)ydoH)dy( e(VeJ(e'e'0fN'xf&f&gS&Og%g%gY%#h%ih$ha$h$:i##i-j#i8#jB"LjMu"jW#"jb!kl!Zkv0!k k !l? blllO%mfmmam%ndnun'no]o >o#o-p7VPp#@ p*Jp1Tqq9]%=q@gxqHqqPzCqW%r`^rhcrprys>s;usssatMtttAtu Sulu%u(u0v9TNv%Av1Jv=RvI[@wUc?wbknwnsuw{{2ww&xjRx(xxxdy#+yVyycy$$y3 yC !zSh Hzb* ozs z z r z6 { *{ N{# r{*G {0 { 6 {< {1C^ |CI% <|VO \|iT |||Z{ |`C |e |k|p}vh/}{2K}f}}2}G_}]+}r}~~_/~.F~\~q~~(k~@;~X ~p~~~S %+;J9uXSJfn s{R+.JfkF"5TssR1@ ?ac?&??%?gu?]>x>n>=c>W`>L:f> @K>m3.>&> =T = j= K=> ,t= S= 0=* =y < < =K =j = T = >&.>3mK>@ f>:L>`W>c=>n>x>]>'?R#?}6?8I?[?l?4`|?d???E?,?a?v?1??<?ug?%??&?ca? @@?1?S?vu?W?:?? ?3?\???|?c?5Jo?c2\?H?3??#?V>>>>'j>]Vd>CF>0&>>> =x==z=*T=g.=x=q<"j\53XY5&)U5wR4O4 LF4nJ4H3Gr3hE*3D2C2eCO2B2B1dCp1C$1D0eE0uF?0jG/h`I/UJU/JL/m@O.5Qh.+T.s W-Zw-# ]&-{a,e,,i1,m+q+5v9+z**?=*))J@)((Tv?(m' d'_ Z=' Q&!H&i!?9&!7%".%s"%3%"$$#$|# ,$##,$ |#$$#$"3%%s"%."%7!9&?i!&H!&Q ='Z_ 'd 'm?(vT((@)J))=*?**z9+v5+q+m1,i,,e,a{&-] #w-Z-W s.T+h.Q5.O@m/LJU/JU/I`h/Gj?0Fu0Ee0D$1Cp1Cd1B2BO2Ce2C2D*3Ehr3G3H4JnF4L 4O4Rw5U)&Y5X35\>5_H3!6dRb6h]6mgD6rq"7w{a7}Y7 7 8r U8& 8 8 9D =9 u9 9g 9 : N: : G : : ;#v L;+1 |;(3;4<;ADg>&>0F>Cd>V]>j'>>>>V?#?3?H?\?2co?J5?c?|???\?3? ??:?W?uv?S?1?@C>l>C@?pCǠ?ǻܿCR?Bz C'A@3=,C1@f5JCkR@&/iCvs@@]-Cg8@fiKCĚ@&.qC_@`׽ӯC= @GZԲC{@poqC@q1 ]C^b@KL/6CG@h0BzCAߗ~iC!A(䓾w WC'AWEC!AHⶾN!2CM*A8ɾC[2Ahkݾ$pC;AXd#+6~C;DAĹ1~CLA/8~CuUATwc>~Cg^Ay&zE~C.gAd2RK.u~C&oA\?QiY~CoxAMW<~CՀAlZ0]#~ChFAh0 d~ChALwj;}C1AJp}CAu}C)Aқ{}}CAΨԀZ}C.A袿뵃6}CAVY}C?A"~bP|CͭAм1.|CO]Aſ\|CA& Ͽv|CAroؿ9gL|CG An"|CA{C[Aa4{CA{CܢAǐr{CJAU C{ClAc{CA?$zCASA*zCVA2 WzC)AK%zYOzCtA+߲zC/A1=yCAo7 yC;Bs=|yC8BYC0FyCB׻IstyCBOxCje BVV0xC( B\0bxC(4BBc@(xCfBeicxwCB~p[ wCsB)9wrwCzB~V4wCNBtïvC߽Bֲ[vCU. BruvC"B 4vC%Bە6uC'B)5!uC)B}ݗczluCq,BLA*(uC.BHtCta1B 7tC3BצVtCbU6By4tC8Bl8 sCM;BRl'%~sC-=BdYף{4sCI@BNPrCBBK/rC)JEBFQRrCGBy^2TrCoNJBxsuqCLB+bVjqCVOBzmqC QBLbTpCqbTB{pCVBNW*pCqYBH)oC[BH5oC^B(q3oCaB ~ƒnCcBy+ފnCG(fB5B„5nChBt}vmCCkB"³mCmBK>1mCbpB$WdlCirB/cCB@@A2LccCGB9C8bCHB!FF@1bC}BHޖ·%bCqBSK¡aC;BM;NaCBhP¡`CޡBR¹u`Cn0BtU 7`CrBXsh_CԥB5Zj+_C 'B^8] ^CyB_0@L^Ca̩Bcb$o]CNBod½j]CerB(gv\CŭB(-j\C BalP#\ClBdo+ˠ[CJBRr,[CBtfZChB@wo \CZC(BCy 7YC\B܂|i^šWYCdB\%XCB[¢iXC Bn6NWC9bB㈃zyWCBۄT•WC B.'VC`BK4/ VCBֈJ¯UC B*^§UC_BE~dpTCBҌ TC B]'jSC _B-|/L&SCHB"ѐ©RC B5&„*RC^Bb{oܫQC(BДɲ²,QC| B%PC^BB{t,PC&BИͰ3OCz B%* +OC^B,{dNCB_М@'NC_ B|%¡MC^B|z…!MCֳBYϠwLC B $wgLC$^BxPUTKC8B̤EAQKC=B [+׊JC2]BtJCBVȨ}~ICB HC[Bn5MpHCFB‡GCBK3M`GCGYBe^ FCB:‚NFCBsECUBW:ECB ‚DCBm¤%DCQB`F]iWCC1BŔv:CCB wBCKB/ACB]|hACBJȽm@CDB6­M@CB^s?CB@0?C@CTBO9VIV>C5BԀ =CCC;7CC~Xk:7CRCx6CCFº6C6C  |5CPH CB+4CE C(zrE]Q4C C 53C< CԒ µ$3C; Cv7 ݍ2C CL ¯1C/ C~| *_1C C-B P0C{C "/0CN!Cr }Z Ÿ/CC6' .C&lC2a ¢d.CNCr, (-CHC?w ^1-C[C ] C,CCge+CC !b+COHC|Bh*CVCb+*C*CL@')C3CL;(C5C`X(ClzC\„'ClCazº'C6C§&CbCF3M%C#C)«F%CEC&: è$C-ICfIW*– $CCV#l#CMCfb5m"C-Cl˹s."C}Cs<6!C:oCy޾¸ CC}?O CCCO C?}C C޾y:oC6!C<s}Cs."C˹l-Cm"C5fbMC#l#CVC $CW*fI-ICè$C &:ECF%C)#CM%C3FbC&C±6C'CzalC'C\lzCX(C`©5C;(CL3C')C@L*C+*C¯bVC*Ch|BOHC!b+C· C+CegCC,C ] ’[C^1-Cw ?HC(-C, rNCd.C 2a&lC.C' …6C/C}Z r N!C"/0C {CP0CB - C*_1C| ~/ C1C •L Cݍ2Cv7 ; C$3CԒ < C53C › C]Q4CrE(zE C+4CƒBPH C|5C³ 6C6CF¾Cx6C«RC:7Ck~XC7C;>hCb8C![C8CGžC9C"I] CG:CmcCq:Cõ´7C9?;CC;CBdRLjCaCVIO9TBơ>C@XB3dCGB1mCKmBmC"£CkBvmC}thB5nC5BG(fBފnCy+¢cBnC ~aBq3oC(^BoCH5[B)oCHqYB*pCWNVB{pCqbTBTpCbL QBqCmzVOBjqCbV+LBqCuxsoNJBTrC2y^GBRrCFQ)JEBrC/KBBrCPNI@B{4sCףdY-=B%~sC'RlM;B sCl88B4tCybU6BVtCצ3B7tC ta1BtCH.B*(uCLAq,BzluCc}ݗ)BuC!)5'BuC6ە%B4vC "BuvCrU. B[vCֲ߽BvCïtNB4wCV~zBrwC)9wsB wC[~pBxwCceifB(xC@Bc(4BbxC0\( BxC0VVje BxCOBtyCs׻IBFyC0YC8B|yCs=;ByC o7AyC=1/AzC߲+tAYOzCzK%)AzCW2 VAzC*ASAzC?$A{CclAC{CU JAr{CǐܢA{CA4{Ca[A{C뿌A"|CnG AL|C9groؿAv|C& ϿA\|CſO]A|C1.мͭAP|C~b"?A}CVYA6}C뵃袿.AZ}CԀΨA}}C{қ)A}CuA}CpJ1A;}CjLwhA~C0 dhhFA#~C0]lZՀA<~CWMoxAiY~CQ\?&oA.u~CRKd2.gA~CzEy&g^A~Cc>TwuUA~C/8LA~C1Ĺ;DA6~Cd#+X;ApC$hkݾ[2AC8ɾM*A2CN!Hⶾ!AECW񤾉'AWCw (䓾!A~iCߗAzC0BhG@C/6KL^b@]C q1@Cqpo{@ԲCZG= @Cӯ`׽_@qC.&Ě@KCfig8@-C@]vs@C/i&kR@CJf1@C3=,'A@Cz BR?CܿǻǠ?Cp@?Cl>>CC>l྾C>>pC#?G\`Cf?»ziCu? 3=7Cg?foʿCAv?ڦ/'Cs?@ݼKC6@fi9C#@&1.!C5@`W/uC@H@GZ>ƟCr,[@poqLCTn@q [~C܀@K̽/6ilC@0Bw"YC@ߗDCd@(w.CϨ@$WC7@H6N!~Cz@8I۝_~C@hk]~C@Xrd#Y~CS@Ĺ䰱~CS+@Ꮎ/u~C`@TwcV~C/@yz6~CIAd貾RL~CR A\¿}C'A;`}CAlھ0}CA0 }Co!AL!^}CnY'AJ+6}CP-A  }CU3Aқ|C'h9AΨ|C?A"d|C@EAVY+]|CKA"3~b /|C7RA<1. {ClXAER{Cg^A& O{Ce]eAroX9gj{CUkAnb7{C#brAk<{CxAuaWzCAgzC$Aǐ!m]zCUAU$l$zCcAc&cyCPA?$)TyC!A*+@syC7A2W.(6yC~AK᥿z0xCQ7A2xC1A=5xxCOAo 77xCAs9wC~AYÿ0vnwCưAϿ@h)wC}rAVֿ0BavC$Aܿ0EavCܻAB㿳@GjTvCsAecI| vC]A~[KuC&A)9MvuCAVO*uCAtïQ/noC] BEQBp9oCץ Bx^F2rnC BxsJus~nC(B+NbVunCBzRmvlmC.$BLVbx`mCIB[ylCqBN_W{lCBcH|9lCBgH5~kCB(lrqkC)!Bp ~ kC\#Bty+֥jC%B5Byց>jC'Bt}}iC*B"UniCA,BKăiCN.B$WdhC=0B͇/hCW3B=ǚgCI5B[1;WgC7B5ƆfCq9B.W{fC&Br eCa@BXj,eC!CBXM<~dChEBbIdCGBy ߈cCqJBkmccC-pLB"У탋AbCNBK7YzbC{)QB}tbCSB [aCtUB{ZGaCMXBeɍ{`CZB_6&`C]BԴ­_C؂_BL(3_CaB_ƹ.m9^CYdBGBϏ=^C8fB@/]C88iB@@2;E]CkB98\ClnB!F@I\CpBޖt sBSK[CuB;ZC*wBhJZCwzB֒YCk|Bt GYCutBshXCB5AXC;B^8WC}B0'9WC!Bc$oVCBo$.VCGB(vUCB(- UC"҉BaPTCvBd+TC_BRهSC=BaRCB@o RtRC8BC 7QCB܂i^o^QC͒B\%PC2B[;FPCcBn6NǖEOCeB+OCsBTNC!KB.NClBK4MCSBJLC7B* ^_LCꇞBE~ dpKC؟B =KC)B]'JC{B-|/:JCͣB"FIC B5&HCUtBb{o^HCMȧBɲSGCB%T5GCqBB{tԟFCIǫBͰ FCGB%*XsECsB,{^DCʯB_DDC"B|%CCyB|zCCQҳBY w{BC+B $"wgUACFBx#PUHACݷB$EAB@C7B &[+@C\Bt'`x?C0BV(>ChHB*ޖ@>CBn+5<=CB,R=CL\BK.3i8CB`F7]i 8CJBŔ8v:tp7CB9 z6C| B/;ה.6C3iB]|R;X*C?GB~XSk)CPBTᙏ|I)Cw BUF(CpB Ww'CBBXO'C_7B(zYrES&CʚBZ%CAB[ԒS%CaB]v7$CHBL^ڌ#C(B~_|HT#CaB-`B"CBa{!C{SBr c}Z7S!CB6d' CB2aeC}Brf,PCpC?gwʼnգCc"Ch ]mC CgieJCC kC?7C|BlhCCbmCCFCLn@BCKCLoІCCp`:CiC\qΌC_CarzCCtC0CCFu3Cr C)v C# C&:w -$C CfIxW*)uC CVyC;6 Cfbz5C Cl{˹4gCD Cs|<CGCy}޾CC}~?WCCCWC?}~CC޾y}GCCRGB@+C!PkB+CGאOB,C"I]N Br5-CmMYB-CõK4B.CJB8'/CBdRI2B}/Cd HBmq0CɒFcoB1C ԀE BH1CVIO9DB1^2CBpKB3C@ÓAB3C^@؉BG4C6?d)BF4CmJ=,Bb5C]|<3iB.6Cה/;| Bz6C 9Btp7Cv:Ŕ8JB 8C]i`F7B>8Cm5YB Q9C •4 /Bs9CW3 Bs:Cs2[sB .;C:0B8;C^e/BiCޖ*hHB>CV(0B`x?Ct'\B@C[+ &7BB@CEA$ݷBHACPUx#FBUACwg $"+B{BCwY QҳBCC|zyBCC|%"BDDC_ʯB^DC,{sBXsEC*%GB FCͰIǫBԟFCtB{qBT5GC%BSGCɲMȧB^HCob{UtBHC5& BFIC"ͣB:JC/-|{BJC]')B=KC ؟BKCdpE~ ꇞB_LC^* 7BLCJSBMC4KlBNC.!KBNCTsB+OCeBEOCNǖn6cB;FPC[2BPC\%͒Bo^QCi^܂BQC 7C8BRtRCo @BaRC=BهSCR_BTC+dvBTCPa"҉B UC(-BUCv(GB$.VCoBVC$oc!B'9WC0}BWC^8;BAXC5BXCshutBGYC tk|BYC֒wzBJZCh*wBZC;uBK[CSt sBޖpBI\C@!FlnB\C89kB;E]C2@@88iB]C/@8fB=^CϏGBYdB9^C.m_ƹaB3_C(L؂_B­_CԴ]B&`C6_ZB{`CɍeMXBGaCZ{tUB[aC SBbC}t{)QBYzbCK7NBAbC탋"У-pLBmccCkqJBcC߈y GBIdCbhEB~dCB fCG&jCց5By%B֥jCy+t\#B kC ~p)!BrqkC(lBkCH5~gB9lCH|cBlCW{N_qBlCy[IB`mCbxLV.$BlmCmvzRBnCbVu+N(B~nCusxsJ BnC2rx^Fץ B9oCpEQB] BoC/nK>^uBoCPmN:`B@KpCףkeY6NBpC'iRl2o?B\pCl8h.eAUqCyf*fPAqCd&@ArCb #6AVrCaH1ArCLA_1A?rCc]|O7APsC![*50BAsC6YڕrRAjsC W hAAtCUr ?AtCֲSܣA׻ɿxAwC0G@H@uC/`W5@C.!&1#@9Cfi6@KC@ݼs?'C/ڦAv?Cʿfog?7C3= u?iCz»f?`C\G#?Cp>>Cl྾>C@E?AEPA(E!B ƾY«EHcBRAdE|B'̄&E4BT“E%B$$&EDBhZtE2DD2tE7D<‹ñsE:fD1{DqQYEDw;1-XE緻D߂c=WEOaD;(EK7D+øn:=ERmEjød.E1:B)EE/,6' (EE_@:&EE3Vą%E;VEٱfĵY$E EWAs#EE}s{!EE E_E_E EE!Es{}E#EAsW EY$Efٱ;VE%EV3ÆE:&E@ĕ_E (E6'/,ÆE:B)E1F>Ezy*E˱9E+E-kfEZ,Ej ćcE.E_ ÁE?J/E( s|ED{0E Eɂ E1Eݫ _l6 ET2EQe aKl ET4EO{ϛ E15ElÜME$\6EpķE7E9|ÇE98Eēò_E9E6Hz_Ea:EďÒE.EjhčL#D.x?E縿:D@ERi"ÚHDsAE̽CQDBE_"ĥiD CE-ĿOSDDE!D FE' wDGEp=jÏZDx,HEŪFD;IEtęæDGJEvý]DRKEKU΢ÕD[LEo$< D"cMEzü^DbhNEċК#DkOE%{ DmPE G{^DWlQE]В2DiREģ&é DdSE}A_D^TE}xԊDfDZE"ĦuDZEjpmD[E&jĞzkToD\EXĢFfȫDV]EXa"DC^E Ě[}Db_EZNVj٣D ?`E-RQ5DaElcLD@aEʈGD3bEB PDcE^= DgdE68'D-5eE3?sD fEĮ.v֎DfE" IJ):DUgES9 f%mDQhE&F *: EDiEvH ^{loD iE(@ b؁DJjE(-Ėæ~DCkEZZ V`yDkEtEìjV-DkJuEUS4(D~uEÜɎ3#DsUvEäDCvE aDSwE=(s“=D[wErfŸhDExE,Y¡ DxEL­DU*yER}@DȗyECøt4—CzEi(ChzEEØcsC2zEaè^KCE,{E+bxoC{EGqC2{Eð nC7|Eжm;CR|ERpLC'|E9ɊÐICw$}E#0ž#CBlTEEdB$buEBhڿ<~NEڜB f#E>B@-Z(~EBtSקI~EB@|҄~EʡBF~EWCv=~E1* C`W2}EC ²q}Eb$C!}EQ0CPy|EivDD^{uHLdEPHD*:'Fn)cEƝMDfT9ß?bEYRD֩"RaEWD‚Qc`E>[D³Ԏp_E`D6¿è{^EeD^è]EpjD\EWoDʈ[EEtDkchZE9yDR,ñYE4~DNYbXED Ä{WEDX!qVE~DFYAdUE>/Dz&jTTE介Djʕ0CSE_KDƒ"/REݐD1DqÚQErDw;1OE DbNE6D;(zME@D!,TLEߝD ~xOKE"D} ïÁbJE$D&ÜÚE)D*ⱖU=ED=j-p'EuÙ(ù?D?Ev%ۊDr@EKU"à׵D/BEo$ &D`CEzZvDDEËtȭDEE%yDFE G{{rDHE]ËʥD/DAdUEYâF~D!qVEX¸D{WE ÚDbXEYN½4~DYE,R9yDhZEkc‚EtD[EʈWoD\EpjD]E^eD{^E6`Dp_EԎ>[DQc`EîWDRaE"ò֩YRD?bET9fƝMDn)cE'F*:PHDLdEuH^{>DD/dE(@bǖE?DeE'-Ö:DfEZZm6DgE…1D?ehEk§`-D9iEBf#E ڜBNE<~€hڿBuE$b€dBETEl>BZĒ('BEd RAE FH4AwE(zDAeE=@`E@NG@30-,,)$ |X5f=b,^]B](YXspnm~mTjHд?EPUUU?9B.?v6 wV9 i< 5x? B _E H eK <|N oQ ^T IW L0Z ] _ >b ge sh Ak  n {p s Sv y D{ y~ ) oփ T $ Ƌ e  ,  J C՝ \ a cߧ Z Ѭ oF  & z I 5a EĽ ~$ i4 + x E  ]P 1  K ú  N ]{ v  N N7 V r Ռ Ѻ  "  + T m z   0b`(t YQ P= ! z ??ozh^B./'_ VӹO ~dE"H#, %n&>'n3)* ?>y|6>՟jV=f >+ ?W>ƈ[^>Eo\FL>/T,X>L \om>0S:zb>Ǔ O>|si\a>d3j>酲j%M>úbxa>zz[>)U]H>ܥY#4o> z>i>GP>G2k>\6t|>)l>C:|>YHȆ>$D2Hi>&Ȏ֊s>7cO>3/c>t~5c m>5DSx>CVj>zzk>C&{y>RUWk>O\>krN>Z5>stu>x_?3->QH,y>^S>#\>ȚS>qzQZu>wЀ>oO؋C>iﮂ>(9>TlZEW>J9>8W>BĮ^>rqJz׎>wQw>YWm>c'bC>VK(؀>X>L!\>&uY">V~ >Ea> 1 AS>™5#i>/#~>1kb҂>~o㴃>xv>5DS>X G>wX:>((b>3>&l|Z>iux>H>!,w>(N>5c>/0@>>L$>7>G.HՃ>VP>]~9>~ߒœ>$`}>15^>4{>->${4>jmU2>[P>gә>?xb> YV؟>T/wё>* 4r{>_y>|>rb:>訖>85Cj>> U>iK->U>ON9_>oO؋C>zQ=>l>-6ǝ>[a>gyi ->;w-s>QE%e>#(s~>^&1>k>g >ܞ삍>΂$>O>*?????Xt?B?0 ?6e?A???Eø?m?T^?'??~?*?R?q?+M?&?0????ZR?#????!\?'???%@?$??\g??A)?Z??G?L?0?a?_??wv??,?Ȇ??9???B?3??G??#?@H??)?D?X?M?=???2??j?$?Bt???#a?X?.?R%?K?>r?o?r?I? ?q/?T?y???_? ?01?[U?^y?7??t??+?(N?q????[????a?S??y?? ?-*?$K?k??@???2?ջջŻubbiNL|Au5*N_(=yy3y3376;6L54kkoonmelxxxvfYFFM@<?@@@$@|w}7}c}c}m}7}|e}~~e}|>߁|U^|MT4l|ӈ?P|=D4|alƃ|W` A%%%%%%%%%%%%ffffffffffffffffffffffffffffffffffffffffffffffff   !!""##$$%%%&&'''(()))**+++,,,---...///000111222333444555566677778889999:::;;;;<<<<====>>>>????@@AABBCCDDEEFFGGGHHIIJJKKKLLMMMNNOOOPPQQQRRSSSTTUUUVVVWWWXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffgggghhhiiijjjkkkklllmmmmnnnoooopppqqqqrrrsssstttuuuuvvvvwwwwxxxyyyyzzzz{{{{||||}}}}~~~~??   ;Ac@0C@(#)mediaLib:0250:20060825:amd64mediaLib:0250:20060825:amd64F2k?c}ؿc}?F2k?;f?c}? iAT!>CT!>DT!>"DT!>*DT!>s,DT!>,DT!>-DT!y>-DT!i>-DT!Y>-DT!I>-DT!9>-DT!)>-DT!>-DT! >-DT!=-DT!=@ `P0pH(hX8xD$dT4t L,l\<|B"bR2r J*jZ:zF&fV6vN.n^>~A!aQ1q I)iY9yE%eU5u M-m]=}C#cS3s K+k[;{G'gW7wO/o_?j|kFEMFRԨԨ&]88H]@@????O??>>L>*>%I>>9==.==ى=%I===p=9c=6W=L=1 C=.:=C2=*= #=ى=&=%I== ==!==><<<9x;>`;;ֹ;;.;s;;;e;;%;9;x;8p;\;g;b;t;;6;+;{; h;! ;;ud;;;(;E;; ; ;e;07;1 ;;;0;;i;dR;>;.;D!;;;a ; ;A ;c;C;; ,;:;AL;+`;v;j;;Jȩ;?; ;/;U;~;ϩ; ף;^;7;1k;; ؟;f;L;ى;Ȝ; ;pL;萚;#י;;h;&;.;O;%; ;E;;;%I;;;8Z;$;z;5x;Rڍ;=;;;4p;؊;B;A;;;;g;ن;K;7;4;;!;0;o;;!;;;;;;~;};|;{;-#z;/y;>x;@Nw;>`v;tu;t;ՠs;ֹr;q;p;p;.o;Pn;sm;l;k;dj;j;e9i;eh;sg;f;me;%e;3Yd;9c;b;xa;5a;8p`;_;\^;'^;g];\;b[;/[;tZ;BY;Y;ZJX;6W;DV;+V;xU;{T;3T; hS;R;! R;TaQ;P; P;udO;N;N;'qM;L;x)L;(K;J;EJ;4I;I;yjH; G;2G; F;tE;eE;D;07D;>C;1 C;xB;A;PRA;@; 0@;0?;+?;>;=;i=;S<;dR<;A;;>;;V:;.:;9;D!9;Û8;8;7;7;16;a 6;H5; 5;:4;A 4;3;c3;|2;C2;1;1;0; ,0;"/;:/;>.;AL.;-;+`-;,;v,;,;j+;+;*;/9*;J);W);?(;y(; (;{';/';&;U&;%;~%;%;ϩ$;)@$; #;rn#;^#;Ϟ";7";:!;1k!;!; ;< ; ;zt;f;ͮ;L;;ى;");;i; ;ߪ;pL;s;;3;#;z;;;h;A;&;uZ;.;P;O;;%;H; ;;E;;;E;;;%I;;; P;;̫;8Z; ;$;g;z;;5x;);R ; ;= ; ; ;U ; ;Q ;4p ;k$ ; ;͍ ;B ;t ;A ;\d ; ;;;?;;p;g;3 ;;#;K;<;7;|y;4;;;^e;!;;0;U;o;u;;NJ;!;6;;(C;;";; @;y y f V F 6 '  Ap@@N3DN7@@ @'%Z!hX]ND?w=940,F(?:kaWMC}oaS; 66%%@P@0@?е ` P  Y P `P`P @oooPppqpqqpqpqqPrrs`s%`%%`%% %%@%0%%`%%b@cPde%0%%%%P% %%% %%%A0M@T`[@`@P  P @  @0p@@ `0Pp@P  P0   P  @  `@pP PP0p   @  `%%%p$$$% 5p550667p77p88p9:: ;;P<P++,p,,`--p.//@0112P304@&p&&&0'p''(P(((@))*`**@pPUY_f@pzpГpp0IpKPNQ `@m`q tw&P&@&&P&&&&@H'C'9'P*'Ф&&&&&p&&P&P'p'#'&'''''_'['pW'T'|'w'o'Pk''`'''fbp^Y@L'M'PO'Q'b'd'e'@h'p''p''RTUpW'Y`'`W`'U 'SRP O@MI`JK L0##0n#N#@#|#@`#<#""""""" "''(P(`""##'0'P''Ъ'''0'#0#`###p#"#'#9(`<(`?( C(,#p/#2#7#,(p.(`1( 5(( (#(@'(p#`##@##0## # ))))))))**.*)*O*+v+V++0; `**`o*+6+p@0`p`PppPp0Ppp@@`ppP0pPжP0 @00@@Ф00P 0 @ `p@@ 0  vw }Ѓx@~ @x}`w y~{Љ`z@`rr0sstupuv p P  D DD`DDD@DDD DD`DDD 0`PЅp |pp0` p  @`@ @ p ppPppepgik`nq_Pt0cy@I KMORTpCWF\@.0024P68(;+P@P @ `  `%pp} zvЁsq0fodmapj\@h0Z XKU@HPSEOP@pM=p;`.09+6`(2#0               **.*)*O*+v+V++0;  /P/ /p / //b hm/tz`0/@p/@ /p/p /\di.@ow|. p`.0Р0.p .00`/@-/4/JO>/@^m@rD/@ЖpK/`/@@/p(./4GM/ Rjo !/t 0$/ @00`PP`u/30_r/1] k/*@Vp@`/e/i/o/{/@)P/ /`HTZm }г0`]/b/pm/x/@-~/9PJXd@o` Prrs`smn`nn@&0'0(0p)00+0+0,0@.0///p0 000 0P1111`11011011`1 1P11`1@111111`11011@111101 1`1P11011p 22p202222 211221P11111p11`111p1111P1 11p1p11@1@11PPpP  PP @PP 0p@22202в2 222@2222222 2$33033 33P220S3O3K3F30=393p5303@3A3@C3D3P*3+3-3.3RP O@MI`JK L\`zB`0`?CDpj{0@@/P{##`###@### $@$$$$@$$@$$$=0C0?0pM0A0J00F0W0H0U0O0@R0g0p0 j00l0P{0s00w000Ђ000p000Ы000000000@01`1D1 181!1q1 -1e1M1Y1%`%%`%% %%@%0%%`%%b@cPde%0%%%%P% %%% %%%A0M@T`[H0U0O0@R0507090;0w000Ђ00pZ0]0`0 d0000000З0Л00 -1e1M1Y1 0 0 0 0}20~2p~2}20}2`}2}2p22 222`22`22p2222 222~22@222 2p2Ѐ2@2222 22222P2202P22222P22022@2Р2p22Т222P202022 2`222@222`22022@2202М2zRx @Gb`<GDTGDlGDG>AI$HWT@HBe`$JWT@JBe`$4LWT@\LBe`$|NWT@NBe`$OWT@PBe`$ QWT@4RBe`4TSBEB E(D0D8G4@WBEB E(D0D8G4ZBHE B(D0D8D4@\BEE B(D0D8D$4]b\`)]`|a)]`$be$defbp @h]@4,iBEE B(A0D8J4dmBEE B(A0D8Jpq]@40rBEE B(A0D8J4vBEE B(A0D8J$,ze4TH|BEB E(A0D8J4~ BEE B(A0D8J4ȀBEB E(A0D8J4p BEE B(A0D8J$4He$\e48jBHB E(A0D8J4pBEE E(A0D8G4苼jBHB E(A0D8J4, BEE E(A0D8G$dib$ib(XP@]`87b`Xzbp47b`Tؗzbpt8]bp$xb]bp$Pb 螼bP$ ؟YbpD bPd Ybp HbP 8Ybp xbP hYbp 7b`$ ȨzbpD (7b`d Hzbp @]` ȭ@]` 讼 ]P د ]P ȰbP$ YbpD bPd 購Ybp (9ep H^bp ibp ظ^bp ibp$ h^bpD ibpd XP 込XP ؿXP XP XP ¼XP4$ üBHB B(D0D8F4\ 0Ǽ;BEE B(A0D8J4 8˼zBEB E(A0D8J4 ͼ;BEE B(A0D8J4ѼzBEB E(A0D8J<ӼXP\Լ@]`|ռS0pּXP`׼XPPؼ@]`pټXP`ڼObp<ۼXP\ܼXP|pݼXP`޼XPP߼XP@XP0XP @]`<@XP\0@]`|P]`P]`P ]P@ ]P0 ]P ]P< ]P\ ]P|]@]@pXP`XPPXP@XP<0XP\ XP|XP@]` XP@]`0bpS0<XP\pS0|XPS0XPpS0XP4BEB B(D0D8D$The$|e$e$` e$H e$ eD8^bpdx^bpXPXPXPXPxXP$hXPDXXPdHXP8XP4(BEB B(A0D8G4BEB B(A0D8Gb`4b`T S0th!XPX"S0"XP#XP$XP%XP4&XPT'@]`t(Obp)XP*XP+XP,XP-XP4.XPT/Ybpt0Ybp2Ybp$H3ib$4RVPH51b`$h69b`D71b`d89b`91b`:9b`<1b`(=9b`H>1b`$h?9b`D@]@dHA)b`XB]@C)b`(D]@D)b`E]@$F)b`DG]@dH)b`I]@XJ)b`hK]@(L)b`8M]@$M)b`DO]@dO)b`P]@Q)b`R]@hS)b`,xTsBHB D(D0D44UBEE B(D0D8D4l@WBEE B(D0D8DXYbpYYbp8[)b`H\)b`$X]XPDH^XP$d8_b`@]`a@]`b@]`d@]`$ eb4 fXPT xg@]`4t hBEB E(A0D8G4 pjqBHB E(A0D8G4 kBEB E(A0D8G4!mqBHB E(A0D8GT!nXPt!o@]`!pXP!q@]`!rS0!sXP"xtS04"uXPT"uBAG t"hwD"pw<BAG "yD"yD"yD"yD #yD$#yD<#yDT#yDl#yD#yD#yD#yD#yD#yD#zD$zD,$zDD$zD\$ zDt$(z"D$@z%D$XzD$`z"D$xz%D$zD%z"D%z%D4%zDL%z"Dd%z%D|%{D%{"D% {%D%8{D%@{D%H{D &P{D$&X{D<&`{DT&h{Dl&p{D&x{BAG 4&} BBB B(D0D8D&A&ȆA 'ІD$'؆iX0D'(A$\'0b'A$'b'ȌD'ЌiX0$' ^BED D(F0$$(X[BED D(F0$L(^BED D(F0$t(ȍ[BED D(F0$(^BED D(F0$(8[BED D(F0$(p^BED D(F0$)SBED D(F0$<)[BED D(F0$d)^BED D(F0$)PSBED D(F0$)[BED D(F0)bp)`bp*AGG <*AGG 4\*0BBB E(D0I8GP*{]0*hx]0$*ȖE_G@$*0E_G@$$+E_G@$L+E_G@$t+hE_G@+И]0+P]0,+ЙxBBE E(A0, , eBEE E(A0LP<,`kMNG0\,gJT0|,b`$,`BGA D(G0,衽yX0,HQbP-\$$-襽 e,L-:BJB B(A0A8$|- eR-h)ep-xýe`-hŽ-SŽ.?Ž,.+ŽD.Ž\.Žt.Ľ.Ľ.Ľ.Ľ.Ľ.Ľ/~Ľ/mĽ4/\ĽL/KĽ d/<Ľ |/-Ľ /Ľ /Ľ /Ľ /ý /ý  0ý $0ýAAD 4D0MŽBBB B(A0A8D4|0ʽ>BBB B(A0A8D@40˽BBB B(A0A8D`40ͽBBB B(A0A8D`4$11Ͻ'BBB B(A0A8D4\1 ѽDBBB B(A0A8D@41,ҽBBB B(A0A8D`41ӽBBB B(A0A8D`42Sս,BBB B(A0A8D4<2G׽BBB B(A0A8D@4t2*ؽnBBB B(A0A8D`42`ٽBBB B(A0A8D`42ڽBBB B(A0A8D$3ܽ:)eD3BPA ,d3BQB B(A0A8,3xBQB B(A0A8$3 BQA A(,3 BQB B(A0A8,4` SBQB B(A0A8L4D,d4hBOB A(A0,4H\BLB B(A0A84xAK,4BLB B(A0A8,5BLB B(A0A8$D5el5`!s5#Q$5'e$5vAD$>ADD>hADd>AD>X AD> AD>H AD> AD4?h BWG B(A0A8GADJ>AD4Jh?BPG B(A0H8G$J0F&e ,K8K8BEB E(A0A8,DKHMBHB I(A0D8,tKOBLB B(D0D8,KhSSBIB B(A0D8,KV)BJE B(A0C8,LZBEE B(A0A8,4L_BEB G(A0C8,dLgBJB B(A0K8,LnBFE E(D0A8,LqBFB B(A0A8,LHtgBFB B(A0A8,$MwBFB B(A0A8,TMhzBEB B(A0D8,MBEB B(A0C8,M< BEG B(D0H8,MDBEB B(K0C8,N(8BEB E(A0A8,DN8BHB I(A0D8,tN蟿BLB B(D0D8,NXSBIB B(A0D8,N)BJE B(A0C8,OBEE B(A0A8,4O诿BEB G(A0C8,dOBJB B(A0K8,O$BEB E(A0A8,OBHB I(A0D8,O¿}BLB B(D0D8,$PſHBIB B(A0D8,TPɿ&BJE B(A0C8,PͿ}BEE B(A0A8,PXҿBEB G(A0C8,PڿBJB B(A0K8,QhBHB B(A0A8,DQ(BEB B(A0A8,tQBBB F(A0A8,QBFB B(A0A8,Q8BIB E(D0D8,R[BBB B(A0A8,4RBBB B(A0A8,dRBBB B(A0A8$R(de ,RpBHB B(A0A8,R0"BEB B(A0A8,S0BBF B(A0A8,LSBFB B(A0A8,|S`BIB E(D0D8,S [BBB B(A0A8,SPBBB B(A0A8, T!BBB B(A0A8$\he_\e\(f ]fE$]gBPJ D]g^\]hmt]Xhb$]h1BTA D(,]iQBMI G(A0,]jQBMB I(D0A8^(lv,^lD^m~$\^m|BTA D(,^nBMI H(A0A8,^xpBMI D(D0^r^r_(s$,_sBTA D(,T_HuFBMB H(A0D8,_hwBTB D(A0_XyE_yA_z^_`zm`zb,`{AJL`{ AMl`|AJ`}v`8~`~~`(9AJ`HBMA aiAJ4aLada(|avAJ$aBHG D(a؇AJaxGaAbH_,bmDbc\b@AJ|b AMb AJbwbxbch>AJ$cBMA DcrAJdcX|cГcxcAJ$cp BHG D(cXAJdG,d@ADdȚ_\dmtdhcdAJdAMd AJdweep4e؟;AJTeBMA tehmAJee0eؤe`zAJ$eBHG D($fAJDfHF\ftfYf`lfgfAJff gl$gXBEB H(G0A8,tx@[BQB B(A0A8,DtBBLJ H(G0D8,ttDqBOJ F(D0F8,tFBKH E(D0D8,tXI5BKE B(D0A8,uhLBSX R(D0A8,4uPBHB B(A0A8,duSOBLL L(G0D8,uVbBBB B(F0A8,u8YBEB H(G0D8,uZBKB B(A0I8,$v\BLJ L(G0D8,Tvx^;BOJ M(G0A8,v`~BKH E(D0D8,vbBKE B(D0A8,vewBSX R(D0A8,wiBBB E(D0A8,DwloBLL L(G0D8,tw(p[BBB B(D0F8,wXrBEB H(G0A8,wt[BQB B(A0A8,x8vBLJ H(G0D8,4xxqBOJ F(D0F8,dxhzBKH E(D0D8,x|1BKE B(D0A8,xBSX R(D0A8,xxBHB B(A0A8$$yhBIO A(,Ly@6BBB B(F0A8$|yPgBEA A(,yBEB B(A0A8$yH~BIM A(,yBIR B(A0D8,,z`BEB B(D0D8,\zPBEB B(D0A8,zBJV B(A0D8,zКBBB E(D0A8$zBIO A(,{=BBB B(F0A8$D{wBEA A(,l{BEB B(A0A8${BIM A(,{BIR B(A0D8,{ب'BEB B(D0D8,$|تBEB B(D0A8,T|xBJV B(A0D8,|hBBB E(D0A8$|8#BIO A(,|@4BBB B(D0D8$ }PBEA A(,4}Ⱥ#BEB B(A0A8$d}ȼBII A(,}`CBIN B(A0A8,}[BEB B(D0A8,}BEB E(A0A8,~wBJV B(A0D8,L~BHB B(A0A8$|~+BIO A(,~9BBB B(D0D8$~BEA A(,~P+BEB B(A0A8$,PBII A(,TKBIN B(A0A8,`BEB B(D0A8,8 BEB E(A0A8,wBJV B(A0D8,hBHB B(A0A8$D8je4lBEE E(D0D8F4BGV E(D0D8J4܀!BBB E(D0D8D4BFN E(D0D8Gp4L0_BBB E(D0D8G`4X+BBE E(D0D8F4PBBB E(A0D8G4BBB B(D0D8Dp4, :BFJ B(D0D8Gp4d(BBB B(D0D8G`4BEE E(D0D8F4Ԃ BGV E(D0D8J4 "BBB E(D0D8D4DBFN E(D0D8Gp4|_BBB E(D0D8G`4/BBE E(D0D8F4BBB E(A0D8G4$HBBB B(D0D8Dp4\;BFJ B(D0D8Gp4 BBB B(D0D8G`4̄"BGB E(A0F8F4%BGT E(D0D8G4<(BBE E(D0A8D4t+/BFM E(D0D8Dp4-BBE E(D0D8D`4x/)BDE B(A0F8F4p1BFL E(D0D8D4TX4HBBB E(D0A8Dp4p6BFF E(D0D8Dp4Ć88BBB E(D0D8D`49BGB E(A0F8F44h<BGT E(D0D8G4lP@BBE E(D0A8D4C/BFM E(D0D8Dp4܇EBBE E(D0D8D`4F)BDE B(A0F8F4LHBFL E(D0D8D4KHBBB E(D0A8Dp4MBFF E(D0D8Dp4OBBB E(D0D8D`,8QAC L(QAC lQAC  QAC PAC ̉PAC PAC  PAC ,PAC LPAC lPAC PAC PAC ̊PAC vPAC  jPAC ,`PAC LVPAC lLPAC BPAC 8PAC ̋.PAC $PAC  PAC ,P#AC Ls\AC lDs_AC sAC tAC ̌t%IAC  AC  TIAC ,<AC L"6AC lFSAC ]}AC z]}AC 4̍]BHE E(A0C8HP4^BME E(A0D8D`4<p`BJE E(A0D8D`4tXbBHE E(A0C8HP40ccBME E(A0D8D`4heVBJE E(A0D8D`4gBHE E(A0C8HX4TxhVBJE B(D0A8G`4jNBJE B(D0A8G`4ďl+BHE E(A0C8HX4mBJE B(D0A8G`44pBJE B(D0A8G`,lrBBB B(D0C8,uBBB B(A0D8,̐PyBBB B(A0D84 ~'BBB B(A0A8G44'BBB B(A0A8G4lHBBB B(A0A8G4h*BBB B(A0A8G4ܑP?3*BBB B(A0A8G4XiKBBB B(A0A8G,L BBB B(D0F8,|BBB E(A0F8, BBB B(D0C8$ܒkblD@pD4xbHThD$lpz eD;RēDܓDD4 BBB B(A0A8G4DBBB B(A0A8G$|BBA D(hAOĔ(D0ܔDbHD,jD0D(D$\0z eDDbHԕD$z eD$,e$TxQe4| BBE B(A0A8GЗ4H BEB B(A0A8Gu4 BBB B(A0C8Gc4$h'`BBE B(A0A8IB4\/BBB B(A0A8G24(5BEB E(A0C8J4̗Sp BBB E(D0A8G4\ BBB E(A0C8Gu4<`j& BBB B(D0A8Ic4tXseBBB B(A0D8IA4yBBB E(A0A8I1$X~e$ Ѐe$4he$\Ke$e$eԙ/D $0ve,LBEB B(A0A8,DzBBB E(A0A84tBBB B(A0A8DH4BBB B(A0A8D4H BBB B(A0A8G4hBBB B(A0A8G4TBBE B(A0A8GA,piBBB B(A0A8,BEB B(A0A84|BEB B(A0A8Dh4$XzBEB B(A0A8G4\,BEB B(D0A8Gf4BBB B(A0A8DH4̜0BBB B(A0A8D4 BBB B(A0A8G4<BBB B(A0A8G4thBBE B(A0A8GA,gBBB B(A0A8,ܝ BEB B(A0A84 BEB B(A0A8D4DH" BBB B(D0A8G4| ,BEB B(D0D8G8xAb`ԞHFeD0$FjeMD0$,pNe$TRme 4|PXBBB B(A0A8G4_{BBB E(A0A8G?4 |(BEE E(A0C8J4$2BBB B(D0A8I 4\BEB B(A0A8G4b BBB E(A0A8Gs4̠ BBB B(D0A8GS4hBBB B(A0A8D4< BBB B(A0A8J<4t#BEB E(A0C8J4p 8 BEB B(A0A8G 4x,BBB B(A0A8G4 ; BBB B(A0A8Gs4TEBBB B(A0A8GR,N{BHB B(A0K8,SBEH K(A0A8XYFZ:TG0$([GD<`[Th[AG$t\^FPP_%Dh_2Ḍ_Z0$@`TP@$a^GP$<pb^G@,dcBGB E(D0A84XeBBE B(A0A8M,̤jBGN C(A0`j&xjj,j5DjR\@kctk/Hl<Km@Ko?Kԥ(pKpKHq2EG$hr5EMDs=EMdtEEMu`vXv̦PvZD$vE^J AT,@ATL DdD|RȎDЎD$ħ؎e`DhDpAT<AT\ЕDtȕD$;e؛D̨U]P D,(BEB B(A0G844 BBB B(A0A8J,lBHB B(A0A8,BEB B(A0A8,̩PBEB B(A0A84GBEB B(A0A8D`44BBB B(A0A8J4l4BBB B(A0D8GA4DBBB B(A0D8GA4ܪBEE E(D0D8G4xhBBB B(A0D8GA4LcBBB B(A0D8GA4qBBB B(A0D8GA40JBBB B(A0D8GA4H BJE E(D0D8G4,  BBB B(A0C8G4d BBB B(A0C8G4 BBB B(A0A8L4ԬH" BBB B(A0A8L4 - BBB B(A0F8G4DH; BBB B(A0F8G4|H BBB B(A0F8G4HVj BBB B(A0F8G4c}BBB B(A0C8G4$rBBB B(A0C8G4\0eBBB B(A0C8G4hBBB B(A0C8G̮ED8EDpBD,BHB B(A0A8,DXBHB B(A0A8,tBHB B(A0A8,SBHB B(A0C8,ԯرxBHB A(C0,(BHB B(A0A8$4~BHA C($\`}BHA C(,BHB B(A0A8,BHB B(A0A8$yBHA C($ }BHA C($4XmBHA C($\BHA C($BHA C(`BD$ıe$e,X BEB B(A0A8,D8BEB B(A0A8,tBEB B(A0A8,eBEB B(A0A84ԲBEB B(A0A8DP4 PBEB B(A0A8DP,D BEB B(A0A8,tBEB B(A0A8,8BEB B(A0A8,ԳBEB B(A0A8,yBHB B(A0A8,4BEB B(A0A84dBEB B(A0A8Dp,@BEB B(A0A8,̴dBHB B(A0A8D4BBB B(A0A8G4L@BBB B(A0A8D4 BBB B(A0A8G4BBB B(A0A8G@D4 'BBB E(D0A8JKDX"MD,\"dBBE B(E0E8,$?BBE B(E0E84&BBB B(A0A8G4*YBBB B(A0A8G4,,BBB B(A0A8G4dX0dBBB B(A0A8G2S@,@3BEB F(E0D8,5BBB D(E0,7BBB F(E0C8$L:'^JTtaXPbD@$0ceԸxALPyVbp4XzBGE B(A0D8G%L@}bp4lBBB B(A0A8G 48BBB B(A0A8G 4ܹЕBBB B(A0A8G(xhAJ44ȞBBB B(A0A8G l0D8aDD$Ժ2e(D$b<D$TeBID D@4|0g BBB B(A0A8Gx,hBHE B(A0A8,8BHE B(A0A8,BHE B(A0A8,DtBIB B(A0A8,t8BEB E(A0A8,BEB E(A0A8,ԼBEB E(A0A84BBE E(H0D8G4<0BFB B(A0A8G4tBEB E(D0A8L4BEE E(D0D8GATAT$ATDxATdHATATDP,RԾhBID hD pD$XD,<`BBB B(A0A8,lBBB B(A0A8,PBBBB B(A0A8,̿pBBB B(A0A8,BBB B(A0A8,,BEB B(A0A8,\BBB B(A0A8,`BBB B(A0A8,HBBB B(A0A8,BBB B(A0D8,BBB B(A0A8,L0 BBB B(A0A8,| BBB B(A0A8, BBB B(A0A8,HBBB B(A0A8, BBB B(A0D8,<`BBB B(A0A8,lBBB B(A0A8,BBB B(A0A8,`$BBB B(A0A8,P&BBB B(A0A8,,0)BBB B(A0A8,\*BBB B(A0A8,.sBBB B(A0A8,`/ BBE E(A0A8,@4QBEB B(A0A8,p6BBE E(A0A8,L`9BBB B(A0A8,|:xBBE E(A0A8,>BBB B(A0A8,p?zBBE E(A0A8, DxBEB B(A0A8,<G@BBE E(A0A8,l JBBB B(A0A8,KBBE B(A0A8,0OBBB B(A0A8PDPD4,PPBBB B(A0A8G4dS}BBE B(A0A8G,WBBB B(A0A8,YBBB E(A0A8,\BBB B(A0A8,,@_BBB B(A0D84\bBBB B(A0A8G4xqBBB B(A0A8G,BBB B(A0A8,BBB E(A0A8,,pBBB B(A0A8,\@BBB B(A0D84BBB B(A0A8G4xBBB B(A0A8G,BBB B(A0A8,,BBB E(A0A8,\pBBB B(A0A8,@BBB B(A0D84 BBB B(A0A8D4H BBB B(A0A8Dp,,BBB B(A0A8,\@qBBB B(D0A8,BBB B(A0A8,BBB B(A0D8,P BEB B(A0A8,@I BBB B(A0D8,L`qBBB E(A0D8,|BEB B(A0A8,0|BEB B(A0A8,BEB B(A0A8, BEB B(A0A8,< BBB B(A0D8,lPBBB E(A0D8,BEB B(A0A8,pBEB B(A0A8,BEB B(A0A8,DDD4\!BBB B(A0A8D`4>BBB B(A0A8Dh4!>BBB B(A0A8Dh4%BBB B(A0A8Dh,<) BBB B(A0A8,lp-FBBB B(A0A841 BBB B(A0A8DX,h BBB B(A0A8DX,<IBBB B(A0A84lK BBB B(A0A8DX,VBBB B(A0A84YSBBB B(A0A8DP, @a/BEB B(A0A8,<@cBBB B(A0D8,lk@BEB B(A0A8,mj BBB B(A0D8,w[BEB B(A0A8@yGyD,y'D Dz6BDD 4d(}k BBB B(D0A8G`[BDD 4BBB B(A0A8G48BHB B(A0A8D4,3 BEB B(A0A8G4dBHB B(A0A8D4pBEB B(A0A8Dp4ȧBBE B(A0D8D4 `TBEB B(A0A8G4D+BBB B(A0A8Dp4|ABBE B(A0A8Dp[BDD 4زBBB B(A0A8G4 pBHB B(A0A8D4D(3 BEB B(A0A8G4|0BHB B(A0A8D4BEB B(A0A8Dp4BBE B(A0D8D4$TBEB B(A0A8G4\+BBB B(A0A8Dp4ABBE B(A0A8Dp BDD 4 BBB B(A0A8G4$BHB B(A0A8D4\ BBB B(A0A8G4BHB B(A0A8D4` BEB B(A0A8G4BHB B(A0A8D4<PeBEB B(A0A8Dp4tBEB B(A0D8D4PBEB B(A0A8G4BBB B(A0A8Dp4BBE B(A0A8Dp$T oBDD D(4|0~ BEB B(A0A8G4xBHB B(A0A8D4BBB B(A0A8G4$'BHB B(A0A8D4\)i BEB B(A0A8G43BHB B(A0A8D405BHB B(A0A8D47BEB B(A0D8D4<@9PBEB B(A0A8G4tX;\BEB B(A0A8Dp4=lBBE B(A0A8Dp$?yBDD D(4 D BEB B(A0A8G4DMBHB B(A0A8D4|OBBB B(A0A8G4X^BHB B(A0A8D4` BEB B(A0A8G4$jBHB B(A0A8D4\lBHB B(A0A8D4nBEB B(A0D8D4 pPBEB B(A0A8G48rmBEB B(A0A8Dp4<ptmBBE B(A0A8Dp4tvBFB B(A0D8Ipy'D yATzATzAT$h{ATD8|ATd|AT}DP,RhBID D D(>a$HD<P^\DtȍEb0DXPSDP,Z BBE B(D0D8,8BBE E(D0D8,DBBE E(D0D8,tBBE E(D0D8BDA BDA SBDA BDA $$XOEHdLNDdWD|cDXiDEAMSD(]DpeDLD,WDDHcD\iDtLD0SDx]DaD$HEMb$@EMb,$BMB F(A0A8,T4BTB G(D0A8,4BTB G(D0A8, BQB G(G0A8,BQB G(A0A8,BTB G(D0A8,DBTB G(D0A8,tBQB G(G0A8,XQBNB G(D0A8,IBVB G(D0F8,IBVB G(D0F8,4BVB G(D0F8,d BSB G(D0C8,8B^B B(D0C8,B^B B(D0C8,kB^B B(D0C8,$X$B^B B(D0C84T(+hBBB B(D0D8L`4`,BLB M(A0A8I4/zBLB M(A0A8I4`1zBLB M(A0A8I443]BLB M(A0A8I4l5BLB M(A0A8I47BEF E(D0A8I40;\BEB B(A0A8D4X>BIB E(A0A8G$LC^JtU<AID`U<AID`UEDVED8VEDpVED$VBEA A($<pBEA A(,dXBEB B(A0A8,BEA A(DAGJA82]0XD`D44hhBGJ B(D0D8G$le$hme$e$xge$ e$4ge4\BBB B(A0A8Gb4H BBB B(A0A8GR4BBB B(A0A8G14BBB E(D0A8Gb4<`BBE B(D0A8GR4t8 4BBB E(D0A8G14@BBB B(A0A8G34 BBB B(A0A8G*4`$5BBB B(A0A8G4Th*BBB E(D0A8G34 1.BEE B(A0A8G*497BBB E(D0A8G$ = ^I4$GBEB B(A0A8S4\@`y(BHB B(A0A8I 4BKB B(A0A8M4pBKB B(A0A8M4oBKB B(A0A8M4<PdBKB B(A0A8M4tBKB B(A0A8M4BKB B(A0A8M4xBKB B(A0A8M4BKB B(A0A8M4THBBB B(A0A8Q4BBB B(A0A8Q4HBOB B(A0A8M4BOB B(A0A8M44BOB B(A0A8M4l0BOB B(A0A8M4&BEB B(A0A8S$p e8a$(D<DTl 1H<pU7B[`1:,@DF\DtHP\h Th f x(!!d4!|L`"d"|#T#] $cx$i$r8%~ %$&<&T'l'X())p*H+40,BMB B(A0A8I42L@3d3DKh545 BOE B(A0A8GCFH0GHG ]P$HaDXZadkb0aDDDDDD4DLDdȞD|ОD؞Eb0DCAA@BAA  A$hBAA 4DXBBE B(A0F8G4|]BGE B(A0F8G48BBB E(A0F8G4BBE B(A0F8G4$BBB B(D0D8G,\bBIB B(A0A8,cBIB B(A0A8,0bBIB B(A0A8,psBIB B(A0A8,sBIB B(A0A8,L BIB B(A0A8,|BIB B(A0A8, BIB B(A0A8, BIB B(A0A8, BIB B(A0A84<pkBBB I(A0A8D@4t? BBE I(A0A8D4BBB I(A0A8D@4(BBB I(A0A8D@4BBB I(A0A8D@4TBBB I(A0A8D@4BBB I(A0A8D@4 BBB I(A0A8D4BIB E(A0A8D44$Q BBB I(A0A8D`4l.R BBB I(A0A8D`487uBBB I(A0A8D`4?BBB I(A0A8Dh48HqBBB I(A0A8Dh4LPBBB I(A0A8D4HcBBB I(A0A8D4v.BBI B(A0A8I4x.BBI B(A0A8I4,BBI B(A0A8D4dxBBI B(A0A8D4BBB I(A0A8D4 BBB I(A0A8D4 BBB I(A0A8D4D85BBB I(A0A8D4|IsBBB I(A0A8D4]BBB I(A0A8D40qBBB I(A0A8D4$BBB I(A0A8D,\BBB A(A0,BBB B(A0A8,pBBB B(A0A8,PBBB B(A0A84@BEB B(A0A8DP4TpBEB B(A0A8DP4 BBB B(A0A8D`4WBEB B(A0A8D`4BEB B(A0A8DP44ȞrBEB B(A0A8DP4lBEB B(A0A8DP4BEB B(A0A8DP4pUBBB B(A0A8G4BGB B(A0A8G LUE^lDD$> e&bDh1[45BEB B(A0D8G4BEE E(A0A8J4TH1BEE E(D0A8G4 HBBB B(A0D8G48'BEE E(A0A8J4?.BEE E(D0A8G44xm;BEB B(A0A8J4lBEE E(A0A8J4h=BEE E(D0A8G4w%BBE E(A0A8J4%-BEE E(D0A8G4L24BBE B(A0A8J4IBEE E(A0A8J4_*6BEE E(D0A8G4EBEB B(A0D8G4, ФBEE E(A0A8J4d 1BEE E(D0A8G  xk *D H D D, DD WS0d D| D D D D D  $ "< gAI,\ ^BPE A(A0O84 @FBEN N(X0I8Gx4 XBRE B(A0F8O,  BVR E(O, (D \ X4t 8!KBHE B(L0I8P4 P) BUE B(A0A8G4 2(BJE B(A0A8G4 E)BIB B(A0A8L4T K BTL B(A0A8G$ PVEOHH[@$ VEHOHMU@ PW X\ P]KAI,,`BVP J(L0S84\@gBHE B(S0L8Y`4mBRE B(A0F8O,s BVR E(O },94DBHE B(T0N8M4|@BEE B(A0A8G4BJB B(T0D8G4BIB B(A0A8L4$ BTL B(A0A8G$\EOHH[@$EHOHMU@bpHD8HDpADAD,HDXH4\PBDB B(D0A8G4fBDB B(D0A8G,7BDB B(D0A8, BDB B(D0A8,,DBVN M(A04t(BEB E(A0A8J4p&BEB H(D0A8G 4(+BEE B(A0G8G,:BIB B(A0A8,L`@BEB B(D0D84| FA&BGB E(A0G8J 8laH4lNBGB E(D0A8MxaH4кTMBJE B(D0G8G TaH4lP$BJM E(D0D8GIH$aH4$BJB E(D0A8GBaH4 pBRBME B(A0A8G D`aH4\`'BEB B(D0A8L4&BEE B(G0D8G 4 BBE E(A0D8G,BIB B(A0A8,4h<BEB B(D0D84dx%BGB B(D0G8J 4@|MBGB E(A0D8M4;LBJB E(D0G8G 4 BJM E(D0D8GI4DBJB B(D0D8G4|pBME B(A0A8G 4'BEB E(A0A8L 4(BEB B(G0D8G 4$X-jBEE B(A0D8G,\=BIB B(A0A8,CBEB B(D0D84pJ&BGB B(D0G8J 4qNBGB E(A0D8M4,$NBJB E(D0G8G 4d BJM E(D0D8GI4*BJB E(D0A8G4XHBME B(A0A8G 4 @gn(BEB E(A0A8L 4Dx(BEB B(G0D8G 4|BEE B(A0D8G,X1BIB B(A0A8,h]BEB B(D0D84'BGB B(D0G8J 4L|OBGB E(A0D8M4LOBGB E(D0G8G 4BJM E(D0D8GI4XFBJB E(D0A8G 4,pBME B(A0A8G d.D|.D(+D@+DX1D6D6D 1D$1D< 1DTH.Dl`.Dx+D+D1D6D6D 1DH1D,p1DD3G\2Ft0G/F6G@:Hh4G6G5F5F,4oBIE B(A0J8,dH^BKE B(A0J8,xBBB B(A0A84BBB B(A0A8DX4BBB B(A0A8DX44 x BBB B(A0A8DX4l @BBB B(A0A8DP4 ?BBB B(A0A8Dh4 HBBB B(A0A8Dh4!(?BBB B(A0A8Dh4L!0$4BBB B(A0A8DP4!8)kBBB B(A0A8Dh4!p.xBBB B(A0A8Dh4!3kBBB B(A0A8Dh,,"8%BBB B(A0A8,\":'BBB B(A0A84"=9BBB B(A0A8DH4"AGBBB B(A0A8Dh,"GsBBB B(A0A8,,#`IxBBB B(A0A84\#LBBB B(A0A8DH4#QbBBB B(A0A8D`,#@VsBBB B(A0A8,#XxBBB B(A0A84,$[BBB B(A0A8DH4d$8`bBBB B(A0A8D`4$peBBB B(A0A8G!4$vBBB B(A0A8G4 %P BBB B(A0A8G4D% BBB B(A0A8G 4|%BBB B(A0A8G!4%BBB B(A0A8G4%p BBB B(A0A8G4$&( BBB B(A0A8G 4\&BBB B(A0A8G!4&HBBB B(A0A8G4&BBB B(A0A8G4'xBBB B(A0A8G 4<' BBB B(A0A8G!4t'BBB B(A0A8G4'pBBB B(A0A8G4'BBB B(A0A8G ,(p BBB B(A0A8,L(0 [BBB B(A0A8,|(` |BBB B(A0A8,( BBB B(A0A8,(03BBB B(A0A8, )@_BBB B(A0A8,<)pBBB B(A0A8,l)BBB B(A0A8,)PBBB J(A0,) BEB B(A0I8,)}BHB B(A0D8,,*]BKB B(A0A8\*vD$t*xBNA A(,*+BKB B(A0J8*D,*xBKB A(A0,+HCBNB A(D0,D+h!BEB B(A0F8,t+h @BGB B(J0H8,+x"BEE B(A0A8,+X$BEB B(A0F8,,H&,BKB B(D0H8,4,H(0BKB B(D0A8,d,H**BKB B(D0A8,,H,.BKB B(D0A8,H.AD,.BSA $,.BTA A(4$-/BBE E(D0A8J\-2&t-2C-2b-(3,-36BPB G(G0A8,-4FBQI G(G0A8,.5{BFB B(D0D84L. 7BBH E(D0A8G4.<BBK B(A0A8Fp.@lAGOP4. ABEB B(D0A8G/I+D ,/I+D D/ID\/ID$t/Ib/J+D /Jb`/K+D /Kb` 0`L+D $0xLb`D08MD\00MEVG0|0MD0MEVG00ND0N$0xNBBA A($ 1OBBA A($41QBBA A(\1PSu$t1SBPK A(1@TBVA 1Ud@410VeBBB B(A0A8G,2hW}BBB B(A0A8$D2XBBA A(l2Y}AA,2ZnBBB B(A0A82P[AA2[BAA $2@\BBA A(4$3]BBB B(A0A8G 4\3^fBBB B(A0A8G,3_nBBB B(A0A838aAA3aAA$4(bBBA A(4,4cBBB B(A0A8G 4d4dfBBB B(A0A8G,4eBBB B(A0A84gAD4hBGA $ 5hBEA A(,458iBEB B(A0A8d5jA,|5kBHB B(A0A85lAD5lAD,5@m6BBB B(A0A86PpA,46p0BBB B(A0A8,d6rBHB B(A0A86sAD6HtAD6uAD6uA 7Pv4D0$7xv4D04<7v;BOG B(A0A8GX4t7|2BBB B(A0A8O@47;BOG B(A0A8GX,7BKB A(A0G48BIG B(A0A8MX4L8@BBB B(A0A8D@48ؔBIG B(A0A8MX8$8xAe89,9D9\9t9x9p9h9`9X9P,:H BBB B(A0A8,4:BEE B(A0A8,d:BEE B(A0A8,:ȺBHE B(A0A84:cBBB B(A0A8Gx,:BEE E(A0A8,,;BEE E(A0A8,\;0BHE B(A0A84;* BBB B(A0A8Gp,;BHE B(A0A8,;BHE B(A0A8,$<xBEE E(A0A84T<8 BEB B(A0A8G4<wBBG B(A0A8Dh4<wBBG B(A0A8Dh,<PBEE E(A0A84,= BBB E(A0A8G(,d=XBEE E(A0A8,=BEE E(A0A8,=BEB E(A0A8,=X PBFB B(A0A8,$>xBEB E(A0A8,T>HBEB E(A0A8,>BEE E(A0A84> BHE B(G0A8M 4>0! BBE B(D0C8G4$?+ BHE B(G0A8M 4\?5BLB F(A0A8U4?X<BJI F(D0A8J4?BBLB F(A0A8U4@IBLF B(A0A8W4<@PRBIH B(D0A8J4t@ZBLF B(A0A8W$@cN^J%@fmG@ gmG4Axg BHE B(G0A8M 4ZBBE B(G0A8G4DRCBBE H(A0A8G,|RF=BBB B(A0A8,RGBBB B(A0A8,RITBBB B(A0A8, SKBBB B(A0A84D04e BBB B(A0A8G4fBBB E(A0A8G?4BEB B(A0A8G4t|h@BEB B(A0A8G4|0CBHB B(A0A8G4|EBBB B(A0A8G,}HBBB E(A0A8,L}pKBBE B(A0A8,|}PNkBBB E(A0A8,}QBBE B(A0A84}pT(BGB B(A0D8M 4~}<BHI B(A0D8M4L~ BEE I(A0J8P 4~BEE I(A0J8P4~PBLI B(F0E8N4~iBLI B(A0E8N4,@OBLI G(A0E8N4dXBII B(A0E8W4BLF B(A0E8W4lBLF B(A0E8W$ @R BHA GP,4x BBE A(C0,d8dBBB E(D0D8,xtBBB E(D0D8,ĀoBBB E(D0D8, BBE A(C0,$x8BBB E(D0D8,T@BBB E(D0D8,9BBB E(D0D8BDA ԁHBDA 8ZBDA xBDA 4BDA TBDA tMBDA BDA 4XBFE B(A0A8J,BQB I(E0E8,BQE J(E0D8,LpBNE J(E0D8,|PBUF F(A0D8,@BNE L(E0E8,܃BTE J(F0A8,  @BNB H(D04< @BHB B(D0D8G`,t]BNB H(E0,?BNB H(D0,ԄBBB G(A0D4jBBB B(D0D8Jp,<WBNB H(E04lBEB B(A0A8J,BBB G(A0D4ԅBBB E(A0A8G, !*BRB J(E0E8,<$9BRE K(E0D8,l';BOE K(E0G8,*UBVF G(A0D8,̆-BOE M(E0E8,@0BUE K(F0A8,,2BKB D(D0Lx4\`6BHB B(D0D8G`,89BKB D(E0Hx,ć<BKB D(D0L,x@EBFB D(A0J4$D'BBB B(D0D8Jp,\GBKB D(E0H4@K}BIB B(A0A8J,ĈOABFB D(A0J4S_BFB E(A0A8J,,WBRB J(E0E8,\[BRE K(E0D8,`_BOE K(E0G8, cBVF G(A0D8,gBRE M(E0E8,jBXE K(F0A8,LnBKB D(D0O4|tDBHB B(D0D8G`,(xBKB D(I0J,}BKB D(D0O,kBFB A(D0J4DXZBHE B(D0A8Gp,|BKB D(I0J4BFB B(A0D8J,hgBFB A(D0J4tBFB B(A0D8J,LBRB J(E0E8,|BRE K(E0D8,BOE K(E0G8,܌`BVF G(D0A8, PBRE K(E0G8,<@/BXF L(D0A8,l@q BJB D(I0J4BHB B(D0D8Gp,ԍ BJB D(I0J,8p BJB D(I0J,4xM BFB A(D0J4dBHB E(D0A8G, BJB D(I0J4̎` BFB B(A0D8J,#I BFB A(D0J44/c BFB B(A0D8Jl<BTD <BLA p=BOA ̏0>BDA >iAG  ?nBIA ,p?bP,L0@BBB B(A0A8,|ABBB B(A0A8,C\BBB B(A0A8,ܐEBBB B(A0A8,  HxBEB B(A0A8,<pIBBB B(A0A8,l KBBB B(A0A8,MQBBB B(A0A8,̑@OTBBE B(A0A8,pPBBB B(A0A8,,QBBB B(A0A8,\pSBBB B(A0A8,0UBBB B(A0A8,VBBB B(A0A8,pXDBBB B(A0A8,ZBBB B(A0A8,L\xBEB B(A0A8,|@^BBB B(A0A8,_BBB B(A0A8,ܓa=BBB B(A0A8, cTBBE B(A0A8,<dBBB B(A0A8,lPfBBB B(A0A8,gBBB B(A0A8,̔iBBB B(A0A84 kRBBB B(A0A8D@44HmBBB B(A0A8D@4lo6BBB B(A0A8D@,rBBB B(A0A8,ԕXtBBB B(A0A8,HvZBBB B(A0A8,4xxBBB B(A0A8,d{uBEB B(A0A8,X|BBB B(A0A8,Ė}BBB B(A0A8,/BBB B(A0A8,$BBB B(A0A8,T(8BBB B(A0A8,8BBB B(A0A8,BBB B(A0A8,HBEB B(A0A8,BBB B(A0A8,Dh;BBB B(A0A8,txBBB B(A0A8,ȑ\BBE B(A0A8,ԘBBB B(A0A8,xBBB B(A0A8,4(BBB B(A0A8,dBBB B(A0A8,x8BBB B(A0A8,ęBBB B(A0A8,BBB B(A0A8,$BEB B(A0A8,TBBB B(A0A8,;BBB B(A0A8,ȥBBB B(A0A8,\BBE B(A0A8,HBBB B(A0A8,DȪBBB B(A0A8,txBBB B(A0A8,XBBB B(A0A8,ԛȯBBB B(A0A8,\BBB B(A0A8,4سBBB B(A0A8,dHxBEB B(A0A8,BBB B(A0A8,ĜHBBB B(A0A8,8QBBB B(A0A8,$hTBBE B(A0A8,TBBB B(A0A8,BBB B(A0A8,BBB B(A0A8,XBBB H(D0A8,BBB E(A0A8,DBBH E(A0A8,tXBEB B(A0A8,(BBE B(A0A8,ԞBBH E(A0A8,BBB E(A0A8,4BEB B(A0A8,dBEB B(A0A8,BEB B(A0A8,ğBEB B(A0A8,hBEB B(A0A8,$HBBB B(A0A8,T8BBB B(A0A8,(BBH B(D0A8,(BBH B(D0A8,GBBB B(A0A8,8IBHB B(D0A8,DXIBHB B(D0A8,tx"BBB B(A0A8,x9BHB B(D0A8,ԡ9BHB B(D0A8,BBB B(A0A8,4BBB B(A0A8,dHBBH E(A0A8,BHE B(A0A8,ĢBHE B(A0A8,BEH E(A0A8,$xBHE B(A0A8,THBEH E(A0A8,BBH E(A0A8,BHE B(A0A8,BHE B(A0A8,xBEH E(A0A8,DHBEH E(A0A8,tBHE B(A0A84 BBB B(A0A8Gc4ܤ6 BEB B(A0A8GR4BBB B(A0A8G24LBBE E(A0D8Gc4 B BEB B(A0A8JR47BBE E(A0A8J14BGE B(A0C8G4,@BGB E(A0A8G!d#|#-#8$IĦH$3ܦp$>$O $1$$:< %@TH%Fl%D%P%\8&ḩ&T&f0'x',(dDh(|\(tX)*M8*V*\Ԩ*b +hx+t+4H,L,d(-|-H..ĩx/ܩ 004 1BMB B(A0A8ID8\8-t(84P8I838:Ԫ8O9189:`9@49FL9Dd9P|0:\x:h:Tī;fܫp;x; PU>^>d(?j̬?x?`@@,`ADA\BtHCDDEԭxF4pGMBPB B(A0A8G$MtA<MAJ\N8BJC |OBPC ,PQLBRB K(D0A8,̮pWiBMB B(D0F8,^ BJB E(D0C8,,piBJB E(D0C8\rAtxrAJ8sDAIhtBPC ,ԯu8BOE E(A0A8,|BJB B(A0K8,4% BPB E(D0C8,dBGE L(D0C84HBJB B(A0A8G4̰iBJB B(A0A8G4ءBOB B(A0A8G4<BBB B(G0A8G4tXzBOB B(A0A8G4NBJB B(A0A8G4 BOB B(A0A8G4BBB B(G0A8GT$BAA t(sBAA ESBAA 4ԲBBB E(A0F8G4 5BGB E(A0F8G4DuBBB E(A0F8G4|ZBBE B(G0A8G-AAԳsAAhA AA4,BBB B(D0F8G 4dH%BGB B(D0F8G 4@gBBB B(D0F8G 4ԴxiBBB H(A0D8G , BBE B(A0A8,<pBBE B(A0A8,l0BEB B(A0A8?DID X$ D jF@lh o8kooloohoxhF>N^n~.@@.0i4@`D@D`p@q4 0+0D/DDe40D`3@Ds4`4@l43D`D`30p)0,0@DDX4P1@&0pu41` Dp0D+`~1PP / 01D`@40+03D`D D'0 D/r43(0`3D1м@p4D01k4@33`04@D`3  D@`3 Dc4D0\4D0GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux)GCC: (GNU) 3.3.3 (SuSE Linux).symtab.strtab.shstrtab.hash.dynsym.dynstr.gnu.version.gnu.version_d.gnu.version_r.rela.dyn.rela.plt.text.rodata.data.eh_frame.dynamic.got.bss.comment  !  )X$X$D1ohh8>o8k8kMoll@\@l@lhf k((p@@Vg2va3a3 ~D4@2 D4xhFxh6jFj608nF8n6X8n6O86x6F 6t  X$h8kl@l ( @ a3 D DxhFjF8nF0BTh *AXn$=Rdw (Kn #9Oew 5Hc{.D[p8Qj*B[t ' ; R f }       ( F ] q        " 6 M a t         ' ? T t      9 [ }    !5Qj 8Sk/BUex #?Xo7Vr3Lcv 2Ol 0Pp$<Qd}2Jc{/Qh 5Ncw6Ww<^ (D[r;Xu*EZt1Lc ) H i     !)!H!h!!!!!"("G"h""""""#/#G#b#|###### $#$=$W$o$$$$$$ %"%<%R%q%%%%%&$&A&]&}&&&&&','C'Y'o''''''((6(N(f(}(((((()2)O)l))))))*<*\*y*****+,+I+f++++++ ,C,f,,,,, -/-O-q------...M.l....../+/B/b/ @*Y/ / Szj0 0 @0 1 Y?1 ^l1 81 1 ;2 i2 @2 2 P2 t^'3 ")3 9363 64 Iii4 pI4 `H-5'C5 Yp5 L5 p;6 p9A6 o6 0.)6 D@;7 `^l7 7 7 8&8 )8 p8 `K9 9 s99 nO: C: : ; H; P~r; ; ); B< m .< @c< < P= z= `%s== P#> 5@H> p9q>> 0;> Aj> @)? `C? 9? ? @ D@  ~@ B@ @RA @{A A T] B `:B 01gB @B `B 0$)2CDC `YC ЩC AqD qtDMD @&E d7E PF Pg7jF @F F AG 1nG GG G 0)H H :@5IFI `7 yI 0I J 0viEJ ?nJ J J @K p9K 2KK 9L P4@@L (gL `>L 0rYM @M YqM &MM @M )nNyN )NwO 4O _O pF@(P @ZP @]P U8Q Q )R 6@.R [R 1R R p-S pXS S yiS pzTT NT  T T T +U6U dU YU 4V p]V V >qV `O]W 0W 0HW X Q7iX{X Y [YY Y Y l  Z @Z 0Z JC[ 01p[ @[ `[ `[ l\ `\V\ p(] W] pz^] )^ П6^ P^Y^ @ ^ 0_ м@_ `` )Q`  ` P` @}` W]n p"o PLo M@o ezip Bp  )q  Pq {izq p@r PErYr Dr Pr ;r B s BWs )s Z-t PmediaLibwrapper_Image.cmediaLibwrapper_util.cmlib_c_ImageAdd.cmlib_c_ImageAnd.cmlib_c_ImageBlend.cmlib_c_ImageChannelExtract.cmlib_c_ImageChannelInsert.cmlib_c_ImageClear.cmlib_c_ImageClrOrdDither8x8.cmlib_c_ImageClrOrdDitherMxN.cmlib_c_ImageColorConvert.cmlib_c_ImageColorErrDiff3x3.cmlib_c_ImageConstAdd.cmlib_c_ImageConstAnd.cmlib_c_ImageConstOr.cmlib_c_ImageConstSub.cmlib_c_ImageConstXor.cmlib_c_ImageCopy.cmlib_c_ImageDataTypeConvert.cmlib_c_ImageFilteredSubsample_Fp.cmlib_c_ImageFilteredSubsample.cmlib_c_ImageFlipY.cmlib_c_ImageHistogram2.cmlib_c_ImageLookUp.cmlib_c_ImageMax.cmlib_c_ImageMean.cmlib_c_ImageMin.cmlib_c_ImageMulShift.cmlib_c_ImageNot.cmlib_c_ImageOr.cmlib_c_ImagePolynomialWarp.cmlib_c_ImageScale_Fp.cmlib_c_ImageSub.cmlib_c_ImageSubsampleAverage.cmlib_c_ImageSubsampleAverage_S16.cmlib_c_ImageSubsampleAverage_U16.cmlib_c_ImageSubsampleAverage_U8.cmlib_c_ImageThresh1.cmlib_c_ImageThresh1_S16.cmlib_c_ImageThresh1_S32.cmlib_c_ImageThresh1_U16.cmlib_c_ImageThresh1_U8.cmlib_c_ImageThresh2.cmlib_c_ImageThresh3.cmlib_c_ImageThresh4.cmlib_c_ImageThresh5.cmlib_c_ImageXor.cmlib_c_ImageZoomTranslate.cmlib_ImageAbs_Fp_Inp.cmlib_ImageAbs_Fp.cmlib_ImageAbs_Inp.cmlib_ImageAbs.cmlib_ImageAdd_Fp_Inp.cmlib_ImageAdd_Fp.cmlib_ImageAdd_Inp.cmlib_ImageAffine_Fp.cmlib_ImageAffine.cmlib_ImageAffineTable_Fp.cmlib_ImageAffineTable.cmlib_ImageAffineTable_u16_3nw.cmlib_ImageAffineTable_u16ext.cmlib_ImageAffineTable_u16nw.cmlib_ImageAnd_Bit.cmlib_ImageAnd_Inp.cmlib_ImageBlend_Fp_Inp.cmlib_ImageBlend_Fp.cmlib_ImageBlend_Inp.cmlib_ImageBlendMulti.cmlib_ImageClear_Fp.cmlib_ImageColorConvert_Fp.cmlib_ImageColorErrorDiffusionMxN.cmlib_ImageColorTrue2IndexInit.cmlib_ImageColorTrue2Index.cmlib_ImageConstAdd_Fp_Inp.cmlib_ImageConstAdd_Fp.cmlib_ImageConstAdd_Inp.cmlib_ImageConstAnd_Bit.cmlib_ImageConstAnd_Inp.cmlib_ImageConstDiv_Fp.cmlib_ImageConstDiv.cmlib_ImageConstDivShift.cmlib_ImageConstMul_Fp.cmlib_ImageConstMul.cmlib_ImageConstOr_Bit.cmlib_ImageConstOr_Inp.cmlib_ImageConstSub_Fp_Inp.cmlib_ImageConstSub_Fp.cmlib_ImageConstSub_Inp.cmlib_ImageConstXor_Bit.cmlib_ImageConstXor_Inp.cmlib_ImageConv2x2_Fp.cmlib_ImageConv2x2.cmlib_ImageConv_32ext.cmlib_ImageConv_32nw.cmlib_ImageConv3x3_Fp.cmlib_ImageConv3x3.cmlib_ImageConv4x4_Fp.cmlib_ImageConv4x4.cmlib_ImageConv5x5_Fp.cmlib_ImageConv5x5.cmlib_ImageConv7x7_Fp.cmlib_ImageConv7x7.cmlib_ImageConvClearEdge_Fp.cmlib_ImageConvCopyEdge_Fp.cmlib_ImageConv_D64ext.cmlib_ImageConv_D64nw.cmlib_ImageConv_F32ext.cmlib_ImageConv_F32nw.cmlib_ImageConvKernelConvert.cmlib_ImageConvMxN_Fp.cmlib_ImageConvMxN.cmlib_ImageConvolveMxN_Fp.cmlib_ImageConvolveMxN.cmlib_ImageConv_u16ext.cmlib_ImageConv_u16nw.cmlib_ImageCopy_Bit.cmlib_ImageCreate.cmlib_ImageDFT.cmlib_ImageDilate4_Fp.cmlib_ImageDilate4.cmlib_ImageDilate8_Fp.cmlib_ImageDilate8.cmlib_ImageDiv_Fp.cmlib_ImageDivShift.cmlib_ImageDivTables.cmlib_ImageErode4_Fp.cmlib_ImageErode4.cmlib_ImageErode8_Fp.cmlib_ImageErode8.cmlib_ImageExp_Fp.cmlib_ImageExp.cmlib_ImageExpTbl.cmlib_ImageExtrema2_Fp.cmlib_ImageExtrema2.cmlib_ImageExtremaLocations_Fp.cmlib_ImageExtremaLocations.cmlib_ImageFilteredSubsam_D64_f.cmlib_ImageFilteredSubsam_F32_f.cmlib_ImageFilteredSubsample_D64.cmlib_ImageFilteredSubsample_F32.cmlib_ImageFilteredSubsample_S16.cmlib_ImageFilteredSubsample_S32.cmlib_ImageFilteredSubsample_U16.cmlib_ImageFilteredSubsample_U8.cmlib_ImageFilteredSubsam_S16_f.cmlib_ImageFilteredSubsam_S32_f.cmlib_ImageFilteredSubsam_U16_f.cmlib_ImageFilteredSubsam_U8_f.cmlib_ImageFilters.cmlib_ImageFlipAntiDiag_Fp.cmlib_ImageFlipAntiDiag.cmlib_ImageFlipMainDiag_Fp.cmlib_ImageFlipMainDiag.cmlib_ImageFlipX_Fp.cmlib_ImageFlipX.cmlib_ImageFlipY_Bit.cmlib_ImageFlipY_f.cmlib_ImageFlipY_Fp.cmlib_ImageGradient3x3_Fp.cmlib_ImageGradient3x3.cmlib_ImageGradientMxN_Fp.cmlib_ImageGradientMxN.cmlib_ImageGridWarp_Fp.cmlib_ImageGridWarp.cmlib_ImageGridWarpTable_Fp.cmlib_ImageGridWarpTable.cmlib_ImageHistogram2_f.cmlib_ImageInvert_Fp_Inp.cmlib_ImageInvert_Fp.cmlib_ImageInvert_Inp.cmlib_ImageInvert.cmlib_ImageLog_Fp.cmlib_ImageLog.cmlib_ImageLogTbl.cmlib_ImageLookUp2.cmlib_ImageLookUp_64.cmlib_ImageLookUp_Bit.cmlib_ImageMaxFilter3x3_Fp.cmlib_ImageMaxFilter3x3.cmlib_ImageMaxFilter5x5_Fp.cmlib_ImageMaxFilter5x5.cmlib_ImageMaxFilter7x7_Fp.cmlib_ImageMaxFilter7x7.cmlib_ImageMax_Fp_Inp.cmlib_ImageMax_Fp.cmlib_ImageMaximum_Fp.cmlib_ImageMax_Inp.cmlib_ImageMean_Fp.cmlib_ImageMedianFilter3x3_Fp.cmlib_ImageMedianFilter3x3.cmlib_ImageMedianFilter5x5_Fp.cmlib_ImageMedianFilter5x5.cmlib_ImageMedianFilter7x7_Fp.cmlib_ImageMedianFilter7x7.cmlib_ImageMedianFilterMxN_Fp.cmlib_ImageMedianFilterMxN.cmlib_ImageMinFilter3x3_Fp.cmlib_ImageMinFilter3x3.cmlib_ImageMinFilter5x5_Fp.cmlib_ImageMinFilter5x5.cmlib_ImageMinFilter7x7_Fp.cmlib_ImageMinFilter7x7.cmlib_ImageMin_Fp_Inp.cmlib_ImageMin_Fp.cmlib_ImageMinimum_Fp.cmlib_ImageMin_Inp.cmlib_ImageMul_Fp_Inp.cmlib_ImageMul_Fp.cmlib_ImageMulShift_Inp.cmlib_ImageNot_Bit.cmlib_ImageNot_Inp.cmlib_ImageOr_Bit.cmlib_ImageOr_Inp.cmlib_ImagePolynomialWarp_0.cmlib_ImagePolynomialWarp_1.cmlib_ImagePolynomialWarp_BC_S32.cmlib_ImagePolynomialWarp_BL_S32.cmlib_ImagePolynomialWarp_Fp.cmlib_ImagePolynomialWarp_NN_Fp.cmlib_ImagePolynomialWarp_NN.cmlib_ImagePolynomialWarpTable_Fp.cmlib_ImagePolynomialWarpTable.cmlib_ImagePolynomialWarpTools.cmlib_ImagePolynomWarpTable_0_Fp.cmlib_ImagePolynomWarpTable_0.cmlib_ImageReformat.cmlib_ImageRotate180_Fp.cmlib_ImageRotate180.cmlib_ImageRotate270_Fp.cmlib_ImageRotate270.cmlib_ImageRotate90_Fp.cmlib_ImageRotate90.cmlib_ImageScale2.cmlib_ImageScale_Fp_Inp.cmlib_ImageScanPoly.cmlib_ImageSConv3x3_Fp.cmlib_ImageSConv3x3.cmlib_ImageSConv5x5_Fp.cmlib_ImageSConv5x5.cmlib_ImageSConv7x7_Fp.cmlib_ImageSConv7x7.cmlib_ImageSConv_D64ext.cmlib_ImageSConv_D64nw.cmlib_ImageSConv_F32ext.cmlib_ImageSConv_F32nw.cmlib_ImageSConvKernelConvert.cmlib_ImageSobel_Fp.cmlib_ImageSobel.cmlib_ImageSqrtTable_U16.cmlib_ImageSub_Fp.cmlib_ImageSubsampleAverage_Fp.cmlib_ImageSubsampleAverage_S32.cmlib_ImageSubsampleBinaryToGray.cmlib_ImageThresh1_Fp.cmlib_ImageThresh2_Fp.cmlib_ImageThresh3_Fp.cmlib_ImageThresh4_Fp.cmlib_ImageThresh5_Fp.cmlib_ImageThresh_Fp_Inp.cmlib_ImageThresh_Inp.cmlib_ImageXor_Bit.cmlib_ImageXor_Inp.cmlib_ImageZoom_BL_S32.cmlib_ImageZoomClipping.cmlib_ImageZoomEdge.cmlib_ImageZoom_NN.cmlib_ImageZoomTranslate_Fp.cmlib_ImageZoomTranslateTable_Fp.cmlib_ImageZoomTranslateTable.cmlib_ImageZoomTranslateToGray.cmlib_ImageZoomTransTable_16ext.cmlib_ImageZoomTransTable_16nw.cmlib_ImageZoomTransTable_32ext.cmlib_ImageZoomTransTable_32nw.cmlib_ImageZoomTransTable_8ext.cmlib_ImageZoomTransTable_8nw.cmlib_ImageZoomTransTable_D64ext.cmlib_ImageZoomTransTable_D64nw.cmlib_ImageZoomTransTable_F32ext.cmlib_ImageZoomTransTable_F32nw.cmlib_ImageZoomTransTable_u16ext.cmlib_ImageZoomTransTable_u16nw.cmlib_sys.cmlib_version.cmlib_SignalFFT_Frw.cmlib_SignalFFT_Inv.cmlib_SignalFFT_D64.cmlib_SignalFFT_D64Disp.cmlib_SignalFFT_D64Disp_8.cmlib_SignalFFT_D64Disp_24.cmlib_SignalFFT_D64Disp_32.cmlib_SignalFFT_D64_1.cmlib_SignalFFT_D64_2.cmlib_SignalFFT_D64_3.cmlib_c_ImageAbs.cmlib_c_ImageAffine_BC.cmlib_c_ImageAffine_BC_S16.cmlib_c_ImageAffine_BC_U16.cmlib_c_ImageAffine_BL.cmlib_c_ImageAffine_BL_S16.cmlib_c_ImageAffine_BL_U16.cmlib_c_ImageAffineIndex_BC.cmlib_c_ImageAffineIndex_BL.cmlib_c_ImageAffine_NN.cmlib_c_ImageChannelExtract_f.cmlib_c_ImageChannelInsert_f.cmlib_c_ImageClrOrdDitherMxN_Bit.cmlib_c_ImageColorErrDiffMxN_Bit.cmlib_c_ImageConvClearEdge.cmlib_c_ImageConvCopyEdge.cmlib_c_ImageConv_f.cmlib_c_ImageConvVersion.cmlib_c_ImageDataTypeConvert_f.cmlib_c_ImageDilate4_16nw.cmlib_c_ImageDilate4_8nw.cmlib_c_ImageDilate4_u16nw.cmlib_c_ImageDivShiftU8.cmlib_c_ImageErode4_16nw.cmlib_c_ImageErode4_8nw.cmlib_c_ImageErode4_u16nw.cmlib_c_ImageExtrema2.cmlib_c_ImageFlipMainDiag_f.cmlib_c_ImageGetAffineFunc.cmlib_c_ImageGetZoomFunc.cmlib_c_ImageGradient3x3Func.cmlib_c_ImageGradientMxNFunc.cmlib_c_ImageLookUp_f.cmlib_c_ImageMaxFilter3x3_16nw.cmlib_c_ImageMaxFilter3x3_8nw.cmlib_c_ImageMaxFilter3x3_u16nw.cmlib_c_ImageMaxFilter5x5_16nw.cmlib_c_ImageMaxFilter5x5_8nw.cmlib_c_ImageMaxFilter5x5_u16nw.cmlib_c_ImageMaxFilter7x7_16nw.cmlib_c_ImageMaxFilter7x7_8nw.cmlib_c_ImageMaxFilter7x7_u16nw.cmlib_c_ImageMedianFilterFunc.cmlib_c_ImageMinFilter3x3_16nw.cmlib_c_ImageMinFilter3x3_8nw.cmlib_c_ImageMinFilter3x3_u16nw.cmlib_c_ImageMinFilter5x5_16nw.cmlib_c_ImageMinFilter5x5_8nw.cmlib_c_ImageMinFilter5x5_u16nw.cmlib_c_ImageMinFilter7x7_16nw.cmlib_c_ImageMinFilter7x7_8nw.cmlib_c_ImageMinFilter7x7_u16nw.cmlib_c_ImagePolynomialWarp_BC.cmlib_c_ImagePolynomialWarp_BL.cmlib_c_ImagePolynomialWarp_Call.cmlib_c_ImageScale2Func.cmlib_c_ImageSConv_16ext.cmlib_c_ImageSConv_16nw.cmlib_c_ImageSConv_8ext.cmlib_c_ImageSConv_8nw.cmlib_c_ImageSConv_u16ext.cmlib_c_ImageSConv_u16nw.cmlib_c_ImageSobel.cmlib_c_ImageVisVersion.cmlib_c_ImageZoom_BC.cmlib_c_ImageZoom_BC_S16.cmlib_c_ImageZoom_BC_S32.cmlib_c_ImageZoom_BC_U16.cmlib_c_ImageZoom_BL.cmlib_c_ImageZoom_BL_S16.cmlib_c_ImageZoom_BL_U16.cmlib_c_ImageZoom_NN_f.cmlib_ImageAffine_BC_D64.cmlib_ImageAffine_BC_F32.cmlib_ImageAffine_BC_S32.cmlib_ImageAffine_BL_D64.cmlib_ImageAffine_BL_F32.cmlib_ImageAffine_BL_S32.cmlib_ImageAffineEdge.cmlib_ImageAffine_NN_Bit.cmlib_ImageAffine_NN.cmlib_ImageAffineTable_16_3nw.cmlib_ImageAffineTable_16ext.cmlib_ImageAffineTable_16nw.cmlib_ImageAffineTable_32_3nw.cmlib_ImageAffineTable_32ext.cmlib_ImageAffineTable_32nw.cmlib_ImageAffineTable_8_3nw.cmlib_ImageAffineTable_8ext.cmlib_ImageAffineTable_8nw.cmlib_ImageAffineTable_D64_3nw.cmlib_ImageAffineTable_D64ext.cmlib_ImageAffineTable_D64nw.cmlib_ImageAffineTable_F32_3nw.cmlib_ImageAffineTable_F32ext.cmlib_ImageAffineTable_F32nw.cmlib_ImageClipping.cmlib_ImageConv_16ext.cmlib_ImageConv_16nw.cmlib_ImageConv2x2_f.cmlib_ImageConv3x3_Bit.cmlib_ImageConv_8ext.cmlib_ImageConv_8nw.cmlib_ImageConvClearEdge_Bit.cmlib_ImageConvCopyEdge_Bit.cmlib_ImageConvMxN_ext.cmlib_ImageDilate4_1nw.cmlib_ImageDilate4_32nw.cmlib_ImageDilate4_D64.cmlib_ImageDilate4_F32.cmlib_ImageErode4_1nw.cmlib_ImageErode4_32nw.cmlib_ImageErode4_D64.cmlib_ImageErode4_F32.cmlib_ImageFlipMainDiag_Bit.cmlib_ImageFlipRotate.cmlib_ImageMaxFilter3x3_1nw.cmlib_ImageMaxFilter3x3_32nw.cmlib_ImageMaxFilter3x3_D64.cmlib_ImageMaxFilter3x3_F32.cmlib_ImageMaxFilter5x5_32nw.cmlib_ImageMaxFilter5x5_D64.cmlib_ImageMaxFilter5x5_F32.cmlib_ImageMaxFilter7x7_32nw.cmlib_ImageMaxFilter7x7_D64.cmlib_ImageMaxFilter7x7_F32.cmlib_ImageMedianFilter3x3Func.cmlib_ImageMedianFilter5x5Func.cmlib_ImageMedianFilterFunc.cmlib_ImageMedianFilterMxNFunc.cmlib_ImageMinFilter3x3_1nw.cmlib_ImageMinFilter3x3_32nw.cmlib_ImageMinFilter3x3_D64.cmlib_ImageMinFilter3x3_F32.cmlib_ImageMinFilter5x5_32nw.cmlib_ImageMinFilter5x5_D64.cmlib_ImageMinFilter5x5_F32.cmlib_ImageMinFilter7x7_32nw.cmlib_ImageMinFilter7x7_D64.cmlib_ImageMinFilter7x7_F32.cmlib_ImagePolynomialWarp_0_Fp.cmlib_ImagePolynomialWarp_BC_Fp.cmlib_ImagePolynomialWarp_BL_Fp.cmlib_ImagePolynomialWarp_Call_Fp.cmlib_ImagePolynomialWarp_NN_2_Fp.cmlib_ImagePolynomialWarp_NN_2.cmlib_ImagePolynomialWarp_NN_3_Fp.cmlib_ImagePolynomialWarp_NN_3.cmlib_ImagePolynomialWarp_NN_4_Fp.cmlib_ImagePolynomialWarp_NN_4.cmlib_ImagePolynomialWarp_NN_5_Fp.cmlib_ImagePolynomialWarp_NN_5.cmlib_ImagePolynomialWarpTable_f.cmlib_ImagePolynomialWarpTable_ft.cmlib_ImagePolynomialWarpTable_it.cmlib_ImageReformat_f1.cmlib_ImageReformat_f2.cmlib_ImageReformat_fp.cmlib_ImageSConv_32ext.cmlib_ImageSConv_32nw.cmlib_ImageSubsamBinToGray2x2.cmlib_ImageSubsamBinToGray4x4.cmlib_ImageSubsampleAverage_D64.cmlib_ImageSubsampleAverage_F32.cmlib_ImageThresh1_D64.cmlib_ImageThresh1_F32.cmlib_ImageZoom_BC_Fp_D64.cmlib_ImageZoom_BC_Fp.cmlib_ImageZoom_BL_Fp_D64.cmlib_ImageZoom_BL_Fp.cmlib_ImageAffineTable_F32nw_2.cJava_com_sun_medialib_mlib_Image_ColorConvert2_1FpJava_com_sun_medialib_mlib_Image_ConstDiv__Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_Thresh5__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3I_3IJava_com_sun_medialib_mlib_Image_MinFilter7x7Java_com_sun_medialib_mlib_Image_Not__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_GradientMxNJava_com_sun_medialib_mlib_Image_ConvMxN_1FpJava_com_sun_medialib_mlib_Image_Gradient3x3Java_com_sun_medialib_mlib_Image_BlendJava_com_sun_medialib_mlib_Image_IFFT_12___3D_3DJava_com_sun_medialib_mlib_Image_Exp_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_MaxFilter7x7Java_com_sun_medialib_mlib_Image_MaxFilter5x5Java_com_sun_medialib_mlib_Image_Rotate180Java_com_sun_medialib_mlib_Image_SubsampleBinaryToGrayJava_com_sun_medialib_mlib_Image_SConv7x7_1FpJava_com_sun_medialib_mlib_Image_ConstAdd_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_Conv3x3expf@@GLIBC_2.2.5Java_com_sun_medialib_mlib_Image_ConstMul__Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_ZoomTranslate_1FpJava_com_sun_medialib_mlib_Image_Abs__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Abs_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2memalign@@GLIBC_2.2.5Java_com_sun_medialib_mlib_Image_ConvolveMxNJava_com_sun_medialib_mlib_Image_Xor__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_PolynomialWarp2_1FpJava_com_sun_medialib_mlib_Image_Conv7x7Java_com_sun_medialib_mlib_Image_MinFilter5x5Java_com_sun_medialib_mlib_Image_ColorConvert1Java_com_sun_medialib_mlib_Image_Add_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Gradient3x3_1FpJava_com_sun_medialib_mlib_Image_Erode8Java_com_sun_medialib_mlib_Image_MinFilter3x3Java_com_sun_medialib_mlib_Image_Log_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2sqrtf@@GLIBC_2.2.5Java_com_sun_medialib_mlib_Image_ConstSub__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_FilteredSubsample_1FpJava_com_sun_medialib_mlib_Image_Log__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Mul_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_SConvKernelConvertJava_com_sun_medialib_mlib_Image_Scale_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3DJava_com_sun_medialib_mlib_Image_Add_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Erode4_1FpJava_com_sun_medialib_mlib_Image_Erode8_1FpJava_com_sun_medialib_mlib_Image_ReformatJava_com_sun_medialib_mlib_Image_Rotate90Java_com_sun_medialib_mlib_Image_Dilate4Java_com_sun_medialib_mlib_Image_SubsampleAverage_1FpJava_com_sun_medialib_mlib_Image_FFT_12___3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_SobelJava_com_sun_medialib_mlib_Image_ColorTrue2IndexInitJava_com_sun_medialib_mlib_Image_Invert_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Max__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_PolynomialWarp_1FpJava_com_sun_medialib_mlib_Image_ColorTrue2Indexcosf@@GLIBC_2.2.5Java_com_sun_medialib_mlib_Image_ConstAdd__Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_Blend1_1FpJava_com_sun_medialib_mlib_Image_Conv4x4SUNW_1.1.1_01Java_com_sun_medialib_mlib_Image_PolynomialWarp2Java_com_sun_medialib_mlib_Image_AffineTable2_1FpJava_com_sun_medialib_mlib_Image_SubsampleAverageJava_com_sun_medialib_mlib_Image_Rotate270_1FpJava_com_sun_medialib_mlib_Image_And__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_BlendMulti_1FpJava_com_sun_medialib_mlib_Image_Erode4Java_com_sun_medialib_mlib_Image_ExtremaLocations_1FpJava_com_sun_medialib_mlib_Image_PolynomialWarpTable2_1FpJava_com_sun_medialib_mlib_Image_IFFT_12___3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_Max_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_initIDsJava_com_sun_medialib_mlib_Image_GradientMxN_1FpJava_com_sun_medialib_mlib_Image_Thresh4_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_Dilate8_1FpJava_com_sun_medialib_mlib_Image_Conv7x7_1FpJava_com_sun_medialib_mlib_Image_Div_1FpJava_com_sun_medialib_mlib_Image_FlipXJava_com_sun_medialib_mlib_Image_ConstAdd__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3Iceil@@GLIBC_2.2.5Java_com_sun_medialib_mlib_Image_Thresh2_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3DJava_com_sun_medialib_mlib_Image_MedianFilterMxNJava_com_sun_medialib_mlib_Image_AffineJava_com_sun_medialib_mlib_Image_Scale2__Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3Dmemmove@@GLIBC_2.2.5Java_com_sun_medialib_mlib_Image_Mul_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Thresh1_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3D_3DJava_com_sun_medialib_mlib_Image_GridWarp_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3F_3FDDIIIIIIIIJava_com_sun_medialib_mlib_Image_Thresh1__Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3I_3IJava_com_sun_medialib_mlib_Image_ZoomTranslateTableJava_com_sun_medialib_mlib_Image_IFFT_11___3D_3DJava_com_sun_medialib_mlib_Image_Or__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Conv3x3_1FpJava_com_sun_medialib_mlib_Image_Abs_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_ColorErrorDiffusionMxNJava_com_sun_medialib_mlib_Image_MaxFilter5x5_1FpJava_com_sun_medialib_mlib_Image_Exp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_And__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2log@@GLIBC_2.2.5Java_com_sun_medialib_mlib_Image_GridWarpTable_1FpJava_com_sun_medialib_mlib_Image_FlipY_1FpJava_com_sun_medialib_mlib_Image_Log_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_SConv7x7Java_com_sun_medialib_mlib_Image_Affine2Java_com_sun_medialib_mlib_Image_Exp__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_FlipAntiDiagJava_com_sun_medialib_mlib_Image_Or__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_GridWarpTable2Java_com_sun_medialib_mlib_Image_ZoomTranslateToGraySUNW_1.1Java_com_sun_medialib_mlib_Image_Conv5x5Java_com_sun_medialib_mlib_Image_Blend2Java_com_sun_medialib_mlib_Image_ClearJava_com_sun_medialib_mlib_Image_getVersionJava_com_sun_medialib_mlib_Image_Scale2__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3DJava_com_sun_medialib_mlib_Image_DataTypeConvertJava_com_sun_medialib_mlib_Image_ConvolveMxN_1FpJava_com_sun_medialib_mlib_Image_ColorOrderedDither8x8strcpy@@GLIBC_2.2.5Java_com_sun_medialib_mlib_Image_ColorOrderedDitherMxNJava_com_sun_medialib_mlib_Image_ConstDiv__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3DSUNW_1.0.1Java_com_sun_medialib_mlib_Image_ConstXor__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3Imemcpy@@GLIBC_2.2.5Java_com_sun_medialib_mlib_Image_Histogram2Java_com_sun_medialib_mlib_Image_Thresh2_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3DJava_com_sun_medialib_mlib_Image_Add__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_ExtremaLocationsJava_com_sun_medialib_mlib_Image_Thresh3__Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3IJava_com_sun_medialib_mlib_Image_Thresh4_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_ConstOr__Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_ConstOr__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_Blend1Java_com_sun_medialib_mlib_Image_Dilate4_1FpJava_com_sun_medialib_mlib_Image_Conv5x5_1FpJava_com_sun_medialib_mlib_Image_Exp_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Invert_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Rotate270Java_com_sun_medialib_mlib_Image_ConstSub__Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_SConv5x5Java_com_sun_medialib_mlib_Image_PolynomialWarprealloc@@GLIBC_2.2.5Java_com_sun_medialib_mlib_Image_MaxFilter7x7_1FpJava_com_sun_medialib_mlib_Image_MedianFilter7x7Java_com_sun_medialib_mlib_Image_Extrema2Java_com_sun_medialib_mlib_Image_LookUp2Java_com_sun_medialib_mlib_Image_ConstSub_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3DSUNW_1.1.2Java_com_sun_medialib_mlib_Image_Extrema2_1FpJava_com_sun_medialib_mlib_Image_Thresh4__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3I_3I_3IJava_com_sun_medialib_mlib_Image_Log__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Dilate8Java_com_sun_medialib_mlib_Image_MedianFilterMxN_1FpJava_com_sun_medialib_mlib_Image_Affine_1FpJava_com_sun_medialib_mlib_Image_MulShift__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2IJava_com_sun_medialib_mlib_Image_ConstAnd__Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_AffineTableJava_com_sun_medialib_mlib_Image_FFT_12___3D_3DJava_com_sun_medialib_mlib_Image_Thresh5__Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3I_3Isqrt@@GLIBC_2.2.5Java_com_sun_medialib_mlib_Image_GridWarp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3F_3FDDIIIIIIIIJava_com_sun_medialib_mlib_Image_Thresh3_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3DJava_com_sun_medialib_mlib_Image_FourierTransformJava_com_sun_medialib_mlib_Image_BlendMultiJava_com_sun_medialib_mlib_Image_Sobel_1FpJava_com_sun_medialib_mlib_Image_Min_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_MeanJava_com_sun_medialib_mlib_Image_Abs__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Conv4x4_1FpJava_com_sun_medialib_mlib_Image_FlipYJava_com_sun_medialib_mlib_Image_FFT_13___3D_3DJava_com_sun_medialib_mlib_Image_MaxFilter3x3Java_com_sun_medialib_mlib_Image_Min_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_FilteredSubsamplesinf@@GLIBC_2.2.5Java_com_sun_medialib_mlib_Image_Max_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_GridWarp2_1FpJava_com_sun_medialib_mlib_Image_SConv3x3_1FpJava_com_sun_medialib_mlib_Image_ConstSub_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_MinFilter5x5_1FpJava_com_sun_medialib_mlib_Image_Thresh3__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3IJava_com_sun_medialib_mlib_Image_MedianFilter3x3Java_com_sun_medialib_mlib_Image_Clear_1FpJava_com_sun_medialib_mlib_Image_Max__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Min__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_PolynomialWarpTable2Java_com_sun_medialib_mlib_Image_MedianFilter5x5_1FpJava_com_sun_medialib_mlib_Image_FlipX_1FpJava_com_sun_medialib_mlib_Image_Rotate90_1FpJava_com_sun_medialib_mlib_Image_Thresh4__Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3I_3I_3Imemset@@GLIBC_2.2.5Java_com_sun_medialib_mlib_Image_ConstDiv_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_ConstDiv_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_Invert__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Add__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_DivShiftJava_com_sun_medialib_mlib_Image_FlipMainDiagJava_com_sun_medialib_mlib_Image_initinterptableIDsJava_com_sun_medialib_mlib_Image_Thresh2__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3IJava_com_sun_medialib_mlib_Image_Invert__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_SubJava_com_sun_medialib_mlib_Image_MinFilter7x7_1FpJava_com_sun_medialib_mlib_Image_CopyJava_com_sun_medialib_mlib_Image_ColorDitherInitJava_com_sun_medialib_mlib_Image_IFFT_13___3D_3DJava_com_sun_medialib_mlib_Image_ConstMul_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_Not__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_MinFilter3x3_1FpJava_com_sun_medialib_mlib_Image_Thresh1__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3I_3IJava_com_sun_medialib_mlib_Image_Conv2x2_1FpJava_com_sun_medialib_mlib_Image_MedianFilter5x5Java_com_sun_medialib_mlib_Image_Scale_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3DSUNW_1.0Java_com_sun_medialib_mlib_Image_ChannelInsertJava_com_sun_medialib_mlib_Image_ColorConvert2Java_com_sun_medialib_mlib_Image_Thresh5_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3D_3DJava_com_sun_medialib_mlib_Image_ZoomTranslateJava_com_sun_medialib_mlib_Image_PolynomialWarpTableJava_com_sun_medialib_mlib_Image_PolynomialWarpTable_1FpJava_com_sun_medialib_mlib_Image_FFT_11___3D_3DJava_com_sun_medialib_mlib_Image_ConstAdd_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_Sub_1FpJava_com_sun_medialib_mlib_Image_GridWarpTable2_1FpJava_com_sun_medialib_mlib_Image_AffineTable2Java_com_sun_medialib_mlib_Image_GridWarp2Java_com_sun_medialib_mlib_Image_Blend_1FpJava_com_sun_medialib_mlib_Image_FlipAntiDiag_1FpJava_com_sun_medialib_mlib_Image_ColorErrorDiffusion3x3Java_com_sun_medialib_mlib_Image_Blend2_1FpJava_com_sun_medialib_mlib_Image_ColorConvert1_1FpJava_com_sun_medialib_mlib_Image_Thresh5_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3D_3DJava_com_sun_medialib_mlib_Image_initcolormapIDsJava_com_sun_medialib_mlib_Image_MedianFilter3x3_1FpJava_com_sun_medialib_mlib_Image_ChannelExtractJava_com_sun_medialib_mlib_Image_Rotate180_1Fpfree@@GLIBC_2.2.5Java_com_sun_medialib_mlib_Image_ConvMxNJava_com_sun_medialib_mlib_Image_GridWarpTableJava_com_sun_medialib_mlib_Image_FFT_13___3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_Conv2x2Java_com_sun_medialib_mlib_Image_Thresh2__Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3IJava_com_sun_medialib_mlib_Image_SConv5x5_1FpJava_com_sun_medialib_mlib_Image_ConstXor__Lcom_sun_medialib_mlib_mediaLibImage_2_3Istrstr@@GLIBC_2.2.5Java_com_sun_medialib_mlib_Image_ConstMul_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_ZoomTranslateTable_1FpJava_com_sun_medialib_mlib_Image_FlipMainDiag_1FpJava_com_sun_medialib_mlib_Image_Mean_1FpJava_com_sun_medialib_mlib_Image_Xor__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Thresh1_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3D_3DJava_com_sun_medialib_mlib_Image_IFFT_11___3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_ConstAnd__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_MedianFilter7x7_1FpJava_com_sun_medialib_mlib_Image_SConv3x3Java_com_sun_medialib_mlib_Image_Min__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_MaxFilter3x3_1Fpstrcat@@GLIBC_2.2.5Java_com_sun_medialib_mlib_Image_AffineTable_1FpJava_com_sun_medialib_mlib_Image_ConvKernelConvertJava_com_sun_medialib_mlib_Image_Affine2_1FpJava_com_sun_medialib_mlib_Image_FFT_11___3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_IFFT_13___3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_ConstMul__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_Thresh3_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3DJava_com_sun_medialib_mlib_Image_MulShift__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Ijai-core-1.1.4/src/share/mediaLib/linux/i386/0000755000175000017500000000000011633360235020311 5ustar mathieumathieujai-core-1.1.4/src/share/mediaLib/linux/i386/libmlib_jai.so0000644000175000017500001614700210473721517023131 0ustar mathieumathieuELF4'84 (77777H777sYvS  wRD~huT *Z;k{yt$UE6>fMK}I,p  m+<.07[Gj\:z!Xea/]`5B3Pl)-4'NO@dbC_ &nHJ8"(ricF^#%xAWo19=?|QgVLq2 ^_a\bb  6 7 7 7705 ` 8va 'p/ *6 0R ' "@ _pu B  " =" -@L > .PX@ s `   > @ ;@E t  0' :} , E    e'-  З0 O@  '+ C 9 !?P 0 )4 (/Y 0 ^  2 p ,0I  -P p ? "A 1_ nn >  V  ,@A =a    0 ]@  zD'D&P,0=  @<7 X? -O  @   V` }>t VB 0 `C% R )8`uB @ 2 sp0 L p 64j5 $p%  /[ =)30 3Pg &2d  r<P %B +`8 0  8 ? 1Dk" D` SP0 ==   .WA  P @ +070 =PX = qD  P0  &CpV DX\/Z5 l `'  )@P  @ fD 4k Lp 0 $ 5n 7sg   k ` 0  {   n- N  x.pTA ,P9 " $p#  P  D60qg    CP $p&  %(P13 p ; XA  9 x  5o5  `@ -1` %)0 "  `  A !0 &* > f!@ P J.0S@ '.  5l5 M#p  ~%( 0 >Кt #p" s -P E7rB C DYC 0 j P x  " I03   C0% b3ph5   X1`0 C''`0 K,|F ? B  Q*5 3', 1Pca  #p! .0\5 ]DD  l5 9z R;}E =4 ,D&D>P4 @@  }1 b0 p=X ;7 Df 0 .u p DC0 9Я d9@ya C% ~#p  Y0 ?-0M 0 <p A   3i .U@ \ 0 <  ## F:{0 2ea A K P J$p$ .QA 6D$p'0 "  < _ &` @0 B  RC0g6@p (2 Java_com_sun_medialib_mlib_Image_Abs__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Abs__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Abs_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Abs_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Add__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Add__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Add_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Add_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_AffineJava_com_sun_medialib_mlib_Image_Affine2Java_com_sun_medialib_mlib_Image_Affine_1FpJava_com_sun_medialib_mlib_Image_Affine2_1FpJava_com_sun_medialib_mlib_Image_And__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_And__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_BlendJava_com_sun_medialib_mlib_Image_Blend1Java_com_sun_medialib_mlib_Image_Blend1_1FpJava_com_sun_medialib_mlib_Image_Blend2Java_com_sun_medialib_mlib_Image_Blend2_1FpJava_com_sun_medialib_mlib_Image_Blend_1FpJava_com_sun_medialib_mlib_Image_ChannelExtractJava_com_sun_medialib_mlib_Image_ChannelInsertJava_com_sun_medialib_mlib_Image_ClearJava_com_sun_medialib_mlib_Image_Clear_1FpJava_com_sun_medialib_mlib_Image_ColorConvert1Java_com_sun_medialib_mlib_Image_ColorConvert1_1FpJava_com_sun_medialib_mlib_Image_ColorConvert2Java_com_sun_medialib_mlib_Image_ColorConvert2_1FpJava_com_sun_medialib_mlib_Image_ColorErrorDiffusion3x3Java_com_sun_medialib_mlib_Image_ColorOrderedDither8x8Java_com_sun_medialib_mlib_Image_ColorTrue2IndexJava_com_sun_medialib_mlib_Image_ColorTrue2IndexInitJava_com_sun_medialib_mlib_Image_ConstAdd__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_ConstAdd__Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_ConstAdd_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_ConstAdd_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_ConstAnd__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_ConstAnd__Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_ConstDiv__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_ConstDiv__Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_ConstDiv_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_ConstDiv_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_ConstMul__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_ConstMul__Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_ConstMul_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_ConstMul_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_ConstOr__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_ConstOr__Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_ConstSub__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_ConstSub__Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_ConstSub_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_ConstSub_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_ConstXor__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_ConstXor__Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_Conv2x2Java_com_sun_medialib_mlib_Image_Conv2x2_1FpJava_com_sun_medialib_mlib_Image_Conv3x3Java_com_sun_medialib_mlib_Image_Conv3x3_1FpJava_com_sun_medialib_mlib_Image_Conv4x4Java_com_sun_medialib_mlib_Image_Conv4x4_1FpJava_com_sun_medialib_mlib_Image_Conv5x5Java_com_sun_medialib_mlib_Image_Conv5x5_1FpJava_com_sun_medialib_mlib_Image_Conv7x7Java_com_sun_medialib_mlib_Image_Conv7x7_1FpJava_com_sun_medialib_mlib_Image_ConvKernelConvertJava_com_sun_medialib_mlib_Image_ConvMxNJava_com_sun_medialib_mlib_Image_ConvMxN_1FpJava_com_sun_medialib_mlib_Image_ConvolveMxNJava_com_sun_medialib_mlib_Image_ConvolveMxN_1FpJava_com_sun_medialib_mlib_Image_CopyJava_com_sun_medialib_mlib_Image_DataTypeConvertJava_com_sun_medialib_mlib_Image_Dilate4Java_com_sun_medialib_mlib_Image_Dilate4_1FpJava_com_sun_medialib_mlib_Image_Dilate8Java_com_sun_medialib_mlib_Image_Dilate8_1FpJava_com_sun_medialib_mlib_Image_DivShiftJava_com_sun_medialib_mlib_Image_Div_1FpJava_com_sun_medialib_mlib_Image_Erode4Java_com_sun_medialib_mlib_Image_Erode4_1FpJava_com_sun_medialib_mlib_Image_Erode8Java_com_sun_medialib_mlib_Image_Erode8_1FpJava_com_sun_medialib_mlib_Image_Exp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Exp__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Exp_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Exp_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Extrema2Java_com_sun_medialib_mlib_Image_Extrema2_1FpJava_com_sun_medialib_mlib_Image_ExtremaLocationsJava_com_sun_medialib_mlib_Image_ExtremaLocations_1FpJava_com_sun_medialib_mlib_Image_FlipAntiDiagJava_com_sun_medialib_mlib_Image_FlipAntiDiag_1FpJava_com_sun_medialib_mlib_Image_FlipMainDiagJava_com_sun_medialib_mlib_Image_FlipMainDiag_1FpJava_com_sun_medialib_mlib_Image_FlipXJava_com_sun_medialib_mlib_Image_FlipX_1FpJava_com_sun_medialib_mlib_Image_FlipYJava_com_sun_medialib_mlib_Image_FlipY_1FpJava_com_sun_medialib_mlib_Image_FourierTransformJava_com_sun_medialib_mlib_Image_Gradient3x3Java_com_sun_medialib_mlib_Image_Gradient3x3_1FpJava_com_sun_medialib_mlib_Image_GradientMxNJava_com_sun_medialib_mlib_Image_GradientMxN_1FpJava_com_sun_medialib_mlib_Image_GridWarp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3F_3FDDIIIIIIIIJava_com_sun_medialib_mlib_Image_GridWarp2Java_com_sun_medialib_mlib_Image_GridWarp_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3F_3FDDIIIIIIIIJava_com_sun_medialib_mlib_Image_GridWarp2_1FpJava_com_sun_medialib_mlib_Image_Histogram2Java_com_sun_medialib_mlib_Image_Invert__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Invert__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Invert_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Invert_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Log__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Log__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Log_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Log_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_LookUp2Java_com_sun_medialib_mlib_Image_Max__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Max__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Max_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Max_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_MaxFilter3x3Java_com_sun_medialib_mlib_Image_MaxFilter3x3_1FpJava_com_sun_medialib_mlib_Image_MaxFilter5x5Java_com_sun_medialib_mlib_Image_MaxFilter5x5_1FpJava_com_sun_medialib_mlib_Image_MaxFilter7x7Java_com_sun_medialib_mlib_Image_MaxFilter7x7_1FpJava_com_sun_medialib_mlib_Image_MeanJava_com_sun_medialib_mlib_Image_Mean_1FpJava_com_sun_medialib_mlib_Image_MedianFilter3x3Java_com_sun_medialib_mlib_Image_MedianFilter3x3_1FpJava_com_sun_medialib_mlib_Image_MedianFilter5x5Java_com_sun_medialib_mlib_Image_MedianFilter5x5_1FpJava_com_sun_medialib_mlib_Image_MedianFilter7x7Java_com_sun_medialib_mlib_Image_MedianFilter7x7_1FpJava_com_sun_medialib_mlib_Image_MedianFilterMxNJava_com_sun_medialib_mlib_Image_MedianFilterMxN_1FpJava_com_sun_medialib_mlib_Image_Min__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Min__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Min_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Min_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_MinFilter3x3Java_com_sun_medialib_mlib_Image_MinFilter3x3_1FpJava_com_sun_medialib_mlib_Image_MinFilter5x5Java_com_sun_medialib_mlib_Image_MinFilter5x5_1FpJava_com_sun_medialib_mlib_Image_MinFilter7x7Java_com_sun_medialib_mlib_Image_MinFilter7x7_1FpJava_com_sun_medialib_mlib_Image_MulShift__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2IJava_com_sun_medialib_mlib_Image_MulShift__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2IJava_com_sun_medialib_mlib_Image_Mul_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Mul_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Not__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Not__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Or__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Or__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_PolynomialWarpsqrtJava_com_sun_medialib_mlib_Image_PolynomialWarp2Java_com_sun_medialib_mlib_Image_PolynomialWarp_1FpJava_com_sun_medialib_mlib_Image_PolynomialWarp2_1FpJava_com_sun_medialib_mlib_Image_ReformatJava_com_sun_medialib_mlib_Image_Rotate180Java_com_sun_medialib_mlib_Image_Rotate180_1FpJava_com_sun_medialib_mlib_Image_Rotate270Java_com_sun_medialib_mlib_Image_Rotate270_1FpJava_com_sun_medialib_mlib_Image_Rotate90Java_com_sun_medialib_mlib_Image_Rotate90_1FpJava_com_sun_medialib_mlib_Image_SConv3x3Java_com_sun_medialib_mlib_Image_SConv3x3_1FpJava_com_sun_medialib_mlib_Image_SConv5x5Java_com_sun_medialib_mlib_Image_SConv5x5_1FpJava_com_sun_medialib_mlib_Image_SConv7x7Java_com_sun_medialib_mlib_Image_SConv7x7_1FpJava_com_sun_medialib_mlib_Image_SConvKernelConvertJava_com_sun_medialib_mlib_Image_Scale2__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3DJava_com_sun_medialib_mlib_Image_Scale2__Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3DJava_com_sun_medialib_mlib_Image_Scale_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3DJava_com_sun_medialib_mlib_Image_Scale_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3DJava_com_sun_medialib_mlib_Image_SobelJava_com_sun_medialib_mlib_Image_Sobel_1FpJava_com_sun_medialib_mlib_Image_SubJava_com_sun_medialib_mlib_Image_Sub_1FpJava_com_sun_medialib_mlib_Image_Thresh1__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3I_3IJava_com_sun_medialib_mlib_Image_Thresh1__Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3I_3IJava_com_sun_medialib_mlib_Image_Thresh1_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3D_3DJava_com_sun_medialib_mlib_Image_Thresh1_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3D_3DJava_com_sun_medialib_mlib_Image_Thresh2__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3IJava_com_sun_medialib_mlib_Image_Thresh2__Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3IJava_com_sun_medialib_mlib_Image_Thresh2_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3DJava_com_sun_medialib_mlib_Image_Thresh2_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3DJava_com_sun_medialib_mlib_Image_Thresh3__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3IJava_com_sun_medialib_mlib_Image_Thresh3__Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3IJava_com_sun_medialib_mlib_Image_Thresh3_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3DJava_com_sun_medialib_mlib_Image_Thresh3_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3DJava_com_sun_medialib_mlib_Image_Thresh4__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3I_3I_3IJava_com_sun_medialib_mlib_Image_Thresh4__Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3I_3I_3IJava_com_sun_medialib_mlib_Image_Thresh4_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_Thresh4_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_Thresh5__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3I_3IJava_com_sun_medialib_mlib_Image_Thresh5__Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3I_3IJava_com_sun_medialib_mlib_Image_Thresh5_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3D_3DJava_com_sun_medialib_mlib_Image_Thresh5_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3D_3DJava_com_sun_medialib_mlib_Image_Xor__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Xor__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_ZoomTranslateJava_com_sun_medialib_mlib_Image_ZoomTranslate_1FpJava_com_sun_medialib_mlib_Image_AffineTableJava_com_sun_medialib_mlib_Image_AffineTable2Java_com_sun_medialib_mlib_Image_AffineTable_1FpJava_com_sun_medialib_mlib_Image_AffineTable2_1FpJava_com_sun_medialib_mlib_Image_ZoomTranslateTableJava_com_sun_medialib_mlib_Image_ZoomTranslateTable_1FpJava_com_sun_medialib_mlib_Image_GridWarpTableJava_com_sun_medialib_mlib_Image_GridWarpTable2Java_com_sun_medialib_mlib_Image_GridWarpTable_1FpJava_com_sun_medialib_mlib_Image_GridWarpTable2_1FpJava_com_sun_medialib_mlib_Image_ZoomTranslateToGrayJava_com_sun_medialib_mlib_Image_PolynomialWarpTableJava_com_sun_medialib_mlib_Image_PolynomialWarpTable2Java_com_sun_medialib_mlib_Image_PolynomialWarpTable_1FpJava_com_sun_medialib_mlib_Image_PolynomialWarpTable2_1FpJava_com_sun_medialib_mlib_Image_SubsampleBinaryToGrayJava_com_sun_medialib_mlib_Image_FilteredSubsampleJava_com_sun_medialib_mlib_Image_FilteredSubsample_1FpJava_com_sun_medialib_mlib_Image_SubsampleAverageJava_com_sun_medialib_mlib_Image_SubsampleAverage_1FpJava_com_sun_medialib_mlib_Image_ColorDitherInitJava_com_sun_medialib_mlib_Image_ColorErrorDiffusionMxNJava_com_sun_medialib_mlib_Image_ColorOrderedDitherMxNJava_com_sun_medialib_mlib_Image_BlendMultiJava_com_sun_medialib_mlib_Image_BlendMulti_1FpJava_com_sun_medialib_mlib_Image_FFT_11___3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_FFT_11___3D_3DJava_com_sun_medialib_mlib_Image_FFT_12___3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_FFT_12___3D_3DJava_com_sun_medialib_mlib_Image_FFT_13___3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_FFT_13___3D_3DJava_com_sun_medialib_mlib_Image_IFFT_11___3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_IFFT_11___3D_3DJava_com_sun_medialib_mlib_Image_IFFT_12___3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_IFFT_12___3D_3DJava_com_sun_medialib_mlib_Image_IFFT_13___3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_IFFT_13___3D_3DJava_com_sun_medialib_mlib_Image_getVersionstrcpystrcatJava_com_sun_medialib_mlib_Image_initIDsJava_com_sun_medialib_mlib_Image_initcolormapIDsJava_com_sun_medialib_mlib_Image_initinterptableIDsstrstrmemcpysqrtfmemalignreallocfreememsetmemmovesinfcosfexpfloglibc.so.6libm.so.6libmlib_jai.soSUNW_1.0SUNW_1.0.1SUNW_1.1SUNW_1.1.1_01SUNW_1.1.2GLIBC_2.0 ND'= ]D!' $fD]D'= $qDfDA $zDqD"' DzD:D ii DDDii D77 77777 7$7(7,707@7D7H7L7P7T7X7\7`7d7h7l7p7t7x7|777777777777777777777777777777777777 77777 7$7(7,74787<7@7D7H7L7P7T7X7\7`7d7h7l7p7t7x7|777777777777777777777777777777777777 7777 7$7(7,7074787<7@7D7H7L7P7T7X7\7`7d7h7l7p7t7x7|77777777777777777777777777777777777 77777 7$7(7,7074787<7@7D7H7L7P7T7X7\7`7d7h7l7p7t7x7|777777777777777777777777777777777777 77777 7$7(7,7074787<7@7D7H7L7P7T7X7\7`7d7h7l7p7t7x7|777777777777777777777777777777777777 77777 7$7(7,7@7D7H7L7P7T7X7\7`7d7h7l7p7t7x7|777777777777777777777777777777777777 77777 7$7(7,7074787<7@7D7H7L7P7T7X7\7`7d7h7l7p7t7x7|777777777777777777777777777777 77777 7$7(7,74787<7D7H7L7P7T7X7\7d7h7l7t7x7|777777777777777777777777777777 77777$7(7,74787<7@7D7H7L7T7X7\7d7h7l7p7t7x7|777777777777777777777777777777 7777$7(7,7074787<7D7H7L7T7X7\7`7d7h7l7t7x7|77777777777777777777777777777777 7$7(7,7074787<7@7D7`7d7h7l7p7t7x7|77777777777777777777777 7$7(7,7074787<7@7D7`7d7h7l7p7t7x7|77777777777777777777777 7$7(7,7074787<7@7D7`7d7h7l7p7t7x7|77777777777777777777777 7$7(7,7074787<7@7D7`7d7h7l7p7t7x7|7777777777777777777777 77777 7$7(7,7074787<7@7D7H7L7P7T7X7\7`7d7h7l7p7t7x7|7777777777777777777777777777777777 77777 7$7(7,7074787<7@7D7H7L7P7T7X7\7`7d7h7l7p7t7x7|7777777777777777777 77,7<7L7\7l7|7777777777777777777777777777777777 77777 7$7(7,7074787<7@7D7H7L7P7T7X7\7`7d7h7l7p7t7x7|777777777777777777777777777777777777 77777 7$7(7,7074787<7@7D7H7L7P7T7X7\7`7d7h7l7p7t7x7|777777777777777777777777777777777777 77777 7$7@7D7H7L7T7X7\7`7d7h7l7p7t7x7|77777777777777777777777777777777 77777 7$7(7,7074787<7@7D7H7L7P7T7X7\7`7d7h7l7p7t7x7|7777777777777777777777777777777777777 7$7074787<7@7L7P7T7X7\7`7d7h7l7p7t7x7|7777777777777777777777777777777777 77777 7$7(7,7074787<7@7D7H7L7P7T7X7\7`7d7h7l7p7t7x7|777777777777777777777777777777777777 77777 7$7(7,7074787<7@7D7H7L7P7T7X7\7`7d7h7l7p7t7x7|7777777777777777777777777777777 77777 7$7(7,7074787<7@7D7H7L7P7T7X7\7`7d7h7l7t7x7|7777777777777777777777777777777777777 7$7(7,7074787<7@7D7H7L7P7T7X7\7`7d7h7l7p7t7x7|777777777777777777777777777777777777 77777 7$7(7,7074787<7@7D7H7L7P7T7X7\7`7d7h7l7p7t7x7|777777777J7l777777777777 7 hhhhh h($h0(h8p,h@`0hHP4hP@8hX0<h` @hhDhpUWVSU}[$Z7RWuVEP8MUEQR}WuV7 ƋEPMQUR}W49VMQUR}W"9VMQEVNjuVEPUR; uVMQURUR:ue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVS U}[4Y7RWMQuV7PEPUR}Wb8VDVNjMQURURu9 ue[^_]ÍPxM1RWEPVZYPMQV8e[^_]UWVSU}[ÔX7RWuVEPv6MUEQR}WuV^6 ƋEPMQUR}W7VMQUR}W7VMQeBVNjuVEPURq9 uVMQURUR8ue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVS U}[äW7RWMQuV5PEPUR}W6VyAVNjMQURUR7 ue[^_]ÍPxM1RWEPVZYPMQV8e[^_]UWVS,M}[W7W}QuVUR4MEWQ}܋uVEP4 UERWMQuV4ƋEPUR}WMQ5 UR}WMQUR5V}WEPMQ5VUR}WiVNjMQuVUR7 MQuVEPUR7uVMQURUR6 u e[^_]ÐxPU2WQURVZYPEPV8e[^_]UWVSU}[U7RWuVEP3MUEQR}WuV3 ƋEPMQUR}W4VMQUR}W4VMQGVNjuVEPUR6 uVMQURUR5ue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVS,M}[T7W}QuVUR2MEWQ}܋uVEP2 UERWMQuV2ƋEPUR}WMQ3 UR}WMQUR3V}WEPMQ3VUR}WAVNjMQuVUR5 MQuVEPURu5uVMQURUR4 u e[^_]ÐxPU2WQEPVZYPURV8e[^_]UWVSU}[ôS7RWuVEP1MUEQR}WuV~1 ƋEPMQUR}W2VMQUR}W2VMQ@VNjuVEPUR4 uVMQURUR3ue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVS`M}[R7QWuV4UMRQuVEP0 MUERQWuV}0XZUR}W,AMQUREPUR1 VMQURMQ1 VURUR41ҋ LB ~ U ERMQPVMQhI EVuVURMQ@3uVURMQuV[2WEPUR@}Wxu>RQEPWZYPuVW8e[^_]à U RMQWVURH EVuVMQUR2EPMQuVUR1WMQuV(@}ne[^_]ÉUWVSpM}u[Q7QVW3MURQURW.U MEQRVW.E^uXVWt?M$XZQWF>UuEREPURW/M QURMQW/U RURW2E1ҋ LB ~MMQQEPURnMQURMQWz1URMQURW0EPU$RW= VuVW>xQ7QRWVYZPWV8e[^_]PPMQUREtYMQUREPW0URMQURW0MQU$RWQ= VuVWc>Eoe[^_]à E PMQURMQURF E냉'UWVS`M}[O7QWuV*1UMRQuVEP, MUERQWuV,XZUR}W|=MQUREPUR. VMQURMQ- VURUR01ҋ LB ~ U ERMQPVMQ@ EVuVURMQ/uVURMQuV.WEPUR =}Wxu>RQEPWZYPuVW8e[^_]à U RMQWVUR? EVuVMQUR.EPMQuVUR.WMQuVx<}ne[^_]ÉUWVSpM}u[QM7QVWj/MURQURW(+U MEQRVW+E^uXVW;M$XZQW;UuEREPURW>,M QURMQW),U RURW(/E1ҋ LB ~MMQQEPURGMQURMQW-URMQURW,EPU$RWG; VuVW9;xQ7QRWVYZPWV8e[^_]PPMQUREtYMQUREPWD-URMQURWb,MQU$RW: VuVW:Eoe[^_]à E PMQURMQURQ= E냉'UWVS,M}[dK7W}QuVURC)MEWQ}܋uVEP+) UERWMQuV)ƋEPUR}WMQ\* UR}WMQURD*V}WEPMQ2*VUR}WbVNjMQuVUR , MQuVEPUR+uVMQURUR+ u e[^_]ÐxPU2WQEPVZYPURV8e[^_]UWVSU}[4J7RWuVEP(MUEQR}WuV' ƋEPMQUR}WD)VMQUR}W2)VMQeVNjuVEPUR+ uVMQURUR)*ue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVS,UM[DI7RQuV}W&'UMERQuV}W' UM܉ERQuV}W&U؍MRQEPuV& ỦRMQURMQ"(URMQUREP ( WURMQUR'VMQURMQ' VWUREPtiEVMQURuV) WMQUR}W)uVMQUREP) }WuVMQUR(UЃue[^_]ÍPx}7RQEPVZYP}WV8e[^_]Ít&'UWVS,M}[G7W}QuVUR%MEWQ}܋uVEP% UERWMQuVs%ƋEPUR}WMQ& UR}WMQUR&V}WEPMQ&VUR}WVNjMQuVURm( MQuVEPURU(uVMQURURp' u e[^_]ÐxPU2WQEPVZYPURV8e[^_]UWVS,M}[ÔF7W}QuVURs$MEWQ}܋uVEP[$ UERWMQuVC$ƋEPUR}WMQ% UR}WMQURt%V}WEPMQb%VUR}WVNjMQuVUR=' MQuVEPUR%'uVMQURUR@& u e[^_]ÐxPU2WQEPVZYPURV8e[^_]UWVS,M}[dE7W}QuVURC#MEWQ}܋uVEP+# UERWMQuV#ƋEPUR}WMQ\$ UR}WMQURD$V}WEPMQ2$VUR}WѽVNjMQuVUR & MQuVEPUR%uVMQURUR% u e[^_]ÐxPU2WQEPVZYPURV8e[^_]UWVS,M}[4D7W}QuVUR"MEWQ}܋uVEP! UERWMQuV!ƋEPUR}WMQ,# UR}WMQUR#V}WEPMQ#VUR}W葮VNjMQuVUR$ MQuVEPUR$uVMQURUR# u e[^_]ÐxPU2WQEPVZYPURV8e[^_]UWVS,UM[C7RQuV}W UMERQuV}W  UM܉ERQuV}W U؍MRQEPuV  ỦRMQURMQ!URMQUREP! WURMQUR!VMQURMQ! VWUREPdEVMQURuV# WMQUR}Wj#uVMQUREPU# }WuVMQURm"UЃue[^_]ÍPx}7RQEPVZYP}WV8e[^_]Ít&'UWVSM}[ÄA7W}QuVURcMEWQuVURN }WEPMQUR V}WMQUR }WVMQwVNjuVEPUR]" uVMQURURu!ue[^_]ÍxPU2WQEPVZYPURV8e[^_]UWVSM}[Ô@7W}QuVURsMEWQuVUR^ }WEPMQURV}WMQUR}WVMQyVNjuVEPURm! uVMQURUR ue[^_]ÍxPU2WQEPVZYPURV8e[^_]UWVS U}[ä?7WREPuVMXZQ}W-VNjURMQURWV{EVEPURuVWMQUR-Uue[^_]ÍxPM1WREPVZYPMQV8e[^_] UWVS U}[>7WREPuVMXZQ}We-VNjURMQURWVGvEVEPURuVWMQURa-Uue[^_]ÍxPM1WREPVZYPMQV8e[^_] UWVSMU[>7Q}RuVEPMEWQURuV}WMQ|,UER}WMQUR VEP}WUR MQV}WVNjuVURMQ uVEPURuV MQURUR>,ue[^_]ÍPxU2QWURVZYPEPV8e[^_] UWVSMU[<7Q}RuVEPMEWQURuV}WMQ\+UER}WMQUR VEP}WUR MQV}W|VNjuVURMQ uVEPURuV MQURUR+ue[^_]ÍPxU2QWURVZYPEPV8e[^_] UWVSM}[;7QWEPuVUMERQ}WuVURMQ<*E_XEP}W**U܉RMQURMQ VURMQURWEPVMQ EVURuVMQwURuVMQURWEP}W) uVMQUR)Uue[^_]ÍPxU2QWEPVZYPURV8e[^_]Ít&'UWVSUM[Ä:7RQ}WuVfMUERQ}WuVNUREP(EZYMQ}W(U܉RMQURMQs VUREPMQ^WURVMQ{ EVURuVMQ7URuVEPMQRWUR}W( uVMQUR(Eue[^_]ÍVx}7RQ}WV_ZPEPV8e[^_]Ít&'UWVS MQuVURMQuVEPURuVMQURURR$u e[^_]ÍvPxM1RWEPVZYPMQV8e[^_]UWVS }WMQUER}WMQURt  VEP}WUR_  MQV}W) VNjuVURMQ: uVEPURuVR MQURURue[^_]ÍPxU2QWURVZYPEPV8e[^_] UWVS U}[T-7WREPuV6 MXZQ}WVNjURMQURq WV( EVEPURuV WMQURUue[^_]ÍxPM1WREPVZYPMQV8e[^_] UWVSMU[Ä,7Q}RuVEPc MEWQURuVN }WMQUER}WMQUR  VEP}WURo  MQV}W>% VNjuVURMQJ  uVEPURuVb  MQURURue[^_]ÍPxU2QWURVZYPEPV8e[^_] UWVS U}[d+7WREPuVF MXZQ}WVNjURMQUR WVg% EVEPURuV WMQURUue[^_]ÍxPM1WREPVZYPMQV8e[^_] UWVSMU[Ô*7Q}RuVEPsMEWQURuV^}WMQ UER}WMQUR  VEP}WUR  MQV}WN4 VNjuVURMQZ  uVEPURuVr  MQURURue[^_]ÍPxU2QWURVZYPEPV8e[^_] UWVS U}[t)7WREPuVVMXZQ}WVNjURMQURWV3 EVEPURuV WMQURUue[^_]ÍxPM1WREPVZYPMQV8e[^_] UWVSMU[ä(7Q}RuVEPMEWQURuVn}WMQUER}WMQUR VEP}WUR MQV}W- VNjuVURMQj  uVEPURuV MQURURue[^_]ÍPxU2QWURVZYPEPV8e[^_] UWVS U}[Ä'7WREPuVfMXZQ}WVNjURMQURWV0 EVEPURuVWMQURUue[^_]ÍxPM1WREPVZYPMQV8e[^_] UWVSMU[ô&7Q}RuVEPMEWQURuV~}WMQ UER}WMQUR VEP}WUR MQV}W)VNjuVURMQz uVEPURuV MQURURue[^_]ÍPxU2QWURVZYPEPV8e[^_] UWVS U}[Ô%7WREPuVvMXZQ}WVNjURMQURWV< EVEPURuVWMQURUue[^_]ÍxPM1WREPVZYPMQV8e[^_] UWVSMU[$7Q}RuVEPMEWQURuV}WMQUER}WMQUR VEP}WUR MQV}W8VNjuVURMQ uVEPURuV MQURURue[^_]ÍPxU2QWURVZYPEPV8e[^_] UWVS U}[ä#7WREPuVMXZQ}WVNjURMQURWV? EVEPURuVWMQURUue[^_]ÍxPM1WREPVZYPMQV8e[^_] UWVSMU["7Q}RuVEPMEWQURuV}WMQLUER}WMQUR VEP}WUR MQV}Wn9 VNjuVURMQ uVEPURuV MQURURue[^_]ÍPxU2QWURVZYPEPV8e[^_] UWVS U}[ô!7WREPuVMXZQ}WEVNjURMQURWVW8 EVEPURuVWMQURAUue[^_]ÍxPM1WREPVZYPMQV8e[^_] UWVSMU[ 7Q}RuVEPMEWQURuV}WMQ<UER}WMQUR VEP}WUR MQV}W>VNjuVURMQ uVEPURuV MQURUR ue[^_]ÍPxU2QWURVZYPEPV8e[^_] UWVS U}[7WREPuVMXZQ}W5 VNjURMQURWVH EVEPURuVWMQUR1 Uue[^_]ÍxPM1WREPVZYPMQV8e[^_] UWVSMU[7QR}WuVMUEQR}WuVMQURL E܋EP}WURMQ V}WURMQE$Y_P} WURMQV}WI VuVURMQuVEPURuVMQURUR u e[^_]Ít&PxU2QWURVZYPEPV8e[^_]UWVSU}[7W}RuVEPMEQWURuVƋEPMQL }EWURMQ}W VURMQEPZ} WURMQV}WF VuVURMQuVEPURuVMQURUR ue[^_]ÍxPM1WREPVZYPMQV8e[^_]É'UWVSMU[ô7QR}WuVMUEQR}WuV~MQUR E܋EP}WURMQ V}WURMQE$Y_P} WURMQV}W VuVURMQluVEPURuVMQURUR u e[^_]Ít&Px}7QREPVZYP}WV8e[^_]UWVSU}[Ô7W}RuVEPsMEQWURuV^ƋEPMQ }EWURMQ}W VURMQEPZ} WURMQV}W VuVURMQQuVEPURuVlMQURUR ue[^_]ÍxPM1WREPVZYPMQV8e[^_]É'UWVSMU[t7QR}WuVVMUEQR}WuV>MQURE܋EP}WURMQt V}WURMQ_E$Y_P} WURMQV}W VuVURMQ,uVEPURuVGMQURURu e[^_]Ít&Px}7QREPVZYP}WV8e[^_]UWVSU}[T7W}RuVEP3MEQWURuVƋEPMQ}EWURMQ}WT VURMQEP?Z} WURMQV}W VuVURMQuVEPURuV,MQURURue[^_]ÍxPM1WREPVZYPMQV8e[^_]É'UWVSMU[47QR}WuVMUEQR}WuVMQURE܋EP}WURMQ4 V}WURMQE$Y_P} WURMQV}WC VuVURMQuVEPURuVMQURURCu e[^_]Ít&Px}7QREPVZYP}WV8e[^_]UWVSU}[7W}RuVEPMEQWURuVƋEPMQ}EWURMQ}W VURMQEPZ} WURMQV}WX VuVURMQuVEPURuVMQURURHue[^_]ÍxPM1WREPVZYPMQV8e[^_]É'UWVSMU[7QR}WuVMUEQR}WuVMQURLE܋EP}WURMQ V}WURMQE$Y_P} WURMQV}WS VuVURMQuVEPURuVMQURURu e[^_]Ít&Px}7QREPVZYP}WV8e[^_]UWVSU}[7W}RuVEPMEQWURuVƋEPMQL}EWURMQ}W VURMQEPZ} WURMQV}W VuVURMQuVEPURuVMQURURue[^_]ÍxPM1WREPVZYPMQV8e[^_]É'UWVS[ú7MQUR>E_XEPuVL} XZW}MQURVWEP螨 VURuV[ MQURURuEe[^_]Ðt&Px}7QREPVZYP}WV8Ee[^_]Í'UWVSU}[7RWuVEPUMEQR}WuVƋEPMQ<UER}WMQUR V}WMQEPXZU4R}0WM,QU(RE$P} WURMQV}Ws 0VuVURMQuVEPURuVMQURURu e[^_]Ít&PxM1RWEPVZYPMQV8e[^_]UWVSU}[ô7W}RMQuVUEWRMQuV~}WUR,MEQ}WEPUR V}WMQURX}0WM,QU(RE$P} WURMQV}Wث 0VuVURMQauVEPURuV|MQURURue[^_]ÍxPM1WREPVZYPMQV8e[^_]É'UWVSU}[Ä7RWEPuVfMUEQR}WuVNMQUR}EWMQUREP V}WMQURoX}0WM,QU(R}$WE PMQURV}W8 0VMQURuV1MQUREPuVLMQURURue[^_]ÍPx}7QR}WVY_PMQV8e[^_]É'UWVSM}[T7W}QURuV3MEWQURuV}WMQUEREP}WURT VMQ}WUR?XM0Q},WE(PU$R} WMQURV}W踱 0VMQURuVEPMQURuVMQURURxue[^_]ÍxVU2WQURV_ZPEPV8e[^_]É'UWVSU}[$7RWuVEPMUEQR}WuV ƋEPMQUR}W4VMQUR}W"VMQ=VNjuVEPUR uVMQURURue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[4 7RWuVEPMUEQR}WuV ƋEPMQUR}WDVMQUR}W2VMQHVNjuVEPUR uVMQURUR)ue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[D 7RWuVEP&MUEQR}WuV ƋEPMQUR}WTVMQUR}WBVMQEUVNjuVEPUR! uVMQURUR9ue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[T 7RWuVEP6MUEQR}WuV ƋEPMQUR}WdVMQUR}WRVMQSVNjuVEPUR1 uVMQURURIue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[d 7RWuVEPFMUEQR}WuV. ƋEPMQUR}WtVMQUR}WbVMQ%UVNjuVEPURA uVMQURURYue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[t 7RWuVEPVMUEQR}WuV> ƋEPMQUR}WVMQUR}WrVMQTVNjuVEPURQ uVMQURURiue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVS,U}[Ä7W}RuVMQcUEWR}uVMQK U܉EWRuVMQ3}؉WEPUR}W| MQUR}WMQdVUR}WEPR MQVUR}W]VVNjMQuVUR) MQuVEPURuVMQURUR, ue[^_]ÍxPM1WREPVZYPMQV8e[^_]Í'UWVS,M}[D7W}QuVUR#MEWQ}܋uVEP UERWMQuVƋEPUR}WMQ< UR}WMQUR$V}WEPMQVUR}WRVNjMQuVUR MQuVEPURuVMQURUR u e[^_]ÐxPU2WQEPVZYPURV8e[^_]UWVSU}[7RWuVEPMUEQR}WuV ƋEPMQUR}W$VMQUR}WVMQ]VNjuVEPUR uVMQURUR ue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[$7RWuVEPMUEQR}WuV ƋEPMQUR}W4VMQUR}W"VMQ[VNjuVEPUR uVMQURURue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[47RWuVEPMUEQR}WuV ƋEPMQUR}WDVMQUR}W2VMQe]VNjuVEPUR uVMQURUR)ue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[D7RWuVEP&MUEQR}WuV ƋEPMQUR}WTVMQUR}WBVMQE\VNjuVEPUR! uVMQURUR9ue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[T7RWuVEP6MUEQR}WuV ƋEPMQUR}WdVMQUR}WRVMQUdVNjuVEPUR1 uVMQURURIue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVS U}[d7RWMQuVFPEPUR}WVfVNjMQURUR ue[^_]ÍPxM1RWEPVZYPMQV8e[^_]UWVSU}[7RWuVEPMUEQR}WuV ƋEPMQUR}WVMQUR}WVMQ%ZVNjuVEPUR uVMQURURue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVS U}[6RWMQuVPEPUR}WVaVNjMQURUR ue[^_]ÍPxM1RWEPVZYPMQV8e[^_]UWVSMU[46QR}WuVMXZQEPEXZUR}WVNjMQUREP?U(RM$QU RMQVWURv EVuVMQUR WEP}WX uVMQURDUu e[^_]ÉPxU2QWURVZYPEPV8e[^_]UWVSMU[46QR}WuVMXZQEPEXZUR}WVNjMQUREP?U(RM$QU RMQVWURb EVuVMQUR WEP}Wx uVMQURdUu e[^_]ÉPxU2QWURVZYPEPV8e[^_]UWVS ƋEPMQUR}WVMQUR}WrVMQ赆VNjuVEPURQ uVMQURURiue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[Ä6RWuVEPfMUEQR}WuVN ƋEPMQUR}WVMQUR}WVMQeVNjuVEPURa uVMQURURyue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[Ô6RWuVEPvMUEQR}WuV^ ƋEPMQUR}WVMQUR}WVMQVNjuVEPURq uVMQURURue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[ä6RWuVEPMUEQR}WuVn ƋEPMQUR}WVMQUR}WVMQEVNjuVEPUR uVMQURURue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[ô6RWuVEPMUEQR}WuV~ ƋEPMQUR}WVMQUR}WVMQVNjuVEPUR uVMQURURue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[6RWuVEPMUEQR}WuV ƋEPMQUR}WVMQUR}WVMQuVNjuVEPUR uVMQURURue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[6RWuVEPMUEQR}WuV ƋEPMQUR}WVMQUR}WVMQ8VNjuVEPUR uVMQURURue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[6RWuVEPMUEQR}WuV ƋEPMQUR}WVMQUR}WVMQVNjuVEPUR uVMQURURue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSM}[6W}QuVURMEWQuVUR }WEPMQURV}WMQUR}WVMQa&VNjuVEPUR uVMQURURue[^_]ÍxPU2WQEPVZYPURV8e[^_]UWVSU}[6RWEPuVMUEQR}WuVMQUR|E_XEP}WjM܉QURMQUR VMQUREPM$XZQU RWEPVMQU EVURuVMQURuVMQURWEP}W' uVMQURUu e[^_]ÐPxM1RWEPVZYPMQV8e[^_]UWVSU}[6RWM}QuVUERWMQuVUREP<}EXZWMQ*U܉REPURMQ VURMQURYXE$PM QWURVMQu EVURuVMQmURuVEPMQWUR}W uVMQUREu e[^_]ÐVx}7RQ}WV_ZPEPV8e[^_]UWVSU}[Ä6RWM}QuVcUERWMQuVNUR}WE܋EY_PMQUER}WMQURr V}WMQEP]XZU4R}0WM,QU(R}$WM QUREPV}W0VuVURMQuVURMQuV5EPURuV MQURUR}ue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Í&'UWVSM}[$6QWEPuVUMERQ}WuVURMQE_}XWEPUER}WMQUR V}WMQEPZU4YR}0WM,QU(R}$WM QUREPV}W1 0VMQURuVMQURuVMQUREPuV1 MQURURue[^_]ÍPxU2QWURVY_PEPV8e[^_]Í&'UWVS@}E [6u]E(]W}VMQURWM}QEP UERWVuVqMXZQEPEXZUR}WMĉEQUR}WEP VMQUR}W VMQUR}LWMHQUDRE@PM ƋEPMQUR}W脴VMQUR}WrVMQVNjuVEPURQ uVMQURURiue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[Ä6RWuVEPfMUEQR}WuVN ƋEPMQUR}W蔳VMQUR}W肳VMQVNjuVEPURa uVMQURURyue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[Ô6RWuVEPvMUEQR}WuV^ ƋEPMQUR}W褲VMQUR}W蒲VMQVNjuVEPURq uVMQURUR艳ue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[ä6RWuVEP膰MUEQR}WuVn ƋEPMQUR}W贱VMQUR}W袱VMQUVNjuVEPUR聳 uVMQURUR虲ue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[ô6RWuVEP薯MUEQR}WuV~ ƋEPMQUR}WİVMQUR}W貰VMQVNjuVEPUR葲 uVMQURUR話ue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[6RWuVEP覮MUEQR}WuV莮 ƋEPMQUR}WԯVMQUR}W¯VMQVNjuVEPUR衱 uVMQURUR蹰ue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[6RWuVEP趭MUEQR}WuV螭 ƋEPMQUR}WVMQUR}WҮVMQVNjuVEPUR豰 uVMQURURɯue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVS U}[6WREPuVƬMXZQ}WuVNjURMQURVWw&EVEPURuVWMQURQUue[^_]ÍxPM1WREPVZYPMQV8e[^_] UWVS U}[6WREPuVMXZQ}W襼VNjURMQUR1VWEVEPURuVWMQUR聼Uue[^_]ÍxPM1WREPVZYPMQV8e[^_] UWVSMU[D6QR}WuV&MUEQR}WuV MQUR}WEPTVMQUR}WBM QUR}WVMQ VuVEPURuVMQURUR- ue[^_]ÍPxU2QWURVZYPEPV8e[^_]Í&'UWVSMU[D6QR}WuV&MUEQR}WuV MQUR}WEPTVMQUR}WBM QUR}WVMQ VuVEPURuVMQURUR- ue[^_]ÍPx}7QREPVZYP}WV8e[^_]Í&'UWVSMU[D6QR}WuV&MUEQR}WuV MQUR}WEPTVMQUR}WBM QUR}WVMQy^ VuVEPURuVMQURUR- ue[^_]ÍPx}7QREPVZYP}WV8e[^_]Í&'UWVSMU[D6QR}WuV&MUEQR}WuV MQUR}WEPTVMQUR}WBM QUR}WVMQW VuVEPURuVMQURUR- ue[^_]ÍPx}7QREPVZYP}WV8e[^_]Í&'UWVSMU[D6QR}WuV&MUEQR}WuV MQUR}WEPTVMQUR}WBM QUR}WVMQ9O VuVEPURuVMQURUR- ue[^_]ÍPx}7QREPVZYP}WV8e[^_]Í&'UWVSMU[D6QR}WuV&MUEQR}WuV MQUR}WEPTVMQUR}WBM QUR}WVMQM VuVEPURuVMQURUR- ue[^_]ÍPx}7QREPVZYP}WV8e[^_]Í&'UWVSM}[D6W}QEPuV#UERWMQuV UR}WMQURTV}WEPMQBU(R}$WM QUR}WVMQ VuVEPUR uVMQURUR% ue[^_]ÍxPU2WQEPVZYPURV8e[^_]UWVSU}[D6W}RMQuV#UEWRMQuV }WURMQ}WTVEPUR}WBM(QU$R} WMQURV}W VEPMQUR uVMQURUR% ue[^_]ÍxVU2WQURV_ZPEPV8e[^_]UWVS,UM[D6RQuV}W&UMERQuV}W UM܉ERQuV}WU؉REPMQ}W< URMQ}WUR$VMQ}WEPVUR}W>VNjuVMQUR uVMQUREPեuVMQURUR u e[^_]ÐPx}7RQ}WVY_PEPV8e[^_]UWVSU}[6RWuVEPMUEQR}WuVޡ ƋEPMQUR}W$VMQUR}WVMQVNjuVEPUR uVMQURUR ue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVS,M}[$6W}QuVURMEWQ}܋uVEP UERWMQuVӠƋEPUR}WMQ UR}WMQURV}WEPMQVUR}WVNjMQuVURͣ MQuVEPUR赣uVMQURURТ u e[^_]ÐxPU2WQEPVZYPURV8e[^_]UWVSU}[6RWuVEP֟MUEQR}WuV辟 ƋEPMQUR}WVMQUR}WVMQVNjuVEPURѢ uVMQURURue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[6RWuVEPMUEQR}WuVΞ ƋEPMQUR}WVMQUR}WVMQſVNjuVEPUR uVMQURURue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[6RWuVEPMUEQR}WuVޝ ƋEPMQUR}W$VMQUR}WVMQVNjuVEPUR uVMQURUR ue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[$6RWuVEPMUEQR}WuV ƋEPMQUR}W4VMQUR}W"VMQEVNjuVEPUR uVMQURURue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[46RWuVEPMUEQR}WuV ƋEPMQUR}WDVMQUR}W2VMQ腾VNjuVEPUR uVMQURUR)ue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[D6RWuVEP&MUEQR}WuV ƋEPMQUR}WTVMQUR}WBVMQVNjuVEPUR! uVMQURUR9ue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[T6RWuVEP6MUEQR}WuV ƋEPMQUR}WdVMQUR}WRVMQվVNjuVEPUR1 uVMQURURIue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVS,U}[d6W}RuVMQCUEWR}uVMQ+ U܉EWRuVMQ}؉WEPUR}W\ MQUR}WMQDVUR}WEP2 MQVUR}W8VNjMQuVUR MQuVEPURuVMQURUR ue[^_]ÍxPM1WREPVZYPMQV8e[^_]Í'UWVSM}[$6W}QuVURMEWQuVUR }WEPMQUR4V}WMQUR"}WVMQVNjuVEPUR uVMQURURue[^_]ÍxPU2WQEPVZYPURV8e[^_]UWVS,M}[46W}QuVURMEWQ}܋uVEP UERWMQuVƋEPUR}WMQ, UR}WMQURV}WEPMQVUR}WVNjMQuVURݙ MQuVEPURřuVMQURUR u e[^_]ÐxPU2WQEPVZYPURV8e[^_]UWVSU}[6RWuVEPMUEQR}WuVΕ ƋEPMQUR}WVMQUR}WVMQVNjuVEPUR uVMQURURue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[6RWuVEPMUEQR}WuVޔ ƋEPMQUR}W$VMQUR}WVMQ5:VNjuVEPUR uVMQURUR ue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVS U}[$6RWMQuVPEPUR}WRVVNjMQURURe ue[^_]ÍPxM1RWEPVZYPMQV8e[^_]UWVS,M}[Ä6W}QuVURcMEWQ}܋uVEPK UERWMQuV3ƋEPUR}WMQ| UR}WMQURdV}WEPMQRVUR}WA?VNjMQuVUR- MQuVEPURuVMQURUR0 u e[^_]ÐxPU2WQURVZYPEPV8e[^_]UWVSU}[T6RWuVEP6MUEQR}WuV ƋEPMQUR}WdVMQUR}WRVMQEVNjuVEPUR1 uVMQURURIue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSUE [a6}u]E(]E0]E8]E@]EH]EP]EX]RVWGMUQREPWU܃ MEQRVWEXZURW!P$؋؃ĞE@]؃Ȟ}fMڃ ؋̞fMmە|mۅ|E@PPURW,E^uXVWEEPMQURW訑u VMQURW蓑u VMQW蒔Ud RE`PMQuVURMQuVURMQuVEPURuVMQURuVMQUR|VEPMQURuVD`MQURMQWUREPMQW"URMQW聠U RMQWpze[^_]؃$V ؃Ȟ}fMڵ fM؋̞mە|mۅ|E@PPPURWğtEY^PW貟MxQuVURW:M QuVURW%M QuVW$Ed PU`RuVMQURuVMQURuVEPMQURuVMQURuVMQUR|PxVtRMQuVC`URMQURW萑EPURMQW讐xRMQW t RMQWVx7QRWVZYPWV8e[^_]Ít&Vx7QRWVZYPWV8UWVSME [Á6}u]E(]E0]E8]E@]EH]EP]EX]QVWgM܍URQURW%Uԃ M؉EQRVWE^XEPWAP$؋؃ĞE@a؃Ȟ}fUҋuh ؋̞fUmەtmXZVWݝXAۅt݅XpE@PPMQW0|EY^PWUxRuVMQW覍U RuVMQW葍U RuVW萐pXZQEPEEPMQuVWQURMQuVWoxRMQW˜| VEPW跜p RuhVW胛Vx7RQWVZYPWV8e[^_]؃$RUh؃Ȟ}fMҵ fM؋̞mەtmRWݝXԚۅt݅XpE@dPPMQWÛ|^uXVW豛xEPURuVW9M QURuVW$M QEPW#ZpYRuVƅtxURMQURWMQUREPWxQURW`| QURWLp QUhRWe[^_]Ðt&PudVM`QURuVMQUREPMQuVURMQuVURMQuVEPURMQtVxR|QuVEP>`Vx7RQWVZYPWV8x&UWVSUE [q6}u]E(]E0]E8]E@]EH]EP]EX]RVWWMUQREPWU܃ MEQRVWEXZURW1P$؋؃ĞE@]؃Ȟ}fMڃ ؋̞fMmە|mۅ|E@PPURW<E^uXVW-EEPMQURW踉u VMQURW裉u VMQW袌Ud RE`PMQuVURMQuVURMQuVEPURuVMQURuVMQUR|VEPMQURuV `MQURMQWUREPMQW2URMQW葘U RMQW耘ze[^_]؃$N ؃Ȟ}fMڵ fM؋̞mە|mۅ|E@PPPURWԗtEY^PW—MxQuVURWJM QuVURW5M QuVW4Ed PU`RuVMQURuVMQURuVEPMQURuVMQURuVMQUR|PxVtRMQuV `URMQURW蠉EPURMQW辈xRMQWt RMQWVx7QRWVZYPWV8e[^_]Ít&Vx7QRWVZYPWV8UWVSME [Ñ6}u]E(]E0]E8]E@]EH]EP]EX]QVWwM܍URQURW5Uԃ M؉EQRVW E^XEPWQP$؋؃ĞE@a؃Ȟ}fUҋuh ؋̞fUmەtmXZVWݝXqۅt݅XpE@PPMQW@|EY^PW.UxRuVMQW超U RuVMQW衅U RuVW蠈pXZQEPEPMQuVWaURMQuVWxRMQW۔| VEPWǔp RuhVW賔Vx7RQWVZYPWV8e[^_]؃$JUh؃Ȟ}fMҵ fM؋̞mەtmRWݝXۅt݅XpE@dPPMQWӓ|^uXVWxEPURuVWIM QURuVW4M QEPW3ZpYRuVqƅtxURMQURWMQUREPWxQURWp| QURW\p QUhRWHe[^_]Ðt&PudVM`QURuVMQUREPMQuVURMQuVURMQuVEPURMQtVxR|QuVEP`Vx7RQWVZYPWV8x&UWVS0u}EV[|6URWM EQEPWEXZU(RWE^1XE8PWϐ;uE},t&}$wE$PE}4v?ERuVU8RWɐM̃ Qu(VW踐U؃ue[^_]ÍE4lፓVx7RQWVZYPWV8e[^_]QVEPW/M؃EvẼ EȉM MM@QMU~ ƋEPMQUR}WVMQUR}WrVMQVNjuVEPURQ uVMQURURiue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[Ä6RWuVEPf}MUEQR}WuVN} ƋEPMQUR}W~VMQUR}W~VMQՒVNjuVEPURa uVMQURURyue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[Ô6RWuVEPv|MUEQR}WuV^| ƋEPMQUR}W}VMQUR}W}VMQVNjuVEPURq uVMQURUR~ue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[ä6RWuVEP{MUEQR}WuVn{ ƋEPMQUR}W|VMQUR}W|VMQ蕑VNjuVEPUR~ uVMQURUR}ue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[ô6RWuVEPzMUEQR}WuV~z ƋEPMQUR}W{VMQUR}W{VMQőVNjuVEPUR} uVMQURUR|ue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSU}[ě6RWuVEPyMUEQR}WuVy ƋEPMQUR}WzVMQUR}WzVMQuVNjuVEPUR| uVMQURUR{ue[^_]ÍPxM1RWEPVZYPMQV8e[^_]Ív'UWVSUu[Ԛ6VuRMQ}WxUVRNjMQuVxUREP-MEXZQUREWEPURMQy VURMQURy M(QU$RE PMQURVW  EVuVMQUR{{W}WuVMQzUREP}WՇ uVMQURUue[^_]ÍPx}7RQEPVZYP}WV8e[^_] UWVSU}[Ä6RWEPuVfwMUEQR}WuVNwMQURE_XEP}WM܉QURMQURsx VMQUREP^xM$XZQU RWEPVMQ EVURuVMQ-zURuVMQURHyWEP}W觇 uVMQUR蓇Uu e[^_]ÐPxM1RWEPVZYPMQV8e[^_]UWVSMu[D6VuQUR}W#vMVQNjURuVvMQEP蝅EXZUREP苅EWURMQUR6w VMQURMQ!w U(RE$PU RMQURVWc EVuVMQURxW}WuVEP xMQUR}WE uVMQUR1Eue[^_]ÍVx}7QR}WV_ZPEPV8e[^_] UWVSU}[6RWEPuVtMUEQR}WuVtMQURlE_XEP}WZM܉QURMQURu VMQUREPuM$XZQU RWEPVMQ EVURuVMQwURuVMQURvWEP}W uVMQURUu e[^_]ÐPxM1RWEPVZYPMQV8e[^_]UWVSMu[ô6VuQUR}WsMVQNjURuVsMQEP EXZUREPEWURMQURt VMQURMQt U(RE$PU RMQURVW EVuVMQUR[vW}WuVEPyuMQUR}W赂 uVMQUR衂Eue[^_]ÍVx}7QR}WV_ZPEPV8e[^_] UWVSU}[d6RWEPuVFrMUEQR}WuV.rMQUR܂E_XEP}WʂM܉QURMQURSs VMQUREP>sM$XZQU RWEPVMQ EVURuVMQ uURuVMQUR(tWEP}W臂 uVMQURsUu e[^_]ÐPxM1RWEPVZYPMQV8e[^_]UWVS$[*6}WuV讀ME_XQEP蜀EXZUR}W誁^NjuXVEP虁M(QU$RUM QPWRMQUR{,EVMQUR褁 WuVEP蓁 MQUR}W? uVMQUR+EuEe[^_]ÍxVU2WQEPV_ZPURV8Ee[^_]Í'UWVSM}[6QWEPuVoUMERQ}WuVoURMQ|E_XEP}WjU܉RMQURMQp VURMQURpWEPVMQO EVURuVMQrURuVMQURqWEP}W1 uVMQURUue[^_]ÍPxU2QWEPVZYPURV8e[^_]Ít&'UWVSMU[Đ6QR}WuVnMXZQEPUEZUYR}WCVNjMQURMQoWURVqEVuVEPMQpWUR}W; uVMQUR'Eu e[^_]Ðt&Vx}7QR}WV_ZPEPV8e[^_]UWVSM}[ԏ6QWEPuVmUMERQ}WuVmURMQL~E_XEP}W:~U܉RMQURMQn VURMQURnWEPVMQ) EVURuVMQpURuVMQURoWEP}W~ uVMQUR}Uue[^_]ÍPxU2QWEPVZYPURV8e[^_]Ít&'UWVSMU[Ô6QR}WuVvlMXZQEP%}EZUYR}W}VNjMQURMQmWURVqEVuVEPMQnWUR}W } uVMQUR|Eu e[^_]Ðt&Vx}7QR}WV_ZPEPV8e[^_]UWVSU}[ä6RWM}QuVkUERWMQuVnk UR}WEPMQlVUR}WMQl UR}WVMQ=VNjuVEPURyn uVMQURURmu e[^_]ÉPxM1RWMQVZYPEPV8e[^_]UWVSU}[ô6RWM}QuVjUERWMQuV~j UR}WEPMQkVUR}WMQk UR}WVMQ}VNjuVEPURm uVMQURURlu e[^_]ÉPxM1RWEPVZYPMQV8e[^_]UWVS,M}[ċ6W}QuVURiMEWQ}܋uVEPi UERWMQuVsiƋEPUR}WMQj UR}WMQURjV}WEPMQjVUR}W7VNjMQuVURml MQuVEPURUluVMQURURpk u e[^_]ÐxPU2WQEPVZYPURV8e[^_]UWVS,M}[Ô6W}QuVURshMEWQ}܋uVEP[h UERWMQuVChƋEPUR}WMQi UR}WMQURtiV}WEPMQbiVUR}W!\VNjMQuVUR=k MQuVEPUR%kuVMQURUR@j u e[^_]ÐxPU2WQEPVZYPURV8e[^_]UWVS,U}[d6RWM}QuVCgUERWMQuV.gUR}WvMEXZQEPvEXZU R}WvM܉QUREPURAh VMQURMQ,h<$URMQVUR觹 EVEPuVMQiURuVMQURiWu VMQYv UREP}WEv uVMQUR1vUue[^_]ÍPxM1RWEPVZYPMQV8e[^_] UWVSU}[6WREPuVeMXZQ}WeuEXZUREPSu}EXZWMQAuVNjUREPURf WMQURV EVuVMQURgWuVMQ5u UREP}W!u uVMQUR uUue[^_]ÍxPM1WREPVZYPMQV8e[^_]Ít&'UWVS,U}[Ԇ6RWM}QuVdUERWMQuVdUR}WLuMEXZQEP:uEXZU R}W(uM܉QUREPURe VMQURMQe<$URMQVURW EVEPuVMQogURuVMQURfWu VMQt UREP}Wt uVMQURtUue[^_]ÍPxM1RWMQVZYPEPV8e[^_] UWVSU}[d6WREPuVFcMXZQ}WsEXZUREPs}EXZWMQsVNjUREPUR]d WMQURVEVuVMQURfeWuVMQs UREP}Ws uVMQURsUue[^_]ÍxPM1WREPVZYPMQV8e[^_]Ít&'UWVSM}[D6QWEPuV&bUMERQ}WuVbURMQqE_XEP}WqU܉RMQURMQ3c VURMQURcWEPVMQa EVURuVMQdURuVMQURdWEP}WQq uVMQUR=qUue[^_]ÍPxU2QWEPVZYPURV8e[^_]Ít&'UWVSMU[6QR}WuV`MXZQEPupEZUYR}WcpVNjMQURMQbWURVqEVuVEPMQcWUR}W[p uVMQURGpEu e[^_]Ðt&Vx}7QR}WV_ZPEPV8e[^_]UWVSM}[6QWEPuV_UMERQ}WuV_URMQpE_XEP}WzpU܉RMQURMQa VURMQUR`WEPVMQ EVURuVMQbURuVMQURaWEP}WAp uVMQUR-pUue[^_]ÍPxU2QWEPVZYPURV8e[^_]Ít&'UWVSMU[Ԁ6QR}WuV^MXZQEPeoEZUYR}WSoVNjMQURMQ_WURVAEVuVEPMQ`WUR}WKo uVMQUR7oEu e[^_]Ðt&Vx}7QR}WV_ZPEPV8e[^_]UWVSM}[6QWEPuV]UMERQ}WuV]URMQQUR}WaMEXZQEPaEXZU R}WaM܉QUREPURQR VMQURMQQREPWZYPuVW8e[^_]ÍUWVSPME[a6u]E ]E(]E0]QV}WCMURQ}W}܋UR? MEQWVEPu? U؉RM8QuVFEEPURuVMQ@ WURuVMQ@ URuVEPH WMQUR}CuQREPWZYPuVW8e[^_]ÍUWVSP}E [D`6u]E(]W}VMQKBURWM}QUR> M܉EWQVu؋EP= EVUHR}W*EMXZQEP)NEXZURMQNuĉEVEPURuV> MQURuVMQ> WURuV&G EPMQURAuLVWMDQU@RuUă RMQuVW=U RMPQWKu VEPWKU RMQWKVx7uRQWuVZYPW}W8e[^_]ËULRMQUDRM@QUURWM}QUR9 M܉EWQVu؋EP9 EVUHR}W@MXZQEPIEXZURMQIuĉEVEPURuV: MQURuVMQ: WURuVB EPMQUR=uLVWMDQU@Ruۅp݅XlE@%VVURWy?MxXZQWg?utVEPURW/u VMQURW/| VMQW8U RuVW2lXZQEPz R|QURWF;uVMQURWt1u VMQURW0t PMQW>x VURW>l QuhVW=Vx7RQWVZYPWV8e[^_]؃$uh؃Ȟ}fUζ fU؋̞mەpmVWݝX<ۅp݅XlE@VVURW=MxXZQW=utVEPURWY.u VMQURWD.| VMQW6U RuVW/1lXZQEPƅR|RMQW9URMQURW/M QUREPW.t RMQWS=x RMQW?=l RMhQW <`e[^_]RUdR|VMQURuVMQURuVEPMQURuVMQURuVMQUREPpQtVxRMQuV`Qx7RQWVYZPWV8MUWVSUE [aM6}u]E(]E0]E8]E@]EH]EP]EX]RVWG/UMQRMQW+M܃ UERQVW*U؃ ERE`PW,2EXZURW GP$؋؃ĞE@؃Ȟ}fMփ ؋̞fMmەxmۅxE@VVEPW;MEXZQW ;U|RuVEPW+M QURuVW|+M QURW3u VMQWj.Ud REPMQuVURMQuVURMQuVEPURuVMQURuVMQURxV|PMQURuVp\MQURW6MQUREPW,M QURMQW+| RMQWB:U RMQW1:e[^_]؃$m ؃Ȟ}fUֶ fU؋̞mەxmۅxE@qVVEPW9pXZURWs9utVMQURW)E PMQuVW)U RMQW%2u VURW,Md QuVEPURuVMQURuVMQURuVEPMQURuVMQURuVxQtRpPMQuV׻\URMQW5URMQURW2+E PMQURWM*t QURW8p QMQW8dVx7QRWVZYPWV8e[^_]ÍvVx7QRWVZYPWV8kUWVSME [!I6}u]E(]E0]E8]E@]EH]EP]EX]QVW+M܍URQURW&Uԃ M؉EQRVW&MЃ EQU`RW-|^XEPWBP$؋؃ĞE@؃Ȟ}fU΋uh ؋̞fUmەpmXZVWݝX6ۅp݅XlE@%VVURW6MxXZQW6utVEPURW/'u VMQURW'| VMQWV/U RuVW*lXZQEPC R|QURW2uVMQURW(u VMQURW't PMQW+6x VURW6l QuhVW6Vx7RQWVZYPWV8e[^_]؃$(uh؃Ȟ}fUζ fU؋̞mەpmVWݝXT5ۅp݅XlE@VVURW#5MxXZQW5utVEPURW%u VMQURW%| VMQW-U RuVWo(lXZQEP}ƅR|RMQW0URMQURW'M QUREPW7&t RMQW4x RMQW4l RMhQWk4`e[^_]RUdR|VMQURuVMQURuVEPMQURuVMQURuVMQUREPpQtVxRMQuVʶ`Qx7RQWVYZPWV8MUWVS6}]E ]W}QURuVMEWQURuV }̉WMQUR}WVEPMQUR}WMQUR}WVEPy VMQUREPuVMQURUR ue[^_]ÍxPU2WQURVY_PEPV8e[^_]Í&'UWVS$[ú=6}EEE7QWVRNRPWjjjM QPVWRpU( ҉EMPQu(VW,U҉EPPEPW*}E}jU$Ru VMQURUuVPRϑ ƃ}q}MPMQURW*M(XQUREPURWGj{tEe[^_]Ðt&utPPURW)EM(t}}uVM$QU REPuVuURMQVU( ƅt}}UtPUREPW) M(QURMQURWEFM( / URVx7RQWVZYPWV8Ee[^_]ÐPEPuVW+E*MQURMQWn,ORURMQWq-뾋MQUREPW].͍t&'UWVSXEZPWEXEZPWE u IttRQMQuV! URMQWU RMQWpue[^_]ÍVx7QRWVZYPWV8e[^_]Ív'UWVS[G$6uU>R^ZhYPRPHZYPVe[^_]Ív'US [#6URURp]Ít&'US [ü#6URUR!]Ít&'US [Ì#6URURP]ÐUWVS[Z#6uE >PVW|URǍƟRPVx URОRWVxURٞRWVx URߞRWVxURRWVx URRWVxRRWVx EPQWVxURRWVx  RRWVxe[^_]Ít&'UWVS8[!6EEEEEEEuj@@Y NjRM QV RM QV GRM QV GRM QV G RM QV LGQU RV G(QU RV G$QU RV MQU RV|Me[^_]Í&'UWVS[Ú 6E}u UMM}AEEH(MwM`t&Vx7RQWVZYPWV8U REPEx WpVHQURPt&+2Mu p(u}x$e[^_]PjRWxp PURUz WrVJQEPRPjRWxPjRWxPjRWxPjRWx}w$uUWVS [j6U}u Jw |⍋Vx7QRWVZYPWV8}XWWe[^_]Ë6j)QuVW|}XWVe[^_]Ë)jQMQW|}XWVe[^_]Ë뤍&'UWVS [Ú6U}u Jw ⍋Vx7QRWVZYPWV8^EP7Ve[^_]Ë6j)QuVW|^EPVe[^_]Ë)jQMQW|^EPUe[^_]Ë뤍&'USP[6PEQM QP|M1]Ít&'UWVS[Ê6}jM QWx11҉ƊP$1ҊHQ1ɊPRQUR7 jVE PW|e[^_]Ív'UWVS[6EU 0RPV|ٞ}EURV}WURx u}썓ߞWRuV}WxM"E1PR}WMQx U}썳. WVUR}W9xUuRWHuVURx M E1PWURMQx}u썓WVR}WuVix} U WVeUR}WxME9PVURMQx uU썻x4RWuVURx}u썓tVREP}Wxu 8>VREPMQxU$ VWuVURx} (WVURuVxU ,WVUREPx0e[^_]ÍUWVS[ê6uj\!R$0RM EERM QV RM QV GRM QV GRM QV  G RM QV GRM QV GRM QV GRM QV4 GRM QV G RM QV8 G$RM QV$ G(RM QV|(M RM QV|,M ARM QV|0M ARM QV|UBY XQVUBYJXQVUB YJXQVUBYJXQVGXMGDGHGLAGPGjM PVx } MG A,j} OQVx M }A$G0>jM APVx M }A(G4>jM APVxM A,MA8E @ u"@$@(@,uQe[^_]Ë} wVO VPU J QuQuU ~,B$uxE @(u?E @,t} wVN VPU J,Q,Qu~8e[^_]Ë} wVN VPU J(QPu~4둋U r VYN VPE H$QPu~0U&UWVS,[Ú6U} uJ,UMJ0UMJ4UMJ8UMJDUMJHUM܋JLUM؋JPUMԋJTMЋUJXMjW,ROQV|jW(ROQV| jW$ROQV|jW RQV|E ;G t PME;G$t PME;G(t PME;G,t P}Mut MQgMU܅t EPQMu؅t MQ;MUԅt uV%MMЅt URMu̅t MQL WL^}WLe[^_]Ít&'UWVS,[6U} uJ,UMJ0UMJ4UMJ8UMJDUMJHUM܋JLUM؋JPUMԋJTMЋUJXMjW,ROQV|jW(ROQV| jW$ROQV|jW RQV|E ;G t PKE;G$t PKE;G(t PKE;G,t PKut MQKU܅t EPKu؅t MQ{KUԅt uVeKMЅt UROKu̅t MQ9K W-K_}W#Ke[^_]Ít&'U EjM QPxÍUPPEjMQM QP|ÍvUPPEjMQM QP|ÍvU EjM QPxÍUPPEjMQM QP|ÍvUPPEjMQM QP|ÍvU EjM QPxÍUPPEjMQM QP|ÍvUPPEjMQM QP|ÍvU EjM QPÍUPPEjMQM QPÍvUPPEjMQM QPÍvU EjM QPxÍUPPEjMQM QP|ÍvUPPEjMQM QP|ÍvU EjM QPxÍUPPEjMQM QP|ÍvUPPEjMQM QP|ÍvUWV1S[6}U RWM$9HU;2E| Ee[^_]ÐQVM QWMMF;1|Ee[^_]Ðt&UWV1S[[6}U RWM$GU;2E| Ee[^_]ÐQVM QWMMF;1|Ee[^_]Ðt&UWV1S[6URMG;u| e[^_]PMjU QMQxF;u|ۍe[^_]Ív'UVPPM1jUE PQxuÍ'UWVS [:6u}Nx!jU QU QW|Ny MQFXU RFe[^_]ÍvUWVS [6u}Nx!jU QU QW|Ny E PFe[^_]Í'UVPM1jURUE PQ|uÍv'UWVS [J6u}Nx!jU QU QW|Ny MQEXU REe[^_]ÍvUVPM1jURUE PQ|uÍv'UWV1S[û 6UR-E;u| e[^_]PMjU QMQxF;u|ۍe[^_]Ív'UVPPM1jUE PQxuÍ'UWVS [ 6u}Nx!jU QU QW|Ny MQDXU RDe[^_]ÍvUWVS [ú 6u}Nx!jU QU QW|Ny E PeDe[^_]Í'UVPM1jURUE PQ|uÍv'UWVS [* 6u}Nx!jU QU QW|Ny MQCXU RCe[^_]ÍvUVPM1jURUE PQ|uÍv'UWV1S[Û 6UR C;u| e[^_]PMjU QMQxF;u|ۍe[^_]Ív'UVPPM1jUE PQxuÍ'UWVS [ 6u}Nx!jU QU QW|Ny MQBXU RBe[^_]ÍvUVPM1jURUE PQ|uÍv'UWVS [j 6u}Nx!jU QU QW|Ny MQBXU R Be[^_]ÍvUVPM1jURUE PQ|uÍv'UWV1S[ 6URMA;u| e[^_]PMjU QMQxF;u|ۍe[^_]Ív'UVPPM1jUE PQxuÍ'UWVS [: 6u}Nx!jU QU QW|Ny MQ@XU R@e[^_]ÍvUVPM1jURUE PQ|uÍv'UWVS [ê6u}Nx!jU QU QW|Ny MQU@XU RK@e[^_]ÍvUVPM1jURUE PQ|uÍv'UWV1S[6UR?;u| e[^_]PMjU QMQxF;u|ۍe[^_]Ív'UVPPM1jUE PQxuÍ'UWVS [z6u}Nx!jU QU QW|Ny MQ%?XU R?e[^_]ÍvUVPM1jURUE PQ|uÍv'UWVS [6u}Nx!jU QU QW|Ny MQ>XU R>e[^_]ÍvUVPM1jURUE PQ|uÍv'UEM QPÐ&UEM QPÐ&UEM QPÐ&UEM QPÐ&UEM QPÐ&UEM QPÐ&UV MUE 1PQuÍ&'UEM QPÐ&UWVS[J6uU >RVƋE)Ɖ4$<UNj<$VuVM QR e[^_]Ð&UWVS[6EuP_< NjjM QVxU RWP>e[^_]ÍvUWVS[Ú6uU }RVMZ)QPWE PV@EPF<e[^_]Í&'US[<6UR<] UVS[6EujVM QP|4$;e[^]Ív'UVS[6EujVM QP|4$;e[^]Ív'UWVS[Ê6uU >RVƋE)ƍ6$:UNj<$VuVM QR(e[^_]Ðt&UWVS[*6uV:U NjjM QRx VWP<e[^_]Ít&UWVS[6uU }RVMZ)QPWE PVHEP:e[^_]Í&'US[|6URT:] UVS[N6EujVM QP|4$:e[^]Ív'UVS[6EujVM QP|4$9e[^]Ív'UWVS[6uM QVP|URǍRPVx <ОEPQWVxU@RRWVx UDRßRWVxUHR˟RWVx ULRRWVxUPRҟRWVx UTRܟRWVx U쉃XRRWVx \EPQWVx`RRWVx U쉃|RRWVxU쉃dRRWVx U쉃hRRWVxU쉃lR RWVx U쉃pRRWVxUtRRWVxxe[^_]ÉUWVS[ê5u j0!7M E싓<RVQ@M A E8RVPD}M GRVQM HAE8RVPL}M G$RVQM PAE8RVPT}M GRVQM XAE8RVP\}M G RVQ|M dE8RVP|h}M RVQ|M `E8RVP|EE^_0}WP1M $5UljU ;2|#VVE0RPu$Ee[^_]ÉPUVMQRM F;1|VVE0RPu$Ee[^_] UWVS [Ê5UJ$rMz8U҉E ȨthVE0jURPxMAt]xPM1WRMQVZYP}WV8u}>e[^_]ÍutUBu u VK4u NEx%WE8jMPEPx}NyۍPPVUR脡}lWU$RM QEP]MA(IPUjMQRxMA, U R3u NEx)t&WE8jMPEPx}NyۍWWVURՠtQU$RM Q}W}[&PPWuV袠u͍*PPRMQ艠u2PPVEPpub&RRWuVU*PPRMQ82PPWuVMVE8jU RPxUWVS[5Mu}A(MUwEQA$uUEy,qG5UE܅  Ȩt{E8jMQURP|}tm}Px}7QREPVZYP}WV8U 2V 2}XW1XMQ1e[^_]ËEz}uu Nx+Mj} QMf8f9}Ѓ91MqMq %ЋU r9~;u}MEUfEU E E uM ʉUU}wUMUMEMOUMU؉M܉uGu}uЉ}E}9}EEURUUEEċMtE ED ED  uE;U7uDu}4 | u؋}܉t | uЋ}ԉt | 9~ЋE;U}"EEE ED ED ;U|EU9UTe^_]UWVluU EJMBEJ MV$Ẻ4$Eȉ4$lj4$Eč;Eu }EeEEU EEM9M}59}!uuĉuMEMü 0B9|EE9E|E}~iUE ‹E ‹M ʉUE ‰UM ʉUE ‰UEU9U M4u<}EEċM)ȉǃ}tUЉUuuԉu}؉}EuE܉EUUUu}u};U}#E ED ED ED ;U|݋UE;Uu}4 | 9~;U }< EtEuE BE EtE ED ED EU9Ue^_]UWVLU %EuEuEuEuEmu9uu}}}E}dEE܋U ffEEU9U}2;U}MMM܍ }ċuf)򋄅F~֋x|Gx|x~ 4@V/1Džt9tDžtDžpȉHFQ NBVA9p1Ɋ!򋵬1ɊH!򋵨1ɊH!򋵤1ɊH !򋵠1ɊH !򋵜1ɊH!򈕊1ɊH!ֺ ֋1ҊPʃщ!¸ ŠOGO G WOGpp9p9p}gp14! pAp9p|GMQWRV=tBt9t VD1e[^_]Ív'UWVS[75EU uHx JxHBUz)R$~كɃ؉W$ٽddf Dždf11EdA٭۝٭)ljH~ŋA~DžtDžx8tx1))G~tx@txtx V3Džp9p|DžpDžl|ȉHFQ NBV9lH1Ɋ!򋵬1ɊH!򋵨1ɊH!򋵤1ɊH !򋵠1ɊH!򋵜1ɊH!򈕆1ɊH!ֺ ֋1ҊPʃ щ!¸ ŠOGO GWOGl l9l9l}gl14! lAl9l|GMQWVRϋpAp9p P1e[^_]Ð&UWVSEDžDžDžDžU H |xHzu鉽xEJP)[u5R$~كɃ؉W$ٽPPf DžPf11EPA٭۝٭)ljH~ŋA~Dž`Džd8d`1x>x)򋄅F~֋`dF`d`~ @Q'Dž\9\hlDž)򋄅F~֋x|Gx|x~ 4@V/2Džt9tDžtDžpȉHFQ NBVA9p1Ɋ!򋵬1ɊH!򋵨1ɊH!򋵤1ɊH !򋵠1ɊH !򋵜1ɊH!򈕊1ɊH!ֺ ֋1ҊPʃщ!¸ ŠOGO G WOGpp9p9p}gp14! pAp9p|GMQWRV}tA~t9t VC1e[^_]É'UWVS[75EU uHx Jx鉽HBUz)R$~كɃ؉W$ٽddf Dždf1t&1EdA٭۝٭)ljH~ŋA~DžtDžx8tx1))G~tx@txtx V4Džp9p|DžpDžl|ȉHFQ NBV9lH1Ɋ!򋵬1ɊH!򋵨1ɊH!򋵤1ɊH !򋵠1ɊH!򋵜1ɊH!򈕆1ɊH!ֺ ֋1ҊPʃ щ!¸ ŠOGO GWOGl l9l9l}gl14! lAl9l|GMQWVR+# pA~p9p P1e[^_]Í&UWVSEDžDžDžDžU |H xHzu[à|5鉽xEJP)ѺR$~كɃ؉W$ٽPPf DžPf11EPA٭۝٭)ljH~ŋA~Dž`Džd8d`1x>x)򋄅F~֋`dF`d`~ @Q蝲(Dž\9\hlDžEEUWV M utt ~;yt e^_]ËF E;A uE;uF;Auۃu֋UE… кuQAUEVvU}t_}}}uURMQURWEyEPVEyEPuV1AMPQURMQg}URMQURWEPV}WuV&#봍MPQURMQ}MQURMQWU}RV}EWuV*XMPQURMQ}|URMQURWUMRV}EWuVQ%UWVS|[ÚJ5Uك$UE|BE}BE]}EE]BEB EB(E]EE]B0UEB8EB@E]EEE]EEEEEEEEEك̞]ME@ɉEEtEBEBE]EE]BE}B E}B(E]}EEy]B0UEwE]B8EuE]B@E]oEEUEh؋E}5}EEEEXEEEXEEEDEEE?ك̞]UDž|B1U1M1Eӽ|EEEۅ|}EfM fMEm]mUEEm]mM?EEm]mMEm]m}EEm]mMWEEm]mMEm]mɋMEEm]mEىMu~MMEm]mEMԁ Ȩu6} Ȩu}} Ȩt vuuu E|[^_]MMem]mEMɋMEem]mEM8em]mEˋMem]mˋEMˋMEem]mEMKem]mEem]mMۅ|}EfM fMem]mu>Eem]mM9&UWVS [C5UBBBB B(B0B8B@EuKEtOكĞEEuZك̞ɋUBUEt>EuEuكĞEEtك̞MuEMR1$}fU1ɶ 1fUt&Um]mE=u~ &1u-t&Em]mE=u~ &1Um]mE=~qؾu~tuFu} M [^_]Aǃ…!ШAǃ…!Ш'JAǃ…!ШXw UWVSl[ZA5U Mك4Ʌك0]B]B]B]B ]B(]B0]B8J@ʋUU׋U]]UEM4@u1;MQE݃X@ESB`bBB;M19E1ɊD9E1D9EEEMEME7`B"B7`B;M"&؋}U ׋MU}MU؃l1[^_]Ív}fE fEm]mEU}fE fEm]mE}t&}fE fEm]mEhv'UWVS[÷>5u Mك8ɍIU]F]F]F]F ]F(]F0]F8N@]]RuEM}u1ɉlu;M},EElUAl;Ml|܋MIMEHI|UEU9UM݃XIMك`8f)`fr9؃ MQm1e[^_]}fU fUm]mUf}fE fEm]muf2}fE fEm]muf2}fU fUm]mUf}fE fEm]muf2}fE fEm]muf2؍e[^_]Í&'UWVSd[j45U M]B]B]B]B ]B(]B0]B8]B@UU֋U]UoEM<@}1;M7E݃Xك<<@UH9DEEDEEMEMEMJʃ9H99v؋} EMM4}uUd1[^_]Ð}fE fEmmbt&}fE fEmmt&}fE fEmmt&UWVST[*25U }كDBBك$]]EGEGEE]EGEfG EmG(EmEEG0EG8EG@EEEEEكĞEEك̞]ME@EEtEG0EFEG8E?G@E?E?EE?E9EE'كĞEE'ك̞]MA1MMR$ɃE}fU fUm]mEm]muEm]mÚ} Ȩu'}} Ȩu }} t&ME}1ɅuEEEm]mEP$ƒ=vؾEكHEكLEuuIEt&Em]mEP$ƒ=ؾEكHE;كLEu EEm]mEP$ƒ=ھEكHEكLEutuu}ET[^_]EG EG(EEA…!Шڅ9ft&em]mEP$t&A…!Ш؅Y&em]mEP$ t&A…!Ш:؅y&em]mEP$+eR$Ƀe$eFh4&'UWVS [j+5EM كP@@@@ @(A@0@8A@@EuKEtOكĞEEuZك̞ɋUBUEtBEuEuكĞEEtك̞ˋMuEMR1$}fU1ɶ 1fUUm]mE=:Mm]mE=~ 8~1u*Em]mE=0m]mE=~8~1u-t&Um]mE=m]mE=~ 8~t}G}uE [^_]Í&Aǃ…!ШAǃ…!ШAi듐Aǃ…!Ш'UWVS[7(5U u$ك4ɋMك0Ʌ]B]B]B]B ]B(]B0]B8]B@]]FN]]UU׋UUEt4@x&1;xZE݃X@|TB`vBB;|19E1ɊD9E1D9EEEEMEMEEE+`BB+`B;|؋}U ׋MU}tUv؁Ą1[^_]Í&}fE fEm]mEO}fE fEm]mEi}fE fEm]mEVv'UWVS[W%5} u$ك8ɋMI]G]G]G]G ]G(]G0]G8]G@]]FN]]xRW\||}u1ɉTu;x}f`fr9؃ tQ(S1e[^_]}fU fUm]mUf}fE fEm]muf2m}fE fEm]muf2}fU fUm]mUf}fE fEm]muf2}fE fEm]muf2؍e[^_]ÍUWVS|[ 5U u$M]B]B]B]B ]B(]B0]B8]B@U]]F]F]UUUEx<@|t&1;|KE݃Xك<<@WX9DEEDEEMEEMEMEE9Ƀ$39$؋} EMx4}uU|1[^_]}fE fEmm]}fE fEmm}fE fEmmUS[Ü5URMQjjjjURE PURf]ÐUWVPMU }tmtiB9At Z^_]Ðt&B 9A uE;uދB9AuօtG}t/}tB}tK}u}U MY^_]4t&띉}U MX^_]W넉}U M_^_]!}U M^^_]? UWV!}#FG}}}}9}|Ӌ}9}}Q9E >!}܋}U}UʋM9M}>M!Љ}1ҋ}:B9~uEUEċ}EM@щEu}M9u`E 4M}σ}M} } 11ɊV N1U}1V ω}M щMu ƉuUWM} ))EHEM80Mă9EDž()ʉ0DыE苍D ED0<ыE< ‹ME<1U@u M(MċEЍT9sNM؊MEm܊HUЋD!ȋ@苍@;(UA;(~E؋DE܋ UDž|U9|MЃ MUu}ЉxEčL7xu))։M̉tƃ)u}E0UtE+} E)ʉ0pыEDžl苍p Ep0hыE苍h Eh0dыEd ‹MẺd1u M̃lpM܃E܋x!‹x;lt@;l~닍pdMȋht0t;}t!Ѓ 0#FAMȋF !tH t뽋Mt ))EMԋEMPTMLMă9=EDž()ʉ0`ыE苍` E`0\ыE苍\ E\0XыEX ‹MẺX1UUu M̃(MċEЍT9sNM؊MEm܊MԋUЋ`!ȋMM;(tA;(~닍`E؉LE܋t\XTPt;}MVm܉ЊMM܃ ȋT!ȋtMFMԉ,‹tP!‰QtMF ӭ,MԉE, L!E؉JE܃ teN%`~N ׉}U ω} U;}EЃ,9Y!ʉM܋t0;,t-0!‰E܉00Uȃt;}}">M̉!Љ}1ҋt:B9~uċ|EMB}u|uM9|;}}ċUЃ9MFm܊MԉEM܃ȃT!ȋtE؉tE9׋PTGMFm܊MԉE؋U܃ЋPt!ȃE؉tLE܉T;}}ċEЋM̃T809sVU؊Mu}؊M1ҍ7T!t B;0~1  ω}}11N  11ҊNV 1Ҋ щʉ uUΉ} Ή}u=MMjUWVPEME uQ;PtEP^_]Íy };x u9};8u܋y};xuыyU؉}܋PxUԋA}}E} Ȩ,E؋ME؋EЋ}9}E9E Ȩm} }:}BVN>UMv uU؋EЋu9ME؋U‹M̉EủEu1M܋U;u/#E#BAE#BAE#B A ;u~;u}*#F;E}E#BAF;E} rE!AMԋu΋UЋM܉uMMvG}E؋UVE}ԋE؋NUM@981;}u؋M܋U~%u؋E#E#BAE#B A NuދMԋu΋UЋMGщuM;}|u9E̋uMEUM܍FE9E8#E#BAE#BAE#B A EEE9E~9u}:!lj9}G9},zE!E}y}9}rE!EUQEljEEE>v}u}9U؋}1M܋U9}EF#E#BAE#B A 9|뉐UWVS [j4M Eutetat3t.t)tt-e[^_]RVQP t&uM Ee[^_]uM Ee[^_] 뷉UWVEMU uEAEEEE;Bt E^_]Ëy };z u94;:u܋y};zuыyE}AzE4}ċ}4} ȋz}UE҉U}R}X }` NMN} ׋V } ʉUEu9uE9E ȨE}9}U9EU!ȨtuEuЃ}EU9U}EЋuЃL8uM}EăM))ƉM)EU+EME E)ʉ0EыEEM EM0EыEU ‹MEU1u MMUE܃M܉ ʍ ;}}8@;E~}EM}U}9E}}-> } FG}}}}9}|Ӌ}9}}Q9E > }܋}U}UʋM9M}>M Љ}1ҋ}:B9~uEUEċ}EM@щEu}M9u`E 4M}σ}M} } 11ɊV N1U}1V ω}M щMu ƉuUWM} ))EHEM80Mă9EDž()ʉ0DыE苍D ED0<ыE< ‹ME<1U@u M(MċEЍT9sNM؊MEm܊HUЋD ȋ@苍@;(UA;(~E؋DE܋ UDž|U9|MЃ MUu}ЉxEčL7xu))։M̉tƃ)u}E0UtE+} E)ʉ0pыEDžl苍p Ep0hыE苍h Eh0dыEd ‹MẺd1u M̃lpM܃E܋x ‹x;lt@;l~닍pdMȋht0t;}t Ѓ 0 FAMȋF tH t뽋Mt ))EMԋEMPTMLMă9=EDž()ʉ0`ыE苍` E`0\ыE苍\ E\0XыEX ‹MẺX1UUu M̃(MċEЍT9sNM؊MEm܊MԋUЋ` ȋMM;(tA;(~닍`E؉LE܋t\XTPt;}MVm܉ЊMM܃ ȋT ȋtMFMԉ,‹tP ‰QtMF ӭ,MԉE, L E؉JE܃ teN%`~N ׉}U ω} U;}EЃ,9Y ʉM܋t0;,t-0 ‰E܉00Uȃt;}}">M̉ Љ}1ҋt:B9~uċ|EMB}u|uM9|;}}ċUЃ9MFm܊MԉEM܃ȃT ȋtE؉tE9׋PTGMFm܊MԉE؋U܃ЋPt ȃE؉tLE܉T;}}ċEЋM̃T809sVU؊Mu}؊M1ҍ7T t B;0~1  ω}}11N  11ҊNV 1Ҋ щʉ uUΉ} Ή}u=MMjUWVPEME uQ;PtEP^_]Íy };x u9};8u܋y};xuыyU؉}܋PxUԋA}}E} Ȩ,E؋ME؋EЋ}9}E9E Ȩm} }:}BVN>UMv uU؋EЋu9ME؋U‹M̉EủEu1M܋U;u/ E BAE BAE B A ;u~;u}* F;E}E BAF;E} rE AMԋu΋UЋM܉uMMvG}E؋UVE}ԋE؋NUM@981;}u؋M܋U~%u؋E E BAE B A NuދMԋu΋UЋMGщuM;}|u9E̋uMEUM܍FE9E8 E BAE BAE B A EEE9E~9u}: lj9}G9},zE E}y}9}rE EUQEljEEE>v}u}9U؋}1M܋U9}EF E BAE B A 9|뉐UWVPMU }tmtiB9At Z^_]Ðt&B 9A uE;uދB9AuօtG}t/}tB}tK}u}U MY^_]t&띉}U MX^_]7넉}U M_^_]!}U M^^_]? UWV1}3FG}}}}9}|Ӌ}9}}Q9E >1}܋}U}UʋM9M}>M1Љ}1ҋ}:B9~uEUEċ}EM@щEu}M9u`E 4M}σ}M} } 11ɊV N1U}1V ω}M щMu ƉuUWM} ))EHEM80Mă9EDž()ʉ0DыE苍D ED0<ыE< ‹ME<1U@u M(MċEЍT9sNM؊MEm܊HUЋD1ȋ@苍@;(UA;(~E؋DE܋ UDž|U9|MЃ MUu}ЉxEčL7xu))։M̉tƃ)u}E0UtE+} E)ʉ0pыEDžl苍p Ep0hыE苍h Eh0dыEd ‹MẺd1u M̃lpM܃E܋x1‹x;lt@;l~닍pdMȋht0t;}t1Ѓ 03FAMȋF 1tH t뽋Mt ))EMԋEMPTMLMă9=EDž()ʉ0`ыE苍` E`0\ыE苍\ E\0XыEX ‹MẺX1UUu M̃(MċEЍT9sNM؊MEm܊MԋUЋ`1ȋMM;(tA;(~닍`E؉LE܋t\XTPt;}MVm܉ЊMM܃ ȋT1ȋtMFMԉ,‹tP1‰QtMF ӭ,MԉE, L1E؉JE܃ teN%`~N ׉}U ω} U;}EЃ,9Y1ʉM܋t0;,t-01‰E܉00Uȃt;}}">M̉1Љ}1ҋt:B9~uċ|EMB}u|uM9|;}}ċUЃ9MFm܊MԉEM܃ȃT1ȋtE؉tE9׋PTGMFm܊MԉE؋U܃ЋPt1ȃE؉tLE܉T;}}ċEЋM̃T809sVU؊Mu}؊M1ҍ7T1t B;0~1  ω}}11N  11ҊNV 1Ҋ щʉ uUΉ} Ή}u=MMjUWVPEME uQ;PtEP^_]Íy };x u9};8u܋y};xuыyU؉}܋PxUԋA}}E} Ȩ,E؋ME؋EЋ}9}E9E Ȩm} }:}BVN>UMv uU؋EЋu9ME؋U‹M̉EủEu1M܋U;u/3E3BAE3BAE3B A ;u~;u}*3F;E}E3BAF;E} rE1AMԋu΋UЋM܉uMMvG}E؋UVE}ԋE؋NUM@981;}u؋M܋U~%u؋E3E3BAE3B A NuދMԋu΋UЋMGщuM;}|u9E̋uMEUM܍FE9E83E3BAE3BAE3B A EEE9E~9u}:1lj9}G9},zE1E}y}9}rE1EUQEljEEE>v}u}9U؋}1M܋U9}EF3E3BAE3B A 9|뉐UWVS,[å4u Mtt ;t e[^_]Ðt&y9~uAE9FtًF E;A uwċpWWQV1PPQV] RRQV2PPQV؋UV}U܋yVuCAEЩu6EEEԋuƃuEuVPW}W xVF$UEQUQ$U9tBuԅTuԃ MQURMQWEP M܋Uу M܋MNuuԅuԋURMQWEP7UMуUMNuRPWMQ &UWV(E}u ~5U)9E+U)ѺM"" ш(^_]ÊM"G" EEUFLMM9M}'tGFUMBU9M} u1u{U1ɃU9U TωT΋UEAU9E~<ύ4΋M9M}ʋM)ʍvGFJuE؃5)"" u*M1҃9MBEE9M~<4덉E )‹MU)EԋEM9E} MEEE9E:MЋEULMMEM ЋMЋUUAU܉M9U~ƋMЍ<4t&UWVTUu B NMENFrzƉ}EzM9}9!ШtUuEU}EM9M}EEE}Eut EM;u}2}ċMȉ}܉ME܋M}LE8L8;u|ڋ}MGuUʉ}EMȋ}uuEUĉM9u|T^_]ËU~Mu}M1E)9E}‰M91AM9|UU9U!MT991T1UЉM9~E9E}M91AUM9|UMMQ1kE)9E}‹M91AM9|1u#U9UM91M9~qME 9M؉ƒ)E)щUM؋MUԃM9M5t&MUԋAMEEԊMM ЋU1ẺUM9~t&UWV`Uu B NMENFrzƉ}EzM9}9!ШtUuEU}EM9ME}uUEEuUE}ut E}ff;u};MUM܉Ut&E܋M}fAfLAE;ufGfLG|֋u}UEMFȉu}uUEUE‹u}U9um`^_]ËM~MEUu}MEĉU1E؃u~1)9}fOfNA9|UU9vOTOTNN;M~;M}t&fOfNA;M|UMMX1E؃tхtffEuU9ON9~뗍WEEԋE@EЋEE9qEԋU@E ЋUԃNU;M~D&'UWVPMu A VUEVFqyƉ}EyU9}9!ȨtEMEE}Eu9u}UMEEEMĉE}Uut EUċ8:;u}5UȋM̉UME}}L}E;uL|׋EU}@׋MuĉE΋UȋM̉}E}uĉUȋuM9uuP^_]Ëu~EUM}uEЉUԉM1uhtEE9t&TT;M~;M}t&A;M|UЋMMl1tU1J9ŋA9~븉UWV}E 1ute~E tU NG BU ~u~U  U 萅~M NGAM ^_]Å~E tU NG BU ~u~M WQM 량E M)щUMUEqMMЊMmMMU ȋM M 5t&UWVU} B rEOBuwEGzNjUE9U9M!Шt}EE}U~DUEUuME1;M}΋TTA;M|UMMuփ^_]Í&'UWVE}u ~WVIu^_]ÐUWVS,[4u Mtt V;Qt e[^_]ÍvF;At e[^_]Ë~ };y uЋ>}Ћ9}~}y}~}y}~}܋y}؋}}}w}Ћ}sE}nE}VEܡ}>E}&E}E0}}LPPQV"1RRQV4+"PPQVyB"PPQV>-"PPQV,"WWQV8,"PPQVM("PPQV"RRQVY"PPQV,]"PPQV["PPQVZ"zWWQV(X"lPPQVzR"^PPQV5"PPPQV2"BPPQV1"4PPQV1"&PPQV40"PPQV," RRQV%"EuEu E:Eu ?Eu EJPP}WRuVMQEPUR$" WWQVR"PPQV:"xRRQV?"jPPQVx<"\PPQVj;"NPPQV9"@WWQV6"2PPQV0E"$PPQVRH"PPQVF"PPQVE"PPQV(D"PPQVJJ"WWQV,P"RRQVnL"PPQV@K"PPQVH"Q}WuVMQK "QQMQR}WuVMQUR "PuVUREP!"WUWVS[è4 U,M4ҋ} )ʋMttuM;t e[^_]ÍvMG9AuM8uM9|I}}uȃ jM4QU0Ru,VE(PM$QU RuVMQURuVWEP螛 L}tM}z jE4PU0RM,Qu(VU$RM QuVUREPMQW}W@ < jU4Ru0VE,PM(QU$Ru VMQURuVEPW}Wr jM4QU0Ru,VM(QU$RE PuVURMQuVWUR 뇐UWVS[(4 U,M4)ʋMt} tu} ;t e[^_]ÍvG}9Gu}8uE99ƒ}!ШtkwI3몃bu jM4QU0R},Wu(VE$PM QUR}WuVMQU REP?)[Rw?Iu jM4Qu0VU,R}(WE$PM QuVUR}WMQu VEP 묃DK jU4R}0WM,Qu(VU$R} WEPMQuVUR} WMQ_ [ jE4PM0Qu,VU(R}$WM QuVUR}WEPM QuV jU4R}0WE,PM(QU$Ru V}WMQURuV} WEPa9 ju4VU0R},WM(Qu$VU R}WEPMQuVU R}W jU4R}0WE,PM(Qu$VU R}WMQuVUR} WEP _ jE4PM0Qu,VU(R}$WM QuVUR}WEPM QuV UWVS [U4M utt ;t e[^_]Ðt&y;~uuOuPPQV1׃tw Jt1ÃtuD wЋh1PPQV艥RRQV{WWQVБPPQVUPPQVzPPQVߕPPQVԎPPQVPPQV.RRQV賓WWQV؋PPQV荃낐UWVS\[%4M } t1Eu… ШuUu… Шte[^_]ÐU$u(… кuڋuqUuQAUq QuUĉE1;U}CuE 49EEEQ$4$$\B;U|}}w2}e[^_]}(x`U$UxI}}uȃ M(UQu$VWEPRuV}WMQURuV}WEPMQZQe[^_]úe[^_]à E(UPM$QWuVRMQ}WuVUREPMQ}WuVM몃 U(MRu$VW}WQURuV}WEPMQURuV}WTl U(MRE$PW}WQuVUR}WMQuVEPUR}W]F.UWVS,[ŷ4M Utt q;rt e[^_]Ívy }؋};z uytEBEԸ;}uʋ;}ԉEEAEBEAEBEtO땋BEÃ}t}} Ȩ}}}}uURMQEPVu}VUREPuV"1 }}} ȨF}{}6}}#}WMQURVuEV}WEPuV##sM}}}}MQ}WURVEPuVEPEP/"}}-}}}@} URA$PjMQVuV}WURMQ}M}}}}}WMQURVEPuVEPEP+#}#}{}}E^A$}} }WPMQURVuV}WMQURh}}}]}}}WMQURVEPuVEPEP|9t&}}}@}}wURMQ}WVEyPuVEyP}Whv}WURMQV}WuVEPuV裬"EP}WURVEPURuVMQ"aMQ}WEPVEgPUR}EWuVU"%UR}WMQVuVEPEPEP#}WMQURVEPMQURuVQ#UR}WMQVEyEP}WMEQuV"UR}WEPVEyEP}WMEQuV5%#E}WMQURVEPURMEQuV&# }WMQURVEPMQURuVGfMQ}WEPVEwPUR}EWuVˉ}WEPURVE0PURMQuV{iURMQ}WVEP}WuMVEP}-UR}WEPVEjP}WMEQuVt MQPjURV}WMQURuV蘖}WMQURVEPURMEQuVv}WMQURV}EWMQuV}Wk"[}WUREPV}MWURMEQuV" }WUREPV}MWURMEQuV"MQUREPV}WuVURMQn"UR}WEPVMQUR}EWuV蝤"MQUREPVM}QUR}EWuV"#RMQUR}WVMEQUR}WuV#!URMQ}WVURMQ}EWuV"MQUREPV}WMQURuV"MQUREPVM}QUR}EWuV>#UR}WEPVMQUR}EWuV]"]UR}WMQVUR}WMEQuVL",E }WPjMQV}WURMQuVHEEEEGEFEEEEDE?E!EcEEE/EEE~EAEUWVXuU }tttJMM;Nt X^_]Ít&J M؋M;N u MԋM;uыJMЋM;NuGME9t몋G M؉E9uMȋM9MuOMċM9MtMċEEOBMEJFWEMFUE܃}}}}MUuuU1;}_EU4)11ȃ!‹E1!ыU)΋M܉4tD)11ȃ!1!)΋M܉t;}~;}}*EU4)11ȃ!1!)΋M܉4}MU}܋EuMMU}A17M~UuuU1;}mEuf xf~ȉf)%)ЋU!ƋEf)uf xfLzfT~ȉf)%)!ƋEf)fLx;}~;}}1Uuf zf~ȉf)%)!f)uf ~}Eu}܋UMωEMu}, Uu}}u1;}YUuf z~)Љf)֋U!ƋEf)uf xfLzT~)f)!ƋEf)fLx;}~;}}'Uuf z~)f)!ƋEf)f x}EuM܋U}EMuMJ5u*U}}U1;}eUu 7(Ё1)ЋU!Ɖ( EuLT7(Ё1)!Ɖu(L7;}~;}}-Eu 7(Ё1)!Ɖu( 7MEȋUM܋u}EMUM8NUVPM uttt8w JtAuÃt,uRRVQW&1uPPVQPPVQPPVQ ؐ&UWVS|[×4Uك$rJűz BMԉMЋrUʉEĉ}QE$ػĞ;E}E9uEUEEEE1;}}NDž<M̅~&M<ʋEԍ U1fEEJu!Dž Ѝ~%ĉ9 xDž?΋G%Gщ%G %Ɖ9DždDž\`DžTDžLDž߭`XP H@߭X ߭Pċ߭HDž9 t;ʃЁNuˋt9!֋%1RQ1,$1VW1,$RP,$VW,$ʋًE ɋ)֋Ƀȋ))׃ȋuMΉu}Љ}U uȅBB Bv E}ȈEEEEEMԋẼUԉDž|A) :!z9&Dž?΋Gցщ%G%%G щ%2[EEEDžA։muDž‹m9t;ʃЁNuˋtA!֋%1V1Q,$‹RW,$ɋE ɋ}ЋMăω}u‰umMȅ^uȈEEEEEEMԋũDž8T1׉ Uԉ$ )E:!zuMM98MDž(?U΋GցUщ%G0%0G 0000щ%2(UMfEu EMMMuE8FEm‰898$t/$EʃЁUENUu׋ t6 }!֋E}EUMEUu1ҍ<EM48}RV}ЋE,$ǃ}‹uЉuɋM UE uȅ@@@@ @MȈUEEEEEŰMԃuԍE}܃}M9U~U9UM9Ms}U܋M1Uu}MEU܊MЋMU9p2B9~`UUMUWV@E UEM } EEtE uMЉ )+M)MEME)MԉM̃EMM)EEEEȋEEEE ~ E@EE ~ MAE܋MEMȉEĉEMM1ʃu UUMЋEmM؋UMmE܊MMȋM ‹E;E 8@;E~ME܃MEU U9aUȊMmM؋UĉEUMEʊMmMMȋM ‰MMċUȃE܃EUȉM;u~;u U )u9~uȋMM9~UċE܋M1uuuMmM؋UeMʊMmMԋEe܋MȋM ‹E9 8@9~@^_]ÍUWVS [Úu4M uEtPtLtHt/t*t%t t e[^_]RPQV褐EM ue[^_]Ѝ&UWVEuM EV``;yte^_]ÐV U;Q uU};9u܋VU;Quы}`;Gt뼋}U;W uU;uU;WuFQEĉŰE`UUxEE}ȋBy}E}ƒ}~ Љ}VU҉UE};Eo;Ef;E]Au"MAuFuU}EU}EEu}9uučL7MEċŨ})‰E։у))uEu)ƉΉEE) +uMċE1Eu MMMU MMUU;U :B;U~EЃP9mMM MMMՋMЍQ̓}EEME E)΍ )ȉMMȉEԃ9MuEMEMЃEd)MЍ~ MQU܋UċEM1ƒEMu UUMUmE܊MMȋ ȃMUU;U :B;U~UЃE܃Ed9FEMmMԉU܉ЋUMȋ ȃME܉EUM;d;uUȋM9UsU2u܋M1Eu EEMEU܊MЋU ȍ ;u:B;U~UE )։EU)‰U؋Ũ9UuUEUEЃUP)~ EHM܋UEEUU1ƒu MMMUmE܊MMȋ ȃMUU;U :B;U~MЋE܃EQ9MMmE܊M؋EMȋM ME܃EEE9gUDžp )։Ep)‰pŨ9UuEE ‰UEUxtEЅP)~MAMI ȉE܋UEM艕xUĉt1Džlu ElMEmpUMȋM;l:B;l~EЋU܉UP9tx ȊMmpEMȋM܉MtxxtPuVRQuVEPW}Wq Mu΋UẺuȋuЋ}MUFu}ẺUĉM9}{1C;u}űE9xst1x Ɖu܋U1Džhu MhMEU܊pЍ ;hB:B;h~.;u%M1uuMM  9:B9~;uŰM9UsE0u܋U1Dž|u E|MEU܊MЋU ȍ ;|m:B;|~YUWVS[÷l4EݝE$pUݝE,M܉uݝE4ݝxE<} ݝpEDݝhELݝ`ETݝXhRQvEu@݅U؃̞Dž04ݝHx*t!Mt,U,; te[^_]ÍO((;JuًJG<8+W M$}sM A@@@@)P転TTQETTPȉ ЉыO 4@,ƒ, Шt }\WDž}\}\ك$GݝۅݝQ$}`ݝ)ݝQEP|RxQRQ\RXQdR`PlRhQtRpQURMQ@D7u}TO 19}W48<@9ȉ8|}\M؉0u\1t}\,t,h,x(1 E;p FPUREHQQRLPHQR0 PQ4RRDDċ}DDDDDٝ|DٝP9t&QTمTم|ٝTTTمTٝTTVAQ TمTٝTTTمTمPٝTTFV 9^;uu V;UeI~;}^UI؅|^U M}u dU Mtİ^_]ÍvDDDDDDDDϋEٝxٝL9wfٝpAٝtA݅pمxA ɃٝhمLٝl݅h^9v;ug V;UWI~;}^EI؅xDDD]DDD]]DDD]DDDˋE]ٝP9wyt&EA]E]EEAA ]E]EE^AAɃ]مP]E^9v;usV V;UEsGI~;}E^s6IV ;UE^s%I ~;}E^ sIE^UWV0UE utttJM;Ht0^_]Ðt&J M܋M;H uߋ M؋M;u΋JMԋM;HuM;Nt묋};~ uMؿ;uM;NuJxM}J~MUȋH}FMuԉEփ}U}}te}7u܅~HM1;U}}}4})B;U|uE}uEUEI}uu1U܅~u1;M}2U}fJfO%)Ћ}!fOA;M|΋EM}MUMEN}Mu됋u܅~}܉}̋EU1ШEu11EtuEMf>f+8f9}Ѓ92MqMq)%)ЋU r9~;u}MEUfuPE P\$$VWI(oPM Q\$$VW:PU R\$$VW&RU R\$$VWvS뫐UWVS, [M4EU 4xp JplxpBLhtz JBDžxHEEك$كĞ鉵0,ۅtݝ`E ܍`ݝP܅PݕPW$ٽf Hf٭۝٭̋)ˉ p EɋE ٭۝٭٭۝٭);p~pl9~1_٭ە٭,݅`٭۝|٭}ۅىUEu Aۅ|݅`Eu |F|t9~9|~|DžXDž\?Ww $ɋh٭۝٭hh~hp =n|@Uщ8EJщR$4ٝUɋh19]Љ(}Uٽf f݅X٭ە٭(ݝXB9|1@~݅1U9}@@9|؅t)ʋ(ttT9~ ()‰ThDžTT,|) xEU FBBBABBABB Q ‰Q 9|Z^_]ÍvUWVPu~ NE19}LEU FB BBBB$ABBB(ABB B,Q ‰Q 9|Z^_]Ðt&UWVPu~ NE19}VEU FB BB0BBB$B4ABBB(B8ABB B,BF V}ENf~ F1;u}G}EfE fEv}E F:P$ɋ}mm:;u|؃^_]ÍUWV}7G OuEfW wG1;}}J}fUE fUvEU GBP$ɋEmm;}|؃^_]ÍvUWV}7G OuEfW wG1;}}K}fUE fUvEU GBBP$ɋEmm;}|؃^_]ÉUWV}7G OuEfW wG1;}}P}fUE fUvEU GBBB P$ɋEmm;}|؃^_]Í'UWV}7G OuEfW wG1;}}d}fUE fUvEU GP$ɋEmmBQЉ$$ɋEm^mA;}|؃^_]É'UWV}7G OuEfW wG1;}}j}fUE fUvEU GBP$ɋEmmB BQЉ$$ɋEm^mA;}|؃^_]ÍvUWV}7G OuEfW wG1;}}p}fUE fUvEU GBBP$ɋEmmB BBQЉ$$ɋEm^mA;}|؃^_]Í'UWV}7G OuEfW wG1;}}v}fUE fUvEU GBBBP$ɋEmmB BBBQЉ$$ɋEm^mA;}|؃^_]Í&UWVEE0x uH}fP p9}@}fUE fUE}U 9P$ɋ}mm9By$$ɋ}m^myBQЉ$$ɋUBUm^my} 9}|؃^_]É'UWVEE0x uH}fP p9}@}fUE fUE}U z 9P$ɋ}mm9zBy$$ɋ}m^myBBQЉ$$ɋUBm^myU} 9}w؃^_]Ð&UWVEE0x uH}fP p9}@}fUE fUE}U z B9:P$ɋ}mm9zBzy$$ɋ}m^myBBB QЉ$$Ƀm^mUByU} 9}j؃^_]Ít&'UWVEE0x uH}fP p9}@}fUE fUE}U z Bz$:9P$ɋ}mm9zBzz(y$$ɋ}m^myBBB B,QЉ$U$BUm^my} 9}]؃^_]Í&'UWVEE0x uH}fP p9}@}fUE fUE}U 9P$ɋ}mm9By$$ɋ}m^myBA$$m^myB Q Љ$$Ƀm^mUBy U}9}j؃^_]Ít&'UWVEE0x uH}fP p9}@}fUE fUE}U z9P$ɋ}mm9zBy$$ɋ}m^myBBA$$m^myBB Q Љ$U$BUm^my }9}Z؃^_]Ít&'UWVEE0x uH}fP p9}@}fUE fUE}U zB 9:P$ɋ}mm9zBz$y$$ɋ}m^myBBB(A$$m^myBB B,Q Љ$$ɋUBUm^my }9}J؃^_]Ít&'UWVEE0x uH}fP p9}@}fUE fUE}U zB z0:9P$ɋ}mm9zBz$z4y$$ɋ}m^myBBB(B8A$$m^myBB B,BN MFNV}Ef~ M1;u}6ljE}E Fȋ 8ϊMMf9MM ;u|Ѓ^_]ÍvUWV}OG MUEOfW wM1;}};‰EMU G‹JUȋ ȊMMf ;}U|˃^_]Í&'UWV}OG MUEOfW wM1;}}A‰EMU G‹JBȋM ȊMMfEM;}|Ń^_]Ð&UWV}OG MUEOfW wM1;}}E‰EMU G‹JBȋJ ȋ Uȋ ȊMMf ;}U|^_]Ít&UWV}OG MUEOfW wM1;}}N‰EME GЋMMfMU J@ȊMMfFJ;}U|^_]Ít&'UWV}OG MUEOfW wM1;}}[‰EME GЋHʋMMfMU HP EʋHʊMMfVH;}E|^_]Í&'UWV}OG MUEOfW wM1;}}e‰EME GЋHPʋMʊMfMU HP ʋHEʋHʊMMfVH;}E|^_]Ít&UWV}OG MUEOfW wM1;}}o‰EME GЋHPʋHʋʋMMfMU HP ʋHʋHEʋHʊMMfVH;}E|^_]Ív'UWVEEHMxH UMpfP }9M}m‰Et&EMU <}ȊMfEBGfFEGBWЋUMBOU fF}}9}|^_]Í&UWVEEHMxH UMpfP }9M}x‰Et&EMU <}J ȋȊMfEBBGfFEGBBWЋUMBOU fF}}9}|^_]ÍUWVEEHMxH UMpfP }9M‰EEMU <}J Bȋȋ ȊMfEBBBGfFEGBBB WЋUMBOU fF}}9}|^_]ÐUWVEEHMxH UMpfP }9M‰EEMU <J Bz$ }ȋȊMfEBBBB(GfFEGBBB B,WЋUMBOU fF}}9}y^_]ÉUWVEEHMxH UMpfP }9M}‰Et&EMU <}ȊMfEBGfFEGBGfFEGB W ЋUMBO UfF}}9}|^_]Ðt&UWVEEHMxH UMpfP }9M‰EEMU <}JȋȊMfEBBGfFEGBBGfFEGBB W ЋUMBO UfF}}9}z^_]ÍvUWVEEHMxH UMpfP }9M‰EEMU <}JB ȋȋ ȊMfEBBB$GfFEGBBB(GfFEGBB B,W MfFO UB}U}9}l^_]Ðt&UWVEEHMxH UMpfP }9M‰EEMU <JB z0 }ȋȊMfEBBB$B4GfFEGBBB(B8GfFEGBB B,BEU FBBBABBABB Q ‰Q 9|Z^_]ÍvUWVPu~ NE19}LEU FB BBBB$ABBB(ABB B,Q ‰Q 9|Z^_]Ðt&UWVPu~ NE19}VEU FB BB0BBB$B4ABBB(B8ABB B,BN MFNV}Ef~ M1;u}@}䍴&}E Fȋ 8ϊMMf9MM ;u|Ѓ^_]É'UWV}OG MUEOfW wM1;}}EU荴&MU G‹JUȋ ȊMMf ;}U|˃^_]Ít&UWV}OG MUEOfW wM1;}}KU荴&MU G‹JBȋM ȊMMfEM;}|Ń^_]Í&'UWV}OG MUEOfW wM1;}}OU荴&MU G‹JBȋJ ȋ Uȋ ȊMMf ;}U|^_]Ív'UWV}OG MUEOfW wM1;}}XU荴&ME GЋMMfMU J@ȊMMfFJ;}U|^_]ÐUWV}OG MUEOfW wM1;}}eU荴&ME GЋHʋMMfMU HP EʋHʊMMfVH;}E|^_]Ít&UWV}OG MUEOfW wM1;}}oU荴&ME GЋHPʋMʊMfMU HP ʋHEʋHʊMMfVH;}E|^_]Ív'UWV}OG MUEOfW wM1;}}yU荴&ME GЋHPʋHʋʋMMfMU HP ʋHʋHEʋHʊMMfVH;}E|^_]UWVEEHMxH UMpfP }9M}mUEMU <}ȊMfEBGfFEGBWЋUW }}GfF}E9E|^_]Í&UWVEEHMxH UMpfP }9M}xUEMU <}J ȋȊMfEBBGfFEGBBWЋUW }}GfF}E9E|^_]ÍUWVEEHMxH UMpfP }9MUEMU <}J Bȋȋ ȊMfEBBBGfFEGBBB WЋUW }}GfF}E9E|^_] UWVEEHMxH UMpfP }9MUEMU <J Bz$ }ȋȊMfEBBBB(GfFEGBBB B,WЋUW }}GfF}E9Ey^_]UWVEEHMxH UMpfP }9M}UEMU <}ȊMfEBGfFEGBGfFEGB W ЋUW }}GfF}E9E|^_]Ðt&UWVEEHMxH UMpfP }9MUEMU <}JȋȊMfEBBGfFEGBBGfFEGBB W ЋUW }}GfF}E9Ez^_]ÐUWVEEHMxH UMpfP }9MUEMU <}JB ȋȋ ȊMfEBBB$GfFEGBBB(GfFEGBB B,W ЋUfFW E}}G}9El^_]ÍvUWVEEHMxH UMpfP }9MUEMU <JB z0 }ȋȊMfEBBB$B4GfFEGBBB(B8GfFEGBB B,BEU FBBBABBABB Q ‰Q 9|Z^_]ÍvUWVPu~ NE19}LEU FB BBBB$ABBB(ABB B,Q ‰Q 9|Z^_]Ðt&UWVPu~ NE19}VEU FB BB0BBB$B4ABBB(B8ABB B,BN M}~ FNVE1M;u}=M䐍t&ME ~݋UMMЉUEE9EuMU)‰ЋU!ƒ! fUA)‰ЋU!ƒ! fVUA)‰ЋU!ƒ! fVUA)‰ЋU!ƒ! fVUA)‰ЋU!ƒ! fVUA )‰ЋU!ƒ! fV UA )‰ЋU!ƒ! fV UA)ƒЋU!ƒ! fVUԃUԋU9Uu9u}5UԋuMVu)ƋEu!у! f pMFu9|ˋuEMMЍFuUNE,^_]ÉUWVDUE UE}EE9EEEEM9M}ċuȉ}u}E 1ҍ4x}E G} ;~!}$fBf!} ;ߋ}(fBf~ߋ}ЋuGM}ЋUuM9U|Eԋ}@uȋMUE֋E}ĉu9E@D^_]Ë} E$U(0M:M uu$}܋AM(VEyuMUɉ}؉M~qUủU1;}uMU)‰ЋU!ƒ#E fUA)‰ЋU!ƒ#E fVUA)‰ЋU!ƒ#E fVUA)‰ЋU!ƒ#E fVUA)‰ЋU!ƒ#E fVUA )‰ЋU!ƒ#E؃ fV UA )‰ЋU!ƒ#E fV UA)ƒЋU!ƒ#E fV;};}}XEuMx)֋U܉u!! Ef ~uLx)΋E؉Mu!փ! fty;}|EMuUM}EU!!EU܋EM܋} ȋMEU9}EU!!E؉UԋE؋Uԋ} ЉE؉U"D9! ˆT9&'UWVPU$ME M Eɋ}%JHu(%ɉE~ DDEJ~ M""MJ ~ UUQN∈~DDN~""N ~EEH}MEM,EE܉U؋E } M EwUuU Au,Ez }1EU,MȋE)щM9E~EȋE1EE9EEE,U)‰EUĸMċU ‹EUԋU x)ʹЋU)Ѻ!ЋM ƋETx)ыUȹ)Ѻ!ЋM ƋETx)ыUȹ)Ѻ!ЋM ƋETx)уȺM! ;}7;}}VU,ڍJt&U ‰EEUIxEG)ЋU!ЋU ;}ȋEUUUUUE|M܋U!!֊U u!u" шE;}sE x1AU)u)Ɖu@ A)Ɖu A)Ɖu A)Ɖu A )Ɖu A )Ɖu A)Ɖu ‹E!Ѓ!u ЋUqAU)u)Ɖu@ A)Ɖu A)Ɖu A)Ɖu A)Ɖu A)Ɖu A)Ɖu ‹E!Ѓ! ЋuUЃDU;};}EUu x)ʋMDy)ƁtyM)@ M uD~)L~u)΃ u ʋMDy )Ɖty M) MDyM )E܉΋M !Ѓ!ʋM ЃUAM;}AMuy)֍W;UDyU)‰ЍW@ ;UTyM)щʍO ;MUMDz)MMMMMM ΍O;M}|DzM)MMMMMM ΍O;M}PDz M)MMMMMOE ;M}$Dz M)MUUMMU ֋U̹))ы}؋U!!} u!ƒ"7 ˆ7uE}ƋUM WuMP^_]UWVL}EU9UEE1;M}HuċEȉuE }(A;M}-U} ʍ4} Uʋ}9~1U$A;M|ӋMUAuȋ}EMƋMUĉu9M|L^_]ËU$} 7U(uE:M~Ջu MEuUuMU܉E؉uEU9UuMܐE)Ѓ!ЋU!ƒ! ‹EQ)Ѓ!ЋU!ƒ! ‹EVQ)Ѓ!ЋU!ƒ! ‹EVQ )Ѓ!ЋU!ƒ! ‹EV Q)Ѓ!ЋU!ƒ! ‹EVQ)Ѓ!ЋU!ƒ! ‹EVQ)Ѓ!ЋU!ƒ! ‹EVQ )Ѓ!ЋU!ƒ! ‰VU UE9Eu9u}=&Mu܋UMu)ƃ!!! u FMu9|ʋu؋MEuMM܉UpL^_]ËUEMU EuEUԉuЉM̉EEM9MuԋMЋE)Ѓ ЋU!ƒ! ‹EQ)Ѓ ЋU!ƒ! ‹EVQ)Ѓ ЋU!ƒ! ‹EVQ )Ѓ ЋU!ƒ! ‹EV Q)Ѓ ЋU!ƒ! ‹EVQ)Ѓ ЋU!ƒ! ‹EVQ)Ѓ ЋU!ƒ! ‹EVQ )Ѓ ЋU!ƒ! ‹EVE E9Eu9u}8MuЋUԋMu)ƃ !! u FMu9|ʋűMEuMMЉUv\t&UWVdM E u$9M(}u$xUM(EVAUE܃}EU9UEEEu9u}~}E}EU} E1ɍ4}} 9~}$A} 9}(A~U}BuUM}u9M|UMB}uEƉUM}u9MJd^_]ËUE҅U~Ju MEMuUuUEMu1;uEMU9}U܉PUUU9UUUU9}ԉPUU܉P UUU9UUUU9}̉PUU܉PU9UUP9}eU܃P ;ueQUQU؋Q UԋQUЋQŰQUȋQUċU9UE}!Ѓ!׉E؉}ԋM؋Eԋ} M؋MU؈9UE}!Ѓ!׉EЉ}̋UЋE̋} ‰UЃU"D9! ˆT9&'UWVLM$UE UEҋ}%u(PA∈~DDA~""A ~ɋN%H%ɉE~ DDEN~ M""MN ~ uuEEHpMEM,uU܉E؉}U } E E2OU ux Mr}U,uEE},MU)M9U~UEEEE9Et&}̋u,)M ыỦMԋMMU1)1Ѓ!1ʍN)!‹M ыỦMЋMDMU1)1Ѓ!1ʍN)!‹ME ЋỦEЋDMU1)1Ѓ!1ʍN)!‹M ыỦMЋMD MU1)1Ѓ!1ʍN!MЋu уMЉűU9Uu9u}q}̋u,)UԋM UԋŰMU1)1Ѓ!E1ʍN!M ы}MЋuM̋UAEM̋EuU}9E|EЋMUЋ}ЋU!!‰}ЊMԋuЃ Eԋu!" ʈEU9U8M̋u~݋UMMЉUt&EE9E>uM&fU%)‰ЋU!ƒ! fUfA%)‰ЋU!ƒ! fVUfA%)‰ЋU!ƒ! fVUfA%)‰ЋU!ƒ! fVUfA%)‰ЋU!ƒ! fVUfA %)‰ЋU!ƒ! fV UfA %)‰ЋU!ƒ! fV UfA%)‰ЋU!ƒ! fVUԃUԋU9Uu9u}A&UԋuMfVu%)ƋEu!у! f pMFu9|ƋuEMMЍFuUNEE,^_]Í&'UWVDUE UE}EE9EEEEM9M}ċuȉ}u}E 1ҍ4x}E G} f%;~&}$fBf&f} %;ڋ}(fBf~ڋ}ЋuGM}ЋUuM9U|Eԋ}@uȋMUE֋E}ĉu9E6D^_]Ë} E$U(0M:M uu$}܋AM(VEyuMUɉ}؉M~qUủU1;}7uMfU%)‰ЋU!ƒ#E fUfA%)‰ЋU!ƒ#E fVUfA%)‰ЋU!ƒ#E fVUfA%)‰ЋU!ƒ#E fVUfA%)‰ЋU!ƒ#E܃ fVUfA %)‰ЋU!ƒ#E fV UfA %)‰ЋU!ƒ#E fV UfA%)‰ЋU!ƒ#E fV;};}}dEuMfx)֋U܉u!! Ef ~ufLxE؁)΋Mu!փ! fty;}|EMuUM}EU!!EU܋EM܋} ȋMEU9}EU!!E؉UԋE؋Uԋ} ЉE؉U"D9! ˆT9&'UWVPU$ME M Eɋ}%JHu(%ɉE~ DDEJ~ M""MJ ~ UUQN∈~DDN~""N ~EE[H}MEM,EE܉U؋E } M EwUuU Au,Ez }1EU,MȋE)щM9E~EȋE1EE9EEE,U)‰EUĸMċU ‹EUԋUf x)ʹЋU)Ѻ!ЋM ƋEfTx)ыUȹ)Ѻ!ЋM ƋEfTx)ыUȹ)Ѻ!ЋM ƋEfTx)ѺȊM! ;};}}ZU,ڍJU ‰EEUIfxGЋU%)‰ЋU!ЋU ;}ȋEUUUUUE|M܋U!!֊U u!u" шE;}E xt&f1fAU)%u)Ɓu@ fA%)Ɖu fA%)Ɖu fA%)Ɖu fA %)Ɖu fA %)Ɖu fA%)Ɖu ‹E!Ѓ!u ЋUfqfAU)%u)Ɓu@ fA%)Ɖu fA%)Ɖu fA%)Ɖu fA%)Ɖu fA%)Ɖu fA% )Ɖu ‹E!Ѓ!u ЋUЈDU;}A;}EUuf x)ʋMfDy%)ƉftyM)΃@ ƒ M ufD~%)fL~u)Ή ƒu ʋMfDy %)Ɖfty M)΃M fDy M%)E܉΋M !Ѓ!ʋM ЋUAM;}jMufy)֍W;UfDyU%)‰ЍW@ ;UfTyM)щʍO ;MUMfDz%)MMMMMM ΍O;MfDzM%)MMMMMM ΍O;M}ZfDz M%)MMMMMOE ;M})fDz M%)MUUMMU ֋U̹))ы}؋U!!} u!ƒ"7 ˆ7uE}ƋUM WuMP^_]ÐUWV,UE UE}EE9EEE1;M}Iu؋}܉u U(A;M}19U 4UEȊU %;~1U$A;M|АMEA}܋uUM׋ME؉}9M|,^_]ËE u$>E(UU}ҋ8~݋uMMЉuEE9EuMU1)‰ЊU!ƒ! 1AU)‰ЊU!ƒ! 1VAU)‰ЊU!ƒ! 1VAU)‰ЊU!ƒ! 1VAU)‰ЊU!ƒ! 1VAU)‰ЊU!ƒ! 1VAU)‰ЊU!ƒ! 1VAU)ƒЊU!ƒ! ˆVUԃUԋU9UM9M}5uԋU1MԊU)‰֊U!! u1AuM9|ˋuM΋EuuMЉu,^_]Ít&UWVLUE UE}EE9EEEEM9M}|}ċuȉ}uM u΋}M11ҋ} ;~E$ABF} 1;E(ABF~}ЋuGU}ЋEuU9E|}ԋMGEċuȋU։}ԋMEĉu9MLL^_]ËM }(u$E}$E M܉UM(pWu}AuUE؉}~W}Ủ}1;}uM&U1)‰ЊU!ƒ#E 1AU)‰ЊU!ƒ#E 1VAU)‰ЊU!ƒ#E 1VAU)‰ЊU!ƒ#E 1VAU)‰ЊU!ƒ#E܃ 1VAU)‰ЊU!ƒ#E 1VAU)‰ЊU!ƒ#E 1VUA)‰ЊU!ƒ#E ˆV;};}}[vEu1ɊU E)Ή!ʃ!1 ʋMuMUD7)E؉!! uT7;}|}UMuM}UaL^_]ÉUWVPUE UE}EE9EEEEM9M}a}u}uU u֋}U1} 1;b}(BAF~}ċuGE}ċMuE9M|UȋuBM}EUUuM9UgP^_]Ð}$뜋M u9vM(Uu$}} UԋU(wM$uzu }ЋU$}(EANE܉MBwME؉u̅~}E E}1;}uMU1)‰ЊU!ƒ#E 1AU)‰ЊU!ƒ#E 1VAU)‰ЊU!ƒ#E 1VAU)‰ЊU!ƒ#E 1VAU)‰ЊU!ƒ#E 1VAU)‰ЊU!ƒ#E 1VAU)‰ЊU!ƒ#E 1VUA)‰ЊU!ƒ#E 1VAU)‰ЊU!ƒ#E 1VA U)‰ЊU!ƒ #E 1V A U)‰ЊU!ƒ#E 1V A U)ƒ ЊU!ƒ#E ˆV ;}|;}uM1U7)Eԉ1!! u7EuU܊LE)Ή!ʃ!1 ʋMTuMU؊D7)Ẻ΋M!! T;}{}UMEM}UP^_]Ðt&UWV\UE UE}EE9EEEEM9M}a}u}uU u֋}U1} 1;b}(BAF~U}BuUM}u9M|UMB}EuUM}E9Mg\^_]Ð}$뜋u M$9u(M U}܋Qu$M(ẺU~Au M$}؉EȋVyu(M U}ԋFQ u$M(EĉU~ A }ЉEEuEvxE}E1;}uMU1)‰ЊU!ƒ#E 1AU)‰ЊU!ƒ#E 1VAU)‰ЊU!ƒ#E 1VAU)‰ЊU!ƒ#E 1VAU)‰ЊU!ƒ#Ẽ 1VAU)‰ЊU!ƒ#E 1VAU)‰ЊU!ƒ#E 1VUA)‰ЊU!ƒ#E ˆV;};}Eu1ɊU܊ E)Ή!ʃ!1 ʋMuMU؊D7)Eȉ1!! uT7EuUԊLE)Ή!ʃ!1 ʋMTuMUЊD7)E΋M!! T}UMuM}U\^_]Í&UWV8} M$uUuE 7E9}(IM؋I҉MMUЃM܋U,EEE},MU)M9U~UE1E9E}OU,ڃUĊMĸUE ‹MEU1ҊẺ)ыUJ!U ϋMAMM9M|M؋U!!׊U }!}" шEE9E~M}1)1A)ǁ@ 1A)lj 1A)lj 1A)lj 1A)lj 1A)lj 1A)lj} ‹E!Ѓ!} ЋU1A)1A )ǁ@ 1A )lj 1A )lj 1A )lj 1A )lj 1A)lj 1A)lj} ‹E!Ѓ!} ЋUDEUU܉E9UM})M}19})1D9)ȋM@ 1D9)lj} 1D9)ȋM 1D9)lj} 1D9)ȋM 1D9)lj} 1D9)E؉ϋM !Ѓ!ʋM ЋUAUMUE9E}yE}U1GEȉ}Uȉ)кI!ЋU ‰UU9|ˋ}AUUظ!Mԋ}!ω}}M ʋM!ƒ"9 ˆ9MEȋUMEMЉM*8^_] UWVTU$E Euu}%M(pB ~U%PA ~UEE#M,EuU}E M,EE0xu}\EM,ŰE)ʉU9E~E̋}EE}9}M܋},ϸ)Eȉ}Mȋ}M1 ыUME})})!Mȋ} 1}}܊DU)‰ЋU!M U܋EĉM9EjU9U}KM܋U,EE)ы}eU1 ЉEE܊ 8@)΋}M!։E uM}MEU}U!!ʉ}EM ʋ}M!" ˆEU9UjM܋}1})1A)ǁ1@A )1A })1A )1A })1A ) }1A)NjE }!Ѓ!} ЋU1}A)1A )ǁ1@A )1A })1A  ) }1A )1A )1A })NjE }!Ѓ!} ЋU؈DE܃U؉E܋U9UMЋ})M܋}19})1D9EE+E@ 1D9E+E 1D9EE+E 1D9E+E 1D9EE+E 1D9E+E 1D9}EME)E ʋM!Ѓ!} ЈUG}؉U܋}9}EE}9}mt&}܋M1U)ȊM1!} ‹EUU܊L)ωU܋MԉIM!} lj}}ԃ9ʉ}~E9E}+}E1ҊMԊ8)֋UJ!Uԋu uuMUA}M!UM!}Mu !ƒ" ˆMU}EMMUT^_]Ðt&UWVtE}E$}M} u}U(8E~E$Ix~ }I$}x~ }$I}:~E$Iz~ }I$}z~ E $IE IMQuUE UEEHxM}M,}0U}܋EM}},U؉EЉM̅EU,M)ыUM9U~UEEEE9E&EU},M׸)UȉM}M ‹MU1UEM)ȹ)M}M!ЋM UM1DEE})})!M} 1}ċ}DU)‰M!} Mĉ}M9M-}9}}iM,EكMt&MUȋM 1UȋUAEMU)Љu!} NjE}ĉU}UOE}9|U}ċM!U!EM܋}ȋU܋M ЊUȉE"E! Ј }U)E}U}܋EMU}EЉM̋M9MM}v1})1A)ǁ1@A })1A )1A })1A })1A ) }1A)NjE }!Ѓ!} ЋU1UA)1A )ǁ1@A })1A })1A  ) }1A )1A })1A )NjE }!Ѓ!} ЋUD}1UA)1A)ǁ1@A )1A })1A })1A )1A })1A })NjE }!Ѓ!} ЋUD}UU}9UU9UME)ʃUEEE9Ev}E1ɉ 8)ʊMM!1D9EEM)ȋMIMM!M} 1DME}E)M!} ƒE уUEM}9Ue}9}E}1ҊM)ֺ!M FM;u}mu1ɉЊL7})ω}MMMI}E!} ȃE;}}1}M1D7)MMuuuE! uEU )tkUE}!Ѓ!׉E}܋ME܉ }MM!u"7 ш 7UMEuMUMt^_]ËUEM!Ѓ!щEM܋U}܋M }ME:UEM!Ѓ!щE؉MԋUEԋM M؊E؈D:UEM!Ѓ!щEЉM̉MЋE MЊM!u"T> шL>uϋQ;Vu~QA~tZW hp1:Dž9|/rD|ȋB9}E}1}=VTȉD렋+uȍMVQRWVQRP!l1;|R|DA;}=Uu<|==}DDĉ|ȉD뺃juȍ}VWQRPVWQ`1;|AtDA;},E}4=хtȉD˃MȍuQVWRQVWP {Dž9}{T؋DȈ198@;~=@9|PWQVffUȍuRVWQRVWQ4}ȍuWVQRPWVQ ut&UȍMRQVPWRQV!sEPURVWQRVPI/t&8@=~ZDDEDD}ȍMWQRVWQRP>uȍ}VWQRPVWRMȍUQRWVPQRW}WuVQRPWQV'}ȍMWQRVWQRPNMȍUQRPVWQRV'UȍuRVWQRVWPKUR}WPQRVWQ?"MQuVWRQVPRC)^&'UWV0}EU9U}uEE1;M}:u؋E܉uE̋Ű}ʍ4}Uʊ} 1҈;}$A;M|ҋMUAE܋} uMMU؉E9M|0^_]ËM E$u9}8~MEuUMEuUЍt&Eu9uNuM&1ЉEȋE+EE!ŠEȃ! 1QЉEȋE+EE!ŠEȃ! 1VQЉEȋE+EE!ŠEȃ! 1VQЉEȋE+EE!ŠEȃ! 1VQЉEȋE+EE!ŠEȃ! 1VQЉEȋE+EE!ŠEȃ! 1VQЉEȋE+EE!ŠEȃ! 1VQЉEȋE+EE!ŠEȃ! ˆVUԋEU9Eu9u};&uԋM1M)΋M!! uԈFUu9|̋uUM EMЉuU=0^_]ÍUWVD}EU9UEEEE9EűUЉuȉUċMu΋UM1ъЈUU %;~!EGAF$ЈUU %;ߋU$GAF~܋MUAuȃM}Uĉu9}|EU@}ЋM uEEỦ}9EAD^_]Ë} u$U} U$MG}rEUu҅U~ȍzuME}܉u؉MԉE1;}Fu؋MԊ1ЉEE+EE!ŠE#E 1QЉEE+EE!ŠE#E 1VQЉEE+EE!ŠE#E 1VQЉEE+EE!ŠE#E 1VQЉEE+EE!ŠE#E 1VQЉEE+EE!ŠE#E 1VQЉEE+EE!ŠE#E 1VQЉEE+EE!ŠE#E ˆV;};}}VEԋu1Ɋ)΋M!ƒ!ȋM 1uԋMT7)E΋M!! T;}|uԋU؋M }MuԉU5D^_]ÍUWVL}EU9UEEEE9E}euUĉuUM}ϋUM1ъЈUU %;aU$FAG~܋MUAuM}Uu9}|EU@}ċM uEEU}9EcL^_]ÊE렋} u v} u$MԉEO} Mu$M$UWF}qE܉Uu؅~uԋ} EUuЉ}̉EȉU1;}űMȍt&1ЉEE+EE!ŠE#E 1QЉEE+EE!ŠE#E 1VQЉEE+EE!ŠE#E 1VQЉEE+EE!ŠE#E 1VQЉEE+EE!ŠE#E 1VQЉEE+EE!ŠE#E 1VQЉEE+EE!ŠE#E 1VQЉEE+EE!ŠE#E 1VQЉEE+EE!ŠE#E 1VQ ЉEE+EE!ŠE #E 1V Q ЉEE+EE!ŠE#E 1V Q ЉEE+EE!ŠE#E ˆV ;}(;}~Eȋu1Ɋ)΋M!ƒ!ȋM 1uȋMT7)E܉1!! üT7EȋuT)΋M؉!ƒ!ȋM ˆT;}|uȋŰM }MuȉUmL^_]Ít&'UWVX}EU9UEEEE9E}fuUuUM}ϋUM1ъˆEE ;aU$FAG~ۋUuBEU}uE9}|MEA}U uMME}9MbX^_]ÊE렋}$u E }$MpU܋M WuU؋u$U$AM ~Er }ԋUA }EuЉU~zMUE}̉MȉUĉEEu9uuȋMĊ}1)lj}!ƒ! 1Q})lj}!ƒ! 1VQ})lj}!ƒ! 1VQ})lj}!ƒ! 1VQ})lj}!ƒ! 1VQ})lj}!ƒ! 1VQ})lj}!ƒ! 1VQ}Ѓ)lj}!ƒ! ˆVEEU9UM9M}Eċu1Ɋ)΋M܉!ƒ!ȋM 1uċMT7}Ȉ)E؉1!! uT>Eċ}TEȈ)ϋMԉ!!1 uT}ċEЊT>}ыu)Ήu!ʃ! ʈT7uċ}ȋM UMuĉ}X^_]Í&UWV u U M>u$}>uɉuu҃UuM܋uuuu䍶EE9EVuM䍴&f‰E؋E+EE!‹E؃! ffQ‰E؋E+EE!‹E؃! fVfQ‰E؋E+EE!‹E؃! fVfQ‰E؋E+EE!‹E؃! fVfQ‰E؋E+EE!‹E؃! fVfQ ‰E؋E+EE!‹E؃! fV fQ ‰E؋E+EE!‹E؃! fV fQ‰E؋E+EE!‹E؃! ‹EfVUU9Eu9u}5MuEfNM)΋M!! ufpFu9|ˋuEƋMUM܉u4Qu: ^_]ÍvUWV,E U$M u8}:}xB}UEEUEԅUEɋEU܉EM؉uЉ1;}NuMf‰E̋E+EE!‹Ẽ#E ffQ‰E̋E+EE!‹Ẽ#E fVfQ‰E̋E+EE!‹Ẽ#E fVfQ‰E̋E+EE!‹Ẽ#E fVfQ‰E̋E+EE!‹Ẽ#E fVfQ ‰E̋E+EE!‹Ẽ#E fV fQ ‰E̋E+EE!‹Ẽ#E fV fQ‰E̋E+EE!‹Ẽ#E fV;};}}ZE܋MfxE)!!ƋE uf~ufTx)΋M!ƒ!ȋM fTy;}|E܋Mȋu}MЍ~E܉U(,^_]ÐUWV0U M$Eu:}@9E܉}z}y}z}U yM}U M ME؉MԋEủEЍ&1;}uԋMf‰EȋE+EE!‹Eȃ#E ffQ‰EȋE+EE!‹Eȃ#E fVfQ‰EȋE+EE!‹Eȃ#E fVfQ‰EȋE+EE!‹Eȃ#E fVfQ‰EȋE+EE!‹Eȃ#E fVfQ ‰EȋE+EE!‹Eȃ#E fV fQ ‰EȋE+EE!‹Eȃ#E fV fQ‰EȋE+EE!‹Eȃ#E fVfQ‰EȋE+EE!‹Eȃ#E fVfQ‰EȋE+EE!‹Eȃ#E fVfQ‰EȋE+EE!‹Eȃ#E fVfQ‰EȋE+Eȃ E!‹Eȃ#E fV;};}EЋMfxE)!!ƋE uf~ufTx)΋M!ƒ!ȋM ‹EfTyMfTxE)!! ufT~;}{EЋM uM̍u$}>uɉuu҃UuM܋uuuu䍶EE9EvuM䍴&f%E؋E+EE!‹E؃! ffQ%E؋E+EE!‹E؃! fVfQ%E؋E+EE!‹E؃! fVfQ%E؋E+EE!‹E؃! fVfQ%E؋E+EE!‹E؃! fVfQ %E؋E+EE!‹E؃! fV fQ %E؋E+EE!‹E؃! fV fQ%E؋E+EE!‹E؃! ‹EfVUU9Eu9u}:MuEfNMց)΋M!! ufpFu9|ƋuEƋMUM܉u4Qu ^_]Í&'UWV,E U$M u8}:}xB}UEEUEԅUEɋEU܉EM؉uЉ1;}nuMf%E̋E+EE!‹Ẽ#E ffQ%E̋E+EE!‹Ẽ#E fVfQ%E̋E+EE!‹Ẽ#E fVfQ%E̋E+EE!‹Ẽ#E fVfQ%E̋E+EE!‹Ẽ#E fVfQ %E̋E+EE!‹Ẽ#E fV fQ %E̋E+EE!‹Ẽ#E fV fQ%E̋E+EE!‹Ẽ#E fV;};}}dE܋MfxEց)!!ƋE uf~ufTxс)΋M!ƒ!ȋM fTy;}|E܋Mȋu}MЍ~E܉U,^_]Í&UWV0U M$Eu:}@9E܉}z}y}z}U yM}U M ME؉MԋEủEЍ&1;}uԋMf%EȋE+EE!‹Eȃ#E ffQ%EȋE+EE!‹Eȃ#E fVfQ%EȋE+EE!‹Eȃ#E fVfQ%EȋE+EE!‹Eȃ#E fVfQ%EȋE+EE!‹Eȃ#E fVfQ %EȋE+EE!‹Eȃ#E fV fQ %EȋE+EE!‹Eȃ#E fV fQ%EȋE+EE!‹Eȃ#E fVfQ%EȋE+EE!‹Eȃ#E fVfQ%EȋE+EE!‹Eȃ #E fVfQ%EȋE+EE!‹Eȃ#E fVfQ%EȋE+EE!‹Eȃ#E fV;};}EЋMfxEց)!!ƋE uf~ufTxс)΋M!ƒ!ȋM ‹EfTyMfTxEց)!! ufT~;}lEЋM uM̍U}M~ڋM} EuUMEu}܉UEU9U'}uE)Ѓ!ȋM!ƒ! ‹EV)Ѓ!ȋM!ƒ! ‹EWV)Ѓ!ȋM!ƒ! ‹EWV )Ѓ!ȋM!ƒ! ‹EW V)Ѓ!ȋM!ƒ! ‹EWV)Ѓ!ȋM!ƒ! ‹EWV)Ѓ!ȋM!ƒ! ‹EWV )Ѓ!ȋM!ƒ!ȋM ƒW MċE9E}9}}>MċE}4E)!ЋU!ƃ!} Ɖ4Gu}9|ȋEMȋ}UM4Eu`H^_]Ë}ME }UuM}؉UԉEЉuEu9u'}؋uԋE)Ѓ ȋM!ƒ! ‹EV)Ѓ ȋM!ƒ! ‹EWV)Ѓ ȋM!ƒ! ‹EWV )Ѓ ȋM!ƒ! ‹EW V)Ѓ ȋM!ƒ! ‹EWV)Ѓ ȋM!ƒ! ‹EWV)Ѓ ȋM!ƒ! ‹EWV )Ѓ ȋM!ƒ! ‰WU UE9E}9}}8MEԋu&}Ѓy ;u~;u|?EE9E~UEUT;M~UUL;}~XM| ;u}[E|}L| ;UEU܉U9UUE؉D;MEUԉT;}M}Љ| ;u|u}UEM4}u=l^_]ÐUWVS[R2M uttQTT;~t e[^_]Ít&Q ;V utt;>uϋQ;Vu~QA~ttZttJth1:1;U<@;|Džt9t|,DtBt9t]t}1DEH^_]Ðt&UWVP}EU9UEEEE9E}euUĉuUU}׋MU1ʊ ȈMM %;~aM$FBG~܋UEBuU}Eu9}|UME B}ċuUMM}9McP^_]ÊE렋}M 1}$UԋU uu$JEME M$~}܋Pq}Uu؅~MԋU uEMЉỦuȉEE}9}}̋uȊU1)‰ЊU!ƒ!1 ʈNU)‰ЊU!ƒ!1 ʈWNU)‰ЊU!ƒ!1 ʈWNU)‰ЊU!ƒ!1 ʈWNU)‰ЊU!ƒ!1 ʈWNU)‰ЊU!ƒ!1 ʈWNU)‰ЊU!ƒ!1 ʈWNU)‰ЊU!ƒ!1 ʈWNU)‰ЊU!ƒ!1 ʈWUN )‰ЊU!ƒ!1 ʈW N U)‰ЊU!ƒ!1 ʈW N Uȃ )‰ЊU!ƒ! ʈW UEЃ U9Egu9u&uEȋ}1Ҋ )׊U}!!u ʈ>1ҋEȋ}L)׊U܉}!!u ʈT>1ҋEȋ}LË)׊U؉}!! ʈTMԉ}9mUȋM̋u }MUȉMP^_]Í&UWVX}EU9UEEEE9E}euUuUU}׋MU1ʊ ȈMM %;~aM$FBG~܋MuAEM}uE9}|UuB}M EUNjUu}9UcX^_]ÊE렋} u$} u$M܉UGV} u$EU؋OFM} M$EԋW }q UMuM~QuEMỦuȉEĉME}9}}ȋuĊU1)‰ЊU!ƒ!1 ʈNU)‰ЊU!ƒ!1 ʈWNU)‰ЊU!ƒ!1 ʈWNU)‰ЊU!ƒ!1 ʈWNU)‰ЊU!ƒ!1 ʈWNU)‰ЊU!ƒ!1 ʈWNU)‰ЊU!ƒ!1 ʈWNUȃ)‰ЊU!ƒ! ʈWMME9Eu9uEċu}1Ҋ )׊U܉}!!u ʈ>1ҋEċ}L)׊U؉}!!u ʈT>1ҋEċ}L)׊Uԉ}!!u ʈT>1ҋEċ}LEȈ)׊UЉ}!! ʈTMċ}ȋu UMMĉ}X^_]Ðt&UWV } EU 7M}$uɋ7Euu}҉u}uU܉uM؍vEE9E.}u&fU)‰ЋU!ƒ! fUfN)‰ЋU!ƒ! fWUfN)‰ЋU!ƒ! fWUfN)‰ЋU!ƒ! fWUfN)‰ЋU!ƒ! fWUfN )‰ЋU!ƒ! fW UfN )‰ЋU!ƒ! fW UfN)‰ЋU!ƒ! ʋMfWMU9U}9}}?&EMufUċ}M4})!!σ!΋M uĉ&}Ѓy ;u~;u|?EE9EUEUT;MUUL;}XM| ;u}[E|}L| ;U~EU܉U9U~UE؉D;M~EUԉT;}~M}Љ| ;u|u}UEM4}u=l^_]ÐUWVS[Â2M tutuy4;~te[^_]Ðt&A ;F u9|;>u׋yxx;FuFQEyDžxp9}@U<$}$9PFx9||tZ||| xxxZ11;x||ȉ|؉|@;x}1Ҋ}t1Ҋ}pHU4Llt|Lp|~%DlLtdT݋4xxxUMuȍ}PPRQVWR4QVPRW1;x|"DDȉD؉DB;xEM<4\M}`4 X|`D~DȉD؉D띋\DDX}zDfxaxx}VUVM؍uVWRQV4PWRQVtLh1;x|tȉt؉t@;xM}4 }T}@Pdt@Tt~%DPN@|xT݃xQx"xUWuWMȍ}RVQWR4VPQWR)jDžhx9hhhT|ȉ01g9}0g0@9|싵h|90@9~h=TG0G8@=~鋵hxFh9hVVRQMQ=xx@}UMȍuPPWRQVW4RQVWR#0xxuQUQ}؍MQVRWP4QVRWQN뛃xxuRMR}ȍUVQWRV4QWRVQ2;xx VVuVEP}WURV4QWRVP1AH||ȉ|؉|LLȉL؉LFDD,Mu}ȍUPPQVWRQ4VWRPQKMRuR}ȍUQVWRQ4VWRQV }UMȍuPPWRQVW4RQVWPUMuȍ}PPRQVWR4QVWRP/y}UuȍMPPWRVQW4RVQWP(3RRURMQuV}WR4QVWRQ7M}UȍuPPQWRVQ4WRVQP=+QQEPuV}WMQR4VWPQRW4aPP}WURMQuVW4RQVPW!;UWV@}EU9UEE1;M}SuċEȉuE },A;M}8U}ʍ4}Uʊ}$1҈;|ҋ} ;~1U(A;M|ȋMUAuȋ} EMƋMUĉu9M|@^_]Ë} E$M,E0}(UuuUE9u}ʋEuME܉uЉMEU9UMЋ}̉M؉}ԍt&uԋU>%)ъU!ʃ֋U)‰ЊU!ƒ! !ϋE MԋuUy%)!ʃ֋U)‰ЊU!ƒ! !ϋE PuԋU~%)ъU!ʉ֋U)‰ЊU! փ!NjE! PMԋuUy%)!ʃ֋U)‰ЊU!ƒ! !ϋE PuԋU~%)ъU!ʃ֋U)‰ЊU!ƒ! !ϋE PUMԋuy%)!ʃ֋U)‰ЊU!ƒ! !ϋE PuԋU~%)ъU!ʃ֋U)‰ЊU!ƒ! !ϋE PMԋuUy%)!ʉ֋U)‰ЊU! փ!ǃ!ϋE P}Uԃ}u܉UԉE9u}9}}\UE̋u<U%)!ʃ֋U)‰ЊU!ƒ! !ϋEЋU BU}9}|UM̋E B}ЋuUM̋M}9M@^_]Ív'UWV\}EU9UEEEE9E}uuUuU1u,GBA~3UuB}UMu}9M}6UMыuU1u$E1E;|u ;~u(롊E럋UE BM}uUEM}9ES\^_]ËM E(U$E9}E },2uM܋u$HUԋU(MM,~B}Uqҋ}E؉uЉU9}}zuE}̉uEEU9UEMEȉMċMċuU9%)!ʃ֋U)‰ЊU!ƒ! ! }ȉuċU~%)ъU!ʃ֋U)‰ЊU!ƒ! !ϋU BMċuUy%)!ʉ֋U)‰ЊU! փ!! }ȉGuċU~%)ъU!ʃ֋U)‰ЊU!ƒ! ! }ȉGMċuUy%)!ʃ֋U)‰ЊU!ƒ! ! uȈVUEx%)ъU!ʃ֋U)‰ЊU!ƒ! ! }ȉWMċuUy%)!ʃ֋U)‰ЊU!ƒ! ! uȈFMċUy%)ъU!ʉ֋U)‰ЊU! փ!ǃ! uȈF}Uă}uȋủU9uU9UMEU<%)ъU!ʃ֋U)‰ЊU!ƒ! ! }u>U|u%UЉ)!ʃ֋U)‰ЊU!ƒ! ! }uD7}U9U[MuE AƋ}UM׉uu}9u\^_]Ív'UWVl}EU9UEEEE9E}cuUuUMEUu1u$E1E;}au,GBA~ދuUF}uMU}9M|UMBu U}UEMU9Eel^_]Ëu ;~u(땊E듋EM$U <@M,2}u}(1M$uԋu(} UȋQEU܋GU ~E}ċrE,}$U(HuGrMЉE؉uEM,E9Ey}cUu MUuME}9}OEMEMuU>%)ъU!ʃ֋U)‰ЊU!ƒ! !ϋU Mu܊Uy%)!ʃ֋U)‰ЊU!ƒ! ! }GuU~%)ъU!ʉ֋U)‰ЊU! ֋U!! BMuUy%)!ʃ֋U)‰ЊU!ƒ! ! }GuU~%)ъU!ʃ֋U)‰ЊU!ƒ! !ϋU BŰMuy%)!ʃ֋U)‰ЊU!ƒ! ! }GuU~%)ъU!ʃ֋U)‰ЊU!ƒ! !ϋU BMu܊Uy%)!ʉ֋U)‰ЊU! փ!ǃ! }GuU~%)ъU!ʃ֋U)‰ЊU!ƒ! !ϋU BMuUy %)!ʃ֋U)‰ЊU!ƒ! ! }G U܋u~ %)ъU!ʃ֋U)‰ЊU!ƒ! !ϋU B Mu؊Uy %)!ʃ֋U)‰ЊU!ƒ! !ϋU B M} Mu}U9u}9}MEuU<%)!ʃ֋U)‰ЊU!ƒ! ! }u>EU|%)ъU!ʃ֋U)‰ЊU!ƒ! ! }uD>U|u%Ủ)!ʉ֋U)‰ЊU! ֋U!! uDu}9}MEAu MUMы}EM9}il^_]ÉUWV|}EU9UEEEE9E}euUuUuEUM1ʊ 1ȋM$;}aM,GBF~܋MuAUM}uU9}|}uGEM U}Ћ}uE9}c|^_]ËM ;~M(땊됋} u$}(u,ME܋} u$UM̋GV}(u,EU؋OF} u$MEȋWN}(u,UMԋGVEUċ} u$O F M}(M,EЋW Eq UMuMDyuEU}uEUEM9MNEUEUMu܊U9%)!ʃ֋U)‰ЊU!ƒ! ! }uU~%)ъU!ʃ֋U)‰ЊU!ƒ! !ϋU BMuԊUy%)!ʉ֋U)‰ЊU! փ!! }GuU~%)ъU!ʃ֋U)‰ЊU!ƒ! !ϋU BMu܊Uy%)!ʃ֋U)‰ЊU!ƒ! ! }GU؋u~%)ъU!ʃ֋U)‰ЊU!ƒ! ! }GMuԊUy%)!ʃ֋U)‰ЊU!ƒ! ! uVEUx%)ъU!ʉ֋U)‰ЊU! փ!ǃ! uV}M}uuM9uU9U%uMU<1%)ъU!ʃ֋U)‰ЊU!ƒ! ! }u>EU|u%)!ʃ֋U)‰ЊU!ƒ! ! }uT>EU|%)ъU!ʉ֋U)‰ЊU! փ!! }uD>U|u%U)!ʃ֋U)‰ЊU!ƒ! ! }uD7E}u UME}O|^_]Ít&UWV,EU u$E:U(}u,M:U }U MUE9UuMEu܉MЉEE}9}fEЋỦE؉UԐEԋUuf8)‰)U!‹u!!ǃ !ϋU Mԋ}f7ufylj)ыU)‰!U!ƒ !NjU!ϋE frUfxu)‰)uU!!΃ !NjU!ϋM }fwufylj)ыU)‰!U!ƒ !NjU!ϋE frUfxu)‰)U!‹u!!ǃ ! }fwuMԋUfy lj)ыU)‰!U! ֋U!NjE! }fw ufx )‰)U!‹u!!ǃ !ϋEԋM Ufq ufxlj)ыU)‰!U!ƒ !!ϋM }fquԃ}M؉uԋM9MU9U}OEűUf}܋xB}QEUЋM UEҋ~UU}؉M E9U EMuẺMuE}9}uUuȉUĐEċUuf8lj)ыU)‰!U!ƒ !NjU!ϋM f2Ufyu)‰)U!‹u!!ǃ !ϋE Ufpuf~ulj)ыU)‰!U!ƒ !ǃU!ϋM fqufzU)‰)U!‹u!!ǃ !ϋE MfpUfyulj)ыU)‰!U!ƒ !NjE! fpuUfz U)‰)uU!! ֋U!!ϋM fq uf~ ulj)ыU)‰!U!ƒ !NjU!ϋE Mfp ufy)‰)U!‹u!!ǃ !ϋU Mfr}ċuu}ĉU9M}9}MuUf}ԋx}z}܋y}ċ~}ЋxB}E؋yF}uE̍vEEM UM MU9M@pUMuUMEE9EM}M}uUf>ulj)ыU)‰!U!ƒ !NjU!ϋM f2Ufyu)‰)U!‹u!!ǃ !ϋE Ufpuf~ulj)ыU)‰!U!ƒ !ǃU!ϋM frUfyu)‰)U!‹u!!ǃ !ϋE Ufpuf~ulj)ыU)‰!U!ƒ !NjU! frUMufy )‰)uU!! ֋U!NjE! fp uf~ ulj)ыU)‰!U!ƒ !NjU!ϋM fr Ufyu)‰)U!‹u!!ǃ !ϋE Ufpuf~ulj)ыU!)‰ЃU!ƒ! !ϋU MfrUfyu)‰)U!‹u!!ǃ !ϋE fpU܋uf~ulj)ыU)‰!U!ƒ !NjU!ϋM frUfyulj))‰ЋuU!ƒ!!ǃ !ϋE }Ufp u}uE9U}9}UMuf}Ћx}z}܋y}~}̋x}z}؋y}~}ȋx }y B MU EԋF }EċEU UEM|MEuEMEut&E}9}uUuUEUuf8lj)ыU)‰!U!ƒ !NjU!ϋM f2Ufyu)‰)U!‹u!!ǃ !ϋE Ufpuf~ulj)ыU)‰!U!ƒ !ǃU!ϋM fqufzU)‰)U!‹u!!ǃ !ϋE MfpUfyulj)ыU)‰!U!ƒ !NjE! fpu܋Ufz U)‰)uU!! ֋U؃!!ϋM fq uf~ ulj)ыU)‰!U!ƒ !NjU!ϋE Mfp ufy)‰)U!‹u!!ǃ !ϋEU }fpUM}E9M}9} UMufu%)ыU)‰!U!ƒ !NjU!ϋEԋM f1ufx%)‰)U!‹u!!ǃ !ϋU }fwuf~u%)ыU)‰!U! ֋U!NjE!ϋM fqufx%)‰)U!‹u!!ǃ !ϋU }fwuf~u%)ыU)‰!U!ƒ !!ϋM fqUEԋufx %))‰ЋuU!ƒ!!ǃ! U}fw uf~ u%)ыU)‰!U!ƒ !NjU!ϋEԋM fq ufx%)‰)uU!! ֋U!!ωU M܋}fwuԉ}؃9Mu}9}}SEM̋Uuf}܋xB}QEUЋM UEҋ~UU}؉M E9U5EMuẺMuE}9}uUuȉUĐEċUuf8%)ыU)‰!U!ƒ !NjU!ϋM f2Ufyu%)‰)U!‹u!!ǃ !ϋE Ufpuf~u܉%)ыU)‰!U! ֋Uă!!ϋM fqufzU%)‰)U!‹u!!ǃ !ϋE MfpUfyu܉%)ыU)‰!U!ƒ !NjE! fpuUfz U%))‰ЋuU!ƒ!!ǃ! ֋M Ufq uf~ u܉%)ыU)‰!U!ƒ !NjU!ϋE Mfp ufy%)‰)uU!! ֋Uȃ!!ϋM }fru}ĉuU9M}9}MuUf}ԋx}z}܋y}ċ~}ЋxB}E؋yF}uE̍vEEM UM MU9M|pUMuUMEE9E M}M}uUf>uԉ%)ыU)‰!U!ƒ !NjU!ϋM f2Ufyu܉%)‰)U!‹u!!ǃ !ϋE Ufpuf~ủ%)ыU)‰!U! ֋U!!ϋM frUfyu%)‰)U!‹u!!ǃ !ϋE Ufpuf~uЉ%)ыU)‰!U!ƒ !NjU! frUMufy %))‰ЋuU!ƒ!!ǃ! ֋E Ufp uf~ uԉ%)ыU)‰!U!ƒ !NjU!ϋM fr Ufyu܉%)‰)uU!! ֋U؃!NjE! fpuf~ủ%)ыU)‰!U!ƒ !NjU!ϋM frUfyu%)‰)U!‹u!!ǃ !ϋE fpU܋uf~uЉ%)ыU)‰!U!ƒ !NjU!ϋM frUfyu؉%)‰)U!‹u!!ǃ !ϋE U}fp u}uE9U}9}UMuf}Ћx}z}܋y}~}̋x}z}؋y}~}ȋx }y B MU EԋF }EċEU UEMMEuEMEut&E}9}7uUuUEUuf8%)ыU)‰!U!ƒ !NjU!ϋM f2Ufyu܉%)‰)U!‹u!!ǃ !ϋE Ufpuf~uȉ%)ыU)‰!U! ֋U!!ϋM fqufzU%)‰)U!‹u!!ǃ !ϋE MfpUfyuЉ%)ыU)‰!U!ƒ !NjE! fpu܋Ufz U%))‰ЋuU!ƒ!!ǃ! ֋M Ufq uf~ uȉ%)ыU)‰!U!ƒ !NjU!ϋE Mfp ufy%)‰)uU!! ֋U!ǃ!ϋE U}fpM}E9M}9}UMufM;Uы}Uĉ G}M9M}M}9}ԋ}MċEًUM} AMMčUEEE;U| E;UЋuċ}E؋T;U| E;UЋUĉD}$u E(OV},UpMWM }$uUu(W A },EUN G 1҉ME;U~jvu;EƋEĉt;M|vE;MȋuĉD ;}|oE;}MĉDE9E|h}E9}EMĉD;U]uDL |;Etu}uċED;M}EċML ;}}uM9u}|}Eċut;U~;U}@uEL;M| E;Mȋ}ĉDEL E;M| E;MȋMĉD MU EċuM<}USt^_]ÐUWVS[e2M tEtuyd;~te[^_]Ðt&A ;F u9;>u׋y;FuFQEyDžp9}@U<T}T9PF9|tZna X?11;|&DDD@;}l}1ҋu}4҉lxt~"Dl|둉TdT }UȍuWRVQdWRVQPK1;|#xDDD@;}W}U4} t|tDt}tD렃U- u}ȍUVWRQdVWRQV]T-Ln1;|5txPt~^DpET@;}A}U4} p~DDD뼉L뤃F1 u}ȍUVWRQdVPWRQuaDž|9||||ȉ`19}`0@9|||؋|d9T{`{8@;d~=`0@=~|G|9|\PVQMQΧ }UȍMWRQVdWRQVP0k }UȍuWRVQdWPRVQ랃  u}ȍUVWRQdVWRQPe&AB }WuVMQRdPVWRQ2vDD M}ȍUQWRVdQWPRQH u}ȍUVWRPdQVWRQ!A MUȍuQRVWdQRVPQ }UȍuWRVPdQWRVQ? M}ȍUQWRVdQWRVPx u}ȍUVWRQdVWRQP5 }WuVMQRdWVPQRv* URMQuVWdRQVPRc- }WuVMQRdWPVQR&lt&'UWV<}EU9UEE1;u}HM̋EЉME}EM1҉}}9ˆE$;| } ;E(UF ;u|ċ}M GỦ}u}MỦ}9M|<^_]Ëu }$M(E1EUu~ڋ}EUM}܉E؉UԉMEM9Mg}؋uԊU1ȉE)ЋU+U ЋU!! 1NUȉE)ЋU+U ЋU!! 1ONUȉE)ЋU+U ЋU!! 1ONUȉE)ЋU+U ЋU!! 1ONUȉE)ЋU+U ЋU!! 1OUNȉE)ЋU+U ЋU!! 1ONUȉE)ЋU+U ЋU!! 1ONUȉE)ЋU+U ЃU!! OMȋU܃M9Uu9u}A}ȋE1ҋu ʉ)u)֋U u!! GE}9|ŋ}ԋM؋E UMĉ}ԉM<^_] UWVT}EU9UEEEE9EuċUȉuUUu֋MU1ʊEE$M;}%EGBF8EE$M; |ۋM E;ЋM(GBF~͐t&UMBuU}Mu9}m}MGU }u}EMĉ}9E%T^_]Ëu$U M >rU(M(}u}$rEu؋uEOEMu~N}uUMԉuЉ}̉UEU9Ui}Ћů1҈ʉUЋU)ЋU+U ЋU!! 1NUȉE)ЋU+U ЋU!! 1ONUȉE)ЋU+U ЋU!! 1ONUȉE)ЋU+U ЋU!! 1ONUȉE)ЋU+U ЋU!! 1OUNȉE)ЋU+U ЋU!! 1ONUȉE)ЋU+U ЋU!! 1ONUȉE)ЋU+U ЃU!! OMEԃM9E}9}}ruŰ} u1҈ʉ))U }u!! 7E̋UuL1ȉ))׋E U!! }Lu}9|E̋}ЋU MMẺ}T^_]Ðt&UWV`}EU9UEEEE9E}quUuUUu֋MU1ʊEE$M; |iE ; aM(GBF~ЋUMBuU}Mu9}|}MGU }u}EM}9EW`^_]ÊE렋M} u(IM$UU$Eȋ>M rU E}ԋA}(Eu܋E$rOU(MЉuxJu}؉M̅~Eȋ} UuEĉ}UuEM9M}uU1ȉE)ЋU+U ЋU!! 1NU܈ȉE)ЋU+U ЋU!! 1ONU؈ȉE)ЋU+U ЋU!! 1ONUȉE)ЋU+U ЋU!! 1ONU܈ȉE)ЋU+U ЋU!! 1OU؊NȉE)ЋU+U ЋU!! 1ONUȉE)ЋU+U ЋU!! 1ONU܈ȉE)ЋU+U ЋU!! 1ONU؈ȉE)ЋU+U ЋU!! 1ON UȉE)ЋU+U ЋU!! 1O U܊N ȉE)ЋU+U ЋU!! 1O N U؈ȃ E)ЋU+U ЋU!! O MUă M9U}9}uU} u1҈ʉ))U }u!! 7EU܋uL1ȉ))׋U u!! }L1ҋEu؊L}̈ʉ)u)֋U !!} Luȉ}9XEMU }MEM `^_]ÐUWVl}EU9UEEEE9E}quUuUUu֋MU1ʊEE$M; |iE ; aM(GBF~ЋUMBuU}Mu9}|}MGU }u}EM}9EWl^_]ÊE렋M u$E(>UU }܋}$M̋rU(GuJ} U$MȋM(wE؉uByu Eԉ}ċE$}(V H Uw MЉuUu҉uwV}uEUu}EEM9Mi}u1҈ʉUЋU)ЋU+U ЋU!! 1NU؈ȉE)ЋU+U ЋU!! 1ONUԈȉE)ЋU+U ЋU!! 1ONUЈȉE)ЋU+U ЋU!! 1ONU܈ȉE)ЋU+U ЋU!! 1OU؊NȉE)ЋU+U ЋU!! 1ONUԈȉE)ЋU+U ЋU!! 1ONUЈȉE)ЋU+U ЃU!! OUEU9E}9}Uu}܊ u1҈ʉ))U }u!! 1ҋEuL}؈ʉ))U }u!! L1ҋEuL}Ԉʉ))U }u!! L1ҋEuL}Јʉ))U u!!u L}Mu EM}Ml^_]ÍUWV$} M$E U7u9u(}E EM҉EuEMuEM܉UE}9}s}u܍t&fUE)ЋU+U ЋU!!ЋU ffNE)ЋU+U ЋU!!ЋU fOfNE)ЋU+U ЋU!!ЋU fOfNE)ЋU+U ЋU!!ЋU fOfNE)ЋU+U ЋU!! fOUfN E)ЋU+U ЋU!!ЋU fO fN E)ЋU+U ЋU!!ЋU fO fNE)ЋU+U ЋU!!ЋU fOMM9UE9E}?EM܋UufEȉAEM9}-EM)9wދUMȋEBEU9|ؐt&UE‹MȉUUMMP^_]Ív'UWVdU U UU}EM9MEEEE9E~UuUu}E1 }E}$;}F&}$;|} ;}(F~ڋ}UGu}MUu9M|UM} BEuUMME9MFd^_]ËM u$9M(Eu E$}M(U~pQEM})ȋ}EEu)UuE}~U OuE}M}uUE1;} uME+E;EEUFE)ЋU;EwUVEԋU)ЋU;EwUV EЋU)ЋU;EwUVE̋U)ЋU;EwUVEȋU)ЋU;EwUVEċU)ЋU;EwUV ;}UAE؉U܋A QEԋAEЋAE̋AEȋAEċE+E;EE܉+E;E;}|1o&MMET);uv=uT;}}@uE);MwƋMUMET);uwËuML;}|}uMEM}UTd^_]ÐUWVlE E EE}EU9UEEEM9M}kEuEu}E1 }E}$;|} ; }(t&F~֋}uGM}UuM9U|UuBM} EUUuM9U]l^_]Ëu M$>u(M }Eyu$M(}UF} QEE$U܋U(wHuzMuE)MU}؋u}))EUuMEɍ@UvBM UEu}Eu}MUE1;}uM}E+E;EE܋UFE)ЋU;EwU؉VE̋U)ЋU;EwUV EȋU)ЋU;EwU܉VEċU);Ew]U؃V;}]AEЉUԋA QE̋AEȋAEċE+E;EUEԉ+E;EUQUăV;}~;}|.MTuET);MvZMT;}}]uE);MwUuMET);uwMU܉TuET);MwuM؉L;}|}EuUM}Edl^_]ÐUWVhU U UU}EM9MEEEE9E}kUȋủUĉu}E1 }Eč}$;|} ; }(t&F~֋}UGuă}MUu9M|UMȋ} BE̋uUMȋME9M]h^_]ËUEU~BMuEԋ}MЉuU M$2U(uM E2U$u܋ArM(uUuE)֋AuMuE)1ɉu;MvEЋtDEEЋDE+E;EwU܉U)Љ;EwU؋uE)ƉT;uUwU܉TEU)ЋU;EwU؉T;M~;M}6EЋU4)ЋU;EvUЋtU)ЋU;EvTM u$AVM(Eu UAM$V Eu(A UEMEV )ȋuM)U1҉EM;U~+vEDME);MwpED;UpEЋtL DEEЋDE+E;Ewutȋu);EwML uE);uvEDME);MvED;U~;U}8MЋtM)ȋM;EvLMЋt M)ȋM;EvL uЋE M uMЍ<h^_]ÐUWV@E UEM } EEtE uMЉ )+M)MEME)MԉM̃EMM)EEEEȋEEEE ~ E@EE ~ MAE܋MEMȉEĉEMM1ʃu UUMЋEmM؋UMmE܊MMȋM1‹E;E 8@;E~ME܃MEU U9aUȊMmM؋UĉEUMEʊMmMMȋM1‰MMċUȃE܃EUȉM;u~;u U )u9~uȋMM9~UċE܋M1uuuMmM؋UeMʊMmMԋEe܋MȋM1‹E9 8@9~@^_]ÍUWVS [z%2M uEtPtLtHt/t*t%t t e[^_]RPQVľEM ue[^_]Ѝ&UWVEuM EV``;yte^_]ÐV U;Q uU};9u܋VU;Quы}`;Gt뼋}U;W uU;uU;WuFQEĉŰE`UUxEE}ȋBy}E}ƒ}~ Љ}VU҉UE};Eo;Ef;E]Au"MAuFuU}EU}EEu}9uučL7MEċŨ})‰E։у))uEu)ƉΉEE) +uMċE1Eu MMMU3MMUU;U :B;U~EЃP9mMM3MMMՋMЍQ̓}EEME E)΍ )ȉMMȉEԃ9MuEMEMЃEd)MЍ~ MQU܋UċEM1ƒEMu UUMUmE܊MMȋ 1ȃMUU;U :B;U~UЃE܃Ed9FEMmMԉU܉ЋUMȋ 1ȃME܉EUM;d;uUȋM9UsU2u܋M1Eu EEMEU܊MЋU 1ȍ ;u:B;U~UE )։EU)‰U؋Ũ9UuUEUEЃUP)~ EHM܋UEEUU1ƒu MMMUmE܊MMȋ 1ȃMUU;U :B;U~MЋE܃EQ9MMmE܊M؋EMȋM3ME܃EEE9gUDžp )։Ep)‰pŨ9UuEE1‰UEUxtEЅP)~MAMI1ȉE܋UEM艕xUĉt1Džlu ElMEmpUMȋM;l:B;l~EЋU܉UP9tx 1ȊMmpEMȋM܉MtxxtPuVRQuVEPW}Wq Mu΋UẺuȋuЋ}MUFu}ẺUĉM9}{1C;u}űE9xst1x1Ɖu܋U1Džhu MhMEU܊pЍ ;hB:B;h~.;u%M1uuMM3 9:B9~;uŰM9UsE0u܋U1Dž|u E|MEU܊MЋU 1ȍ ;|m:B;|~YUWVS} [Î2uEEE E(tt 9t؍e[^_]ËN9Ouك$uغÁ  ~غʃ UȉMRM4QU0R T$\$\$$WVݝhCG@݅hwpUc}0"}0G}0>}0xIf@݄XEQQRV4…E0}4 !1Ҩ}4}4}41g-#1؋E0… к)M1҅PN$QW$RV8'݄8Eu= R؃ Px V}4t }4 Hƒ!Ш?ȃ!ȃ}0}0}0}0VVPW t&PPRRt#H Vct&tHw1H W.:ItWO Q WՋ0RRPQ 빃}06}0}0}0k PT뀃 }0}0}0}0 Pɍ2}0}0r<}0}0 RZ W  W  R5%}0tw}0r$}0tW}0| Q V1+z؃ V R蘨A Wԇ- QpA VleRRPRHVVPVK R V Pxp Wp QМy V,>eUS [2URR]ÐUWV@EUEM tt r;qt @^_]Ívz }};y u:}};9uҋz}܋};yuBuȉEBEAEAE}tn}uU~UMȋuuM̋MỦϋu9sVQ9rE}MuME}u1-U~MMċMUȋu<}t uT}؃9sV%Q9r;MsMu}EMĉMuupE؃9s]F]E9r뫐US [2URR]ÐUWVSL[2uM tt ~;yt e[^_]ÍvV UU;Q uUU;uыVUU;Qu}Eȃ}U ШuzFUEFEAEAEċEAu FuMEЉMU}t;}JPPRWUR}WEPuV,,RRQVBPPRWMQUR}WuVPUSQ[Î2QME PQQ]ÐUWVEUEE utttJM;Ht Ĉ^_]ÐJ M؋M;H u MԋM;uϋJMЋM;HuM;Nt뭋};~ uMԿ;uM;NuJxM}J~MUH}FMuЉE֋u؉U}tP}IM;M;M!Шt;MuUu־U}tf}t1M뽅~EMu܋U~EHuuUMu܋EΉUu܋uOuu렅~u}E܋M}̉EȃuljȃljUăvu܋}uȉ}E}̋UăU}9sQU2zUȋREuEUEEɋUuȃ]]EU}Eu;Mr؋}};Ms uȋEMEU܋}MuMMU܉}Etv}ȋUăU}9sNE̋8pE}E8P}uuEEɃu]]EEUE;Mr؋UUDtiuău9.Űz2UȋRuxE؅x|ŨU}}E؅|}Y;MrEăE9Ű2zUȋRupE}tE؅pɋ}؅tɋuȃ}̉u]]E;MraUSQ[^2QME PQQ ]ÐUVS[)2u Mttv e[^]Ít&PPjUREPURVQe[^]ÐUWVS[ò 2E MDž|0;1t e[^_]Ívx;yu뉽}DžxDžt}r}wDžxDžt MMEEjjURMQtRtQxR8xPEhXRM QURlPB[@‰2EpjppvUtDMt=|UzOE|x%H|M EO}‹q$H$ кyWQVlV%}M֋}!΅tE,DžxDžtl}H}}w0uPPuVlQ|TtDž|Dž| P CPDž||~||pUEԋ1M Ί| ׃|!σt@|@|| |!Ȩu| lVTUKPPMQlP|Dž|Dž|||Dž|a pE0jjURjtQtWxRxQhXWE PURWW@w0NDžv P,Aeu҃}tRMQWlV$QMQWlWm$ዅ| lV | lWePuVWlRJW$PPWlP\$농&UWVS [Å2} ut"tƒ!Шtt e[^_]ÉQQjEPURURWVie[^_]ÐUS[2PMv ]Í URUREPU RQ‹]ÐUWVS|[ú2u UN FMEN>MQF$F}&}U׍<tM Q>1҅t>~FRPW}WURuVujVB }tPPMQV=Ce[^_]ËuEEE~V}~uuuuuu~EUME1҉M;U}Dt117E1D7E\1D7EB;U|ʋUE׋MM4uuEB 1Ҩ51ҍe[^_]Í'UWVS[2U DžtMtU :U;:t e[^_]ÉM q|;ruM u AV M u AMVAuMEVpuMAN PPV$$J?)YXQ$$!?)ƒt6t,t"tt֋,M(0|< 8\4犍≕XdTEDžRR}WxP4Džt Dž}!ȨuE@ @@R⊍$ٽf f٭۝H٭4$$Ƀ} E٭۝L٭EE}}!Ȩ}} ȨtDžDž QWURjVQWRjjE PuVWP@ƅEV RQuVEPRVQRhVuM QURVNP@T;t P9TPMQxWU RD}X}z}E(bu;)1P09T}P@;0|P4‹1J9}"00 @09|ꋅF49} B;4|M̋uU@DP~1URxPuVURMQuVRMQ׃ T;t P7TE} 4V1X7҉L19} @9|􋅤1HJ9}H4@|9|ꋽy9}H4B9|ITXϋIPXTXC@XD URxRQPWVHPDVW0ƅE\=L# LQ P56 P$6 P6MPPWQ_R$t P5Eu1 P5냽|t (PjW}WLL$뛅EMUl}th\Ẻpd`t@ uNDuVxV\VQRWHQDRV0ƅxMEL0H@D;0p t44;1[tN4@4)Ƌ`4l4)Ƌ\4hB;0~ EPxR\V`QtRWHVDQUR0ƅp uVxPhQlRdVpQHRDVMQ0ƅ!E UpD@RxV\Q`RdQWHVDQPtH P3 P3X@DL LR2 XU@DRxV\Q`RdPWv'UVS[Î1uQƒ!Шtt e[^]Ít& MQURMQU RVe[^]UWVLEHHx0MP4H\pLM܉}HpUMЋxTHPXuMȋp`H }Uu؋xdPtplMɉ}ԉŰxhMM܋U!ЋMȋUM@!ЋM ȉM@@M@MȋM1ɍBEHE9EEMϋMf QE$f BfM $E$f Bˉ%$M$Jf $E$f B̉%͉$M$fJʉс $$ʋMEU؃ȊM܉!ЋMȋUM@!ЋM ȉM@@M@MȋMMBEMMMf2VE$fuɋ}EEf U RQR QQ} U| U RPQRQ t&DžDž  Dž P+} U RQR QQ U RQR QQ U RPQRQo U RQR QQ?} U RPQRQOM U RQR QQv'UWVĀEp4HHx0uMpXH\PL}u܋xTM؋phHlU}P`űxdMȋpH Uԉ}ЋPpxtUĉ}uy~z~QV AFUG90pDž\pۋDpy$}=ZE Q蚻DžK,뢋 WRDž|<61x9}ك$|@9|1;M}E: HuItpppTXwXHt :lRDžtDž}~!@;u|䋕`uNT\1;\}d1\|:QQtQxQ|QL:Q :%QjX Nu\|QVtQxV|QtV Q%RXT0l t/WQuVtRjjjWl E~#Mr:r:z rIuTM9tt tP  |W`9t VٸDž&US [̀1jURMQURE PUR]Í'US [Ì1jEPURMQU RUR]Í'UVSV[M1Eu$MU~+ك$E@uɃNuY[^]Ív'UVS[1PEu$MU@~Zك$#BE@t.ɃNt1E@tBE@u!QNuX[^]Í믍UVS[N1PEu$MU@@~~ك$4BE@t@BE@uGQ NtDE@tBE@u0QBE@tɃ NuX[^]뙐t&UVS[Î~1PEu$MU@@@ك$N&BE@tSBE@uZQB E@tWɃNtZE@tBE@u\QBE@t:B E@u%Q NuX[^]Ígt&L'UVS[~}1EU$uM~,ك$&E@uɃJu[^]Ít&'UVS[}1Eu$MU@~[ك$$BE@t.ɃNt1E@tBE@u!QNu[^]Í&믍UVS[~|1Eu$MU@@~~ك$4BE@t@BE@uGQNtDE@tBE@u/QBE@tɃNu[^]뚍&UVS[þ{1Eu$MU@@@ك$GBE@tSBE@uZQBE@tWɃ NtZE@tBE@uTQBE@t3BE@uQ Nu[^]nt&T'UWV M E$M}MU~4Eu1FfE1GEP$ɃuMuσ ^_]Í&UWVS[Zz1M EMEMMU$M }u݄ ك(ك$UWًU؋M ыEM؋UU؋UM؃" BMAMUMAMME11ҊQE$щM؉ȉ$$ɊM ܌øEuE]}EfM fMMm]m ȋMU!ƒ" ‹E@UMBAUMEML؃[^_]Í'UWV ME$Mu UM~NE1P1F$$1}$EEG}BZMu ^_]Í'UWVPE$} uMU~XEt&1P1G$$1$$$1FB$$ɃAYMuX^_]Ív'UWVPPME$Mu MU~QE1P1F$$1}$G$$BɃɃ}AYMuZY^_]ÍUWVS[Zw1M EMEMMU$M u}݄ك(ك$UvۋE؋U ЋM"ɋU؊M F܌ӸEuEًU؋M ыEM؋UU؊M؃"HOMUAMMUVMMU11 Q1ɊJ$ $U$U M؉ʉ$$ʊM ܌ӸEuE}EfM fMMm]m ȋMU!ƒ" ˆ}EfM fMMm]m ȋMU!ƒ"A ˆWEUMBUVMEM؃[^_]Ðt&UWVS [:u1uMuMM } U$E}E܅ҋ}݄(ك(ك$UڋUԋM ыEMԋUUԋU܊Mԃ" GEV$ɋMAˊM ܌øEuE-ًuԋU MUԋEEԊEԃ"AM܈AEuUPGuUEMM>u111RN$ $u$VMԉ։$$ɋUʊM ܌ӸEuE}EfUMԶ fUm]m ȋMU!ƒ"M ˆWUV$ɋE@}EfUMԶ ufUm]m U!ƒ"F ‹E܈PUuMRGuMUEM؃ [^_]ÉUWVS [úr1uMuMM } U$E}E܅ҋ}݄ك(ك$UۋUԋM ыEMԋUUԋU܊Mԃ" ɋMGAˉM ܌ӸEuEىM "AM܈AEuUPGFuUEMM*u11ɊRN$ $M$֋Uԉ4$$ʋEM ܌øEuE}EfUMԶ fUm]m ȋMU!ƒ"M ˆɋEO@v}fU fUm]m uU!ƒ"F ‹E܈PUMuARGMuUEM؃ [^_]Í&'UWV uE$uM Uu~gE1P1A$$1A$$1}$EEG}BBZZMu ^_]Ít&UWVPE$} uMU~yEt&1P1G$$1G$$1$$$1FB$$1AYFB $$ɃAYMuX^_]É'UWVPPME$Mu MU~kE1P1F$$1F$$1}$G$$BBʃɃ Aʉ}AYYMuZY^_]ÉUWVS[Ún1M EMEMMU$M u}݄iك(ك$Uv܋M؋E E"ʋU؊M F܌ӸEuESۋE؋U ЋM"AGɋU؊M F܌ӸEuEFًU؋M ыEM؋UU؊M؃"HOMUAMMUVVMMWU11 Q1ɊJ$ $1ɊJ$ $U$U M؉ʉ$$ˊM ܌ӸEuE}EfM fMMm]m ȋMU!ƒ" ˆz}EfUMض fUm]m ȋMU!ƒ"A ˆWv}EfM fMMm]m ȋMU!ƒ"A ˆWEUMBUVVMEM؃[^_]Ít&UWVS$[k1M EMEMMU$M }u݄ك(ك$U<vۋUЋM ыEMЋUUЋUMЃ" EɋE܋VGE̊M ܌øEuEڋUЋM ыEMЋUUЋUMЃ"HJEɋE؋VGEˊM ܌øEuEًUЋM ыEMЋUUЋUMЃ"HJMFMFM WWMUMU11 Q1ɊJ$ $1ɊJ1$ $M$QU1ҊQU؉MЉʉ$$ʊM ܌ӸEuEM}EfUMж fUm]m ȋMU!ƒ"M ˆEɋM܋VGȉM5}EfUMж fUm]m ȋMU!ƒ"AM ˆQEɋM؋VGȉMGt&}EfUMж fUm]m ȋMU!ƒ"A ‹EPMUFWWFMU EM[؃$[^_]Ít&UWVS [Êh1M EMEMMU$M }u݄ك(ك$U6vɋUԋM ыEMԋUUԋUMԃ"ʈ GE܋V͉EԊM ܌øEuEۋUԋM ыEMԋUUԋUMԃ"HɈJGE܋VˉEԊM ܌øEuE}ًUԋM ыEMԋUUԋUMԃ"HJMAFMFM WWMUMU1Ɋ Q1ɊJ$ $1ɊJ$ $1ɋU$ M܋EщʉMԊM ܌ӸEuEc}EfUMԶ fUm]m ȋMU!ƒ"M ˆM܋VGщȉMK}EfUMԶ fUm]m ȋMU!ƒ"AM ˆQM܋VGщȉM[}EfUMԶ fUm]m ȋMU!ƒ"A ‹EPMUAFWWFMU EMr؃ [^_]ÐUWV uE$uM UuE1P1A$$1A$$1A$$1}$EEG}BBBZZZ Mu ^_]Í&UWVPE$} uMUE1P1G$$1G$$1G$$1$$$1FB$$1AYFB$$1AYFB $$ɃAY MgX^_]ÉUWVPPME$Mu MUE1P1F$$1F$$1F$$1}$G$$BBB ˃ʉ}AɃAAYYY MuZY^_]ÍUWVS[:c1M EMEMMU$M u}݄U}fU ك(ك$fU܋E؋M ȋM"AʈGU؊M F܌ӸEuEۋE؋U ЋM"AɈGU؊M F܌ӸEuEًU؋M ыEM؋UU؊M؃"H OMUAMMUVVVM MU11 Q1ɊJ$ $1ɊJ$ $1ɊJ$ $U$U M؉ʉ$$̊M ܌ӸEuEum]mʋU؋M ʋEM!Ѓ" ЈU؊M F܌ӸEuE1m]mU؋M ʋEM!Ѓ"Q t&m]mU؋M ʋEM!Ѓ"Q 8vm]mE؋M ȋUM!ƒ"A ˆWEUMBUVVVME Mf؃[^_]Í&'UWVS([`1M EMEMMU$M }u݄vU}fU ك(ك$fUKۋŰM ыEM̋UŰUM̃"HJEɋE؋VGE̊M ܌øEuE ڋŰM ыEM̋UŰUM̃"HJEɋEԋV GEˊM ܌øEuEًŰM ыEM̋UŰUM̃"H JMFMFMF WWW MUMU11 Q1ɊJ$ $1ɊJ$ $1ɊJ1$ $M$QU1ҊQU1ҊQUԉM̉ʉ$$ˊM ܌ӸEuEum]mŰM ʋEM!Ѓ"M ЈEɋVMGʉM̊M ܌ӸEuEm]mŰM ʋEM!Ѓ"QM ЈAEɋU؋NGЉUm]mŰM ʋEM!Ѓ"QM ЈAEɋUԋN GЉUm]mE̋M ȋUM!ƒ"A ‹EPUMFWWFF WUM EM؃([^_]Í&UWVS [J\1M EMEMMU$M }u݄TU}fU ك(ك$fUC܋UԋM ыEMԋUUԋUMԃ"HʈJGE܋V͉EԊM ܌øEuEۋUԋM ыEMԋUUԋUMԃ"HɈJGE܋V ˉEԊM ܌øEuEًUԋM ыEMԋUUԋUMԃ"H JMAFMFMF WWW MUMU1Ɋ Q1ɊJ$ $1ɊJ$ $1ɊJ$ $1ɋU$ M܋EщʉMԊM ܌ӸEuEum]mʋUԋM ʋEM!Ѓ"M ЈVMGщʉMԊM ܌ӸEuEm]mUԋM ʋEM!Ѓ"QM ЈAU܋NGʉЉUm]mUԋM ʋEM!Ѓ"QM ЈAU܋N GʉЉUm]mEԋM ȋUM!ƒ"A ‹EPUMBFWWFF WUM EM؃ [^_]É'UWV M E$M}MU~4Eu1FfE1GEP$ɃuMuσ ^_]Í&UWVS[:X1M E$MUMU}uMك(ك$EJ&ى ыUȋM"BMAMUMAMME1Ҋ1RM$$$ $$EuEt}fU fUm]m ȋMU!ƒ" ‹E@UMBAUMEMY؃[^_]Ív'UWV ME$Mu UM~NE1P1F$$1}$EEG}BZMu ^_]Í'UWVPE$} uMU~XEt&1P1G$$1$$$1FB$$ɃAYMuX^_]Ív'UWVPPME$Mu MU~QE1P1F$$1}$G$$BɃɃ}AYMuZY^_]ÍUWVS[jU1M E$MUMUuM}ك(ك$Et&܉ȋU "FEuEى ȋM"AGMUAMMUVMMM11R1ҊQ$$MU$ $$ $$EuE}fU fUm]m U!ƒEۋE E Uۈ}fU fUm]m ȋMU!ƒ"A ˆWEUMBUVMEM؃[^_]Í'UWVS[zS1Uu E$UMUuMuU܅ك(ك$Eډ ыUȋM"NW$}GQ$EuE ى ȋM"AM܈AEU}PFU}EMM(}111RO$ $}$W$$ɉ׋U $$EuE}fU fUm]m ȋMU!ƒ"M ˆNW$ɋE@}fU} fUm]m U!ƒ"G ‹E܈PUM}RFM}UEM؃[^_]Í&'UWVS[:Q1UE$u UMUuM}U܅ك(ك$Eۉ ыUȋM"ɋOuFQ$EuEى ȋM"AM܈AEUuPGBUuEMMu11ɊRN$ $M$$$ʋE $$EuE}fU fUm]m ȋMU!ƒ"M ˆɋEO@&}fUu fUm]m U!ƒ"F ‹E܈PUMuARGMuUEM؃[^_]UWV uE$uM Uu~gE1P1A$$1A$$1}$EEG}BBZZMu ^_]Ít&UWVPE$} uMU~yEt&1P1G$$1G$$1$$$1FB$$1AYFB $$ɃAYMuX^_]É'UWVPPME$Mu MU~kE1P1F$$1F$$1}$G$$BBʃɃ Aʉ}AYYMuZY^_]ÉUWVS[ZM1M E$MUMUuM}-ك(ك$Et&݉ȋU "FEuE-܉ȋU "BGFEuE1ى ȋM"AGMUAMMUVVMMLM11R1ҊQ$$1ҊQ$$MU$ $$ $$EuE}fU fUm]m U!ƒEۋE E Uۈ}fU fUm]m U!ƒEۋE@ E UۈW}fU fUm]m ȋMU!ƒ"A ˆWEUMBUVVMEM؃[^_]ÍvUWVS [J1M E$MUMU}uMzك(ك$Et&ۉ ыUȋM"EɋM܋VGQ$EuEYډ ыUȋM"ABEɋM؋VGQ$EuEDى ыUȋM"ABMFMFM WWMUM_M11R1ҊQ$$1ҊQ1$$U$JM1ɊJM؋$$ $$EuE}fU fUm]m ȋMU!ƒ"M ˆr}fU fUm]m ȋMU!ƒ"AM ˆQ}fU fUm]m ȋMU!ƒ"A ‹EPUMFWWFUM EM؃ [^_]ÍUWVS[*H1M E$MUMU}uMdك(ك$Et&܉ ыUȋM"ʋM܋VGQ$EuEFۉ ыUȋM"ABɋM܋VGQ$EuE4ى ыUȋM"ABMAFMFM WWMUMOM11R1ҊQ$$1ҊQ$$M$E܉E $$EuE}fU fUm]m ȋMU!ƒ"M ˆ}fU fUm]m ȋMU!ƒ"AM ˆQ}fU fUm]m ȋMU!ƒ"A ‹EPUMBFWWFUM EM؃[^_]ÍUWV uE$uM UuE1P1A$$1A$$1A$$1}$EEG}BBBZZZ Mu ^_]Í&UWVPE$} uMUE1P1G$$1G$$1G$$1$$$1FB$$1AYFB$$1AYFB $$ɃAY MgX^_]ÉUWVPPME$Mu MUE1P1F$$1F$$1F$$1}$G$$BBB ˃ʉ}AɃAAYYY MuZY^_]ÍUWVS[ZC1M E$MUMUuM}}ك(fUEܶ ك$fU&݉ȋU "BGFEuE܉ȋU "BGFEuE{ى ȋM"A GMUAMMUVVVM MM11R1ҊQ$$1ҊQ$$1ҊQ$$MU$ $$ $$EuEum]mˉ U!ƒEۋE E UۈFEuEvm]m U!ƒEۋE@ E UۈWZvm]m U!ƒEۋE@ E UۈW`m]m ȋMU!ƒ"A ˆWEUMBUVVVME Mw؃[^_]ÐUWVS$[Ú@1M E$MUMU}uM}ك(fUEж ك$fU&ۉ ыUȋM"ABEɋM؋VGQ$EuEډ ыUȋM"ABEɋMԋV GQ$EuEى ыUȋM"A BMFMFMF WWW MUMM11R1ҊQ$$1ҊQ$$1ҊQ1$$U$JM1ɊJM1ɊJMԋ$$ $$EuEum]m ʋME!Ѓ"M ЈEɋM܋VGQ$EuEm]m ʋME!Ѓ"QM ЈAt&m]m ʋME!Ѓ"QM ЈAm]m ȋMU!ƒ"A ‹EPUMFWWFF WUM EMD؃$[^_]Í'UWVS[Z=1M E$MUMU}uM}ك(fUEض ك$fU &܉ ыUȋM"ABʋM܋VGQ$EuEۉ ыUȋM"ABɋM܋V GQ$EuEى ыUȋM"A BMAFMFMF WWW MUMM11R1ҊQ$$1ҊQ$$1ҊQ$$M$E܉E $$EuEum]mʉ ʋME!Ѓ"M ˈM܋VGQ$EuE5m]m ʋME!Ѓ"QM ЈA"m]m ʋME!Ѓ"QM ЈA;m]m ȋMU!ƒ"A ‹EPUMBFWWFF WUM EM^؃[^_] UWVPM E$M}MU~0ƉEP$ɋEENuZ^_] UWVS[91M E$MUMU}uMكHكLك$EPډ Mf# ‹MEMfUEM܉UtqEMP$ $$EuEq}fU fUm]m U!Y؃[^_]ÉUWVPPME$Mu UM~?ǐEEFEʋEɃɉEBZOuY^^_]Í&'UWVPE$} uMU~FEt&GP$FB$$ɃAYMuZ^_]ÍUWVPPME$Mu MU~BE}FP$BɃɃ}AYMuZY^_]Ít&'UWVS[j71M E$MUMUuM}nكHكLك$E݉ ɃfEڋEFff!Ef UfEuEډ Mf#AM ƒEfWUVM܉UMEEMU@ P$ $$EuE}fU fUm]m U!}fU fUm]m U!t&؃[^_]ÉUWVS[ú51M E$MUMU}uM~كHكLك$Eۉ MEf#ɋM GfVMQ$EuEډ Mf#AM ƒEWfPFUUM؉MEEUJ@M܋P$ $$EuE}fU fUm]m U!}fU fUm]m U!t&؃[^_]ÉUWVS[31UE$u UMUuM}U܅{كHكLك$E܉ Mf#ɋM fOuFQ$EuEډ uMf#F ‹uE܃GfPUMuREM؉UEM0AV$ʋU $$EuE}fU fUm]m U!}fU fUm]m U!t&؃[^_]ÉUWVPPuE$uM Uu~PǐEEEAAɋEɃˉEBBZZOu_X^_]Í'UWVPE$} uMU~^Et&GGP$FB$$FAB Y$$ɃAYMuZ^_]Ít&UWVPPME$Mu MU~TE}FFP$BBʃɃ Aʉ}AYYMuZY^_]É'UWVS[ú01M E$MUMUuM}كHكLك$Eމ ˃fEڋEFff!Ef UfEuE#݉ ɃfEڋEFf@f!Ef UfWEuEډ Mf#AM ƒEfWUVUVMM܉EEMU@@ P$ $$EuE}fU fUm]m U!}fU fUm]m U!}fU fUm]m U!t&؃[^_]ÉUWVS [Ê.1M E$MUMU}uMكHكLك$E܉ MEf#ɋM GfVMQ$EuEEۉ MEf#AɋM GfQVMQ$EuEډ Mf#AM ƒEWfPFUFW MԉUMEUEJM@J@M؋P$ $$EuE}fU fUm]m U!w}fU fUm]m U!t&}fU fUm]m U!t&؃ [^_]ÉUWVS[*,1M E$MUMU}uMكHكLك$E݉ ʋMf#ɋM GfVMQ$EuE6܉ Mf#AɋM GfQVMQ$EuEډ Mf#AM ƒEWfPFUFW M؉UMEMEU܉Eɋ@@Q$ɃEuE}fU fUm]m U!}fU fUm]m U!t&}fU fUm]m U!t&؃[^_]ÉUWVPPuE$uM Uu~cǐEEEAAAʋEʃɉEBBBZZZ OuXZ^_]Ív'UWVPE$} uMU~xEt&GGGP$FB$$FABY$$FAB Y$$ɃAY MuZ^_]Ív'UWVPPME$Mu MU~dE}FFFP$BBB ˃ʃAɉ}AAYYY MuZY^_]É'UWVS4[(1M E$MUMUuM܋}>}كHfUE̶ كLك$fU݉ ɃfEƋEFf@f!Ef UfWEuE~܉ EɃfEƋEFf@f!Ef UfWEuEKډ Mf#A M ƒEfWUVUVV MM̉E EMU@@@]Љ P$ $$EuEum]mɉ ȋUF!ƒfEƋEff!Ef UfEuETm]m U!Im]m U!wm]m U!؃4[^_]ÉUWVS$[Ú%1M E$MUMU}uM}كHfUEж كLك$fU*܉ ȋMƒf#AM fQEɋM؋VGQ$EuEۉ ȋMƒf#AM fQEɋMԋV GQ$EuEډ Mf#A M ƒEWfPFUFWWF UMЉMEGUEJMJM؋M@@@QUP$ $$EuEum]m ʋME!Ѓf#ME fVMGQ$EuEm]m ʋME!Ѓf#QM fA t&m]m ʋME!Ѓf#QM fA0m]m U!Z؃$[^_]ÉUWVS,[ú"1M E$MUMU}uM܅}كHfUEȶ كLك$fU'܉ ȋMƒf#AM fQɋM̋VGQ$EuEۉ ȋMƒf#AM fQEɋM̋V GQ$EuEډ Mf#A M ƒEWfPFUFWWF UMȉME,MEỦEɋ@]@@Q$ʃEuEum]m ʋME!Ѓf#M fVMGQ$EuE.m]m ʋME!Ѓf#QM fA#m]m ʋME!Ѓf#QM fAJm]m U!t؃,[^_]UWVPPM E$M}MU~DƐEffE%Pf%$$$ɋEENuXZ^_]É'UWVS[Ê1M E$MUMU}uMك,ك$ER&ى ыUȋMf#MfMMMUMEfRM$f%$$ $$EuEc}fU fUm]m ȋMU!ƒf#M ƒEfMUUEMH؃[^_]ÐUWVPPME$Mu UM~aǐf%PfF%$$E$ffE%$$ʋEɉEBZOuY^^_]ÍUWVPE$} uMU~hEt&f%PfG%$$f%$$$fF%B$$ɃAYMuZ^_]Ív'UWVPPME$Mu MU~dǐf%PfF%$$E$ffE%$$BɋEɃAɉEYOuZY^_]É'UWVS[z1M E$MUMUuM}ك,ك$Et&܉ȋU f#fFEuEى ыUȋMf#AMMMfGUVMMMfRfQ$$MU$f% $$ $$EuE }fU fUm]m U!ƒfEڋEff!Ef Uf}fU fUm]m ȋMU!ƒf#AM ƒEfWUMUVEM؃[^_]ÐUWVS[z1UE$u UMUuM}U܅ك,ك$Eډ ыUȋMf#OfV$ɋuFQ$EuEىʋE ыUΉUMf#pGEfquPuEMM+ufRfN$ $u$ffV%։$$ɋU $$EuE}fU fUm]m ȋMU!ƒf#M fOV}EfU fUm]m ʋMuM!փf#PGE ֋UfpuRuUEM؃[^_]Í&UWVS[:1UE$u UMUuM}U܅ك,ك$Eۉ ыUȋMf#ɋOfuFQ$EuEىʋE ыUΉUMf#pGEfquPuEMM!ufRfN$ $E$fց4$$ʋU $$EuE}fU fUm]m ȋMU!ƒf#M fɋO}EfU fUm]m ʋMuM!փf#PGE ֋UfpuRuUEM؃[^_]Ð&UWVPPuE$uM Uu~~ǐf%PfA%$$fA%$$E$ffE%$$ˋEˉEBBZZOu_X^_] UWVPE$} uMUEf%PfG%$$fG%$$f%$$$fF%B$$fF%B AY$$ɃAYMrZ^_]Í'UWVPPME$Mu MUf%PfF%$$fF%$$E$ffE%$$BBʋEɃAʃAɃ EYYOuZY^_]Ð&UWVS[1M E$MUMUuM}Zك,ك$Et&݉ȋU f#fFEuEK܉ȋU f#BfGFEuEVى ыUȋMf#AMMMfGUVVMMqMfRfQ$$fQ$$MU$f% $$ $$EuE}fU fUm]m U!ƒfEڋEff!Ef Uf}fU fUm]m U!ƒfEڋEf@f!Ef UfWt&}fU fUm]m ȋMU!ƒf#AM ƒEfWUMUVVEM؃[^_]ÍUWVS [J1M E$MUMU}uMك,ك$E t&ۉ ыUȋMf#fEɋM܋VGQ$EuEډ ыUȋMf#AfBEɋM؋VGQ$EuElى ыUȋMf#AMfBMMFWWF MUMMfRfQ$$fQ$$Eff@%$EfA%Eщ$$ $$EuEn}fU fUm]m ȋMU!ƒf#M fN}fU fUm]m ȋMU!ƒf#AM fQa}fU fUm]m ȋMU!ƒf#AM ƒEfPFUFWWUM EMy؃ [^_]Ð&UWVS[Z1M E$MUMU}uMك,ك$Et&܉ ыUȋMf#fʋM܋VGQ$EuEaۉ ыUȋMf#AfBɋM܋VGQ$EuEPى ыUȋMf#AMfBMMFWWF MUMkMfRfQ$$fQ$$M$f%E܉E $$EuE}fU fUm]m ȋMU!ƒf#M fk}fU fUm]m ȋMU!ƒf#AM fQ}fU fUm]m ȋMU!ƒf#AM ƒEfPFUFWWUM EM؃[^_]Ð&UWVPPuE$uM Uuf%PfA%$$fA%$$fA%$$E$ffE%$$̋ẺEBBBZZZ OdXZ^_]Ít&'UWVPE$} uMUEf%PfG%$$fG%$$fG%$$f%$$$fF%B$$fF%BAY$$fF%AYB $$ɃAY MGZ^_]ÉUWVPPME$Mu MUf%PfF%$$fF%$$fF%$$E$ffE%$$BBB ˋEʃAɃA˃AYYY EOaZY^_]Ð&UWVS[ 1M E$MUMUuM}}ك,fUEܶ ك$fU&݉ȋU f#BfGFEuE܉ȋU f#BfGFEuEى ыUȋMf#A MMMfGUVVVM MMfRfQ$$fQ$$fQ$$MU$f% $$ $$EuEum]mˉ ȋUF!ƒfEڋEff!Ef UfEuEQm]m U!ƒfEڋEf@f!Ef UfW2m]m U!ƒfEڋEf@f!Ef UfW8m]m ȋMU!ƒf#A M ƒEfWUMUVVVE MN؃[^_]UWVS$[1M E$MUMU}uM&}ك,fUEж ك$fU&ۉ ыUȋMf#AfBEɋM؋VGQ$EuEډ ыUȋMf#AfBEɋMԋV GQ$EuEى ыUȋMf#A MfBMMFWWFF W MUMMfRfQ$$fQ$$fQ$$Eff@%$EfA%EfA%Eщ$$ $$EuEum]m ʋME!Ѓf#ME fVMGQ$EuEm]m ʋME!Ѓf#QM fAt&m]m ʋME!Ѓf#QM fAm]m ȋMU!ƒf#A M ƒEfPFUFWWWF UM EM؃$[^_]Ív'UWVS[ê1M E$MUMU}uM}ك,fUEض ك$fU&܉ ыUȋMf#AfBʋM܋VGQ$EuEۉ ыUȋMf#AfBɋM܋V GQ$EuEى ыUȋMf#A MfBMMFWWFF W MUMMfRfQ$$fQ$$fQ$$M$f%E܉E $$EuEum]mʉ ʋME!Ѓf#M fVMGQ$EuEm]m ʋME!Ѓf#QM fAm]m ʋME!Ѓf#QM fAm]m ȋMU!ƒf#A M ƒEfPFUFWWWF UM EMA؃[^_]Ív'UWVM$} uUE~!ɃIu^_]ÍvUWVS[*0u ME$}uMu}M݃Xك<ك$v݃EuEډ%E܋E#E ЋUE܋E܉EUEEEOt~EUEuE^}fU fUm]mE :t&}fE fEm]mE!B؃[^_]UWVu$M }EU~4A˃Ƀ@XNu^_]UWV}$u MUE~;FA@ʃXBZOu^_]É'UWVu$M }UE~:AɃ@XBZNu^_]Ív'UWVS,[0M E$M}MuMMك$}fUEԶ ]݃Xك4IM<t&19|.TT9|Iu^^_]It뷍&UWVQuM V FU~AAANu~K4M<19|=t&TTT9|Iu^^_]It뤐UWV U Mtt q;rte^_]Ðy }};z u9}};:uԋy}};zuu}tAzE}BQE}t:}uPMQEPVExMPRExYPuV[P}WMQVEx.PRExPUR,E뫋EދEʋE럍t&UWVM utt ~;yt e^_]ËF E;A uE;uF;Auۃu֋UE… кuQIUVUv}t<}uURURURWExIPVExUPQgURURURWEx(PVExPQu͋E믋EEЋE룍vUVS[N0TuPjHQVvb VU RURe[^]ÉUVS[0TujHQV6b VU RURYe[^]ÉUVS[μ0tujHQVaكpU]كԢU]RVE PEP#e[^]ÍUVS[n0tu(jHQVa~k"MUf`@ffffMU`MqL7AMUMQVU REPe[^]ÐUWVPM }U}A8]AA@]Au]A]A ]A(]A0}]}4vU19}dDEED̃EEMEEXX 9|}U M U}MEu؃P1^_]ÍUWVHM }U}A8]AA@]Au]A]A ]A(]A0}]}4vU19}dDEED̃EEMEEXX9|}U M ׋U}MEu؃H1^_]ÍUWVhU M$}uA]BA]B]B]B ]B(]B0]B8]B@U]UыU]ЉU4v}19}ut&DEED̃EEMEEMEMXX 9|UE }M UMEd؃h1^_]ÐUWV`U M$}uA]BA]B]B]B ]B(]B0]B8]B@U]UыU]ЉU4v}19}ut&DEED̃EEMEEMEMXX9|UE }M ‹UM׉Ed؃`1^_]ÐUVEuUM ~7AAɃBBZZHu^]Í&'UV UE uM@@]B~ZM@M@A΃MAYYJu؃ ^]ÍUV,UE uMB]@]]B@ ]@@@(]Ѓ0MEMMEA@EM@EƒEMEAYE]YJE]n؃,^]Í&'UVLUE uM]B@]B@]B@@]@]@ ]@(]@0]@8H] MEMMEM@@ÃEAMEMAEMEEEYEMEEYEJ]E]]]E]F؃L^]Ð&UVEuUM ~HAAAʃ BBBZZZ Hu^]Í'UVUE uM]B@]@@] ~ME@M@@à EMAYEYEMA]EE]Y Ju؃^]É'UVDUE uMB]@]]@ B]@(]]@0]@8@@]@MEMMEA@EM@@EɃ EMEAMYE]EMEYEMAEYE]]E J]]>؃D^]Í'UVlUE uM]B@]B@]B]@]@ ]@(]@0]@8]@@]@H]@P]@X`] vMEMMEM@@E@AɃ EMEMMAEEMYEMEEMYEMA]EMEEYEE]EE]EE]E J]E]]]]]؃l^]UWVSUu [[0tMDžlLtt ~;yt e[^_]ËF \;A uyuE(VX;PtʋU(BT;uEU9E% uыEU 9E % uPQFQTIVAPN,XU(uҺ ȨE$كĞ}$~كU$ɃU$}$؊M$R$M EuU))NJD}} ȉ@ݝ8EMȋuETDƋ@19}܍8lA9|UDX4:u Dž փ R|}1;}Qt H|tA H ;|׋|Dž1xD9}ك$|@9|؋u(1ɋFE(Pƒx !ШtPT4u@~EU Ћ@J ЋDJ lكtE@lكxBE@Bك|E@BكE@DžDžDXDJX U(d`B TuM(Q()‰\_\0tplxlDž4Dh94LDžu9|sWhPXpʉ$4$PxR`M򉕘9u)ƃ~yp }(WU$RE PUR}WUR}WVQiUuE 0Ql8 thu4A4Dh94@QWVU(RhPX@pD4VxRQd @9|)Ɖ0 |R=L9lt lP1vTtNX19xF@9|hXu(N,)X19612P$xB9|mcAL9l~ lVCgt&'UWVS[70Muك$U E]AlE]AU E]Aݕ`]U]FUݝxFUUUݝp9U}쉽\E݅`EE݅`E؋\UȊMĈUOGURjEPW}M1EȊ R$]Ed1]W}$dE؋M̋UABEЉM̋} ^VU9}+ك$ݝ`l1FF F(R1ҊQ$$1ҊQ$$EM$M˃EMEEMEME܍xEEMEME܍pẺlE]]݅x]݅pݝx]݅`ݝpESك(EE݅`E<ك(EE݅`E%ك(Eu}ؿ\EȊUĈEQQURjURQ1ɋEEȊIM$]Ed]W$d}fM fMm]mcv}fM fMm]m}l}fU fUm]m?v؍e[^_]ÍUWVSl[Ú0}M }M}E }EMW7UQEEEEEuĉU9EuEuExP }H}EĉM4R 1u}uE)<E} u}<@1F}}E)<M}}E܍IMEE$ddɋM UBUVVɃ9M}fM fMm]m}fM fMm]mTt&}fU fUm]m؍e[^_]ÍvUWVS<[êz0UEك$U u]ȋ}9UEEEE/EEFEE]؋UĊE܊M؈EJMBJEPjWRMEEE$dd1QME $dʃMAMVVV 9Eك$MȋM EMAMAA M Eك(E!EEك(EREEك(EEEك(EEMĊE܊U؈EQUԈQUQURjWQ5MEEEE$dddʋM UBUVVVɃ 9M|t&}fU fUm]mv}fM fMm]m8t&}fU fUm]mv}fM fMm]mT؍e[^_]ÐUWVS<[zw0uE$uuU M }@puȍr]BBcUMĉUE+EEDEE]؋E̊U܊M؈UH@URjMQP蕱U E܍IME$d1QM$dɃEEE̋M̓W˃W̉MFF}$~:MȋU$JF˃@@XXJuڃM1EEEEEEEEuYEE̊U܊M؈UHMHURjMQPEE܍IMEE$dd}fU fUm]m}fU fUm]m}fU fUm]m؍e[^_]ÐUWVSL[út0uE$uuU M B@p}]BuB]]r %UMUEEEEEEEEEEUԊMЈUHM@HURjMQPMMEEE$dd1QM$dɃEE̋M˃WWWEEF̓ FɃΉM}$]F]~DMU$F J˃@X@@XX Juڃ MEE?EEhE)EEEOEEEEE5EEEEUԊMЈUHM̈HMHURjMQPMMEEEE$ddd}EfU fUm]m}EfU fUm]me}fU fUm]m}fU fUm]m؍e[^_]Ít&UWVS['q0MuU EA]]AA]]ك$Uݕ@UUUݕpݕxU]ݝhFFɉLEݝ`ݝXFEU ݝP9U}쉽<E݅@EE݅@EE݅@݅0EM1ҊEEEE$dddG^^^ M&e[^_]}fM fMm]m}fU fUm]m9}fM fMm]mt&}fM fMm]mNvUWVS<[*]0MU ك$u}]MUȉMEE EE%؋M܋UfMMfQfAURjWQ3M1E܍@E$dQM$dGVVMBMɋE MM@@ʃE EEEEEEEEEM܋UfMMfQUfQURjWQ21ɊE܍IMEE$ddGVVM؍e[^_]}fM fMm]m}fU fUm]mMt&}fM fMm]mvUWVS<[ÊZ0MU ك$u}]…MUMEEAEE\EEu؋M܋UfMMfQfAUfQURjWQa:M1ҊEEE$ddQM$dGVVV MMɋE MM@M@@ʃ E EEGEEEpEEEEEEEU܋MfUEUfJMfJMfJPjWR81ҋMEEEE$dddGVVV Mnt&؍e[^_]}fM fMm]mdv}fM fMm]m.t&}fM fMm]mbt&}fM fMm]m"vUWVS<[:W0uE$u܋uU M }@pur]BBUMUčEJEEcEE|؋M؋UfMMfQfAURjURQ-1ɋUE؊ EɍIM$dQM$dɃEEE̋UBW˃W̉U܃FF}$~AMU$JF&˃@@XXJuڃMGɍEEEEEEEEu]EM؋UfMMfQUfQURjURQF,1ɋEE؊EԍIME$ddr}fM fMm]m}fM fMm]m}fM fMm]m؍e[^_]ÍUWVS\[:T0uE$u܋uU M B@p}]BuB]]r YUMUEEEEEEEEEM̋UfMMfQfAUfQURjURQ31ҋME̊MEE$ddQM$dɃEE̋UBWWWEEF̓ FɃΉU܃}$]F]~DMU$F J˃@X@@XX Juڃ MɍEfEREEME6EEE4EEEEEEEEM̋UfMMfQUfQUfQURjURQ11ME̊MEEE$ddd}EfM fMm]m_}EfM fMm]m`}fM fMm]m}fM fMm]m؍e[^_]UWVS[wP0}uEU G]]GG]]ك$UUUUݕhݕpݕx]ݝ`FFݝXE܋} ݝPFݝHM艕4D EEEEE݅8EUMfUEDfJfBMfBQjMQR/EM1EAE$d]Edɸ]]PE$dEȉM^]EEE^^ 4Gݝ8EMF GE܍`F(GƒEF0F8EME܍xE܍XEEME܍pE܍PEEME܍hݝh݅8܅hݝ8E܍H܅8ݝ8E܅8ݝ8EM]݅8Eݝ8݅`ݝ`݅X]݅Pݝx݅HݝpݝXݝPݝhݝHEErEEt&EEEeEDE݅8EL݅8EDž0UEfUMDfBfJ0MfBQjMQR-U܋M1EBME$ɉUd]Ed]E]ۅ0dE]E^^E^ 4#e[^_]Ð}fU fUm]mt&}݅8fM fMm۝0m}fU fUm]m}fU fUm]mvUWVS[WK0U}U}̋uM E M>1ɋu}EEVEuUEE~EuE}9V UuEEEE|xu}FWtuEp~Uplth1ɋu}EEVEuUEE~EuE}9V UuEEEE|xu}FWtuEp~Upltht&}fU fUm]m=t&}fM fMm]mt&UWVS4[ê0EMEMHU @M؋MЉE)EMMЋ} eȋuE̅:ك$ك(UEE EEEE(ڋMȋU!ʋEMM!ȊM‹EMM!ȊMM‹M1 1ҋMMEEE$ddR$dUBU^^^ MEGGG EE'EEE`EEEEEEEMȋU!ʋEMM!ȊM‹EMM!ȊMM#MȊM‹M1 UBMEEEE$ʉUddd^^^ M؃4[^_]É}fU fUm]mt&}fU fUm]mt&}fU fUm]mt&}fM fMm]mvt&UWVS0[Ê0EMEMHE@M܋MԋU )E؉MMԋ} e̋Muك$EЅك(Ut&EEEEۋM̋U!ʋEMMM!ȊMM‹M1 1ҋM@MEE$dR$dUBUVVMGGE&EEEEEEEEM̋U!ʋEMMM!ȊMM#MȊM‹M1 UB@MEEE$ɉUddVVM؃0[^_]}fU fUm]m}fU fUm]m5t&}fM fMm]mt&UWVS4[0EMEMHE@M؋MЋU )EԉMMЋ} eȋMuك$E̅^ك(UvEE-EE@EEOًMȋU!ʋEMM!ȊM‹EMM!ȊMM‹M1 1ҋMMEEE$ddR$dUBUVVV MlGGG EE4EEElEEt&E!EEEEMȋU!ʋEMM!ȊM‹EMM!ȊMM#MȊM‹M1 UBMEEEE$ʉUdddVVV M؃4[^_]}fU fUm]mt&}fU fUm]mt&}fU fUm]m~t&}fM fMm]mFt&UWVSL[z0EEuU HuM؋uЋHE)΋E$uЉMuɉM̍rMЋ}@eHBBˋU M]ك$U ]UEE6EEEOEEEh؋MȋU!ʋE܊MMM!ȊMM‹M1 1ҋM@MEE$dR$dɃMAMEEEWW̓FF}$~MU$JFt&˃@@XXJuڃMhEEEEEEEEEEEEEUM!MEMԋE!ЋUM!ʊMЋUM1MA@UEEE$ɉMdda}fM fMm]ml}fM fMm]m}fM fMm]m؃T[^_]Ít&'UWVSl[ú/UEuE JuMȋuJE)΋U$uMuɉM}BMp ]@@eJU M@]]كLU ]UEEEEEEEEEEEEEUԋME!MM!ȊM‹EMM!ȊMM‹M1 MMEEE$ddR$dɃMAMEEWWWEEF̓ F΃}$]F]~FMU$F J˃@X@@XX Juڃ MEE]E~EEEDEgEEEE+EEEEEEEEEUԋME!MM!ȊMUMEME!ȊMM!ʊMЋUM1MAUEEEE$ʉMddd}EfM fMm]m"}EfU fUm]m)}fM fMm]m}fM fMm]m؃l[^_]Ív'UWVS[Ç/UEE}J}zM@Mu)}@ʉM]]@ك$EM}} e]]UUUUݕhݕpݝxFݝ`ݝXFFɋU ݝPݝH$كLU ݝ@4E݅@E~E݅@EE݅@݅8EUċME!MM!ȊM‹EMM!ȊMM‹M1 MME$Ed]Edɹ]]QM$dMAM]EEE^E^^ 4MGE܍`ݝ8EN E܍xEMGE܍XF(EEG܍p˃EMF0E܍PE܍hEF8EM݅8ݝ8E܍H܅8ݝ8EEM܅8F ݝ8݅`ݝ`݅XݝXݝx݅H݅Pݝh݅@ݝpݝPݝHEEE݅@EEE݅@EiEvE݅@݅8EP݅8EDž0UċME!MM!ȊM0M,ME!ȊMM!ʊMЋU,1MAUME$Ed]Ed]E]ۅ0d]EE^E^^ 4؁[^_]Ív}fM fMm]mv}݅8fU fUm۝0m}fM fMm]m}fM fMm]mUWVSEEEMEp}uU p})uMuMuEeM xtEEEEE1ɉE9ыx~x}xFw E|EEEEpxxxFdxx`dW~\XU4@X[^_]ÍUWVS0[ O/E}}U xu }܋ME܁}؉Eԅك$ك(UvEEEEڋ}܋E1Ҋ8E}؉U1Ҋ8}ԋE1Ҋ}1҈}@EE$dR$dE@EYYMFFEIEEE:t&EEE'EE}܋E1Ҋ8E}؉U1Ҋ8}ċE1҉E̋EԊEЋUB@}EEE$ɉUddYYM؃0[^_]}fU fUm]mMt&}fU fUm]mDt&}fU fUm]m$t&UWVS8[êL/E}}U xu }؋ME؉}}؉EЁ҉}@ك$ك(U&EEEEEE(ڋ}؋E1Ҋ8E}ԉU1Ҋ8E܋}}ЉU1Ҋ8}̋E1Ҋ}1҈}EEE$ddR$dUBUYYY MGFFF E E*EEEaEEEEEEE}؋E1Ҋ8E}ԉU1Ҋ8}܋EЋUЉE11ҋ}NjE}ċ}̊8EЋUB}EEEE$ʉUdddYYY M؃8[^_]Ív}fU fUm]mt&}fU fUm]mt&}fU fUm]mt&}fU fUm]mvt&UWVS0[ÊI/E}}U xu }܋ME܉}}Eك$Åك(UEEEEۋ}܋E1Ҋ8E}؉U1Ҋ8}ԋE1Ҋ}1҈}@EE$dR$dUBUQQMFFE-EEEEEE&EE}܋E1Ҋ8E}؉U1Ҋ8}ċE1҉E̋EԊEЋUB@}EEE$ɉUddQQM؃0[^_]}fU fUm]m }fU fUm]mX[^_]ÍUWVS,[ê./E}}U xu }܋ME܁}؉Eԅك$ك(UvEEEEڋU}EW}E̋EEx}ԋE}1f}@$dR$dʋEYYEMFFEIEEE:t&EEE'EؿE܋UEPEŰUEBU‹EԉUxEЋUfU@$dW$dʋEYYEM؃,[^_]Ðt&}fU fUm]mNt&}fU fUm]m}?}fU fUm]m$t&UWVS4[J,/E}}U xu }؋ME؉}}؉EЁ҉}ك$ك(U&EEEEEE(ڋU}EW}EċEEx}ċE}܉Ux}̋E}Ef1}$ddR$dʋ}YYY }MFFF E EEEvE!EEEEEEؿE؋UEPEUċUEBU‹E܉UċUEBU‹ẺUxEЋUfU$ddW$d}fU fUm]m}fU fUm]m}W}fU fUm]mt&}fU fUm]mt&؃4[^_]Ít&UWVS,[Z)/E}}U xu }܋ME܉}}Eك$Åك(UEEEEۋU}EW}E̋EEx}ԋE}1f}@$dR$dʋ}QQ}MFFE-EEEEEE&EؿE܋UEPEŰUEBU‹EԉUxEЋUfU@$dW$dʋ}QQ}M؃,[^_]}fU fUm]m&t&}fU fUm]m}8}fU fUm]mt&UWVS4[&/E}}U xu }؋ME؉}}؉EЁ҉}̋}ك$ك(UEE)EE:EEIًU}EW}EċEEx}ċE}܉Ux}̋E}Ef1}$ddR$dʋ}QQQ }MFFF EEEEE*EEEEEEؿE؋UEPEUċUEBU‹E܉UċUEBU‹ẺUxEЋUfU$ddW$dv}fU fUm]mt&}fU fUm]m}O}fU fUm]mt&}fU fUm]mt&؃4[^_]Ív'UWVSD[Ú#/EuuU pM u؋}E؁uԉEЋuE$@pur]BBpك$M]EE,EEEEEEE^؋EUE BEԉMMEHMЋEM1fM@$dR$dEEE͋M̃WˉMW̃FF}$~AMU$JF&˃@@XXJuۃM<EEEEEEEEEEEu\عU؋EEBU܉EEEPEЉEEHEЋUfU@$dQ$d~}fU fUm]mM}fM fMm]m}fM fMm]m؃D[^_]Í&UWVSd[ê /EuuU pM uȋ}EuċuȉEE$uuB@p]Bur B]]Cك$M]EEEEEEEEEEEEEEԋUE BEĉMMEHMEʋM̉UHMEMEf1M$ddR$dEE̋M̓WWWEEFσ FɉM΃}$]F]~DMU$F J˃@X@@XX Ju؃ MEE|ETEEEcE=EEEEJEEEEEE0EEUȋEEBUЉEEEPEЋỦEEEPEЉEEHEЋUfU$ddQ$d}EfM fMm]mn}EfU fUm]mMQ}fM fMm]m}fM fMm]m؃d[^_]Í'UWVS[/EU}UPMUu GUUGʉUU]]Gك$ʁ} ]]UUUUݕhUݕpݝxAݝ`ݝXAAݝPݝHh< EEEEE݅@EEċUE4BE4uEp4Eu4pu4ufu$d]Ed1]]Vu$du]uEEYEEYY <T1ҊA(A0A8R1ҊW$$1ҊW$$1ҊW$Ή$M$˃E܍`ݝ@EI E܍xEMEE܍X܍pEEEM܍hE܍PEEM݅@ݝ@E܍HEM܅@ݝ@E܅@A ݝ@݅`ݝ`݅XݝXݝx݅H݅PݝhݝpݝPݝHEEPEEsEEEZ&E+E݅@E:&݅@EUEEBU4EEP4ЋU4EP4Љ4Ep4ЋUfU$d]Ed]]V$dU]U}fU fUm]mv}݅@fU fUm]mu}fU fUm]m}fU fUm]m؁[^_]ÍvUWVS|E}uuU OEM܋} E܋uEЁEuԉEM؋uM}HEFMEEEEE1M9׋A EoUEHPMpHuE|4R 1uuuE)4uuu uvu1FuuE)4uuu uvu1Fu)4Mu4|IM1AEƋE+E̍0EƉMEMMȉUMUMĉEMEME|MUMU !¸UU ‰UUE EыU!¸UM ыU MMMѺ!Ǹ ׋U Ѻ!ƋE ֋U܁ BxE؋MHMԋxyMqM1fMM)1҉MMTM)1҉MMT)׊TM)֋EűuỦuuF~V uM|[^_]Ív'UWVS[g/uMEUF]FU} P]Uāك$ʉUU]]FAɁu ]ɉUUUUUݝxAݝpكLdݝhE݅hEE݅hE؋E̋}%18}E%8}%1}ˆ}RE$]Ed]W$EdEE@EQYEd0MAEMFA EFA(ƒEMMEME܍xEEMEME܍pEE]݅x]݅pݝx]݅hݝpEwEE݅hE^EE݅hEEEDžTE̋}%18}ȁ\UT\%}\:}%\ˆERE$ۅT]Ed]}fU fUm]mI}fU fUm۝Tm:}fU fUm]m*؁Ĥ[^_]ÐUWVSEM}}ԋU qEuM }uu}uM1EEFEuEE9׋NM EEEEUMEQ pMuUAM|E4 R΋UEE)֋U UER UBUЋUE)EU̍ |@‰pUBUЋp)‹EEEEEEEEEEEE|ED.؋u! ƋD)؋M!  dL*؉!%1 ϋMd%pU p%pMpыUԈ MQ)ƋduȋuDV)MDVUu)NjE@2Jz EMUĜ[^_]É'UWVS,[ /E}}U xu }܋ME܁}؉EԅكLكHUvEEEEڋE}%18}؋E%8}%1}¸}REE$dP$dUBUYYMFFE5EEE&EEEEEE}%18}UU؊}ԋU%ЋUȁE:U%‹E@R}EEE$ɉEddYYM؃,[^_]}fU fUm]mt&}fU fUm]m$t&}fU fUm]mt&UWVS4[z /E}}U xu }؋ME؉}}؉EЁ ҉}كLكHU&EEEE0EE?ڋE}%18}ԋE%8}%‹E%8}%1}¸}EEE$ddP$dUBUYYY MqFFF EEQEEEEEEAEEEEE}%18}UUԊ}ЋU%ЋU܁E:Ű}%}E}%UˆGEEEEE$ʉ}dddYYY M؃4[^_]}fU fUm]m~t&}fU fUm]mt&}fU fUm]mnt&}fU fUm]m6t&UWVS,[/E}}U xu }܋ME܉}}Eك$…كLكHUt&EEEEۋE}%18}؋E%8}%1}¸}REE$dP$dUBUQQM;FFEE EEEEE&EEE}%18}UU؊Uȋ}%}ԉE̊:U%‹E@R}EEE$ɉEddQQM؃,[^_]}fU fUm]m}fU fUm]mt&}fU fUm]mt&UWVS4[J/E}}U xu }؋ME؉}}؉EЁ ҉}̋}ك$كLكHUEEHEEWEEjًE}%18}ԋE%8}%‹E%8}%1}¸}EEE$ddP$dUBUQQQ MFFF EEbEEt&EEEEJEEt&EEE}%18}UUԊ}ЋU%ЋU܁E:Ű}%}E}%UˆGEEEEE$ʉ}dddQQQ Md؃4[^_]}fU fUm]mI}fU fUm]mt&}fU fUm]m>t&}fU fUm]mt&UWVSL[ê/EuuU pM u؋}E؁uԉEЋuE$@pur]BBكLM]EE=EEEVEEEo؋EM%1MԋE%M%1M¸MREE$dP$dɋE@EEEEWW̓FF}$~:MU$JF˃@@XXJuڃMjEEEEEEEEEEEEEM%1E%UUԊ EU%MMЊEMш@IEMEEE$ddc}fU fUm]mf}fM fMm]m}fM fMm]m؃L[^_]ÍUWVSl[z.EuuU pM uȋ}EuċuȉE E$uuB@p]Bur B]]كLM]EEEEEEEEEEEEEEԋM%1MċE%M%‹E%M%1M¸MEEE$ddP$dɃUBUEEWWWEEF̓ F΃}$]F]~DMU$F J˃@X@@XX Juڃ MEE\EEEECEsEEEE*EEEEEEEEEEԋM%1MЁUUĊMU%ЋÚE UM%ȉEE% UʋMAEEEEE$ʉMddd}EfM fMm]m}EfU fUm]m}fM fMm]m}fM fMm]m؃l[^_]Í&UWVS[G.EUuUPMU} FUUFʉUU]]Fك$ʁ u ]]UUUUݕhUݕpݝxAݝ`ݝXAAݝPݝH=كL4ݝ@ E݅@EE݅@EE݅@݅8EEċ}%18}E%8}%‹E%8}%1}ˆ}E$Ed]Edɿ]]W}E$d@EɉE]EYEEYY 4MFE܍`ݝ8EI E܍xEMFE܍XA(EEF܍p˃EMA0E܍PE܍hEA8EM݅8ݝ8E܍H܅8ݝ8EEM܅8A ݝ8݅`ݝ`݅XݝXݝx݅H݅Pݝh݅@ݝpݝPݝHEEE݅@Et&EE݅@EcEE݅@݅8EJ݅8E!Dž$Eċ}%18},U,%},E%8,$,U}䋕,%ˆGEE$Ed]Ed]]ۅ$d]}EEYEEYY 4؁[^_]}fU fUm]m}݅8fU fUm۝$m}fU fUm]m}fU fUm]mwUWVSEM}}ԋU qEuM }E }}uEx7GOuM|EEEEEEE9U|N MEEEEt|||wQ`xu|\dE(Eă j EЃ}u uDž} M 3)ЃuE EȀÈ-E E 8xEEEE}(uEЃE(U(EЃE(EċEċËEċBEȈ-Eċ%Ẽ} ~Eċ@%EẺEȋUЊËUBEȈ} ~UЃEE EЃE =} E EċE EȋE EEEEX}u Dž DžTTP}uPPXu E E}u"o@Džl @Džl}(E(LLELELEXuL E XPP)EЃ} unDž TPX PEЃ}u u&DžXPEЃETEETEETEEXTEE(U(EU(EU(EU( EXPEЉEEh}yEEd}yEE`}yEE\}yEEEEE=~F EEEEPu uDžEEE}$EHP$d$݅ݝhuDž;E|ۅ܍݃ٽf f٭۝٭lfDž;E|vE)Pۅ܍݃ٽf f٭۝٭lf눋fUEĉ}$EHP$d$݅ݝduDž;E|ۅ܍݃ٽf f٭۝٭lfDž;E|vE)Pۅ܍݃ٽf f٭۝٭lf눋fUEȉ}$EHP$d$݅ݝ`uDž;E|ۅ܍݃ٽf f٭۝٭lfDž;E|vE)Pۅ܍݃ٽf f٭۝٭lf눋fUẺX}ELEHEDDžxx;E|Dž||;E|Dž;E|yLLH|HDxDz|Sx,EDEHELDžxx;E|Dž||;E|Dž;E|DffDH|ffHLxffLn|Gx }$EHP$d$݅ݝ\uDž;E|ۅ܍݃ٽf f٭۝٭lfDž;E|vE)Pۅ܍݃ٽf f٭۝٭lf눋fU E}PEDEHELE@Džtt;E|nDžxx;E|Dž||;E|Dž;E|DDH|HLxL@t@S|,xtE@EDEHELDžtt;E|Džxx;E|Dž||;E|Dž;E|@ff@D|ffDHxffHLtffLG| xt}u Dž DžP}u2h d ` \ Ph%hd%d`%`\%\EE}u݃ݝ ݃ݝ݅ݝ8}tۅP݃݃ݝ ݃ݝ݅ݝ0EHP$d$܍0ݝ(EHP$d$܍0ݝ EHP$d$܍0ݝEHP$d$܍0ݝX}xEEȉ@EЉDPDHPHLDžpp;P|pD‹h3pP$d$܅8܍(݃ٽf f٭۝٭E$pH‹d3pP$d$܅8܍ ݃٭۝٭EĈpL‹`3pP$d$܅8܍݃٭۝٭@pEEȉ@EЉDPDHPHLDžpp;P|(pDh3pP$d$܅8܍(݃ٽf f٭۝٭E$fp Hd3pP$d$܅8܍ ݃٭۝٭Ef p L`3pP$d$܅8܍݃٭۝٭@f p}EEȉ@@ẺDEЉHPHLPL P Džpp;P|{pH‹h3pP$d$܅8܍(݃ٽf f٭۝٭E$pL‹d3pP$d$܅8܍ ݃٭۝٭EĈp ‹`3pP$d$܅8܍݃٭۝٭@p‹\3pP$d$܅8܍݃٭۝٭DpuEEȉẺ EЉ@P@DPDHPHLDžpp;P|p@h3pP$d$܅8܍(݃ٽf f٭۝٭E$fp Dd3pP$d$܅8܍ ݃٭۝٭Ef p H`3pP$d$܅8܍݃٭۝٭f p L\3pP$d$܅8܍݃٭۝٭ f pi9t lEE}~ EQEyUE ; EE}EEUE(UEBUEB$UE؉B UEBUE܉BUEBUE BUE$B E Eԃ}uEEԃ}U u u u DžU䋅 B( E EPy@@u. Ep( uDžMU䋅@B,}E(D}>Dž;E |4 D%D% D% ȉ7Ѝ @D%P$d$@HD%P$d$@HD%P$d$Dž;E |L4 DD% D% D % ȉ7@D%P$d$@HD%P$d$@HD%P$d$@HD %P$d$E(D}BDž;E |4 Df%7 xDfƁDf% Ѝ4@D7@pD@pDDž;E |< DfƁDf% : xDfƁD f% @D7@pD@pD@pD E@(E@,E܉t+Utt]Et t, uE uDEDžUt t) usE uIEmDžDžEt t& u E ukE Dž1}u}t E@UEЉBUEDže[^_]US[J. u$u uuuuu ju0]US[ . u$u uuuuju uc0]US[Ъ.EE}uE@EE@E}t u}5E@E}t}t uO uƹEx(t Ep(,Ex,t Ep, u]US[. u]UVSP[ީ.E@ EE@E+EEDž|DžxDžpDžlDžhDždDž`Dž\DžXDžTDžPDžLMMMEUEB $ EPE}uDž@EEEE܋EEظMEMEE EEE@EEEEEE@ t? $ u uDž?E(E)E*E+E,E-E.E/E%Eȃ}u uMEuȸMM ЈE@E@E@ E@E@E@E@E@ E;EtMȋUEDMȋUEDMHЉE̋E̋U!‹E)ыE̋U!‹E)E̋U!‹E)ыE̋U!‹E)ƋE̋U!‹E)ыE̋U!‹E)0EЋE̋U!‹E)ыE̋U!‹E)E̋U!‹E)ыE̋U!‹E)ƋE̋U!‹E)ыE̋U!‹E)0EԋE;EsMȋUED MȋUEDuUM"MUċEȉDEE j$<Eȃ}u uEE UȸME@E@E@ E@E@E@E@E@ MȋUEDuUM"MUċEȉDEȉE}DUE|tEUDE(uUM MUEDEE}fEEEEEe[^]UWVS[--E@EE@pptpEEȋEE̋EEЋE Eԃ jDC:E}uDžtsEfE@E@E@ E@E@E@E@E@ E@$E@(E@,E@0E@4E@8E@<E@@EUE;B|EEEE܋EEȊ%EEE̊%EEEЊ%EEEԊ%EMEƒEM ‹EM ‹EM ЉEċEf%MU܋EċDEEEȊ%EEE̊%EEEЊ%EEEԊ%EUE)ыUE) UE) UE) ȉE}u E[E+E;EIMHЉEEU!‹E)ыEU!‹E)ЉEU!‹E)ыEU!‹E)<0EU!‹E)ыEU!‹E)ЉEU!‹E)ыEU!‹E)08EEU!‹E)ыEU!‹E)ЉEU!‹E)ыEU!‹E)<0EU!‹E)ыEU!‹E)ЉEU!‹E)ыEU!‹E)08EE;Es M܋UċE؉DEEĉEEMEƒEM ‹EM ‹EM ЉEĊMEƒEM ‹EM ‹EM ЉEE;Eu+E;E} jD#6E}u u DžtE uMĉM fE@E@E@ E@E@E@E@E@ E@$E@(E@,E@0E@4E@8E@<E@@E;EtMUċE؉DMUEDXMHЉEEU!‹E)ыEU!‹E)ЉEU!‹E)ыEU!‹E)<0EU!‹E)ыEU!‹E)ЉEU!‹E)ыEU!‹E)08|EU!‹E)ыEU!‹E)ЉEU!‹E)ыEU!‹E)<0EU!‹E)ыEU!‹E)ЉEU!‹E)ыEU!‹E)08xx;|sMUċE؉D MUċEDu܋UܸMf#fM܋UEDEE] jD{3xxu u> Džt xMfx@x@x@ x@x@x@x@x@ x@$x@(x@,x@0x@4x@8x@<x@@xUċEDu܋UܸMf#fM܋UxDxE܃}NFU܋Eă|tE܋UċDE*u܋UܸMf fM܋UċE؉DEE}EDEEȋEE̋EEЋE Eԃ jD1E}uDžtEfE@E@E@ E@E@E@E@E@ E@$E@(E@,E@0E@4E@8E@<E@@EUE;B|Dž|EEE܋E؍EEE؍EEE؍EEE؍EEMEƒEM ‹EM ‹EM ЉxEf%xU܋xDEEEEEEEEEEEEEUE)ыUE) UE) UE) ȉE}uDž|SE+E;EWMHЉEEU!‹E)ыEU!‹E)EU!‹E)ыEU!‹E)ƋEU!‹E)ыEU!‹E)ƋEU!‹E)ыEU!‹E)0EEU!‹E)ыEU!‹E)EU!‹E)ыEU!‹E)ƋEU!‹E)ыEU!‹E)ƋEU!‹E)ыEU!‹E)0EE;EsM܋xE؉DDž|xEEMEƒEM ‹EM ‹EM ЉxMEƒEM ‹EM ‹EM ЉEE;xu+E;E} jD-E}u uDžt6uxM fE@E@E@ E@E@E@E@E@ E@$E@(E@,E@0E@4E@8E@<E@@x;Et"MxE؉DMUEDZMHЉEEU!‹E)ыEU!‹E)EU!‹E)ыEU!‹E)ƋEU!‹E)ыEU!‹E)ƋEU!‹E)ыEU!‹E)0EEU!‹E)ыEU!‹E)EU!‹E)ыEU!‹E)ƋEU!‹E)ыEU!‹E)ƋEU!‹E)ыEU!‹E)0EċE;EsMxE؉DMxEDu܋UܸMf#fM܋UEDEDž|! jD^*E}u u'DžtUxfE@E@E@ E@E@E@E@E@ E@$E@(E@,E@0E@4E@8E@<E@@MxEDu܋UܸMf#fM܋UEDEE܃}sUU܋x|tE܋xDE3u܋Uܸxf fM܋xE؉DDž|E|EDžt Ette[^_]US[,EE}tcEE}~?E%#Eu"UE|t UEtE E빃 u(]US[Ì,EE}tdEE}~@Ef%#Eu"UE|t UEtE E븃 u(]ÐUWVS\[,U Mtt r;qt e[^_]Ívz }};y uyu܋}tЋExE}B9E}܋ByE}E};xt뙋E};x$u}9}u}}mzE}ċ}}}c}>}UAE}}M~>E}EUURVMQURTiMUʃMUċUOMu1BE؃}y}}UAE}}zM~ҋ}UȋURVMQURMUʃMЉU؋UOMuk}`E}EUURVMQURMUʃMUċUOMuQU}}ME}EURVMQURMUʃMUċUOMu}ҋ}ŰURVMQEP'UMуUЉM؋MOUuoQUԃ}}ME}URVMQURX!MUʃMԉU؋UOMu}E}EURVMQURMUʃMUċUOMu}}URVMQURMUʃMԉU؋UOMu'UWVSP[ ,Eit&Eu |E 1Ҋ1ɋp 7u F%ƋE)ЉЋE)ȋM)ɍ9E vM9E }G}}'U1MuM}TuAM~ًE e[^_]VU REPMQuV}WU RMEtV 됍t&'UWVS[,,M uMu$E<)9E EEU$u<1}UDE1ɋ2 0r1Ҋ0uUUz1Ҋ>}UG)ʉ})y‹E)9E vM1E MAM}mE e[^_]Ë}TtًU$R} OWuVUR}WuVE PUtV 말U܊M$eEmUMԋ|1ɋ2 7r1Ҋ7U؋UBE֋)ʉ@+E‹A)9E vU:E E@Eԃ};u1MԊuEԋU|tыu܅t/E$PM IQuV}WMQuV} WEԋLQ WMQ}WRGP7V}WU RMԋu|WUWVS[,,M Eu$}M}UG)ʉ})y‹E)9E vM1E MAM}mE e[^_]É}Tt׋U$R} OWEuVUR}WuVE PUtV 렐U܊M$eEmUMԋ|1ɋ2 7r1Ҋ7U؋UBE֋)ʉ@+E‹A)9E vU:E }G}ԃ}4u1MԊuEԋUttы}܅t,VuVuNQ~WQ}Wu VLQ 띉}$Wu NVURMQ}WuVE PUԋM|WʐUWVS[,WE|t&Eu |E yp ~u F4xE)ЉE)ȋM)9E vM9E uFu}+U1MtM}TuAM~ՋE e[^_]ÉQE PURMQuV}WU RMEtV 늉UWVS[,M uMu$E<)9E EEU$u<1xTuU>E W~U4G}GWURM Qu܋E|W̍vUWVS[f,M Eu$}ME W~U4G}GE W~U4G}GE W~U4G}G})1}Њ >}); sD &L~n( 1tUȋ}̊ U)1Ɋ >})1}Њ >}); s D P~p( 1t}̋UȊ U)1Ɋ >})1}Њ >}); sD y( 1\L}̋uȊ1u%)Š9%}h)ljҋ}Š9}%d)ljǍ; s D }X‰)9 rt Dž }h8)9 rt Dž U +d9 rt DžX!!M h!!d!ljM\!$IU,G$,$$lh ($DžLDžDž | ʉ)9 )9 & O)9 v( DžPDžHDžDž1{LeDžHDžP \ʋh)9 h)9 g\})1}Њ >});s@&L~n( 1tUȋ}̊ U)1Ɋ >})1}Њ >});s @P~p( 1t}̋UȊ U)1Ɋ >})1}Њ >});s@( 1L}̋uȊ1u%)Š9%}h)ljҋ}Š9}%d)ljǍ;s @ MT)9vTtDž& }h8)9rt Dž U +d9rt DžT!!M h!!d!ljM\!,$UOA$,$$lh (DžLDžDž { ʉ)9)9d O)9*t&( DžPDžHDžDž1LcDžHDžP \ʋh)9h)9\PWUR}WURWPTR \f> f<ʋDDʃ٭<۝8٭>8)Љ!+F!Ћ;~؋U Ef r`F`9؍e[^_]ËMq1;M}11GfG}fV1ҊWfW} fOA};M}|\M@fA YMD3MA t\}A4)Dž ӥF444}4)W}؉ F44XDžDM9Dc 1ҋ1!ʊ! 1!ȋ 1 M DfQB D M9|4DžDžH)ʍD Uȉ9H 1ҋ1!ʊ⋍!Ȋ ‹1!ȃ ‹1 M HfQB H M9{DžLM9L 1ҋ1!ʊ! ‹1!ȃ 1 M LfQB L M9|DžPM9Pv 1ҋ1!ʊ!ȃ 1!ȋ 1 M PfQB P M9|Dž4Dž)ʉ)ʉ)ʋM9 1ҋ1!ʊ!Ȋ ‹1!ȃ ‹1 M fQB  M9{)Dž4M94 1ҋ1!ʊ! 1!ȋ fQM 4fQM B4 9|4DžDž8)ʍD Uȉ98T 1ҋ1!ʊ⋍!Ȋ ‹1!ȃ ‹fQM 8fQB 8 M9|Dž<M9< 1ҋ1!ʊ! ‹1!ȃ fQM })1}Њ >});sD&L~n$1tUȋ}̊ U)1Ɋ >})1}Њ >});s DP~p$1t}̋UȊ U)1Ɋ >})1}Њ >});sDy$1\L}̋uȊ1u%)Š9%}`)ljҋ}Š9}%\)ljǍ;s D}T‰)9rt Dž}`8)9rt DžU +\9rt DžT!!M `!!\!ljMX! IU(G ( lh$$DžLDžDž| ʉ)9)9& O)9v$DžPDžHDžDž1{LeDžHDžPXʋ`)9`)9gX})1}Њ >});s@&L~n$1tUȋ}̊ U)1Ɋ >})1}Њ >});s @P~p$1t}̋UȊ U)1Ɋ >})1}Њ >});s@$1L}̋uȊ1u%)Š9%}`)ljҋ}Š9}%\)ljǍ;s @MP)9vPtDž&}`8)9rt DžU +\9rt DžP!!M `!!\!ljMX!( UOA ( lh$DžLDžDž{ ʉ)9)9d O)9*t&$DžPDžHDžDž1LcDžHDžPXʋ`)9`)9XPWUR}WUR|WPTR肋 Xf> f<ʋDDʃ٭<۝8٭>8)Љ!+F!Ћ;~؋U Ef rF9؍e[^_]ËUMAr1;u1A1ҊfGfW1ҊQfW} fwF;u|`M@fA iMD3MA tcy4E)@Džu}ӥ 44|M4UyB)}؋u 44Dž@M9@U 1ҋ1!ʊ! 1!ȋ 1 M @fQB @ M9|4DžDžD)ʍD Uȉ9D 1ҋ1!ʊ⋍!Ȋ ‹1!ȃ ‹1 M DfQB D M9{DžHM9H 1ҋ1!ʊ! ‹1!ȃ 1 M HfQB H M9|DžLM9Lh 1ҋ1!ʊ!ȃ 1!ȋ 1 M LfQB L M9|Dž4Dž)ʉ)ʉ)ʋM9 1ҋ1!ʊ!Ȋ ‹1!ȃ ‹1 M fQB  M9{Dž0M90 1ҋ1!ʊ! 1!ȋ fQM 0fQM B0 9|4DžDž4)ʍD Uȉ94F 1ҋ1!ʊ⋍!Ȋ ‹1!ȃ ‹fQM 4fQB 4 M9|Dž8M98 1ҋ1!ʊ! ‹1!ȃ fQM 8fQB 8 M9|5Dž<M9< 1ҋ1!ʊ!ȃ 1!ȋ fQM }ԁ)ы>%})‰ʊ >)ω}̋Š >)ϋȍ;s$t& )9V596)9`2H@F@H@8fB%8N|MЋEԋu)1ҊE)1EȊE)ЋU)Ѝ;s$t& U)9=99L7<0)9?@%)‹EҊ%ƋE) ;s $<@8ω׋HDH(8,LЉ0ȉ48f%Z|uԋMЊM)1Ҋ7u)֋UɊu%U)Š7%ƋE) ;s $f% |uԋMЊM)1Ҋ7u)֋UɊu%U)Š7%ƋE) ;s$@f%b|uԋMЊM)1Ҋ7u)֋UɊu%U)Š7%ƋE) ;s$&D f%"|uԋMЊM)1Ҋ7u)֋UɊu%U)Š7%ƋE) ;s$&H f% |uԋMЊM)1Ҋ7u)֋UɊu%U)Š7%ƋE) ;s$&L f% |uԋMЊM)1Ҋ7u)֋UɊu%U)Š7%ƋE) ;s$&(f%R |uԋMЊM)1Ҋ7u)֋UɊu%U)Š7%ƋE) ;s$&, f% |uԋMЊM)1Ҋ7u)֋UɊu%U)Š7%ƋE) ;s$&0 f% |uԋMЊM)1Ҋ7u)֋UɊu%U)Š7%ƋE) ;s$&4f%r|uԋMЊM)1Ҋ7u)֋UɊu%U)Š7%ƋE) ;s$&f%t}ԋMЊM)ъ>}ĉ)׉ы}̋UȊU%)Š>%})lj ;s $Mʋ򉍘)9rt Dž}>)9rt DžM)9rt DžU4+9rt Dž!Ƌ!Ƌ#u!E!!!ljM!AUJlҋhiHDžLDžHDž@Dž7)9)9E'~)9,Dž8fDž<%DžDDžB&tVUR}WuVRWV|WfMU M)9')9&w)9$H8G@H8@fF%@;|6Dž8Dž<DžD]U)9u񉵘)9M)9ta})9UЉljP)9&+)9d( B)91@PuVURuVURVRtV_` &|VUR}WuVURWVTR` J&TPUR}WUR}WRWDP_ &E} fH f WuGM܉UE}9|EUM܉uUȍ Dž )99c)9%Dž8fDž<%DžD1t}Ћ>}ԁ)ы>%})‰ʊ >)ω}̋Š >)ϋȍ;s t& )9V9a)9H@F@H@8fB%81|MЋEԋu)1ҊE)1EȊE)ЋU)Ѝ;s t&U)9T9<0)9#@`%)‹EҊ%ƋE) ;s <@8ω׋HDH(8,LЉ0ȉ48f%9 |uԋMЊM)1Ҋ7u)֋UɊu%U)Š7%ƋE) ;s f%|uԋMЊM)1Ҋ7u)֋UɊu%U)Š7%ƋE) ;s t&@f%b |uԋMЊM)1Ҋ7u)֋UɊu%U)Š7%ƋE) ;s &D f% |uԋMЊM)1Ҋ7u)֋UɊu%U)Š7%ƋE) ;s &H f% |uԋMЊM)1Ҋ7u)֋UɊu%U)Š7%ƋE) ;s &L f%R |uԋMЊM)1Ҋ7u)֋UɊu%U)Š7%ƋE) ;s &(f% |uԋMЊM)1Ҋ7u)֋UɊu%U)Š7%ƋE) ;s &, f% |uԋMЊM)1Ҋ7u)֋UɊu%U)Š7%ƋE) ;s &0 f% |uԋMЊM)1Ҋ7u)֋UɊu%U)Š7%ƋE) ;s &4f%2 |uԋMЊM)1Ҋ7u)֋UɊu%U)Š7%ƋE) ;s &5f%t}ԋMЊM)ъ>}ĉ)׉ы}̋UȊU%)Š>%})lj ;s ?u)9vt Dž}>)9rt DžuM)9rt DžU4+9rt Dž!Ƌ!Ƌ#u!E!!!ljM!AUJlҋh1DžLDžHDž@Dž7)9)9 ~)99Dž8fDž<%DžDDžd tVUR}WuVRWV|WfPMU [)9)9w)9H8G@H8@fF%@{!|Dt&Dž8Dž<DžDmU)9M)9&})98%}ʉ)9UЉƉP)9)9v B)9HDFAHDLAf%L"MЋuԋTP M%)2%)}Ȋ:P%Ut&UЉljP)9C )9> B)9@)ʋMԉ)׋}ȋ>%ʋ})>%)lj;DDHLHLDžf%*LuЉTU1T%)‹EҊ É)}Ȋ >})ϊ ΋M)ύ;3T;#<<@L@LDžf%L}Љt9}ԁ9)֋}%t)ҍ2PEȊU%)‹E%t&4BHDHDDžfL%LDMuЉD0DEԁ)ъ)׋U}Ȋ>Dx)ʋMԉ)׋}ȋ>%ʋ})>%)lj FwDž8fDž<%DžDDžTZREPURuVRVRtV?M} 9]88@Hf@%HDžrTWURuVWEPRV|W ;DDHLHLDžf%0LuЉU1%)‹EҊ É)}Ȋ >})ϊ ΋M)ύ;w\<@})ϊ ΋M)ύ;L@<@<DžfL%Lt_L}Љl9l}ԁ)֊9}ā)ɍ1uȉPU 0É)ʋlTTREPVUR}WVRtV{6 TVWuVUR}WVR|VVUR}WuVURWVDP5M}uUă DV}WuV}WVWV|W5NDž8fDž<%DžDDž6DDHLHLDžf%LuЉU1%)‹EҊ É)}Ȋ >})ϊ ΋M)ύ;0DfTVWuVUR}WVR|@})ϊ ΋M)ύ;XDž8fDž<%DžDS|jOWjRWRWTR13M E})ϊ ΋M)ύ;隵HDFAHDLAf%LMЋuԋT M%)2%)}Ȋ:LjIQPVQPVQtV'7|jOW)RPRVWTR0)'|`jNV)RPWVR|W(T3jJR)ƉWRVWPT[jJR)VRVRPtV2(|jNVPWPQVWLQn%WURVuV}WPRDPPuVURWuVRWtV?tEjOWPQPVWQtL6jNVPWQVPWtVTjNVjDjNVjtjOWPQPVWQtTsjNVjLyjNVPWQVPWtVD*jNVjMEDžuxP Jx,9 ݝGݝGݝGݝu11R1$ݕT$1ҊT$$D$$$݅݅݅݅˃Dž;ٽf fϋ݅)AAAɃ ٭۝٭㋅)Љ!)F!;x؋M 48Ef4yG9e[^_]ËUr1ɉL;M?E1҉NJ1GfG}fV1ҊWLfW1ҋ}WfW} fOA};M}|U fB M fqU$݅MA tk}APP)DžӥF KUuJ8V@)~؍NP<DžxE9xȥ1ҋP1!ʊP!ȃ@ 1P!ȋ< 1P!ȋ8 1 M xfQB@x@< DžDžDžDž1T PRURURWPWTRM} PRuV}WURVPTR[ DžDžDž1|jOWRVW RP|WU E  Dž1:|PVURWuVRWDP  Dž1hRPWuVUR}W VR|W{ 2|!jNVRPWVR|W$  Dž1|PVURWuV RWDP   Dž1RDžDžDž1tjNVjRVRPTR B@G 1\|  F@G 12L|jNVRPW VR|W-DjNV)ΉVQPVQtV|njOW)RWRWRDPdTjJR)ljVRWVRDPtjIQVWQVWtVH F@G 1aTjOWjRV WRtV, AG A1ъDžDžDž1tjNVjRV RPTR@DzjNV)ΉVQP QVLQ|HjOW)RWR WRDP'tjIQVWQ VWtVTjJR)ljVRW VRDPPWURVEP WRtVt\jOWPVWVWTjOWjTjOWjRVWRtVT[jOWjt8jOWPVW VWAEDž(uP xJ9(x, DžݝGݝGݝM݅1D1D1݅݅;Dž Dž$9ٽكf f vˋ DD˃٭۝٭)Љ! +$F!Ћ$;$z؋$ȋu (fNEA(9e[^_]ËE1x;u]UfBf ff%fGfOMfAfM%fG} fwF;u|}fG YMfA EuF ta}DžXG)ы}TӥX o ዽ)UM؋q։ 8ውDžddDž)ʍD d`Uȉ\9!ʃ\⋍!Ȋ` ‹!Ȋd ‹1 M fQBM9n*DžM9!ʃ!ȋ !ȋ 1 M fQBM9|DžppDž)щpLRph)ЋMl9;!ʃh⋍!Ȋl ‹!Ȋp ‹1 M fQBM9nDž||Dž)ʉ||)ʉx)ʋMt9W!ʃt!Ȋx ‹!Ȋ| ‹1 M fQBM9nDž44Dž)ʍD 40Uȉ,9wX!ʃ,⋍X!Ȋ0 ‹X!Ȋ4 ‹TfQM fQMB9oDžM9X!ʃX!ȋX !ȋT fQM fQBM9|HDž@@Dž)щ@LR@8)ЋM<9X!ʃ8⋍X!Ȋ< ‹X!Ȋ@ ‹TfQM fQBM9ocDžLLDžP)ʉLL)ʉH)ʋMD9PX!ʃDX!ȊH ‹X!ȊL ‹TfQM PfQBPM9oUWVS\['+MQMAt#50-c/e[^_]ËMEDž1E~Nu}9}UȉM̋Uu؉MЉ}܉E1Dž}؋u1DžFu܉MM>1}u}}E Džuӥ !u M!ȋM ȋME!1 Mt EЋtMȉqḾq P )) Ћ) Y );@ )ȋ)ȋ)ы4MȉK)919 8)9\DžDžDž1ъ'LvuN)׋u̍ DžDžDžDž1T PRURURWPWTRM} PRuV}WURVPTR{ DžDžDž1|jOWRVWRP|WU E  Dž1:|PVURWuVRWDP4  Dž1hRPWuVUR}WVR|W蛰 2|!jNVRPWVR|WD  Dž1|PVURWuVRWDP褯   Dž1RDžDžDž1tjNVjRVRPTR B@G 1\|  F@G 12L|jNVRPWVR|W֮-DjNV)ΉVQPVQtV԰|njOW)RWRWRDP脰TjJR)ljVRWVRDPtjIQVWQVWtVh F@G 1aTjOWjRVWRtV5, AG A1ъDžDžDž1tjNVjRVRPTR`DzjNV)ΉVQPQVLQ|HjOW)RWRWRDP训'tjIQVWQVWtVTjJR)ljVRWVRDPPWURVEPWRtVt\jOWPVWVWTjOWjTjOWjRVWRtVT[jOWjt8jOWPVWVWAEDž$uP xJ9$x,ݝGݝGݝ$M݅DDD݅݅;DžDž 9ٽكf fˋDD˃٭۝٭)Љ!+ F!Ћ ; z؋ ȋu $fNEA$9e[^_]ËEuP 1;}bfFfff fPfHfFf%fAM fyG;}|}fG tMfA `uF thUEz)DžTPu}ӥT sdዽU)}r؉u} ውDž``Dž)ʍD `\UȉX9!ʃX⋍!Ȋ\ ‹!Ȋ` ‹1 M fQBM9n)DžM9!ʃ!ȋ !ȋ 1 M fQBM9|t&DžllDž)щlLRld)ЋMh9;!ʃd⋍!Ȋh ‹!Ȋl ‹1 M fQBM9nDžxxDž|)ʉxx)ʉt)ʋMp9|W!ʃp!Ȋt ‹!Ȋx ‹1 M |fQB|M9nDž00Dž)ʍD 0,Uȉ(9wT!ʃ(⋍T!Ȋ, ‹T!Ȋ0 ‹PfQM fQMB9oDžM9T!ʃT!ȋT !ȋP fQM fQBM9|HDž<<Dž)щ1}uč}}EDžNj }|苽 Nju}ĉxӥ Nj!ȉ !ϋu0 El}|!Njux !}u ω}f%Uȋ}̋t rwMЍqMԁ q)ȉ$)ʋ| Ћ)ʋx Ћ) );)ʋ)|)ʋx)}ȍ O0)9`9I;׋0)9Džf%DžDžIL}ȋ4O)}̉4O)}-4O|)‰|uԋ|G@ G f%nG|UȋE zU) xE)-‹E x)΍u~uʉ)-;s "U)99E6 0)9O3GG G f%{F|UȋE zU) xE)-‹E xE)-‹E x)΍;st&΋֋ ȋf%TuȉE)9rt Dž0u|0)9rt Dž=U +x9rt Dž!h!M0!!xMl|!!ǃ!!UɋJuF,(>Dž DžDžDž~0)9)9(0)9rt DžPu|0)9rt DžU +x9rt Dž!`!M0!!xMl|!!!΃u!NɉUB,(&Dž DžDžDž0ʋ)9)90w)9 DžfDž%DžDžLyDžDžDž l)9W )9F l}u}WExhPxYPUREPVQMQZ1k}WEx#Px!PUR}WVQUR'ˋEՍBڍB뢋E됍t&'UWVEEu1EEEEEEEEEEEEEMU$u;E} \@;E|Ut&׋E)Njt؉tB~uE uЅ}؉M}}ȋU4}utE }EUЋMЃ}n ƋẼGGG E9wjt&Vtمtĉt]مt]EFV tمt‰t]مt]EY;Mv؉;MrD;Mru }U M }M ȍ4uuĐ^_]؋u }U M }M ȍ4uuĐ^_]ËUGGG 9Uw4]@]E@]@ ]EY9v*UGG9Uwf@]]E@@ ]]E@Y@Ƀ]]EY9vt&UWVM$u}Ѻ} }Ѻ}ыUMу}UM}JUU܍v19-D\D\D\9~;E}(čP;U}Ã;E}M܋EU <Ǎ4u؃^_]؋M~MQMMM MM19)D\D\9~݋ENjEIu؃^_]ÐUSQ[+QME PQQp]ÐUWVEMU }EqE;rt E^_]ËA E;B u9uAE;Bu׋AEBEEĉE܋BE؋AEԋB$E̋A$ME΃}th}W}hwG  w 1 ˆXE9 U9~E99, MUЋMʉ։UN}P1MЊX}Љ)} …WEEE})Eu܊MEeщtÚ)Љ|PEuUNEu M̋})Ǎ+EЉEMى$t@MЉ9U M"W!" GF$$t9$}9W"W"ON$t$9$|NjM9$}Kʃ9$G"WF$$M9$}MW"!" ЈM}E؋uPM}EAMDž0ً0$ME);}䉅0;$twMЉ9U3M苅$);0~1ҊWUMEe0UЋ|!ȉM!G" F$UU$t9$}mM1ҊWeЊ0MU1"WЊOMMU0"WF$t$9$|U9$у9$=M1GGeE0EM"WF$MM$U9$8Dž,$M)ӥ,;0~1ɊOMME0|}M,!Њ,!" ˆ7W1 wG 1 1XX1XX<UˉUˊXUE]}܅^EʊM%EEUEEM)xE<}UЋuO)Mσ@}űM)+UЉLH,EʊX#$EЊU؉DDž$UЋ$M)֋}1҉$X$1 EˉƉϊM$M MŠM$@9UL$AL!M#$" ЉD}$#DUGx9DML$""NO#"NOLDxD9D~E9DE9DcL$A!L$DEGD9D)A!ЉLGD#$DE9D}+LM $"$! ЈE}M؋Ut A9Ft e[^_]ÍvA 9F u;9uA9Fu߃t)w Ot=˃t#uPURQVr1PEPQVR}WQV4PURQVFҍt&USQ[.+QME PQQ0]ÐUWVS<[+MEu xUątMF9Ate[^_]Í&F 9A uM;u;~uۺ}wЊM1QP1,$9}M\@9|؃}t5}w }tK늃}t-}u}PWVuV@1hW}WVEPzRURVuV׍MPQVURƍUWVS<u[+U~19}Lك$DŽF;},ٝمtٜF;|1;ك̞ك(ل1 BJ tٽf f٭۝٭ B~؉G;z؍RWM QURIe[^_]Ív'UWVSl[×+EM PxpQx pAщ։Dž9كٝكHٝكLٝuDž9ٝمNممم&)Љ=~ Džϋы}u}WExhPxYPUREPVQMQ1k}WEx#Px!PUR}WVQUR'ˋEՍBڍB뢋E됍t&'UWVEEu1EEEEEEEEEEEEEMU$u;E} \@;E|Ut&׋E)Njt؉tB~uE uЅ}؉M}}ȋU4}utE }EUЋMЃ}n ƋẼGGG E9wjt&Vtمt̉t]مt]EFV tمtʉt]مt]EY;Mv؉;MrD;Mru }U M }M ȍ4uuĐ^_]؋u }U M }M ȍ4uuĐ^_]ËUGGG 9Uw4]@]E@]@ Ƀ]EY9v*UGG9Uwf@]]E@@ ]]E@Y@Ƀ]]EY9vt&USQ[^*QME PQQ]Ít&'UWV U}u ~SMUU MU1;E}(M$M~‹MUIu@;E|؋MUMuƒ ^_]ÐU1WVUS[ò*<׉Uċu EttM~9yt e[^_]Ë~ 9y u>99u~9yuEPURVQ' ͍vUSQ[>*QME PQQ`]ÐUWVEMU }EqE;rt E^_]ËA E;B u9uAE;Bu׋AEBEEĉE܋BE؋AEԋB$E̋A$ME΃}th}W}hwG  w 1 ˆXE9 U9~E99, MUЋMʉ։UN}P1MЊX}Љ)} …WEEE})Eu܊MEeщtÚ)Љ|PEuUNEu M̋})Ǎ+EЉEMى$t@MЉ9U M W!" GF$$t9$}9W W ON$t$9$|NjM9$}Kʃ9$G WF$$M9$}MW !" ЈM}E؋uPM}EAMDž0ً0$ME);}䉅0;$twMЉ9U3M苅$);0~1ҊWUMEe0UЋ| ȉM!G" F$UU$t9$}mM1ҊWeЊ0MU1 WЊOMMU0 WF$t$9$|U9$у9$=M1GGeE0EM WF$MM$U9$8Dž,$M)ӥ,;0~1ɊOMME0|}M, Њ,!" ˆ7W1 wG 1 1XX1XX<UˉUˊXUE]}܅^EʊM%EEUEEM)xE<}UЋuO)Mσ@}űM)+UЉLH,EʊX#$EЊU؉DDž$UЋ$M)֋}1҉$X$1 EˉƉϊM$M MŠM$@9UL$AL M#$" ЉD}$#DUGx9DML$  NO# NOLDxD9D~E9DE9DcL$A L$DEGD9D)A ЉLGD#$DE9D}+LM$"$! ЈE}M؋U*QME PQQ]ÐUSQ[*QME PQQ]ÐUWV UM tt r;qte^_]Ðz }};y u:}};9uԋz}};yu}tzB}EQI}t>}u}WExhPxYPUREPVQMQS1d}WEx#Px!PUR}WVQUR ˋEՍBڍB뢋E됍t&UWVEEu1EEEEEEEEEEEEEMU$u;E} \@;E|Ut&׋E)Njt؉tB~uE uЅ}؉M}}ȋU4}utE }m؃UЋMЃ}n ƋẼGGG E9wjt&Vtمt쉕t]مt]EFV tمtꉕt]مt]EY;Mv؉;MrD ;Mru }U M }M ȍ4uuĐ^_]؋u }U M }M ȍ4uuĐ^_]ËUGGG 9Uw4 ]`]E`]` ]EY9v*UGG9Uwf `]]E`` ]]EY``Ƀ]]EY9vt&UWVM$u}Ѻ} }Ѻ}ыUMу}UM}JUU܍v19-$d\d\d\9~;E}($ǍP;U}$׃;E}$M܋EU <Ǎ4u؃^_]؋M~MQMMM MM19)$d\d\9~݋ENjEIu؃^_]ÐUSQ[n*QME PQQ]ÐUWVEMU }EqE;rt E^_]ËA E;B u9uAE;Bu׋AEBEEĉE܋BE؋AEԋB$E̋A$ME΃}th}W}hwG  w 1 ˆXE9 U9~E99, MUЋMʉ։UN}P1MЊX}Љ)} …WEEE})Eu܊MEeщtÚ)Љ|PEuUNEu M̋})Ǎ+EЉEMى$t@MЉ9U M2W!" GF$$t9$}9W2W2ON$t$9$|NjM9$}Kʃ9$G2WF$$M9$}MW2!" ЈM}E؋uPM}EAMDž0ً0$ME);}䉅0;$twMЉ9U3M苅$);0~1ҊWUMEe0UЋ|1ȉM!G" F$UU$t9$}mM1ҊWeЊ0MU12WЊOMMU02WF$t$9$|U9$у9$=M1GGeE0EM2WF$MM$U9$8Dž,$M)ӥ,;0~1ɊOMME0|}M,1Њ,!" ˆ7W1 wG 1 1XX1XX<UˉUˊXUE]}܅^EʊM%EEUEEM)xE<}UЋuO)Mσ@}űM)+UЉLH,EʊX#$EЊU؉DDž$UЋ$M)֋}1҉$X$1 EˉƉϊM$M MŠM$@9UL$AL1M#$" ЉD}$#DUGx9DML$22NO#2NOLDxD9D~E9DE9DcL$A1L$DEGD9D)A1ЉLGD#$DE9D}+LM0$"$! ЈE}M؋U1NuƉUƉ!փtTp[tNOe[^_]ÍE@04VVVPRQVR =OvMg UR}WuVQRPWVRY(01} MQUR}WPQ* t&1҃WURWPQVRWX ZMl UR}WMQRW**n).} MQUR}WVQRWPV*M MQUR}WVQRPWV)\}u UR}WMQRPS?wM: UR}WMQRP$؋9t Py1e[^_]؋9X <ȋم݅ɍ<֍4э Ћݝݝݕ݅Eeٽ݅f f٭٭@9AݝݝݝAݝXݝGFݝPGݝxݝHGFݝ@F ݝpݝ`FG @@@AAݝhA @ ݝ8ݝ0Bݝ(Bݝ BݝB ݝ݅܍܍ ܍(܍0܍8܍P܍X܍`ݝ݅܍@݅݅܍H݅܍h݅x܍pݝ݅p܍x݅݅h܍ݝ݅`܍݅݅X܍ݝ݅P܍݅݅H܍ݝ݅@܍݅݅8܍ݝ݅0܍݅݅(܍ݝ݅ ܍݅݅܍ݝ݅܍݅݅Eݝ݅݅EB9\\\Ћ P щ9;tt Wte1[^_]ËLȉу1҉9مpٽf ݝ݅hمdf݅0͋܍@܍@݅(݅ ݅0݅(E݅ DuEu٭٭EuEu݅ʋ٭٭ɍ4\;19 ٽمpf ݅hمdf݅0̋܍@܍@݅(݅0݅(EDuEu٭٭EuEuɋ٭٭ʍ4T;$;݅@܍0܍( |19ٽمpf ݅hمdf݅0͋܍@݅@݅0EDuEu٭٭EuEű٭٭ʍ4T;|6;݅@܍0݅hEtcمdEtca;9݅@܍0܍(܍ ݅hEuمdEuٽمpf f٭٭׋<4FQU1WVS|[7*ujU Rh(VWQRUPR …}N1NutxϋNjU!׃p~at~Xt+Ce[^_]ÍvE`UREPQR1҃uP}WVVPRQVb 1{PPWRIQOWRQM &UR}WVQRPWV)kW}WPVRQWV8URMQRQNMQURVPWRQVyU1WVSl[ 5*u(j} WhVQPERWP+ …NM>N1NuƉMƉ!΃tJp t፶EJ04VVRPIQOWVREK wve[^_]ËMw拍QURjjw URMQURWP 1҃uVuVPRQWVRdx 1ZMJ URMQURWW끋M URMQURWWlLE EPMQURWWG!MPURjj΋d MQURMQWW7| u^ uVMQ}WRVQWPRЇ0M MQ}WuVRQWPVRJN!먋uR}WjjЊ UR}WuVQRWPVQ2MB MQUR}WVQRWPVg}QURjj MQUR}WVQRPWViRMQWVRQWVn UREPMQWW MQUREPWWC c UR}WuVQRWVPQ MQUR}WVQRWPVhU1WVS|[þ.*ujU Rh(VWQRUPR …}N1NutxϋNjU!׃p~at~Xt+Ce[^_]ÍvE`UREPQR1҃uP}WVVPRQVB 1{PPWRIQOWRQD &UR}WVQRPWVR%kW}WPVRQWV8URMQRQOIMQURVPWRQVuU1WVSp[+*ujjjhjU R(VWQRUPR0…EN>1NuƉMƉ!΃tTp[tNOe[^_]ÍE@04RRVQJRNVQRB ;OtMe UR}WuVQRPWVRǀ01} EPURMQWR 1҃R}WQRVWQPn \}l MQ}WURQP* p).} MQUR}WVQRWPVM MQUR}WVQRPWVz^}u }WMQURPQyM: UREPMQWRX|>u uVMQURWVQRPW5iPURVWQRPVeU1WVS|['*ujU Rh(VWQRUPR5 …}N1NutxϋNjU!׃p~at~Xt+Ge[^_]ÍvEoUREPQRə1҃uP}WVVPRQV 1{PPWRQWRQ= "UR}WVQRPWV'gW}WPVRQWVK 4URMQRQ{GMQURVPWRQV%xU1WVSl[N$*ujU Rh(VWQRUPRe …EN>1NuƉUƉ!փtZpttO+e[^_]ÍEM04RRVRQVRQq: qOjMa UR}WuVQRPWVRsy01}jURjj{ MQUR}WQPG t&)1҃RURQWVRQP(g @MLj}Wjj-{ MQUR}WQR T]}jURjjz  MQUR}WVQRPVW]eMtjuVjjUz/ uVMQURWVPQRWM URMQEPWR襩)M UR}WMQRPtu uVMQURWVQRPWgn/ MQ}WEPRW蠡 t MQuV}WRQVPRW} EP}WMQRW覿 MQUR}WVQRWPV[P}WVRQPWV\US[L*URMQjjjjURE PURܸ]ÐU1WVSl[*ut:> Ȩt0QQEPURMQjjjj}WU RVȸe[^_]jM hQVR(QMRPQȹ …uMtN1NutƉUƉ!փp~Ut~Ld} MQ}WURQP 1҃WMQWPRVQWa E04VVVPQRVQ~3 M uVMQ}WRVQWPRf|01} URMQ}WPRFp}W uV}WMQRVWPQRrdQEPRVQWRVX\UWVPMEQyU}U q ‰u;U~ EEE u};u~ UEU}tt#P^_]E u݋qEyH}uEEyeEMWE؅E tM܋EU1;u ]UE}PEM)UEM1EU9U}$M܍9MȋMEċM+EHuFU;u |1;}}^MUʋuE)ƉMuuNt&U9U}/EUƋMȋMEM+EHuGN;}|1;u}01M~M܍9MEMHuFU;u|1;}}3uNE~!EMȋMEMHuGN;}|M1P^_]Ëq}QOUԉuE}y ɉMMx̋MȅM t}̋ME1E;} }TUM)ʋEЉU1UЉUE9E}(ŰM2ȋMԍEM+Et&HuGE;} |1;}}UMUʋuE)ƉMuuNU9U}+EUƋMȋMԍEM+EHuGN;}|1;u}01M~M̍9MԍEMHuFU;u|1;}}3uNE~!EЋMȋMԍEMHuGN;}|UWVMEu Qy U}QUU;U~ EEE}E;}~ UEU}tt&bĴ^_]E uڋyV}A~M}܉UuEEIu܉MyɉMMMЅM tuEMu9M}U)Dž\}&E9EU}uԋ\U܍ }4}U} E})`}`JuU؋\B}U؋E9E؉\wDžXu9X}UE)H}dM9M}bM싅dU}uԋU }4}U} E})h}vhJuXdBNXMd9XmDžTE9TDžpDžl}~=uԋl}Mԋp4ȋ}U }&JuTl@pu}܉TElp9T|DžP}9PMItU~BUt‹M܋tыuԋ}4}E U}JuPtAOPEt9P|M21Ĵ^_]Ë}؋\GE}؋u9u؉\AyQNFM}̉Eȋ}EEUH}Ey땍EMxEE tMEɋuM9uU})DžxUu9u}eUċ}uxUu̍ <΋MȋuU4MEMM|)ыVW|֋UIuuxF}uEx9EoDžLU9L}uE)H}EM9M}bMEUċ}uUu̍ <΋MȋuU4MEMME)ыVWU֋UIu鋽LuGNLEu9LsDžHU9HEEM~?}UűME<Uȍ4ʋMUUVWU֋UIu鋕HMB}uEƉHM}u9H|DžDu9DEHE}~KUċE‹MUы}uMȍ<Ƌu4ыMMMVWU֋UIu鋽DuGNDEu9D|GUWVU M Ez r}uzp}zu}pzu]A}E܋u]A@}]AUU]EU9UMU)щMIxMȋEAM9EM܋u)E$ItۋUȋuM֋UuȉEЅҍ<}ԉ}~ U<׉}EE9E}u}M)|tEuԋ|u]1|M];tudu܃EʋuMu]E]Mɋu]EMEMEMEME‹uEMEMɋuMEMEMddd;tu+uEEp9}^udp)vEˋdMMEddOup|)|l);}}=u܋U)vlEMMEJu؋x9U} }ԋMωEԋMЋŰ}BŰu9ủEMȋEAM9EOĘ1^_]Ð&UWVE U Mz r]@}u]@yr]@]@ }]@(z}y]J}@0MɉM]@8UDžl]@@]EU9UhMU)щh`hHd\uȋEFu9E&M})E$ItۋMȋEUȍ4ȋEMu؉t<щ}u؋hp~}؋`ω}EE9E}u}Ml)PXtlEɋpݕxMu‹PE t݅xMˍ tEʉpE}E4ЋEẺ|U)֋l<D8t&UEUuU uudB]B]]B B]B0ɉ}`}]]B(ɋu]]B8]]}ɋT]1];8hElE]E]E]]EMMEMEEMEMEMEMEEEMEMEM񋵬򋵬;8 ɋ|+uݝ EEEE49l4) v݅ ݝ ݅ M͋ME̋MEEɋEEOu4D)֋D);|J؋M dU}A@AHݝAPݝݝA`AXݝApݝݝAhd`ݝݝAxɋ}ݝݝTɍ <1ݝݝ90h݅l݅ݝ݅ݝ݅ݝݝ݅܍܍݅܍݅݅܍݅܍݅܍݅݅݅܍݅܍݅܍݅܍򋵬񋵬;0ˋD)֋D);|l|) ݅ ݝ ݅ M̋MEMEEEEHu1ɋ|+uݝ݅݅݅݅,9il,)݅ݝ݅܍͋܍݅̋܍݅݅ɋ݅݅Oe,֋D)֋D);|؋@9\}dHd`x\@4ω\M`9\3XlAX9XCP1^_]ˉ֋D)֋D);|P|)݅ݝ݅܍̋D܍݅܍݅݅݅݅JuLdHdLdH‰ds|})El~E|)‹lEJVJv'UWVE Uux H|xzHtxpzPlh҉d0Dž@ @< xE)ƃp DždP9d{\ EM͋d˃ʼnd݅̋P݅M݅݅݅E݅݅݅9dUDždP9d\EɋdMʃdMɋPE9d~DždP9d3\EM̋dʃ݅ʼnd݅̋P݅M݅݅E݅9d_DždP9dd\EM͋d˃ljd͋PME9d~DždP9d\EMȋd˃Mɉd‹PE9d~}ԋu}E؋}MEMuU }[^_]&UWVS2E MxxP qx[)x@E)‰uBƉhQ4 1u Dž@~ RDžh9mh)ϋ9~Dž9ك$ٝlG9)EItɋщ 1;}مl@;|؋muمlIٝptdDžt9)}<<BݝBBɍ1ݝ1;v݅ˋ܍ŋ܍݅D\;|؋< tщ<9E=*UMfQ}X,u}9U\EdE)‰(MfUM08э :U}47},}0‹8ыLd,Ћ4LHЉHEЉEE(ELfH%8fU%4fU%08‹4UЋ0ȋMъM=Uf1^_]ËXU`L)‹H$`)ЋU쉅 `)‹Eԉ9EU)Љ MfUMэ :U}7},}‹ыE؋`,Ћ E$f %f%fU%‹UЋȋMъM= ~rMf&\9E}}t׉}u܋MUAM`\PL,Džf z$fyf ~%4 L(P$X0,T8Ƌ`ȋ\Ћhы@Ћ<d‹HpHDlH‹ыX΋P 1;E}t& @;E|F;u|_ظH5fTEf8QG$ы|ΉTH;H8;}CE ‹)Tf%P$TJu1;M}uTTA9|1;} }8EL4ϋM LDIu} M()΃9}XNXLF;x BLB!񉕴93B9 t98t 8VO$1e[^_]à R#<<ʍt&U  Q#8D됃׋XTσLHq( @ݝ0Ed99 Ш1;`ID܍0@D܍0D\;`~ыXLTHt-=BdFݝF FFݝݝBʋ}9ݝݝBBݝݝB B(9 ݝxݝpݝhݝ`1;`݅݅ݝ݅݅ݝD0܍ʋ@ݝ݅݅܍݅܍xݝ݅܍pD(݅܍hݕ܍`݅܍݅܍x݅܍p܍h݅܍`D\;`dFFݝ8}9Bݝ0ݝ(BBݝ B 9 Fݝݝݝݝ81;`B݅(݅0݅8ݝ0݅ˋ@ݝ8܍ D D(݅8܍ݕ(݅0܍܍ ݅݅8܍݅0܍݅܍D\;`1n1;`م\مXمTݝݝݝm݅܍@݅܍݅܍(݅܍ ݅܍ݝ݅܍݅܍DݕX݅ܥ݅ōX܅Xܥ.غȋH5݅fЋ5fQH@T\щTH;`T݅f%݅fJ%LD݅ʋdݝM`݅݅ʋ9ݝݝ݅ۅ`ݝݝD0D8ۅd܍\݅܍ݕݝݕ݅ɋ@܍(݅܍ ݅܍݅܍܍ݕX݅ܥ1݅ƍX܅Xܥnع݅܍@݅܍݅܍(݅܍ ݅܍ݝ݅܍݅܍DݕX݅ܥHȋ@5f݅fB\THT?܅ٽfff fd܅Xܥ٭d۝`٭f`܅ٽfff fd܅Xܥ٭d۝`٭f`PdF}9BBBݝ9 Fݝݝݝݝ 1;`~D ݅ݝ݅ˋ@D܍܍݅݅݅݅݅܍D\;`qe1;`م\مXمTݝ@ݝPݝHM݅X܍@݅܍݅܍x݅܍p݅܍h݅܍`DݕX݅HܥPōX܅XܥPغȋH5݅@fЋ5fQH@T\щTH;`T݅f%݅fJ%LD݅ʋdݝM`D(ݝX݅ɋ9ݝۅ`܍݅Xݝ܍D0ۅd݅\܍xɋ@݅܍p݅݅ݕ܍hݝ܍`ݕX݅HܥPmčX܅XܥPNع݅X܍@݅܍݅܍x݅܍p݅܍h݅܍`DݕX݅HܥP>Hȋ@5f݅@fB\THT{ٽfff fd܅XܥP٭d۝`٭f`ٽfff fd܅XܥP٭d۝`٭f`v1;`م\مTݝݝhمX݅0̋@˹D݅hXۺȋH5݅fЋ5fQH@T\щTH;`jTDf%fJ܍0%ËLDdM`ۅ`9Dۅd\@ݝP݅hX܅Pٹ݅0̋@D݅hdHȋ@5f݅fB\THT;`Vٽfff fd٭d۝`٭f`ٽfff fd܅P٭d۝`٭f`d}9BB9 Fݝݝݝ1;`~݅ʋ@D܍D܍݅݅݅D\;`~1;` م\مXمTݝݝݝ3݅܍ @݅8܍݅0܍݅݅(܍DݕX݅ܥnčX܅XܥغȋH5݅fЋ5fQH@T\щTH;`T݅(f%݅0fJ%LDdM`݅8ʋ9ݝ0ۅ`܍ D(ݝ8܍D ۅd݅8\܍ɋ@ݝ݅0܍݅ݝ(ݝXܥ݅X܅Xܥ2ٹ݅܍ @݅8܍݅0܍݅݅(܍DݕX݅ܥHȋ@5f݅fB\THTٽfff fd܅Xܥ٭d۝`٭f`(ٽfff fd܅Xܥ٭d۝`٭f`1;`م\مXمTݝݝݝڹ݅܍@݅݅݅܍DݕX݅ܥغȋH5݅fЋ5fQH@T\щTH;`ɋTݝf%fJ܍%݅LDdM`ۅ`9D D܍ۅd݅\ݝ݅ɋ@݅ݕX݅ܥ(čX¹܅Xܥٽfff fd܅Xܥ٭d۝`٭f`ÍXº܅XܥtVٽfff fd܅Xܥ٭d۝`٭f`81;`م\مTݝhݝpمXڹ݅x܍@݅݅DݕX݅pغȋH5݅hfЋ5fQH@T\щTH;`oʋTݝxf%fJ܍%݅xLDdM`ۅ`9DD܍ۅd݅\ɋ@ݕX݅p\ÍX¹܅Xٽfff fd܅X٭d۝`٭f`Xº܅XtLٽfff fd܅X٭d۝`٭f`u ظٽfff fd٭d۝`٭f`ZPu0VM(QU$RuV ȨDž 98DNjT‰T 9~888 GV88y @p@ @G@V@y @p}9J9 Ш Dž 9&8DLʋ‹Ћ‹Ƌыʋʋ‹Ћ‹ыTʉT 9888zq @@yr@@r H@P9E9 ШDž 98|D‹Ћʋыȋȋ‹Ћ򋕼֋֋ЋT‰T 9Dž 9f=A`fOTDNO 9Ox|t|8TxLtMʋfD%`fADfV%D|ʋ‹Ћ‹ƋыxЋ4 x΋|ʋ‹Ћ‹NjыtЍ TЁf7=`fW88>P@x@@@pBQ E99 Ш Dž 98| DЋ‹ȋʋ‹򋵴DЋ D9:Dž 9f=`fJTD z~ 9Q8LDMʋfD%`fADfT%Dʋ‹Ћ‹Ƌ4 ΋ʋ‹Ћ‹ TЁ0 f7=`TfGDNO 98@p8@@VA|U9x9 ȨDž 98|T Ƌ|xȋ|xDȉD 9l.Dž 9 f=k `fQTDN N9}8XL|Mf dʋDd`fAdЋD%DыX‹ȋʋʋ‰XЋ‹Ƌ򋵠ƍtӽXXX Xf=`fJTD4QQ Dž 9jT(f=T`fQD(Љ 984EXt\DfЋ\%`fB‹\TX׋ ʉXLΊӽXXXXf=`fA@8@29Ptu9p9 ШDž 9l8L|t‹pЋtȋpDȉD 9~Dž 9T҉4f=`fB4񋅴D 9NX8L| Mf dʋDd`fAdЋD%DϋʋXȋʋ‹X‹ƍtӽXXNXXf=<`fN1Dž 9T҉0f=~`fB0Dȋ 9 8dD |Ef`ƋD`4`f4p`%Ddd|dxȋ|xDf=`fNwDž 9T,f=T`fB,񋅴D 988dL|UЋ`Df2``fF`ЋD%Dptd򋵴‹tȋpǍDf=|`fN?f P Pe9t W<f7`fG `frf`fNfW`frf6f]`fBkfd`fBrff`fB#&'UWVSL3E UxH xpJx[̪(HB1҉U)@9~Mt|D6=@DžDž EM)9t)򋅬9~UDžD29p|$q@9R)E Itɋ4BW1;}ك$@;|Dž9_tUu׃|1;M;p!Шf0V$&GA;M;p!Шu;;p!Ш)ȋuG)AƋfF%P$;;p!ШuE9;p!Шt])GtAD6Ƌf0%P$֋E9;p!Шu1ɋU;1BGGݝݝBB;|w݅ˋ܍DD ŋ܍݅D\;|؋ ;)E)ȃl= 1;| ك$ك@ك<ؾ݅̋Dغ5ff4W<T;|c ʋDݝIXHٽf f٭۝٭ w9x9t W1e[^_] PDR΍DžXt@ٽf f٭۝٭1FBݝhBBFݝ`ݝXݝHݝ@;|ك$ك@ك<ݝ(ݝ8ݝ0ھ݅P܍`݅X݅H݅h܍@Dݕ݅0ܥ8Yغ5f݅(f4W<\;|ݝP܍`݅PD ܍X݅h݅HݝhD݅@ݕ݅0ܥ8čXþ܅ܥ8ٽf f܅ܥ8٭۝٭>ÍXº܅ܥ8tVٽf f܅ܥ8٭۝٭?%;݅`ɋ݅Xʋ݅h܍H݅@Lك@ك<$ظ5fك$ϋ BFG91BB@ݝ ݝݝ;|1ك$ك@ك<ݝݝݝھ݅ ˋ݅݅D݅ܥ+ں5f݅f4W<\;|Z܍ ݅ʋD݅Dݕ݅ܥčX¾܅ܥٽf f܅ܥ٭۝٭ÍXºܥtPٽf fܥ٭۝٭w_;4݅ ɋ݅ʋ݅Lك@ك<݃Xٽf f٭۝٭v1ݝB;|ك$ك@ك<ݝݝݝپ݅ˋD݅ܥں5f݅f4W<\;|*D܍݅Dܥ ÍXþܥٽf fܥ٭۝٭XºܥtLٽf fܥ٭۝٭T;}h݅Dك@ك< ݃XtBI݃Xtظ;}ك<ك@[݃XtUWVSl)[(MU$ك8~كɃؿW$EH鉍BݝBݝB ݝBݝxݝpBBݝhݝ`BB ɋU ʋr ɋU ݝXzrJzUݝPB Dž  Ή p9K 4 ?LHB9 )EItɋ4WLQL1ω9}j1ɐf<ݝf~%DTP$Lȉ$$܍@͋H\݅8˃܍ ݅܍0݅(݅݅܍݅݅܍8݅܍@܍ ݅0݅܍(܍݅܍\;z݅TPݝx݅݅ݝX݅݅ݝpݝ8݅ݝ`ݝP݅݅ݝɋݝHݝ0ݝFAFʉ$1ݝݝݝ;ممݝݝ\݅܍pH݅h܍x݅܍`ݕX݅܍X݅@܍P݅܍H݅܍8݅(܍0ݝPDݝܥ݅݅XōX܅P܅ܥAع5f5$fwq$fyf ~%4 L(P$X0,T8Ƌ`ȋ\Ћhы@Ћ<d‹HpHDlH‹ ыX΋Pfyf ~%񋍄Ƌȋ|ЋxыЋt‹lpldhl‹΋ ϋ򋕀΋ʋ|‹xЋt‹dpdhdы LL9򉍘ff0ׁ֋׉Nj׋ʋ|‹xЋt‹ph‹ ϋ8D@f2L\1e[^_]Å~d8fo R诇  n븐8fA8f|8f 'UWVS<u hXE hT\~ PpxEU [gO(M$pDžBDžuك8V~ ؋V$uMݝn19}MT܍@9|}d$u@} ȨOE1@p9}G \B;p|1;p}u\AD;p|M\Dž4EX)G4U)ɉu Uы@׉ x9|&@9)ыE(Itɋ4GDž݅ƍX܅pܥnؾ݅܍8X݅܍@݅܍0݅܍(݅܍ ݝ݅܍݅܍Dݕp݅ܥ X5f݅fB|\ƉL܅ٽfff fd܅pܥ٭d۝`٭f`܅ٽfff fd܅pܥ٭d۝`٭f`Pd}99P Ш 1;`݅݅݅ݝ݅8ɋXD܍@D ܍@݅܍0ݕ܍(ݕ݅܍8܍0܍(D\;`Q51;`'مPمLمHݝݝݝG݅܍@݅܍8݅܍0݅܍(݅܍ ݅܍Dݕp݅ܥōX܅pܥغ5݅fЋ5fVX\|։;`݅D(ݝ݅D0݅݅f2ݝݝݕݝ݅ɁݕV$fr%$܍@݅$܍8ɋX\܍0˃݅܍(݅܍ ܍ݕp݅ܥÍX܅pܥNؾ݅܍@X݅܍8݅܍0݅܍(݅܍ ݅܍Dݕp݅ܥjX5f݅fB|\Ɖٽfff fd܅pܥ٭d۝`٭f`ٽfff fd܅pܥ٭d۝`٭f`v1;`مHمPݝhمL݅@ʾ݅܍8DXغ5fЋ5fVXT|։;`fɋDf2݅ɁݝDV$fr%$܍@݅8$Ƀ\X݅hXؾ݅@ɋX݅܍8D݅hn5ffBXT|։։;`fٽfff fd٭d۝`٭f`ٽfff fd٭d۝`٭f`1;`مPمHݝݝمLؾ݅܍@X݅܍8݅܍0݅܍(݅܍ Dݕp݅X5f݅fB|\Ɖ;`_݅ݝ݅D D(݅f2ݝݝݝ݅ɁݕV$fr%$܍@݅$܍8ɋX\܍0˃݅܍(݅܍ ݕp݅ÍX܅pٽfff fd܅p٭d۝`٭f`čX܅puغ5݅fЋ5fVX\|։ ٽfff fd܅p٭d۝`٭f`K1;`jمPمHݝݝمLؾ݅@ɋX݅܍8݅܍0݅܍(D݅غ5݅fЋ5fVX\|։;`݅DD f2݅݅ݕݝݝV$fr%$܍@݅8$ɋX\˃܍0݅܍(ݕp݅JX¾܅pٽfff fd܅p٭d۝`٭f`čXútLٽfff fd٭d۝`٭f`x1;`مPمHݝݝ`مLؾ݅@ɋX݅܍8݅܍0D݅`غ5݅fЋ5fVX\|։;`+DDf2݅݅ݕݝV$fr%$܍@݅8$ɋX\˃܍0݅`X¾ٽfff fd٭d۝`٭f`,ÍXºtLٽfff fd٭d۝`٭f`)u ظٽfff fd٭d۝`٭f`PP}(WU RMQTVE P}W\&UWVS|XU rBTrJ [)(u}Džvru$҃HUu)u)@AM uNȉ@Eu=Mu19}DB9|Dž9|(3t&@9)E(ItɋDžN 9 w}$TG9|܋UDžPE9P DžLu9Le΋}Oɉt&LϋLNM)Lf2Lf B(f4zfɋffw%DNjTDЋDЋD9׉~E9P9L ȨDž9 (($$f fJ4% ʋ0‹(<Ћ$8‹D@ƋT򋵰ыʋ4ʋ0‹$<Ћ 8‹D(@TыTʉTǃ9(E9P9L ȨDž9 (($$f fW$%<(Nj8׋@ЋDʋ4 T‹$<‹ 8ЋD(@֋4ЋT‰T׃9Dž9f=fQTD9 (($$ f fJ4% ʉ0‹(<Ћ$8‹D@Ƌ,ыЋ4 T ΋4ʋ0‹$<Ћ 8‹D(@Nj,TыЍ THЁ )f2H=TfGD}9P9L Ш Dž9 (($f:$fz<%( $׋8NjDϋ@ʋT<׋$< <׋8NjD(@Ƌt։tΉ9Dž9f=| fJTD9U (($f $fJ4% ʉ0‹(<Ћ$8‹D@Ƌ4 T ΋4ʋ0‹$<Ћ 8‹D(@T THЁ9 f2H=-TfG׉D4}9P9L ШODž9_҉($f>(f~(@$D‹<T<Nj(ЋuUЋE؉M?E )ME ʉ ʋM })}M! ϋMMM ϋMMM] ωu! ׋U)UM H9|&u 0H9}T^_]ËMU)ʋuMEU܃ ~ Ex}Eu}Muԃ }E)EeMmсMM Mы} ω}}M ыUMEM E ʊM܉EeM܋}) }uM ցUuMu } }EMu ʋ}]!!}uE)Mu ʋMEԃ HM9}8H9}T^_]ÃME ʉ ʋM })}ME! } }}} ! ыU9|} 8H9}M U)΃MUM;u}U:}UM}UUE σ MEU ϋM 9E MċU)ʉUЉ} }Wщ ω ϊMe ׊MЉM щMMU ʋMMM ʋMMME ‹EEM;uh;uE})uƉE ~UzMUEEċ1u ))щeM̋E ʉ ʊMe u}M UuMu } u}}M! ʉ}!ME HM̃t&}8HyT^_]ÃMщցE M ֋}UUU ϋMU ϋM }܉MM U)}M ցU}} M} ʋEM ƒE!!ƋM 9|}8H9} EM)ƋU}ML}t&UWVTEuMU ։σuEu}<֍4ȉ}u9}ME9E9u E)euM)EmщMM Mыu ΉuMu) ։MuMEe ʋu !}!lj}M ʉ։ ыU T^_]Ðt&UME)ʋ}U9}}pu}EE9E E)ue)E u֊MmM} }u ΋Mu}u ׁU}MM }u muM܋uԋE) ‰uԋ}MԋuM U}M} u MuE ‹E!u )Eщu Ή ֋U)‰uU؉M؉e ʋu M}!!}u ׉ }  ֋U؉7Uu}}E EĉU׋MMM ׋UM ׋U 9EE EU)‰Ủ} }Oʉ ׉ ϊMmMM щMMU ʋMMM ʋMMME ‹EEM;uh;u E})9E}u~uEE  )ƉUm ΁ ֋MU) UȋUMmM} Mu}M }u u}uE!M ‰}!ʋM ʉ։  ыU} )փE  Ή ΋M ցuUuu ʋuMu ʋuЊM܋EmM )΋EuЊMЋuM ыUME Mu ʉuM ʋM!M!u ׉ ΋} ։7u}UM)UME EUWVSP['P1M}utiEU AUE QAy…q  AA(A A!A"A#A$uUƒ} Шt 1ZY[^_]1} wU U҉U&1UuʼnE u u 9uEtf0QA%AZY[^_]ËUU떋EljE1뉋U׉Ѓx=UUau uuE9uU넉A낍B뾍v'U1҉WVS [H'}~: j@#1҉ƅt%QEPW}WMQU RURV! t e[^_]à V#1ҍe[^_]ÍUWVS ['}u ȨeU }  ȨL1}E<E E EP"E j@"…M@(M Hp MHxM@$ H u u@ @!@"@# utfJBB%BЍe[^_]ËM ωMUFU}~E}F < E~$UEЅE;M ШuU9U| 1e[^_]ÅU;}~E)ȉEE9E~})E}U׋~1}w}Xu΍JRRPMQuV}WURMQtu …!ШtU؉Q$ȍe[^_]ËEȍ뱋}ύ릋}ύ뛋}F$ȅx-M؍yUEU1ɉ}Aΐ&UWVS [*'U EJrMBM:1wTtEU AU~VPQVURuVMQWEP …!Шt}y$ȍe[^_]ËEU Ut&UPBލPV뛋EU 뀋EEB$x UbEURyۉ׍U1҉WVS ['Et EHʅt e[^_]ËM y quq R]… 1ҨuQ1Q9T}tU@9|Eyxe[^_]Í'UVS[n'utFt P5Fe[^]Í&UWVU }U}uUuUMUUuU}t-%;A}1E;A | ^_]ÊU1Q UQ!UQ"UQ#^_]Í&'UUtM 1J(]Í&UWVS [j'}Mu… ШE… ШE }… Ш} U UE҉UUEU 9E ШEEUE WEU G G!G"G#GWEʃw  ‰O G G(u G$Ћu 9uE}0WO1O [^_]ø [^_]ËUEEEE UщЃx@ߋUEu uuE9hEGfB뻍UWVS [Ê'uUҋ} ȨFE… Ш/}%}  E MɉMU9UEM uE UB;E tB 6UM JME UQEqA A!A"A#U y AAA(A$DE AE P ыEU 9EuN}OO1ɍe[^_]ùe[^_]ËMUUEƉE1U։ЃU u-UE9`ua P~EHHUBHB MU׋A A9!zBwM1ҋAQ% UWVPEME]9EMEMEEEԋEME̍uЋ} UMu׋uUΉMu܅u1Eԉu؋MuDuDʋu]]EDDUɋu̓EeeMMXeXXX X(X0X8IEu}ЃMԋŨ@׉uE}ЉM9EP^_]Ít&UWVS$[Â'EME]9U`EU EDž} UM؉EuЋ}؉E܋UȋMEEЋuljE׋EƉuȅ݃݃ɋu1 ݝ0ݝ($Mt&uDɋuݝDʋuݝDuݝDuݝDʋݝDuݝDݝ݅ܭ]݅]]݅݅܅ܭ]D]݅]݅ܥܥܥ]݅ܥ]ݝEEdM]EEMMݝxEe$ʃMݝ`EeݝXEmݝpEmݝPEݝHE܅ݝhݝ8ݝ@EEM݅EE܅@ee݅x܅8MM܍0܍0܍(܍0XEܥ@݅XܭX]݅xܥ8X(ݕXݝx݅pܭpX݅hܭhݝp݅Hݝh݅PX݅`ܭPܭ`ܭHX0X8E݅xX ݝ`ݝHX@ݕPXH݅p݅hXP݅`݅Hʋ$XXX`XhXpXxIuU䋅  U䉍M≅ 9U$[^_]Í'UWVM~bM uMuMEMU~(11MEU8D8\UIu}EMωEM}u^_]ÐUWV1PM} 9}Et&MTTF;u|X^_]Ít&U1WVMuU 9}>vDDD\\\9|^_]ÍvUWV1S[û'PM9}0ك$E1ɉ} T}FTT;u|X[^_]Í&U1VSMu [O'9}8Eك$DʃPXP 9|[^]U1ҋM9}9E@@@XXX 9|]ÉUWVS[ê'U uEU9UMMMك(ك$1;M}gU3t>}fE fEm]mfE1A;M}'tu1A;M|؋EMȉEEO{؃[^_]ËEME9E}}ك(fMǵ Uك$fMuum]mMfE1AM9|낐t&UWVS[J'UM ʍ4 M;UM}U}ك,ك$v1;M}bU,t7}fE fEmNmA;M})tufNA;M|؋UE‰UUOu؃[^_]ËEME9E}}ك,fMǵ Uك$fMuuɋEmFm@E9|뉍'UWVS['UM ʋE4 ;UM}U}كHكLv1;M}bU,t7}fE fEmNmA;M})tufNA;M|؋UE‰UUOu؃[^_]ËEME9E}}كHfMǵ UكLfMuuɋEmFm@E9|뉍'UWVS[Ê'UM ʍ4M;UM}U}݃Xك<1;M}cU,t7}fE fEmmA;M}*tuA;M|؋UE‰UUOu؃[^_]ËEME9E}}݃XfMǵ Uك}fM fMMm]mfE B9}'tu؋EB9|؋u܋EUMuq؃[^_]ÊM1eE9}}ك(fMƵ ك$fMuum]mMfE B9|덍&UWVS[j'Uu EƋ}u;UMuEuUuuك,ك$t&1;U}cu+t7}EfM fMmPmB9})tu؋EfPB9|؋u܋EUMuu؃[^_]ÊM19}}ك,fMµ ك$fMuuɋEmpmF9|뗐&UWVS[ 'Uu EƋ}u;UMuEuUuuكHكLt&1;U}cu+t7}EfM fMmPmB9})tu؋EfPB9|؋U܋MuMUu؃[^_]ÊM19}}كHfMµ كLfMuuɋEmpmF9|뗐&UWVS[ê'Uu E};UuMuEuUuu݃Xك11ҋEE ΋tELt\u؃HuɋUxBU9M11ҋEE lELl\u؃Huɋ pB 9 *URuVMQURUuVEPMQUR4uVMQURuVMQUREQU8 9yPDž9:Dž4Mӥ4Dž49MDžhIMӥhЋ%1 0u ʊMꊍEƋM}d{u~>11ɋEMdDMd\u؃HuɋhB9*URuVMQURUuVEPMQURuVMQURuVMQUREQU4 9yDž9Dž0Mӥ0Dž09MDž`IMӥ`Ћ%1 0u ʊMꊍVEƋM}\u~>11ɋEVM\DVM\\u؃Huɋ`B9*URuVMQURUuVEPMQURuVMQURuVMQUREQU0 9yDž9Dž(Mӥ(Dž(9MDžLIMӥL%1 0E ʋMꊍ‹EƋM艕H}DDU~kE$11ɉH1E $ ‹HD21ҊT$E ЋU؋D\0uLA9MQuVURMQUuVEPURMQuVURMQuVEPURMVU(ƒ 9Mt&1;UM؋MdB;U|1;M#H1E ƋDAE;M|1;UM؋EtB;U|kFE1;U8M؋ElB;U|1;M[u؋Tf%P$MPA;M|(1;U2M؋M\B;U| uEU9UDž@ӥ@UuЋM΋EUR4ȋQEPVXZURQWVURMQUU@EU 9E|}9}t MQ  UR 11M/U!|EԋMЋ}uEVQ}WR}u@փu9}|bMtvuЋM΋UP4ʋRMQVUURQEPVXZURQWVUREPUu@΋Eu 9E|&uЋU֋}PQ4׋EPVU}WRMQV)U}@ǃ}9U||w|E4bs UPuU R EM$?ETu?ExS*}tPhEQUEċUЉ lDž ]HMuUWVShu[N'jM QVjjjRURS u ~te[^_]ËuUd}uM`u\xt+uPPQWVdW\P`Vƭ PPQWVdQ\R`Wu zUWVShu[~'jM QVjjjRURR …u ~t e[^_]ËUu`MU\}UXxdƒ 1Ҩuw PPQVdQ`RXV\WaoWWQVdR`VXP\W螨 WWQVdQ`RXP\VWQPPQVdW`RXQ\V@Y넋}WURQVdQ`WXP\V3j TUS ['E PURХ]ÐUS [̴'E PURp]ÐUWV}M uɋUttWU;Qt e^_]ÍW UU;Q uU;uًWUU;QuƋU;Vt뷋E;F uU;uU;Vu}t}uPVQW1RVQWt&UWV$uM F~VEF }Eҋ~EAUIBEԋEPEEE؋E~DEuuԉEu܋u1;E}<@;E|ENjEENu؃$1^_]ÍBEAlUIBiav'UWV$uM F~VEF }Eҋ~EAUIBEԋEPEEE؋E~DEuuԉEu܋u1;E}<@;E|ENjEENu؃$1^_]ÍBEAlUIBiav'USQ[ޱ'QME PQQ ]Ít&'USQ[î'QMQE PQ]ÐUWVS [u'}M ɋUtut W;Qte[^_]Ëw u;q uu;1uwuu;quӋu;VtċUE;B uu;2uu;ru}w}tP}w}uuVURQWe}t}uuVURQW1UEPuVQWUREPQW׍UWVS [g'Mu }QA tQA|V‹Fwt֋Qك$tEwٕݝمpDždt9dnككHٝxكLtd)Ɓ~dUEE UEEUEEUE\\ ]]QQMQjjj@PHVWRM QuV 0ً}EH,ٝdمdٝdمdaHэ4(9sZ,&EtEu/9s-AEtEt;(sPEu_ٝʋمEu8ٝم@tzٕ뙃\\b]]PPuVjjj@WHPQRu V}W<SɋMU YZ1ҍe[^_]ًEuٝ$vٝdم$مdHÅHt9rS~vEtEu[م$Et ٕ$Eu1؍ 9s0AAEtEt@<XɋU} Z_Zم$_ٕdمdٕdu}MمdʋH8]]<4UH  40)94&ٝمEٝʋمEuxٝʋمEuOٝ9مsLɋEEPٕIvٕP;0sPEucٝɋمEu<ٝم@EuU]ȋUȋM̉UȉMEEEu&ًEUEUUEMЍ ‰Mt9]ȋEȋUUK0tYʋ}u EEEE_E^_^_^@^_]Ð&UWV,ME} u$M ]EUU]]EE܋E(EUMME)9LE&EEEEEEEEEEtUEEًM,;UʋE(EIEEC]E@EE<]E9EE5]-]L;Us9E]؋M؉MEEEۋEE ;Ms:E]؋M؉MEEEڋEE ;Ms2EuF]؋U؉UEEEu ًEEUEM܍ MtP]؋EU]؋EKU]؋ErUBʋ}u EEE_^E_E^_^,^_]ÐUWVS\[ò'}MuUE … кuyUE … кu^xh GUEWG |xWt}tm}6}M}e[^_]Ít&Mx WEUGW |xGt}utUDžpNjx)|xu x)ϋM Tt} l ȨuW} Mpd}t~}}7}}1ҍe[^_]ËtEDžpB'DžpEtpu} } }Q} e[^_]Džp떋uU } }|}uEEPPUR}Mɍ4VQWdRUȋlVu؋QWRV0}U }| }o}3EEQQMQu}vRWVuȋdPlQM؋RWVQuU܋E9~‹E̋M9}ȋ}u 3} }}EEE1҉`\LPTXDž<Dž@DžDDžH;llT98`49R&41ҋX0ЋX)ЋT!ȋ4ljXA%ljƋT)Ɖ!TPA%)Љ!ЋP‰ȉP1ɊHL)Љ!ЋL‹HL0)Љ!ЋH)‹D)HNjD!@))‰D@!Nj\)<)ω@<!4)Ɖ<494d``8fuX} HTu} DFPuO} @V)lj!ϋM)ȋuU!)ΉuM UE} MVUOFWH^_]Ít&'UWVh}u UMMMME؉E܉EEu,E(UuEUMuE)E)9}MЉ}t&E 2űM$V ;lm);MtCF;u$JddHHDž GAЉ;l~Fd>NV !Ȩpu NV `;m);MtCF;u$ddHHDž GAЉ;~FdV~ NDžMNDžNDžNDž늍&UWVS [9'u}@DžVFVNF Uxu`M)ʉ}p@EUDžЋ4BE ȸVUUUtpM)9U$~ DžU$Dž9U ME84U< }4u,M0},u(t4xM(M9UDžDž9b&;tH};}d;uދG9ut&G};|xaM$1Dž7d;u$Dž!Ȩxau$Dž7d;u$=Dž!Ȩ:pu z򉅴9@9:t Rm 1e[^_]ÉuU;eE;}+;uGE;|܍&u$~Džx1\|;u$]Džt Wl e[^_]ËUDžl9&};}69;u拽lGll};|ʋlM$Dž2d;u$JDžE Exuux{ Rk  9~aF;u$/|PxPDžx` |\A9<\`)ЋU9ux`B\xA9~čF|xN>V ;lm);MtCF;u$JddHHDž GAЉ;l~Fd>NV !Ȩ pu NV b;m);MtCF;u$ddHHDž GAЉ;~FdV~ NDžMNDžNDžNDž늍v'UWVS [70'E}@DžPHPpH UUu)}phEUDžЋ4VUUUU pʋul)9U$~ DžU$Dž9} EU8 4u<\`9Džt VMb e[^_]ËE ExkUUxS Ra  h9~_A;M$^xPtPDžtX x\F9<\X)ЋU9ut@tX\F9~ċAxtWO ƒ!Љl4} 9i9\)u9tU`F`u$9` F|FDž| |G@\|9u`|2@Jz !ȉ~;m);MtCF;u$iddHHDž GAЉ;~Fd2zJ ;m);MtCF;u$ddHHDž G@;~FdN~ IDž`DžH`NDžNDž{&UWVS\[*&'U} r19}MD؋ LB9|PM QURMQMȋURU؋EPQR …'19| 2 B9}*EL؋E9E Lȋ9}։B9|֋M$1҅M(M,U0Һ ȨM4U8Һ Ȩ19}dU4 M8@9|19|';vD 9LDB9}M9DuDЋuwH&e[^_]ÃuڍUPMEE}`d<Dž<ȉ@)E؍<}EEE9<E@0E,t&E ʋ<ɋ}ʋ0<ك$Nj<@<,0}09<F؍t&؋d} `dE0`)|O|9|^ltxtJh)xu4lh9xuEFuUE9UtuMphUWVS[×&u(EM0},)M4)ϋU HMH MHXHBMEJBMJ \MXJMXrEMȋUMNEʍWM\щ|MU))AB‹EUE)ʋ)ȍHE))u))1Jx }Džxʉ}x}Džx};M|MIU9|J)1Ap 4IVp xxpxщt1xD@;p|uƋ\ȋ‹UMuЋU XMM4ul9EDžh}E9hU,J U| T `}4P99u(N$&$TPM0}LHd9E411DžD9`E09d!Шt DžDU4E49`!ШtU0E09d!Ш `E$ЋdE ]ȋU$`‹E ]d ЋE U$]]DQFU$Eك̞ E4E$]nM Eك̞ E0E ]EHMLhEȍ1҉;pEE4}EfM ݃Xك<fM&M‹xuum۝mtxD4B΋4ȋ;p9؋Lu HLE0H)dNd9dRT\\`PN)`}4TP9`hEGhUE9hs\}MX4l}AulU9l xR 1e[^_]Í&`E$ЋE d Љ ]PPtVuȋhE QURMQWpPVHRLQxV0yEhHL 0,)Eȍ1E؉;p-E(E݃Xك<{ًtx0D(BƉ0;p0xu9}tfM fMm۝m hEHL 1҉E;pE 8E݃Xك<}fM fMtm۝m xD8B΋8ȋ  ;px??؋t x8D΋8B ȉ OHLDž<)Eȍ4pEE؉9<uuˋP1$̉1Ҋ։4$$̋Eu }fU¶ fU|mmpEȋX1ɋTك̞E;x&}ك(fU‰, ك$fUˋ\1Ҋ1Ҋ81Ҋ1҉\0P$ˋEuuɋ|mmˊ,EEANjEƋE;x,QMUWVSL[×&u(U0M,)֋Eɋu4} )hHp POpWGw ODžU)‹}BE‹)ljGENjE})׋)Ћ)H)ы)֋)Ox}Džx}x ʉ}Džx};|O9|I1)G R> [Ƀ 1;}S4@;|1;} 4A2;|鋅M,($ʋ,4Dž 9O@u,)9 !9@DD !щΉ) Q艵 J VFF1;}tR11Ҋ1P1$9Љ$1$yˉ$$ˋF;|؋U( ))׉|)֋|1;}111ҊP$ɋF;|؋||UʈT>E}Elju؉}܋UU9U?M؋}Muf>fFfV}fNu؉NJEEEEFE0T0 FLFMQuMuf~fVfF%|fNU؉E1fMŠBMEAUUUE |D |||QDQ}GUˆWEԋU֋M؃u܉EԋuM؉}9uU9U}tEffpρufHfpE؉P}UUUUT xLx}Ĉ8L8EuM}1ωU)ϋuU܋UЃUЋ}EE9}S}U9U}K֋M)1;M}$1ҋ}}OEDDOA};M|ދMU}ENMUu|^_]ËU;EIu؋}EEuEfpfPfHufpfuu%V}L77 ~UT~M}MEUWM܉u؋u}MMt;UU؋}uuUEffxΉ}fHfxE؉PuUUUUT pLpUJ}؋M܋EM}؉UMmNjE)]EU}4@R)u)Ɖ}u UE RȋMEE}4@}MU؉U؉EM}DyEMUIu]ΉuыU)SvUWV u MVAu UEV FuNv 9~ƉM9~U))1~xE1~B1ɍ}‹E xE}ljUI<UEHωUMUxl~Pu܋u~111ҋME0ED0DED0DEIu֋MuEljMMu ^_]؋M)E1UvLM녋UEJM]щMdыU) UWV} Mu WA}UEV FOw u9~EM9~U))1Ʌ~H11~B1ҋ}ǍnE E]IыU)t&UWV$u MVAu UEV Fu~ N9lj~ljM9~U))1~pE1~B1ɋUpu F4ЋEuuƍ4HEP uMMɍVMxv~Z}ԉU܋UU؋}~&11MEU2D\2UIu}Eu܋MMԉ}Eu$^_]؋M)E*MtuvEu E]ΉuOыU)t&UWV(u MVAu UEV Fu~ N9lj~ljM9~U))E~puE1~r1ɋURU֋u ЋV4‹UIuMuȋVM4ƒEu܅QM~i}Ћ}U}Ԑt&U~011MEU2D\2D\2UIuًuM܋}؋EMЉuMu(^_]؋u)EMtuiEu E]Ήu>ыU)t&UWV$u MVAu UEV Fu~ N9lj~ljM9~U))1~pE1~B 1ɋUu F4ЋEuuƍ4EP uMMɍVM~i}ԋ}U}؍t&U~1M1u쐋UDYDYDUY׃ Nu؋ME}܋uMԉMEu$^_]؋M)EMtugEu E]Ήu<ыU)UVS[9?&u Uttw e[^]Ít&jjVR e[^]ÐUWVS[>&M ME0} uGNW dv 9~d9~))~uHPBPVdPRQUM Q\RVWdVhjjMQVU\RVSme[^_]Í&1Ʌ~|r~wDž`~uDžTPPREdRVQM QXPvUWdWTVh`QURVMUXWe1`DžTT)ЉTTTjUWVS["=&M }ttut e[^_]ÃvM w AQ Od9~d`9~`))~uHBPP`VdPRQUu VXR?T`QdVhjjWVTXRVke[^_]1ɅrDž\DžPPPE`RdRVQu VTPS`QdVhPR\QWV{STWY1s\vDžPP)ЉPPP]UWVSp[J;&uU EFJuE؋BM܋~J u}~ r$uԋuv$uЋuu9~Ẻ}9~MȋuE)΋M)~x}ąE1~<1EuċM΋R}M‹}ʋMUQMȅ}ƒE؉Mǃ<ẺUM)ȉMEЊMEE)Ee;u}8UME1}M}Uȅ})}ȉUM}E}OM;u…!ШtUEuOE1ҋuy1BA0OE9|MuE܋}MuMuup[^_]ÉUtMME))U9M1%1ҊEM"GM! ЈG}$M1ЅEUuM̋U)ыEUE)‰MUUU39E}@M11VM FU% OMAMEyƋE9E|EU)‰Ѕ9E7UM11V+M %F OUt:M9M1%Mu%1Ҋ!! ЈMuE܋}MuMup[^_]ËE1Ҋ1MAM c1BA0OẼ9|뗊M11Vu) N1BQE})lj}EETFM‹}ʋMUQMȅAx)AM,EBtUWV@u MVAu UEV FuNv M9~Eu9~U)1ыU)~x11~ 1ҋM yMNjEH΋MΉu}EE9EEEE}TUut EU ;u}4UȋEЋMċUM܍ )E8D8AQ;u|Mu΋UuEu@EUUʉUċUuU9u|@^_]ËM~UE׃u}̋}EԉuЉ}ŰMJẺUغ)1Ш19}‹u)uЈ 7G9|;}1u؋E)V щ ;}~ѐ;}}u؋M)G;}|Ű}ЋuEMỦ}+ŰMJẺUغ)1ШVE̋M1Ȩug}Ѓt E̋UЊH ;}&E)fpfHf uf7f fT7;}~8}Ѓt űUЊN ;}E1)P1ҊP@% E f ft;}~)8)BA&UWVpu N} MGMq֋W ϋIuM9w ~Eu9~U)ȉыU)1~p11~ 1ɋU0u V4Buu}>uNuNu9u<MEUEtEMuUEĉM̍&ut EȋUЋ ;u};MԋU؉M܋M)E܋UUUDUTD;u|ՋMuAUȉME̋MUȋuE؋UԉMЋMȋuUԉE9uo8^_]ÿ))ʍJnRUWVS<[j.&u MVAu UEV FuNv M9~Eu9~U)ȉыU)1~pF11~ ?1ҋM4pE HE‹U΍ xuB}uM }EU9U}EE}ȋu~-}ȋENjM܋U49M}ъQVFOuE؋uU@֋Mċ}EuuM9u|e[^_]ËuBEU}܉ẺUuEpJM)1ШtfJfM96U)Rw9~UB;u}"6)11ҊQ fwF;u|E̋uU׉EMuEpJMW1ɍr;M}‰؊FDFOTOA;M|EuMM1M9dMffQfTwfw;u~APP} WMQ[e[^_]1J&))BUWVSL[Ú+&u N} MGMq֋W ϋIuM9w ~Eu9~U)ȉыU)1~p/11~ (1ɋUpu F4PEuuPE ~u fVE} ʉUG}9|@1;}5֍vNMϊFEΊVU͋U EψDMΈLE͈DG;}|1;}ыEfqffftG;}|EE9EUU1Ɋ frMԉB%MԉƃU } ΋M4uAM9|TEu9uAUċ}1ɊfM1ɊOWfMЁG%}‹Mf UҋUfEҁfE ֋MċUftGMĉ}9|)W)ʍJ^t&UWVS[&} MGuEA‹NMЋv W E܋G9~E؉u9~U)ȉыU)1~p1E1~ 31UЋEǍ }ЋU rN Ű}UBHE܋M }EM9MuDž|Džxu&E؅||Eu؋xMf yffMfLyfMƋMfLyfMċMfLyf6fffLBfMfLBMfLBf6fLBNuuȋMFxuȋE܋|ƋMԉx|9MDe[^_]ËEԅ}̋uԉU}tu؋UELtU)4|E؉t|}E}9}@ȍPPRVWQWPURuVWQZRRRQURMQRQ UWVSĀ[ %E} pOu܉M؋pOxPuȉUԉUMH 1։}̉Uu9}̞}}@;E|XxEU9Uك̞t]ك,|u]ME}uĀ<>}؋EUM}ME}UuDž|EȋU B1t1ҋM]]A]G]AM]G]UȊ UE1;U}vtE1EɊMŋEEEEɋE͋EʋEBE;U|؋EЋMEM|EuċE1;uU}U}fU} EEfUɋEMum]mɋEMUыEFf HEŠU;u|؋}ċuG}9uu܋U؋EԋMxu܉U[^_]ÍvUWVSt[*%EU pxJu܋rM؉}ԉuЋx Hp}ȋE,ủMčT UE1;E}̞}M@;E|EE9Eu$)E@EEE9EU(}<u؋EM}M}U}HE,}}GEUE}11;U}.},:B;U|ȋ})9}"})}Nj},0:Bȋ})9|ދ},D8%9}},:B9|U}1]]B]G]Bt]G},]1GUE1;U|qE̋}E1̊:tMEƋ}EʋEE͋}EB;U|؋E};E | ;E}U֋U}BEU}E}uE1;uك̞ك,DɋMUf|ЊFM;u}wɋUMt}MfUE fUm]mɋUf H|ŠFM;u|؋}EG}9ERu 9u|U9U} E؋MȉE؋M}AU׉Muȉ}9ut[^_]Ít&'UWVS[Ç%E} pOPuЉM̋pOuxUU} ։MċH U1u9}̞}؉}ԉ@;E|VMEU9UEM<E̋uԋUf|ЊFE;u}wɋUMt}EfUM fUm]mɋUыf H|ŠFE;u|؋Mȋ}AM9}Eu܋}؋UMEuhe[^_]ÍUWVSL[:%u EVNU܋xP pỦuЋU}։MԋxM4}؉ux1};EЍ֋UE$UU(@U, A U;}r;}sM 14F4M9;EsU$4F4U:;Es},4F4^_]Ít&UWVEU U9UUEf<E}fLfTuȋuE}E}ׁEu4F4}u@U@UEuEUL>M1ɊL>UEU@U;u~ƃ ^_]ÐUS [ÜZ%URR]ÐUWV8EUEE tt p;rt 8^_]Ðx }ċ};z u8}};:uҋx}};zuzH}MzP}U}t_}uMą~FU19}}Tϋ}ωTA9|M}UEMĉM}u1BEą~EUƋ}č tM؉}ȉűME؉ϋulj}tua}9sV5Q9r;Ms >9}MUűEMȉ}UuwDU9s>}Ћ~}E9r떐US [ÌX%URR]ÐUS [\X%E PUR]ÐUWVSL["X%UM tt r;qt L[^_]Ëz ;y u܋:;9uŋz;yuBByQ&N;;!ȨtDžDž|9|DكԢٝTكxٝP݃ݝ@݃ݝ8݃LHݝ0݃ݝ(| |t|)ʍpx~ Džx1;x}EvtD ‰ ?-TF;x|1;xمTٽfLݝمP Hݝ݅@݅8ݝ݅0݅(f݅٭۝٭W$܍܍ɋB;xhDžlx9lltT։Ё-) lƒ%t! Ƌ!!‹!! ΋tl !ϋl pωDxAl93|x׋|9|@l1:~ዽDž9كԢٝ$كxٝ ݃dݝ݃h`ݝ݃ݝ )։~ Dž1;}IdTׁ?|hA;|1;م$ٽfݝX م ݅݅݅fvdh݅X٭۝٭W$˃ˋ`B;x1;}n4% )Љ`# !ȋ!΋ G;|ʋ9'US [ P%URR]ÐUWVS\[O%MU utt q;rte[^_]Ëy };z u9}};:u܋y}};zuɋ}A}ԉEЋyB}ȉE̋z}ċ}}uB}} Ȩ;E;E!ȨtuUEu}}~}4}ԅuԉuEE9EMuċ1ɉp ƊljMωMыM} UAM9yEċ}ȋűUMEĉ}G1c}TEMԅ~؋}1;u}UMfq%P U苌ȋM ȋMfqF;u|UċE‹uЉUċUOUu[}ԅP}1;u}%89E>ˋ}XXX ED}9|% 0%71}M0ˉ___ ;M}>1A4Ћ|B0x;M|‹ME} MUM20^_]ÅE$}$wM}U҃߃uM}1OEUu@9}]}1҃4Ћ|E1҉x0}W4Ћ|EUpx uEuU;@|EtT9ˋHhdZZZ hH0d9hb%`\ ȋX%>T9ˋHh^^^;Pd1`L<ƋHH$x h;P}Yd1\BL<ƋHH,x(h;P}$dX1G4|Hq0y4uM }΋E ljuUnu`1ҋ\`4ы|MX\U1yLH,TNBXTP}`1U4|M11yB\(<ƋLExH XH`TLT,UX\P },1U4|M11yB(L4|M1y q}$GT4|E,\px H`(XPUhDž} 9}^Eu<֋E$ MM~%M MM1ҋEЋU׋U Iu狽u G9|MU }ыuMEj(U1҉WVPuM$;U }<|B;U |}K<uEE 9EMEMuE}E w|}Ћ} 4xuȉŰu U Qu$~MEωEU߉Uti1Ҋ4Ћ|E1҉0x4Ћ|E1҉px A|4ЋExp}OE}uDžUMM\9'}uuuU |UɋEN~EM^ ^(^^9}1}L̋}7}u\OLWH_0_8_@_P_X`}}9}}uuuU |UɉN~MMM^ ^(^^9}1}L̋}OLWH_0_8_@_P_X`M}9}x1Ҋ4Ћ|1҉0x4Ћ|1҉px A4Ћ|pxF}9|uMU <΋Mщ}Mz6.U$u$ ~U$u$LHJ~ D@MEωEu߉u0L1Ҋ4Ћ|E1҉0xH4Ћ|E1҉px D4Ћ|E1҉px@A4Ћ|EUJpx U0Eu9Dž,}MMX9,}HL} 7|7Du@ʋE~HE}^^^L|D 7|7u@ʉN ~$^(EHE}^0^8LxD 7|7u@ʉ~DH}N@^H^P^XLtD 7|7@ʋU2uXEUH`xd,UXhXpXx,耉E9,}HLp 7|7Du@ʋULU~M^^^ωlLHD@ʋ}W O$_(uHuL_0_8}h 7|7Du@ʉN@LM~D^H^P^XωdLHD@ʋ}W`E,OdM_h_p_x($9L1Ҋ4Ћ|$1҉0xH4Ћ|$1҉px D4Ћ|$1҉px@A4Ћ|$px( F$(E9(guMU <΋Mщ} Mvn4Dž8} 98}X8Eu<ЋE$ MM~aM MM1ҋEFЋUIu닽8u G898|MU }ыu4MEp8M B898d뺍U1҉WV`uM$;U }<|B;U |}YJuEU 9UMUuffzfEME f}uMEEu}T EUEUuẺ}ȉU9u}^} }čt&E̋}NjEȋ4Nj|EŰUBU EȋE|4ы}Uԃ}uU9E|}ЋE̋U ENjEȋ4Nj|4щ|tuuЋ|Ƌu1|1}M G}9M}uE M uMFU`^_]Å~uEE 9E}TMUEu<ʋUL҉M~(U MU؍U܃‹UIuu} Fu9}|uEM} MƍyUEu`^_]Í&U1҉WV`uM$;U } <|B;U |}}uEU 9UFMU}ffrfEME fu}MEUM}uEEfUfEL%}M̉UȉEĉu9}}lu uvMЋEȋ}fNjEāUfQ4NjM |EԉUċU4ȉ|MuM}EԉU9}|űMȉU ΋ME|4΋E4Љ|t#MЋEf9Mt}ԉtuU Fu9Uu}EU M ʉ811ҊO1~ 1G$ DžD ȉ @D$ !@!эW D NyDBDD~}4u,M0t <0}$}Dž,09T1ɊGlhVlhV F,9~491@ωL49}'NNjL49}ʋ4)1H9t&1A9~t&E90tQ4QP0VTuEM UuEk(99})1H9 :B9~M9tWRQWOMu} EMu9t V,M 1e[^_]à jU$R}WQEP Ey'UWVS|E[%u(=~WWQVdQ`RXP\VPPQVdW`RXQ\V넋}WURQVdQ`WXP\VrTUWVShu[Þ %jM QVjjjRUR u ~te[^_]ËuUd}uM`u\xt+uPPQWVdW\P`VPPQWVdQ\R`WzUWVShu[ %jE PVjjjRUR( …u ~t e[^_]ËU}`uU\MUXxdƒ 1ҨutIwI뚃tXuPPVWdR`WXV\QOdPPVWdR`QXV\WPPVWdQ`VXP\WLPPVWdR`QXV\Pվ널UWVShu[n %jM QVjjjRURȦ u ~te[^_]ËuUd}uM`u\xt+uPPQWVdW\P`VPPQWVdQ\R`W zUWVShu[Þ %jE PVjjjRUR …u ~t e[^_]ËU}`uU\MUXxdƒ 1ҨutIwI뚃tXuPPVWdR`WXV\Q?dPPVWdR`QXV\WPPVWdQ`VXP\W<PPVWdR`QXV\PU널UWV$UM tt r;qt$^_]Ðz }؋};y u:}};9uԋz}};yuzA}EzIU}}}u}؅F}؉E1;UEEQ͋EEusʋEXEuZEXEuEEXEԃ ;U܉E}:E@@@DDDEt9| %EB9}EEtUE‰UUO 1U؅~~}}1;UEEQ͋EEusʋEXEuZEXEuEEX Eԃ;UE}:E@@@ DDD Et9| %EB9}EEtUE‰UUO UWV@UM utttBE;At@^_]Ðt&B E܋E;A uߋE؋E;u΋BEԋE;AuE;Ft묋};~ uEؿ;uE;FuByE}B}UԉEA}NEMv}}J}܅M}܃M1;M|Vt&͋EEusɋE\Eu[ًE\EuEًE\;M}@UDDDDDDEt;M|$ًEA;M}UEtދMUыEMMȋMΉEO1BU܅~ME9}EUDDDDD D E΋EUEɋEU\EًEU\EuxًUE\ U9eE9E}+EUEu=ًUEBEU9|ՋUEЋUEE‰UUIhF#UWV0u }ttttDe^_]Ã~UPPRVwFt=~y 1e^_]Ðt&~MRRQVzFuEEE]ЋUЋuԉwEEE_HuEEE]ЋUЋMEEU؉ME]ЋMЋuԉMЉuEEEus]ЋUЋuԉwQQWVsGPPWVU؋u܉wMuw OMUWOuMOwUa4&UWVS$[%Ur BJuzrE9uك<} Uك<WW…!U~UE9}~QًUDEu]ڋUDEu]ۋUD Eu]܋UԃU9}]UEt؋UDEt؋UDEt؋UD Et؋UԃU9}~19u|F؋EB@E9u}1EE EtڋE ЋEB@E9u|֍&MU E Eu^]؋u }؋MF>NEub]؋u U؋MFVN Eu#]؋u U؋M^VN$[^_]]؋u U؋M^VN$[^_]뜋}Et&UWVSP[Ü$MAQy ƋA@9ك<Hu19~.eDEu9DEu9ۃ99EtDEtDEt؃9~NjENuU ZZX[^_]ÉǿN3UWVS$Ur BJuzrE9u} WW WGGU܉EE؋U…!U~UE9}~QًUDEu]ڋUDEu]ۋUDEu]܋UԃU9}]UEt؋UDEt؋UDEt؋UDEt؋UԃU9}~19u|F؋EB@E9u}1E ЋEEtڋE ЋEB@E9u|֍&MU E Eu^]؋u }؋MF>NEub]؋u U؋MFVN Eu#]؋u U؋M^VN$[^_]]؋u U؋M^VN$[^_]뜋}Et&UWVSP[\$MAQy ƋA@9݃Hu19~.eDEu9DEu9ۃ99EtDEtDEt؃9~NjENuU ZZX[^_]ÉǿN3USQ[^$QME PQQpM]ÐUVQM uttttuÉRRVQ1uPPVQt&UWVS[$Eك$xH}Px MH֋EljEEEػĞ;EtrI1؃[^_]Ãu EEBBB HuEE}~(EBBB HuEt B̋UItE뢋}} ʋUIT!ɋM Y[^_]ÅEE}~(EBBB HuEtB̃Et̋UIم~I}~(E &BBHuɋEItɋu ^^[^_]u [^_]ʋM YYY&UWVS[$Eك$xH}Px MH֋EljEEEػĞ;EtrI1؃[^_]Ãu"EEBBB HuEE}~(EBBB HuEt B̋UItE뢋}} ʋ}IV!ɋM Y[^_]ÅEE}~(EBBB HuEtB̃Et̋UIم~I}~(E &BB HuɋUItɋu ^^[^_]‹u [^_]ʋM YYYUS [$jURMQURE PUR]ÐUWVE}w0WuUE9u|uMFu9MM})EItڋU EM}2BM}܋Mr}EEM؉}ԉp9EU܋}11ɊuE1ҊE܋}ԉM1ɊU1Ҋ E؉M1Ɋ M1Ɋ >}܊w}؉U1Ҋw}܋uԉU)1ҊF1UU})׉E1uE)1EuЋUЋE)ЋỦ!NjE)T}̋Ed)Ή!NjE)}ȉ`)UȉL!`)dX)`d!T)‹XH))!X!))TLL)@ȋH!щ)ȉ!֋H)84@)Љ!Ƌ@)08u,)֋8!u))Љ!ȋ0))!Љ‹,U)‹,)}!)UU!u)})!uE)))ȉU}!׉}u)!)Ή!u)E)ȉ!ȋM))!֍>8)ϋ8!ϋM 0 ))щ!Nj0)ω4)׉ʉ!4)),)!!Nj,֋}))!M)<0)ы)Ƌ!)!)ȉƋM!))ыu!))!֍ >}u >M܋}؍q wU܋}UԉM؍ wprMUEԃU9}u9umu܋E1ɋ}Ԋ1҉u؊1ɉ1Uԉ}M܊1ɉ1 7u܉)U؉u1}܊})U1u؋U)Uԋuԉ|1Ήl)!Nj))!))!))!)‹)Ƌ!))lj!))ȉ)!!)Ή)!)Ɖ)!Ƌ))!)Nj)Nj!))ϋ)!!x)x|t)ϋt!|)lj)!)t)ljȉ!)Љp!Njp)l)Njl!)})ȉ!uMFu9MlČ^_]Ít&'US [$jMQURMQU RURr]Í'UWVEEpH0uptMxXUFVEE}MU9E|-MEAuU@FBM}EuU9}EM)EItƋuUE}u9}}u} 1ɋT|u`} }M1Ɋ }M1Ɋ 1M1ɊuE1E1 }`}E1}E1U}ԉE)׋E!EE܋}ԋU)}})׋U؉!})}EϋE)׋U!}))EU!})uU)U}!ƋE))Ɖ}E!u))UϋUE!})U!})Ή})։EE!)E|UM)ы}΋U!΋M)u)}!))x|!u))׉!ϋ|x)!x)Ɖp})ϋp!<M)))щƋp!)!hщ)Ή!)ƉdMċd)}ĉ!))!ύ7}MuEA}M9EMEAuU@FBM}EuU9}1Ę^_]Í&'UWVE}w0GuEU9u|yUMBU9MfMu)EItۋUu֋}uMu EVщ}܋FU}ƒM؉Uԉ}9}}}}؋u11Ɋ1U1Ҋ >uԋ}܉M1Ɋ}܉EȋEuԉMċM)1MU1Ҋ9}؉EO1}̋M)%Ɖ!}lj}})NjE)}ȋ}!ȉ))!)ljщ})!ȋM)‰)ȉ!ȉ)M})!}})щ!ȉ))щ!E)΋M)!֋Uu)Ή}M!u))!uu))ȉ!NjE)MM)щ)Ɖ!})!֋M}u)ω!)ƍ<M)}!))!׍7u}M 7}Uu؋MԉU܋Uu؉MԉUMuu9M"M9Mu؋}11Ҋ1ɉEE7}؉UuԋU)1 }؁}؉֋U}EE)‰!ljx})lj)ȉt!)})ω!})|)!|)Ɖ}xϋU)lj!)t)UU}!׉}Ut׉)!)8)׉!NjE))Ή!UMBU9MĄ^_]É'UWV}؁G΋}̋M)ω!}lj}})NjE)}ȋ}!ȉ))!)ljщ})!ȋM)‰)ȉ!ȉ}))E׉M!})щ!ȉ))ыUȉ!E))!΋Mu)։}!u))!uu))ȉ!)M}M)ϋM!)})M}!)!ȋM48}))ljȋu!))!΍>u}U~uM܋}؋UM܉}؉UԋMEM}E9}E9EnU}܋u1Ɋ :)֋}ԉM1M:)ωu܉|U܋u11ɊUx|%)׉Ɖ!}ljp|)Njxhx)!)tt)Ћh!Njt)l)ȉ!Njl)p))!l)!֋hd)!d)Ǎ0)։!))!9}M}AM9}@Ĕ^_] UWVTE}G0MWOtEUwXQE؉UԋAUuE9UvJU/uȋ}FOuȋU؋MԋuBAF}ċ}U؉Mԉu9}AEMtŋEUEME9M}E M̋}̋4M̉uTU}TE1Ɋ 81}Њ7}ԋuE17}Ћu؉E܊%׉ƋU)ʉ!ЉMM)E)‰MЋM!Љ))ʉ!))!ЋU)lj)Љ!ЋU))!׋U׉)!Ѝ8ω))щ!))!֍ >}űEFljủ}}9}T^_]ÍUWVxEuF0VEUM9E؉EuMFu9MgM})EItڋ}UEz} EE7 FwM܍ FwM؍ W~M܍ W~M؍4W}uԉ}9}MMu؋UMu܉E}EE̋U@4yfuE̋U9UL^_]Ív'UWVEuF0VEUM9Ex؉EuMFu9MXM})EItڋMEUE4HE uu䉕p89U wxM܍ wxM؍wEU܋MԋEf:f0}ЋEf9ủ}ȋ}f4zfxыE܉}ԉMf Wuf W~M܍ W~M؍4W}uԉ}9}MM}؋Ef4Gf}܉úuԉUfEf}߉MȉUċM܋Uf4xEf} u؍ EԍM܋u EU؃E}9}MԉUvE9EM܋u؋E܋>Mԉ}1M} uFMuY0^_]Ív'UWVLEuV0FEM9U]ىMuFu9UDы})EItߋ}MEE4M} u7uFu9UL^_]zX6e떍&UWV4EEH0uxptMHX}FE؋}FMU܉E9}O}/UԋMB}܉Uԋu؋UIGFBEMЉ}܉u؉U9ELEMtŋ}ԋE4u1;uAEu]EEEu]EEu]EEu]EEEu]EEEu]EEEu]EʋỦUEIEEuEEu]EEEEE؋}EE MF;um} TL}}}}܋E}]ȋU]Eu]EEu]EEu]EEu]EE EU<}9U t&^k4^_]Ít&'UWV(EMq0AEU9u|_UBU9uM})EItߋ}MEMUU <}zM} G&0^_]Ív'UWVDE}O0GEȉMU9EuUMFډEȉuȉỦE5MЋEEMԋM)EIuB}UG}9Uo}9}|ËUEu‹E)UԋM)EItU uEE: BuM Eu-]}uMO7UMBU9M@^_]뇍v'UWV@E}G0uWwtEUOXVEUFUME9UbJU/M؋EAUu}܉MHBFGMEԉUu}9M-EMtŋuE؍<1;u|ZEEu]EEEEF;u<[M M UċDTEMEȋEMʋEMEu]EEu]EEu]EEu]EEE>&@^_]Ív'UWVXE}w0GEuU9E}uMGމE}ȉuE5UMEUċM)EIuB}uG}9uE9E|ËUEM‹E)ȉUċM)EItM }EE4A}u4AU܍ }ă}9}>}U}}U]EUEЉUEu]EEu]EEu]EEEEuEEEqEREEu]EUEˉEȉUEu]EEEuɋEȋỦEU]EEEEuEEEEtEE}؋UEE܉UUE܃EU9EEU]ыU]ȋEUEE(0~QX]Ef}9}5EUыMEu ]EEu]EEu]EEu]EEuuEudEuOEu)ȋ)1:)ȋ)18)ȋ)18)ȋ)18))ƃ 1ҊAփ |틽QE <1: 8)1 8)1 )ȋ|[^_]Ë1DI)ƃ }1AQU 417 8)Ћ12 8)ȋ12 )17 )ȋЋ17 )B9Qt&UWVE}w0Gu􋗈EEU؉uUމuE9U|#&UMBU9M Mu)EItۋu U}M׋V}M}N׋F U}ʋ}ljU܋MVщ}؋}Mԅ~u쉵\&uE11Ҋ}MЋu1ɉU1U܊}E1u؉EċE M1Ɋ 1UЉE}1uĉE)1ỦEE!u)u}̋uE)!‰UUȋ}))NjU}Eȉ!)}E)׋U!})uU)U}!ƋE))Ɖ}E!u))UϋUE!})U!})Ήx)։EUE!)t|M})ы|!΋M)|)!)Ήptx)ω!x))׉!ϋtp)!p)Ɖh})ϋh!<M)))щƋh!)!)Ή!)Ɖ`M`)}!))!׋U7Mu}ϋEԉu}u܋}\u܉}؉EԉUwUMBU9MĜ^_]Ív'UWVEEH0}pxtMHXuGMUwOW EEuMU9E|>vu}FGuUMu}B}AFGEUMu}9EMU)ыEItU}MU~u Eu`&EƋ8HV @\VuX1Ҋ>}U1ҊuMU1Ҋ1ɉU1Ҋ}U1Ҋ\UЋU 1Ҋ1}Ů7XE17EċEUu)֋U܉!u)u}܋uЉE)}Љ!)ljU}U؋E)֋U!)}E)׋U!})uU)U}!ƋE))Ɖ}E!u))UϋUE!})!})EΉ|EU)֋U!)xEM̋})ыU!΋M)u)|!))tx!|))׉!ϋxt)!t)Ɖl})ϋl!<M)))щƋl!)!)ȉ!d)}ċd)uĉ!))Ή!֋UM}`UMuĠ^_]ÍUWVE}w0Gu􋗈EEU؉uUމuE9U|3&UMBU9MMu)EItۋu U}M׋V}M}N׋F U}ʋ}ljU܋MVщ}؋}Mԅ~u쉵\&uE}1Ҋ01ɉU171҉E̋uE}01UȋU7}u܉EċEЊu؉M1Ɋ 21Ҋ7}uԉU1Ҋ7}U1Ҋ7}ĉUU)׉!EE̋}ċU)}})׋Uȉ!})}EϋE)׋U!}))EU!})uU)U}!ƋE))Ɖ}E!u))UϋUE!})U!})Ήx)։EE!)|tUM)ы}΋|!΋M)|)x!))pt!x))׉!ϋtp)!p)Ɖh})ϋh!<M)))щƋh!)!)ȉ!`)}`)u!))Ή!MuU}EU}܋U؋}EU؉}\MgUMBU9MĜ^_]Ív'UWVEEH0}pxtMHXuGMUwOW EEuMU9E|>vu}FGuUMu}B}AFGEUMu}9EMU)ыEItU}MU~u Eu`&}WOw }T\uO}X1Ɋ }M1Ɋ }M1TE؋u1EԋU}11ɋ\EЊ 21Ҋ7}XU1Ҋ7UȋE}1Ҋ7}ԉUċU)׋U܉!})}E}ЋE)׋U؉!})}EϋE)׋U!}))EU!})uU)U}!ƋE))Ɖ}E!u))UϋUE!})!EE}U))։|U!)xEM̋})ыU!΋M)u)|!))tx!|))׉!ϋxt)!t)Ɖl})ϋl!<M)))щƋl!)!)ȉ!d)}ċd)uĉ!))Ή!֋U}MUM`bĤ^_]ÍvUWVE}w0GuEUڋuEMމuU9E|&}UG}9UME)ȋuHt׋ME4HM uu9wyUwyUwy U܍wyU؍wuUԅ~U쉕u}U~>EЉ}ȋE VE}vUEBMA@}uUGUFBEM}MuU9MM})EItEUEuvMċ}AGUuEMċM}BF@A}UuEM9}MU)ыEIt}Eu xM~u UuPMuAyI LNHM4JuuOv}EG@}MUu}ABFGEċEMUu}9EMU)ыEItM}uOU~u EuP}uWO L~uĉHxvMuAF}EUMMuG@BAu}EUM9u?E})UHШt}uEU~9UE}} u|TuT uTMfHMUfHMUfHMUfHUԋUf4PEUuf4Gf WUM̉uf WMċUEf Wf4GME}uf4AEfyu}f4AEUfyfvuMFAu}EUuG@BFMM}EUu9MM})EIt}uU~҉E~U MUP}w LGwOHufvUMBAU}EuUG@FBMċM}EuU9MM})EIt}UuzE~u Mul}w WOhw}ĉdf4xuuf[ vuMFAu}EUuG@BFMM}EUu9M" M})EIt}uEU9UE뗍&EpEEpE9E~EpEEpEE9E~EpEEpE;U~ЋUEE9E~EpEEpE9U~EU‹E9E~EpEEpEE9E~EpEEpEE9E~EpEEpEE9E~EpEEpE;M~ȋME;M~ȋME;x{ xx9|~$|px|px9|~||9t~ttNjE9E~E؉pẺE؋pE̋E9E~EԉpEȉEԋpEȋE9E~EpEȉEpEȋE9E~EpEԉEpEԋE9E| EЉpEĉEЋpE9E~E܉pEĉE܋pEċE9E~E܉pEЉE܋pEЋE9E~EpEEpEE9E~EpEEpEE9E~EpEEpE;UЋUE9E~EpEEpE9U~EU‹E9EE9E~EpEEpEE9E~EpEEpEt9|#|pt|pt9~ȋtt;|~ȋ||9~9}~E}9u~EuƋx9E~EpxEpx9M~ME9EnEpEEpE9E~EpEEpEE9E~EEE9EE9E~E܉pEE܋pEE9E~E܉pEE܋pE|9EEp|Ep|9E~E؉p|E؋p|E9E~E؉E9~Љ9u~Eԉuԉ9U~EԉUԉ‹x9EEpxEpx9E~ xEЋE9E~EЉpEEЋpEt9Et9E~ẺptE̋ptE9E~ẺpEE̋pE9}~E}9}$E9E }Eȉ}ȉE}9}E9E~}}9ut9Eut9uĉEu}9}ZuE9u܉E7}9}~uԉu9M~M9M~M9M}9}~ }ЋEEЉ}9U~uȉUȉu9uE}ĉEĉ}E9Eu9u~}Љ};UtE9E[}ċu9}ȉu~ }ȋEĉEȉ}9U~}UE9E9M~M9~׉ʉ9MuM9~ uċEEĉu;U~ыUM9U}9}~EE;|||9ue|M9*}M}UGE}9U} EMltUlhLt |UUUU܋UU؋UUԋUEUЋhE̋EEȋEEċEEEUEEEEUEEE MMEE EM EEE4uu|E4ExEtE}Mt&uE9uԉE}9}t&Eu9E}9}jt&ut9u?E}9EE9Et&E}9vEptEptPx|st&EpEEpEvE;tEt&xt&EpEEpEIvEĐ^_]Í&'UWVTE}w0GuEE؋uEUދEu9E|ZUMBU9MGMu)EItۋ}u}u E VM VM V M܍ VM؍ }Mԅ~UMMUMUʋM9M~MM;uu9ωM~};U~ыUЉM9~;U~ыUȉM9~֋E9EhUЋEȉE9׉U~}9u~MЉuЉ;}~}ĉU9}~};u~u9~;u;}uEU}M܋EljU}U؋}ƉM܉U؉}ԉuM}EM0}܉uMEЋE49u?E̋EM؉EȋEċEԋM~Mu9~щϋM9M~ EЉMЉE9UE9EMuUE9}܉UU؉}}EU؉}ԉM MAM9U. ыu)EItߋ}EM4E} u7 vUMBAU}EuUG@FBMċM}EuU9Mh M})EIt}UEu9uE 뗍&}]}EEE u]ԉuEEE M]؉MEEE U]؉UEu]EEE ͋}̉}]EE ɋủu]EE[ ̋MM]EEuˋUȉU]EEEE U]ȉUم|E ]}|Eم|E u]|Euʋ}}]EمxEEy xٝxMEEO ]UUمxE* ]}xEEEu Euu]EE ]uuEE ]MMEEE U]UEEEu EMM]EEE[ }]}EEE4 u]uEEE M]؉MEEu UԉU]EEE ɋUU]EEE }]}EE ͋uЉu]EEű}܉}]EEEEI M]܉MEE' ʋUU]EE ʋ}ȉ}]EEEu Euȉu]مxE]uxEمxEuمxMx]EEvˋMM]م|EEuE|Uٝ|EEu]UEˉUم|Eu]م|ˋ}|EEEu E}]}EEu]E͋uuEEu]uE͉uEEEu EMM]EEE^EMUMEEu EUU]EEEu EU]UEEuEEEEu E}]}EEEEEEuE]EEu]EˋuuEEEjEuU܉uEuˋM܉M]EEEEu EM]MEEEEEEuE]EEu]Eʋ}}EEEEUUԉUEuʋuԉu]Eم|EEuE|ٝ|}EEE&Eم|Euم|u]Љ|EEu]EɋMMEEEEMỦMEuɋỦU]EمxEEuExٝxUEEuEمxEu]}مxxEEuEEEuEم|EEu Eٝ|مxEEu Eٝxم|Eu م|مxEu مxEEEuE]EEEuE]EEuEEEuEEEuEEEuˋuЉu]EEu]EEu]EEEű}Љ}]EEEuEEu]EEEu]EEu]EEu]EEEu]EEZEu]EEEu]MEʉME'EEuEEEu]EɋuuEEuEE؋UMuAEEEt&cĈ^_]Ð&UWV~U~UM~ ɍ4Eȋ} u܋w<~MuKt&Eu]EEEu]EEu]EEu]EEtEu]EEgEu]EEEu]EɋEEEEEuEEu]EEEEEuE؋EMMMNE9EEEEEEEE]]u ]EEu]EEu]EEu]EE)$8U<^_]ÐUWVLEEH0UpPtxXMu}E܋JB rEЋ}BM؉uԉE9}O}>U}JGEȋM@uԉU}܋UЋ}̉EAFBGEM؉uԉUЉ}9EqEMtUȋ}u M~u EuĉELvEu]EEEu]EEu]EEu]EEEu]EEEu]EEEu]EʋUUE^EEuEEu]EEEEE؋}EE MMăMMGuċEċUԋx}}MGw U؋MUUԋM]M]Eu]EEu]EEu]EEu]EE}E4u$Ut&GTL^_]Ít&'UWV@E}w0uUUڋGuԉUЉEދUu9UJUt&ŰuBNŰMu9MEMtڋM }̋U1ỦEAuȋ}FOE܉u@M؋UԋuЉ}A}BFGE܉M؋EUԉuЉ}9EzEMtuȋ}U ҉M~U EUĉENvEu]EE Eu]EEu]EEu]EEEu]EEEu]EEEu]EɋUUEgEEuEEu]EEEEE؋MEE4uMăMMEMċy}QϋI w}}}؋E}UԋEUЋ}ɋM]E]u ]EEu]EEu]EEu]EEuE<} &U?IH^_]ÉUWVE}w0GE􋏈6؉UMډUE9u|MAM9utU)ыEItߋMEU<ȋEM }9 NjE} MO ʋEU8<]EE ʋEU8<]EEEuEEUEU]݅(E ݝ88<(,E݅(Eu݅(EU(,]EEN ˋEU8<]݅0EEuE04EUݝ0EEuݝ88 vuMFAu}EUuG@BFMM}EUu9MM})EIt}uEU9UE뗍&u؋}]؉uЉ}EEEUM]UMEEEuȋ}]ȉu}EEEUȋM]ȉUMEuݝ0݅0EE͋u}04]EEɋUM04]EE̋u}04]EEuˋUM04]݅0EEE/UM]UM݅(Eݝ004(,݅x݅(Ex|ݕx(,Eu(ʋx|04ݝx݅0݅ ݅pEG $ݝ pt݅pE ݝ004pt݅ Eݝ004 $݅h݅`Eu$݅`hl`dݝh݅`E\ݝ004`d݅hE ݝ004hl݅P݅HEPTݝPHL݅X݅HEu$݅HX\HLݝX݅X݅PEcX\ݝXPT݅@݅8E#@Dݝ@8U}BOEԉU@MЋűUȉ}A}FBGEԉMЋEủUȉ}9EzEMtU}u ׅM~u EuERvEu]EE Eu]EEu]EEu]EEEu]EEEu]EEEu]EʋUuUuE_EEuEEu]EEE EE؋}EE؍ MMMMAuEŰx}}ыMGw ыUЋMЋUЋŰM]M]Eu]EEu]EEu]EEu]EEEu<}$&U?Kh^_]ÉUWVPE}w0uUUڋGuȉUĉEދUu9UJUt&uUFJuMU9MEMtڋuUUu EF<ыN}<ыN}<ыN 4Љ}܋E<х~MUU\Eu]EEEu]EEu]EEu]EEEu]EEEu]EEEu]EʋEUEUEkEEuEEu]EEEEE؋UEωU΋UEЉUUȉU܉EM.EUЋUЋUE‹E‹UċE׋U֋UȋE]]Eu]EEu]EEu]EEu]EEEUωE΋EȉEEʉE܉U)U5AP^_]ÉUWV`EEH0UpPtxXMu}EԋJB rEȋ}BMЉủE9}O}>U}BOEԉU@MЋűUȉ}A}FBGEԉMЋEủUȉ}9EzEMtU}u ׅM~u EuETvEu]EE Eu]EEu]EEu]EEEu]EEEu]EEEu]EɋU}U}EaEEuEEu]EEE EE؋MEE؍4uMMM?My}QϋI w}}}ЋE‹}ŰE׋Uȋ}ɋM]E]u ]EEu]EEu]EEu]EEt&E}4U?I`^_]ÐUS [ìz#jURMQURE PUR]ÐUWVS[gz#E} MUutDt VRQPjjWURze[^_]ÍVRQPjjW}W^e[^_]Í0,LUЍX(Dž4Dž8Dž(DžPuԉMPRWEP9ne[^_]ÐUS [Ìy#jMQURMQU RUR]Í'UWVUuz0NU}EMu@uU}L|DML}܉E؉MЋD| Ẻ}ԉMEE9E|uċ}Fu9}EċM)EItۋE uċ}UHuĉ}΅҉u,UEX&EU)‹}u1) ME}1)NJU1ɋu}MU1Ɋ>%1:UE1:U}E)׋U!})ljU}U)׋U)!})!ЋU)ƉUE)‰!ЋU)‰})׋U!)})׋U!}))ʉ!ыU)!ЋU)ljU)ʉ!ЋU)Ɖ))U!)!)!)<0u)}!ƋE))!ыUȍ49}u4U؋u܋ T UЋuԉLP 4ŰPHT)‰!ЋT‰|LP)ljt})׋U!L)ljU)ʉ!Љ)Ƌ|)‰!Ћ|)‰l)׋t!))׋U!t))։!֋U֋l)!Ћl)ljd)!))!d))Ɖʋd!)ω!)lj\H\H)ыU!))!ύ7u2MEXEu}u܃E؋MԋUЉ}u܋}̋uȃE؉MԉUЉ}̉uȋuċ}Fu9}LEB ~Mzİ^_]ËMİ^_] UWVUuz0NU}EMu@uU}L|DML}܉E؉MЋD| Ẻ}ԉMEE9E|uċ}Fu9}EċM)EItۋ}ċEM 4xEċyuuGU!UEXt&}UߋME4zuuFUVM}ȉEċN ~FUUM}E9U|P&U}ȋuċMBGFAUEU}ȉuĉM}uMBG@FU9M}EuMU)ыEItM uU}ȋUu11Ɋ}U1ҊuU1Ҋ}UU 1Ҋ}U1ҊE}UU)!׋U)}ʉ)E!׋U)E)!))׋!)‰)׋!Nj))!׋))ʉ׋!ы)‰!Ћ)lj)ʉ!Ћ)Ɖ))!)!)!)7u})׋U !))!}ȍ11ɉEuċB1Ҋ}U1ҊuU1Ҋ}UU 1ҊU1ҋ}E|}U)!׋U)}ʉ)E!׋U)E)!))׋!)‰)׋!Nj))!׋))ʉ׋!ы)‰!Ћ)lj)ʉ!Ћ)Ɖ))!)!)!)7||)׉!))!11ɉE܋uċU }ȋB1Ҋ}t1Ҋup1Ҋ}lU 1Ҋ}h1Ҋttdp)!׋p)lʉ)l!׋h)h)!))׋!)lj)׋!Nj))!׋))ʉ׋!ы)‰!Ћ)lj)ʉ!Ћ)Ɖ))!)!)!)7dd)׋U !))!}ȍ11ɉE؋uċB 1Ҋ}\1ҊuX1Ҋ}TU 1Ҋ}P1ҊL\\X)!׋X)Tʉ)T!׋P)P)!))׋!)‰|)׋!Nj))!׋))ʉ׋!ы|)‰!Ћ|)ljx)ʉ!Ћx)Ɖ))x!)!)!)7LL)׋U !))!}ȍ11ɉEԋB1ҊD}u1Ҋu@1Ҋ}Mu_uuMEAHM}E9}uB ~B}h^_]ÍvUWVEUz0uBrtJX}EV>FUVM}ȉEċN ~FUUM}E9U|P &}UȋMċuGBAF}E}UȉMĉuUMu@BGFE9MU}u MU)ыEItuMU }ȍquĉEU xFUVM}ȉEċN ~FUUM}E9U|P&}UȋMċuGBAF}E}UȉMĉuUMu@BGFE9MU}uMU)ыEItuUM ruċUȉEfEUE܍ }܉MMuEAME]]]9E,EE;6vM5hA+-o~@ya8 ^l.X^_]ÍUWV,EUuz0Nu}M􋖈}űM}UvE܉U ẺM9EHEMEAHM}̉E9}EMtڋU Eu:rM̋}uMEu]EEEu]EEu]EE_EEEu]EEEE̋MNMEMEʋMMEu ]EEu]EEu]EEu]EEt&E̋MNUuuMEAHM}̉E9}IE@ ~E܃},^_]w'UWVĀEuN~0MNt}~XU1A}uyEq UA}uEqy9U|= MUABMUMUABMUMUABMFMGU9Mk EM)EItEU ‹E MEEEEEEu]EEu]EEu]EEu]EE Eu]EE Eu]EEu]EE[ EC Eu]EE E ؋E ]؋HEEEEEEu]EEu]EEu]EEu]EEu Eu]EEJ Eu]EEu]EE EEu]EEE؋E ]ЋHEEEEEEu]EEu]EEu]EEu]EE&Eu]EEEu]EEu]EEEEu]EE~Ef؋E ]ȋH EEEEEEu]EEu]EEu]EEu]EEEu]EEEu]EEu]EEnEVEu]EE/E؋E ]HEEEEEEu]EEu]EEu]EEu]EEEu]EE]Eu]EEu]EEEEu]EEE؋E ]HEEEEEEu]EEu]EEu]EEu]EE9Eu]EEEu]EEu]EEEEu]EEEy؋E]E9@Eu]EEEu]EEu]EEEEu]EEEEEEEEE]ݕxEuEEEEEu EEEEEuEE]Eu]EE EEu]EʋEUEUEEuEEu]EEu]EEmEUEu]EE#ݕxEًUEEE݅x]EE]Eʍ]]]]UA;MwU UЋUЋUЋUЋUEu]EEu]EEu]EEu]EED?EEȋEUE]]EE]ȍ]]]E0݅xvE;6Q7jC+3sO$}V>F.b7^_]ÍUWV0EMq0UAUE<6 v}UME9u苺U~}t&MUAJMU9uEMt݋U E: NjEw<U}ԉU؋}Eu]EEEu]EEu]EE[ECEu]EEE؋UEԍ OUUEыUʉыUEu ]EEu]EEu]EEu]EEًUEԍ OU[t&}}MUAJMU9uGE@ ~E܃u0^_]ÐyUS[,"jjU RMQURMQURE PUR]ÐUWVS["}E E}p}wE0U1JE 8Mߊ 8M߈:8Es J:8Er9>FM߈ :>8vF>8Ew9Z:JM߈ >볊UGOwWEGE8EvEEuE8vȈшŠE8EvEEEEEE8MvEME8EvEEEEEE:EvuE:UvЊUE8vΈ:UvЊUE8vֈ:MvȊME8vȈшŠEWOGMOEGe[^_]ÊUOGW8MwEvEM8vЉ:UvЊUE:EvuE8UvEU:MvȊME8vȈш‰8vȉƉ8vЉƊEWOOEGe[^_]ÊEWOw8UvEU‰8vȉƉ8vЉ8MvEM8vЈʈEOWOe[^_]7OW8vΈ8vֈ8vȈш‰OWe[^_]ÊO8vЈʈOe[^_];u}!E|>)HU9|uQQVWRRM)ML>UJRQ̍&'UWVS[Ê"}E E}}wELU1JEf GfMff GfMffWf9E} JfWf9E|9fwfMFf Wfwf9~&Ffwf9E9fWfMJf wffGfUfwfEfOfG fWf9ufE~ fEfuf9~ȉщ‹Ef9E~EfEދEfEfEfEf9M~ fEfMEf9E~fEfEދEfEfEfEf;u~ ufEf;U~ ЋUfEf9~Ήf;U~ ЋUfEf9~։f;M~ ȋMfEf9~ȉщfEfOfMEfwfWfOfG e[^_]ffGfMfWfOfwf9MfE~ EfMf9~Љf;U~ fUfEf;u~ fufEf9U~ EfUf;M~ fMfEf9~ȉщf9~ȉf9~ЉƋEfwfuffOfWfwe[^_]ffWfEfOf9Ufw~ fEfUf9~ȉf9~Љf9M~ fEfMf9~ЉʉfEfWffOfwe[^_]f7fOf9fW~Ήf9~։f9~ȉщf7fOfWe[^_]ffOf9~։ʉffOe[^_];u}!E|w)HU9|uPPVWqPPM)MLwUJRQS̍&'UWVS["}E E}}wEhU1JEf GfMff GfMffWf9Es JfWf9Er9fwfMFf Wfwf9v&Ffwf9Ew9fWfMJf wffGfUfwfEfOfG fWf9ufEv fEfuf9vȉщ‹Ef9EvEfEދEfEfEfEf9Mv fEfMEf9EvfEfEދEfEfEfEf;uv ufEf;Uv ЋUfEf9vΉf;Uv ЋUfEf9v։f;Mv ȋMfEf9vȉщfEfOfMEfwfWfOfG e[^_]ffGfMfWfOfwf9MfEv EfMf9vЉf;Uv fUfEf;uv fufEf9Uv EfUf;Mv fMfEf9vȉщf9vȉf9vЉƋEfwfuffOfWfwe[^_]ffWfEfOf9Ufwv fEfUf9vȉf9vЉf9Mv fEfMf9vЉʉfEfWffOfwe[^_]f7fOf9fWvΉf9v։f9vȉщf7fOfWe[^_]ffOf9v։ʉffOe[^_];u}!E|w)HU9|uPPVWqPPM)MLwUJRQS̍&'UWVS([ "}E E}c}wEU1JE M̋ M̉9E} J9E|9aMF 9~F9E99MJ 볋GU؋wEԋOGW E9u~E؉u؉9~ȉщ‹E9E~EԉEЋEEԋEЉE9M~E؉M؉E9E~E؉EЋEԉE؋EЉE;u~uE;U~ЋUE9~Ή;U~ЋUԉE9~։;M~ȋMԉE9~ȉщ‹E؉OMԋEwW OGe[^_]ËGM܋WOw E9M~E܉M܉9~Љ;U~ЋUE;u~uE9U~E܉U܉;M~ȋME9~ȉщ9~ȉ9~ЉƋE܉w uOWwe[^_]ËWEOw 9U~EU9~ȉ9~Љ9M~EM9~ЉʉEWOw e[^_]Ë7OW9~Ή9~։9~ȉщ‰7OWe[^_]ËO9~։ʉOe[^_];u}!E|)H(U9|uPPVWQQLE)EUJRQ UWVSP["P}E E}}wEU1J EuJEt9&FEuvFEt9JGGG GGEu ]EEu]EEu]EEu]EEu]EEu]EEu]EEu]EEu]EEu]EEu]EEu]E___ __e[^_]GGG GEu ]EEu]EEu]EEu]EEu]EEu]EEu]EEu]EEu]E___ _e[^_]GGG Eu ]EEu]EEu]EEu]EEu]E___ e[^_]GGEu ]EEu]EEu]E__e[^_]GEu]E_e[^_]9}!E|)H`U9|uQPPVW+PPM)MLUJRQ ̐&UWVS["U }U}`}wEM1IEEut&IEt9sFEut&FEt9AIGGGG G(EuEuEuEuEuEuEuEuEuEuEuEu____ _(e[^_]GGGG EuEuEuEuEuEuEuEuEu____ e[^_]GGGEuEuEuEuEu___e[^_]GGEuEuEu__e[^_]GEu_e[^_];u}$M|)IME9|uQQVW΍LRRU)UEHPQ̐&UWV1S[f"Dž M}!}9!кUEU E$ 8Et@E$w+إ Q& Dž ƅe[^_]Ë L0,DžDžP(84$ƒ$ Шu $(PQU R}W# DžL L0,DžDžP(84uNj}( E(48(, 0LDžPDž}4 V  \ h " ƅsDž 1ك$@=DD|)@&US[ì"jjU REPMQURMQU RUR]ÍvUWVĀM}EqW0uUuUFJ u}}M܉EԋMFWuUЋQ EȋU̅EыE􉎘9EEE}@MωEu}9uMU)ыEItЋMuE}uE֋ẺU))M1;M}} }9A};M|PPUR}WgMUыE}ƒE̍ 1ҋ}O;U}} D2B});U|WWEPV}̋MUȉMuȋME)ыUu ~}ԊDu E8E8t@8uP:N9s BB9rN}1OUu}9B8JvFJ@;E|E8ʋEЋM u܋}ԋDM}E8E8t t&@8uPE:AV9s BB9rEA1Uu;Et&B8JvFJ@;E|E8ʋEЋM ft&f9VFt&f9uPEf;P9rt&f2fr;UsEfU9U-UuЉUN1ҋ}9Eff0EHxvuuEf0B;xwf9~߃f} uЋM GTEU}ԋMuEfyf ~}f9ʍ4xf9tf9uf;NP9sf:fz9rfNU9UM}uEffEE؍ GVEMEHE9Efff~";}fUEBU9Ef;~~Ճ;uff~f9VFt f9uPEf;9rf2fr;UsEfU9U2U؋u1J}u9fuUfff@;ETf9~UUufPV} WMQ:dže^_]fBff9|fJfBff9fJM9MEu}uffUU؍VUJE4O1uU9ufvfBff9fJ*fBff9|fJUf Uff~f;~3f;M|W;Uf~ufE}Ѝ xMf~f;E| ~;}r`ȘϋuEfff9!uf;|wE);u|Yы}f룉ЋUf;|}E);}|BȋMfzfFf9~f~fGf9RKfGf9~뚋UfBf9~밋UEf2ff9 }f;7|UE);UʋMfuȋMދ}fquw;U|1;M}1E}NjE }}̍;Mt}fxfA;M|MPPURV,EċUЋMf|Ff:J}̋UGEЉ}9UU}MGI}EM9Ee^_]Í&'UWV`M}EqW0uUuUFJ u}}M܉EԋMFWuUЉEȋQ U̅W׋EE􉎘9EQEU}uBUU w9UM)Mu)EItϋU}M4WŰuu}ȉ)ыUM)1;M}} }fPfOA};M|PPUR}WMUыE}ƒ }̋UD1;U}} Df;U|UPPEPVUMԋU|NM9sf:fz9rfqUMBU9MCf9QAt&f9uMPf;19rt&f:fz;UsEf0뛉ȋMf;rOE);M| }ffGf9vufFf9UEf ff9w%f;Mr M;Mr1щȋU%ff;Ur W;UrȋMfufFf9vÉfGf9vfBff9rfr&fBff9wfr@;E} @;E|ff;UrfW\@;E}M@;EM|}ff;M5EfH)UB9} B9|ff;MfOE@;E}@;E|ff~fMf9~r f~f~f~e&'UWVPUMEz0qU}uH u}E}VtUuWM}HXFU؋M܋}M׉EԉA}ĉEEM9M"IM}MGI}EM9EEMtڋ}EUE x}ԉMЋUE9U}MčT UE}NjM U P1;U}}Bf;U|1;M}1E}NjE }}̍;Mt}fxfA;M|MPPURV|EċUЋMf|Ff:J}̋UGEЉ}9UU}MGI}EM9Ee^_]Í&'UWV`M}EqW0uUuUFJ u}}M܉EԋMFWuUЉEȋQ U̅W׋EE􉎘9EQEU}uBUU w9UM)Mu)EItϋU}M4WŰuu}ȉ)ыUM)1;M}} }fPfOA};M|PPUR}WMUыE}ƒ }̋UD1;U}} Df;U|UPPEPVűMԋUf|NM;U|MEȋUM܉EMMoPPUR}W豮uENu9u=E Űu xE̋L}MEuEU؋M|EuЉ:} u9E9t v9uP;N&9s :z9rN}E9Wt&9uPE;9r :z;UsE܉PV} WUR}dže^_]ÍF9Vt9uu܍P;S9r 2r;UsU܉ }ȋUEȋM4u}u)ƋEuƉuu܉uEE8E09=u9u}&9|}9}r;UsbE}09w} 6v9|1}uu9}r;Us':9;UrE9EsU21UE9Er}E؋Uu}u}Ѝ ׋uUEBM؉UM}ЉE9MMEU؋t2B9JB9|#B9JB9|J|EE9}9|1u9u}9}s0E0E9}9|ҋ21;Ur'UWV@EuEUN0xM}M}uuA WtE}NXUM܋WMAȉUE9EMHEE}@OEU}9UEMtڋMEE;U|MMMu͋}PPURVHM̋E܋|M؍9EԉU@UE9UdE}@OEU}9Ue^_]ÍUWVĀuF V0NUMVUщE؉ʋFщEM̉}NMEuME9u|t&UuBU9uM})EItۋUM}ċuU E܋E )1;M}u}A;M|PPUREPMuĉMEM UԃuN;U|1;M}/E}NjE }}̍;Mt }A;M|MPPURVPEċUЋM܋|:}̋UGEЉ}9U[}MGI}EM9Ee^_]Ít&UWV`M}EqW0uUuUFJ u}}M܉EԋMFWuUЉEȋQ U̅/׋EE􉎘9E(EU}uBUU 9UMMu)EItϋU}M4Űuu}ȉ)ыUM)1;M} } }A};M|PPUR}WמMUыE}ƒ }̋U1;U}&} D<B9F9D=vB9|JbB9JNB9rt&B9|r{B94-F9&UWV@EUuEJ0~M􋐈MU}}qtuGXuEVFщUԋV щẺMЋM9MIMMEAHMuE9uEMtڋUEE4Uu9U}uЍL6M1;U}REM EȋE܍4vEȋME ;UtEԋM)ЋDMB;U|ËMPPURWEЋU؋Mt24U܋MBu؉U9MgMEAHMuE9ue^_]ÉUWV@EEU}Hr0MuMuy UFE}E֋}UHM܋BuUw u؉E9U|&EM@E9MfM})EItۋU uE })񋐠4U 1҉M;U}M}ΉB;U|RRUR}WіMsME;U|UPPEPV角MԋU؋|M9rv:z;UsE돍e^_]Ðt&B9|oB9J^t&'UWVpuV0~U􋆈V NU؉E׋MEċN}ЉỦMȅM̋E;U|oPV} WURkedže^_]Ë2r9rYiAQE@t&E@uًut"ZEtZ J;UsE}̋UűME}E)uljEE}E}؋EɉEEuEt؋E9Er;Ust}GEu ;Etك;us#}GEu :9;Ur;us;ur}E܋Uu}u}ԍ ׋uUEBM܉UM}ԉE9MMċEU܋t2ɃEuEt;uE9EsBuuEuEt؃:9;Ur UWV@EuEUN0xM}M}uuA WtE}NXUM܋WMAȉUE9EMHEE}@OEU}9UEMtڋMEE;U|MMMu͋}PPURV(M̋E܋|M؍9EԉU@UE9UdE}@OEU}9Ue^_]ÍUWVpuF V0NUMVUщE؉ʋFщEM̉}NMEuME9u|t&Eu@E9u~M})EItۋUM}čU E܋E )}1;M}u}A;M|PPUR}WڈMuĉMEM UԃuN;U|1;M}/E}NjE }}̍;Mt }A;M|MPPURV0~UЋEċM܋|:M̋UAEЉM9U[}MGI}EM9Ee^_]Ít&UWV`EuUxN0}}MuMuWA U؋N}UuEMԋBO MЉE̅)}Eщ}9}UEUMuBUE<9E}MU)ыEItϋu}EUč uMM̉u)׋U)1;M}vu 44Au;M|PPEPW|UE‹MuȋU uU1E;U}"&u DuBu);U|PPuVMQV|UċMEȉUűM U)uquu؋E LuMUE E@tE@uAɃcZEtZEEHME9EEu MuEٍJ);MuUMuE)MUʋuU܋MċUΉE9UM̋U)ʋE̋M܉UЋUM)ЋUUE EE@tE@uAɃo ZEtZ2r9rYbAQE@tE@uًut"ZEtZ J;UsEEEHME9ENBt ؃uEFu9E|%u UԋuE؍QE);U$AE VuVM Q}WYEǀe^_]ËB9rYbAQE@tE@ut) &ZEtZ J9sMEȋUMEE EE@tE@uAɃ t&ZEtZEE9E@EMYٍJ);MTUE U‰MU܋MċU)׋MUBU9M^XB-t&B9rY]AQE@tE@ut$ZEtZ J9sEE9EBt ؃E@EE9E|EUu&؍Q);UAEuBUWV@EUuEJ0~M􋐈MU}}qtuGXuEVFщUԋV щẺMЋM9MIMMEAHMuE9uEMtڋUEE4Uu9U}uЍL6M1;U}REM EȋE܍4vEȋME ;UtEԋM)ЋDMB;U|ËMPPURWNuEЋU؋Mt24U܋MBu؉U9MgMEAHMuE9ue^_]ÉUWV@E}uEW0NUMU}J MUA΋BEuMV MU؋AEԋE9E|uMFu9MEM)EItۋU uM)4MU 1҉M;U}MΉB;U|PPMQWtEyUEȉEЋE EċUŰUDUЉUEumE@tE@uAɃt. t&ZEtZtt&B9rY_AQE@tE@ut$ZEtZB9sEUЍ4EMĉUuMFu9MWMB;U|})e^_]Ë}e^_]UWVDMuEyV0M}Uyu}GUU}RME QUR9EE9EHEMEAHM}E9}EMtڋM}U싁EȋEDž҉}~U؋}}čLUMԉUЋ}Mȅ~H}܋UJuMWWURVqEȋ}؋L}MωEȉ}1EPPMQVTqMȋU؋E|MȋU:‰UMqMEAHM}E9}e^_]Ít&'UWVPEuEUN0xM􋲈M}}uA uEWtENXUM܋xU􋰐}9UzEG@}EM}AM9}ZMU)ыEItۋMUEMEыEU‰MԉU9U}UԋE|Eȉ}Đt&}E Mȋ1;U&}B<>;U|UPPEPVoMԋU؋|M}:E <9}:B}});U|WWEP}W5XZURMQ5u1Uu;E}[}t&B8Jv uJFu}@;E|E8v=uЋE ʋM؋<:9rM}A);MUNju:EE8wË} uЈE4}:7rzE);}|}MuM}MUu}ȋEGMMԉU)ȉ}ȋUuE9UuEFUЉuME9MHe^_]ËEP:URJMB8xEp'UWV`EEP H0pMuH։M΋UΉ}űxpUUuȉ}ĉM9UE}uG}Eu9EMU)ыEItҋME}ȋU ȋMɉE܋)ȋM1;M}u}ffOA;M|PPuV}W6MȋE }U4EuUԉ}9uV1;U}u Mԋf09u9u}9|E9Er;Us_}7}9w} 39|1Euu9Er;Us':9;UrE9EsuuE9ErEU؋}uEuEЍ u܋}UG։M؉}MEЉu9MuE}؋TuUFu9Ue^_]ËEE9}9|}19}u9us4}7}9}9|΋21;Ur%'UWV`EEP H0pMuH։M΋UΉ}űxpUUuȉ}ĉM9UE}uG}Eu9E]MU)ыEItҋME}ȋU ȋME܋)ȋM1;M}u}A;M|PPuV}W+MȋE }U4EuUԉ}9uV1;U}u MԋWWQVdQ`RXP\VPPQVdW`RXQ\V`넋}WURQVdQ`WXP\VӴTUWVShu[.!jM QVjjjRUR舛 u ~te[^_]ËuUd}uM`u\xt+uPPQWVdW\P`V%PPQWVdQ\R`W%.zUWVShu[^!jE PVjjjRUR踚 …u ~t e[^_]ËU}`uU\MUXxdƒ 1ҨutIwI뚃tXuPPVWdR`WXV\Q_8dPPVWdR`QXV\W$PPVWdQ`VXP\W\PPVWdR`QXV\P-널UWVShu[!jM QVjjjRURX u ~te[^_]ËuUd}uM`u\xt+uPPQWVdW\P`VAPPQWVdQ\R`WMzUWVShu[.!jE PVjjjRUR舘 …u ~t e[^_]ËU}`uU\MUXxdƒ 1ҨutIwI뚃tXuPPVWdR`WXV\QZdPPVWdR`QXV\W?PPVWdQ`VXP\W3PPVWdR`QXV\PM널UWV$UM tt r;qt$^_]Ðz }؋};y u:}};9uԋz}};yuzA}EzIU}}}u}؅F}؉E1;UEEG΋EEuiEXEuVEXEuCEXEԃ ;U܉E}8E@@@DDDEt9| !EB9}EEtUE‰UUO01U؅~~}}1;UEEG΋EEuiEXEuVEXEuCEX Eԃ;UE}8E@@@ DDD Et9| !EB9}EEtUE‰UUO0UWV@UM utttBE;At@^_]Ðt&B E܋E;A uߋE؋E;u΋BEԋE;AuE;Ft묋};~ uEؿ;uE;FuByE}B}UԉEA}NEMv}}J}܅M}܃M1;M|Vt&΋EEuuʋE\Eu]؋E\EuG؋E\;M}BUDDDDDDEt;M|&؋EA;M}UEt܋MUыEMMȋMΉEO1>U܅~ME9}EUDDDDD D E͋EUEʋEU\E؋EU\Euz؋UE\ U9eE9E}-EUEu=؋UEBEU9|ӋUEЋUEE‰UUIfD!UWV0u }ttttDe^_]Ã~UPPRVwFt=~w 1e^_]Ðt&~MRRQVjFuEEE]ЋUЋuԉwEEE_딉HuEEE]ЋUЋMEEU؉ME]ЋMЋuԉMЉuEEEus]ЋUЋuԉw!QQWVsIPPWVU؋u܉wMuw OMUWOuMOwU_4UWVS$[z!Ur BJuzrE9uك8} Uك8WW…U~UE9}~K؋UDEuU؋UDEuU؋UD EuU؋UԃU9}UUEtًUDEtڋUDEtۋUD Et܋UԃU9}~19u|Dt&؋EB@E9u}1EE Et؋E ЋEB@E9u|Ԑt&MU E Eub]؋u }؋MF>NEud]؋u U؋MFVN Eu#]؋u U؋M^VN$[^_]]؋u U؋M^VN$[^_]뚋}E&'UWVSP[NEud]؋u U؋MFVN Eu#]؋u U؋M^VN$[^_]]؋u U؋M^VN$[^_]뚋}E&'UWVSP[ !MAQy ƋA@9݃ |Hu19~*aDEu3DEu3؃99EtDEtDEtۃ9~͍ENuU ZZX[^_]ÉǿR7USQ[!QME PQQpf]ÐUSQ[!QME PQQ]ÐUWVEUEE utttJM;Ht Ĉ^_]ÐJ M؋M;H u MԋM;uϋJMЋM;HuM;Nt뭋};~ uMԿ;uM;NuJxM}J~MUH}FMuЉE֋u؉U}}EM;M;M!Шt;MuUu־U}te}~KEMu܋U~E HuuUMu܋EΉUu܋uOuu1~u}E܋M}̉EȃuljȃljUăvu܋}uȉ}E}̋UăU}9sQU2zUȋREuEɉUEMɋUuȃ]]EU}Eu;Mr؋}};Ms uȋEMEU܋}MuMMU܉}MVEtv}ȋUăU}9sNE̋8pE}E8P}ɉuEMɋu]E]EuUE;Mr؋UU9tiuău9#Űz2UȋRuxE؍x|ŨU}}E؍|}Y;MrEăE9Ű2zUȋRupE}tE؍pɋ}̋uȃ؍tɃ}̉u]]E;MrVUSP[î!UERM QPPf]ÐUWVPEMU q;rt EP^_]Ëy ;z u9uAE;Bu݋A}ԉE؋BE܋BEЋAE̋B$EċUA$EȋE9 M9~ E9EMȋuUԉMI҉MV}ԉ}EHUЋu؉EUȋE)‰ЃUċM܍<)+}ȉM}}tDMȉM9MuU)!ƋEM؃@E!"F ЈU9#MAFM9~;}}l;uuU)ѺыUʃ"! OV;M}2;UuU)ѿ}ȃW"F! ЈFM܋E؋}ЋuMM܉EE EUM})ыEEM9EUE@EtwuU)փ~ E1ɊHMuMȉuM9MuU)!ƊMEeMMȃM؃!"F ˆUUU97E1ɃMMeEMMȃFMMMA9׉M~;}U);U~ }1E;uuU)MEM}Mȉ"! 9)E}t&E7BPE1ɊMUS [!URRCh]ÐUWVEEM }P;Qt E^_]Ëp ;q u0u;1u܋pu;qu;Wt EË;w uuE;7uu;wupuȋquЋwűuċqI$uMwO$uMpH$uEM‹U9M9y9n9 Eԋu}EĉuNugMԋUă`dEHyE}UE}ȋE)‰ЃyBuU) ƋuU)uM)ЃyBMU)MU)ȋUE tGU;}u+M!ˆUMEA @EMEM!" ЈG;d UMB AUGM;d~;u}1;}uU)ѾEu "! ЈMЋEỦMЋuMȋ}`ỦMEUE)‹uMEމU9Mu M1ҊUE@\ME)~ M1ҊQUEM\X;}u+M!‰XUM1UeMMʋM AXMX"! ˆGUU;dI\M1҃eЊM؉UUM BU\EAGE\;d~;uU);U~ \1ɊM;}uU)EM1ҊEM}M ‰!"2ME)‹uEDžxމU9Eu M1Ҋ|E@TME)~M1ҊQxEMTP;}u+M!‰PMxӥ|M1|ȋMA ЉM䊕PP!" ˆGx|;dXTM1҃ӥ|ЊM܉xU| BU䋍TxAG|T;d~;uU);U~T1Ɋx;}uU)M|M1ӽxxȋM "! qu9uE)ƋMى(UE))DžpDžh4$ M1ҊtU싵4) u1ɊlMuAF,04}~u1FpEԍ ~}1ɊOhuM0UƃM艕,9Mu})!ƊMpӥt(htȊMӥl$l׋Mȉ !" G4ph4tld940,1ҊM1ppӥt(htʊMӥl$l ˆGhpl04,FAt4d0,94PM94ȋ4);(~01Ҋp;$~,1Ɋh;}uU)Mt⊍(lӽppʊM$ӽhhEUكĞEMM]EEEŰEUEMʁUUUɉUċUEʁUԉEˋʃ˃]]v. VuWVE PQ躳e[^_]ÉػĞ^ UVWR} WQe[^_]Ít&ك$ۀE@u*EEp>EكĞ1p>EUكĞEMM]UWVS[×!U EzH(U Lp,H0)׋P8UU}ҋx4dPHLE Ƀɉuɋ7ʉU}|E4EU~M]]]F]E̍4M]GGMG MЋ}O}9}~ك<}fM׵ ]fMeEۋuMċuBɋE@4MEBMB MЋU9U%FɋEMUMɃ‰EFM؋E˃MF MЋ UE7M@M@M@ M@M@M@ M]ɋu DˍT]]]E]݃XQEQˋuMċummJLlXɉdɉp`Ƀhhʍ8]E]̍]Xm]E]ɍ8]E]hݝx ɍ` ݝXܭXݝX} DžtLE4U~FGMɍ4MGMG ܍x}O}9tك<}fM׵ ݝPfMt݅PʋutummBMɍ4MMBMB ܍xtBt9tiFMɋpldMɃ‰pFMh˃lF ܍xhd7@MM@M@ ܍x@MM@M@ ܍xX͍8܍X]̍`E]ʍ]hm]E]ʍX]E]ݝx̍8ܭxݝxɍh`̋u ΋D΍T݃XݝXx݅Prڋutuoك<}fU ݝPfUFMɋEM }\,MA h|PMQUJQRQRP|Q P|RQR݅M؃ĞANݝuLPPM`Qu\VPRVQRVQREPuVW}WPPu`VM\QRVQRPQVRQVRQVPRQuVURW}W"\9EE\W 1W ЈP$Eg1P$ݝhP1ЈP$݅pɃEu1P$ݝp1ҊPtۅtEًd)Љdۅdݝx1ҊPtۅtEu )Q$]hك̞$M`QE\PQVQVRhQRVW}W50 RcZWV1كТ ݕh QEUWV Mq0A4uQ81EU;u}0}U }F} ;u<ЋW}ωT|Ѓ ^_]Ðt&UWV1 u;}N0F4V8MEU}=UM4U GU  ;}UAQ MDT |à ^_]Ð&UWV1 u;}N0F4V8MEU}GuM4RU GU ;} IU A\AQMDT| ^_]Í&'UWV1 u;}N0F4V8MEU}KUM4U GU  ;}UA\A\AQMDT| ^_]ÐUWV Mq0A4uQ81EU;u}'}U } }F}9;u|ك ^_]Í&'UWV Mq0A4uQ81EU;u}0}E }F} }BOPTO;u|Ѓ ^_]Ðt&UWVMq0A4uQ81EU;u}:M} R}F} @}9PT9PT9;u|ƃ^_]Ít&'UWVMq0A4uQ81EU;u}>U}E  Qʴ1e[^_]ËEN W覴X|V͍UWVSX[Ú|!}@E@G}EE]E]E 49]E(]E0]E8]RϳE1M}EU@49UEEEM@ȉEE@+EQE197EʋE MɋEȃAMɍT;M~ɋEM@MɉE9M{؋uM΋uE.ƍUWVSl [ !Myt:G)e[^_]19ك̞UUݜݜF9|19ك(ك$݄݄1MtXٽf f٭۝٭ B-tu B~؉F9Z؍PWu VMQOe[^_]19}׋UUݜݜF9|19}كHكL݄݄1NtYٽf f٭۝٭fQB/tufQB~؉F9W19ك̞UUݜݜF9|19ٽك$f ك,f݄݄1uu٭۝٭fQB~؉F9|19ك$݃Xك<ك̞ك(UU1NuWtWٽf f٭٭B;ttuB~؉F9M11ɍ9!u1΋u@=~؉A9|1ɍ9u1΋uΐt&P$ʃ@=~؉A9| UWVS[7!EU x}rxu}x r}x}xt=  tĜ[^_]1;M}$ك̞UʋU\\ͨA;M|غ}EE]DݝxDը}ݝ`DըDȺ}ݝpDȋEݝhDըUݝX}MUH&19ك(ك$t&F}fE fEm]mE:20}fE fEm]m‹ED:}fE fEm]mED:}fE fEm]mED:9VMDVDVDV܍x܍p܅`܍h܅X:D:D:D:9;VMك(uك$u}fE fEm]mE:B;CV܍x܅`uum]mE:B;V܍pm]mE:vU֋U 1;M}UʋU\ͨ\A;M|}EEݝPDըݝHDȺ}ݝ0DDը}ݝ@DըEݝ8DȋUݝ(|}H M, UHx19كHكLM}fE fEm]mEfW:8}fE fEm]m‹EfDW%#}fE fEm]mEfDW}fE fEm]mEfDW9V܍PDVDVDV܍H܍@܅0܍8܅(fWfDWfDWfDW9;|V܍PكHuكLu}fE fEm]mEfWB;|! V܍H܅0uum]mEfWB;| V܍@  m]mEfWU֋Ux1;M}(ك̞t&UʋU\ͨ\A;M|غ}EEݝ DըݝDȺ}ݝDDը}ݝDըEݝDȋUݝt}4 MJUHp198}ك$fE ك,fEt&V܍ DVDVDV܍܍܅܍܅uum]mEfWuum]mEfDWuum]m‹EfDWuum]mEfDW9;tV܍ ك,uك$u}fE fEm]mEfWB;t V܍܅uum]mEfWB;tN V܍F 0 m]mEfWU֋UpUUݝ}EЋEݝк}EݝЋEЉʉM}ݝыMEݝыUݝl}MYUHh19݃Xك<=}fE fEmm,,}fE fEm\m}fE fEm\m  }fE fEm\ m9V܍DVDVDV܍܅܍܅܅DDD 9;lV܍݃Xuك<u}fE fEmmB;lxV܍܅uummB;l5V܅-mmU֋UhVMUݝ}Eʺ}ݝыMѺ}EЋEݝыUݝd}wMUH`19\vFDFDFDF܍܅܍܅\\\ 9~;d}<F܍P;d܅}V˃;d} FU֋U`D1Ĝ[^_]ËM~كHM1;|}fE كLfEt&V܍PDVDV܍H܍@܅0uum]mEfWuum]mEfDWuum]mEfDW;|,؋U֋UIMUݝ}Eʺ}ݝыMѺ}EЋEݝыUݝ\}XM;UHX19_FDFDFDF܍܅܍܅\\\9~;\}<F܍P;\܅}V˃;\} FNjU֋UXAkMd݃XM1;l}fE كfN%P$܍;X}f VQ$ʃ׋MUPM݃XM1;l}fE كMhك(M1;}fE ك$fEfV%PfDV%$$$MfDV%܅h$$܍x܅`uum]mE:uum]mED:uum]mED:;؋U֋UI1Mك$M1;t}fE ك,fEt&fV%PfDV$%܍ $$܍fDV%܅$$܍uum]mEfWuum]m‹EfDWuum]mEfDW;t ؋U֋UIv'UWVS[ç MEȉME1EEEEEEEEEDžxDž|DžpDžtDžhDžlDž`Džd}u$;M}΋TTͼD͸A;M|MȋU)ЋtżTŸT͸tͼA~M EEuLݝP\uUE<\}t[M u݃X܍PM Hك<,}}fU fUmmƒ}>}FFFD98݃Xك<<}fE fEm۝xmE//}xfE fEm]mE:B|yA }EE  }fE fEm۝pmE}pfE fEm]mEztB ;D9A}EEEDžx؋xBDž|yA }EEEDžp؋pDžtBB ;D;Usv݃Xك<2tG}fE fEmm;Us:ʃtu;Ur؋M EuU L<} Etļ1[^_]Ë}FFFD9'݃Xك<$}fE fEm۝hmA}hfE fEm]mE:BlA}fE fEm۝`mA }`fE fEm]mEzdB ;DADžh؋hBDžlAA Dž`؋`DždBB }FFD9}݃XfE ك ܍6  m]mEfWU֋UhUUݝ}EЋEݝк}EݝЋEЉʉM}ݝыMEݝыUݝd}MYUH`19݃Xك<=}fE fEmm,,}fE fEm\m}fE fEm\m  }fE fEm\ m9܍DDD ܍܅܍܅܅DDD 9;d܍݃Xuك<u}fE fEmmB;dh܍܅uummB;d%܅mmU֋U`VMUݝ}Eʺ}ݝыMѺ}EЋEݝыUݝ\}hMUHX19\vDDD ܍܅܍܅\\\ 9~;\}<܍P;\܅}˃;\} U֋UXD1e[^_]ËM~كHM1;t}fE كLfE܍HDD܍@܍8܅(uum]mEfWuum]mEfDWuum]mEfDW;t,؋U֋UIMUݝ}Eʺ}ݝыMѺ}EЋEݝыUݝT}PMCUHP19YDDD ܍܅܍܅\\\9~;T}<܍P;T܅}˃;T} NjU֋UPGyMr݃XM1;d}fE ك;yM8HbDžU݅puum]mU ݅puum]mu4Dž݅pܵ ;ۅܥh܅Xu VuE $L0M8SVE $<ʋ| x tmmB;"EtۅEu؉ًBKnEhRݕ ݕ Ƀ Pݝ8ݝ(M݅8A݅(‰E|xtUF؃}, |u 9 9!Шt:|AE<9<9!ȨuƋ9}L|E<9<~;&|OM<9<u~@CUNl}wG(W4w$O0w,_EM|u8H8} PxM}tx

ЋU%!Ѓ5,(d048p@hlt^EPdPQQVWQPRpWlVhRu!0lNjϋ}WQVȋxVQWRP QMQVxQVQRPqQMQVxVQVRPJQMQVxQVQRP#PUR|RQQVWR PEPQQVWWVԋURdRQQVWRQtPpWlVhRz t&UWVSl[Ú u} MuEuE)EEMU܉}؉u9EEO}ċE MFuE܋}؍}B@ @ʋ}AÍ&ًMȋϋE U؋ME@W؃$FU׉֋MI8AU}u08U}9Wu( u 1;u0}0OًEFO;u0E4tEMU0‰M ɋUM0EU ʋM0ˋU܍1M ʍ2E@9؃$ޗM&}( }$1;}0U0JUًEuGN;}0uE4MtuEMu0)u MEȋM+M0ʋMŋU0uʍ1M ɉ)E@?؃$,E, E 1;u0}E }MU)}0UOًUMFO;u0E4tUEЋMU0‰M ɋU0M܍21MʋEM0M ɋUE@>؃$ԕ+u, u$41;u0)U}׋EUM NjE})‹}0UOًMEȋMFO;u0E4tEU+M0M M؋E+U0UʋMUEM ɋU0)E@,؃$誔u(}0E0U‹MЋu0}0E0HHU0D@&ًMȋϋE ‰U؋ME@W؃$U׉֋MȉI8AU}u08U}9Wu( u 1;u0}0OًEFO;u0E4tEMU0‰M ыɋUM0EU ʋʋM0ˋU܍1M ʍ2E@9؃$~M&}( }$1;}0U0JUًEuGN;}0uE4MtuEMu0)u MƋEȋM+M0ʋMŋU0Ƌuʍ1M ɉ)E@?؃$肁,E, E 1;u0}E }MU)}0UOًUMFO;u0E4tUEЋMU0‰M ыɋU0M܍21MʋEM0M ɋUE@>؃$t+u, u$41;u0)U}׋EUM NjEщ})‹}0UOًMEȋMFO;u0E4tEU+M0M ыM؋E+U0UʋʋM‹UEM ɋU0)E@,؃$Ju(}0E0U‹MЋu0}0E0HHU0D@ЋU%!Ѓp,(t048@|H1ҍe[^_]Ã<0OǃOIV}WVQWQRPqZ<0OǃO7IAV}WVQWVRP PWMQWVQVRPs 뼋<0ǃIW}WVQWVRPY<09ȃI=V}WVQWQRPA^ …BWMQWVQVRPVuVQWVQRPfpsVuVQWVQRP-AURtRQRVWQRPWV|RJb0PURRQQVWR PEPQQVWWV6 VuVQWVQRPlRURtRQRVWVQRPW|VCA4V}WVQWQRPFWMQWVQVRPIVuVQWVQRP^VuVQWVQRP? UEPtPQQVWQPRWV|Rl=URtRQQVWWRQVP|W3W}WVQWVRPvV}WVQWQRPI`sV}WVQWQRPB&UWVS\[:M} EMuE})EUEUu؉E9UEu)E IlUE}܍ PE؍4W1ɉx :‰t1ɋE} 4JM܋Ul׋u1ɉh 8>%hƍ1lBE )׋xlы}1}ۅlȋ}Qt1Ɋ N)R$ȃub؋|uf׋U9F}0Hu9}4tW}拃fU拻| fUm۝m拵f pӽ}1u܈>u܋F}0uI9}܉y؁Č[^_]N@OkU(vDžu09]Nك,E4} Mu0xU0ȉ}ȋMxϋU0׋}ċM0}Eω}}}|Og؋Džf}ӽE0ŰuȋMljỦuȉMĉ}*}0u11Ɋ7E0)tt1ҊlϋMĊ1ɍ4W}̉`ů 0)1`֍ Bt)`Eȋ}01ۅ`)Ql1ɊNJ E0U 4O}u1ɉ %ƍ1BE )׋Ћ1}0ۅȋQ}1Ɋ u`Š >1\ʊ} BE0TX)1ɉP P%ƍ1XBT)׋XЋ`1}0ۅXȋ\Q}1Ɋ N)R$ȃu}拳fM拓| fMm]mEf ~<9D8GNU09؋M, M$DžE09u} MϋU׋uԋU M<Ћ})ȋuM,E؋}0‰$Uы$U0)J ك, E4,<ʋ} 1}81784NJW},0ʋE01)Ɗ 14V0)֋$08񋵰 ۅ0ȁ4Q1%ƍ1 BE )׋< Ћ1}0ۅ ȋQ}1Ɋ %ƍ1BE )׋Ћ1}ۅȋQ}1Ɋ }%ƍ1B)׋ы}ԋU }‰Љ1Ҋ}Q1Ɋ ۅ0Ћuĉ P4(f wfGf7%$}ȍp()֋8(f4x0$ۅ(4yȋ4fy4P)ۅE@w؋8كTأ@P}كTfU fUCLFU0L9gU( U DžE09HZ}ufM fMm]mUfNGNE09aE4tȋMu0} fGUȍ fG fO PMщʉf WUЁfG֋fW%F)֋ u0ۅf4Gȁ q֋UfGPQU0pf VflЋu0 Pdf rpfr%`f%GE0d)׋tщdۅdfE4tUM0M MM UʋM0M ɋMM ɋU0č2E؋M ɍ0E@݃XEv؃$2؃$؃$؃$؃$*}( }$1;}0u0Nu,}UfM fMmmuGN;}0uE4MtMUʋEM 4u0U)MEM Í4ċu0)uɋUÍ2U0ˉ)E@݃XE }, } 1;u0 }M })}0UO+}UfM fMMmmFO;u0E4tMUʋM0M MM M0U؍1Mɍ2UƋM0ÍM ʋEE@݃XEE, E$ 1;u0 }M })}0UO0؋MU}fM fMMmmFO;u0[ E4tUEЋMȋMM +U0UMMÉ+E0MUM‹M ʋE0)E@݃XE u(%Dž(U09( MЋ}̋E0plht&M0()ыE4I~U l֋M hlE‰$M pΉ }<M0EȋU0HMUG}MfU fU$mmUЉ$։ U0M$E@݃XE6} fM$ fMM݃XmmϋU ։$ ((hGlp(}0hlp9( u( u 1;u0}0O&}MfU fUmmFO;u0E4tUЋM0M MM UʋM0M ɋMM ɋU0č2E؋M ɍ0E@}݃XE&؃$&}( }$/1;}0$u0Nt2}UfM fMmmtGN;}0tE4ttڋMUʋEM 4u0U)MEM Í4ċu0)uɋUÍ2U0ˉ)E@L݃XE؃$P؃${؃$f؃$Q.} Džu09uM0)E4I@M UЍF V}N~ F1}E9}<}EfE fEt&}E Fmm;u|؃^_]UWVu>F V}N~ F1}E9}?}EfE fEt&}E F@mm;u|؃^_]Í'UWVu>F V}N~ F1}E9}B}EfE fEt&}E F@@mm;u|؃^_]Ív'UWVu>F V}N~ F1}E9}E}EfE fEt&}E F@@@mm;u|؃^_]Í&UWVu>F V}N~ F1}E9}P}EfE fEt&}E Fmm@BmYmR;u|؃^_]ÍUWVu>F V}N~ F1}E9}V}EfE fEt&}E F@mm@B@mYmR;u|؃^_]ÍUWVu>F V}N~ F1}E9}\}EfE fEt&}E F@@ mm@B@@(mYmR;u|؃^_]UWV u>F V}EN~ F1;u}dW$}fE fEv}E F@@ @0mm@B@@(@8mYmR;u|؃ ^_]Ív'UWVu>F V}N~ F1}E9}d}EfE fEt&}E Fmm@BmYmR@BmYm R;u|؃^_]Ð&UWV u>F V}EN~ F1;u}oW$}fE fEv}E F@mm@B@ mYmR@B@(mYm R;u|؃ ^_] UWV u>F V}EN~ F1;u}xW$}fE fEv}E F@@0mm@B@ @8mYmR@B@(@@mYm R;u|؃ ^_]ÍUWV u>F V}EN~ F1;u~W$}fE fE}E F@@0@Hmm@B@ @8@PmYmR@B@(@@@XmYm R;u|؃ ^_]ÍUWV u>F V}EN~ F1;u}zW$}fE fEv}E Fmm@BmYmR@BmYmR@BmY mR ;u|؃ ^_]Ít&UWV u>F V}EN~ F1;uW$}fE fE}E F@ mm@B@(mYmR@B@0mYmR@B@8mY mR ;u|؃ ^_]Í&UWV u>F V}EN~ F1;uW$}fE fE}E F@ @@mm@B@(@HmYmR@B@0@PmYmR@B@8@XmY mR ;u|؃ ^_]Ít&'UWV u>F V}EN~ F1;uW$}fE fE}E F@ @@@`mm@B@(@H@hmYmR@B@0@P@pmYmR@B@8@X@xmY mR ;uw؃ ^_]ÐUVS[Eك$MU u EEu7كĞu&Eutظe[^]ك̞E@uLـE@tYكE@u*ـE@t Vj\$$RQFVjRQ\vVjRQict&UWVS [ Eك$uM U E}$Eu;كĞu*Eutvظe[^_]ك̞E@uUـE@tMكE@u3ـE@t9]]}$U M ue[^_]0WRQVkoWRQV9]t&UWVS[ò كĞEuM tEٝٝمV FQFQA Q$A~҉ƒ>~ Љyu9ƒ Шtظe[^_]E ٽf f؃̞٭۝٭ۅˋٝۅمɉl)ٝpمٝمٝمEbɋU ٭۝٭٭۝٭)Љ9~9~م٭۝٭م٭ە٭ۅEu BۅEu B9~p9~=Nۅ؍؃̞٭۝٭9A؋~ Džp=14 1҉;}Mٽك̞f fE 4V$ʋ٭٭B;|؋~8Lp 9~y|1;}Kٽك̞f fR$؍٭٭B;|؋~(T;~JL1;|'`t&A;};Љ…yȍPA;|ŋ1;}=1҉D9}4 @2B;D|G;|Dž9h1;}0@;|틽dDž9}Jxl)@h~ DžhhJ1;x}yh)|t&@ 1:T:) Њ|!F;x|tl @1Ҋ8);h}@D8% ‹h)!ʋ:9ldlGu$?A;tJ‹D)tlljl9t9t P<1e[^_]RRU$R} WRW\$$QV2 Ƀ U$R} WP\$$QVg PٝH;مHBU$RjjWjPjV t&UWVSP[ÜPMUʋu UU9w/t&1ҋXFЃyljy;MvՋE;u 1t ׋u9wbX1Ɖ1;Ms:uX)΋l<!19}&@9|t&XZ[^_]É볉U1҉WVS [èMuMy&&} 1:<lj׉N>Uu9dU}9}  1ɨt E 1Ɋ@E }u9}  1tu Fu ׋EuE9E  Шt u 1E܉ȋdfF։ ыdfJM 9MwHU }܋u܃?} dUf FE܁fF ΋E9Es0U)ыl!1҉u>9}}:B9|,[^_]Ð&UWVSP[ìPuU} UU9w8t&P1Ɋ1ҊWf HfP%щ;uv̋U9w1PGf BfEt1P GY^[^_]ÐUWVS|[كĞEMU Eٝ BqB q BqBqB$ryƒ9 Шu9O Ȩtظe[^_]E$ٽ؍f f؃̞٭۝٭ۅɋٝۅمɋ)ٝمٝمٝمEwɋ}$‹٭۝٭٭۝٭)9~9~OЃم٭۝٭ۅمEu B9~} U D=PDž9Ɖt4:9!ЍVt)A9!ШuV9wt&9v9sA9r jQRPQ RjQWNu;tPVPW4}(vE$tV$ٽ؍f f؃̞٭۝٭<;)ʉ)tƉBΉ9 9t W11e[^_] Pi1Bvt&RQPWNu_}(WjjVjRjQ? \&UWVS["كĞEMU xDžEٝٝAq AqBryB BBB$ƒ9 Ш9O ȨE(ٽ؍f f؃̞٭۝٭ۅʋٝۅمɉt)ٝمٝمٝمEɋE(٭۝٭٭۝٭)9~9~M ~ DžH=K~# QH.1҉;}Qٽك̞f fM(P$؍٭٭B;|؋~0LE$>9~QT1;}Kٽك̞f fR$؍٭٭B;|؋~+E T4;~ rt1;|#\A;};Љ…yȍPA;|ŋ1;}=1҉d9}4 @2B;d|G;|Dž91;}@;|U$}$lDžM 9H|&tU )`p~ DžppJ1;|}p) `1:T:) Њ!Ȋ ʉF;||ts `1Ҋ8);p}`D8% ‹p)!1:<ljUEDDtEt\믋MUыuMM<؃L^_]Í'UWV@U u$E M]E ]BE]F]BE]F]B]Fu]ԅU MMEЋ}uUȉMEM9Mt&EEZEZEEEZ EEEZEEZEEZuEZE  EE9EA ]EAAAAAAuEEEZZEE EEZ  EZEZEZU9U|qvEUEEM\EE܋U\EuEԋE\ MĉE9M}kMEDDD sUEvME\yUE\yMȋUMi@^_]ÉUWV@} M Uu}$]~}u}M}}U܋}}1;}Uu؉U&ދUM̋EHEMYEEʋUZEɋMYEʋUZ  EɋMY( EUZ0EEM@Y8@;}MF]F8FFF F(F0]]ȋUȋM̋EHMEYEʋUZɋMYUZ ɋMY(UEZ0EM@Y8@;}M;}}&uuEɋUG;}|ڋu؋MUԋ}MЉu؉M؃@^_]Í'UWVXE U$u M@BɋE}E]]UUЉEԋU}UuĉM1;}MЋuȉM&܋UM̋EH(EUZE%UMEEɉPHEʋMYEˋUZ EɋMY(OEUZ0EEM@Y8@;}ԉMFF]F8]FF F(F0]]UM̋EHUEZUME؋M@Y8@;}ԉMb;}|?EʋUuEʋM\;}uDtʋu봋UEZ0vMyULEʋMUȋ}ЋMċuMUȉ}i؃X^_]Í&'UWVxU M$Eu A]BAɋM}@M]B]]]ЉE UEUE}Eu1;}uMLt&݋EUEVEE^EЋUEEɉFVE^EE^ E^(EEE^0EE^8EE^@EEEE^HEE^P~EЃ `^X`;}~AA]A@]AH]]APA]AXA ]EA(A0A8h]EUEVeE^jEUEEɉFVjEE^j^ mE^(rE^0yE^8EE^@EEEE^HEE^PE `^X`;};}|JEuEu]EʋU\uEЋE\;}}DMEDDtʋMEtʋu\릋}UMuMˋUEEvEEbE܋M\EE_EEEEЋU\EE@EE,EċM\ ;u't&UM׋EM4uT^_]EEEEEcEYEE0E&EEUWVP} M Uu}$ɋ}(]]}u}܉M}}؉Uԋ}}Ѝ1;}uU؋uЉU&EEUMEE͉HEPEEHE͋MEYˋUZEɋMYEʋUZ EKɋMY(E ɋUZ0EEu^EU@Z8@;}܉UtF]F8]]EFFF F(F0E&EEEtEU@Z8@;}܉U~t&;}}9uEtEEt EɍvɋuG;}|NjUЋ}؋űMMȉUЉ}#؃P^_]EEEMY0EEEUZ(EEfEMY ^EE-EUZ%t&EEEMYt&EUMw'UWVpE M(U$} ]]@]BAʋEuME]]]6UUMUEĉM}uUv1;}euuu&EEUMEEEɉHEYEENEMYEE̋UZEEʋMYEEUZ EElMY(EE8UZ0EEuPEX8@@E;}iF]EUFFF F(F0F8EvEEtEȋEX8@@E;}~t&;}|yU\;}}juEDEtEEtEɋMEEtEEtEȋu\;}|vU}MuMU}p^_]EEEEEEvEELEBEEEEEEEUMot&UWVE U$M(u]]@B]A]]@BʋEU ]A}4v]]]U EuYVEMUEM|1;}2uMH&EE3݅hE݅`Em݅`^EEE^EtEE^EE^ EEf^(EE3^0EEp^8EEEEEE^@EEEE^HEEEE^PEE؃ `^X`;}Aݝhݝ`A@AH]]APAX]]E݅hAAA A(A0A8Efs݅`EE{E^EE~EEEY_EEE$E `^X`;}$v;}|5U\EEu\;}uEDDEtEEt EʍvʋMEEtEExEU\EEsEE_Eu\;}Z}M E|4ϋMu<}Ę^_]ÉEE|E^8EEEwEEEbEEE^@EbEEEMEEE^HEMEEE8E3EEEEEE|EESEIEEUWVE U$M(u ]]@B]A]]@B]A]]@B]U}A]]]UEEUEUE}Eut&1;}Mul$t&EE݅pl݅`EE&݅`lYEE;ݝxx|lEPHEtEEʋlZEElY EElZ(EEjlY0EElX8@@l;}Fݝ`EݕpFFF F(F0F8Et&݅`EEEȋlYEEE݋UMEEE6ElX8@@l;}1;}|N*ɋM\EEu\EEU\;}uEDDDEtEEt EˍˋUEEhEETEȋu\EEQEE=EU\EE8EE$EM\;}}ENjMuM}<}Ę^_]ÉEEExEEIE?EEEEbEUS[\hMEQURM QPPgw]ÉUSQ[.hMEQU RPPx]Ð&USP[gUERM QPP}]Ð&US [gUERMQURM QPP#]Í&'US[ÌgMEQURM QPPG]ÐUS[\gMEQURM QPP']ÉUSQ[.gMEQU RPP}D]Ð&USP[fUERM QPP}|]Ð&US [fUERMQURM QPP]Í&'US[ÌfMEQURM QPPW]ÐUWVEEM }P;Qt E^_]Ëp ;q u0u;1u܋pu;qu;Wt EË;w uuE;7uu;wupuȋquЋwűuċqI$uMwO$uMpH$uEM‹U9M9y9n9 Eԋu}EĉuNugMԋUă`dEHyE}UE}ȋE)‰ЃyBuU) ƋuU)uM)ЃyBMU)MU)ȋUE tGU;}u+M!ˆUMEA2@EMEM!" ЈG;d UMB2AUGM;d~;u}1;}uU)ѾEu2"! ЈMЋEỦMЋuMȋ}`ỦMEUE)‹uMEމU9Mu M1ҊUE@\ME)~ M1ҊQUEM\X;}u+M!‰XUM1UeMMʋM1AXMX"! ˆGUU;dI\M1҃eЊM؉UUM2BU\EAGE\;d~;uU);U~ \1ɊM;}uU)EM1ҊEM}M1‰!"2ME)‹uEDžxމU9Eu M1Ҋ|E@TME)~M1ҊQxEMTP;}u+M!‰PMxӥ|M1|ȋMA1ЉM䊕PP!" ˆGx|;dXTM1҃ӥ|ЊM܉xU|2BU䋍TxAG|T;d~;uU);U~T1Ɋx;}uU)M|M1ӽxxȋM1"! qu9uE)ƋMى(UE))DžpDžh4$ M1ҊtU싵4) u1ɊlMuAF,04}~u1FpEԍ ~}1ɊOhuM0UƃM艕,9Mu})!ƊMpӥt(htȊMӥl$l׋Mȉ1!" G4ph4tld940,1ҊM1ppӥt(htʊMӥl$l1ˆGhpl04,FAt4d0,94PM94ȋ4);(~01Ҋp;$~,1Ɋh;}uU)Mt⊍(lӽppʊM$ӽhhmm\>mm\>m EM.EEЋuMmUmꀋp <@))1)yuUƋ$ME։ ΉM9:}EEUƋuΊ7֋U1Ҋ1Ҋ91Ҋ ) )$ ΋)΋) ,MU։,,,,z!% !;M;uUuʋE}u΋UЊ7֋U1Ҋt1Ҋ9x1Ҋtp x) )$t p΋x))(MU(UuBU9uĐ^_]Í'UWVS[çuNM؋xPH$}ԋxUЉM̉}ȋPH8x UĉM}PH,x1ʊ211T17؍hʋP1>1ˊ211T17؍dʋP1>‹1Ί21Ҋ11 ʋP ͋T؍`ʊ 21Ҋ0 ݅(؍\݅݅tpuMHtp}Ủ$ك0݃Xٝك<ɋtM̉DݝٝDž$}($OD@U)Aɉ@ٝٝAA ɋ@ @M@@!!$@@ lj@ }!!F H ǃ!u! NjЃ ƃ!֋U!Ћ@ ƋEٝٝ @Eȉ08$1781ʊ:1941>؍ʋ801781ˊ:1941>؍ʋ8017‹81Ί:1Ҋ91 ʋ4 ˊ 801 ̊8؍݅( ؍ ݅ ݅؋d(Mu(d&$Mu$}Ủك0݃Xٝك<ɋtủ ݝٝDž}O U)Aɉ@ٝٝAA ɋ M!! lj }!!F H ǃ!u! NjЃ ƃ!֋U!Ћ ƋEٝٝEȉMMMԅ|م | Ƌݝم݅ɋu䉍MݝʋEԉݝhd݅9}hfU fUm]mMuU׉h NE)ȋ@ ƃ!GGG }! ։E!!΋ AJ ׋!}! ы !}!Ɖu Љ>}E} > 121̊7 191>1ʊ2ʋ1Ɋ7ɋ؍191>‹1ˊ2ˋ1ʊ7ʋ؍1ʊ91>1Ί21 ɋƋ݅Ί0؍ 1Ŋ1 1Ҋ0 ؍݅݅؋hEU׉hMu}Uȉك0݃Xٝك<ɋtuȉݝٝDž}OU|)Aɉ@ٝxٝtAA ɋ M!!| lj }!!F H ǃ!u! Nj|`Ѓ` ƃ!֋U!Ћ ƋEٝpٝlEȉMMM܅ |م |Ƌݝم݅ɋu䉍 MݝʋE܉ݝld݅;}lfU fUm]mMuU׉lNEX)ȋXT@ ƃ!򋵈GGG X}T!X ։XE!! ։ʉPBTND T!}! D !}!ƉDXDu ȉD >P}E}>D111̊71:1> 1ʊ1ʋ1Ɋ7ɋ ؍x1:1>‹1ˊ1ˋ1ʊ7ʋ؍t1ʊ:1>1Ί1 1Ɗ1݅ 121؍p ɋÊ0 ؍l݅݅؋lE}lMuU}BU9}T[^_]UWVSL[÷`Eu耋Np$xuԋPp M}܉uċHxuU؉MЉ}̋P8Hx,UȉM}P<xUM}p0P uUuUNFI~ HMNE}ȋzMU؋v ‹Wu)‰ȋUWЋW EUEȉ)ʋwUƋMUȉEB(|EE9Ej}UЉtك0݃Xٝك<ɋtMЉݝٝ|DžxPt}xtOdUl)Aɉ@ٝhٝdAA ɋd dMdd!!tddl ljd }!!F H ǃ!u! NjlPЃP ƃ!֋U!Ћd ƋEٝ`ٝ\dEȉ`MMM܅}|م fU|,`ݝم|u݅ɋxEݝ }ܶ ݝ0ThfU_݅#m]mhUxuMωxThxMxppN)HȉD@ ƋH!xBBB HU!ЋH NjUȉHpI ƃ!!U G@E !уp! ωD !H!u ʋMȉ4E@uNj}u\4u}1X,21̊1\17X,1>1ʊ211\17؍hʋX1> 1ˊ211\17؍dʋX 1>‹1Ί21Ҋ11 ʋX ͋\؍`ʊ 21Ҋ0 ݅؍\݅0݅txuMPtx}Ủ$ك0݃Xٝك<ɋtM̉LݝٝDž$}($OLHU)Aɉ@ٝٝAA ɋH HMHH!!$HH ljH }!!F H ǃ!u! NjЃ ƃ!֋U!ЋH ƋEٝٝ HEȉDMMM؅@|م |(Dݝ0م݅ɉuMʋEݝ ݝ(ld݅ 1}lfU fUm]mM(uU׉(l((L  IE)(@ !ʋM!ȋ FFF ׋ ƃE!!ω FJE ׃!}! ֋ !ƒ!MȋE ׉}E8@(17@1ʊ:19<1>؍ʋ@817@1ˊ:19<1>؍ʋ@817‹@1Ί:1Ҋ91 ʋ< ˊ 881 ̊8؍݅0 ؍ ݅(݅ ؋l(}EƉ(l&$Mu$}Ủك0݃Xٝك<ɋtủݝٝDž}OU)Aɉ@ٝٝAA ɋ M!! lj }!!F H ǃ!u! NjЃ ƃ!֋U!Ћ ƋEٝٝEȉ MMMԅ|م |$Ƌ ݝم݅ɋu䉍MݝʋEԉݝpd݅9}pfU fUm]mMuU׉pNE)ȋ@ ƃ!GGG }! ։E!!΋ AJ ׋!}! ы !}!Ɖu Љ>}E} >$121̊7$191>1ʊ2ʋ1Ɋ7ɋ؍191>‹1ˊ2ˋ1ʊ7ʋ؍1ʊ91>1Ί21 ɋƋ݅Ί0؍ 1Ŋ1 1Ҋ0 ؍݅݅؋puU׉pMu}Uȉك0݃Xٝك<ɋtuȉݝٝDž}OU|)Aɉ@ٝxٝtAA ɋ M!!| lj }!!F H ǃ!u! Nj|`Ѓ` ƃ!֋U!Ћ ƋEٝpٝlEȉMMM܅ |م | Ƌݝم݅ɋu䉍MݝʋE܉ݝtd݅;}tfU fUm]mMuU׉tNEX)ȋXT@ ƃ!򋵈GGG X}T!X ։XE!! ։ʉPBTND T!}! D !}!ƉDXDu ȉD >P}E}>D 111̊7 1:1>1ʊ1ʋ1Ɋ7ɋ؍x1:1>‹1ˊ1ˋ1ʊ7ʋ؍t1ʊ:1>1Ί1 1Ɗ1݅ 121؍p ɋÊ0 ؍l݅݅؋tMU׉tMuU}BU9}L[^_]UWVS[7H}OM܋pPH$u؋pUԉMЉűPH8p UȉMĉuPH,pdم\ 1211مd 1$؍X7d م` 1>dم\ 1211مd 1$؍T7d م` 1‹Š>dم\ 121Ҋ11مd ˋ$ ʊ 0 ؍Pم` 1Ê 0م\ ݅p؍L݅݅h}fU fU싕0m]mM t}E‰t0pP}EM䉕pPVuMȉ$ك0݃Xٝxك<ɋtUȉݝpٝl|Džh$}($OU)AɍBٝ ٝAA ɋ M!!$ lj }!!F B ǃ!u! lj Ѓ!u!u Ћٝٝu΋uMu7x|مx<ϋ ʋuݝمl݅pɉ8Mh0ʋEݝ4ݝ4d݅j}4fU fUm]mM(uElj(4T(( I ΋UM)@G GʃٝG ʋ!u!( ljȉ !Ѓ!J ƃF Ѓ!ƋE!‹ ։ ʃ!׋U! ϋMȉE܋}ƍu}}ٝٝuE <12م1Ɋ1 17 م 1<> 8م 1211م 1؍ 7 م 18> 4م 1211م 1؍7 م 1‹4Š> 0م 121Ҋ11م ˋ ʊ 0؍م 1Ê 0م ݅؍݅݅؋4(uElj(4$hMu$hd}Uȉك0݃Xٝك<ɋtuȉݝٝDž}OU)AɍBٝٝAA ɋ M!! lj }!!F B ǃ!u! lj Ѓ!u!u Ћٝٝu΋uMuDxمϋʋuݝم݅ɉMʋEݝ򉽨ݝ8it&݅x}8fU fUm]mMuElj8\AAٝA ʋMJ)։ȉ@ ƃ!u! ljȉM Ƌ!! AʋNE !ыU!֋  !ƒ!u ׋E܉}ٝɍ7ٝuم 7u1:1Ɋ> 11م 17م 1:ʋ1Ɋ>ʋم 11؍م 1Š7م 1:ˋ1ʊ>ˋم 11؍م 1ʋ7م 1: 1Ê:م 1Ҋ91؍Ê 8م م ݅؍݅݅؋8MU׉8MuW|Uĉ ك0݃Xٝك<ɋtuĉݝٝDžp}OU)AɍBٝtٝpAA ɋ M!! lj }!!F B ǃ!u! lj Ѓ!u!u Ћٝlٝhu΋uMuQxم`ϋʋuݝم݅ɉ\MTʋEݝxXݝt\مx 1211م 1؍t7م| 1\>Xمx 1211م 1؍p7م| 1‹XŠ>Tمx 121Ҋ11م ˋ ʊ 0؍lم| 1Ê 0مx ݅؍h݅݅x}fU fU싕\%` 1ҋu `ʊ%؍0 X1ˊ7X} `؍,T% 1ҋu ˋ`%؍( ȋ} 1Ҋ1`%\ 1ҋu `Ɋ%؍0 ʉX1؍ ʊ7`X} ؍,%T 1ҋu ̋`%؍( ȋ} 1Ҋ1`%\ 1ҋu `Ɋ%؍0 ʉX1؍ʊ7} X`؍,%T 1ҋu ̋`%؍( 1Ҋ9M ̊%\ 1ҋXɊ819؍ʋ`؍0 ʋ1Ҋˋu ؍, 1ɋTˊ :1؍(>` ؍$݅H؍݅@݅8}}fU fU싕@m]mM :@EMʉ@@4؋% L 1ҋu  ʊL%؍ 1ˊ7 } ؍% L1ҋu ˋ H%؍ ȋ} 1Ҋ1 %H 1ҋu  Ɋ%H؍ ʉ1؍ʊ7}  ؍% H1ҋu ̋ D%؍ ȋ} 1Ҋ1 %D 1ҋu  Ɋ%D؍ ʉ1؍ʊ7 } ؍% D1ҋu ̋@ %؍ 1Ҋ9M ̊% 1ҋɊ819؍ʋ ؍ ʋ@1Ҋˋu ؍ 1ɋ@ˊ :1؍>  ؍݅؍݅݅؋DMUEljD؋UE}Mȉك0݃Xٝ ݝك<ك ɋtuȉٝٝDž&}OU|)AɍBٝhٝdAA ɋ M!!| lj }!!F B ǃ!u!| lj Ѓ!u!u Ћٝ`ٝ\u΋uMuxم ݝمʋu݅ɉM|ݝʉxݝمHh݅B}ufM fM싍Hm]mU1}u񉕈H&NET)ЋTP@P GɃ!񋵈TٝxٝtGG ɋ}T! ։TE!!΋PA OEL P!!у Hы}E !}!ʋT LƉ@UЍ :ٝpٕlu܋H}׉@։1ҋu 9ϊ>% 1ҋu ʊ%؍x 1ˊ7} ؍t% 1ҋu ˋ%؍p ȋ} 1Ҋ1% 1ҋu Ɋ%؍x ʉ1؍hʊ7} ؍t% 1ҋu ̋|%؍p ȋ} 1Ҋ1%| 1ҋu Ɋ%|؍x ʉ1؍dʊ7} ؍t% |1ҋu ̋x%؍p 1Ҋ9M ̊% 1ҋɊ819؍`ʋ؍x ʋx1Ҋˋu ؍t 1ɋxˊ :1؍p> ؍l݅؍\݅݅؋HM u}H؋UE|Mĉ(ك0݃Xٝ(ݝ ك<ك ɋtuĉٝٝ,Džpt&(},(OU )AɍBٝ ٝAA ɋ M!!( lj }!!F B ǃ!u!  lj Ѓ!u!u Ћٝٝu΋uMuYx,م(݅ ݝمɋʋuݝمɉMݝxʉٝtLh݅x}ufM fM싍Lm]mU1,}u,Ltg,,$$NE)Ћ@ GɃ!,ٝٝGG ɋ}! ։E$!!΋A OE !!у$ ы}E !}!ʋ ƉUЍ :ٕٝu܋}׉։1ҋu 9ϊ>% 1ҋu ؍%؍ 1؍7} ؍% 1ҋu ؍%؍ ȋ} 1Ҋ1%؍ 1ҋu ؍Ɋ%؍ ʉ1؍ ʊ7} ؍؍% 1ҋu ؍%؍ ȋ} 1Ҋ1%؍ 1ҋu ؍Ɋ%؍ ʉ1؍ʊ7؍} ؍% 1ҋu ؍%؍ 1Ҋ9M ؍% 1ҋɊ8؍19؍ʋ؍ ʋ1Ҋ؍u ؍ 1ɋ؍ :1؍> ؍؍݅؍݅~݅x~؋LM,uU׉,Lt(UEp(B'UWVS[÷}OM܋pPH$u؋pUԉMЉűPH8p UȉMĉuPH,pdم\ 1211مd 1؍X7dم` 1>dم\ 1211مd 1؍T7dم` 1‹Š>dم\ 121Ҋ11مd ˋ ʊ 0؍Pم` 1Ê 0م\ ݅h؍L݅݅H}fU fU싕(m]mM t}E‰t(pH}EM䉕pHVuMȉ$ك0݃Xٝpك<ɋtUȉݝhٝdtDž`$}($O U)AɍBٝ ٝAA ɋ M !!$  lj }!!F B ǃ!u! lj Ѓ!u!u Ћ ٝٝ u΋uMu7xtمp4ϋʋuݝمd݅hɉ0M`(ʋEݝ,ݝ,d݅j},fU fUm]mM(uElj(,T(( I ΋UM)@G GʃٝG ʋ!u!( ljȉ !Ѓ!J ƃF Ѓ!ƋE!‹ ։ ʃ!׋U! ϋMȉE܋}ƍu}}ٝٝuE412م1Ɋ1 17م 14>0م 1211م 1؍ 7م 10>,م 1211م 1؍7م 1‹,Š>(م 121Ҋ11م ˋ ʊ 0؍م 1Ê 0م ݅؍݅݅؋,(uElj(,$`Mu$`d}Uȉك0݃Xٝك<ɋtuȉݝٝ|Džx}OU)AɍBٝٝAA ɋ M!! lj }!!F B ǃ!u! lj Ѓ!u!u Ћٝٝu΋uMuDxمϋʋuݝم|݅ɉMxʋEݝ򉽨ݝ0it&݅x}0fU fUm]mMuElj0\AAٝA ʋMJ)։ȉ@ ƃ!u! ljȉM Ƌ!! AʋNE !ыU!֋  !ƒ!u ׋E܉}ٝɍ7ٝuم 7u1:1Ɋ> 11م 17م 1:ʋ1Ɋ>ʋم 11؍م 1Š7م 1:ˋ1ʊ>ˋم 11؍م 1ʋ7م 1: 1Ê:م 1Ҋ91؍Ê 8م م ݅؍݅݅؋0MU׉0xMuxW|Uĉ ك0݃Xٝك<ɋtuĉݝٝDžh}OU)AɍBٝtٝpAA ɋ M!! lj }!!F B ǃ!u! lj Ѓ!u!u Ћٝlٝhu΋uMuQxم`ϋʋuݝم݅ɉ\MTʋEݝpXݝxl4J݅ph؋4MElj4lI`΋UM)@G GʃٝG ʋ`!u`!` ljȉ` !Ѓ!JG Ɖ Ѓ!NjE!‹` ׋TLL ʃ!׉L}!L ʋ}M܉LȋEƍT}EƋ}ٝ|ٝx}LEƋ`12م1Ɋ1 17م| 1`>\مx 1211م 1؍t7م| 1\>Xمx 1211م 1؍p7م| 1‹XŠ>Tمx 121Ҋ11م ˋ ʊ 0؍lم| 1Ê 0مx ݅؍h݅x݅p}fU fU싕4m]mM Eu򉍐4l|t&MEhJU} BOUu܉} 9uČ[^_]Ëu}̉<ك0݃Xٝݝك<ك ɋtM̉dٝٝDž(<}@T%X 1ҋu Xʊ%؍0 P1ˊ7P} X؍,L% 1ҋu ˋX%؍( ȋ} 1Ҋ1X%T 1ҋu XɊ%؍0 ʉP1؍ ʊ7XP} ؍,%L 1ҋu ̋X%؍( ȋ} 1Ҋ1X%T 1ҋu XɊ%؍0 ʉP1؍ʊ7} PX؍,%L 1ҋu ̋X%؍( 1Ҋ9M ̊%T 1ҋPɊ819؍ʋX؍0 ʋ1Ҋˋu ؍, 1ɋLˊ :1؍(>X ؍$݅@؍݅8݅0}}fU fU싕8m]mM :@EMʉ@8,؋%D 1ҋu ʊD%؍ 1ˊ7} ؍ % D1ҋu ˋ@%؍ ȋ} 1Ҋ1%@ 1ҋu Ɋ%@؍ ʉ1؍ʊ7} ؍%  @1ҋu ̋<%؍ ȋ} 1Ҋ1%< 1ҋu Ɋ%<؍ ʉ1؍ʊ7} ؍ % <1ҋu ̋8%؍ 1Ҋ9M ̊% 1ҋɊ819؍ʋ؍ ʋ81Ҋˋu ؍ 1ɋ 8ˊ :1؍> ؍݅؍݅݅؋% 1ҋu ʊ%؍x 1ˊ7} ؍t% 1ҋu ˋ%؍p ȋ} 1Ҋ1% 1ҋu Ɋ%؍x ʉ1؍hʊ7} ؍t% 1ҋu ̋|%؍p ȋ} 1Ҋ1%| 1ҋu Ɋ%|؍x ʉ1؍dʊ7} ؍t% |1ҋu ̋x%؍p 1Ҋ9M ̊% 1ҋɊ819؍`ʋ؍x ʋx1Ҋˋu ؍t 1ɋxˊ :1؍p> ؍l݅؍\݅݅؋@M u}@؋UE|Mĉ(ك0݃Xٝ ݝك<ك ɋtuĉٝٝ$Dž ht&(},(OU )AɍBٝ ٝAA ɋ M!!( lj }!!F B ǃ!u!  lj Ѓ!u!u Ћٝٝu΋uMuYx$م ݅ݝمɋʋuݝxمɉM ݝpʉٝlDh݅p}ufM fM싍Dm]mU1,}u,Dlg,,$$NE)Ћ@ GɃ!,ٝٝGG ɋ}! ։E$!!΋A OE !!у$ ы}E !}!ʋ ƉUЍ :ٕٝu܋}׉։1ҋu 9ϊ>% 1ҋu ؍%؍ 1؍7} ؍% 1ҋu ؍%؍ ȋ} 1Ҋ1%؍ 1ҋu ؍Ɋ%؍ ʉ1؍ ʊ7} ؍؍% 1ҋu ؍%؍ ȋ} 1Ҋ1%؍ 1ҋu ؍Ɋ%؍ ʉ1؍ʊ7؍} ؍% 1ҋu ؍%؍ 1Ҋ9M ؍% 1ҋɊ8؍19؍ʋ؍ ʋ1Ҋ؍u ؍ 1ɋ؍ :1؍> ؍؍݅؍݅x~݅p~؋DM,uU׉,Dl( UEh( B'UWVS[7M}AEOG$wEԋWG M؉EċOEu܉UЉM̋w8WO,uȉUMw<OuUMG W0}EUwONG IuwMEM؉}W uUUzO)7MƋOuȋO EME)EMuOJ(E|EE9EU}Љt`݃Xuݝك<Džٝtt}xOlUٝdA)ٝ`ЉTA@ٝ\A M!!t} Ɖ l!! ЉHA ljٝXT!ʃM!ȋ ‹ME !!E u΋u։M܉u݅}Eݝ مMݝfU fU\݅m]muMUf~uxʉxxxppNEL)ЋLH@H ƃ!uL!xAAA L ׉LEp!!ϋE ׋HDJG փ!u! ׋H !ƒE!8M8 ֋Lʉ8uٝhDE񉅸8}u|~~ˋFʋF؍h~~ɋ؍dFˋF؍h~~ɋ؍`FˋF؍h~N؍\V~؍h݅ ؍X݅tuEt}Ủ ݃XMݝك<Džٝht& }$OUٝA)ٝ ЉA@ٝA M!! } Ɖ !! ЉHA ljٝ!ʃM!ȋ ‹ME !!򋅌 uuMщE؉݅UݝxمMݝpll݅p5}fMu EfM싍$m]m$Uf~U׉l$$NE)ȋ$@ ƃ!GGG }! ƋE!‰! FO !ΉM!  !ʃM!Nj ׋ʉ}ٝ}E}Elj}u|~~ˋFʋF؍~~ɋ؍FˋF؍~~ɋ؍ FˋF؍~N؍V~؍݅x؍݅p؋E$UMfxω$l MEh }Ủ݃Xuݝكٝ\`} >}u|X`~\`Fˋ`~ʋXTF؍T~\TFϋT؍~ˋXPF؍P~\PFϋP؍~ˋXLF؍\~LNjX~ɋL؍~ʋL~؍݅@؍݅8؋MU}fA4ME0}Uȉx݃Xuݝك<,Džٝt&xx},|Op(UٝhA)ٝdЉXA@ٝ`A ( (M((!!x(}( Ɖ( p!! ЉHA ljٝ\X!ʃM!ȋ ‹ME !!E u((΋u֋E܉݅UݝمMݝl݅)}fMu fMMm]mUf~u|ʉ|||,ttNEP)ЋPL@L ƃ!uP!|AAA P ׉PEt!!ϋE ׉ʉHBLO< !u!< L !P!Hٝl$(}><}u| (~$(Fˋ(~ʋ F؍l~$F׋؍h~ˋ F؍l~$F׋؍d~ˋ F؍l$~Nj~ɋ ؍`N~؍l݅؍\݅؋M|uEfylj|xM}xEU@E9Ue[^_]Í'UWVS[wM}AEOG$wEԋWG M؉EċOEu܉UЉM̋w8WO,uȉUMw<OuUMG W0}EUwONG IuwMEM؉}W uUUzO)7MƋOuȋO EME)EMuOJ(E|EE9EU}Љt`݃X@uݝك<Džٝtt}xOlUٝdA)ٝ`ЉTA@ٝ\A M!!t} Ɖ l!! ЉHA ljٝXT!ʃM!ȋ ‹ME !!E u΋u։M܉u݅}Eݝ مMݝfU fU\݅m]muMUf~uxʉxxxppNEL)ЋLH@H ƃ!uL!xAAA L ׉LEp!!ϋE ׋HDJG փ!u! ׋H !ƒE!8M8 ֋Lʉ8uٝhDE񉅸8}u|~~ˋFʋF؍h~~ɋ؍dFˋF؍h~~ɋ؍`FˋF؍h~N؍\V~؍h݅ ؍X݅tuEt}Ủ ݃X@Mݝك<Džٝht& }$OUٝA)ٝ ЉA@ٝA M!! } Ɖ !! ЉHA ljٝ!ʃM!ȋ ‹ME !!򋅌 uuMщE؉݅UݝxمMݝpll݅p5}fMu EfM싍$m]m$Uf~U׉l$$NE)ȋ$@ ƃ!GGG }! ƋE!‰! FO !ΉM!  !ʃM!Nj ׋ʉ}ٝ}E}Elj}u|~~ˋFʋF؍~~ɋ؍FˋF؍~~ɋ؍ FˋF؍~N؍V~؍݅x؍݅p؋E$UMfxω$l MEh }Ủ݃X@uݝكٝ\`} >}u|X`~\`Fˋ`~ʋXTF؍T~\TFϋT؍~ˋXPF؍P~\PFϋP؍~ˋXLF؍\~LNjX~ɋL؍~ʋL~؍݅@؍݅8؋MU}fA4ME0}Uȉx݃X@uݝك<,Džٝt&xx},|Op(UٝhA)ٝdЉXA@ٝ`A ( (M((!!x(}( Ɖ( p!! ЉHA ljٝ\X!ʃM!ȋ ‹ME !!E u((΋u֋E܉݅UݝمMݝl݅)}fMu fMMm]mUf~u|ʉ|||,ttNEP)ЋPL@L ƃ!uP!|AAA P ׉PEt!!ϋE ׉ʉHBLO< !u!< L !P!Hٝl$(}><}u| (~$(Fˋ(~ʋ F؍l~$F׋؍h~ˋ F؍l~$F׋؍d~ˋ F؍l$~Nj~ɋ ؍`N~؍l݅؍\݅؋M|uEfylj|xM}xEU@E9Ue[^_]Í'UWVS<[÷M}AEOG$wEԋWG M؉EċOEu܉UЉM̋w8WO,uȉUMw<OuUMG W0}EUwONG IuwMEM؉}W uUUzO)7MƋOuȋO EME)EMuOJ(E|EE9EU}Љt^ك݃Xٝك<ɋuݝٝDžxt}xtOUl)AɉЉT@ٝdٝ`AA ɋ M!!t} Ɖ l!! ЉHA ljٝ\ٝXT!ʃM!ȋ ‹ME !!E u΋u։M܉_م}݅ݝمEݝMfU| ݝ fUa݅m]muMUf~uxʉ|xxxppNEL)ЋLH@H ƃ!uL!xAAA L ׉LEp!!ϋE ׋HDJG փ!u! ׋H !ƒE!8M8 ֋Lʉ8uٝhDE񉅐8}u|fٝ,0} >}u|(fHٝl}><}u|fٝ48} >}u|0fHٝl}><}u|fك(ك(ݝPك̞ݝHݝ@كТك,ݝ8ݝ0كĞك0ݝ(ݝ ݃Xك<ɋ}Džݝٝ&xEx|V$܍PO݅H݅@ɉɉU)B Ɖݝ`ʋ!΋M!܅` Ɖݝ`݅8ݝX݅0ܭX܅(ݝX݅ ݝP܅PݝPݝHux !! BO ƃ!΋M! ƋEw} !!u ы֋Uu΋Uم}݅ɋUݝ`ufM ݝXfMuݝh݅`݅hSݝhu݅h|UmmE䋍|||tVtM)щNUA !Ѓ|!$ ƉDȋDt ǃD܍PDD!׉D}!D ljDGQ Ѓ!NjE! ׋t ȃ!‹E!E݅Hɋu ɍ E݅8D݅@݅0݅ ܅(ˉEE̍uɋݝhˋ܅hݝh͋܅hݝh̋܅hݝhˋʋɋ܍Xˋȋȋ̋܍P݅h܍`܍Hݕh݅XvxuExR}MЉ4lك(ك(ݝك̞ݝݝكТك,ݝݝكĞك0ݝݝx݃Xك<ɋUDžݝٝHt&4E48V$܍O݅݅ɉɉtUt)B Ɖttݝ ʋt!΋Mt!܅ t Ɖݝ ݅ݝ݅ܭ܅ݝ݅xݝ܅ݝݝtu4 !! BlO ƃ!΋M! ƋEhw} !l!u ыthtUl}hϋu܉dم݅ɋMݝPUݝXLsv݅Pݕ(g}fUM 8fUE݅(mmUlj8LP880NQ0M)$A NjU!!08 ܍ȉ} !!G ƉEq !׋U!֋0  ΃!!EƋu ݅ ɋEʉp݅ʋEʋE݅ˍ݅x̋t݅ɋu܅ݝ(tpɋt܅(ݝ(ˋt܅(ݝ(ʋl܅(lpݝ(ʋl̋lhhpɋh܍͋hdȋpdˋd܍ɋd݅(܍ ܍ݕ(݅X݅P݅(E8UMω8Lt&4}EH4"MUЉlك(ك(ݝ@ك̞ݝ8ݝ0كТك,ݝ(ݝ كĞك0ݝݝ݃Xك<ɋuDžݝٝt&EV$܍@O݅8݅0ɉɉ U )B Ɖ ݝʋ !΋M !܅ Ɖݝ݅(ݝ݅ ܭ܅ݝ݅ݝ܅ݝݝ u !! BO ƃ!΋M! ƋEw} !}!u ы ֋ Uʋ}؉م݅ɋMݝUݝsv݅ݕg}fUM fUE݅mmUljPNQM)$A NjU!! ܍@ȉ} !!G ƉEq !׋U!֋  ΃!򋵼!EƋu ݅8 ɋEʉ݅(ʋEʋE݅ ˍ݅̋ ݅0ɋu܅ݝ ɋ ܅ݝˋ ܅ݝʋ܅ݝʋ̋ɋ܍͋ȋˋ܍ɋ݅܍܍ݕ݅݅݅MU}t&}E"MỦlك(ك(ݝك̞ݝݝكТك,ݝݝكĞك0ݝݝ݃Xك<ɋuDžݝٝxt&EV$܍O݅݅ɉɉU)B Ɖݝʋ!΋M!܅ Ɖݝ݅ݝ݅ܭ܅ݝ݅ݝ܅ݝݝxu !! BO ƃ!΋M! ƋEw} !}!u ы֋Uʋ}م݅ɋMݝUݝ|sv݅ݕg}fUM fUE݅mmUlj|PNQM)$Att NjU!!tt ܍ȉt} !!G ƉpEq !׋U!֋  ΃!t!EƋu ݅ ɋEpʉ݅ʋEʋE݅ˍ݅̋݅ɋu܅ݝɋ܅ݝˋ܅ݝʋ܅ݝʋ̋ɋ܍͋ȋˋ܍ɋ݅܍܍xݕ݅݅݅MuElj|t&}Mx"EU@E9U|[^_]Ít&UWVS[GSMquPxpUP$}܉U؉uԋxP p8}ЉủUȋxpT>EԋuȉȉU)u !‹E܃Eċ4:űt:u19nE؋UԉUЉTỦTUT ȉ +E!‹E:EԋD:EЉ +E!‹E;uċ:E̋D:E~EtU؋EԋMЉLt&UEMȉUu)Eu؋EƉu؋uMe1[^_]VMQUREu)VUEMUȋu)U؋uEU뚍&'UWVS<[j}uWF }EUGO wMP xuMp}܃Uȋx,uąEH1Q48Mʉu؋L8t8MԉuЉыuU1ҍIMIM9}WM&M܋u؉4uԉtuЉt 48L8u؉MԋuL8MЋMvuMuuU̍ RE܋u؍4uԋMЉrJ&PE4@VMQU܋E)R9MȋEȁuMȋUȉ)ыu܋UE֋UuMe1[^_]É'UWVSl[}uWF }EUGwO uP 0Mu܋xHpM؉}ԃUu!EpE؋NыM Ȩ1}ԋuM48u|uԉ}ЋL0uԉM̉ |0 }ȋu}1O}9}au܋EN}8}Љx}̉x}ȉx }ԋ<9}}ԋ|9}Ћ}ԋ|9}̋}ԋL9 }Mȉ NuEu؋U}ЉMỦxPH T&E1‰UUԋ4DuEuԋET7L7 UMċ}U܋M J}ULEUEU؋u}4|u}ĉt| uԋ1T1EUEԋU|t M} E}ƒMuĉU}u}uMEU >D>MEĉL>D> MEUȋ}U)Eu؋UuMue1[^_]ÍPURuV}؋E)W6U}MωUu)E}}lj}؋UuMuwe1[^_]Ëu؋UM}EĉN~F 뗍v'UWV(}WOUMOw MG MuEr QEU܋AQu؉UЅ~uEԋE1P;u}$ȋ}TNj}܉T}F;u|܋E؋M%}E؉ϋU)׉MMuEuEMԉuu(1^_]ÉUWVHEp HuP pxMUN }U܋F~Vu}؉UԉMEuE܋VU~iEU܋E}4NjL}ԉM̉uȋL tMEċM؉u}̋uȉ4|u}| t}MσJ}Eu}uU։}E)‹MEЉuuu։MMu>H1^_]ÐUWVXEpP uH pUuM~V }H}FMUwuaIEM1U܋}4‰uOu܋|}TMUЋL U܉MԋMt|uȉ}̋uU1҉M;UčMĉMM}u|4}ԋuЉ| t}̋uȉ|t}܃4NjL}܉uMtL }܉uЉMԋtLuȉM̋}uM}MIuUč REU}uD<ʍʋ}ЋMȋUԉxH}̋Mx}P ȉ}uU})ЉMUUUMUX1^_]Ðt&UWVXEpP uH pUuM~V }H}FMUwuIEM1U܋}4‰uOu܋|}TMUЋL U܉Mԋt|uȉ}̋u܋}LTMUċMUʉU;EEU}ux0}ԋuЉx p}̋uȉxp}ċuxp}܃ 4ϋ|}u}܋t| }ԉuЋ}܋t|}̉uȋ}܋t|uuM}}J}pE}MuỦ tMЋ}ȋuԉxH}ċMxp }HuMP}U)ЋuU‹}MuƉ}MuvX1^_]ÐUWVS[Eu MEݝhE E(ttu 99>t؍e[^_]Ívy9~uك$݅hu غ뺋E x x ~ غɃ Uȍ}Pu4VU0R$\$lWhV$E PQ}U @J"DždE~J}0}0}0d 4Q…u0}4 !1Ҩ}4 E4DždmكĞ݅hEu;d 4P{ RX_d t؍e[^_]Í&UJ9Nut uظ뻋VFPDVEXN UHHBL@ME0QT U0M0݅̋BQݕݕݝݝݝ]]ݝE0H PPP R$$H)‰XZHR$$Dž)ioTP@HX LU4(D犍≕DH@Dž QQ}0WPF < | EEEQWPU4RQWRU PhQV}WR@UMċ},4UȋM08}̉$UEM4<80U}4~cM0DQWR0Q4R8W$QEP tE'.U4}4!ȨtՃ}4}4 DžtDžDž$}4 tT WPQjRWQ RjjV}xWV9@u(}4}4}4Dž PgDž<Dž< P8Dž<IDD<Dž<VDž<GDž<8}4t }4 ;@IH@}4LUH\,DžM4M49;U~B)F09, P 4V, 119} @9| 1ыPJ9}4@;|拍 P 9}4B;|\\}4BN \\V0p D )<}4K(݅hػĞ)1J9ك̞ۅۅܥ` V$ٽvfv ft1V$ʋ(٭t٭vG9EtۅEu^뱋04ϋ,x \4U'R؋ <U09qRWVR(WQVR\yW0)<Dž}4(_݅hػĞ1GW)$ك̞ۅɋܥ`H9I ٽvfv ۅ ft4vV$ʋ(٭t٭vB9EtۅEu뭋)ϋGx E,MB;U|1;u}EMMF;u|19}ك$U@9|؋U(E(uz8H4}}ЉM)M֋EMN)E̋M)ȃM)ȊMMJEUE$MЋUx`pd}uHP@XPhxlVC}MDž7}$uwxGpu֋uԍ;Mu$tVpu419Ћu}u@9|獶؉;MxMA}MU)9U<EF9e[^_]ÐMA}R}}M$DžE싱9Š!ʊMMG EMЋDB9|[E$U싰h1;;Њ!ȋMȋh ȉhʋMGDMGHDGHD UMGHTF;c1E9Mt&Š!ʋMʊMG DGJDGJD GJEUDF;|U$1E;pŠ!ʋMʊp UGDUGHDUMGHDF;|^E$U싰d1;>Њ!ȋMȋd ȉdʋMGDMGHDMGJETF;uM$1U;``\Њ!ȋMȋ\Ȋ` EW TWHUETF;|U$E싲l1;Š!ʋMʊl UGDGIEMDF;||O;MMtM$IpDM TMBLPMBLLMBD >H1;D}ITPɋLHˋM@ >;D|gWM9[EM$Ipt4M @MBL@;4|JO;MxEE$t@pEED@1;ɋU >@;|U$t|R U$RpUU 1;}fE ك$كHكLfEʋ ̋EuummʋEB;|tMt9tt)ʉtMt|0M$I M$BIp1҉$M;$ ,MD >(g }ɉfM ك$كHكLfMʋ,(ɋEuuɋ0mmE0 >B;$|PPM$Q|PuVtRMQE4VC MPPM$Q|VURtQuVERɍUWVS$[*EUM }p u؋ppEDEDED UEBBB19كLM]كHك$&fEB9}gEЋE΋E͋E̋EuEt}fE fEmmEB9|؃$[^_]ÐUWVS[EUM }p uppEDEDUEBB19MكHكLك$t&fEB9}^EЋE΋E͋Eut}fE fEmmEB9|؃[^_]Ív'UWV0E} ppPPuUp`HduG1MU;}]EMuU !ЋU AAAM}EM$ЋU4TUqQU͋LU$MJMˋD MBU(rurMJBʋUMDGE;}G؃0^_]Ív'UWVEU }(ppHPuMp`HduM1M;uBEMMU !ЋU AAM}EMЋM$UQUˋTUQUˋM$DUAʋMWOˋMˋTGɋEFU;ud؃^_]ÐUWVS[u E U$݆HDrA<]HD QiǃtMV$MكĞuB WM$Qu VURMQuVEPU RMQI$Wp1e[^_]Í WU$RM QuVURMQuVE PUR' 뼐t&UWVS[7M } u$EA8W4M EE UyQD}y HT}܉M؋xPFMENF$ENFMEEM(ȉx19}ك$x@9|؋N4V8Mu)U)ЉMĊMpE؋M)ȋM)ȊMMJE|} }V } }c U B\MԋU MԉBX)ωrdzP|}ĉJlz`EMu}Bh9e)Ћu@EEЋM yHM)} GxOpE܋}Ѝ4WU܉t9UE苽tU}ȉE9}EuUȋM 4UȋyL)MЋ}|E!ȊMUU ME9Mr|t&}M)ω}~E}uU}EM9M} U MEwPȋzpM U q`zd E u } HXV\G<E }U MENj<ȋU GE)‰M̉UE} W }8ҋ84I10;M!ЋUȍ Љ!ʋ!ȋM‹8QBIɋ4 0BHJHxG;Vt&؋}ENju}9uU}MU9MuE1ɍp;M}pu}fE ك$كHكLfEʋxuummʋxA;M|؋UMBU9MuEFu9Ee[^_]É},}}u M1;&M!ЉȊ!ʋ!ȋM‹Q xF;|Z} U O },ɉ$1(;M!Ћ$ȍ Љ!ʋ!ȋM‹,QBIɋ( xBJG;az}U M B 1 ;LvU!ȍ щ!ʋ!ȋM‹ QB ɋxIG;wDž}9UE‰Eu UЋu ;M#u pVpu419Ћu}|@9|v؉;MxE@UEM)9MuG9e[^_]ÐuA}}b}1;v!Ȋ!ʋ!ȋM‹QM MG;|ZE M x TTPRH1L;t&M!ЋHȍ Љ򉍬!ʋ!ȋM‹TQBIɋP LBHJHUG;Xcv} O lɋlh<@d19U&M!ЉȊ!ʋ!ȋM‹lQBIɋh dBHJHMG;dE x `\1;W&M!ЉȊ!ʋ!ȋM‹`QBIɋ\ BEJG;r} E O D<1@;M!Ћ<ȍ Љ򉍬!ʋ!ȋM‹DQBIɋ@ BIEG;fE M x 81;4U!ȍ 4щ򉍬!ʋ!ȋM‹8QB ɋIMG;z$E x X1;t&M!ЉȊ!ʋ!ȋM‹XQB ɋEIG;|W;UKEM IppM ,MBL(MBL$MBD > 1;}R,(ɋ$ ˋ|@ >;|iWM9EM Ipp M MBLMBD1; }ɋ|ʍ >@; |GO;MEE p@pEED@1;ɋ| >@;|U pxR U RpUU1;}fE ك$كHكLfEʋ̋|uummʋ|B;|pEp9pp)p{MpxM I M BIp1҉M; MD >[}ɉfM ك$كHكLfMʋɋ|uuɋmm| >B;|}t*}bE}[E @\VE6QQE PxR|VpQURE4VC MPPM QxV|RpQuVERƉ'UWVS$[jsEUM }p u؋ppEDEDED UEBBB19كLM]كHك$&fEB9}gEЋE΋E͋E̋EuEt}fE fEmmEB9|؃$[^_]ÐUWVS[ZrEUM }p uppEDEDUEBB19MكHكLك$t&fEB9}^EЋE΋E͋Eut}fE fEmmEB9|؃[^_]Ív'UWV@EUxPpp}uxXp`}Hdxu$ypM$0V`u yd($,אt&Uu$M)щMEE1U;0Њ($!ȋMȋM ȉ MʋMDMHDHD UMHTF;0g&؋UuEĉU9E&ME}M9}IUȋ,1;M}ku}fE ك$݃Xك<fEʋEuummʋEA;M|؋}UG}9Udu,FuM؉,9M(e[^_]Ã}I}}SU$MDžE싲049Š($!ʋMʊ 4ȋMM UDA09|XE$U싰E@1;03vЊ($!ȋMȋ@ ȉ @ʋMDMHDMJETF;0suM$U싁1<;0y<8Њ($!ȋMȋ8Ȋ < E THUETF;0|DžU9ME,xRRRRQWMQUR}WMQE$PUR}WE09u$E xx׋TыxTxT uĉ)փ~/PRPQWURMQ}WURM$Q}WEP*Džu9}Mω}$,,E ֋T׋,TM) ~ Dž  1 X yM$DžUAptE$HPpM$A`lE$HdEht@@9dЊlh!ȋMȋdpMMʋMTt@9s؋ Njuĉ9MDžM9u}E$U ,<,LU)~ Dž!E$M$DžUppAPTPu$E$N`pdLHMT9DA}}ЊLH!ȋMȊPDMMɋMTT@9|؋Njuĉ9PE$DžUHPpp`9H`\M$AdMXA}Њ\X!ȋMȋȊ`@MMDˋʋMʋMT@9lPRRQWUR}WMQURE$PMQ}WTPRRQWMQUREP}WM$QUR}W^PPVWQuVEPUR}Wu$VMQUR0PPVWQUREP}WMQu$VUR}WPPVWREP}WuVMQU$R}WuV뜃 WV}WURMQuV}$WURMQ 0 WWURMQuV}WU$RMQEPʍUWVS,[`M$U$y8r4M$u}܋AQ yDqTE؉UԋAPU(M(EЋűQEċu(UE(U(Np$MBuEE,uM(Q UM4U1;U}>E,MB;U|1;u}EMMF;u|19}ك$U@9|؋U(E(uz8H4}}ЉM)M֋EMN)E̋M)ȃM)ȊMMJEUE$MЋUx`pd}uHP@XPhxlVC}MDž7}$uwxGpu֋uԍ<9xMxE܉UȋuDžtM9E؋MU EM;uL;Mu$tVpu419Ћu}u@9|獶؉;MxMA}MU)9U<EF9e[^_]ÐMA}R}}M$DžE싱9Š!ʊMM EMЋDB9|[E$U싰h1;;Њ!ȋMȋh ȉhʋMDMHDHD UMHTF;c1E9Mt&Š!ʋMʊM DJDJD JEUDF;|U$1E;pŠ!ʋMʊp UDUHDUMHDF;|^E$U싰d1;>Њ!ȋMȋd ȉdʋMDMHDMJETF;uM$1U;``\Њ!ȋMȋ\Ȋ` E THUETF;|U$E싲l1;Š!ʋMʊl UDIEMDF;||O;MMtM$IpDM TMBLPMBLLMBD >H1;D}ITPɋLHˋM@ >;D|gWM9]EM$Ipt4M @MBL@;4|JO;MyEE$t@pEED@1;ɋU >@;|U$t|R U$RpUU 1;}fE ك$݃Xك<fEʋ ̋EuummʋEB;|tMt9tt)ʉtMt|0M$I M$BIp1҉$M;$ ,MD >(f }fM ك$݃XكB;$|PPM$Q|PuVtRMQE4VA MPPM$Q|VURtQuVERɍv'UWVS$[ SEUM }p u؋ppEDEDED UEBBB19كPWRMQ|V} WURQ PQV}W|RM QuVPȍUWVS\[w6M U Ey8r4M u}Q AU؉E܋U$APqTEԋyDuЉEM$u$E$QNp$UMuuU$BM$EE(Q U|4MU19}/E(MB;U|1;u}EMF;u|19}ك$|@9|؋E$uM$P4EUy8Uԉ})M)zMpEЋM)ȋM)ȊMMJEU} } }uE} M A\MŰM)щMԋM Uԉy`QP}AXqdyhEU}uAl 9)ȋ}uDžP} wH} )WpGx}؋UM؉49tFM䋕tEUċuDžpM9E܋MUEM;uL;M#u pVpu419Ћu}|@9|v؉;MxE@UEM)9MuG9e[^_]ÐuA}}b}1;v!Ȋ!ʋ!ȋM‹M MG;|ZE M x TTPRH1L;t&M!ЋHȍ Љ򉍬!ʋ!ȋM‹TIɋP LHHUG;Xcv} O lɋlh<@d19U&M!ЉȊ!ʋ!ȋM‹lIɋh dHHMG;dE x `\1;W&M!ЉȊ!ʋ!ȋM‹`Iɋ\ EJG;r} E O D<1@;M!Ћ<ȍ Љ򉍬!ʋ!ȋM‹DIɋ@ IEG;fE M x 81;4U!ȍ 4щ򉍬!ʋ!ȋM‹8 ɋIMG;z$E x X1;t&M!ЉȊ!ʋ!ȋM‹X ɋEIG;|W;ULEM IppM ,MBL(MBL$MBD > 1;}R,(ɋ$ ˋ|@ >;|iWM9EM Ipp M MBLMBD1; }ɋ|ʍ >@; |GO;MEE p@pEED@1;ɋ| >@;|U pxR U RpUU1;}fE ك$݃Xك<fEʋ̋|uummʋ|B;|pEp9pp)pzMpxM I M BIp1҉M; MD >Z}fM ك$݃XكB;|}t*}`E}YE @\TE4QQE PxR|VpQURE4VB MPPM QxV|RpQuVERƐ&UWVS$[&EUM }p u؋ppEDEDED UEBBB19كu$ypM$PV`u ydLH}׋Uu$M)щMEuU1;PLH!ȋMȋM $DMʋMt8%$ MDt8%$IMDt8%$IMD t8$H xETF;P%t&؋UuEU9EME}M9}0Uċu1;M}o}̞fE ك(ك$fExuum]mfEx}4A;M|؋MUAM9UIEu@E9u[^_]Ð}}}M$uDž$E苑PT9$ŠLH!ʋMʊDTȋMt8% $xUDAP$9|VuM$U苁1`;P1ЊLH!ȋMȋ` $D`ʋMt8%$ MDt8%$IMDt8$HxETF;PBmuM$U苁1\;PH\XЊLH!ȋMȋXȊD \ Et: ETt : JxETF;PdDž@}9@}$uMU$GP,1M$rpU$8y`4rd} U0 tʋM(M@u$M)щ<~ Dž<<<a<}@Dž$8U艍dG9$΋tЊ40!ȋMȊ,dM1( M1Ɋ x ɋ$MT8@$9zt&؋@<NjU@9@MDžp8UA9p}40}!t@ EAAA ,(<1űDƉ1Mtt̋($1tEˋT 1 uʊ 7́ȋt ̋$17t} 191Ɋ :x ɋpu8TAp9u}$EU苏1l;8@M40M!lȍ 1AA,M4(tuˋDΉ$1Ɋ 0utTt(1 1uʊ 0$t 1Mʊut 12x uTG;8EM$E1U苹;8h@t&Њ40!ȋMȋh,hʋM@($1ҊUʋD1҉1ɋE$ʊ  ʉ1 ɋxM TF;89Dt&UWVS|[w M$U$y8r4M$u}܋AQ yDqTE؉UԋAPU(M(EЋűQEu(UE(U(Np$MBuEE,uM(Q Ux4U1;U}>E,MB;U|1;u}EMMF;u|19}̞x@9|U(E(uz0H,}}ЉM)M֋EMN)E̋M)ȃM)ȊMMJEUE$MЋUx`pd}uHP@XPhxlV9 }MDž7}$uGpwx@uGt}9p MpE܉UċuDžlM9 E؋MU EM;uLU䋽pE}U9U܋u}EEu4} OLw|)ʊ|x!ȋMȊMMEU9M+M})M~EME4u}dE}9}MEȋU u E zPVp} pdU E u O`zXH\V<UMʋu }} u)։MĉuEE x @<@}R18;M!ЋUȍ !ʋ!ȋM1ʋt1 @t1I<t1I8ttH G;#؋uEƋ}u9}UuMU9MU}1;M}n}̞fE ك(ك$fE𐋅tuum]mfEE‹t4A;M|؋UMBU9M}uG}9u4[^_]Ð}}}uM 1; &M!ЉȊ!ʋ!ȋM1 tt F;|PE U H E4,10;vM!Ћ,ȍ !ʋ!ȋM1ʋt1 4t1I0ttHG;?KE U H E$1(;M!Ћ$ȍ !ʋ!ȋM1ʋt1 (ttHG;^jDžM9jU E}u NjJPE VpM pdE U u y`JXx\Vu$ypM$0V`u yd($,׋Uu$M)щMEE1U;0vЊ($!ȋMȋM ȉ MʋMNjDMNjHDNjHD UMNjHTF;0g&؋UuEȉU9E &ME}ĉM9}QM̋,1;u}9}ك$t&}TQF;u|؋MuAM9uU,BUE܉,9Ege[^_]Ð}}}}YU$MDžE049Š($!ʋMʊ 4ȋMM UDA09|u$EU1D;0d@Њ($!ȋMȋ@Ȋ D E׋ TE׋JTMHUTF;0su$EU1<;08Њ($!ȋMȋ8Ȋ < E׋ TME׋JTF;0|"Džu9)EUЋ,|RRRRQWMQUR}WMQE$PUR}W[E09}$E ||׋Tы|T|T uȉ)փ~/PRPQWURMQ}WURM$Q}WEPz*Džu9}Mω}$,,E ֋T׋,TM) ~ Dž  3 Z {M$DžUApxE$HPtM$A`pE$HdElx@@9h&Њpl!ȋMȋhtMM ɋMTx@9s؋ Njuȉ9L%DžM9 u}E$U ,<,LU)~ Dž"E$M$DžUppAPXTu$E$N`pdPLMX9HA}{ЊPL!ȋMȊTHMM ɋMTX@9|؋Njuȉ9E$DžUHPppd9H``M$AdM\A}Њ`\!ȋMȋȊd@MыMʋD ׋ʋM ˋMT@9jPRRQWUR}WMQURE$PMQ}WPRRQWMQUREP}WM$QUR}Wl\PPVWQuVEPUR}Wu$VMQUR[0PPVWQUREP}WMQu$VUR}WwPPVWREP}WuVMQU$R}WuVY뜃 WV}WURMQuV}$WURMQ0 WWURMQuV}WU$RMQEPʐt&UWVS,[M$U$y8r4M$u}AQ yDqTE܉U؋APU(M(EԋuЋQEȋu(UċE(U(Np$MBuEE,uċM(Q UM4U1;U}>E,MB;U|1;u}EċMMF;u|19}ك$U@9|؋U(E(uz8H4}}ԉM)M֋EMN)EЋM)ȃM)ȊMMJEUE$MԋUx`pd}uHP@XPhxlV}MDž7}$uwxGpu֋u؍<9|^M䋕|EŰuDžxM9E܋MU EM;uċLu$ypM$V`u yd׍vUu$M)щMEE1U;Њ!ȋMȋM MʋMDMHDHD |MH TF;dt&؋UuEȉU9E&ME}ĉM9}KŰ1;M}3E܋$t&|E,MB;U|1;u}EMMF;u|19}$x@9|U(E(uz0H,}}ЉM)M֋EMN)E̋M)ȃM)ȊMMJEUE$MЋUx`pd}uHP@XPhxlV }MDž7}$uwxGpu@uԉGt9u E U$݆HDrA<]HD Q9vǃtMV$MكĞuB WM$Qu VURMQuVEPU RMQI$W@v1e[^_]Í WU$RM QuVURMQuVE PUR뼐t&UWVS[>M u }$EQ4N8MUM q QDAuqPEM u܋wATuE؉MċwGO$EME(uOM1t9}$t4@9|U܋O,w0)‹})MuzMpE؋M)ȋM)ȊMMJ≅|x}_}%}W}2U B\MUԋM)щM܋M U܉QPAX|xy`qdQhAlu}M9F)Ћu@EEЋE,MB;U|1;u}EMMF;u|19}ك̞|@9|؋U(E(uz8H4}}̉M)M֋EMN)EȋM)ȃM)ȊMMJEUE$M̋Ux`pd}uHP@XPhxlV]}MDž7}$uwxGpu֋uЍ;MSpE$PpuE19}#v|@9|t&؉;MhMA}MU)9U"uB9e[^_]ÐE$}}`}U$DžE苺9!ʊMf FQME$ MЋDB9|=E$U苸d1;!ȋMȋd ȉdʋMfF%P$Df F $M$HDfF%$$HD f F $U$HMTG;0H1E9M4t&!ʋMʊMfF%PM$D fF%$M$DJfF%$M$D JfF%$$JUMDG;FqU$E苺l1;Q!ʋMʊl UfF%P$DfF$U$HDf F $U$HMDG;YE$U苸`1;t&!ȋMȋ` ȉ`ʋMfF%P$Df F $M$HDf F $M$JETG;NM$1E;\\X!ʋMʋXʊ\ UfF%P$DfF$E$IMDG;pU$E苺h1;t&!ʋMʊh UfF%P$DfF$E$IMDG;{VO;MEM$Ipp@M PMBLLMBLHMBD >D1;@}Qt&PLɋHDˋ|@ >;@|IWM9~EM$Ipp0M @;0|GO;MEE$p@p EED@1; ɋ| >@; |U$pxR U$RpUU1;}fE ك̞ك,ك$fEʋ̋|uum]mʋEf|B;|pUp9pp)щpyMpx,M$I M$BIp1҉ M; (MD >$T}ɉfM ك̞ك,ك$fMʋ($ɋ|uum]mʋ,Ef|, >B; |PPM$QxP|VpRMQE4VB MPPM$QxV|RpQuVER*Ɛ&UWVS,[*EUM }p uԋppEDEDE܋D UEBBB19ك$M]ك,ك̞&fEB9}nEЋE΋E͋E̋EuEt}fE fEm]mEfEB9|؃,[^_]Ív'UWVS[ EUM }p u܋ppEDEDUEBB19Mك,ك$ك̞t&fEB9}eEЋE΋E͋Eut}fE fEm]mEfEB9|؃[^_]ÍvUWV(EEU }ppHPuMHdp`Mu苈BɋUM]9U$Mu!M uȊMBBB>M$u;MS lE PpuE19}#vx@9|t&؉;MhUBEUM)9M}F9e[^_]Ð}!}}}1;v!Ȋ!ʋ!ȋMfQ%PU$ |G;|:E M x PPLRD1H;t&!ЋMDȍ Љ򉍨!ʋ!ȋMQf QP$fJ%$$ILfJ%$$IHfJ%$|$JG;E H hɋhd1;}M($ɋ ˋx@ >;|IWM9EM IplM MBLMBD 1;}ɋ xʍ >@;|GO;MEE l@pEED@1;ɋx >@;|U ltR U RpUU1;}fE ك̞ك,ك$fEʋ̋xuum]mʋEfxB;|lMl9lGl)ʉl4MltM I M BIp1҉M; MD >T}ɉfM ك̞ك,ك$fMʋɋxuum]mʋEfx >B;|}t*}E} M A\EvQQE PtRxVlQURE4VC MzPPM QtVxRlQuVER+Ɖ'UWVS,[ÊEUM }p uԋppEDEDE܋D UEBBB19ك$M]ك,ك̞&fEB9}nEЋE΋E͋E̋EuEt}fE fEm]mEfEB9|؃,[^_]Ív'UWVS[jEUM }p u܋ppEDEDUEBB19Mك,ك$ك̞t&fEB9}eEЋE΋E͋Eut}fE fEm]mEfEB9|؃[^_]ÍvUWVHE}uPpUPPUP`UPdUPXUP\UUU܋PM8,0(ݝH$8Љ 0Љ8ȉ0ȉ@EEɋD݅PMMɋ<ݝݝɋ,ݝE4E(ݝ݅HݝM$݅PM܍H܍Pɋ担 EݝEMMݝݝ͋ݝEEݝݕ܍Hݝ݅PM΋DM܍H܍݅܅݅܅@݅ɋ܅݅ˋ4܅ݕ݅ܥɋ<ݕ݅ܥ,ʋ(݅܅EM݅ʋ ܭ݅̋$܅݅ˋ܅ݕ݅ܥ̋ݕEEM]EMm]]EEEɋUB}MЉU9}M]EM]q\[^_]Í&'UWVS[TMEe}كĞ4}苻8R$DɋUEUUD ɋ}Uʍ ЉMUMɍ<UE]U]]]EM}} EE NjE ׉M}MݝPEM@܅P480,@ݝP(8Љ$ @ȉ8ȉHEEʋLMMɋDݝݝɋ4ݝEU }t4tЃ ~ jQRPRjUR]Ëʍv'USP[U }t4tЃ ~ jQRPRjEP']Ëʍv'USP[~U}t6Ѓ ~ jQRPRE PEP]ËȐ&USP[U}t6Ѓ ~ jQRPRE PEPe]ËȐUWVSL [òU}w0}4}Z}@1}}1e[^_]É} |;UM)V$ػĞ} ݝ`U W MDžX\XEDžPDžL)DžHDDž@Dž<Dž8DžT\XӥPӥLӥHӥDӥ@ӥ<ӥ89T}TT ݅` %X ЋH苕LP Ƌ< ‰@8D U݅` ݅`Lݝ0݅`Lݝ( ɋݝ ݅`݅` ݅`Lʋ ʋݝ݅`݅`L ɋ ݅`݅` 2ʋݝL2݅`L2݅`ݝ݅` 2ݝLݝ݅`L2݅` 2ݝ݅0ܭ0ݝ݅(ݝ݅(ܥ ܅ ݝ݅ݝ݅ݝ݅ܥݝ݅ݝ݅ݝ݅܅ݝ݅ݝ݅ܥܥܥܥܥݝ݅܅ݝ݅ܥܥݝ݅ܭݝ݅ܭݝݝݝ݅݅ܥ݅܅ݝݝ݅݅݅܅ݝ݅܅܋ܥ܋܋܋_݅ܥT݅ܥɃT\_H݅ܭ_@_݅ܭ_P_݅ܥ_X_ ݅ܭ_`_(݅ܭ_h_0݅ܭ_p_8_x9TPM QEP}WvJuMك̞FFFYYYn;UE} )ЈW$ػĞ} ݝpU MDžhlhEDž`Dž\)DžXTDžPDžLDžHDždlhӥ`ӥ\ӥXӥTӥPӥLӥH9dMXdd ݅p71%h ЋX Ƌ\ ‰P ƋT ‹` NjH ƋL ‰u݅p݅p݅p ʋL >ݝ@L>ݝ8ݝ0ݝ(݅p݅p Lݝ ݅pL݅p݅p ݝL݅p ݅pݝ݅pL݅p > ݝݝ݅p ݅pL>݅@ݝ݅@ܥ0ݝ݅pݝ݅8ܥ(L݅8܅0ݝ݅ݝ݅ ݝ݅ݝ݅ܥ܅܅(ܥ ݝ݅܅ܥݝ݅ɋXܥݝ݅ܥݝ݅ܥݝ݅ܥݝ݅ܭݝ݅ܭݝpݝݝh݅݅݅܅ܥݝݝݝx݅݅ܥ܅݅܅܋ܥ܋܋܋݅܅dd_݅ܥ_@݅ܥ_H݅ܭ_݅ܭ_P__X_ ݅xܥx_`_(݅pܭp_h_0݅hܭh_p_8_xXl9dm&MDžEDž)DžDžӥӥӥ9}v ݅`% ‹ꋅ ЋMt p݅` ɋpLtt݅`݅` 1݅`L1ʋtppL1݅` 1ݝ݅` ݝx݅`L݅˃Ή動݅xݝhܥܥx݅hܥh__ __(__0_8@9T5Ue} U z MDž`d`EDžXDžT)DžPLDžHDžDDž@Dž\d`ӥXӥTӥPӥLӥHӥDӥ@9\LM`\\ 71%` ЋT ƋP ‹L ƋH ‹DX lj@M 9D9Dݝ8D99ݝ0ݝ(ݝ 1D1Dˉݝ9ݝD1Dݝݝݝ݅8݅0܅(ܭ8D9ݝݝ1ݝ݅0ܥ(݅ܥݝ݅ ݝ݅ݝ݅ܥܥ ܥݝ݅ݝ݅ݝ݅܅ܥݝ݅܅ݝ݅ݝ݅ܥܥܥݝ݅ݝ݅͋`ܭܭݝݝ݅݅ܥݝݝ݅ݝ݅݅܅݅܅ݝ݅܅^݅݅ܥܥ܋ܥ܋^@܋܋^H݅ʋ\ܭʃ\d^݅ܭ^P^݅ܥ^X^ ݅ܭ^`^(݅ܭ^h^0݅ܭ^p^8^x9\`D} Y})׉}MV$ػĞMEA(AA0]A8A ]AA]EE]EeEeeeXX X(XX0XX8i} uM)ѺMMR$ػĞ}G(GG`ݝGhݝGPݝGXG@GHݝG8ݝG ݝGGݝݝG0GpOxݝݝ݅ݝ݅ܭܭ݅ݝݝ݅܅ݝ݅ݝ݅ܥ݅ݝ݅܅ݝݝݝ݅܅݅݅ݝ@݅܅ܥܥܥݝ0݅ܥܥܥݝ݅ܥϋEݝ݅ܭݝ(݅ܭݝݝ ݅ܥ݅܅ݝݝ݅݅ݝ8ݝH݃݅0݅@܅Hܥ8܋݅0܅8X݅@ܥHX@XH݅(݅ܭ(ܥX݅ ܭ XPX݅܅XXX`X ݅ܭX(݅ܭXhX0݅ܭXpX8XxMEDžDžDž)Džӥӥӥ9:M\ 07% ‹ꋅ Љt u݅p݅p݅p LʋtLt݅pL>t݅pݝ݅p ݅p ݝxL݅p >ݕ݅x݅ܥݝhˋ\ܥx͋ܥ΃ʉ݅h΋ܥhZ ZZ(ZZ0ZZ8@9\`MDžEDž)DžDžӥӥӥ9udv >9% ‹ꋅ ЋtMt tDD1tݝ9ݝxD݅D91΋d݅xݝhܥܥx݅hYY ɋYܥh˃ɉˋY0Y(YY8@9dUU?} cU ^MDžx|xEDžpDžl)DžhdDž`Dž\DžXDžt|xӥpӥlӥhӥdӥ`ӥ\ӥX9tMPtt 71%x Ћh苕l ‰`d ‰X\ ‹p NjuD>D>ݝPDݝHݝ@ݝ8ݝ0DDDݝݝ(>ݝ ݝD>ݝ݅Pܥ@݅P݅H܅@܅8ݝDݝ݅Hܥ8ݝ݅(ݝ݅0ݝ݅ ݝ݅ܥ ܅ܥ0ܥ(ݝ݅܅ݝ݅ܥݝ݅ܥݝ݅ܥݝX݅ܥݝH݅ݝ@݅ݝ8ܭɋPܭ݅ܥݝ ݅ݝ(݅܅ݝPݝ`ݝ0݅݅ܥ܅݅X܅`܋ܥ܋݅H܅P܋܋_݅Xܥ`_@݅HܥP_H݅@͋tܭ@̓t|_݅8ܭ8_P__X_ ݅0ܥ0_`_(݅(ܭ(_h_0݅ ܭ _p_8_x9tPzU}BBB___! ME)ыUMMe E ػĞB0BB(]B8B ]BB]EE]EEeeeeXX X(XX0pMDžEDž)DžDžӥӥӥ9MT 07% ‹ ֋ Ћu DTDD>ɋݝݝDݕ݅>݅ݝxܥܥ݅xܥZ̋ܥx˃ɉZ ZʋZ(ZZ0Z8@9TUuEFF0]F ]FF8EF(FE]eeEeX XXEu)ЉEMe E ػĞFF(F`ݝFhF@FHݝFPF ݝݝFXݝFݝF0FݝF8ݝFpNxݝݝ݅ܭݝ݅ݝ݅ݝ݅܅݅ݝ݅܅܅ݝݝ݅ܥݝ݅ݝ݅ܥ݅ܥܥݝ݅ܥܥݝ݅܅ݝܭEݝ݅ܥ݅ݝݝ݅ܭݝݝݝ݅ܥ݅݅ܥܥ݅܅ݝݝ݃ܥ݅܅܋݅܅X݅ܥX@݅ܭ݅ܥX݅XP݅X݅ܭܭܥX ݅܅XHXXX(X`X0݅ܭXhX8XpGUB@ݝxBPBݝXB8ݝpݝH݅xB`BHܭxݝ8݅pݝ`ܭpBXB(BhݝPBpB BݝhݝBB0ݝ@ݝ0Bxݝ(ݝ ݅h܅݅`݅ݝ݅XܥXݝܥhݝ݅Hܥ`݅Pݝ݅8܅ܥPݝH݅0܅ݝ݅@ݝ8݅8ܥܥHܥ@ݝ݅0ܥݝ݅(ݝ0݅ ݝ(ܭ ˋEܭ(݅ܥݝ݅܅ݝݝ ݅݅ݝ@ݝP݃݅8݅H܅Pܥ@܋݅8܅@XHX݅HܥPX@݅0ܭ0X݅(XXP݅܅ ݅ܭ(ܥ X ݅ܭXXX(݅ܭX`X0݅ܭXhE@]@0@ @]@8E@(@΋EE]eeEeX X}GXݝݝXG8G@ݝH݅GݝxGHݝ8݅xG(GPݝpݝ0݅Gh݅pG`ܭxݝ`G0ݝhݝPGpG GGݝ@ݝ(Gx݅hݝ ܥpܥh݅Xݝ݅`ܥ`ܥXݝ݅P܅@ݝ݅HܥHݝ݅Pܥ@ݝ݅8ܭ8ݝݝp݅0݅0܅ ܥ ݝݝ`݅݅(ܭ(ܥݝݝxݝX݅ܥE݅݅ݝhݝ݅ܥ܅ݝ݃݅ܥ݅܅ܥ܋XH݅܅X݅ܥX@݅ܭX݅xܭxXPXXX݅pܭpX ݅`܅hX`݅`X(ܥhX0݅XܭXXhUEuxE~uPx ~ V&'UWVS| [U}w0}4} }/}c}T1e[^_]É} 6;U M)V$ػĞ} ݝ0 U  MDž$ƋUDž$)щ($Dž(DžDž DžDžDž ,ӥӥӥӥӥ ӥӥ9   ݅0  ʋ$ Љ ֋ ׉ Љ} э} ݅0ɍ%9 1ꊍ≕;| v؉%1Ҋ1 0 Gꊍ ‹MȉM@AXYE ʋ M;BFRV ]]ɋ U  D \ 22D2\2F9M;U7M)ѺW$ػĞE}Džӥ);@XEE؉1Dž% ꊍ≕9| ؋0 0u ʊꊍ⋍  M@BRʋBP9V]]ɋU G;22D2\2UJك̞UBBBZZZ{PM Q}WUR7b} b M)ѺMMR$ػĞE@(@@ ]@0]]@8@@EEݝxEe݅xeeXܥxXX X(XX0X8eMDžDžӥy)Dž9EEEu؋}܋1Dž% ꊍ≕911Ҋ7}%1 ‹ꊍ  Ήu1yA0x @ɉXɋ ‹EYMʋpx Bɉrz BP9݅ݝʋA9IUDžDžDžӥ)9EEEu؋}܋1Dž% ꊍ⋍91Ҋ7}%1 ‹ꊍ⋍ ȋuApBʋx rʉz P9]݅ݝʋB96} Xu)։uMW$ػĞE@@H@(ݝp@`ݝhݝX@Xݝ`@h@PݝH@8@@@ ݝ@@ݝPݝ@@0@pHxݝ8ݝݝ0݅pܥh݅p݅X܅hݝ ݅PܥPݝ(ݝ݅8ݝ݅`݅H܅ݝ݅0܅ݝݝ݅@܅ݝ݅(܅݅ݝ݅ܥHܥ`ܥXܥ@ܥ8ݝ݅0ܥݝh݅(ܥݝ`݅܅ݝ݅ ܭ ݝݝxݝX݅݅ܥݝ݅ݝp݅݃ܥ݅܅܋݅܅X݅ܥX@݅ܥXH݅ܭX݅xܭxXPX݅h܅pXX݅hX ݅`ܥpܭ`X(X`X0݅XܭXXhX8XpXxكĞju})։uMe E ػĞG(GG8]G0]G GG]EE]EeEeee__(__ __0_8MAݝ@A0A Aݝ8A8݅@A(A݅8]ܥ@ܥ8EeYY YY(YY0Y8:M)ыUMMe E ػĞBB(B`ݝ0BhB@BHݝBPB ݝ(ݝBXݝ BݝB0BݝB8ݝBpJxݝݝ݅0ܭ0ݝ݅ݝ݅ ݝ݅܅݅(ݝ݅܅܅ݝݝ݅ܥݝ݅ݝ݅ܥ݅ܥ(ܥ ݝ݅ܥܥݝH݅܅ݝ8ܭݝ ݅ܥ݅ݝ0ݝ݅ܭݝ@ݝ(݅ܥ݅ܥݝ݅݅ܥ܅ݝݝP݃ܥ݅H܅P܋݅8܅@Z݅HܥPZ@݅0݅8ܭ0ܥ@Z݅(ܭ(ZHZ݅ ܭ ݕ0Z ݅܅Z`݅Z(ܥZPZ0݅ܭZXZhZ8ZpZxE@(ݝ@@ݝ@Pݝݝpݝ@8@p@@@Hݝݝ݅݅@`ܭܭ@hݝ@X@ @ݝxݝ@0@xݝݝݝݝ݅݅݅ݝ݅pܥ܅x݅xܥݝ݅ݝݝ݅ܥ݅܅pݝx݅ܭܥݝ݅܅ܥܥݝxݝ݅ܥݝp݅ݝ݅ݝܭ݅ܥxܭݝ`݅܅xݝ݅݅ݝݝh݅ݝ݃ܥ݅܅܋݅܅X݅ܥX@݅ܥXH݅ܭX݅XXP݅x܅ܭX ݅pXX݅xX(݅hܥܭpܭhX0݅`ܭ`X`mUBݝPB0B BݝHB8݅PB(B݅H]ܥPܥHEeZ ZZ(ZZ0ZZ8E@ݝ0@X@@ݝ@8@(ݝ@p@ ݝ@xݝ(ݝ@Hݝݝp݅0@`@hܭ0݅(ݝ @Pݝ`݅ @@܅ݝݝh@0ݝX݅ܥ ܥ(݅ݝ݅ݝP݅ݝH݅܅ܥܥݝ@݅ܥܥݝ8݅pܭpݝ݅h܅Xݝݝ݅hܥXݝ݅`ܥݝ݅`܅ݝݝ݅@݅P܅H݅8ݝݝܥ8݅PܥHݝ݃ܥ@݅݅܅ܥ܋݅܅X@X݅ܥXH݅ܭX݅ܭXPX݅XXX ݅܅݅ܭܥX(X`X0݅ܭXhX8v]]ʉu؉}]]]&UWVS|[U}w,}.}v}~}} 1e[^_]E;Uu)։P$ػĞUDžэz)ЋUӥM Dž9t&1Ҋ7Dž%1 ꊍ≕91Ҋ>%9 ‹ꊍ ‹M <ыM 4ɋM ʉM ˋɋM u <֍4ƍэ@9 NjUM ɋ F ɋ9.؋} WuVM QURb;UM)щR$ػĞ}u DžӥM)Dž9]1Ҋ1 71 ʊӭӥ;%1Ҋ1 0 Gꊍ ;u ֋u ֋u4ɉu ʋl ƋM} B9&Jlك̞UM BAZYP}  })׉}MV$ػĞM EAA@]@]]A@EE]EeEeeeXYXYXY[}DžDž)ӥDž91Ҋ>Dž%9 ꊍ⋍91Ҋ7}%1 ‹ꊍ ‹ u ׉4ƋM <эɋF9\B9hUDžDžDžӥB)щ91Ҋ7Dž%1 ‹ꊍ≕ 9"1Ҋ7}%1 ‹ꊍ ⋍ ʍ ׋} 4׋} Nj} 4Njɋ  } ׉uM 4ƍ<эG9B9et} NM)ѺMMR$ػĞ} EG Gݝ@0ݝݝG(ݝG0@(ݝpG@ @ݝG@ݝxݝh@@8O8ݝ`ݝ݅ܥݝX݅ݝH݅xܥx݅܅ݝ݅`ݝPݝ@݅݅p܅ݝ(݅X܅@ݝ8ݝ0݅h܅ݝ݅P܅8݅ݝ ݅ܥpܥܥܥhܥ`ݝp݅Xܥ@ݝP݅Pܥ8ݝH݅0܅ ݝh݅HܭHݝxݝ`ݝ@݅(݅0ܥ ݝ݅ݝX݅݃ܥ(݅܅܋݅p܅x݅ܥX ݅pܥx_ ݅hܭhX݅`ܭ`_݅P܅XX(_(X݅H݅PܥXܭH_X0_0X݅@ܭ@_X8_81كĞu)֋U uMe E ػĞMBBB]AA]A]EE]EeEeeeYZYZYZKM uAFݝ0FAݝ(݅0AF݅(]ܥ0ܥ(Ee^Y^Y^5u} )֋EuMe E ػĞG@0ݝXG0@ G ݝ@(@ݝPݝ@G(ݝHGݝ8@@ݝ0Gݝ(@8O8ݝ ݝ݅XܭXݝ݅@ݝ݅Hݝ݅0܅ ݅Pݝ݅(܅܅ݝݝ݅ܥ(ݝ݅ݝ݅0ܥ ݅8ܥPܥHݝ݅ܥ@ܥ8ݝ0݅܅ݝ ܭݝ݅ܥ݅ݝݝ݅ܭݝ(ݝ݅ܥ݅ܥݝ݅݅ܥ܅ݝݝ8݃ܥ݅0܅8܋݅ ܅(݅0ܥ8X ݅݅ ܥ(ܭ_ X݅ܭݕ_݅X(_(X݅܅ܭ_X0݅ܥ_0X݅ܭ_uU }G0ݝU ݝBݝݝG(B(ݝG8G B ݝݝ݅݅B0GݝBGBݝxݝGB8ݝݝ݅܅ܭܭ݅ݝ݅ݝ݅ݝ݅xܥܥݝݝ݅݅܅xܥݝ݅ܥܥݝ݅ܭܥݝ݅܅ݝ`ݝ݅ܥݝX݅ܭ݅ݝx݅ܭݝPݝp݅ܥݝH݅܅ݝh݅ݝ݅ݝ݃ܥ݅܅܋݅܅݅ܥ_ ݅ܥZ ݅x_݅pܭxܭpZ݅`܅h_(Z(_݅X݅`ܥhܭXZ݅P_0Z0_݅HܭPܭHZ_8Z8} UGBݝ@BGݝ8݅@GB݅8]ܥ@ܥ8EeZ_Z_Z_U Mݝ B(A ݝBBݝA8AݝB8ݝݝB ݝ]݅ A0B0ܭ ݅ݝA(]݅AB܅ݝ]A]݅ܥܥ݅ݝ݅ݝx݅ݝp݅܅ܥܥݝh݅ܥܥݝ`EmݝEEݝݝEeݝEܥݝE܅ݝݝ݅h݅x܅p݅`ݝݝܥ`݅xܥpݝ݃݅ܥ݅܅ܥh܋݅܅݅ܥY Z ݅ܭY݅ܭZ݅Y(Z(Y݅܅݅ܭܥZY0Z0Y݅ܭZY8DUWVS[Ã}#DžM}⍍u }w0}2}}}1}e[^_]Á w,jMQWuVhi1e[^_]à RbttQj}WRuVi_R|O UMك̞BYs;}v U)V$ػĞ} ݝ ) U DžEM)DžDž DžDžDžDžDžӥӥ ӥӥӥӥӥ9 ݅  ʋ  ЋL ыH ЋM ֋ ׉  ȋE݅ LɋLLLݝ݅ ݝݝ݅ Lݝ݅ ݅ ݅ 0݅ L݅ L0̋ ɋHݝ݅ L0ˉH 0H݅ ݝP݅ ݅ 8L8 0ݝݝݝ݅ L0݅ 0݅ݝݝ݅ܥ݅ L0݅ݝ݅ܥ܅܅ݝ݅܅Pݝx݅ܥݝp݅ܥݝh݅PܥݝP݅܅ݝ`݅ɋܥݝX݅ܥݝP݅xܥxݝ݅pܥpݝ݅ܭݝݝ݅݅h܅`ܭݝݝ݅X݅hܥ`ݝݝݝ݅P݅PܥP܅P݅܅܋ܥX܋܋܋݅܅݅ܥ_ ݅ܥY ݅ܭ_݅ܭY_(Y(_݅ܥY݅_0Y0_݅ܭܭݕYݕ_8Y89j}WVMQb4 Py U)UMV$ػĞEM@ @@0@@8YYYH U)UMV$ػĞMA A@A(ݝXAXݝA`ݝxA0AAݝpA8ݝݝAPAhݝhAxAp݅X܅ݝ`݅ܥݝXϋEܥ݅pݝ݅x܅`ܥxݝ@݅hݝP݅pݝPܥ`݅Pݝ݅@܅ܥPݝHܥhݝݝ@݅H݅XܥH݅ܥ܋ܥXɀG܋X ݅ܭX݅@ܥ@X(XX0XX8JDžHEMH)Dž@<Dž8DžDLӥ@ӥ<ӥ8H9D{DD ݅ } ʋ@H8ꋍ< D݅ ϋD$݅ % Ћ苕 , Ɖ( Nj $U݅݅ L  ݝxݝp݅݅ Lɋ,ݝhݝ`,,݅L݅݅ ʋ(ݝX݅L: (݅ :(݅݅ݝݝP݅݅ 2L2ɋ$L ˉ$$ :ݝHݝ@݅݅L: ݝ8ݝ0݅xܥh݅L ݅xݝ݅pܥ`܅h݅p܅`ݝ݅Pݝ݅Xݝ݅Hݝ݅ܥH܅ܥXܥPݝ݅@܅0ݝ݅8ܥ8ݝ݅@ܥ0ݝ݅ݝ݅ɋܥܥݝ݅ܭݝ݅ܭݝݝݝ݅݅݅܅ܥݝݝݝ݅݅ܥ܅݅܅܋ܥ܋݅܅܋܋_݅ܥ_@݅ܥ_H݅ܭ_݅ܭ_P__X_ ݅ܥ_`_(݅ܭ_h_0݅ܭ_p_8Wx9ݝ$PjQEPLM֋8M$D9ݕxثĞݝ ك̞ك(ɋMWݝݝDM@D܍܍܍܍\X݅x݅ʃʃJ܍x܍HAʋ$ɋ$҉ك$Qɀt9\9 1e[^_]ËU DžE)DžDžDžӥӥӥ9} %݅1 0 ʋꋍ M݅݅ LʋL݅ 1ݝ݅L݅L1݅ ݅ ݝݕ݅݅ܥݝˋܥ̓ܥΉʋ݅ܥ_ __(__0__8@9rʃHDžE)DžDžDžDžDžDžDžӥӥӥӥӥӥӥ9eM 27% Ћ苍 ‰, Ƌ  ( $  MD,,,Dݝxݝ`DD919ݝpݝhD1ݝXݝ($(($$1D1ɋ  ݝP ݝHݝ@9Dݝ0݅xܥh݅x݅p܅hݝ݅pܥ`܅`ݝ8ݝ݅Pݝ݅XD9ܥXݝݝ݅ܥH݅H܅ܥPݝ݅@܅0ݝ݅8ܥ8ݝ݅@ܥ0ݝ݅ܥݝh݅ܥݝX݅ܭݝP݅ܭݝ8݅ݝ0݅܅ݝHݝpݝ`݅ܥ݅ݝ@݅ܥ܅݅h܅p܋ܥ܋݅X܅`܋Z݅hܥp܋Z@݅Xܥ`ZH݅PܭPZ݅HܭHZPZZXZ ݅@ˋܥ@ɃZ`Z(݅8ɉܭ8ʋZhZ0݅0ܭ0ZpZ8Rx9ݝ0U DžE)DžDžDžӥӥӥ9cM  ʋ ֋M D91DݝDݝD1݅9͋݅ݝܥܥ݅ܥ_ __(___0_8@9 E@ك$Pɀt8$\9UU)DT DT 1;|e[^_]Ã}M)ȾV$ػĞ}GG(G@ݝ G`ݝGhݝGPGHG ݝGXݝGݝG0GݝG8ݝGpOxݝݝ݅ ݝݝ݅ܭ ݅܅ݝ݅܅ݝ݅ݝ݅܅ݝݝ݅ܥ݅ܥ݅ݝ݅ܥܥܥݝ݅ܭܥݝp݅܅ݝHݝ`݅ܥݝ8΋E݅܅ݝX݅ܭ݅ݝxݝ0݅݅ܥܥݝP݅ݝhܥݝ@݃ܥ݅p܅x܋݅`܅hX݅pܥxX@݅`ܥhXH݅XX݅PܭPܭXX݅HXX݅8X ݅8܅@ܭHܥ@XPX(X`X0݅0ܭ0XhX8XpXx݃ك̞ك(Uݝݝ݃xuDD܍܍܍܍\E݃xʃ\ʃIM܋xJFʋMɃxvOك$ݞVݞ}AQTQDT ~}M)ʿW$ػĞE@0@@(]@8@ ]@@̋E]EE]EEeeeeXX ʾ X(XX0XX8Eك̞@8@0@@͉Ƀ8݃ك(ˀp/XX0@X8Xك$X@PXH}AQTQDT ~_MVMu$AAA؋ɃVك$Vݕ(ɋ,(V~^ ^(^0^8}tWك̞EM@ك$YQY}Mك$wqYaكĞ맋UB]BhB`ݝBX]]B8]BPB@BHݝpEEm]mB0B(ݝxBpB B]ݝhBBxݝ`ݝXݝP݅eE܅Eݝ݅x܅hݝHݝ8݅peEݝ0݅xܥhݝ@Eݝ(ܥpeݝ ݅`܅Heݝ݅`ܥHݝ݅X܅ݝ݅PݝܭPE݅Xܥݝ݅@܅8ݝ݅0ݝ(݅@ܥ8ܥ0ݝ݅(ܥ(ݝ݃݅ ܅(܋݅܅X݅ ܥ(X@݅ܥXH݅X݅XܭܭX ݅܅XX݅X(݅ܥܭXPX0݅ܭX`XhX8ݕUEBB0]B BB8]EB(BE]eeEeX Xit&UWVS [jJM}uU A~(~b=PRVQWURE PURG e[^_]PRVQW}Wu VMQie[^_]ÐPRVQWUR} WMQ1e[^_]ÉPRVQWuVE PURʲe[^_]ÉUWVS [úIM}uU A~(~b=PRVQWURE PURwe[^_]PRVQW}Wu VMQe[^_]ÐPRVQWUR} WMQe[^_]ÉPRVQWuVE PURe[^_]ÉUWVS [ IM}uU A~(~b=PRVQWURE PUR7e[^_]PRVQW}Wu VMQ e[^_]ÐPRVQWUR} WMQe[^_]ÉPRVQWuVE PURjge[^_]ÉUWVS [ZHM}uU A~(~b=PRVQWURE PURe[^_]PRVQW}Wu VMQ9xe[^_]ÐPRVQWUR} WMQ:e[^_]ÉPRVQWuVE PUR4e[^_]ÉUWVS [êGM}uU A~(~b=PRVQWURE PURG:e[^_]PRVQW}Wu VMQe[^_]ÐPRVQWUR} WMQ*`e[^_]ÉPRVQWuVE PURe[^_]ÉUWVS [FM}uU A~(~b=PRVQWURE PURG#e[^_]PRVQW}Wu VMQye[^_]ÐPRVQWUR} WMQHe[^_]ÉPRVQWuVE PURe[^_]ÉUWVS[BFu M)E}9U ~ 1҉<9p݃݃]]@@8@H@]@`@@`@ ]@(]]@X@0@x]@@`P`X`(ݝ0@p]@P]@h@@`0`h]EE`p]EEݝpEeݝPEeݝHEݝhEm]ݝ`ݝ@EEEe݅0ݝxݝX݅0EE݅p܅xMeMMMXEeX@݅pܥxXH݅hܭhX݅`ܭ`XPX݅P܅XXX݅PX ݅HܥXܭHX(X`X0݅@ܭ@XhX8XpXx;<vMA׉MM)9E9[^_]ø1҉<9}&@@@8̃@(@@ @0]`(`0EeXX XX(XX0X8@;<a UWVS[bBu M)E}9U ~ 1҉D9B݃݃]]@@@ @8@@]@`@x@]@0]@(@X`(`0]@P@H`P]@H`Xݝ8@`@p]@h`h]@``p]Ee]EEݝpEeݝPE܅8ݝhEmݝ`ݝHEEEe]Eeݝx݅8ݝXeEEMMMM݅p܅xXEeX@݅pܥxXH݅hܭhX݅`XP݅PXܭ`ܥXX ݅P܅XPXX`X(XhX0݅HܭHXpX8Pxݝ`;DݝHMA׉MM)9Egļ[^_]ø1҉D9}t&@@@8̃@(@@ @0]`(`0EeX XX(XX0XX8@;DkUWVS[>MUMEM)Ѻ9UKMEE&E ~ MEEM܉})MܺMEEEEEE}eeeeeuЉU9}`݃݃ɋUݝݝu؋U M܊:>%} ЋuU ‰llMЋU} h ƋU  dU Dlll 2]]DD lD:]]]D2:ldlh2hhd]D2d]D Eݝx2ݝpD2mݝpEEݝPEݝXEe ݝHEeݝ@Eeݝ8Eݝ0e݅x܅peݝ`݅pݝ ݅pݝ(݅@ܥxܥ@ܥpݝ݅X܅8ݝ݅Xܥ8ݝ݅PܭPݝݝ݅H݅(ܭHܥ(ݝݝ݅0݅0܅ ܥ ݝݝݝ݅`݅`ɋ݅܅݅܅_݅ܥ܍܍_@݅ܥ_H݅ܭ܍_݅ܭ܍WP_݅ܥ_X_ ݅ݝ_(_`݅ˋEܭɃܭˉE؋M_0݅ܭWh_8Wp_x9MݝݝM苽AU uMEM)U u9}Ĝ[^_]ÊMDžM)DžDžӥ 9L}쉅|| % ‹ ֋ \E   DD\\\ݝ0ݝD0݅D݅ݝܥܥ݅ܥ__ _(_ɋʃɉ__0_8@9t&'UWVS[G7MUMEM)Ѻ9U(MEE&E ~ MEEM܉})MܺMEEEEEE}eeeeeuЉU9}=݃݃ɋUݝݝu؋U M܊:>%} ЋuU ‰ldMЋU} ` ƋU  \U Dɋlll]]D]]D D:2 D2:]ݝpd`\dd``2\D2]\ ]ݝx2D ݝhEeEEEݝPEeEݝpݝHD2ݝ@Eeݝ8Eݝ0݅peE܅peݝp݅x܅hݝ(݅pܥpݝ ݅xܥhݝ݅@ܥ@ݝ݅8ܥ8ݝ݅PܭPݝ݅HܭHݝݝ݅0܅(ݝݝ݅ ݝ݅0ܥ(݅܅pݝ݅pܥ݅܅܍ܥ ܍݅܅܍_݅ܥ܍_@݅ܥ_H݅ܭ_݅ܭ_P_ݕ_X_ ݅̋EܥˉE؋M_(݅ܭ_`_0݅ܭ_h_8WpWxɃ9Mݝݝ&M苽AU uMEM)U u9}Ĕ[^_]ÊMDžM)DžDž ӥ9 L}쉅| | % ‹ ֋dE  DDdddݝDݝ0D0ݕp݅݅ݝܥܥ݅ܥp_ˋ ܥ̃ʉ __ _(__0_8@9 UWVS[/MUMEM)Ѻ9UMEEuE ~  WMEEM܉})MܺMEEEEEE}eeeeeuЉU9}ك$݃݃ݝhUݝxݝpd`}؋Ed M܊07%u ‹}E Љ4EMЋEu щE ֋E ЉEE Ћ4ݝ(݅(UʋM}]݅(䍳EݝPm`݅hܭhݝ@]&ݝHݝX݅h&ܭh݅P܅X܍x܍x݅h܍pX݅PܥX܍xX@݅hXH݅HܭHXEXXP݅@܅hɋUmɃU؋MX ݅hXX݅@X(ܥhܭhX0X`XhX8XpXx9M؉`MU MAu}MU MU)Ѿ}9u[^_]ÊMEMuU)EEEUe}u9Ef}쉅<ك$كuE< %M ‹u ֋E 4Mu  ֋4‹M̓M̋UW_ _W(__0_8@9U:|UWVS[*MUMEM)Ѻ9UMEEt&E ~ jMEEM܉})MܺMEEEEEE}eeeeeuЉU9}ك$݃݃ݝXUݝhݝ`pT}؋Ep M܊07%u ‹}EċM щE$EM щE ֋E ЉEE Ћ$̋Uu‹}ݕ]]㍳EEeeݝ8݅⋍T݅XܥXݝ0݅XܭX]ݝ@݅XܥXݝH݅XܭX݅H܅Hݕ܍hܥ܍`܍h]݅8܅@܍h݅XY݅8ܥ@Y@݅XYH݅0YEϋTܭ0ɋEmσEYQPYX_ ݅XɋM&ܭXݝ0_(Ee_`_0_h_8_p_x9M؉TMU MAt}MU MU)Ѿ}9u[^_]ÊMEMuU)DžxEEUӥx}|9EZ}쉅,ك$uE, %M ‹u ֋E $|x  ֋$‹M̓M̋UW_ _W(__0_8@9U:xU ]ÍUWVSL[ú%M 8u}Ee9EE ثĞ]D]+UM҉U)U}}ك̞LU؉MЋUك(ɍ U6E]ȍPU]4Nj}E)ЉEEAD}MMMMT^Q}EEɃ}MMM0؃L[^_]ÉUWVSd['$}MuU }9 )V$ػĞ ݝ~&  DžODž)DžDžDžDžDžDžӥӥӥӥӥӥӥ;U$M< ݅ % Ћ苕 ‹t p Ƌ ‰l hU ݅ Lɋݝݝ݅݅L݅L ݅ ݝ ݝttt݅݅݅ ݅ 2݅L2ˋppL ʋplݝ݅ lhhݝ݅L ̋l ݝ݅L hݝ݅ ݝx݅L ݅ܭݝ݅܅ݝX݅ݝ`݅ܥܥݝP݅ݝ݅xݝH݅ܥܥݝ@݅ݝ0݅Hݝ8݅܅ܥܥxݝ(݅ܥݝ݅`܅@ݝ݅`ܥ@ܥHݝ݅XܭXݝ݅PܭPݝݝݝ݅8݅0ܥ(݅8܅(ݝݝ݅݅݅܅ݝ݅܅܋ܥ0܋܋܋_݅ܥ_@݅ܥ_H݅ܭ_݅ܭWP_݅ܥ_X_ ݅ܭ_`_(݅ܭWh_0݅ܭ_p_8_xݝݝU$9d[^_]9)eEػĞ ݝ~  DžODž)DžDžDžDžDžDžӥӥӥӥӥӥӥ;U$(Mʉ ݅>9% Ћ Njl h ljd lj` \} ݅ ݅݅Lݝ݅ ݅LݝLݝ ݝݝhlhhl݅Ldl`d݅݅ 7ݝL7d݅݅ 7L7ɉ``\ݝݝ݅݅ L݅ ݝݝx\\݅ ݅L݅ݝp݅ܥ܅݅܅ݝP݅ܥݝH݅܅ݝ@݅ܥݝ8݅ܥݝ0݅ܥݝ݅܅pݝ(݅xܥxݝ ݅ܥpݝ݅@ɋܥ@ݝx݅8ܥ8ݝh݅PܭPݝ`ݝH݅H݅0܅(ܭHݝXݝ݅ ݅0ܥ(ݝ@ݝpݝP݅݅ܥ܅݅x܅܋ܥ ܋܋܋݅h܅pZ݅xܥZ@݅hܥpZH݅`ܭ`Z݅XܭXZPZZXZ ݅PܥPZ`Z(݅HܭHZhZ0݅@ܭ@RpZ8ZxݝHꀉu$9 Dž$O$Dž)DžDžE$ӥӥӥ$ 9ыU<  ݅%$ ‹ꋍ ы u ݅݅ ݅LˋLʉ ݅ݝ݅ ݅ ݝ݅LL݅݅ݝˋ ܥ̃ܥΉ ʋu$݅ܥ__ __(__0_8@9 ud[^_]É ~ .DžODž)DžDžDžDžDžDžӥӥӥӥӥӥӥ;U$nMʉ >9% Ћ Njt pt lt ljh d} DݝtDDݝݝݝݝDlpllphpdh7D7hݝDddݝD7݅ݝݝxDܭݝ݅܅7ݝPݝX݅ܥݝH݅ܥ݅ܥݝ݅xݝ@݅ݝ(݅@ݝ8ܥݝ݅X܅8݅ݝ݅Xܥ8ݝ0݅ݝ݅P܅ܭPݝ݅Hݝ ݅ܥܥ@ܥܥxݝݝܭH݅0ܥ ݝ݅(݅0ݝݝ݅݅܅ ݅܅ݝ݅܅^݅ܥ^@݅ܥ܋܋^H݅^݅܋ܥ(܋^݅̋ܭ΃ܭ̉^ ݅ɋU$ܭܥVP^(݅ܭ^X^0݅ܭ^`Vh^8^p^xɃ9ݝݝ_DžODž )DžDžӥ ӥӥ;U$`} 8>% ‹u ꋍ ы  ݅L݅ >݅L݅L݅݅ ͋ݝL>݅ ݝ݅ ݅݅ݝܥܥ݅ˋܥʃZ Z̉E$Z(ZZ0ZZ8@9DžODž)DžDž ӥӥӥ ;U$u >9% ‹u ꋍ  Dݝ>DݝD>݅D΋݅ݝܥܥ݅ܥYY YY(Yɋ}$Y0Y8@9$ ~ t& xEOEE)EEEEEEEeeeeeeẻU;U$uuU M:>%} ЋU NjMt ‹}  p}ԋp ljl} ljh} d} ]DttDDʋt]D]]]phlplDlhDݝdhdd]]]7DݝxEeEEݝXEeEE]D7ݝPE܅ݝHEeݝ@Eeݝ8݅eݝE܅xݝ0Eeݝ(Eܥxݝ ݅HܥHݝ0݅@ܥ@ݝ ݅XܭXݝ݅PܭPݝݝݝ݅8܅0݅(ݝ8݅8ܥ0ݝ(ݝ݅݅ ܥ ܅݅0܅8܋ܥ(܋݅ ܅(܋܋Y݅0ܥ8Y@݅ ܥ(YH݅Y݅YϋEܭ˃ܭ͉E΋U$Y ݅ܥYPY(݅ܭYXY0݅ܭY`YhY8QpYx9UݝDžODž)DžDž ӥӥӥ ;U$6u >9% ‹ ꋍu  D>DݝDݝD>ˋ݅݅ݝܥܥ݅ܥ_ __(__M$_0_8@9UWVS[E }MU$9 )ΉP$ػĞ ݝ~ t&  DžG)DžDžDžDžDžDžDžE(ӥӥӥӥӥӥӥ9 07% ‹ꋍ щ ։|}u Љxύ<΋Mu4݅ ֋}݅ ݝx݅ ׋}ݝp݅ ݝh݅ ݅ 񋵈݅ݝ`݅ ݅݅݅ݝX݅ ͋ ʋݝp݅ ɋ|ݝP݅ ݝH ʋxݝ@݅ ݝ8݅p܅h݅x݅ݝ ݅Xݝ(݅x݅pܥhݝ݅Hݝ݅`ݝ݅pܥPݝ݅P܅pݝp݅8 ܥHܥ`ݝ݅@ܥXܥ@ܥ8ݝ݅(܅ݝ݅ ܅͋4ݝ݅(ܥݝx݅ ܥݝp݅ݝ݅ܭݝݝݝh݅܅݅ܥ݅pݝݝ݅p݅܅݅܅܋ܥ܋܋܋݅ܥU(9X ݅ܥ_ ݅ܭX݅ܭ_݅x܅X(_(X݅p݅xܥܭp_X0_0X݅hܭh_X8_8BČ[^_]9T)ΉP$ػĞ ݝ~ > GDžDžDžDžDžDžDžDž)ӥӥӥӥӥӥӥM(9 ݅>9% ‹ꋅ щ щEu ׉<ȍΉLEM݅ ݅ ݝ݅ ݝ݅ ݅ 񋵨݅ݝ݅ ݅ ݅ݝx݅ ͋ݝ݅ ΋ݝp݅ ݝh݅ ΋ݝ`݅ ݝXݝP݅܅݅h܅݅ݝ@݅ܥ ݝ8݅xܥxݝ0݅ܥݝ(݅pܥpݝ ݅ܥhݝ݅`܅Pݝ݅XܥXݝ݅`ܥPݝ݅0ݝX݅@܅(ݝH݅@ܥ(ˋLܥ0ݝ(݅ ܅ݝ@݅8ܭ8ݝ`ݝ8ݝ ݅݅ ܥ݅ݝPݝ0݅ܥ܅݅X܅`܋ܥ܋݅H܅P܋܋݅Xܥ`_ ݅HܥPU(Y ݅@ܭ@_݅8ܭ8Y_(Y(_݅(܅0݅(ܥ0Y_0Y0_݅ ܭ Y_8Y89hDžGDž)DžDžӥӥӥ;U(( %݅1 0 ʊ ֋ ыul݅ ыE ݅4E݅ ЋE݅ ЋEl݅ ЋE ΋Eˋ ЋE ̋Uݝ݅ ݝU;}(݅ ʋ݅݅ܥܥݝ݅ܥZ^Z^Z^nČ[^_]É ~ t& GDžDžDžDžDžDžDžDž)ӥӥӥӥӥӥӥM(9 >9% ‹ꋅ щ щEu ׉<ȍ΋uDEM֋ݝxЋEݝݝx݅xʋݝpݝ8ݝh݅xɋݝpʋݝ`ݝP݅ݝH݅܅xܥxݝXݝ0ݝ(݅h݅pܥpݝ ݅Xݝ݅`ݝ݅pܥ`܅pܥhݝp݅HܥXܥHݝ݅PܥPݝ݅8܅ ݝ݅0܅ݝ݅8ܥ ݝ݅0ܥݝݝ݅(ݝܭ(D݅݅ݝ݅܅ܥݝ݅pݝݝ݅p݅܅܋܋݅܅݅ܥ܋ܥ_ ݅ܥ܋Y ݅_݅Y݅ܭɋU(܅ܭ_(Y(_݅݅ܥܭY_0Y0_݅ܭY_8Y89}GDžDžDžDž)ӥӥӥM(9 07% ‹ꋍ щM ݅9%M ‹}EM щEȉMċE؋uԋM щ}EuE}Ћu MM ׉}<ȍΉdMEu]u]]uɋu]ˋuݝu]]ɋuݝxݝpݝhEEݝXEeݝPEeݝHEeݝ@Eݝ8݅eE܅eݝ݅x܅hݝ0݅pܥpݝ(݅xܥhݝ ݅HܥHݝ݅X܅@ݝ݅Xܥ@ݝ݅8܅0ݝ݅PܭPݝݝݝ݅(ݝ݅8ܥ0d݅ ݝ݅ܥ ܅݅܅܋ܥ(܋݅܅܋݅ܥ܋_ ݅ܥY ݅ܭ_݅ܭY_(Y(_݅܅U(݅ܥY_0Y0_݅ܭY_8}}Y89UfyDžG)DžDžDž E(ӥӥӥ 9 07% ‹ꋍ щM Ѝ%9M 1MU;}| ؉%1Ҋ1 0uE GMM ‹MȉMA@XYM ʋM MʋM;}BFRV9]]M} U}u ֋EU D\77D7\7uFu9U؁Č[^_]9)V$ػĞ|U}Mӥ|Džxx)щxG_;|}t&1Džtx% M≕p9t| ؋t0t 0tp ʋuxM M@BRʋtBtP9ta]]pU G;|22D2\2[EEeċUA)EuĉEU9u'}M1Ҋ7E%1M MMU9M}M1Ҋ7}%1M ‹uM Mu Ήuu1yA0xuċ} @ɉ}XE ‹EEEuYMʋ8@HBʉ:JZUBXUE9EDžt%9M MUU9ttt1Ҋ>t%9M ‹}ME MuB@8HJ:ZXtU@t9tvUEBU9EUWVS[uE9)¸щW$ػĞEEEEeMz)щ}U MM9Ml}M1E1 7u ʊMUmMe9u&}M1Ҋ7%1M ‹uME ‹ME< M 4ɋM ʉMM MMMMɋMM ʋ}M u <֍э4ƍʋuE@E9u.}M ϋUM Eɋu } ɋUE@E9U؁Č[^_]9)¸щW$ػĞ}|x|ӥxM DžttDžp)xt9popp1Ҋ>%9t 1ꊍ|≕l;pv%1Ҋ1 0up ʊtꊍ| ‹l G ;pu ɋu4ʉhu Ƌhlpl E} pFpx9pDEEыue)֍BEUEu9Uu䋻M1Ҋ>E%9M ‹}MU9}u܋M1Ҋ>u%9M ‹}ME M u <щ}؍ ֋}MԋM 4ljuЍE%9M ‹}MU9}}x&}M1Ҋ7}%1M ‹uME ‹ME 4׋} ׍uFuU9U|UEBU9E7UWVSl [òU}w0}4}:}/}}1e[^_]É} \;UEM)W$ػĞ} ݝHE  Dž@EM@)Dž8Dž4Dž0,Dž(Dž$Dž Dž<Dӥ8ӥ4ӥ0ӥ,ӥ(ӥ$ӥ @9<}<,݅H8@% ‹4 0 ‹( Ƌ$ ‰ U݅H ݅HLɋݝ݅H ݅HLݝ݅HLݝ ݝ݅H݅H݅H 2L2ʋ ݅H݅H ʋݝL ݅HL ʉݝ݅H ɋݝݝ݅H݅H ݅HL Lݝݝ݅ܭ݅܅ݝ݅ܥݝ݅ݝ݅ܥܥݝ݅ܥݝ݅ݝ݅ܥܥݝ݅ݝ݅܅ݝ݅܅ݝ݅ܥݝ݅ܥݝ݅ܭܥݝ݅ܭݝݝݝ݅݅ܥ݅܅ݝݝ݅݅ݝ݅܅݅݅܅ܥ܋ܥ܋_܋܋_@݅ܥ<D<_H݅ܭ_݅ܭ_P_݅ܥ_X_ ݅ܭ_`_(݅ܭ_h_0݅ܭ_p_8_x9<Pu VEP}Wt&JMUك̞AAAZZZ;UE)ЋU R$ػĞ} ݝhE Dž`EM`)DžXDžTDžPLDžHDžDDž@Dž\dӥXӥTӥPӥLӥHӥDӥ@`9\M8\`L7P%T ƋX D ‹H@ ƉU݅h݅h Lɉݝ8ݝ0݅h݅h L ݝ(ݝ ݅hL ݅h ɋݝ݅h݅h݅h ݅hL ˋLʉ ʋݝ݅h :݅hL:ݝ݅h ݝݝ݅8ܥ(݅h 2݅hݝ݅0ܥ L ݝݝ݅ݝ݅ݝ݅ݝ݅ܥ܅ݝݝ݅܅݅hL2݅8ݝ݅݅0܅(܅ ܥܥݝ݅ܥʋ8ܥݝ݅ܥݝ݅ܥݝp݅ܭݝh݅ܭݝPݝ`ݝH݅݅݅܅ܥݝxݝݝX݅݅ܥ܅݅܅܋ܥ܋܋܋݅p܅x\\Z݅ܥZ@݅pܥxZH݅hܭhZ݅`ܭ`ZPZZXZ ݅XܥXZ`Z(݅PܭPZhZ0݅HܭHZpZ8Zxꀉ8d9\DžEM)DžxtDžpDž|ӥxӥtӥp9|!}|u݅Hx% ‹p\ t ݅H ݅HLʋ\L\݅H \݅H ݝh݅HL݅HLݝ`݅H ݅h݅`ݝHܥhܥ`݅Hɋ|ܥHɃ_̉|ɋ_ __(__0_8@9|vU}  E  DžPEMP)DžHDžDDž@<Dž8Dž4Dž0DžLTӥHӥDӥ@ӥ<ӥ8ӥ4ӥ0P9LM@t&LH@8P% ‹D Ɖ4< ‹ Nj0 ƋU:ݝ(D:DݝD::ݝ ݝ:D::D:ˋݝ2ݝD ݝݝD2݅(ݝ݅ ܅ܭ(Dݝݝ ݝ݅ ܥ݅ܥݝ݅ݝ݅ݝ݅ܥܥܥݝ݅ݝ݅ݝ݅܅ܥݝ݅܅ݝ݅ݝ݅ܥܥܥݝ݅ݝ݅͋@ܭܭݝݝ݅݅ܥݝݝ݅ݝ݅݅܅݅܅ݝ݅܅^݅݅ܥܥ܋ܥ܋^@܋܋^H݅ʋLܭʃLT^݅ܭ^P^݅ܥ^X^ ݅ܭ^`^(݅ܭ^h^0݅ܭ^p^8^x9L@a}  M)ѺMMR$ػĞ}EG(GG0]G8G ]GG]EE]EeEeeeXX X(XX0XX8} E)ЉEMV$ػĞUB(BB`ݝBhݝBPݝBXB@BHݝB8ݝB ݝBBݝݝB0BpJxݝݝ݅ݝ݅ܭܭ݅ݝݝ݅܅ݝ݅ݝ݅ܥ݅ݝ݅܅ݝݝݝ݅܅݅݅ݝ ݅܅ܥܥܥݝ݅ܥܥܥݝ݅ܥϋEݝ݅ܭݝ݅ܭݝݝ݅ܥ݅܅ݝݝ݅݅ݝݝ(݃݅݅ ܅(ܥ܋݅܅X݅ ܥ(X@XH݅݅ܭܥX݅ܭXPX݅܅XXX`X ݅ܭX(݅ܭXhX0݅ܭXpX8XxBMDžƋUDž)щ|DžxDžӥӥ|ӥx9U<x1|% ‹M ݅hL1݅h ݅hL9݅hL݅h݅h ݝhL݅h 9̋<ݝ݅h 1݅h݅ݝXܥhܥ݅X_ˋܥX̓ɉ_ _ʋ_(__0_8@9<iMDžƋUDžx)щtDžpDž|ӥxӥtӥp9|ED&|p>t%x ‹M D1ݝh9ݝ`DD̋D݅hD91݅`ݝPܥhܥ`݅PܥPZZ ZZ(ZZ0Z8|@|D9|U&UT} E ADžxEMx)DžpDžlDžhdDž`Dž\DžXDžt|ӥpӥlӥhӥdӥ`ӥ\ӥXx9tM0txd7h%l Ƌp \ ‰`X UDݝPݝH D ɋݝ@D  ݝ8ݝ0 D Dݝ:D:ݝ( 2ݝ݅Pܥ@ݝ D ݝ݅H݅P܅@܅8ݝD2ݝ݅Hܥ8ݝ݅(ݝ݅0ݝ݅ ݝ݅ܥ ܅ܥ0ܥ(ݝ݅܅ݝ݅ܥݝ݅ܥݝ݅ܥݝ8݅ܥݝ(݅ݝ ݅ݝܭˋ0ܭ݅ܥݝ݅ݝ݅܅ݝ0ݝ@ݝ݅݅ܥ܅݅8܅@܋ܥ܋݅(܅0܋܋Z݅8ܥ@Z@݅(ܥ0ZH݅ ͋tܭ ̓t|Z݅ܭZPZZXZ ݅ܥZ`Z(݅ܭZhZ0݅ܭZpZ8Zx9t0GEu@@@^^^  uE)։uMe E ػĞ@0@@(]@8@ ]@@̋E]EE]EEeeeeXX X(XX0DžEM)DžDžDžӥӥӥ9M4t&9% ‹M D1D9ݝDݝD݅91̋4݅ݝpܥܥ݅pܥp^ ^^(^^0^^8@49UMEAA0]A ]AA8EA(AE]eeEeX XRM})щMMe E ػĞGG(G`ݝGhG@GHݝGPG ݝݝGXݝGݝG0GݝG8ݝGpOxݝݝ݅ܭݝ݅ݝ݅ݝ݅܅݅ݝ݅܅܅ݝݝ݅ܥݝ݅ݝ݅ܥ݅ܥܥݝ݅ܥܥݝ݅܅ݝܭEݝ݅ܥ݅ݝݝ݅ܭݝݝݝ݅ܥ݅݅ܥܥ݅܅ݝݝ݃ܥ݅܅܋݅܅X݅ܥX@݅ܭ݅ܥX݅XP݅X݅ܭܭܥX ݅܅XHXXX(X`X0݅ܭXhX8XpuF@ݝxFPFݝXF8ݝpݝH݅xF`FHܭxݝ8݅pݝ`ܭpFXF(FhݝPFpF FݝhݝFF0ݝ@ݝ0Fxݝ(ݝ ݅h܅݅`݅ݝ݅XܥXݝܥhݝ݅Hܥ`݅Pݝ݅8܅ܥPݝ(݅0܅ݝ݅@ݝ݅8ܥܥHܥ@ݝ݅0ܥݝ݅(ݝ݅ ݝܭ ˋEܭ(݅ܥݝ݅܅ݝݝ݅݅ݝ ݝ0݃݅݅(܅0ܥ ܋݅܅ XHX݅(ܥ0X@݅ܭX݅XXP݅܅݅ܭܥX ݅ܭXXX(݅ܭX`X0݅ܭXh}EGG0]G GG8]EG(GE]eeEeX X>UBXݝݝXB8B@ݝH݅BݝxBHݝ8݅xB(BPݝpݝ0݅Bh݅pB`ܭxݝ`B0ݝhݝPBpB BBݝ@ݝ(Bx݅hݝ ܥpܥh݅Xݝ݅`ܥ`ܥXݝ݅P܅@ݝ݅HܥHݝ݅Pܥ@ݝ݅8ܭ8ݝxݝP݅0݅0܅ ܥ ݝhݝ@݅݅(ܭ(ܥݝ`ݝXݝ8݅ܥE݅݅ݝHݝp݅ܥ܅ݝ݃݅hܥp݅x܅ܥ܋XH݅h܅pX݅xܥX@݅`ܭ`X݅XܭXXPXXX݅PܭPX ݅@܅HX`݅@X(ܥHX0݅8ܭ8Xht&UYMU9AB:yA B z8t&UWVS| [ÒU}w0}4}}.}}1e[^_]É} ;U M)V$ػĞ} ݝ0t U I MDž$ƋUDž$)щ($Dž(DžDž DžDžDž ,ӥӥӥӥӥ ӥӥ9  $% ‰ Ƌ lj ‰}݅0 } ݅0ɍ% ‹, ‹( Ƌ $U݅݅ Lɋ  ݝxݝp݅݅ ݅L,,,ݝh݅Lݝ` ݝX݅ ((݅݅ :݅L:(݅ݝP݅ 2ݝL2ʋ$ :$݅L $݅xݝ@݅ ݝH݅L:ݝ0݅xܥhݝ8݅p݅L ܅h܅`ݝ݅pܥ`ݝ݅Pݝ݅Xݝ݅Hݝ݅ܥH܅ܥXܥPݝ݅@܅0ݝ݅8ܥ8ݝ݅@ܥ0ݝ݅ܥݝ݅ܥݝ݅ݝ݅ݝܭˋܭ݅ܥݝ݅ݝ݅܅ݝݝݝ݅݅ܥ܅݅܅܋ܥ܋݅܅܋܋_݅ܥ_@݅ܥ_H݅͋ܭ̓_݅ܭ_P__X_ ݅ܥ_`_(݅ܭ_h_0݅ܭ_p_8Wx9ݝGvPjREP M֋8M$D9ݕxثĞݝ@ ك̞ك(ɋMWݝݝDM@D܍܍܍܍\X݅x݅ʃʃJ܍x܍HAʋ$ɋ$ك$Qɀt9\9\ 1e[^_]ËU DžE)DžDžDžӥӥӥ9}݅ %苍 M݅ L݅ 1ݝ݅L݅݅L1݅ ݝ ݅ݕL݅݅ܥݝ̋ܥ˃ܥΉˋ݅ܥ_ __(__0__8@9QMI "ʃ,DžDžEDžDž)ljDžDžDžDžӥӥӥӥӥӥӥ9zM>%苍 Ɖ,  ( $  MD,,,Dݝxݝ`DD919ݝpݝhD1ݝXݝ($(($$Dɋ  ݝP 9ݝHݝ@1D9ݝ0݅xܥh݅x݅p܅hݝ݅pܥ`܅`ݝ8ݝ݅Pݝ݅XD1ܥXݝݝ݅ܥH݅H܅ܥPݝ݅@܅0ݝ݅8ܥ8ݝ݅@ܥ0ݝ݅ܥݝh݅ܥݝX݅ܭݝP݅ܭݝ8݅ݝ0݅܅ݝHݝpݝ`݅ܥ݅ݝ@݅ܥ܅݅h܅p܋ܥ܋݅X܅`܋_݅hܥp܋_@݅Xܥ`_H݅PܭP_݅HܭH_P__X_ ݅@ˋܥ@Ƀ_`_(݅8ɉܭ8ʋ_h_0݅0ܭ0_p_8Wx9ݝ0U DžE)DžDžDžӥӥӥ9M % ƋM D91DݝDݝD1݅9̋݅ݝܥܥ݅ܥ_ __(__0__8@9[E@ك$Pɀt8$\9U)DT DT 1;|e[^_]Ã}M)ȾV$ػĞ}GG(G@ݝ G`ݝGhݝGPGHG ݝGXݝGݝG0GݝG8ݝGpOxݝݝ݅ ݝݝ݅ܭ ݅܅ݝ݅܅ݝ݅ݝ݅܅ݝݝ݅ܥ݅ܥ݅ݝ݅ܥܥܥݝ݅ܭܥݝp݅܅ݝHݝ`݅ܥݝ8΋E݅܅ݝX݅ܭ݅ݝxݝ0݅݅ܥܥݝP݅ݝhܥݝ@݃ܥ݅p܅x܋݅`܅hX݅pܥxX@݅`ܥhXH݅XX݅PܭPܭXX݅HXX݅8X ݅8܅@ܭHܥ@XPX(X`X0݅0ܭ0XhX8XpXx݃ك̞ك(Uݝݝ݃xuDD܍܍܍܍\E݃xʃ\ʃIM܋xJFʋMɃxvOك$ݞVݞ}AQTQDT ~n}M)ʿW$ػĞE@0@@(]@8@ ]@@̋E]EE]EEeeeeXX ʾ X(XX0XX8Eك̞@8@0@@͉Ƀ8݃ك(ˀp/XX0@X8Xك$X@PXH}AQTQDT ~MVMu$AAA؋ɃVك$Vݕ(ɋ,(V~^ ^(^0^8 }tWك̞EM@ك$YQY}Mك$wqYكĞ맋UB]BhB`ݝBX]]B8]BPB@BHݝpEEm]mB0B(ݝxBpB B]ݝhBBxݝ`ݝXݝP݅eE܅Eݝ݅x܅hݝHݝ8݅peEݝ0݅xܥhݝ@Eݝ(ܥpeݝ ݅`܅Heݝ݅`ܥHݝ݅X܅ݝ݅PݝܭPE݅Xܥݝ݅@܅8ݝ݅0ݝ(݅@ܥ8ܥ0ݝ݅(ܥ(ݝ݃݅ ܅(܋݅܅X݅ ܥ(X@݅ܥXH݅X݅XܭܭX ݅܅XX݅X(݅ܥܭXPX0݅ܭX`XhX8ݕUEBB0]B BB8]EB(BE]eeEeX Xit&UWVS|[ E}MU9 )ѺR$ػĞ ݝ~G DžODž)ȋ} DžDžDžDžDžDžU$ӥӥӥӥӥӥӥ9U uU } ݅1% Ɖ\X ‹| ‰T PU ݅ Lɋ\\\ݝݝ݅݅L݅L ݅ ݝ ݝXXX݅݅݅ ݅ 2݅L2ˋ||L ʋ|Tݝ݅ TPPݝx݅L ̋T ݝp݅L Pݝh݅ ݝ`݅L ݅ܭݝ݅܅ݝ@݅xݝH݅ܥܥxݝ8݅ݝ݅`ݝ0݅ܥܥݝ(݅pݝ݅0ݝ ݅h܅ܥpܥ`ݝ݅ܥhݝ݅H܅(ݝ݅Hܥ(ܥ0ݝ݅@ܭ@ݝ݅8ܭ8ݝxݝݝp݅ ݅ܥ݅ ܅ݝݝ݅݅݅܅ݝ݅܅܋ܥ܋܋܋_݅ܥ_@݅ܥ_H݅ܭ_݅ܭWP_݅ܥ_X_ ݅ܭ_`_(݅xܭxWh_0݅pܭp_p_8_xݝݝM$9|[^_]Ðt&9)щP$ػĞ ݝ~G DžOU )DžDžDžDžDžDžDž}$ӥӥӥӥӥӥӥ9} M} u Ή % Ƌ Xd` Ƌ ljP\u dT݅݅ Lɉddݝݝ݅݅݅ ݅LL>ݝ >ݝݝ\`\\`݅LX`TX݅݅ ݝLX݅݅ LɉTTPݝݝ݅݅ >L>݅ ݝxݝpPP݅ ݅L݅ݝh݅ܥ܅݅܅ݝH݅ܥݝ@݅܅ݝ8݅ܥݝ0݅ܥݝ(݅ܥݝ݅x܅hݝ ݅pܥpݝ݅xܥhݝ݅8ɋܥ8ݝ`݅0ܥ0ݝP݅HܭHݝHݝ0݅@݅(܅ ܭ@ݝ@ݝh݅݅(ܥ ݝ(ݝXݝ8݅݅ܥ܅݅`܅h܋ܥ܋܋܋݅P܅X^݅`ܥh^@݅PܥX^H݅HܭH^݅@ܭ@^P^^X^ ݅8ܥ8^`^(݅0ܭ0^h^0݅(ܭ(Vp^8^xݝ0U$9&Dž ODžDž Dž)ϋU$ӥӥӥM 9΋M<1 ݅u %苍  L݅݅ ݝL݅ ݅ ݝ݅L݅L݅ ݅݅ݝܥܥ݅ɋܥɃ_̉ʋU$_ __(__0_8@9|[^_]É ~G(DžOU )DžDžDžDžDžDžDž}$ӥӥӥӥӥӥӥ9[U MU } ωt& % Ƌ Xd` Ƌ ljP\u dTD>ɉdݝD>`d``ݝDݝݝݝDX\TX\PX\T>D>ݝTDPPݝxD>݅ݝpݝhDܭݝ݅܅>ݝ@ݝH݅ܥݝ8݅ܥ݅ܥݝ݅hݝ0݅ݝ݅0ݝ(ܥݝ݅H܅(݅xݝ݅Hܥ(ݝ ݅pݝ݅@܅ܭ@ݝ݅8ݝ݅ܥxܥ0ܥpܥhݝݝܭ8݅ ܥݝ݅݅ ݝݝ݅݅܅݅܅ݝ݅܅_݅ܥ_@݅ܥ܋܋_H݅_݅܋ܥ܋_݅̋ܭ΃ܭ̉_ ݅ɋu$ܭܥWP_(݅ܭ_X_0݅ܭ_`Wh_8_p_xɃ9ݝݝfDž O Dž)΋} DžDžU$ ӥӥӥ9BU EU M U 9 %苍 ݅L ݅ 2݅L:݅L݅݅ ݝL2݅ :ݝ݅ ݅݅ݝܥܥ݅^ˋܥ̓ɉ^ ^ʋ}$^(^^0^8@9Dž O u )DžDžDžE$ ӥӥӥ9sM EM } ljU 1 %苍 D ݝ2:DݝD2݅D:ŋ ݅ݝܥܥ݅ܥ__ __(__0_8@u$9+ ~GEOuU )EEEEEEE}$ueeeeeeẻU9U MU } ωU苋}uԊ U؊M% ƋM܉t ‰hpű} ƉlMЋt `u tdDt]>D>]pD]]]pdplDlhlhD>hd`>ݝd`D]`>]]D>ݝxEeEEEE]DݝXEeݝPE܅ݝHEeݝ@Eeݝ8݅eݝE܅xݝ0Eeݝ(Eܥxݝ ݅HܥHݝ݅@ܥ@ݝ݅Xݝ݅PݝܭX͋ܭP݅(ݝ݅8܅0ݝݝ ݅8ܥ0ݝݝ݅݅ ܥ ܅݅܅ ܋ܥ(܋݅܅܋܋_݅ܥ _@݅ܥ_H݅ϋEܭσEu$_݅ܭ_P__X_ ݅ܥ_`_(݅ܭ_h_0݅ܭWp_8_x9uݝDžOu )DžDžDž E$ӥӥӥ 9)M EM } ljU 1 %苍 D 2D:ݝDݝD2݅:Ë ݅ݝܥܥ݅ܥ_ __(__0__8@u$9vUWVS[çE uU}$9 )щP$ػĞ ݝ~V DžVDž)DžDžDžDžDžDžE(ӥӥӥӥӥӥӥ9>%苍 Uu x lj|<ʍ΋u4݅ ƋUM݅ ݝx݅ ‹Uݝp݅ ݝh݅ ݅ 񋵈݅ݝ`݅ ݅݅݅ݝX݅ ͋ ʋݝp݅ ɋ|ݝP݅ ݝH ʋxݝ@݅ ݝ8݅p܅h݅x݅ݝ ݅Xݝ(݅x݅pܥhݝ݅Hݝ݅`ݝ݅pܥPݝ݅P܅pݝp݅8 ܥHܥ`ݝ݅@ܥXܥ@ܥ8ݝ݅(܅ݝ݅ ܅͋4ݝ݅(ܥݝx݅ ܥݝp݅ݝ݅ܭݝݝݝh݅܅݅ܥ݅pݝݝ݅p݅܅݅܅܋ܥ܋܋܋݅ܥu(_ ݅ܥY ݅ܭ_݅ܭY݅x܅_(Y(_݅p݅xܥܭpY_0Y0_݅hܭhY_8Y89_Č[^_]9)щP$ػĞ ݝ~V DžFDž)DžDžDžDžDžDžE(ӥӥӥӥӥӥӥ9%苍 }u ‰ύ<ΉL}M݅ ݅݅ ݝ݅ ݝ݅ ݅ 񋵨݅ݝ݅ ݅ ݅ݝx݅ ͋ݝ݅ ͋ݝp݅ ݝh݅ ͋ݝ`݅ ݝXݝP݅܅݅h܅݅ݝ@݅ܥ ݝ8݅xܥxݝ0݅ܥݝ(݅pܥpݝ ݅ܥhݝ݅`܅Pݝ݅XܥXݝ݅`ܥPݝ݅0ݝX݅@܅(ݝH݅@ܥ(ˋLܥ0ݝ(݅ ܅ݝ@݅8ܭ8ݝ`ݝ8ݝ ݅݅ ܥ݅ݝPݝ0݅ܥ܅݅X܅`܋ܥ܋݅H܅P܋܋݅Xܥ`Z ݅HܥP_ ݅@ܭ@Z݅8ܭ8_Z(_(Z݅(܅0݅(ܥ0_Z0_0Z݅ ܭ _Z8U(_89DžFDž)DžDžӥӥӥ;}(=݅ % Ƌ ul݅ U ݅4U݅ ‹U݅ ‹Ul݅ ‹U ΋Uˋ ‹U ̋Eݝ݅ ݝ݅ ;}(݅݅ܥݝܥ݅ܥZ^Z^Z^Č[^_]Í& ~VDžFDž)DžDžDžDžDžDžE(ӥӥӥӥӥӥӥ9%苍 }u ‰ύ<΋uD}MƋݝxNj}ݝݝx݅xɋݝpݝhʋݝ8񋵐ݝp񋵌ݝ`ݝP݅xݝH݅݅܅xܥxݝXݝ0ݝ(݅h݅pܥpݝ ݅Xݝ݅`ݝ݅pܥ`܅pܥhݝp݅HܥXܥHݝ݅PܥPݝ݅8܅ ݝ݅0܅ݝ݅8ܥ ݝ݅0ܥݝݝ݅(ݝܭ(D݅݅ݝ݅܅ܥݝ݅pݝݝ݅p݅܅܋܋݅܅݅ܥ܋ܥZ ݅ܥ܋_ ݅Z݅_݅ܭɋ܅˃ܭZ(_(Z݅݅ܥܭ_Z0_0Z݅ܭ_Z8U(_89DžVDž)DžDžыE(ӥӥӥ9d7%苍 M ݅u%}M UȉMċU؋M }UuU}Ћu MM lj}<ʍΉdMUu]u]]uɋu]ˋuݝu]]ɋuݝxݝpݝhEEݝXEeݝPEeݝHEeݝ@Eݝ8݅eE܅eݝ݅x܅hݝ0݅pܥpݝ(݅xܥhݝ ݅HܥHݝ݅X܅@ݝ݅Xܥ@ݝ݅8܅0ݝ݅PܭPݝݝݝ݅(ݝ݅8ܥ0d݅ ݝ݅ܥ ܅݅܅܋ܥ(܋݅܅܋݅ܥ܋_ ݅ܥY ݅ܭ_݅ܭY_(Y(_݅܅u݅ʃܥʉuY_0Y0_݅ܭY_8}(Y89} DžFDž)DžDž E(ӥӥӥ 97 %苍 M @E>D>\>} >>D>\>}9}؁Ĝ[^_]9)¸щW$ػĞUtptӥpDžlDždl)pl9dBZpdl01t9|v؋1 lFtd ȋMȋM;d@BPR|]]du pdGɉd9dD\EEEeċMB)ыuĉEM9uQu1M>EME9uu1UMMM M}Mu}AFY^uM ȉuMUuU}F@XɋUMBU^9MXE}@E9}fEuEMUe)Dž|EU9|,|1Džd|MMx9d}td1M>|M} Ћdxu @BXZd|Bd9d||M@|9|7^&UWVS|[JUE9ѿ)V$ػĞUE}ȋEEeȍq)ȋM uċuȃE9uMu1M>EMME9MM1U1uMM ЋMU X 1Ҋ9` Њh苍\ PL NjTH  u݅p݅p݅p L >ݝ8L>ݝ0ݝ(ݝ ݅pL݅p ɋݝ݅p݅p݅p ݅pLˋLʉL>ݝ݅p >݅p ݝݝ݅p >݅p݅pL>ݝ݅8ܥ( ݝݝ݅0ܥ ݝ݅pݝ݅ݝ݅݅8݅0ݝݝ݅ܥ݅L܅(܅ ܥ܅ܥݝ݅܅(ݝ݅ܥݝ݅ܥݝ݅ܥݝp݅ܥݝ`݅ܭݝX݅ܭݝ@ݝPݝ8݅݅݅܅ܥݝhݝxݝH݅݅ܥ܅݅p܅x܋ܥ܋܋܋݅`܅hddZ݅pܥxZ@݅`ܥhZH݅XܭXZ݅PܭPZPZZXZ ݅HܥHZ`Z(݅@ܭ@ZhZ0݅8ܭ8ZpZ8Zxꀉ(l9dqDžpEMp)DžhdDž`Džltӥhӥdӥ`p9l}l\\݅@Ջ%2 1Ҋ1u Њp苍dh ‰D` ݅@ LD݅@݅@ɉD ݅@LD݅@L݅@ ݝP݅@LݝH ݅P݅Hݝ8ɋlܥP̓ܥHωl⋍t݅8ܥ8__ __(__0_8@9lP?UQ} zU n DžPEMP)DžHDžDDž@<Dž8Dž4Dž0DžLTӥHӥDӥ@ӥ<ӥ8ӥ4ӥ0P9L\M0L,,9,%>@ 1Ҋ9H ЊP苍D < ‹4 8 lj0 u>D>Dݝ D>>ݝݝݝ>D>>D>ˋݝ>ݝDݝݝD݅ ݝ݅܅ܭ D>ݝݝݝ݅ܥ݅ܥݝ݅ݝ݅ݝ݅ܥܥܥݝ݅ݝ݅ݝ݅܅ܥݝ݅܅ݝ݅ݝ݅ܥܥܥݝ݅ݝ݅͋0ܭܭݝݝ݅݅ܥݝݝ݅ݝ݅݅܅݅܅ݝ݅܅^݅݅ܥܥ܋ܥ܋^@܋܋^H݅ʋLܭʃLT^݅ܭ^P^݅ܥ^X^ ݅ܭ^`^(݅ܭ^h^0݅ܭ^p^8^x9L0:} =M)щMMW$ػĞUEB(BB0]B8B ]BB]EE]EeEeeeXX X(XX0XX8Q} YE)ЉEMV$ػĞUB(BB`ݝBhݝBPݝBXB@BHݝB8ݝB ݝBBݝݝB0BpJxݝݝ݅ݝ݅ܭܭ݅ݝݝ݅܅ݝ݅ݝ݅ܥ݅ݝ݅܅ݝݝݝ݅܅݅݅ݝ݅܅ܥܥܥݝ݅ܥܥܥݝ݅ܥϋEݝ݅ܭݝ݅ܭݝݝ݅ܥ݅܅ݝݝ݅݅ݝݝ݃݅݅܅ܥ܋݅܅X݅ܥX@XH݅݅ܭܥX݅ܭXPX݅܅XXX`X ݅ܭX(݅ܭXhX0݅ܭXpX8XxzMDžƋUDž)щDžDžӥӥӥ9FU,||݅p9|%> 1Ҋ9 Њ ƋM L9݅p 1݅pL݅pL݅p݅p ݝhL1݅p ݝ݅p 9݅h݅ݝXˋ,ܥhʋܥσʉ݅XˋܥX_ __(__0__8@9,jMDžƋUDžx)щtDžpDž|ӥxӥtӥp9|.E4|ll9l%>p 1Ҋ9t Њ ƋMx D91ݝ`ݝXD1D݅`D9͋4݅XݝHܥ`ܥX݅H__ ˋ|_ܥHʃˉ|䋵_0_(__8@9|4UUY} YU ]DžxEMx)DžpDžlDžhdDž`Dž\DžXDžt|ӥpӥlӥhӥdӥ`ӥ\ӥXx9tM t% 217 ȋh Њx Ƌpl` lj\u d>D>X DݝPDݝHݝ@ݝ8ݝ0DDD>ݝ>ݝ(>ݝ݅Pܥ@ݝ D>ݝ݅H݅P܅@܅8ݝDݝ݅Hܥ8ݝ݅(ݝ݅0ݝ݅ ݝ݅ܥ ܅ܥ0ܥ(ݝ݅܅ݝ݅ܥݝ݅ܥݝ݅ܥݝ(݅ܥݝ݅ݝ݅ݝܭ͋ ܭ݅ܥݝ݅ݝ݅܅ݝ ݝ0ݝ݅݅ܥ܅݅(܅0܋ܥ܋݅܅ ܋܋Z݅(ܥ0Z@݅ܥ ZH݅͋tܭ̓t|Z݅ܭZPZZXZ ݅ܥZ`Z(݅ܭZhZ0݅ܭZpZ8Zx9t u/u}FFF___ uE)։uMe E ػĞ@0@@(]@8@ ]@@̋E]EE]EEeeeeXX X(XX0DžEM)DžDžDžӥӥӥ9M$% 217 ȋ Њ NjM  D91DݝDݝD1ˋ$݅9݅ݝxܥܥ݅xܥx^ ^^(^ˋɃ^0^^8@9$@UMEAA0]A ]AA8EA(AE]eeEeX XRM})щMMe E ػĞGG(G`ݝGhG@GHݝGPG ݝݝGXݝGݝG0GݝG8ݝGpOxݝݝ݅ܭݝ݅ݝ݅ݝ݅܅݅ݝ݅܅܅ݝݝ݅ܥݝ݅ݝ݅ܥ݅ܥܥݝ݅ܥܥݝ݅܅ݝܭEݝ݅ܥ݅ݝݝ݅ܭݝݝݝx݅ܥ݅݅ܥܥ݅܅ݝݝ݃ܥ݅܅܋݅܅X݅ܥX@݅ܭ݅ܥX݅XP݅X݅ܭܭܥX ݅܅XHXXX(X`X0݅xܭxXhX8XpcuF@ݝxFPFݝXF8ݝpݝH݅xF`FHܭxݝ8݅pݝ`ܭpFXF(FhݝPFpF FݝhݝFF0ݝ@ݝ0Fxݝ(ݝ ݅h܅݅`݅ݝ݅XܥXݝܥhݝ݅Hܥ`݅Pݝ݅8܅ܥPݝ݅0܅ݝ݅@ݝ݅8ܥܥHܥ@ݝ݅0ܥݝ݅(ݝ݅ ݝܭ ˋEܭ(݅ܥݝ݅܅ݝݝ݅݅ݝݝ ݃݅݅܅ ܥ܋݅܅XHX݅ܥ X@݅ܭX݅XXP݅܅݅ܭܥX ݅ܭXXX(݅ܭX`X0݅ܭXh}EGG0]G GG8]EG(GE]eeEeX XUBXݝݝXB8B@ݝH݅BݝxBHݝ8݅xB(BPݝpݝ0݅Bh݅pB`ܭxݝ`B0ݝhݝPBpB BBݝ@ݝ(Bx݅hݝ ܥpܥh݅Xݝ݅`ܥ`ܥXݝ݅P܅@ݝ݅HܥHݝ݅Pܥ@ݝ݅8ܭ8ݝhݝ@݅0݅0܅ ܥ ݝXݝ0݅݅(ܭ(ܥݝPݝHݝ(݅ܥE݅݅ݝ8ݝ`݅ܥ܅ݝp݃݅Xܥ`݅h܅pܥ܋XH݅X܅`X݅hܥpX@݅PܭPX݅HܭHXPXXX݅@ܭ@X ݅0܅8X`݅0X(ܥ8X0݅(ܭ(XhUM}qwQq w Wt&UWVS [ÂU}w0}4}}0}}1e[^_]É} F;U5M)V$ػĞ} ݝU  MDž ƋUDž )щ DžDžDžDžDžDžӥӥӥӥӥӥӥ91҉݅9>% 19 Š ׋ Ћ ׉ ׋ Ћ} ы} ݅ɍ%< 19H ŠPꋍ@ щ(D 80 щ$4}u  ЋL݅Xύ<΋uM݅X ɋ(ݝ݅X ݝ$݅X ݅X ы ݅Xݝ݅X ݅X ֋݅Xݝ݅X ΋ݝx݅X ɋݝ݅X ݝ݅X ɋݝ݅X ݝݝ݅܅݅ܥ݅Xݝ݅ݝ݅ݝ݅ ܥݝ݅xܥݝ݅܅xݝx݅܅ܥܥݝ݅ܥݝ݅ܥݝ݅ݝX݅܅ݝH݅ܥܥݝ(݅܅ݝ@݅ܭݝ`ݝ8ݝ ݅݅ܥ݅ݝPݝ0݅xܥ܅x݅X܅`܋ܥ܋݅H܅P܋܋݅Xܥ`X ݅HܥPT_ ݅@ܭ@X݅8ܭ8_X(_(X݅(܅0݅(ܥ0_X0_0X݅ ܭ _X8_8LL9LSDž@EM@)Dž84Dž0Dž<Dӥ8ӥ4ӥ0@9<\&<1҉,,݅9>%0 19@ ‹4ꋍ8 щ(d% 19( Š0ꋍ щ$ щ}u Ћ,ύ<΋uM֋uݝ֋uݝݝ݅ʋݝݝݝ֋݅ɋݝhʋݝ֋ݝ݅ݝ݅܅ܥݝݝݝ݅݅ܥݝ݅ݝ݅ݝx݅hܥ܅hܥݝh݅ܥܥݝp݅ܥݝh݅܅ݝ݅܅ݝ݅ܥݝ݅ܥݝݝ݅ݝܭ݅p݅xݝ݅x܅hܥhݝ݅hݝݝ݅h݅܅݅܅݅݅ܥܥ܋ܥp܋X _ ݅ܭ܋X݅܋_X(݅ܭɋ4܅_(X݅݅ܥܭ_X0_0X݅ܭ_X8_8,,9,D} a8})׉}MV$ػĞEMU @@A]@A]͋EA]EE]EeEeeeXZXZXZh} LE)ЉEMV$ػĞU}B0GG0ݝݝG(ݝB(B BݝGG ݝBݝpݝGBݝB8O8ݝݝx݅ܭ݅ܭݝ݅܅pݝ݅ݝ݅ܥݝݝh݅܅xݝݝݝ݅܅݅݅pݝ݅܅ݝ݅݅xܥܥܥܥܥݝ݅ܥ͋Mݝ݅ܥʋU ݝ݅ܭݝ݅ܭݝݝ݅ܥ݅܅ݝݝ݅h݅hݝݝ݃݅܅܋݅܅݅ܥY ݅݅ܥܭ݅ܥZ Y݅ܭZ݅܅Y(Z(Y݅ܭZ݅Y0Z0Y݅ܭܭZY8Z8MDžxƋUDžpx)щlxDžhDžtӥpӥlӥh|9t&t1҉dd݅X9>%h 19x ‹pꋍl щ\`ut ݅X<΋tD} ݅Xʍ }@M ݅X `݅X ݅X ݝP\D݅X ׋t݅X ы@ݝH΃剅t݅Pʋ|9t݅Hݝ8ܥPܥH݅8ܥ8^_^_^_I9MDž`ƋUDžX`)щT`DžPDž\ӥXӥTӥPd9\\1҉LL9>%P 19` ‹XꋍT щDHu\ Ѝ<΋\,} }(MHDݝ8ݝ0݅8݅0ݝ ͋,ܥ8ˋ(⋅\ܥ0̃݅ ʉ\ܥ ̋d9\^_^_^_{;UU}  U MDžxƋUDžpx)DžlxDžhщdDž`Dž\DžXDžtӥpӥlӥhӥdӥ`ӥ\ӥX|9tPt1҉TT9>%d 19p Šxꋍh щPlH`X щLD@\}u 8 Ћt<ύ<΋Mu֋Pݝ0ݝ(LʋHݝ ̋Dݝ֋@ݝxы<ݝݝɋ8ݝ݅0ݝ݅0܅(ܥ(ݝݝ݅ݝݝ݅݅ ܥ ݝ݅xܥݝ݅܅xݝx݅܅ܥܥݝ݅ܥݝ݅ܥݝ݅ܥݝ݅܅ݝ݅ܥݝݝ݅ܭݝݝ݅݅݅܅ܥݝݝݝ݅݅xܥ܅x݅܅܋ܥ܋݅܅܋݅ܥ܋X ݅ܥ_ ݅ܭX݅_X(ˋ|ܭ_(X݅܅݅ܥ_X0_0X݅ܭ_X8tt_89tU}Mu BGY^8 ME)ыUMMe E ػĞ@BB]B@]͋U @̋E]EE]EEeeeeXZXZDžEM)DžDžDžӥӥӥ9t&1҉9>% 19 ‹ꋍ щu Ѝ<΋l} }hMݝxݝp݅x݅pݝ`ˋlhܥx̓ʉ݅`ˋܥ`9ܥpZ^Z^Z^UUEB@]@]BEBËU @̋EE]eeEeXRu})֋UuMe E ػĞBG0B ݝB0G ݝpG(GݝݝB(BݝݝGGݝBݝG8J8ݝݝx݅ܭݝ݅ݝ݅ݝ݅܅݅ݝ݅܅x܅pݝݝ݅xܥݝ݅pݝx݅ܥ݅ܥܥݝ݅ܥܥݝ݅܅ݝܭMϋU ݝx݅ܥ݅ݝݝh݅ܭݝݝݝ`݅ܥ݅x݅ܥܥx݅܅ݝpݝ݃ܥ݅܅܋݅܅݅ܥY ݅݅ܥܭZ Y݅ܭZ݅xY(Z(Y݅h܅p݅hܭxܥpZY0Z0Y݅`ܭ`j}MuUB ݝxF ݝpFF0ݝhF(B0ݝPB8B(ݝ@݅xܭxݝxݝ8݅pFBFݝ`ݝXBBݝHݝ0F8ݝ(ܭp݅`݅xݝ ݅h܅xݝܥhݝ݅XܥX݅Pݝh݅Hܥ`ݝݝ݅8܅݅@ܥPݝ݅0܅ܥHܥ@ݝ݅8ܥݝ݅0ܥݝ݅(ݝ݅ ͋}ܭ ϋU ܭ(ݝ݅ܥݝ݅܅ݝݝ݅h݅hݝݝ݃݅݅܅ܥ܋݅܅݅ܥ_ ݅Z _݅Z݅܅ܭܭ݅ܥ_(Z(_݅ܭZ݅_0Z0_݅ܭܭZ_8 }uEU GF]FG]EGFE]eeEeX}MUuG(ݝݝXGB ݝH݅ݝxB(G ݝ8݅xݝ`ܭxBB0GݝPB8ݝ0݅BBݝpݝhG0Gݝ@G8ݝ(݅pܥp݅hݝ ݝx݅`ܥh݅Xݝ݅P܅@ܥ`ܥXݝ݅HܥHݝ݅Pܥ@ݝ݅8ܭ8ݝPݝ(݅0݅0܅ ܥ ݝ@ݝ݅x݅(ݝ8ݝ0ܭ(M݅ܥU ݅ݝ݅ݝ ݝH݅ܥ܅ݝX݃݅@ܥH݅P܅Xܥܥx܋݅@܅H݅PܥXY ݅8Z Y݅0Z݅(ܭ8ܭ0ܭ(Y(Z(Y݅܅ ZY0݅ܥ Z0Y݅ܭZeUE}pEwu HNt&'UWVS[XU}w,}.}}~}I} 1e[^_]E;UM)щW$ػĞUMӥy}DžDž)ȃ9BZ77D7\7qEEؐt&1Ɋ :>% >%1 ӭӥ9| ؉1Ҋ1 0% ʊ 0 ʋ ꊍG ‹MȉMA@XY ʋ MʋM;BFRV]]ɋ } ֋ D\77D7\7F9;UM)ѺW$ػĞE}Džӥ);@XEE؉1Dž% ‰ щӭӥ9|t&0Ł0% ‰% 0u ʊꊍ⋍  M@BRʋBP9?]]ɋU G;22D2\2mJz ك̞UBBBZZZWU REPuV?}   M)ѺMMR$ػĞE@(@@ ]@0]]@8@@EEݝxEe݅xeeXܥxXX X(XX0X8MDžDžӥA)Dž9EEEu؋}1DžƊ% %% ӭӥ9H1Ҋ17ρ% 7  ʋ}ꊍ  Ήu@A1yx0XY ϋ ‹uEpx Bɉrz BP9݅ݝʋA9' UDžDžDžӥ)9?EEEu؋}1DžƊ% %% ӭӥ9t&1Ҋ17ρ% 7  ʋ}ꊍ⋍ ȋupx Bʉrz ʋBP9D݅ݝʋB93l} Xu)։uMW$ػĞE@@H@(ݝp@`ݝhݝX@Xݝ`@h@PݝH@8@@@ ݝ@@ݝPݝ@@0@pHxݝ8ݝݝ0݅pܥh݅p݅X܅hݝ ݅PܥPݝ(ݝ݅8ݝ݅`݅H܅ݝ݅0܅ݝݝ݅@܅ݝ݅(܅݅ݝ݅ܥHܥ`ܥXܥ@ܥ8ݝ݅0ܥݝh݅(ܥݝ`݅܅ݝ݅ ܭ ݝݝxݝX݅݅ܥݝ݅ݝp݅݃ܥ݅܅܋݅܅X݅ܥX@݅ܥXH݅ܭX݅xܭxXPX݅h܅pXX݅hX ݅`ܥpܭ`X(X`X0݅XܭXXhX8XpXxكĞju})։uMe E ػĞG(GG8]G0]G GG]EE]EeEeee__(__ __0_8MAݝ@A0A Aݝ8A8݅@A(A݅8]ܥ@ܥ8EeYY YY(YY0Y8M)ыUMMe E ػĞBB(B`ݝ0BhB@BHݝBPB ݝ(ݝBXݝ BݝB0BݝB8ݝBpJxݝݝ݅0ܭ0ݝ݅ݝ݅ ݝ݅܅݅(ݝ݅܅܅ݝݝ݅ܥݝ݅ݝ݅ܥ݅ܥ(ܥ ݝ݅ܥܥݝH݅܅ݝ8ܭݝ ݅ܥ݅ݝ0ݝ݅ܭݝ@ݝ(݅ܥ݅ܥݝ݅݅ܥ܅ݝݝP݃ܥ݅H܅P܋݅8܅@Z݅HܥPZ@݅0݅8ܭ0ܥ@Z݅(ܭ(ZHZ݅ ܭ ݕ0Z ݅܅Z`݅Z(ܥZPZ0݅ܭZXZhZ8ZpZxxE@(ݝ@@ݝ@Pݝݝpݝ@8@p@@@Hݝݝ݅݅@`ܭܭ@hݝ@X@ @ݝxݝ@0@xݝݝݝݝ݅݅݅ݝ݅pܥ܅x݅xܥݝ݅ݝݝ݅ܥ݅܅pݝx݅ܭܥݝ݅܅ܥܥݝxݝ݅ܥݝp݅ݝ݅ݝܭ݅ܥxܭݝ`݅܅xݝ݅݅ݝݝh݅ݝ݃ܥ݅܅܋݅܅X݅ܥX@݅ܥXH݅ܭX݅XXP݅x܅ܭX ݅pXX݅xX(݅hܥܭpܭhX0݅`ܭ`X`mUBݝPB0B BݝHB8݅PB(B݅H]ܥPܥHEeZ ZZ(ZZ0ZZ8E@ݝ0@X@@ݝ@8@(ݝ@p@ ݝ@xݝ(ݝ@Hݝݝp݅0@`@hܭ0݅(ݝ @Pݝ`݅ @@܅ݝݝh@0ݝX݅ܥ ܥ(݅ݝ݅ݝP݅ݝH݅܅ܥܥݝ@݅ܥܥݝ8݅pܭpݝ݅h܅Xݝݝ݅hܥXݝ݅`ܥݝ݅`܅ݝݝ݅@݅P܅H݅8ݝݝܥ8݅PܥHݝ݃ܥ@݅݅܅ܥ܋݅܅X@X݅ܥXH݅ܭX݅ܭXPX݅XXX ݅܅݅ܭܥX(X`X0݅ܭXhX8]]vʉu؉}]]]ZUWVS|[=U}w,}.}}~}+} 1e[^_]E;Uu)։P$ػĞUDžэz)ЋUӥM Dž9t&1Dž% ljׁ% ӭӥ9&t&1Ҋ17ρ% 7  ʋꊍ ‹M <ыM 4ɋM ʉM ˋɋ M u<֍4ƍэ@9 NjUM ɋ F ɋ9؋} WuVU RMQ';U&M)щR$ػĞu} DžӥM)Dž9]1Ɋ 27ׁ% 7%1 ӭӥ91Ҋ1 0% ʊ 0 ʋuꊍ ‹ G ;u ɋu4ʉu ƋS EU F9Jك̞UM BAZY}  })׉}MV$ػĞM EAA@]@]]A@EE]EeEeeeXYXYXY}DžDž)ӥDž9b1Dž :>% >% ӭӥ91Ҋ17ρ% 7  ʋ}ꊍ ‹ u ׉4ƋM <эɋFɉ9CB9EDžDžӥ)xDž91Dž  % lj ׁ % Ɖ ӭ ӥ 951Ҋ17ρ% 7 } ʋ ꊍ ⋍ ʍ ׋} 4׋} Nj} 4Njɋ  } ׉uM 4ƍ<эBʉ9B9 } NM)ѺMMR$ػĞ} EG Gݝ@0ݝݝG(ݝG0@(ݝpG@ @ݝG@ݝxݝh@@8O8ݝ`ݝ݅ܥݝX݅ݝH݅xܥx݅܅ݝ݅`ݝPݝ@݅݅p܅ݝ(݅X܅@ݝ8ݝ0݅h܅ݝ݅P܅8݅ݝ ݅ܥpܥܥܥhܥ`ݝp݅Xܥ@ݝP݅Pܥ8ݝH݅0܅ ݝh݅HܭHݝxݝ`ݝ@݅(݅0ܥ ݝ݅ݝX݅݃ܥ(݅܅܋݅p܅x݅ܥX ݅pܥx_ ݅hܭhX݅`ܭ`_݅P܅XX(_(X݅H݅PܥXܭH_X0_0X݅@ܭ@_X8_80كĞu)֋U uMe E ػĞMBBB]AA]A]EE]EeEeeeYZYZYZJM uAFݝ0FAݝ(݅0AF݅(]ܥ0ܥ(Ee^Y^Y^u} )֋EuMe E ػĞG@0ݝXG0@ G ݝ@(@ݝPݝ@G(ݝHGݝ8@@ݝ0Gݝ(@8O8ݝ ݝ݅XܭXݝ݅@ݝ݅Hݝ݅0܅ ݅Pݝ݅(܅܅ݝݝ݅ܥ(ݝ݅ݝ݅0ܥ ݅8ܥPܥHݝ݅ܥ@ܥ8ݝ0݅܅ݝ ܭݝ݅ܥ݅ݝݝ݅ܭݝ(ݝ݅ܥ݅ܥݝ݅݅ܥ܅ݝݝ8݃ܥ݅0܅8܋݅ ܅(݅0ܥ8X ݅݅ ܥ(ܭ_ X݅ܭݕ_݅X(_(X݅܅ܭ_X0݅ܥ_0X݅ܭ_uU }G0ݝU ݝBݝݝG(B(ݝG8G B ݝݝ݅݅B0GݝBGBݝxݝGB8ݝݝ݅܅ܭܭ݅ݝ݅ݝ݅ݝ݅xܥܥݝݝ݅݅܅xܥݝ݅ܥܥݝ݅ܭܥݝ݅܅ݝ`ݝ݅ܥݝX݅ܭ݅ݝx݅ܭݝPݝp݅ܥݝH݅܅ݝh݅ݝ݅ݝ݃ܥ݅܅܋݅܅݅ܥ_ ݅ܥZ ݅x_݅pܭxܭpZ݅`܅h_(Z(_݅X݅`ܥhܭXZ݅P_0Z0_݅HܭPܭHZ_8Z8} UGBݝ@BGݝ8݅@GB݅8]ܥ@ܥ8EeZ_Z_Z_U Mݝ B(A ݝBBݝA8AݝB8ݝݝB ݝ]݅ A0B0ܭ ݅ݝA(]݅AB܅ݝ]A]݅ܥܥ݅ݝ݅ݝx݅ݝp݅܅ܥܥݝh݅ܥܥݝ`EmݝEEݝݝEeݝEܥݝE܅ݝݝ݅h݅x܅p݅`ݝݝܥ`݅xܥpݝ݃݅ܥ݅܅ܥh܋݅܅݅ܥY Z ݅ܭY݅ܭZ݅Y(Z(Y݅܅݅ܭܥZY0Z0Y݅ܭZY8Dt&UWVS[c##DžM}⍍u }w0}2}}})1}e[^_]Á w,jEPWuVH1e[^_]à RBZttQj}WRuV^R\ZO MUك̞AZs;} M)V$ػĞ} ݝ0@ U MDž(M(Dž )DžDžDžDž DžDž$ӥ ӥӥӥӥӥ ӥ,(9$,$1݅077% 11 Š(ꋍ ЉlE ։ ׋$h ȋ$ M݅0 Lɋlllݝ݅0 ݝݝh݅0hLh݅0݅0 ݝL݅0݅0 1ݝL1݅0L1݅0 1ɋݝp݅0݅0݅0 9L9 1ݝݝݝ݅0 ݅0L1݅ݝ݅ܥݝ݅0ݝ݅ܥL݅܅ݝ݅܅pݝ݅܅ܥݝ݅ܥݝx݅pܥݝp݅܅ݝp݅ɋܥݝh݅ܥݝ`݅ܥݝ݅ܥݝ݅ܭݝݝ݅݅x܅pܭݝݝ݅h݅xܥpݝݝݝ݅p݅`ܥ`܅p݅܅܋ܥh܋܋܋݅܅݅ܥ^ ݅ܥ_ ݅ܭ^݅ܭ_^(_(^݅ܥ_݅^0_0^݅ܭܭݕ_ݕ^8_8$,$9$t&juVQEPz$ Wi  M)MMV$ػĞUEB BB0BB8XXX}a U)UMV$ػĞE@ @@@(ݝX@Xݝ@`ݝx@0@@ݝp@8@xݝݝ@P@hݝh@p݅X܅ݝ`݅ܥݝXϋEܥ݅pݝ ݅x܅`ܥxݝ@݅hݝp݅pݝPܥ`݅pݝ݅@܅ ܥPݝHܥhݝ ݝ@݅H݅XܥH݅ ܥ ܋ܥXɀG܋X ݅ܭX݅@ܥ@X(XX0XX8<MDžXMXDžP)ȉLDžHDžTӥPӥLӥH\X9TT1݅077%T 11H ŠXꋅPL щl E݅0<4T݅0Nj}݅0 0݅0LʋlL̉ll 7݅0L݅0 ݝ8 ݝp݅0L7T̋4勽0݅8̉T狕\݅p9Tݝ(ܥ8ܥp݅(ܥ(^_^_^_AU}uG^} wU bMDž8M8Dž0)Dž,Dž($Dž DžDžDž4ӥ0ӥ,ӥ(ӥ$ӥ ӥӥ<8944177%  11( Š8ꋅ$ Љ Љ4E0 ֍% 1Ҋ9 Њ苕  Ƌ ‰, ‰( $U݅݅ Lɋݝxݝp݅݅݅ L݅L ݝh ݝ`ݝX݅L:,(,݅݅ 2ݝ(L2,݅ 2((݅ݝP݅ :ɋ$ $݅L2$݅xݝ@݅ 2ݝH݅L ݝ0݅xܥhݝ8݅p݅L2܅h܅`ݝ݅pܥ`ݝ݅Pݝ݅Xݝ݅Hݝ݅(ܥH܅(ܥXܥPݝ(݅@܅0ݝ݅8ܥ8ݝ݅@ܥ0ݝ݅ܥݝ݅ܥݝ݅ݝ݅ݝܭˋܭ݅ܥݝ݅ݝ݅܅ݝݝݝ݅݅(ܥ܅(݅܅܋ܥ܋݅܅܋܋_݅ܥ_@݅ܥ_H݅͋ܭ̓_݅ܭ_P__X_ ݅ܥ_`_(݅ܭ_h_0݅ܭ_p_8Wx9ݝ$t&PjQEPM֋8M4D9ݕثĞݝ ك̞ك(ɋMWݝݝDM@D܍܍܍܍\X݅݅ʃʃJ܍܍HAʋ4ɋ4҉ك$Qɀt9\9 1e[^_]ËU DžMDž)DžDžӥӥӥ9}݅Ձ%2 1Ҋ1 Њ苕  U݅ L݅L ݅ 2݅L݅L2ݝ݅ ݝ(݅ ݅݅(ݝ͋ܥʃܥ(ω̋݅ܥ_ __(__0__8@9l7MI :ʃDDž׋MDžDž)DžDžDžDžDžӥӥӥӥӥӥӥ9hu:>% 1Ҋ9 Њ苕  Ƌ  , ‰( $UDɋݝxD ݝpݝhDD:2 :ݝ`ݝXD2ݝ(,($,,((:$ݝP$ D:ݝ@2ݝHݝ0݅xܥhD ݅x݅pݝ݅pܥ`܅h܅`ݝ8ݝ݅Pݝ݅XD2ܥXݝݝ݅(ܥH݅H܅(ܥPݝ(݅@܅0ݝ݅8ܥ8ݝ݅@ܥ0ݝ݅ܥݝx݅ܥݝh݅ܭݝ`݅ܭݝH݅ݝ@݅܅ݝXݝݝp݅ܥ݅ݝP݅(ܥ܅(݅x܅܋ܥ܋݅h܅p܋_݅xܥ܋_@݅hܥp_H݅`ܭ`_݅XܭX_P__X_ ݅PɋܥPʃ_`_(݅HˉܭHɋ_h_0݅@ܭ@_p_8Wx9ݝ@U DžMDž)DžDžӥӥӥ9d}Ձ%2 1Ҋ1 Њ苍U  DD 2Dݝݝ(D2݅ ݅(ݝܥܥ(݅__ 動_ܥ˃ʉˋ_0_(__8@9E@ك$Pɀt84\9FU)DT DT 1;|ʍe[^_]Ã}u)R$ػĞ}GG(G@ݝ G`ݝGhݝGPGHG ݝGXݝ GݝG0GݝG8ݝGpOxݝݝ(݅ ݝݝ݅ܭ ݅܅ ݝ݅܅ݝ݅ݝ݅܅(ݝݝ݅(ܥ݅ ܥ݅ݝ(݅ܥܥܥݝ݅ܭܥݝ݅܅ݝXݝp݅ܥݝH΋E݅܅ݝh݅ܭ݅(ݝݝ@݅݅ܥܥ(ݝ`݅ݝxܥݝP݃ܥ݅܅܋݅p܅xX݅ܥX@݅pܥxXH݅hX݅`ܭ`ܭhX݅XXX݅HX ݅H܅PܭXܥPXPX(X`X0݅@ܭ@XhX8XpXx݃ك̞ك(Uݝݝ݃xuDD܍܍܍܍\E݃xʃ\ʃIM܋xJFʋMɃxvOك$ݞVݞ&}AQTQDT ~}u)R$ػĞ}EG0GG(]G8G ]GG]EE]EEeeeeXX ̾ X(XX0XX8Eك̞@8@0@@͉Ƀ8݃ك(ˀp/XX0@X8Xك$X@PXH}AQTQDT ~UMVMu$AAA؋ɃVك$Vݕ8ɋ<8V~^ ^(^0^8}tWك̞EM@ك$YQYu}Mك$wqYWكĞ맋MA]AhA`ݝ(AX]]A8]APA@AHݝpEEm]mA0A(ݝxApA A]ݝhAAxݝ`ݝXݝP݅(eE܅(Eݝ(݅x܅hݝHݝ8݅peEݝ0݅xܥhݝ@Eݝ(ܥpeݝ0݅`܅Heݝ ݅`ܥHݝ݅X܅(ݝ݅PݝܭPE݅Xܥ(ݝ݅@܅8ݝ݅0ݝ8݅@ܥ8ܥ0ݝ݅(ܥ(ݝ(݃݅0܅8܋݅ ܅(X݅0ܥ8X@݅ ܥ(XH݅X݅XܭܭX ݅܅XX݅X(݅ܥܭXPX0݅ܭX`XhX8ݕMEAA0]A AA8]EA(AE]eeEeX Xe'UWVSd[uMU} }9 )ѺR$ػĞ ݝ~V DžNDž)DžDžDžDžDžDžӥӥӥӥӥӥӥ;}$!Mϋ݅2Ձ%2 1Ҋ1 Њ苕 ‹t p Ƌ ‰l hU ݅ Lɋݝݝ݅݅L݅L ݅ ݝ ݝtltt݅݅ ݅L ʋpp 2݅L2ʋpݝ݅ 2ll݅ݝ݅L2ɋhh ݝ݅L hݝ݅ ݝx݅L ݅ܭݝ݅܅ݝX݅ݝ`݅ܥܥݝP݅ݝ݅xݝH݅ܥܥݝ@݅ݝ0݅Hݝ8݅܅ܥܥxݝ(݅ܥݝ݅`܅@ݝ݅`ܥ@ܥHݝ݅XܭXݝ݅PܭPݝݝݝ݅8݅0ܥ(݅8܅(ݝݝ݅݅݅܅ݝ݅܅܋ܥ0܋܋܋_݅ܥ_@݅ܥ_H݅ܭ_݅ܭWP_݅ܥ_X_ ݅ܭ_`_(݅ܭWh_0݅ܭ_p_8_xݝݝu$9d[^_]Ðt&9")щeEػĞ ݝ~V= DžNDž)DžDžDžDžDžDžӥӥӥӥӥӥӥ;}$Mω:>%u  1Ҋ9 Њ苕 l Nj ‰h ‰d ‰` \݅ ݅݅ݝ݅݅L݅ LݝL>ݝ >ݝݝhlhhl݅Ldl`d݅݅ ݝLd݅݅ Lɉ``\ݝݝ݅݅ >L>݅ ݝݝx\\݅ ݅L݅ݝp݅ܥ܅݅܅ݝP݅ܥݝH݅܅ݝ@݅ܥݝ8݅ܥݝ0݅ܥݝ݅܅pݝ(݅xܥxݝ ݅ܥpݝ݅@ɋܥ@ݝx݅8ܥ8ݝh݅PܭPݝ`ݝH݅H݅0܅(ܭHݝXݝ݅ ݅0ܥ(ݝ@ݝpݝP݅݅ܥ܅݅x܅܋ܥ ܋܋܋݅h܅pZ݅xܥZ@݅hܥpZH݅`ܭ`Z݅XܭXZPZZXZ ݅PܥPZ`Z(݅HܭHZhZ0݅@ܭ@RpZ8ZxݝHꀉ}$9t&Dž$N$Dž)DžDžU$ӥӥӥ$ 9M< ݅Ձ%2 1Ҋ1 Њ$苍 u ݅  L݅݅ ݝL݅ ݅ ݅ݝ݅LL݅݅ݝ̋ ܥσܥˉ ˋu$݅ܥ__ __(__0_8@9 cd[^_]Ð ~VCDžNDž)DžDžDžDžDžDžӥӥӥӥӥӥӥ;}$3Mωt&:>% 1Ҋ9 Њ Njt ‹ p ljl ljh d} DtttݝDDݝ7ݝݝݝD7lpllphpdhDhݝDddݝD݅ݝݝxDܭݝ݅܅ݝPݝX݅ܥݝH݅ܥ݅ܥݝ݅xݝ@݅ݝ(݅@ݝ8ܥݝ݅X܅8݅ݝ݅Xܥ8ݝ0݅ݝ݅P܅ܭPݝ݅Hݝ ݅ܥܥ@ܥܥxݝݝܭH݅0ܥ ݝ݅(݅0ݝݝ݅݅܅ ݅܅ݝ݅܅^݅ܥ^@݅ܥ܋܋^H݅^݅܋ܥ(܋^݅̋ܭ̓ܭ̉^ ݅ɋ}$ܭܥVP^(݅ܭ^X^0݅ܭ^`Vh^8^p^x9ݝݝYtDžNDž )DžDžӥ ӥӥ;}$Mω:>%u  1Ҋ9 Њ苍  ݅L݅ >݅L݅L݅݅ ݝL>݅ ݝ݅ ݅݅ݝܥܥ݅ˋܥʃ_ _̉E$_(__0__8@9DžNDž)DžDž ӥӥӥ ;}$0u7%>u  1Ҋ9 Њ苍  Dݝ>DݝD>⋽݅D݅ݝܥܥ݅ܥ__ _(_ʋ뉅M$__0_8@9 ~VENEE)EEEEEEEeeeeeeẻ};}$?MωM苻:>% 1Ҋ9} ЊM NjU䉽t ‹}؋M p} ljl} ljh} d} DttDʋt]Dp7]]D7]]phlplDlhD7ݝ7dhdd]]]DݝxEeEEݝXEeEE]DݝPE܅ݝHEeݝ@Eeݝ8݅eݝE܅xݝ0Eeݝ(Eܥxݝ ݅HܥHݝ0݅@ܥ@ݝ ݅XܭXݝ݅PܭPݝݝݝ݅8܅0݅(ݝ8݅8ܥ0ݝ(ݝ݅݅ ܥ ܅݅0܅8܋ܥ(܋݅ ܅(܋܋^݅0ܥ8^@݅ ܥ(^H݅^݅^͋Uܭ˃ܭωŰ}$^ ݅ܥ^P^(݅ܭ^X^0݅ܭ^`^h^8Vp^x9}ݝDžNDž)DžDž ӥӥӥ ;}$uv7%>u  1Ҋ9 Њ苍  D>DݝDݝD>ˋ݅݅ݝܥܥ݅ܥ_ __(_ˋɃ_0__8M$@9X&UWVS['E uU}$9{ )щP$ػĞ ݝ~V! DžVDž)DžDžDžDžDžDž|E(ӥӥӥӥӥӥӥ|9x1xx>% 19 Šꋅ щtp| щlhdEu \ ׋`݅<ȍ΋uE ݝX݅ ݝPEt݅ M݅ p݅ݝH݅ ݅ l݅ݝ@݅ ݅݅ݝ8݅ ΋h ̋dݝP݅ ɋ`ݝ0݅ ݝ( ݝ \݅ ݅X݅ݝ݅P܅Hݝ݅Xݝ݅8݅PܥHݝ݅(ݝ݅@ݝ݅Pܥ0ݝ݅0܅PݝP݅ ܥ(ܥ@ݝ݅ ܥ8ܥ ܥݝ݅܅ݝ݅܅̋ݝx݅ܥݝX݅ܥݝP݅ݝp݅ܭݝݝhݝH݅܅݅ܥ݅Pݝݝ`݅P݅܅݅x܅܋ܥ܋܋܋݅ܥu(_ ݅xܥY ݅pܭp_݅hܭhY݅X܅`_(Y(_݅P݅Xܥ`ܭPY_0Y0_݅HܭHY_8Y89"Ĭ[^_]Ív9)щP$ػĞ ݝ~V FDžDžDžDžDžDžDžDž)ӥӥӥӥӥӥӥM(91>% 19 Šꋅ щ щEu ׋݅<ȍΉ% 19 Šꋅ щ щ|Eu t ׋x<ȍ΋u,EݝXݝhME݅Xʋݝ`ݝXݝ ݝPɋ|ݝPxݝHtݝ8ݝ@ݝ0݅h܅`ݝ݅P݅X݅hݝܥ`݅@ܥPݝ݅Xݝ݅PܥHݝ݅H܅PݝP݅0ܥXܥ@ܥ0ݝ݅8ܥ8ݝ݅ ܅ݝ݅܅ݝ݅ ܥݝx݅ܥݝp΋,݅ݝ݅ܭݝݝݝh݅܅݅ܥ݅Pݝݝ݅P݅܅܋݅܅݅ܥ܋ܥ܋_ ݅ܥ܋Y ݅_݅΋ܭʃܭΉY݅x܅_(Y(_݅p݅xܥܭpY_0Y0_݅hܭhY_8}(Y89 VDžыDžDžDž)ӥӥӥM(91>% 19 ‹ꋍ щM ݅% 19 ‹ꋍ щM Ѝ%u 19} ŠMEM щEĉME؋M щ}Eu}ЋEu M ׋M}<ȍΉ\uċEM]]uʋu]ˋu]uݝ`u]ݝxɋuݝpݝhݝ`EEݝPEeݝHEݝ@E݅x܅`eݝ8Eeeݝ0݅`ܥxݝ`݅p܅`ݝ(݅hܥhݝ ݅pܥ`ݝ݅@ܥ@ݝ݅P܅8ݝ݅Pܥ8ݝݝ݅HܭHݝݝ݅ ċ\݅0݅0܅(ܥ(ݝݝݝ݅݅`ܥ܅`݅܅܋ܥ ܋݅܅܋܋݅ܥ_ ݅ܥY ݅_݅Yܭ̋u(ܭ_(Y(_݅܅݅ܥY_0Y0_݅ܭY_8}}Y89uIFDžDž DžDž)ӥ ӥӥM(9H1>% 19 ‹ ꋍ щM Ѝ>%  19 Šꋅ щ< E<} Nj}D<<M%M M>M%1 MMmMe9| ؉1Ҋ1 0% ʊ 0E ʋuM MG ‹MȉMA@XYM ʋM MʋM;}BFRV ]]M} u}U ֋E D\77D7\7u}Fu9}e؁Č[^_]9q)V$ػĞ|U}MDžxӥ|x)։xG_;|}1Džtpp ։pp щpxӭpMӥp9t| tt0Ł0% ‰% 0tp xuM MʋtB@At9tRPF]]pU G;|22D2\2 EEeċEy)Euĉ}E9u}1EUU}% lj}ׁuM% ƉumMe9UM1Ҋ1M7ρ% 7 u ʋ}MM Mu Ήuu1yA0xuċ} @ɉ}XE ‹EEEuYMʋ8@HBʉ:JZUMBUX9M#EU@E9U|EUEMEeM})9}MH}1DžtUU}% lj}ׁuM% ƉumMe9tt1Ҋ1t7ρ% 7 t ʋEM M} uMʋH8@J:tBAZɉtUX9t[UEBU9E UWVS[÷uE9)¸щW$ػĞEEEEeMz)щ}U MM9MU1E 2U7ׁ% 7% MMmMe9UvM1Ҋ1M7ρ% 7 E ʋ}MM ‹ME <ыM 4ɋM ʉMM MMMˋMɋMMu M u<֍4ƍэʋuE@E9u}E NjUM uɋE uF}u ɋU9Uk؁Č[^_]9)¸щW$ػĞ}|x|ӥxM DžttDžp)xt9pop1Ҋlpl% Ɖlցlt% ljl1ӭl|ӥl91Ҋ1 0% ʊ 0p ʋutꊍ| ‹l G ;pu ɋu4ʉhu ƋhSpl Eu pGpx9pEEUe)xEM}U9M}䋋1EUU}% lj}ׁuM% ƉumMe9UM܋1Ҋ1M7ρ% 7 } ʋu܊MEM Mu <щ}؍ ֋}MԋM 4ljuЍݝ@L>ʋݝ8ݝ0ݝ(݅pL݅p ɋݝ ݅p݅p݅p ݅pLˋLʉL>ݝ݅p >݅p ݝݝ݅p >ݝ݅@ܥ0݅p ݅pݝ݅8ܥ(L>ݝݝ݅ݝ݅ ݝ݅ݝ݅ܥ܅ݝݝ݅܅݅pL݅@ݝ݅݅8܅0܅(ܥ ܥݝ݅ܥʋHܥݝ݅ܥݝ݅ܥݝ݅ܭݝx݅ܭݝ`ݝpݝX݅݅݅܅ܥݝݝݝh݅݅ܥ܅݅܅܋ܥ܋܋܋݅܅ddZ݅ܥZ@݅ܥZH݅xܭxZ݅pܭpZPZZXZ ݅hܥhZ`Z(݅`ܭ`ZhZ0݅XܭXZpZ8ZxꀉHl9diDž EM)DžDžDžӥӥӥ9}1҉||݅`݅`0% ‰%݅`ʊ0% 11u Šꋅ d  Ld݅`ˉd Ld݅`݅`L݅`  ݝpݝh݅`Lϋ݅pˉ݅hݝXܥpܥh݅XܥX__ __(__0_8@9E0U6} U  Dž` EM`)DžXDžTDžPLDžHDžDDž@Dž\dӥXӥTӥPӥLӥHӥDӥ@`9\MMPt&\1҉ 0% ʊ 017P ʋX Š`ꋍT щHD ыL Ћ@ щuD>D>ˋݝ8D>>ݝ0ݝ(ݝ >D>>D>ˋݝ>ݝDݝDݝݝ݅8݅0܅(ܭ8D>ݝݝݝ݅0ܥ(݅ܥݝ݅ ݝ݅ݝ݅ܥܥ ܥݝ݅ݝ݅ݝ݅܅ܥݝ݅܅ݝ݅ݝ݅ܥܥܥݝ݅ݝ݅͋Pܭܭݝݝ݅݅ܥݝݝ݅ݝ݅݅܅݅܅ݝ݅܅^݅݅ܥܥ܋ܥ܋^@܋܋^H݅ʋ\ܭʃ\d^݅ܭ^P^݅ܥ^X^ ݅ܭ^`^(݅ܭ^h^0݅ܭ^p^8^x9\P6q} uM)щMMW$ػĞUEB(BB0]B8B ]BB]EE]EeEeeeXX X(XX0XX8.} E)ЉEMV$ػĞUB(BB`ݝBhݝBPݝBXB@BHݝB8ݝB ݝBBݝݝB0BpJxݝݝ݅ݝ݅ܭܭ݅ݝݝ݅܅ݝ݅ݝ݅ܥ݅ݝ݅܅ݝݝݝ݅܅݅݅ݝ0݅܅ܥܥܥݝ ݅ܥܥܥݝ݅ܥϋEݝ݅ܭݝ݅ܭݝݝ݅ܥ݅܅ݝݝ݅݅ݝ(ݝ8݃݅ ݅0܅8ܥ(܋݅ ܅(X݅0ܥ8X@XH݅݅ܭܥX݅ܭXPX݅܅XXX`X ݅ܭX(݅ܭXhX0݅ܭXpX8XxWMDž ƋUDž)щDžDžӥӥӥ9/UL1҉݅p 0% ʊ 017 ʋ Š ֋M L9݅p 1݅pL݅pL݅p݅p ݝxL1݅p ݝ݅p 9ɋL݅x݅ݝhܥxܥ݅hˋܥhʃY Ỷ⋽Y(YY0YY8@9LdMDž ƋUDž)щDžDžӥӥӥ9 UT1҉ 0% ʊ 017 ʋ Š ֋M D91ݝݝxDD1݅D9̋T݅xݝhܥܥx݅hܥhYY Y(YˋɃɉYY0Y8@9TUsU+} |U oDžx EMx)DžpDžlDžhdDž`Dž\DžXDžt|ӥpӥlӥhӥdӥ`ӥ\ӥXx9tM@vt1҉ 0% ʊ 017h ʋp Šx ֋l ы` ։\u ыd>D>X щDɋݝPDݝHݝ@ݝ8ݝ0DDD>ݝ>ݝ(>ݝ݅Pܥ@ݝ D>ݝ݅H݅P܅@܅8ݝDݝ݅Hܥ8ݝ݅(ݝ݅0ݝ݅ ݝ݅ܥ ܅ܥ0ܥ(ݝ݅܅ݝ݅ܥݝ݅ܥݝ݅ܥݝH݅ܥݝ8݅ݝ0݅ݝ(ܭ͋@ܭ݅ܥݝ݅ݝ݅܅ݝ@ݝPݝ ݅݅ܥ܅݅H܅P܋ܥ܋݅8܅@܋܋Z݅HܥPZ@݅8ܥ@ZH݅0͋tܭ0̓t|Z݅(ܭ(ZPZZXZ ݅ ܥ Z`Z(݅ܭZhZ0݅ܭZpZ8Zx9t@fu}FFF___l) uE)։uMe E ػĞ@0@@(]@8@ ]@@̋E]EE]EEeeeeXX X(XX0\Dž EM)DžDžDžӥӥӥ9mMDt&1҉ 0% ʊ 017 ʋ Š ֋M D91DݝDݝD1ˋD݅9݅ݝxܥܥ݅xܥx^ ^^(ʋ^˃≅^0^^8@D9UMEAA0]A ]AA8EA(AE]eeEeX XRM})щMMe E ػĞGG(G`ݝGhG@GHݝGPG ݝݝGXݝGݝG0GݝG8ݝGpOxݝݝ݅ܭݝ݅ݝ݅ݝ݅܅݅ݝ݅܅܅ݝݝ݅ܥݝ݅ݝ݅ܥ݅ܥܥݝ݅ܥܥݝ݅܅ݝܭEݝ݅ܥ݅ݝݝ݅ܭݝݝݝ݅ܥ݅݅ܥܥ݅܅ݝݝ݃ܥ݅܅܋݅܅X݅ܥX@݅ܭ݅ܥX݅XP݅X݅ܭܭܥX ݅܅XHXXX(X`X0݅ܭXhX8Xp+uF@ݝxFPFݝXF8ݝpݝH݅xF`FHܭxݝ8݅pݝ`ܭpFXF(FhݝPFpF FݝhݝFF0ݝ@ݝ0Fxݝ(ݝ ݅h܅݅`݅ݝ݅XܥXݝܥhݝ݅Hܥ`݅Pݝ݅8܅ܥPݝ8݅0܅ݝ݅@ݝ(݅8ܥܥHܥ@ݝ݅0ܥݝ݅(ݝ ݅ ݝܭ ˋEܭ(݅ܥݝ݅܅ݝݝ݅݅ݝ0ݝ@݃݅(݅8܅@ܥ0܋݅(܅0XHX݅8ܥ@X@݅ ܭ X݅XXP݅܅݅ܭܥX ݅ܭXXX(݅ܭX`X0݅ܭXh}EGG0]G GG8]EG(GE]eeEeX XUBXݝݝXB8B@ݝH݅BݝxBHݝ8݅xB(BPݝpݝ0݅Bh݅pB`ܭxݝ`B0ݝhݝPBpB BBݝ@ݝ(Bx݅hݝ ܥpܥh݅Xݝ݅`ܥ`ܥXݝ݅P܅@ݝ݅HܥHݝ݅Pܥ@ݝ݅8ܭ8ݝݝ`݅0݅0܅ ܥ ݝxݝP݅݅(ܭ(ܥݝpݝhݝH݅ܥE݅݅ݝXݝ݅ܥ܅ݝ݃݅xܥ݅܅ܥ܋XH݅x܅X݅ܥX@݅pܭpX݅hܭhXPXXX݅`ܭ`X ݅P܅XX`݅PX(ܥXX0݅HܭHXht&UyM}qwQq w WXt&UWVS [ÒeU}w0}4}+}0}}1e[^_]É} V;UfM)V$ػĞ} ݝ 5U  MDž ƋUDž )щDžDžDžDžDžDžӥ ӥӥӥӥӥӥ91҉݅ 9>΁% Š>% 19 Š ׋ Ћ ׉ ׋ Ћ}  ы} ݅ ɍ΁% Š>D% 19P ŠXꋍH щ0L(@8 щ,$ <}u  ЋT݅`ύ<΋uM݅` ɋ0ݝ݅` ݝ,݅` ݅` ы(݅`ݝ݅` ݅` ֋$݅`ݝ݅` ΋ ݝ݅` ɋݝ݅` ݝ݅` ɋݝ݅` ݝݝ݅܅݅ܥ݅`ݝ݅ݝ݅ݝ݅ ܥݝ݅ܥݝ݅܅ݝ݅܅ܥܥݝ݅ܥݝ݅ܥݝ݅ݝ`݅܅ݝP݅ܥܥݝ0݅܅ݝH݅ܭݝhݝ@ݝ(݅݅ܥ݅ݝXݝ8݅ܥ܅݅`܅h܋ܥ܋݅P܅X܋܋݅`ܥhX ݅PܥX\_ ݅HܭHX݅@ܭ@_X(_(X݅0܅8݅0ܥ8_X0_0X݅(ܭ(_X8_8TT9TADžH EMH)Dž@<Dž8DžDLӥ@ӥ<ӥ8H9DLt&D1҉44݅ 9>΁% Š>8% 19H ‹<ꋍ@ щ0lDM ݅ ΁% Š>$% 190 Š8ꋍ( щ,  щ }u Ћ4ύ<΋uM֋uݝ֋uݝݝ ݅ʋݝݝݝ֋݅ɋݝpʋݝ֋ݝ݅ݝ݅܅ܥݝݝݝ݅݅ܥݝ݅ݝ݅ݝ݅pܥ܅pܥݝp݅ܥܥݝx݅ܥݝp݅܅ݝ݅܅ݝ݅ܥݝ݅ܥݝݝ݅ݝܭ݅x݅ݝ݅܅pܥpݝ݅pݝݝ݅p݅܅݅܅݅݅ܥܥ܋ܥx܋X _ ݅ܭ܋X݅܋_X(݅ܭɋ<܅_(X݅݅ܥܭ_X0_0X݅ܭ_X8_84494} u})׉}MV$ػĞEMU @@A]@A]͋EA]EE]EeEeeeXZXZXZ%} E)ЉEMV$ػĞU}B0GG0ݝݝG(ݝB(B BݝGG ݝBݝxݝGBݝB8O8ݝݝ݅ܭ݅ܭݝ݅܅xݝ݅ݝ݅ܥݝݝp݅܅ݝݝݝ݅܅݅݅xݝ݅܅ݝ݅݅ܥܥܥܥܥݝ݅ܥ͋Mݝ݅ܥʋU ݝ݅ܭݝ݅ܭݝݝ݅ܥ݅܅ݝݝ݅p݅pݝݝ݃݅܅܋݅܅݅ܥY ݅݅ܥܭ݅ܥZ Y݅ܭZ݅܅Y(Z(Y݅ܭZ݅Y0Z0Y݅ܭܭZY8Z8LMDž ƋUDžx)щtDžpDž|ӥxӥtӥp9|t&|1҉ll݅`9>΁% Š>p% 19 ‹xꋍt щdhu| ݅`<΋|L} ݅`ʍ }HM ݅` h݅` ݅` ݝXdL݅` ׋|݅` ыHݝP΃剅|݅Xʋ9|݅Pݝ@ܥXܥP݅@ܥ@^_^_^_7MDžh ƋUDž`h)щ\hDžXDždӥ`ӥ\ӥXl9dd1҉TT9>΁% Š>X% 19h ‹`ꋍ\ щLPud Ѝ<΋d4} }0MPLݝ@ݝ8݅@݅8ݝ(͋4ܥ@ˋ0⋅dܥ8̃݅(ʉdܥ(̋l9d^_^_^_iUU} <U MDžx ƋUDžpx)DžlxDžhщdDž`Dž\DžXDžtӥpӥlӥhӥdӥ`ӥ\ӥX|9t&t1҉9 8% ʊ 8l 1Ɋ >d ʋpxꋍh щTL`X щPHDu \} ʊꋍ щu Ѝ<΋t} }pMݝݝx݅݅xݝhˋtpܥ̓ʉ݅hˋܥh9ܥxZ^Z^Z^sUUEB@]@]BEBËU @̋EE]eeEeXRu})֋UuMe E ػĞBG0B ݝB0G ݝxG(GݝݝB(BݝݝGGݝBݝG8J8ݝݝ݅ܭݝ݅ݝ݅ݝ݅܅݅ݝ݅܅܅xݝݝ݅ܥݝ݅xݝ݅ܥ݅ܥܥݝ݅ܥܥݝ݅܅ݝܭMϋU ݝ݅ܥ݅ݝݝp݅ܭݝݝݝh݅ܥ݅݅ܥܥ݅܅ݝxݝ݃ܥ݅܅܋݅܅݅ܥY ݅݅ܥܭZ Y݅ܭZ݅Y(Z(Y݅p܅x݅pܭܥxZY0Z0Y݅hܭh-}MuUB ݝxF ݝpFF0ݝhF(B0ݝPB8B(ݝ@݅xܭxݝݝ8݅pFBFݝ`ݝXBBݝHݝ0F8ݝ(ܭp݅`݅ݝ ݅h܅ݝܥhݝ݅XܥX݅Pݝp݅Hܥ`ݝݝ݅8܅݅@ܥPݝ݅0܅ܥHܥ@ݝ݅8ܥݝ݅0ܥݝ݅(ݝ݅ ͋}ܭ ϋU ܭ(ݝ݅ܥݝ݅܅ݝݝ݅p݅pݝݝ݃݅݅܅ܥ܋݅܅݅ܥ_ ݅Z _݅Z݅܅ܭܭ݅ܥ_(Z(_݅ܭZ݅_0Z0_݅ܭܭZ_8}uEU GF]FG]EGFE]eeEeX}MUuG(ݝݝXGB ݝH݅ݝxB(G ݝ8݅xݝ`ܭxBB0GݝPB8ݝ0݅BBݝpݝhG0Gݝ@G8ݝ(݅pܥp݅hݝ ݝ݅`ܥh݅Xݝ݅P܅@ܥ`ܥXݝ݅HܥHݝ݅Pܥ@ݝ݅8ܭ8ݝXݝ0݅0݅0܅ ܥ ݝHݝ ݅݅(ݝ@ݝ8ܭ(M݅ܥU ݅ݝ݅ݝ(ݝP݅ܥ܅ݝ`݃݅HܥP݅X܅`ܥܥ܋݅H܅P݅Xܥ`Y ݅@Z Y݅8Z݅0ܭ@ܭ8ܭ0Y(Z(Y݅ ܅(ZY0݅ ܥ(Z0Y݅ܭZ(UtE}pEwu HNOt&'UWVS[Â4U}w,}.}=}~}}. 1e[^_]E;UoM)щP$ػĞUMӥDž A)ω}Dž9BZ77D7\7EEؐt&1Ҋ% Ɖց% NJ%1 ӭӥ9| ؉1Ҋ1 0% ʊ 0 ʊ 0 ʋ ꊍG ‹MȉMA@XY ʋ MʋM;BFRV]]ɋ } ֋ D\77D7\7G9_;UM)ѺW$ػĞEuDž ӥ)Dž9@XEEDž>֋ 8% Ί 8Ё Ί 8 Ί9| t&1Ҋ98% ‰%8% ‰ 8  ʊꊍ⋍ ʋMʋMȋ@BRP@91]]ɋM 򋵤FD\9+^J ك̞UBBBZZZWU REPuVq} h ; M)ѺMMR$ػĞE@(@@ ]@0]]@8@@EEݝxEe݅xeeXܥxXX X(XX0X8tMDžDž ӥA)Dž9EEEu؋}1DžƊ% %% % ӭӥ9Z1Ҋ17ρ% Š7% 7  ʋ}ꊍ  Ήu@A1yx0XY ϋ ‹uEpx Bɉrz BP9݅ݝʋA9QEDžDž Džӥ)9EEEu؋}1DžƊ% %% % ӭӥ9&1Ҋ17ρ% Š7% 7  ʋ}ꊍ⋍ ȋuApBʋx rʉz P92݅ݝʋB93B} Xu)։uMW$ػĞE@@H@(ݝp@`ݝhݝX@Xݝ`@h@PݝH@8@@@ ݝ@@ݝPݝ@@0@pHxݝ8ݝݝ0݅pܥh݅p݅X܅hݝ ݅PܥPݝ(ݝ݅8ݝ݅`݅H܅ݝ݅0܅ݝݝ݅@܅ݝ݅(܅݅ݝ݅ܥHܥ`ܥXܥ@ܥ8ݝ݅0ܥݝ`݅(ܥݝX݅܅ݝx݅ ܭ ݝݝpݝP݅݅ܥݝ݅ݝh݅݃ܥ݅܅܋݅܅X݅ܥX@݅ܥXH݅xܭxX݅pܭpXPX݅`܅hXX݅`X ݅XܥhܭXX(X`X0݅PܭPXhX8XpXx كĞ/ju})։uMe E ػĞG(GG8]G0]G GG]EE]EeEeee__(__ __0_8#MAݝ@A0A Aݝ8A8݅@A(A݅8]ܥ@ܥ8EeYY YY(YY0Y8pM)ыUMMe E ػĞBB(B`ݝ0BhB@BHݝBPB ݝ(ݝBXݝ BݝB0BݝB8ݝBpJxݝݝ݅0ܭ0ݝ݅ݝ݅ ݝ݅܅݅(ݝ݅܅܅ݝݝ݅ܥݝ݅ݝ݅ܥ݅ܥ(ܥ ݝ݅ܥܥݝ@݅܅ݝ0ܭݝ݅ܥ݅ݝ(ݝ݅ܭݝ8ݝ ݅ܥ݅ܥݝ݅݅ܥ܅ݝݝH݃ܥ݅@܅H܋݅0܅8Z݅@ܥHZ@݅(݅0ܭ(ܥ8Z݅ ܭ ZHZ݅ܭݕ(Z ݅܅Z`݅Z(ܥZPZ0݅ܭZXZhZ8ZpZxE@(ݝ@@ݝ@Pݝݝpݝ@8@p@@@Hݝݝ݅݅@`ܭܭ@hݝ@X@ @ݝxݝ@0@xݝݝݝݝ݅݅݅ݝ݅pܥ܅x݅xܥݝ݅ݝݝ݅ܥ݅܅pݝx݅ܭܥݝ݅܅ܥܥݝpݝ݅ܥݝh݅ݝ݅ݝܭ݅ܥxܭݝX݅܅xݝx݅݅ݝݝ`݅ݝ݃ܥ݅܅܋݅܅X݅ܥX@݅ܥXH݅ܭX݅XXP݅p܅xܭX ݅hXX݅pX(݅`ܥxܭhܭ`X0݅XܭXX`mUBݝPB0B BݝHB8݅PB(B݅H]ܥPܥHEeZ ZZ(ZZ0ZZ8E@ݝ0@X@@ݝ@8@(ݝ@p@ ݝ@xݝ(ݝ@Hݝݝp݅0@`@hܭ0݅(ݝ @Pݝ`݅ @@܅ݝݝh@0ݝX݅ܥ ܥ(݅ݝ݅ݝP݅ݝH݅܅ܥܥݝ@݅ܥܥݝ8݅pܭpݝ݅h܅Xݝݝ݅hܥXݝ݅`ܥݝ݅`܅ݝݝ݅@݅P܅H݅8ݝݝܥ8݅PܥHݝ݃ܥ@݅݅܅ܥ܋݅܅X@X݅ܥXH݅ܭX݅ܭXPX݅XXX ݅܅݅ܭܥX(X`X0݅ܭXhX8]]$ʉu؉}]]]UWVS|[U}w,}.}B}~}} 1e[^_]E;UFu)։P$ػĞUDž )֍BUDžӥM 9Qt&1Dž% Ɖց% lj% ӭӥ9:1Ҋ17ρ% Š7% 7  ʋꊍ ‹M <ыM 4ɋM ʉM ˋɋ M u<֍4ƍэ@9 NjUM ɋ F ɋ9؋} WuVM QURL;UOM)щR$ػĞ}Dž )ӥM} Dž9]1Ɋ 27ׁ% 7% 7%1 ӭӥ91Ҋ1 0% ʊ 0 ʊ 0 ʋ ꊍG ;u ֋u ֋u4ɉu ʋ@ uU G9}J\ ك̞UM BAZY}   })׉}MV$ػĞM EAA@]@]]A@EE]EeEeeeXYXYXYUDžDž Džӥ)991Dž% Ɖց% lj% Ɖӭӥ91Ҋ17ρ% Š7% 7  ʋ}ꊍ ‹ u ׉4ƋM <ыɋ@91B9hUDžDž DžӥB)щ9H1Dž  % Ɖ ց % lj  % Ɖ ӭ ӥ 9G1Ҋ17ρ% Š7% 7 } ʋ ꊍ ⋍ ʍ ׋} 4׋} Nj} 4Nj} ʋ u ׉4ƋM <эBɉ9B93} NM)ѺMMR$ػĞ} EG Gݝ@0ݝݝG(ݝG0@(ݝpG@ @ݝG@ݝxݝh@@8O8ݝ`ݝ݅ܥݝX݅ݝH݅xܥx݅܅ݝ݅`ݝPݝ@݅݅p܅ݝ(݅X܅@ݝ8ݝ0݅h܅ݝ݅P܅8݅ݝ ݅ܥpܥܥܥhܥ`ݝp݅Xܥ@ݝP݅Pܥ8ݝH݅0܅ ݝh݅HܭHݝxݝ`ݝ@݅(݅0ܥ ݝ݅ݝX݅݃ܥ(݅܅܋݅p܅x݅ܥX ݅pܥx_ ݅hܭhX݅`ܭ`_݅P܅XX(_(X݅H݅PܥXܭH_X0_0X݅@ܭ@_X8_8uكĞu)֋U uMe E ػĞMBBB]AA]A]EE]EeEeeeYZYZYZM uAFݝ0FAݝ(݅0AF݅(]ܥ0ܥ(Ee^Y^Y^Eu} )֋EuMe E ػĞG@0ݝXG0@ G ݝ@(@ݝPݝ@G(ݝHGݝ8@@ݝ0Gݝ(@8O8ݝ ݝ݅XܭXݝ݅@ݝ݅Hݝ݅0܅ ݅Pݝ݅(܅܅ݝݝ݅ܥ(ݝ݅ݝ݅0ܥ ݅8ܥPܥHݝ݅ܥ@ܥ8ݝ0݅܅ݝ ܭݝ݅ܥ݅ݝݝ݅ܭݝ(ݝ݅ܥ݅ܥݝ݅݅ܥ܅ݝݝ8݃ܥ݅0܅8܋݅ ܅(݅0ܥ8X ݅݅ ܥ(ܭ_ X݅ܭݕ_݅X(_(X݅܅ܭ_X0݅ܥ_0X݅ܭ_uU }G0ݝU ݝBݝݝG(B(ݝG8G B ݝݝ݅݅B0GݝBGBݝxݝGB8ݝݝ݅܅ܭܭ݅ݝ݅ݝ݅ݝ݅xܥܥݝݝ݅݅܅xܥݝ݅ܥܥݝ݅ܭܥݝ݅܅ݝ`ݝ݅ܥݝX݅ܭ݅ݝx݅ܭݝPݝp݅ܥݝH݅܅ݝh݅ݝ݅ݝ݃ܥ݅܅܋݅܅݅ܥ_ ݅ܥZ ݅x_݅pܭxܭpZ݅`܅h_(Z(_݅X݅`ܥhܭXZ݅P_0Z0_݅HܭPܭHZ_8Z8} UGBݝ@BGݝ8݅@GB݅8]ܥ@ܥ8EeZ_Z_Z_4U Mݝ B(A ݝBBݝA8AݝB8ݝݝB ݝ]݅ A0B0ܭ ݅ݝA(]݅AB܅ݝ]A]݅ܥܥ݅ݝ݅ݝx݅ݝp݅܅ܥܥݝh݅ܥܥݝ`EmݝEEݝݝEeݝEܥݝE܅ݝݝ݅h݅x܅p݅`ݝݝܥ`݅xܥpݝ݃݅ܥ݅܅ܥh܋݅܅݅ܥY Z ݅ܭY݅ܭZ݅Y(Z(Y݅܅݅ܭܥZY0Z0Y݅ܭZY8Dv'UWVS[Ó#DžM}⍍u }w0}2}}}:1}e[^_]Á w,jEPWuVx1e[^_]à Rr4ttQj}WRuV)^R4O MUك̞AZs;} M)V$ػĞ} ݝ0c U MDž( M(Dž )DžDžDžDž DžDž$ӥ ӥӥӥӥӥ ӥ,(9$=$1݅077ρ% Š7% 11 Š(ꋍ ЉlE ։ ׋$h ȋ$ M݅0 Lɋlݝݝlhl݅0 ݅0hLɋhݝ݅0݅0 ݝL݅0݅0 1ݝL1݅0݅0݅0L1 9݅0L9ݝp 1ݝݝ݅0 1݅0ݝ݅0 ݅L1݅ݝ݅ܥݝ݅0ݝ݅ܥL܅܅ݝ݅܅pݝ݅ܥݝ݅ܥݝx݅pܥݝp݅܅ݝp݅ܥݝh݅ܥݝ`݅ܥݝ݅ܥݝ݅ܭݝݝ݅݅x܅pܭݝݝ݅h݅xܥpݝݝݝ݅p݅`ܥ`܅p݅܅܋ܥh܋܋܋݅܅݅ܥ^ ݅ܥ_ ݅ܭ^݅ܭ_^(_(^݅ܥ_݅^0_0^݅ܭܭݕ_ݕ^8_8$,$9$juVQEP WX M)MMV$ػĞUEB BB0BB8XXXul U)UMV$ػĞE@ @@@(ݝX@Xݝ@`ݝx@0@@ݝp@8@xݝݝ@P@hݝh@p݅X܅ݝ`݅ܥݝXϋEܥ݅pݝ ݅x܅`ܥxݝ@݅hݝp݅pݝPܥ`݅pݝ݅@܅ ܥPݝHܥhݝ ݝ@݅H݅XܥH݅ ܥ ܋ܥXɀG܋X ݅ܭX݅@ܥ@X(XX0XX8+MDžX MXDžP)ȉLDžHDžTӥPӥLӥH\X9TT1݅077ρ% Š7P% 11H ŠX ׋ElLT  <4T݅0ɍNj}0lL݅0ʉl Ll݅0݅0݅0 L ݝ8݅0 74݅0L7Tݝp͋0݅8ʉTϋ\9T݅pݝ(ܥ8ܥp݅(ܥ(Z^Z^Z^/U}uG^} U tMDž8 M8Dž0)Dž,Dž($Dž DžDžDž4ӥ0ӥ,ӥ(ӥ$ӥ ӥӥ<8944177ρ% Š7 % 11( Š8ꋅ$ Љ Љ4E0 ֍>΁% Š>% 19 Šꋅ щ ֋ Љ, Љ( $E݅ Lݝxݝp݅݅ Lݝhݝ`݅݅Lɋ,L8,݅݅ 0ˋ$ݝXL0,݅ݝ(݅݅ 8ˋ( 0(݅L0 ɋ(ݝPݝH݅݅ Lݝ@ݝ8$$݅ 0݅L0݅xݝ0݅xܥh܅h݅p܅`ݝ݅pܥ`ݝ݅Pݝ݅Xݝ݅Hݝ݅(ܥH܅(ܥXܥPݝ(݅@܅0ݝ݅8ܥ8ݝ݅@ܥ0ݝ݅ɋܥݝ݅ܥݝ݅ܭݝ݅ܭݝݝݝ݅݅݅܅ܥݝݝݝ݅݅(ܥ܅(݅܅܋ܥ܋܋܋݅܅_݅ܥ_@݅ܥ_H݅ܭ_݅ܭ_P__X_ ݅ܥ_`_(݅ܭ_h_0݅ܭ_p_8Wxݝ9 PjRMQgM֋8M4D9ݕثĞݝ ك̞ك(ɋMWݝݝDM@D܍܍܍܍\X݅݅ʃʃJ܍܍HAʋ4ɋ4ك$Qɀt9\9 1e[^_]ËU Dž MDž)DžDžӥӥӥ9}&1݅00% ‰%݅Ɋ0% 11 Šꋅ щ E L݅L݅ 0݅L݅L0ݝ݅ ݝ(݅ ݅݅(ݝ͋ܥʃܥ(ω̋݅ܥ_ __(__0__8@9e&MI LʃVDž ׋MDžDž)DžDžDžDžDžӥӥӥӥӥӥӥ9Wu1>>΁% Š>% 19 Šꋅ щ ֋  , Љ( $EDɋݝxDݝpݝhDD808ݝ`ݝXD0ݝ(,($,,((8$D8ݝP$ݝHݝ@8Dݝ0݅xܥh݅x݅p܅hݝ݅pܥ`܅`ݝ8ݝ݅Pݝ݅XD8ܥXݝݝ݅(ܥH݅H܅(ܥPݝ(݅@܅0ݝ݅8ܥ8ݝ݅@ܥ0ݝ݅ܥݝx݅ܥݝh݅ܭݝ`݅ܭݝH݅ݝ@݅܅ݝXݝݝp݅ܥ݅ݝP݅(ܥ܅(݅x܅܋ܥ܋݅h܅p܋Z݅xܥ܋Z@݅hܥpZH݅`ܭ`Z݅XܭXZPZZXZ ݅PˋܥPʃZ`Z(݅HˉܭHɋZhZ0݅@ܭ@ZpZ8Rx9ݝ@~U Dž MDž)DžDžӥӥӥ9A}100% ‰%0% 11 Šꋅ щ EDD0Dݝݝ(D0݅݅(ݝܥܥ(݅__ 勵_ܥ˃ʉˋ_0_(__8@9MAك$Qɀt94\9!U)DT DT 1;|ʍe[^_]Ã}u)R$ػĞ}GG(G@ݝ G`ݝGhݝGPGHG ݝGXݝ GݝG0GݝG8ݝGpOxݝݝ(݅ ݝݝ݅ܭ ݅܅ ݝ݅܅ݝ݅ݝ݅܅(ݝݝ݅(ܥ݅ ܥ݅ݝ(݅ܥܥܥݝ݅ܭܥݝ݅܅ݝXݝp݅ܥݝH΋E݅܅ݝh݅ܭ݅(ݝݝ@݅݅ܥܥ(ݝ`݅ݝxܥݝP݃ܥ݅܅܋݅p܅xX݅ܥX@݅pܥxXH݅hX݅`ܭ`ܭhX݅XXX݅HX ݅H܅PܭXܥPXPX(X`X0݅@ܭ@XhX8XpXx݃ك̞ك(Uݝݝ݃xuDD܍܍܍܍\E݃xʃ\ʃIM܋xJFʋMɃxvOك$ݞVݞ&}AQTQDT ~}u)R$ػĞ}EG0GG(]G8G ]GG]EE]EEeeeeXX ̾ X(XX0XX8Eك̞@8@0@@͉Ƀ8݃ك(ˀp/XX0@X8Xك$X@PXH}AQTQDT ~/MVMu$AAA؋ɃVك$Vݕ8ɋ<8V~^ ^(^0^8}tWك̞EM@ك$YQYO}Mك$wqY1كĞ맋MA]AhA`ݝ(AX]]A8]APA@AHݝpEEm]mA0A(ݝxApA A]ݝhAAxݝ`ݝXݝP݅(eE܅(Eݝ(݅x܅hݝHݝ8݅peEݝ0݅xܥhݝ@Eݝ(ܥpeݝ0݅`܅Heݝ ݅`ܥHݝ݅X܅(ݝ݅PݝܭPE݅Xܥ(ݝ݅@܅8ݝ݅0ݝ8݅@ܥ8ܥ0ݝ݅(ܥ(ݝ(݃݅0܅8܋݅ ܅(X݅0ܥ8X@݅ ܥ(XH݅X݅XܭܭX ݅܅XX݅X(݅ܥܭXPX0݅ܭX`XhX8ݕMEAA0]A AA8]EA(AE]eeEeX Xe'UWVSd[çuMU} }9)ѺR$ػĞ ݝ~V Dž NDž)DžDžDžDžDžDžӥӥӥӥӥӥӥ;}$!U׋1݅00% ‰%݅0% 11 Šꋅ Ћt щp  Љl hE ݅ Lɋݝ݅Lݝ ݝ݅Ltt݅ ɋtݝ݅݅ ݅Lʋpp 0݅L0ʋlpݝ݅ 0l݅ݝ݅L0ɋl 0ݝ݅L0ݝhhh݅ ݅ܭݝx݅LݝX݅ݝ݅܅ܥݝ`݅ܥݝ݅xݝP݅ܥݝH݅ݝ0ܥݝ@݅ܥݝ8݅܅ݝ(݅H݅ܥܥxݝ݅`܅@ܥHݝ݅`ܥ@ݝ݅XܭXݝ݅PܭPݝݝݝ݅0݅8݅8܅(ܥ(ݝ݅ݝݝ݅݅܅܋ܥ0܋܋܋݅܅_݅ܥ_@݅ܥ_H݅ܭ_݅ܭWP_݅ܥ_X_ ݅ܭ_`_(݅ܭWh_0݅ܭ_p_8ݝݝ_xM$9d[^_]Ðt&9Z)щeEػĞ ݝ~V_ Dž NDž)DžDžDžDžDžDžӥӥӥӥӥӥӥ;}$Mω1݅>>΁% Š>u % 19 Šꋅ щl  Љh Љd Љ` \݅ ݅݅Lݝ݅ ݅LݝL>ݝ >ݝݝhlhhl݅Ldl`d݅݅ ݝLd݅݅ Lɉ``\ݝݝ݅݅ >݅L> ݝݝx\\݅ ݅L݅ݝp݅ܥ܅݅܅ݝP݅ܥݝH݅܅ݝ@݅ܥݝ8݅ܥݝ0݅ܥݝ݅܅pݝ(݅xܥxݝ ݅ܥpݝ݅@ɋܥ@ݝx݅8ܥ8ݝh݅PܭPݝ`ݝH݅H݅0܅(ܭHݝXݝ݅ ݅0ܥ(ݝ@ݝpݝP݅݅ܥ܅݅x܅܋ܥ ܋܋܋݅h܅p^݅xܥ^@݅hܥp^H݅`ܭ`^݅XܭX^P^^X^ ݅PܥP^`^(݅HܭH^h^0݅@ܭ@Vp^8^xݝH}$9Dž$ N$Dž)DžDžE$ӥӥӥ$ 9M<1 1݅00% ‰%݅Ɋ0% 11 Š$ꋍ щu   L݅݅ ݝL݅ ݅ ݅ݝ݅LL݅݅ݝʋ ܥσܥΉ ɋU$݅ܥ__ __(__0_8@9 `d[^_]É ~VaDž NDž)DžDžDžDžDžDžӥӥӥӥӥӥӥ;}$!U׉v1>>΁% Š>% 19 Š ׋t щpt ׉lt ׉h d} ݝDtDDݝݝݝݝDlpllphpdh7D7hݝDddݝD7݅ݝݝxDܭݝ݅܅7ݝPݝX݅ܥݝH݅ܥ݅ܥݝ݅xݝ@݅ݝ(݅@ݝ8ܥݝ݅X܅8݅ݝ݅Xܥ8ݝ0݅ݝ݅P܅ܭPݝ݅Hݝ ݅ܥܥ@ܥܥxݝݝܭH݅0ܥ ݝ݅(݅0ݝݝ݅݅܅ ݅܅ݝ݅܅^݅ܥ^@݅ܥ܋܋^H݅^݅܋ܥ(܋^݅̋ܭ΃ܭ̉^ ݅ɋM$ܭܥVP^(݅ܭ^X^0݅ܭ^`Vh^8^p^xɃ9ݝݝGRDž NDž )DžDžӥ ӥӥ;}$Elj1݅>>΁% Š>u % 19 Šꋍ ы  L݅ >݅L݅L݅݅L>͋ݝ ݅ ݝ݅ ݅݅ݝܥܥ݅ˋܥ΃_ _̉̋M$_(__0__8@9uZDž NDž)DžDž ӥӥӥ ;}$U׉1>>΁% Š>u % 19 Šꋍ ы Dݝ>DݝD>⋽݅D݅ݝܥܥ݅ܥ__ _(_ʋ뉵M$__0_8@9 ~VE NEE)EEEEEEEeeeeeeẻ};}$U׉M苻1>>΁% Š>% 19} ŠM ׋E䉽tM} щpt} l}Љt ׉h} d} DD]pDt]D]]]phlplDlhDݝdhdd]7]]D7ݝxEeEEݝXEeEE]DݝPE܅ݝHEeݝ@Eeݝ8݅eݝE܅xݝ0Eeݝ(Eܥxݝ ݅HܥHݝ0݅@ܥ@ݝ ݅XܭXݝ݅PܭPݝݝݝ݅8܅0݅(ݝ8݅8ܥ0ݝ(ݝ݅݅ ܥ ܅݅0܅8܋ܥ(܋݅ ܅(܋܋^݅0ܥ8^@݅ ܥ(^H݅^݅^ϋEܭ˃ܭΉE΋M$^ ݅ܥ^P^(݅ܭ^X^0݅ܭ^`^h^8Vp^x9MݝDž NDž)DžDž ӥӥӥ ;}$U׉1>>΁% Š>u % 19 Šꋍ ы D>DݝDݝD>ˋ݅݅ݝܥܥ݅ܥ_ __(_ˋ_0__8M$@9UWVS[ÇE uU}$9 )щP$ػĞ ݝ~VJ Dž VDž)DžDžDžDžDžDžE(ӥӥӥӥӥӥӥ91>΁% Š>% 19 Šꋅ щ|x щtplEu d ׋h݅<ȍ΋uE ݝ`݅ ݝXE|݅ M݅ x݅ݝP݅ ݅ t݅ݝH݅ ݅݅ݝ@݅ ΋p ̋lݝX݅ ɋhݝ8݅ ݝ0 ݝ(d݅ ݅`݅ݝ ݅X܅Pݝ݅`ݝ݅@݅XܥPݝ݅0ݝ݅Hݝ݅Xܥ8ݝ݅8܅XݝX݅ ܥ0ܥHݝ݅(ܥ@ܥ(ܥ ݝ݅܅ݝ݅܅̋ݝ݅ܥݝ`݅ܥݝX݅ݝx݅ܭݝݝpݝP݅܅݅ܥ݅Xݝݝh݅X݅܅݅܅܋ܥ܋܋܋݅ܥu(_ ݅ܥY ݅xܭx_݅pܭpY݅`܅h_(Y(_݅X݅`ܥhܭXY_0Y0_݅PܭPY_8Y89Ĥ[^_]Ð9I)щP$ػĞ ݝ~V FDž DžDžDžDžDžDžDž)ӥӥӥӥӥӥӥM(91>΁% Š>% 19 Šꋅ щ щEu ׋݅<ȍΉDME݅ ݝ݅ ݅ 񋵨݅ݝ݅ ݅ 񋵤݅ݝx݅ ݅ ݝp݅ ϋݝh݅ ͋ݝh݅ ݝ`ݝX݅ ݅ ݅`܅hݝPݝH݅܅݅ ݝ8݅ܥݝ0݅pܥpݝ(݅xܥxݝ ݅hܥhݝ݅hܥ`ݝh݅X܅Hݝ݅Pݝ݅XܥHܥPݝ݅(ܥ(ݝ@݅8܅ ݝ0݅8ܥ ݝ݅܅ݝ(݅0ܭ0ݝHݝ ݝ݅݅ܥ݅ݝ8ݝ݅hܥ܅h݅@܅H܋ܥ܋܋܋݅0܅8D݅@ܥH_ ݅0ܥ8Y ݅(ܭ(_݅ ܭ Y_(Y(_݅܅݅ܥY_0Y0_݅ܭY_8Y8}(9>K&Dž FDž)DžDžӥӥӥ;}(1҉݅݅0% ‰%݅0% 11 Š ֋ ыuT щE ݅4E݅ ЋE݅ ЋET ЋE ͋Eˋ ˋE ݝU;}(݅ ʋU݅ݝ݅ ʋ݅ܥܥݝ݅ܥZ^Z^Z^EĤ[^_]Ít& ~V^FDž DžDžDžDžDžDžDž)ӥӥӥӥӥӥӥM(9c1>΁% Š>% 19 Šꋅ щ щEu | ׋<ȍ΋u4Eݝ`ݝpME݅`ʋݝhݝ`ݝ(ݝXɋݝX񋵀ݝP|ݝ@ݝHݝ8݅p܅hݝ ݅X݅`݅pݝܥh݅HܥXݝ݅`ݝ݅XܥPݝ݅P܅XݝX݅8ܥ`ܥHܥ8ݝ݅@ܥ@ݝ݅(܅ݝ݅ ܅ݝ݅(ܥݝ݅ ܥݝx΋4݅ݝ݅ܭݝݝݝp݅܅݅ܥ݅Xݝݝ݅X݅܅܋݅܅݅ܥ܋ܥ܋_ ݅ܥ܋Y ݅_݅΋ܭʃܭΉY݅܅_(Y(_݅x݅ܥܭxY_0Y0_݅pܭpY_8}(Y89~Dž VDž)DžDžщU(ӥӥӥ9~1>΁% Š>% 19 ‹ꋍ щM ݅΁% Š>% 19 ‹ꋍ щM Ѝ>΁% Š> % 19 Šꋅ щ< E<} Nj}D<<֋t 8% Ί 8Ё Ί 8 ΊxM9d| d1Ҋ9d8% ‰%8% ‰ 8d  ʊxM⋍t ʋMʋMȋt@BRɋdBdP9d8]]tM |tFɉt9tD\{EE eċUA)EuĉEU9uu1EUUu% Ɖuց}% NJ}%}M lj}mMe9UvM1Ҋ1M7ρ% Š7% 7 u ʋ}MM Mu Ήuu1yA0xuċ} @ɉ}XE ‹EEEuYMʋ8@HBʉ:JZUMBUX9MuUFu9U~)EUE Mue)E}u9}u1DždUUu% Ɖuց}% NJ}%}M lj}mMe9dd1Ҋ1d7ρ% Š7% 7 d ʋEM M}u Mʋ8@HBʉJ:ZdMBdX9dKUEBU9EvUWVS[÷vuE>9f)¸щW$ػĞEE EEeMz)щ}U MM9MU1E 2U7ׁ% 7% 7% MMmMe9UM1Ҋ1M7ρ% Š7% 7 E ʋ}MM ‹ME <ыM 4ɋM ʉMM MMMˋMɋMMu M u<֍4ƍэʋuE@E9u}E NjUM uɋE uF}u ɋU9UI؁Č[^_]9)¸щW$ػĞ}|x|ӥxM Džt tDžp)xt9pot&p1Ҋlpl% ljlׁl% Ɖllt% ljl1ӭl|ӥl91Ҋ1 0% ʊ 0 ʊ 0p ʋutꊍ| ‹l G ;pu ɋu4ʉhu Ƌh@pl E} pFpx9p]EE ыue)֍zEM}u9Mu䋋1EUUu% Ɖuց}% lj}uM% ƉumMe9UvM܋1Ҋ1M7ρ% Š7% 7 } ʋu܊MEM u M<э ։}؉Mԋ}M 4Ǎu1Njuf)f>IuuۋM}UM̍4y}uu WMM,1^_]ËE&'UWV,EM }EMMEMU}MMJ҉E3U̐t&UuuЋEHpx @EE@E܋E@E؋E@Eԉ1)‰1)1)Ɖ1)NjE1E)EE1E)E܋E1E)E؋E1E)EԋEHx M܋}pxHu؋M}ԃ px MMЉE?E;E}'M)u>uu1)lj>IuuދM}UM̍4}uu MM,1^_]ËEUWVS|[ cEpxH upP}Mx uH(p,U}܋PUAEAQx9uvM)}уEЋMUtLGUxA9EAQ}uȋ}%}ԋuЋ}NjEuu11ƊU481}E׋}‹}1 Uщu1ҋuQA֋}411ҊA׋}‹E 1uuQA׋U1ҋu1uAƋu UA%1AU1ƊE1AM‹ME %UPHUMPMUUEUuMщuыEE9%}8p}uxp}u}UЋMEtLGUxA9EAQ}%}ƋE11ҋ}U1}E‹}1 1҉u4MVFыM E1Ҋ1FEыE U|N1ɊN1ϊM1MF‹M1 U1ҊVFыM1ŠE1FuuʋE M׋|UuΉ΍>t(UUuBU9u|1[^_]ËMfvUWVS[]}GOw EGWMuOUAEhAQ9|vf"|ЃuU}Dtx} 7|UAEAQ|h9|uȋ}%}uċ}u}NjE11ҊU481}E׋}‹}1 Uщx1ҋuQA֋}411ҊA׋}‹E 1tuQA׋U1ҋu1uAƋu UA%1AU1ƊE1AM‹ME %UPHUMPMUUExtMщtыtuE%98}xp}ux}| %}ƋE11ҋ}U1}E‹}1 Up4 M1ҊFVы} 11ҊF׋}‹E UlEN1ɊN1ϊM1MF‹M1 U1ҊVFыM1ŠE1FuuʋM pu׋U΋l:t?|MAM}UuBU9ufČ1[^_]Ë|ŋEUWVS[×X}GOw EGWMuOUAEhAQ 9|vf(|ЃMUtIM܋|} 7|UAEAQ |h9|uȋ}%}uċ}u}NjE11ҊU481}E׋}‹}1 Uщx1ҋuQA ֋}411ҊA׋}‹E 1tuQA ׋U1ҋu1uAƋu UA%1A U1ƊE1AM‹ME %UPHUMPMUUExtMщtыtE9%}8}x}x}x}| %}ƋE11ҋ}U1}E‹}1 Up4 M1ҊF Vы} 11ҊF׋}‹E UlEN1ɊN 1ϊM1MF‹M1 U1ҊVF ыM1ŠE1FuuʋM pu׋U΋l:t?|MAM}UuBU9uVČ1[^_]Ë|ŋEUWVS['S}GOw EGWMuOUAEhAQ 9|ve!|ЃuU}Dt} 7|UAEAQ |h9|uȋ}%}uċ}u}NjE11ҊU481}E׋}‹}1 Uщx1ҋuQA ֋}411ҊA׋}‹E 1tuQA ׋U1ҋu1uAƋu UA%1A U1ƊE1AM‹ME %UPHUMPMUUExtMщtыtuE%98}xp}ux}| %}ƋE11ҋ}U1}E‹}1 Up4 M1ҊF Vы} 11ҊF׋}‹E UlEN1ɊN 1ϊM1MF‹M1 U1ҊVF ыM1ŠE1FuuʋM pu׋U΋l:t?|MAM}UuBU9ufČ1[^_]Ë|ŋEUWVS[M}GOw EGWMuOfN}Mf~fN}`}Eԍ 7ffyfq|fQx֋UDBd9EűU}MDftxf>fN}Mf~fN}`} 7ffyfq|fQx֋UUd9UUċEЋỦEЋE‹EŰUЋ`UU‰‹E‹EEEEEEEE‹xtUU>}|}ljxxuNjE<fwfOpUffG΁M%ыMMUfOfG%ʋUfEfG}%Nj}сUffEUfGfEfGfEfG}fpE΋EUtpʋpЍ0 uE%8H}Mpxu}ouf}̋UMDtHdvUEЋ`UU‰‹E‹EEEEEEEE‹xlUU>}|}ljxxuNjE<fwfOhUffG΁M%ыMMUfOfG%ʋUfEfG}%‹}ыEUl}΋h׉h׋hǍ7 =~(MfMEAM9E[Ĕ1[^_]Å~#uf,Mf-Ef UWVS[/}GOw EGWMuO}|}ljxxuNjE<fwfO dUffG΁M%ыMMUfOfG %ʋUfEfG}%NjtсUffEUfGfEfGfEfG}fdE΋EUhdʋdЍ0 up%8H}Mpxu}mlftpMu܍DQTbEEU‹EEEEEEEE‹x`UU>}|}ljxxuNjE<fwfO \UffG΁M%ыMMUfOfG %ʋUfEfG}%‹}ыEU`}΋\׉\׋\ύ7 =~}ủU9~ UuBU9uE؋MU}uMM}|}ljxxuNjE<fw fOdUffG΁M%ыMMUfO fG%ʋUfEfG}%NjtсUffEUfGfEfGfEfG}fdE΋EUhdʋdЋM0 p%ȁ8H}Mpxu}klftpMUDvЋT`vEEU‹EEEEEEEE‹x`UU>}|}ljxxuNjE<fw fO\UffG΁M%ыMMUfO fG%ʋUfEfG}%‹}ыEU\U΋`ʉ\\ʍ2 =~}|}ljxxuNjE<fwfOdUffG΁M%ыMMUfOfG%ʋUfEfG}%NjtсUffEUfGfEfGfEfG}fdE΋EUhdʋdЍ0 up%8H}Mpxu}mlftpMu܍DTbt&EEU‹EEEEEEEE‹x`UU>}|}ljxxuNjE<fwfO\UffG΁M%ыMMUfOfG%ʋUfEfG}%‹}ыEU`}΋\׉\׋\ύ7 =~Xv'UWVS [g|Ur JzBrʍ&'UWVS["}GWO GW wO,G(W0wŰMBU9MD1^_]99t&'UWVLEPpxUP H u}pUxMMu΋MuЃ)‰EUtb}}UM;}s G;}r}U uMEMuMUE}z^_]ËEE9wvE0PHWOGu ;}uvluMϊGʃ)щU҉M0MM98vU֋JR ȈOGWu;}vuu9uVNWOʃ Wu;}v&UWV(EUM~aE܋}u} ~EtSttƃ9wBA9v;MsME ME}UM܉EMu(^_]ËU9Us}uMAUtu뎉֋E)ωuUtRUUu;}sA;MsBA;MrUM ʉUQE9w&uVNffOЃ ufG fW;}vyUE92fMuЋQAq MfGffO fWU ;}UvuE9UEUpH@ fwE؉fOM ‹u ffOu;}vuE9MqЋIfwffO fWU ;}Uvbv'UWV EMU~|EEu}  9@v'UWV0UEuM<@U}E}NjE }~U v;Ms >9JuFuE u9w5vuVvuЋEEЃ Q}Y E;Mv;Msu~Q;Usyt&uM EMP$E}u G}9uEY؃[^_]Ð}fU fUm]mevUWVS[uك$V:U1ɉEE NEM9E#ك(nًU(u֋}ЋM!" Ј11VM$P$EMu AM9uEʋ} Ƀ} EtEE}܋uM()}Uω1փЋM!" Ј1}E܊>P$EMu AM9uES؃[^_]Ðt&}fU fUm]m]vUWVS [z}u W:1҉MWEU}U$BBU E؉u9U`ك$ك(EۋU(u֋}ЋM!" Ј11Qu$7P$}‹u܃};U$}E؃vʋ}܃B;U$|ًMUAMu U9uE@EuXEE(}MNjuU)Ή1փЋM!" Ј1}EԊ>}fU fUm]m؃ [^_]Ð&UWVSUE p`JuxH rHp[_J$8dp}$x ȉ6u U)EuJ)ƉXN}} ȉTكĞ~كɃ؉VUE‹MX$t1ҋENjT 9}ɋpB9|؋M(EQƋXDu DžD 4V1;}1xDxA;|狕DDX֋Dž 1|9}ك$@9|؋T~M(QhNLx1ҍ4;t}(t&12P$|B;|܋pDžPXl9Pt&1;}\N&QlRPtR|Q\;}u)~뤋MuE 0R px' `9pt pRe[^_]ËlPMFPϋXl9P dWTRPQu(VlWXtT4R|WVhB09|)񉍄L V`9pt pR1ҍe[^_]ÃPHUWVS[×UEr zu}u JƉM;u~ EEE Mu;M~ }E}}tw e[^_]E ؋J}rOuԉME}y ҉UMUąU tMċ}ȋUEE Mf49E}YUM)ʋEЉU1UЉUE9E}$UȋM:ȋMԍAEM+Ef2HuM̋UA׉M̋M 9M|Džh}9h}jMU)ʋEЋM}ȉUOME9E}.EUNjMȋMԍAEM+Eɐt&f2HuhMBOh9h|1;}}AEM~!UȋEЋMԍAEMɍvf2HuMUGM;}|Džd}9d}OE~!EЋMȋMԍAEMf2HudUAOd9d|KzuBNE}Euy+҉UMUU t}UMɋ41M;} }jUM)EMUMщUt&M9MqUEЋMȋMEM+Et&2HuMUGM;} |Dž`E9`}iMU)ʋEM}ȉUOME9E5EUNjMȋMEM+E2Hu`MBO`9`|1;}}=EM~UEЋMEM2HuMUGM;}|Dž\}9\}OE~!EMȋMEM2Hu\UAO\9\|HrzUuJEUyPt&҉UM=U؅U tM؋U܋uɉME U 9U}VUE)1UE׉tE9E} U܍2UЋt‹E+E HuUBUU֋U 9U|Džpu9p}oEU)‹uEUNxt&U9U;EUЋUЋx‹E+E HupEBNp9p|1;u}EDž|U~ U܋|ЋUЋUt&EJu|F;u||Džlu9luN&U~!UЋUЋU&EJulEBNl9l|?VE P}WuVMQ}Wu VRkO e[^_]ËEMGE;} npNBpU9p`UAO`9`1e[^_]Ít&'U1WVuS,[ûM9w$P}WVuV}WEPU RQe[^_]PURVMQ}WuVU RMQe[^_]ÐUWVS\[GMEu Qy U}QUU;U~ EEE}E;}~ UEU}twE ve[^_]ËA~E̋QF}ȉE}MEUOM}yR ɉMMD MM tMEɋuM9u}U)Džpt&p9E Uċ}uU }̍4O}p}ȍ GE})}ffJuUB}UE9EqDžu9}UE)Hll9M}hM苅Uċ}uU }̍4O}l}ȍ GE})}ffJu틽GJE9dDžu9Dž Dž&M~7E‹}̋M 4Wȋ}ȋU G}ffJuF Eċ}u 9|Dž}9MIt&U~F}ċϋu֋E}E4O}ȍ WU}vffJu󋅼@JM9|FQEyFUEUMDž|}JMUy" |M |E t䋍|Eɋu|9u}U)Dž0``9u}nU}u0Uu <΋MuU4ME8`4)ыVW4֋8Iuu0F}uE09EcDžU9}uE)H\<\9M}tM苅<U}uUu <΋MuU4MED\@)ыVW@֋DIu㋽U܋‹uM4EMʋMŠE‹EIuBNM9|+WE P}WUR}WURVQ5G e[^_]ËU؋BuU؋E9E؉IMAUMu9u@EBƋE9B1e[^_]ÐUS[URMQURE PUR4 ] UWVS [ګu}jVjju" WVURE PUR謙 e[^_]Ít& WV}Wu VMQ誥 e[^_]ÉUS[lMQURMQU RUR脪 ] UWVS [*u}jVjju" WVURE PEP蜻 e[^_]Ít& WV}Wu VMQZ e[^_]ÉUS[üMQURMQU RUR ] UWVS [z}u$jVWUR8u1 U(RVM QURWEPuVM QUR e[^_]Ív M(QVu VURWMQEPu V}W e[^_]ÉUS[M(QU$RM QUREPURMQU RURt ] UWVS [Úu$}(jVjjYu2 WVE PUR}WuVMQU RUR e[^_]Ít& WVu VMQUREP}Wu VMQ* e[^_]ÉUS[ U(RE$PM QURMQURMQU REP ] UWVS [úu$}(jVjjyu2 WVU R}WuVMQURE PUR e[^_]Ít& WVu VMQUR}WuVM QEPJ e[^_]ÉUS[,M(QU$RM QUREPURMQU RUR+ ] UWVS [ڧ}u,jVWURu4PM0QVU(Ru$VM QURWuVEPM QUR< e[^_]PM0QVu(VU$RM QuVWEPURu V}Wd e[^_]É'US[<URMQURE PEPT ] US[UREPMQU RUR蔙 ] US[üM(QU$RE PURMQURMQU RURĐ ] US[lU(RM$QU REPMQURMQU RURt ]ÐUWVSMu[}R$ػĞt\ 1Ҩt8E W$ɃكآكEuM[^_]Í&؉[^_]Í&U W$Ƀك؋آEu 1҉[^_]ú묐UWV EU Hp MuJrMPHxʋU}uUUEy`'GFFFFFVFUBU9U|t61E1yUG҉ЁUFȉʃ)9|܋}UuEM܉UE}u+ ^_]ÊGFFFFFVFUBUQ9U|.vUG҉ЁUFAʃ)9|<UWV ME QxUq PuЋqu‰}AUQ‹UEEEymJF%ffGfGfGfGfG fWUfG BU9U|t?1ҊU1y%UF҉ЁUfȉʃ)9|ًUu֋}UM܍WuuE ^_]ÊF%ffGfGfGfGfG fWUfG BUQ9U|t&UF҉ЁUfAʃ)9|*vUWV$EU pxuJp up@EB~UuMUEUUEye;F%AAA AAQU܉AB U܉9U|t=1E1y#t&UF҉ЁUԉ)9|ڋUu֋MEM؍uuU$^_]ÊF%AAA AAQU܉AB U܍W9U|zUF҉ЁUԉG)9|*t&UWV1S[ëRu UH9}]E ȋUG%ˆˆȃ^^^ EyM9}U X[^_]ÍvUWVS[UuUU썃EUuE9U1H9}]E ȋUG%ˆˆȃ^^^ EyM9}U 멋} UuUEU@MUE}Mu9}f[^_]Í&'UWVS [*u MEU)}fF%}E]]f~fFfN%}E}EMfN %@E}MfV}fN u ]]]]}}E% EEu% E}u}E %}E}uE %}E}u }ً} ω}ڋM щMUUUM܃tdE1}ľ9}/t&EfEU]U!UU Iu֋E܅xIƋUE" }MuO}EM}MEM`^_]ËE܃믍&'UWV EM pxuP }@uAu}׉Uxq@}uEҋAE~\EMU؉E܋U܅~%M܉f ЈFIu}uMwuΉEM؉uNjuu ^_]Ð&UWV EM pxuP }@uAu}׉UqxI@}Mu~cMMU؉EM܋U܅~)Mf) %fIuڋ}EuUM؍ VE}Mu ^_]ÉUWVE} Pp H@uGu־щUHW@EUWMUuܐt&uUE%y tۅ9wRz2V$ٝ$$ٝ9q;v;s Nj9Ny tۅ9wQwR$ٝ$$ٝ9q;v;s 9gw<p^_]Í&UWV@EM pxP @uAu׉xpAybDž9Nj)ׁ~H4ȉt ы9s*FAVF ;r;s F19}ۄ@9|닅‹9 w<։@^_]Í'UWV`EU HxMp JMHMJMUHUE}܉E@uEYu9u}Wffw O ىMO }fwfG % w ƋEf@fE% NjE@EЁ Ef@fE% EЋE@E̋E]f@fE% E]̋E@EȁEf@fE% EȋE%@EԋE ʋM }]ȁ}ЉM̋uЋ} uȁ uȋ}M G}uU؃ BuU؋U܅}܃9}+uVff~ N f~ىMN MM MfN ~ ߉}}NfG%w uىMufOfG% ΉufwO w} ]@ډ}} } ]}E}% E}E}} } }ށ}} @EM܃t_E1}ľ9}*UBffU ЋU! ljUIu֋E܅xIƊMU"  u}MEMuAEUUE`^_]ËE܃믍UWV EHpM؋P u@u} Guu։UغM puq@M uҋIM~gM؉UMMEU~*M !ЈFIuߋ}EuMM܉Eu}uu ^_]Í&UWV EM pxuP }@uAu}׉Up@uEAqEuM҉M~cEMU؉EܐU܅~+M܍)ƒ !fIuދ}uMUM؍ VEljMu ^_]ÍUWV EM xpP }@uAux@}E}AyE}M҉M~}MUu܋U܅~Hu܉U )ȉU !! fNuu}MM؍}EE4Ouu ^_]Ít&UWV8EUErzJ uBuE @uBu REvUuU RɉUUM}؋}ME}t}UUEU9w7}7G}}V$]ċ}ĉ$$]ċuĉ9q;MvɋE;Mr+uU}܍ UȉM4uM_8^_]ÐUuM܋uE<}ϋU9w]@uЃ]ԋ}ԉ1y9v{t&'UWV E} H pMuH@MuGuuUEuHpGWE؉ME؉E؋E~OEEuE܋uuuE܅~E܍t&HuMEU׉MNMu̓ ^_]Ít&UWVS([jwEM pxuP }@uAu}׉Uxp}Ay}u؉Eԅك(ك$ŰEME9EU)с~EЋu1ҍ fG:% fG<% ‹E%E؋E%@EԋE E؋E% EЋE E؋E%E̋E EفE ]ȁE؋M M؋Mȁu؉MȋE Ɖu؋u ։u؋MM؊E؋M@AU܉MBU܋Eu9u=Wf7fO fw ʁO MfwUU fO UUfw ʁUfWM fwMO fW fwWMfO fw ʁO& Mfw UU fO"UU ]UEfw$MfW( %@MO.fw* сfW, fw0W6MfO2 fw4 ʁ fO8ډUW>fw: ʁfO< uE ʉuMu ΋M uMuE ƋE%uEuM ]uuMMME ƉMuuM uMuuE Ɖuu ։uUUEM}EE}ľ9M}9Wf% fG% fG% ‹E! IEuȋExQ}MċU}" E UE}uMU wuMωuX^_]ËE말&UWVS([êiEM pxuP }@uAu}׉Uxp}Ay}u؉Eԅك(ك$ŰEME9EU)с~EЋu1ҍ<Ƌu9}9 2B9}$tu]2B9|މ؋}ЋEϋM}9E|؋UuԋM}M̍UEH؃([^_]Í}fE fEm]mfE2cUWVS$[:hEM xpP }@uAux}y}xAE܉}M҉MMЉك,uԉuك$UԋuM~^U'}fE fEm]mEfJt*EuEufJu؋MEO4E܋Mu4Auv؃$[^_]Ív'UWVS[gEM xpP }@uAux}y}xAE܉}M҉M׋UكHكLu؍vU؋uM~GUfJt*tuDfJuڍt&؋uEO ƋE܋uM FMu؃[^_]Ðt&}fE fEmm넍&UWVS [eE} HpMP u@uGuu։UpO@uME܋GE؋E҉E׋EU݃Xك1ҋ( >T>1ҊL>T)ωы!ʋ))‰!!)(ȃ$!ЋUAd$951^_]ÐUWVEuE}uU MU}EM䉅X9EEMPLM}uEOM}UP~E@*@)M@!DM}uUMtU}9ECM9Mt 1^_]Ë}U׋uE<Dž8p498}8}<1D91҉,4101ҊD1T1%1D9)Љ!‹0)Ƌ,)‰!΋0!8,)(!ȋMGp89V1^_]ÐUWVEuE}uU MU}EM䉅P9EEMHDM}uEOM}UH~EfFuDžuǃH9ыu12E2 %2ȋ4Ef8fPׁUE2UH9_ыu12E2 %4:U΋M}ID79}+E1Ɋ  1ɋEJ O;D|;u}M }UEM }UVfNDžfBu%fzE@9u111E 17}%2ȋ4MffPUU2U@9\u11Ҋ11ɉu% 0E}7%Nj4:}΋MƉMEU0 :LjHMI<9}+E1Ɋ  1ɋEJ O;<|;u(M}Uu$M$}$QEUEUEwЋMu؃M!WUEEUEf8fpEU89Eыu12uU% 0u7}%Ƌ2ʋMM4U}U}UfUfGǁE0uEu89uNыu12uU% 0u7%4:΋M};M7}EE1ҋu2WM;M}*1ҋuP 2O};}}U1ɊHuFM}Eu M}}EHDžfWfHfP}M49M1׋U%ƋE EUʋu}E}uu4f%fG%EEE98}U1Ɋ >1Ɋ E1}u7%2ʋMuUU}V;}&1U EH;M}1ҊV:P;}}1ҊV :HUu1Ɋ 1MʈB}M}u}uNGMu}}u11Ҋ 0EWM Pu}‹u}}M붋uU1} 1҈VM G1UF O}u}u_}X}$E$pp}$M$lWXA \Mdȉhu؃Qxf>fFtDž|\ǃ09|ыp12hl% 0d7%2ȋ4tf8fPׁxt2|x0|9|Bыp12hl% 0d7%4:΋|x;\7}`t1ҋp2W|;\}91ҋlP 2O|;\}h1ɊHxFM}Eu M}dVfNDžfB\%fz`,9p1lh1Ɋ 1ҋd`7%2ȋ4`ffP`x2x,9;p1l1h1ɉ% 17`d7%2`ȋ`x`pV;\!1l xH;\h1ҊV:P;\d1ҊV :Hp1F 8ElhpAdldh\J\mp1ɊM1ҋlV:Q‹hMhdpld\똋p1Ɋ9M1ҋlV:1҈AVhQhdMdppl\\Dž M 9 }Z MU$ uFEԊEE UEȃUUыU9E|U1׉E1ҋEԊ8E} Et u؊0y}M G}9M#uEƋMU Mĉu4QuP^_]Å~uE} 9}}QU}Mu΋D WE܋U~#E UE}܊8} }JuUu BU9u|Mu΋E} M xuMuP^_]ÉU1҉WVPuM$;U } <|B;U |}B3uEU 9UEEċUMEuƋ|Q}EfMԋM fEЊEE UEăUUыU9E|U1׉E1ҋEЊ8E} Et uԊ0y}M G}9M&uEMU M4EuP^_]Å~M uME} 9}}VEUMu΋| }܋U~(} U}t&}܊8} }JuUu BU9u|}MuEM}MuP^_]Ð&UWVEU ‹M c}uEM 9M EuUE rU$uЋE̋PẺ9o ?f4ff f<u<Ẽxǃ;M}1ҋAFfPf;M|Uu EM1ҋfPf1ҊVfPfQUU;|Et(1Ҋ}FfWUJfUUM>MDžu9 ?UEf4f EufEf<>uEPE9i ?UEf4fEf Ef<<MExUJ9}/1ҋEfPEf1ҊQfPfW;|;u}1ҊMfQfu} ME|AuU{1ҋ}DžtfWU}fO xl>NMp9tUEf4f EfE}f<fx}x2ppp6ulFtlt9tDUEf4fEf E}f<l 1uOM}uf9Mf4MMMMEf 8l<2xtJxfH p9}/1ҋEfPEf1ҊQfPfW;|;x0,P4,494Q ?($f4f f f<4;1,F}l01ҋ(f PfN4;}C1G$f G4,;fO }01ҋ Vf PfO }M uϋEF}U(%NDžDfGHf:4Ju⋵U F9|}Mu OMEM^ U1҉WVPuM$;U }<|B;U |}(uEU 9UEE̍&uE}U pEw|}4PU uUU 84U9U}LEEЋEx}ԉUxE >FEԋEfE fAUЋEуUЉE֋E9E|EEx}ԉUx}f9} fytUf4Bf4}M G}9M.MEu MȍAMUqEP^_]Å~uEu 9u}QEċMU}4A BTU~%E UE}fG}f}JuUċu BU9u|MuE MEЋEfE fAEċUUUыU9E|EEx}ЉUx}f9} fyt Uf4Bf4}M G}9M+MEu MAMUEP^_]Å~uEu 9u}YEMU}4A TU~-} E U}؉E}fG}fEJuMu AM9u|MuE MDžXu9 4 <EЉ}̋8Eĉ0Hűp uЉPXEĉu9y 4 <EĉHx ǃ0Pu;M}1ҋAF;M|UE M‹uu$}$G M}MM}ϋU߃ 1EWT9}C1ҋE 1ҊQEPUEU;T|t,1uAPME<~U}Uu}M1}471Au EO1ɊHuE Ju䋕(M B(9(|MuE FEЋEE ŰEуUȉE֋E9E|Eԋ}ЉU}9} Et Uԋ44}M G}9M/MEu MMUqEP^_]Å~uEu 9u}XEMU}4 BTU~,} E }؉EU}܋}؉}JuUu BU9u|MuE MẺUȋE UUUUыU֋U9U|UЋEȋ<9} EtuЋu UM BU9M2UEu} M ME`^_]Å~}Eu 9u}OMuUFUЉUԉE UЈẼh9|}1Ҋ81Ҋ8`U ` Et :} yuM Fu9MuU֋EM \%NjE EMMuU1Ҋ0uUU 0E1Ɋ EĉMUMUMMUыUUU}}0UfAu%f11҉,u1 7΋M,(UU}9%1U 1(((UΉ$   0<fAƋ0fPUUĉJ`z4 4Uĉ094I}11Ҋ>%NjE EMu1Ҋ0uU 0E1Ɋ Eĉ ыы   01fAf1%u1 7΋MUU}19}9%1}։wO0w 4;}}CGfEыUFEV;}|MEUu EE UẼUU9E|U1׉E1ҋEԊ8E} Et u؊0y}M G}9M?}U׋uM Mč}EP^_]Å~M uMEE 9E}=U}uM|U~U8E JuMu AM9u|ËEUu}MEUuP^_]Ð&UWV`}~MUʁ}* Mu DžE 9&}UU$E41҉ xu11ҊF;}sE GG11FfE fȋuEE}up11UhO Dž`l9h`h1ҋ/(d.(lh1ҊPpx H`ldp lp9[hp`**//P pEHt(l1ɋp1ɉGG}u MEA}U EiuEuf\< 1ɉx}1FNDžM9 (\)\.\(‹Xы11B<:JXr z\9(\)\.\׋(‹Ƌщp 8xNjHfPE|1Ɋ1ɉfwfWfGfWAMeuDžE 9}eU$MU4yM~+U ҉U1AfJu㋍u A9|UE u‹MfE fAEЋUEȉU9E|EEx}ԉUx}f9} fytUf4Bf4}M G}9M<}UuE Mč WFMUP^_]Å~uEu 9u}SUM}D4QE܅M~*E UE&}܃fG}fJuUu BU9u|M}u My}E wMuP^_]Ðt&U1҉WVPuM$;U } <|B;U |}@1uEU 9UMEMĉEȍt&U}E4WDUEf fzUȉ}ЉM9U}}\M M̋MԋEHUU PfE UfWUЋUfUf FEȋMփM9E|MԋUEJMJM ffNtf}%f G} f Uu BU9uuU} M VuM~EP^_]Å~uE} 9}}Q}UM4zDUE܅~(} U}f}%fG}fJuuM Fu9M|uME MEԃEfE fAEȋUUU9E|EEx}ԉUx}f9} fyt Uf4Bf4}M G}9MI}UuE M WMU`^_]Å~uEu 9u}QUM}D4QE܅M~(E UE}܃fGfEJu}u G}9u|Mu}U My EMu`^_]ÍUWV$E}O}o W`EU 9UMEx|t&}U$E41҉u u11ҊF;|x}ZE E܋}苅E䋅E1}1FEE <};||U<9} Et1Ҋuu UM BU9M&}E MNjU`4}u$^_]Ã} t} } $^_]Å~}$u$V0,uEEȃUىȉŨt510,Eȉ1ҊFPEȋEHIEu͋>Dž(}ԉu؋ũu9(Uԋ},}ԁ0}ԋ 240<Eȉx P}ԉp}ԋ0Uԋ} ,40<E؉$UԋUȉJr$(Jz Uȉ(U؋M9(U*Uԋ,0 24UUԋ0}ԁ<EȉPx UԉpUԋ0Uԋ} ,40<Eȉxp(ƃH PM;}})1ҋ0G,1ҊAV;}|׋uM} MEM$^_]Åzu$M$>Qu$~ MEωEU߉UtH1E1ҊP 1ҊAJu苕\u B\9\|uM} MEXMjG\} B\9\X봉U1҉WV`uM$;U }<|B;U |}"uEU 9UEUMf8frEf}fuΉMEȋU}u΋DEE؍ Eȉu}Ƌu9E}IU UЍvU؋}ԉEFEԋEE >UEȃUU9E|ËE؋}ԉU}9} Et U؋44EM @E9MKuUM MEE E̋UUU9E|ŋE؋}ԉU}9} Et U؋44}M G}9MQ}UuE Mč MUP^_]Å~uEu 9u}JUM}D4E܅M~!E UE}܃EJu}u G}9u|Mu}U M EMuP^_]ÐUWVSlEUꉍ(M4[UMEDž`U u ru|prE9q49``M `|QLpU `tB`DQ)׋)!ϋ!f+։ʉ)‹!׋f+ϋ`f49lx4ALWDBʉ)‹!ʋ)ʋ!ы)ы!щ))!ы!fxf ~f+)!)f4Hf+!GfN;4|J|HDJtJ)։!֋֋)֋!֋։)!֋fDJf+f|N)Nj!fTH)f+!;4fDOb|JTHTN)ΉLH!)‰ϋ!)!)!)DDfTp!ϋDDf+fDw)!‹fTp)ʋf)ȉ!ȋfDq\U\Q09\u9\tD(9t Qe1[^_] P蝯>uDž4<9}N)*!ˆT>)׋p11!ϋ`$D1T7 1ҊT D7)ω! )׋!ϋ)׉!׋׉ʉl)!‰\ )!׋7)ωx!th)ы!p)ы|!|l)ы!x)щ!(t,dpV`O(lB,\OxBhW)*!$*)!T)*!T)*!Td)d*dddd!`)ъ``*```!ȋЈD\)ʊ\\*\\\!ȋDh)ʊhh*hhh!ȈD>0(,$(,9~<9$$19 111D91D1)։ !)։!)!֋ )֋! )։!֋֊11)*!‹‰1*)!9G;<1D91D91D91D9)׉!׋׋)!׋)‰!‹ŠDD+*!‹‰T1)*ʉ!D9;<N11ҊD91D1$T1ɊL>))!!)!)!׉)ω!ϋT>ʋD>ȋ*)!T>)(ȉ!ȋDTuT89TU9TtH49t W聠e1[^_]à RUDž<uETN)!)!4H׉f))!ufJDN )f)!ƍ78fDJ9[49}huT~)ʋ!)!ʋN)f)!ƍ7fNDž49 w4xE4J)H!Ɖ)։!)f+}!4fOA9R UR豃1ҍe[^_]ÐUWVS<[çKU uU}UM8ύF8}9})(у!Eш uD7%(Љr) rMrЈDD}9K<9M}[U}1ɋ|L:)! 1u)1ϊ2ƒ!|ω)(ʉփ!ƋU 7} :UMBuUM}iM}Dž4F@D9UD%Ƌ)Ɖ!ƋƉ)!1‰)(у! 1ɋEL(΋)!΍ 7L2D9F<9}oM1ҊT1)!ʋ)ʉ!׋U)(ʈу!9Dž<91Ɋ>%Nj M1Ҋ)Ή!)։!Ή)(ȃ!}9A<9Y uVx1ҍe[^_]ÐUWVSL[ç@EU MEUUu}0MMFω40E…ɉ} 1Ҩt e[^_]à <)WwEt1ҍ 6DЉDBȃ~)!tщf+)!}ʋfNtOf+)!tfTO,9(9MTq )‹׋!q)ȋ!)lj!ϋ)f+t!f~uUDžrxxNp@ )lj!Njlj)!֋,9-ty )։!֋։q)!֍ >)!pщf+)!ʋfNtOf+)!pfTO,9(9Tq )‹׋!q)ȋ!)lj!ϋ)f+p!f~E)Ήx!ufVf4yG;(}E4}uU]u!4u9$9M|wdW uOl`Gx}hVGWwE)ʉ@x ֋@)!򋅔@‹@@!։@@,Dž9MTy )!򋵌q)||!΋)!ϋxf+|)!|򋵌fqMDqf))!ʋx,fTq9 (9MTq )‹׋!qt)ȋtƋ!ȋt)lj!x)f+t!Njfxt>%11ɉ )!)!)ʉ!щȈ)*T!‹‰)ψ(ȉ!ȋM1|GFTP9uE FuM<U<]}!`}9<nL9<}DxHp }P1ɊO1҉G%W1ɉNJN1F)Dž)!!9\9}1ҋT>)!}>)%lj!ы)ʉ!‰ш(‰)!1>UD)(!ʈT>\9/P9U1ɊL)!%Nj)Љ!Ћ)!ȉ)(ʃ!‹DžP911ɉ >1ɋ )1ʊ>>%!ыы)׉!׋)щ!ʈ)*!ЋUFP9/ uVQ1ҍe[^_]ÐUWVSl[ÇEuMEU }EUuQ4DEƋE@…u 1Ҩt e[^_]Íq QPEtӉ1ɍ6TȉTAƒ~DEHt&u}EfVfNDhfVdEfNfF h)ljց!hd)`ʋd!ыH9U\Eu`fTF \`)!:}\f~`)ωƉlE!\)ω!ωf))u!l}f~ftxEf))!΋MftAHE9&D9M}uuMfTq )!ʍ :`}fwNj\)Ё!Ћ`)!ȉ)f)!}f wUu}FuzE}UMDž<DUM9<}Lh)ъhh*hhh!ЈD>  ,$9$491Ɋ 1Ɋ 71ɊL1D)щ΋!)։)ʋ!Ɖ!)Ɖ׋)lj!!Nj8 8*)!ʉ(ȋ)!B7;411L7D1D1ɉL)׋!)Ƌ!)lj!TL8*)Nj!T񊅼(ȋ)񉍼!D>;4L11ҊL>T1ҊT)LΉ!ʋ)!)Ήы)!ʋ)!Ή!NjT8L8񋅬*)!ʉT(ȋ)񉍰!Dt&T}T09Tu9TtH(9t Rw!e1[^_]à <W {}Dž4y9}111ҋ191ω<1ҊT11ɊL7)8Ƌ!<)<<<!Ɖ<<8)։888!Ɖ88)(!ЋȈG49UWVSlEUꉍ(M4[UM U E΍| )!xЋl)Njt!)ЉNj!p)Ћ!|)Ћ!xf: hf tfGpdfzfB| fHfzlf+fx)!‹fx+f+!‹‹fTx+f+!‹‹fTx+f+!‹‹fTxhhf))hhhh!Ћfzdf+)dd#dfDzf+)l!ȋfDz)lf+llll!fDr$  909f4Pf,9}NE|P)!Ǎ7)ȉ!u`XXX)ω!׋TPf+TP)fhf!Pftыpf zL~\Pf+P\)!hfLzh99hht\tTw )ʋd!\p`L)ȉ!d)lj!)f)֋p!ƋL<0hf\l!)ω!lʉ*l)K KlK‹E}D>%(ȉJ) JJʋMTu9"9E}tUu11L2)!`ϋM `\)ȉ!)lj!׊})*! U}UGE‰}U}}MDž}M9}JvMuŠ 7(%֋}1҈)!Ƌ 8@9|}EDž}|9}I|u 2(%1҈)Ћ! 2B9|Dž9}M8pxH tP}ppx1G1ɉ1OG1ɊWO֋)Љ!Ћ)ϋ!1;t&M1D)!Ѝ01u7)ʉ!)Ή!񋵼ʉ*)/ //‹tuD7%(ȉ.) ..ʋtT;';U1ɋEL)!э1(1Ҋ)!()Ɖ(((!Ή((t)(! 7Eu1҉1ɊV11ҊNFV)1ɋN!)!1;1D)Љ!ЋЋ1)ʉЋ!)Ή!񋵜ʉ*) 1p7D(ȋ) ʈT7;;11T7)ʉ!ʋʋ7)ȋ!)!򋵘Њ)*p! 7uDž}9:Ut1x|11>1ɋp)Њ >x1Ɋ >1Ɋ >)!<!ы)Ή!|*|) |Јxx)(ȉxxxx!ȋM1FxG9UE4BUuM]}!׉}99}Dp L@HPuxDHX1ɊN1҉t1VtF1ɉp)1ҊNVp`)‹tDžl!Љ!ыphd9lUl1dDh`h)l!`d1h)ʉ\!ыd)ʉ!шʉ*\׋\)Њ\!Ǎ:X>1UD`(ȋ`)!֍1lT>l9 9llu`1ɊL2)1!`ϋM hThd)Љ!T)lj!׊TX)*T! l:DžP9PPLH1Ɋ >1҉81Ɋ X17D)Ȋ7@<1Ҋ7!<)!<)!P9M8*8)8!FP9I URm1ҍe[^_]ÐUWVSl[gu}Eu}M}U UE4QHƋED…u 1Ҩt e[^_]Íq QpEtӉ1ɍ6TȉTAƒ~HEPt&u}EfVfNDhdEfN fF%ϋhfV)ȁƋd)!dh`!P9u\t&MufDN \%`)`!Ѝ8E\Ufu)ɋ}E̋W4G08}ȋ~(u}ċ~,}#IM2UEUNIffU%U@@@ EffEE‹E 41B Nu]B1͊A1A1ANjEˍ411ɊN 1ɊN1ɊNJEM9M/ۋuN1Ɋ E1ʊJ41ɊJ1ɊJ1Ҋ1ɊN1ҊV1ʊNM͋UBU΋M9MMɋUEf fUUэ01u]A]1ΊB1B1B1ϊ 1NJBA 1B 1BA ΋MɋEËJˉMIM]Eu݃Xffu%uu @@@ ͋u!PEȋuԋ Nك<uK1uN1Ɋ 1ʊJ1ɊJ1ɊJϋM͍411ҊN9}fE fEm]mEɋM11uȊ1ˊB1B1͊B1  1ɊJ 1ÊJ݃X 1ʊJ̍ ɋMM"u9ذuVuЅHuE@N(EЋV4MċF0N,uMFuك<u1uVuЅ~\[^_]}fM fMm]mE낍vUWVSd[:u }UEFO@u M>)ҋ}E̋w8O4G0uȋw(,}oJUEJHMEԍRffu%uu@@@ EffEEu A 1u]uA1͊B1B1B 11ɊB 1B1B IEu9uyۋMEF1Ɋ 1ʊJ1ɊJ1ɊJ ϋM1 1ɊJM1ɊJ1ɊJ uϋM9MʋEuffE1u]A]1ΊB1B1B NjE1A 1BA ˋM1B 1B J ɉM‹ER0NM]‰EM݃XffM%M@@@ ΋E ʋuM u @܉MOك<EuEM 01Ɋ E̋u1ʊJF1ɊJ1ɊJ ‹E1Ɋ 1ɊJM1ɊJ1ɊJ u9E-}fM fMm]mMMQʋu1E̋u 1ˊJ1ɊJ1ɊJ 1̊ 1ĊJ݃X 1ɊJ 1ɊJ U M @u7ذu}FUЋuBUЋF0N4v(R,UċUBUك<u1u}~d[^_]}fM fMm]mEUWVS\[Ju }MEFW@u U>u)ɋ}E̋W4G08}ȋ~(u}ċ~,}.IMt&2UEUNIffU%U@@@ EffEE‹E 41B u]B1͊A1A1A NjEˍ411ɊN 1ɊN1ɊN JEM9M7t&ۋu1Ɋ E1ʊJ41ɊJ1ɊJ 1Ҋ1ɊN1ҊV1ʊN M͋UBU΋M9MMɋUEf fUUэ01u]A]1ΊB1B1B 1ϊ 1NJBA 1B 1B A ΋MɋEËJˉMIM]Eu݃Xffu%uu @@@ ͋u!Eȋuԋ Nك<uO1u1Ɋ 1ʊJ1ɊJ1ɊJ ϋM͍411ҊN9t&}fE fEm]mEɋM11uȊ1ˊB1B1͊B 1  1ɊJ 1ÊJ݃X 1ʊJ ̍ ɋMM"u8ذu}GuE@N(EЋV4MċF0N,uMFuك<u1u}~\[^_]}fM fMm]mE널&UWVSl[Êu EVH@u MH(>M)p8H,Uȋx4P0uċEME7 }f8UNfEEIEEffGGG NjE‹E B4]؍ NuB ]AɍquNBɉuAA U9uUP݋ufNBɋu̍uFuBB͋U9UMm@ɋuMU@@u@@@ȋ UI‹@ɃuJM؋u@@ff%uuu@@@ fMf%]@@]@ E ]Ћ4E݃X΍Nuċ މMك<ufEMuEfN}fM fMm]mMfM@ɋ}͍4x ~}@@΋E̋FFFAMAA݃XMuظufVl[^_]ك<u ظ}fM fMm]mE뻍t&UWVS[|EU }Mp@}؉u܋x8r}U x(E})֋P4Uuɋp0@,uEEIMff}%}u2I@@@ EUfNfE܁EƒEB HE ]Ћ4B Nu]A̍qB΋uAA U9udU܋MufuBɋd̍MAMB9uB M@ɋdd@@ ȋM1@ɃMNʉuu@@ ЋU@M@@ EʋEIff%UffM@@@ ́Eƒ]BuB]B U]ȍrU ݃X͋ AMEċ4݉uك<uVfDžhMu؋hfMBɋd̍BB ΋UBU9Mtt&}fM fMm]mMfh@ɋ}͍4x~}@@ ϋEFFF BMBB ˋ݃XMu6ظMfUEEEuEU@}EMك<u ظMfU~Ĝ[^_]}fM fMm]mE넉UWVS[xEU }Mp@}؉u܋x8r}U x(E})֋P4Uuɋp0@,uEEIMff}%}u2I@@@ EUf IfE܁NEEE B4]Ѝ NuB ]A̍qB΋uA A U9uDV݋MufuBɋDF̍uċM9MB BM@ɋDDM@ @ЋMJ@ɍR@ @u‹@ɃuIMЋu@ @ff%uuu@@@ fMf%]@@]@ E‹E ]ȋ4M݃X΍Vud4@uك<uKfDžhMu؋hfMBɋD̍uFuB 9Mlv}fM fMm]mMfh@ɋUM͍M)p8H,Ux4P0uEMj 7U}U}NIffU%}ff}@@@ E‹} B]Ѝ}u }ԃɉ}}u }؃}ċ<Wf:fBNjE܁fDB%EEfBɉU}MfU fUɉ)QU))$$ʋ}E)$ˉ $UԋE‹:$ɉUԃEЋEE̋Em]mEȋEEE yf:fJϋM܁fDJE܁Mf BMMUEfQUċEăMUċM}fU fU)QU))$$ɋ}E$)ˉ $}$ʃm]mMuf wD^_]UWV8}u UG(O,EMFO8u Mċ6)w0ủEЅҋG4EU}fU fU}̋Mċuȋ}̋9uȉMċu MMMɉM M4fJf:M܁MЉ}ufzfDJfr%Eԁȉuf4JfLJfTB+EP+E$$ˋE$))$$ˋEm]m}lj<$M܋u)$4$E܋M)$ $̋}؋u$)M)Ή4$E$m]mU‰$$m]m}MMfm]mufw8^_]ÍUWVX}UG(w,EuE O8MpE u0E)҉Ew0G4uE}fU fU&MuMuMR1MM 4vPE E4pEEE]Ef]%P$fB%]$fA%$$$f%]؉$fB%$$$fB%]Љ$fA%$$fA%$$fB%$$$fB %]ȉ$fQ $$$fQ]ɉ$eMEMeEMM$M̓EOeEMMEMMm]mɋMfm]mUfVm]mMfN5X^_]Í&'UWVxuUF(~,E}E N8MxE }8E)҉E~0F4}El}fU fU&MuMuM1MM ЋE E4EEE]Ef]%P$fB%]$fA%$$$f%]؉$fB%$$$fB %]Љ$fA %$$$fA%]ȉ$fB%$$$fB %]$fA %$$$fA%]$fB%$$$fB%]$fQ$]$$fQ]$EeeMeMEEM$MʃOMEEEMeMEeMMMEM]m]mEEɋUfm]mʋMfNMm]mUfVm]mMfNx^_]ÐUWVS[7DE (ݝE(u ݝxhRWGX h( l $hWQNE݅xP8Et؃̞PhFlhV vdUݝPB`EP \UBU0XEPpt ƒU p1);dMDB;d~1҃tƒ уVl Xp4@W֋\R`QdVhW|RxQVWURMQuVtW$@e[^_]ËEDžHPDt$DžHtH}0tƒ}0!ШlL1;d}v} @;d|HD}0\=\PlR`PRQTRPQURL PMQURtQ݅P؃ĞtXNtݝPue[^_]Ël PU]ÐUWVS [Ç@E Up xHrJPx҃L Dž|EU‹E )։Hu(HT6ЋE u Vu$H VE$u$HV 9| E)xpxNtT|A|9||)ыE,Itɋ||w}4H$$1;E}*$  @;E|1;H}?1ɋ}7F$OO;H|1;u}/H} FTT;u|1;}{<Nj|׋|ЋЋ4Ƌt֋tЋA;|xD~Njp׉DDž9E)‹lɋlhdPD}0+t&dDž@P<9@~E)Nj`\ff\<@<@`fq9@@@< 4|L<0<BЋ‰‹Ƌ@D ΋΋΋ъ2:=-=f"t&fH@\<@`<9@ l89@[XF@X2 <4 @;E|1;}e1ɋE0FOOOO;|1;u}K} FDDDD;u|Ë t&NN ыЋыNʋNЋЋOO ыЋыOʋOЋЋJQ ЋЋHʋЋQ J Q ЋЋ Hʋ ЋQЋ ~8~0Dž@9@U)֋,,($M:ɉT9fP}NȉL}(~$fHDž$‰94M)΃ mf=fFw p 9$ )ыE,Itɋ ,(<Q DžN<9q8Uك<݃Xك$)׋E$HHٝݝٝu yݝHfP$}P$}~ fP$}~ fP$}~ fP1$Aݝ@ݝAAݝݝA A(ݝA0ɋݝݝݝ9~qt&ɋ$r݅H݅@ݝ@݅܍͋ ݝH݅݅H܍ɍr݅܍݅݅݅݅݅H܍݅݅݅@܍\9݅H8u)ݝ9݅@ˋ);8f݅ݝR݅܍܍݅݅݅$Ƀ݅݅@;8|؋9~+ TPLH09|IG09u< 41Ҋ‹Ћʋыȋы|x֋ϊDtǍ0=G։09)׋9|;G9e< 41Ҋ‹Ћʋыȋы|x֋ϋtD0=A9$9@}Ћ@@@9@1ɋu T,1ɈP},"9t P1e[^_]à R%ˍ&A_4;.4t&UWVS1E U[H x8`lj`t9```

Dž4D9~x&t&ы ʃ49 lD`|h\LݝW$f~%$݅͋h$܍΃܍݅܍݅܍݅܍Tݝ݅0ōX܅Sۃ݅܍f݅܍܍݅0a؋fy{ Rott#89tt tW1e[^_]Íٽf f٭۝٭5fy4~ y;;LɋD); م݅ݝݝ(م&fDB;ɋplf>W݅̋h$܍݅܍݅܍܍V݅(؃fDB;8؋@9d}4HxplxpxhdBldh9dxf2V}$H Df2VD$41;<;Lم݅ݝݝ م"fB;LɋpЋlf%P݅͋h$܍݅܍݅܍܍[݅ uq؃fB;LAٽf f܅٭۝٭5kt&ٽf f٭۝٭5Wٽf f٭۝٭5P4J|&UWVS)[E Uكp H<8pJ}(鉵4p0,J8P$ҍ( ݝs  E M)΃~ك݅Ƀݝ؉R$ܽE u$Dž$9@ݝ@ݝ@ ݝ@ݝݝFݝFݝݝF FݝݝW$Uݝ` fׁW$ݝX}D ׁW$ݝP}~ f1ҁW$;ݝHم8})ݝمd݅܍f݅x܍݅p܍݅h܍܍݅ÍX؋$fy 4F A;d ݅`DɋݝDݝxDݝpDݝh݅Pf>ݝ`݅H݅XݝXW$$f~ݝP%$$܍΋ݝH݅Ƀ܍݅`܍݅X܍݅P܍݅`܍݅X܍݅P܍݅H܍܍܍܍܍T܍ݕX݅čX܅X ك݅܍f݅x܍݅p܍݅h܍܍݅؋$fy Rob.Dc9t R1e[^_]ٽf$ f٭۝٭5fy 4~ y;d݅`݅Xݝݝ݅P݅H;مم݅ݝݝx$ݝ fB;ɋ ݅ˋݝЋ݅ˋݕf%P܍ʋ܍$܍Ƀ݅܍܍݅܍݅܍܍ݕX݅xܥ݅܅Xܥud؃fٽf f܅X٭۝٭5t&ٽf f܅Xܥ٭۝٭5Zɋ);8مم݅ݝpݝhݝ)t&fB;8@ɋ ݅׋׋f>ݝ݅ݕW܍̋܍$܍Ƀ݅܍܍݅܍݅܍܍ݕX݅hܥp݅܅XܥpكfB;8؋9}0w G<9vٽf f܅Xܥp٭۝٭5fpcv׉Di:-v'UWVS1E U[ÕH x8ݝW$f~%$݅͋l$܍΃܍݅܍݅܍݅܍Tݝ݅PōX܅^ۃ݅܍f݅܍܍݅P\؋fy\\;L|;0+؋(| W( ٽf f٭۝٭5qٽf f܅٭۝٭5`;0مD݅݅8ݝ ˋݝمHfB;0tЋpf%P܍l$݅ʃ܍݅ ܍܍e݅u#fB;0Lٽf f٭۝٭5fX9xt xRu1e[^_]Ð&UWVS|)[gE UكHx 鉽HDxJu鉍@H 4B9 4)EItɋ< F,xD$yD(݅XݝhݝXW4$f~%$$܍΋ݝP݅ʃ܍݅`܍݅X܍݅݅`܍݅X܍݅݅P܍܍܍܍܍T܍ݕ݅ܥčX܅ܥڃ݅܍f݅x܍݅p܍݅h܍܍݅ܥً4fy R蜶  ٽf4 fܥ٭۝٭5fy04~ y;;݅Pم܍4مݝ݅ݝݝݝ fB;/݅PЋ݅`Ћ݅X΋ݝ`f%ݝXP܍$܍݅܍݅`܍܍݅܅܍܍܍ܥ݅ܥ<كfB;؋D$@4yvٽf f܅ܥ٭۝٭5Y݅u ؃Iٽf f٭۝٭5 UWVS\[úo} U)ЋMΉEUEE}܉u؉uU9E;ك$ك,ENuE MMu΋EM؋U}܉M׋MЋU1҉M1ɊMQ1ɋUԊ 1ȉE1VFUGUGB1҉EEԊPEЉEȋE)QM)$$$E}}ԃGu|tuMEËuCڋMGfM%M̈AUEBM̉UEȋUBUUUNʋEU1Ɋ11ÉEEE AщMȋM)P$ȃEfE؋UfUGM̈AEU@BEM̋EMĉUMȉENI؋u}FOu}}"UMыu֋EM@M}u؉EuЋEM܍< uЉ}9E؃\1[^_]}fU fUUm]mEf J0UWVS\[lu E)NjMEʉEEE}u܉U؉U9E]ك$ك,EJUE MMu΋UE}܋MUẺMЉ}ԉE1Ɋ1M}ЍA11Ɋʋ}ԉU1ҊVUGU}ЍB1ҊW<E)QM)$$$UԉuuЃUuM6tu|uEÉUuQt&ڋMEf9Mȁ}U}U}Mȃ}M}ĉuʋuU11Ɋ u1ÍAuɍ4ĉ)Q$ɃEaEغЋ}f G}MȈU}U}UMȃUM}ĉuB؋UuBNUu}E}Nju}U}̋Mu}F<uEU܉M؉}9E؃\1[^_]}fM fMm]mU%UWVS\[*ju E)NjMEʉEEE}u܉U؉U9E]ك$ك,EJUE MMu΋UE}܋MUẺMЉ}ԉE1Ɋ1M}ЍA11Ɋʋ}ԉU1ҊVUGU}ЍB1ҊW<E)QM)$$$UԉuuЃUuM6tu|uEÉUuQt&ڋMEf9Mȁ}U}U}Mȃ}M}ĉuʋuU11Ɋ u1ÍAuɍ4ĉ)Q$ɃEaEغЋ}f G}MȈU}U}UMȃUM}ĉuB؋UuBNUu}E}Nju}U}̋Mu}F<uEU܉M؉}9E؃\1[^_]}fM fMm]mU%UWVS\[Zgu E)NjMEʉEEE}u܉U؉U9E]ك$ك,EJUE MMu΋UE}܋MUẺMЉ}ԉE1Ɋ1M}ЍA11Ɋʋ}ԉU1ҊVUGU}ЍB1ҊW<E)QM)$$$UԉuuЃUuM6tu|uEÉUuQt&ڋMEf9Mȁ}U}U}Mȃ}M}ĉuʋuU11Ɋ u1ÍAuɍ4ĉ)Q$ɃEaEغЋ}f G}MȈU}U}UMȃUM}ĉuB؋UuBNUu}E}Nju}U}̋Mu}F<uEU܉M؉}9E؃\1[^_]}fM fMm]mU%U1]ÐUWVS[wd}EG`G]كĞw@PpUuW MUWUUPUPGtxE9ٝ|}ȋU } RhEM1ҋEDȉDBȃ~EU)‹MU}UEJωlsE}fU‰h fUE؋E Džppm]m)pMpA]]AA l1҉;M}*lf44D;M|֋upƋxT1U;u}Et%E BHBHB H UF;u|plAEƉplp1UuMDȋT̉EUD1ҋtЉd;Uك0ك@ك<݃XMEEE d uum]m‹ME B;U|E؅|MEmUm]um]m‹U)MUU~NEȋlΉlMUhM }W1e[^_]Í&UWVS[`UEB`JB ]كĞr@EqBuEyEAEEEEA%tBpUE9ٝ|}ЋM lMȍ QɗEU1ҋElDȉDBȃ~UM)׋Ed}x}}fU‰` fUE؋U Džhhm]m)hMуhA ]AA] dx1Ҋ;M}0df49x4D;M|ЋuhƋt|1;u}yvʋxЉ%E @B@@ ɋpBBBB BB\F;u|hdAuhdhEMuTȋDЉU|̋U}EL1;Uك0ك@ك<݃X&EuE ɋ} E uum]m‹xE}:B;U|E؅|UEmUm]um]m‹})UMM~VEЋd։dM}`MZ uV1e[^_]ÍUWVS[\MEA`Qy ٝ|كĞq@}ByE}r:u}Ez}<@}<@A}lzpٝxDžhDž` ك$dٕ\ٝXtpXpXn0pH1;u}nك$كPك(}E }E E uupF;u|؃`6\ d0\ hMك t}ٝ(ك$DHٝ@كĞك(ك$U`H}مT 1م(ʊۅEuم@1DمL EuEum]mŋE1DBمL NEuEum]m‹EB1DBمL NEuEum]mƒEBmߝm‹}ApXpXEd|1;u ك(كĞك$ك$`HHEuEuEum]m‹}EBEuEum]m̋}EDJEuEum]m‹}EDFHm]mfE;upH}t`$UHكĞك(ك$&}ċE uJNjUuG]]GG ]]ԅك0}ك@]ك<]݃X]fUt }]fU*m]mUE4ẺUOA%E @@@ EÍP1Ê 1B 1B 1B E1 1BM 1B 1B E1 1BM 1ŠB 1B E1͊ 1BM 1ĊB 1B MeEuEEt&EȋuƋUuċMuċ}MЋ|AƉ}MЋ}u9}} }WUMы}UuBU|u}~ |1[^_]ËMQ_&'UWVS|[ú?M}EQBrEG}E̋G }EȋG }EG:E}B}E}E)ƃ|uz |EuM}9u>&}ċE uJNjUuG]]GG ]]ԅك0}ك@]ك<]݃X]fUt }]fU*m]mUE4ẺUOA@%E @@@ E1Ê 1B 1B 1B E1 1BM 1B 1B E1 1BM 1ŠB 1B E1͊ 1BM 1ĊB 1B MeEuEEEȋuƋUuċMuċ}MЋ|AƉ}MЋ}u9}} }WUMEB}uƉU|u} MQi|1[^_]Í&'UWVS|[Ú<M}EQBrEG}E̋G }EȋG }EG:E}B}E}E)ƃ|uz |EuM}9u>&}ċE uJNjUuG]]GG ]]ԅك0}ك@]ك<]݃X]fUt }]fU*m]mUE4ẺUOA%E @@@ EÍ1Ê 1B 1B 1B E1 1BM 1B 1B E1 1BM 1ŠB 1B E1͊ 1BM 1ĊB 1B MeEuEEt&EȋuƋUuċMuċ}MЋ|AƉ}MЋ}u9}} }WUMEB}uƉU|u} MQi|1[^_]ÐUWVS[w9}EG`G]كĞw@PpUuW MUWUUPUPGx|E9]}ȋM } QkpEU1ҋEDȉDBȃ~M}UϋME)щMUp]M}fU‰l fUE؋E Džtm]m‹Mt)At]AA tp;M}pNt;M|utƋ|T1U;u}Ex%E BHBHB H UF;u|pM4HtAptt;uEMTȉUt̋Tԉudt1;Uك@ك<݃XEME ɋd uum]m‹MEfQB;U|EEMEmUm]um]m‹U)MUU~T6EЋp։pUEl BM }Wn1e[^_]ÍUWVS[5UEB`JB ]كĞr@EqBuEyEAEEEEA%tBpUE9ٝ|}ЋM lMȍ QlEU1ҋElDȉDBȃ~UMҋE)׃dx}}}fU‰` fUE؋U Džhhm]m)hMуhAA]]A dx71;M}$dOxt;M|܋uhƋt|1;u}vʋxЉ%E @B@@ ɋpBBBB BB\F;u|dhuBh wdhMuTȋ|̋MUU}DЋLԉE1;Uك@ك<݃XE}E ɋE E uum]m‹xE}fWB;U|E؅|MEmUm]um]m‹})MUU~L6EȋdΉdMU`:;M}'v4tJ|;M|܋dsM8Up9dD׍t‰%E @B@@ ɋlB B0BBB$B4BYBB(B8B BB,Bt\t\w 4 tL1;u}nك$كPك(E} ɋU E E uutF;u|؃d` h` FMك t}ٝ,ك$HUٝDكĞك(ك$LdL}مX م,OEuمD:HمP EuEum]m‹HمPɋ}Nf:z EuEum]m‹HمP}Nfzz EuEum]m‹E}fzmHmƒdt\t\ch51;u/ك(كĞك$ك$dLLEuEuEum]m‹}B͋EfEuEum]m̋}JEfDEuEum]m‹}EfDFLmOmƒ;utLUdLtu<(كĞك(ك$UمT(2مX EuEum]m‹(مXɋuIf2r EuEum]m‹(مXuIfrr EuEum]mƒufr}ك كĞٝ$ك$ɋEtٝ ٝ@ك(ك$ULdLمX Eم$م م pم Eu م@PمP $ EuEum]m‹EfBPمP $IEuEum]m‹EfBBPمP $IEuEum]mƒEfBEmpmƒML1ɋU}mm@m\m@m\mƒJu'UWVSd[jExH }MȋVMUpP ủUpUuEJR UċU)׃ɉ}}MMu}AȋE UƉ}F]]FF ]]Lك@}ك<]݃X]}fU] ufU)m]mUEfẺUNʋEP%E @B@@ BBBMBBBMBBBBBMMEuEEuȋU}EUċU GE}ĉMM4xu9d1[^_]É'UWVS[EEpPuŰvyP M}Ux }ȉUxU}ҋ})֋MGEMtu}䋍tEMW Uك@ك<]݃Xɋu]ݝxp}ċE uMNjVMG]]GG ʋU]]ЅKE}E]݅x]}fU] ufU0&m]mUEfẺUNȉ%E @@@ EBBB BMBB BMBB BBB MMEuEEuȋE}UEċMt4WUupكĞ}ƋufMݝX PfMEm]mE}EF܅X}MUʋTE<}Dž0u0)00xM0t1;M}@U&}}@J @J@J A;M|ʋu0MB0<}0~uEDž,U|ȋLԉ}tЋ|̉4E9,݃Xك<,Mɋ4uuɋ,EmmA,9,|E܅p}uEmUmƍ]MU)PM URr=1e[^_]1;MكĞ}ك̞ݝHكТfEƋU ݝhݝ`ك(ك,ك0fEmUmƋEP$u݅hA܅x݅`܅HZZZ ;M]؍e[^_]ËuEƉu}0MM"؋(؋Т؋,؋0؋̞܅Xo'UWVS[çEEEE@`PHl]كĞp8MrHuMp zuMru@Xp@ݝxݝpRݝ8:݅8EMK1ҋEDȉDBȃ~MUщUM}1;M}كĞfEƴ fEmUmƋuV$ɍvu܅xʉAZZZ ;M|؋U)׃ T}=كĞ}ƋufMݝX PfMEm]mE}EF܅X}MUʋTE<}Dž0u0)00wM0t1;M}?U}}@ J @J@$J A;M|ʋu0MB0<}0~uEDž,U|ȋLԉ}tЋ|̉4E9,݃Xك<,Mɋ4uuɋ,EmmA, 9,|E܅pMuEmUmƍ]}U)P} UR71e[^_]1;MكĞ}ك̞ݝHكТfEƋU ݝhݝ`ك(ك,ك0fEmUmƋuV$v݅hu͉A܅x݅`܅HZZZ ;M\؍e[^_]ËEMȉE}0}}"؋(؋Т؋,؋0؋̞܅XoUWVS[EEEE@`PHl]كĞp8MrHuMp zuMru@Xp@ݝxݝpRݝ84݅8EMK1ҋEDȉDBȃ~MUщUM}1;M}كĞfEƴ fEmUmƋEP$ɋu܅xAZZZ ;M|؋U)׃T}=كĞ}ƋufMݝX PfMEm]mE}EF܅X}MUʋTE<}Dž0u0)00wM0t1;M}?U}}@J @ J@0J A;M|ʋu0MB0<}0~uEDž,U|ȋLԉ}tЋ|̉4E9,݃Xك<,Mɋ4uuɋ,EmmA,9,|E܅pMuEmUmƍ]}U)P} UR11e[^_]1;MكĞ}ك̞ݝHكТfEƋU ݝhݝ`ك(ك,ك0fEmUmƋEP$u݅h͉A܅x݅`܅HZZZ ;M\؍e[^_]ËEuE}0}}"؋(؋Т؋,؋0؋̞܅XoUWVS['}EG`G]كĞw@PpUuW MUWUUPUPGtxE9ٝ|}ȋM } Q/EU1ҋEDȉDBȃ~M}UϋME)щMUl~M}fU‰h fUE؋E Džpm]m‹Mp)AAp]]A plf;M}#l4H4D;M|݋upƋxT1U;u}Et%E BHBHB H UF;u|lM4HpAlpp0uEMTȉUt̋Tԉudt1;Uكك@ك<݃XvEME ɋd E uum]m‹ME5fQB;U|E؅|MEmUm]um]m‹U)MUU~T6EЋl։lUEh BM }W,1e[^_]Ð&UWVS[ÇUEB`JB ]كĞr@EqBuEyEAEEEEA%tBpUE9ٝ|}ЋM lMȍ Qi+EU1ҋElDȉDBȃ~UMҋE)׃dx}}}fU‰` fUE؋U Džhhm]m)hMуhA]]AA  dxf71;M})d4Ox4D;M|׋uhƋt|1;u}yvʋxЉ%E @B@@ ɋpBBBB BB\F;u|dhuBh wdhMuTȋ|̋MUU}DЋLԉE1;Uكك@ك<݃XE}EE ɋE uum]m‹xE}5fWB;U|E؅|MEmUm]um]m‹})MUU~L6EȋdΉdMU`:;M})4tt\t\w 4tL1;u}nك$كPك(E} ɋU E E uutF;u|؃dJ` hD` Mك t}ٝ,ك$HUٝDكĞك(ك$LdLمX Eم,f} xD>U D>uEƉxtu;}܋EuԉEMU؋u}EGƉu}ủM9u} uVĈ1^_]Ít&UWVEMQMz rBuEu}A}E܋N Ew M؉uԋx rE}ЉM̉uȉx9uh}rEỦu䉽p9UxE@Eux%lt1ҋ@xUċ@1ҊU1ҊTUl1ҊP<1ҊTUP1ҊTxU1ҊPU1ҊTU1ҊPU1ҊTxU1ҊPU1ҊTU1ҊPU1ҊTuĉUMċU@)% Uup֋uыNtم<֋Dمˉ|lf م٭۝8٭م٭۝<٭f fLfL݅8B9;Tx4~t֋|ٽممɉflʵ f|؍f4م٭۝0٭٭۝4٭f4݅0ыhh`lhh)ϋXhrlD,1e[^_]O؋h\pE9;L}$vNHT;L|ߋuxt,~tDž Ox(9Kٽf f&d8%P$$؋(H$DDɋ,̋x؋(͉ٝ ٕ$݅ ‹ƋT|مˉ٭۝٭م٭۝٭݅f΋lffDfDd(AFtم<΋Tمٽˉf |fمɋ|٭۝ ٭;P}'pOLt;P|ًMxt4ADž 1ɋW;T(ٽf ft&R$؋(LكĞDDDʉٕ(ٝ,݅(ʋT|مʉfωم,٭۝ ٭م٭۝$٭݅ ϋlffDfDAd;TlL lLv؋lLlL\px4wtpDDžD0 |Q=5t&UWVSD[:UBEH x}MԉNjJ pMȋB MuE̋ruЉErAUuĉUك(EEMԋx}Q$UE9U}fU fUu؋UVMuf >fD>U܋MEԃ‰MMUtuЋUEMȉU؋u̍Ntم<֋Dمˉ|lf م٭۝8٭م٭۝<٭f fLfL݅8B9;Tx4~t֋|ٽممɉflʵ f|؍f4م٭۝0٭٭۝4٭f4݅0ыhh`lhh)ϋXhrlD,%1e[^_]O؋h\pEh\\pH4zf>9;L}(vNH%T;L|ۋuxt,~tDž Ox(9Gٽf fvd8%P$$؋(H$DDɋ,̋x؋(͉ٝ ٕ$݅ ‹ƋT|مˉ٭۝٭م٭۝٭݅f΋lffDfDd(A9;L}%NH%T;L|ۋ}xtOtxQ1;Pٽf fR$؋(HكĞDDD ʋxٕ0ٝ4݅0م@ʋϋTمʉ|fم@؍4٭۝(٭م٭۝,٭݅(ϋlffDfDA`;P DRի||e |Q'UWVS\ M|Dž(QDž,Dž Dž$r zBplydEq M`\Džy DžHDžDž DžDžXT[0sE2PDž@r1IL)PLBLDNjD)ʍD=,D| ЋDx@щt<1H9}ك$t@9|؅pDPP @dW$؋(@كĞjPыDDž8Džh9hٽf 41fvhx<‹Gthم4‹|مˉ|Bf<مɉh٭۝(٭م٭۝,٭݅(Ƌlf<|fDfD4 90Phx4h>Ftم<΋Tمٽˉf |fمɋ|٭۝ ٭;P},p4OL4D;P|ԋuxt4NDž 1ɋP;T#ٽf fR$؋(LكĞDDDʉٕ(ٝ,݅(ʋT|مʉfωم,٭۝ ٭م٭۝$٭݅ ϋlffDfDAd;TlL lL{؋lLlL\px4wtpDDžD* |Q跜/ UWVS<[êd}GWEUЋHpUMO uM̋p MG EȋuԉEUwAuĉU)ك(EEUԋHMR$}E9}}fU fU}؋UWEȋu$<f VfTVf~ʉ%U)ȁf|z $$$$ˉ4$)$$$̋U܋M}Ѓm]muf4QuMBU܉M9V؋ű}M}ԋEċuԋ}AEM pUM؃<1[^_]ÍvUWVSP[ cUBEH x}MԉNjJ pMȋB MuE̋ruЉErAUuĉUك(EEMԋx}Q$UE9U;}fU fUU؋MRU}$f4O׉uf4BfTUufTftGׁ}}EfTG׋UEfLfDBU)UEU)%4$)‹u$)Eʉ<$$͉ $$ˉ$M$ˋ}UЃm]mʋEm]mufEftAuE؉M9؋M̋}ϋEuĉ}ԋ}ȍpEuԉUM pM_؃P1[^_]ÐUWVSĀ[`}OMqQEOUMԋW MuŰp UG EЉu؋wEċMBuȉMcEEU؋puR$؋(}ă]}}fME }fMEu܍<@E}VuUЋUf ~fEF}E܋~M؉Ũu܅E}ȍ@U}9EU؋x1O 0M׊L0M֊L0MՋMI~FMIMĐt&E׈MֈJEՈBMωI 0M׊L0M֊L0MĈMuƊM׈ EֈBMՈJuE}U؋M̉E}̋uUuM8e1[^_]PEPMQU؋})R聑맍UWVS,[ZWuMVy uU}VF uEz JFE2BuE؋rU؉MԉU܉}Ѓu}9EU܋}ԋpffEʋENH~'fMfM΋M܉JffEufUf}EuUԋMЉEuЋ}EƉUM̉uje1[^_]V}WURMԋu)QG뢉UWVS<[*V}uWF }EUGwO uP 0xMu܋HpMЉ}ԃ}؉UąE܉u<@}u9EM؋UЋxE؋wf8E܉uHfqf}fu΅fy~Mfzu}fEf}fE΋ufBIvufمM΋U֋UMыUMƋUEHEMMMٝlEH M؅lEEٝlEH0EHɉ؅lʁH ٝlEH0ɉMR$$؋($؋(̃MٝTمTمTMٝ MٝPٝLمPإ ]م إP؅d]م ح مL]]مlMمLإT؅d]؅T]]tDU܋]DBB B0ˋU;hٝ@@ @0مM΋uMMMEHEMEH MMMEH0EEHEMH EH0͋t@MMMMtttUuԋMЍ<}R$$؋($؋(م`م`]]م\EٝDمXحD؅dٕH؍\]ٝ@؍`إDمXe]مHح@]Ee؅d]]]إ@؍`EحH=;hم`ٝ<م\ٝ0مXٝ$مM΋U֋UMыUMƋUEHEMMMٝpEH M؅pEEٝpEH0EHɉ؅pʁH ٝpEH0ɉMR$؋(‰$م<̉$ȃم<ٕ8؋(ٕ,؍0ٝ م,ٕ4إ8M]م$ٕ(؍0ح M؅dٝ؍<]ح,م(إ ]؅8مpإ4MM]]م$ʉtʍDحˉ؍<ʋUإˋ؅dح(ɋDʍ؅4ɉ]]BB B0̋U;h]ٝ@@ @0^1[^_]ÐUWVS[}OGWw MEO GUu܋W(E؋wME4 U}ȉ9M؋uԉNj uA)щE4E}̉EEDžɉX1كĞ`PVP$p $$ɋEɋDžT)ƋF!Ήu)u@!փ!֋PЋ!֋U49T}`fU dfU1ɋ81ҋd81GL18HɋLHɋXʋTm]mfU1Ad@Td9K؋uXUX؋}A9]U9}؋Mԋ}4MFl}4MF4 }Elj};Nj}tЋ)ЅrpكĞhDVD$p $$ɋEɋDž)ƋF!Ήxu)u@!փx!֋DxЋ!֋U49}hfU |lfU1ɋxx81ҋl81G@1|8<ɋ@<ɋtʋm]mfU1Al@l9K؋utUpt؋M}4 *X\X\)Mȋ uA 9 )Ћ}}Eэ4)كĞٝωV$p$$ɋEɃ)ƋF!Ήu)@!Ѓ!։!׉ыM41;م}ɍ48fE fE싅ʋmmA;|؋U׋E>uA9}9M؋uԉNj uA)щ|E4E}̉E‹}ɍ4hكĞdٝ|ωxV$p$$ɋEɃ)ƋF!Ήpu)@!xlЃp!։tpl!׉ltEщt4t1;م}plɍ48fE fE싅plʋhmmA;|؋U|׋E|hdh>uA9E)Mȋ}ыt֋ȋxE<Ћ|)ȉ`كĞ\txщpV$p $$ɋEɋp)ƉF!Ήhu)@!pdЃh!։lhd!׉dlEщl4l1;}gdh 7hd͋`ɃB;|؋utxU׋`\tΉx`[؋MB9E9}ԋu؋ }A4)щTu}WQP,R08}W\QP,R8}WQP,R7EMȉE9EU؋MBrɋ܍BʋBrʋ܍܍rˋrBˋ܍Bˋ܍rʋr‹Bˋ܍B܍rz݅܍݅؋u}H\rBɋ<8܍@rʋ`rTBʋX܍@܍ Bˋ4B0rˋP܍@rˋ,܍Bʋ(B‹rˋL܍@r܍zB݅܍݅؋MpȋlUp֋rBɋ܍rʋrBʋ܍܍BˋBrˋ܍rˋ܍BʋB‹rˋ܍r܍zB݅x܍݅p؋MȋU֋lh ɋˋ$܍ʋ܍̋܍ʋ܍ɋ܍ʋʋ܍܍݅܍݅؋}`\E`Ƌ\/EU4 TXTX)Ћ}U‰XEX ) 1ك̞كك,كĞݝݝ݃Xك<ݝݝݝٝpQ$݅݅ʁ݅ȉ܍ݝP܅$$ɋ݅Ë݅IȉWσωx㋕x!$ݝ8݅!x܅ݝ0܍ݝ ݝ(tU)ЋH!u#)rl|$!|!|d!Ѓl)Ƌ|l!ʋp΋!d)ƋEd|!4h1ɍ;NxtNjt`tp}h\lhXxNjlLpHhم<0xdƋpDdƋl݅ɉ@dݝfEݝ fE6݅Zً mmA;@`tɋ\Xʋxl܍Pʋp܍8ˋLH܍Pˋh΋D@ʋd܍P܍0ˋ܍(܍P݅܍ ݅؋}EƋ  'UF9yE9)M؋uԉNj uA)щE4E}̉E‹}ɍ4d1ك̞كك,كĞݝݝ݃Xك<ݝݝݝٝ`pQ$݅݅ʁ݅ȉ܍ݝ܅$$ɋ݅Ë݅IȉWσω㋕!$ݝ݅!܅ݝ܍ݝxݝU)ЋH!u#)r$!!򉕼!Ѓ)Ƌ!ʋ΋!)ƋE!41ɍ;NNj}Njم<0ƋƋ݅ɉݝhfEݝp fE6݅hZًdmmA;@ɋʋ܍ʋ܍ˋ܍ˋ΋ʋ܍܍ˋ܍܍݅p܍x݅h؋}EƋd`d'UF9\S$EUȉ\9\M؋uԉNj uA)щXE4E}̉4E‹}ɍ4ك̞كك,كĞݝPݝHݝ@ݝ8XpQ$4݅P݅Hʁݝ݅@܍P܅84$$݅P݅Hʋȉw˃!։0(0DNݝ݅@ك!܅8ݝ܍Pݝݝ$U)Ћ0H!u#)r,D!,!,!Ѓ)Ƌ,!ʋ ΋0!)Ƌ,!u1;($ $$(4 <4( $݅ ݅ ݅ ̋( ˋ ܍ ɋ ̋ ܍ ˋ ɋ ʋ݅ ɋ܍ ˋ ܍B;؋uX4U׋X4\MG\9\U9\5 \E؋Uԋ4M\u܋<GM,Nj@4E E}̉;ENj}4Ћ)Ѕك̞كك,كĞݝݝݝݝ0pQ$݅݅ʁݝp݅܍܅4$$݅݅ʋȉw˃!։DNݝP݅ك!܅ݝH܍ݝ8ݝ@U)ЋH!u#)rD!!򋵐|!Ѓ)Ƌ!ʋ΋!|)Ƌ|!u1;xlh4d<`4\||X|x݅p h݅p l݅p ̋ ˋ ܍P ɋd ̋\ ` ܍H ˋ ɋ| ʋ݅p ɋX܍@ ˋ4 ܍8B;؋uU׋404\ME4 )Ћ}}(Eэ4,)ك̞كك,كĞݝ ݝݝݝ(pQ$݅ ݅ʁݝ݅܍ ܅4$$݅ ݅ʋȉw˃!։DNݝ݅ك!܅ݝ܍ ݝݝU)ЋH!u#)rD!!!Ѓ)Ƌ!ʋ΋!)Ƌ!u1;4<4݅ ݅ ݅ ̋ ˋ ܍ ɋ ̋ ܍ ˋ ɋ ʋ݅ ɋ܍ ˋ ܍B;؋u(U׋(\MB\9\E9\\}ԋu؋ }A4)щuUMU9MU9U}Elj}ύ&EԋUM)֋M؃!NJU%)ыŨ NjuȋE؋MUuBE؉UM9u|}MċUAM9UP^_]ÐUWVu ɋtʋt 1>u} EH[^_]Ív'UWVS [gh M } E u$Q DždOxUMU M 8B(}Q,|xU zJ B8tzDpU lzL\VNXTV$NPLVMH$} V,LDF0lT)ʉ@)LP⋍TJ⋍H)ϊHP)Hu l~\NHF`|xNTVX<9~'Av(Du @\ыE V0L} U XO4TzLHE @M } pHATDO\ut ɋM ED[^_]É'UWVSH[jM UJ0B4MzHJTrLM܉EJpBXM̉}J E؉Mċz\ɋBtMuMċr`}ԉuЉEȋrlIzhMԉEM!ȋMM܉M!ȋM ȊM@@@ EEMM IMMMẺMMBdHE9EEˋU1׊U֋t 1UĊ t 1UŠ t 1UɊ tM UMԋUM!ȋMM܉!ЋM ȉM@@@ MM@U̍ UBUUE‹EU9E3ˋtu11u ʋtt ɋt1u ʋ}1u ʋEH[^_]ÐUWVSP[:K UJ0zHBL}ME܋JTz\B`M؉}ЉE̋JhzpBlMȉ}MЉNjr4M̉u!ωEċrXEljuԊM؋EȋrtuM u!ȊM@@EGEEM܋uE}u BdHωE9E2EˋMu΋UuȋEuċM1uċ4t>E̋D‰E1:UE̋Lt19}EUT}1t 1 1MU MЋu}̋E!MƋUMԋ}!ʋEFu MuM܉}BBU}E UϋEMAuM9UɋMuEuU1Ɋ :tLˉM19EˋLu19}DU‹}u1̋t 1ʊ 1U E}P[^_]Ðt&UWVS[ÇH M$}$E$u(Q DždOxUMU$M$8B(}Q,|xU$zJ B8tzDpU$lzL\VNXTV$NPLVMH$}  V,LDF0lT)ʉ@)LtP⋍TJ⋍H)ϊHP)Hu$l~\NHF`|txFPNTVX<9~$EMu΋Euȋ}ċuM1}ċŰDtE1 8EUˋL t19}EU|1}U:ˋtU 1u 1u 1ʊMU ʋE}MЋU!MƋ}MԋE!ʊMFu M܉}BBB EuU} EUBMȉUE9uMuEuU1Ɋ :tL̉M19ŰELʉM1Ɋ :tUˋL t1Ҋ9}uʋTuЋ}‰E1t 1u ɋt11ҋu ͋EɊE ɋU`[^_]ÍUWVS@[z3 Ex4HLPH}MxXH\U}؉MԋP`xh}̋p0UЋxlMԋPtu}ȋpTUMЉu!ϋU׋ppM܋UuċM u!ʊMBUGUEM}uUċ ʋHdIM9ME &ʋMu΋EűMuƋEuȋ4t1EMˋDE1UċtMˋDt11 u ʋUȊM ̋uЋE}!MʊM܋u }̋E!NJMBUGUuM}EUċ E@ʋME΋Eu9EɋEM4t1EMʋDE1UċtMʋDt11 u ɋU E}@[^_]ÉUWVSD[1 Uz0r4BH}zTEJL}܋BXuz`r\E؉uԉ}ЋBtzlMJpEĉMȉMԋrhMЉu!ȋMȊM@u!M MEEEMEMȉu BdHME9EEɋMEȋuMUE׋Et1Ɋ EuMDuˊ Et UЋMMԉ!֊M܋UM؋E !ʊMUFUMEUȉu uʋEċMAUMUu9U7ɋUuE1ɋˋu ͋tt 1>u} ̋EL[^_]Ít&'UWVS,[' M } E u$Q Dž`OxUMU M 8B(}Q,|xU zJ B8tzDpU lzLXVNTPV$NLHVMD } F,N0Hl)։@P)Ɖ<ЊHPhpDž拍D)ϋL)ϊD⋍LJ≽pt*pOpDžt Džl)ϋM yHAPqXhy\|xATQ`89~$Y8x@898:`tU8E}ʉU 98}8EE4Et ED} G0}8D } G4p)}B`\}Džd9}ك̞ @;`|E dDž4x@P<)HtMpl)ыp`PdHh94\4E 4xp}~ Dž0T90"T0)( XDž(Dž$X9$׋<004vDž,((L E Pu VR$@u ʋ}tt ɋM1>}t 1ʊ}M ʋuL[^_]ÐUWV@EP4HHp0UMPXH\uUM܋pTPpHtxLuUԋpdMЋPH }uMȋx`plɉ}؉ŰxhMĉM܋U!ЋMȋUM@!ЋM ȉM@@M@MȋMMI‰M19EMUM΋UȋMɋUȍЋMȋU ɋM ɉ̋UъM܋U!ЋMȋUM@!ЋM ȉM@@M@MȋMM‹UщMM@Uuȋ}ɋu<̋u ɋu ʋ}u@^_]ÐUWVS<[G M } E u$Q Dž OxD@U M 8B(Uuȋ}ɋu<̋u ɋu ʋ}uD^_]Í&UWVSL[ M } E u$Q DžOxD@U M 8B(< 9t W 1e[^_]Ë(1(9D@9|~ 0tWEl U RQR QQvW~ U RPQRQODžtDž(Q(Dž/ PG[u U RQR QR U RQR QQE s U RPQRQ C U RQR QQ U RPQRQ U RQR QQUWVxEp4HHx0uMpXH\PL}uxTM܋phHlU}P`uЋxdM̋pH U؉}ԋPpxtUȉ}ĉutODžtɋE< ɋ}D( Ћ !8!ϊG t2ɋU ɋD( ,4,`KɋU< ɋ| (pMU<1;h}fE ك$ك(fE(uum]mfEم$(pA;h|J P((PP}$WMQVR,H Q(P<4` 9։,PP}$WMQRV,H Q(Rj 8PPu$V}WQR,HUuȋ}ɋu<̋u ɋu ʋ}uD^_]Í&UWVSL[ç u E }$V Hxu E UMH(u E UxV,H8u E tpVH u E lhVDHLd`GWL7GO$HPP}WMQjjuVUR|}WEPMQuVU R}W{ UtMUԋ}ЋE̋uȉyAq 1FE0u)E)E)Eu!u0Eȉu}E,)E)΋E)Eu!u,EЉu0t&USP[~c PM$APPQQM QURMQUREPU RURJ]ÐUWVSl)[!c M$u U ك8~كɃؿW$N F~~BBݝݝB BݝBݝxݝpBBݝhB ɋUʋJVɉMݝ`ɋAݝXݝP Dž  EM)Ή9! M)LDLH$G9 )E(Itɋ ~}4B L~D1;E}M ɋ@;E|1;}J1Ґ}AWVNjWNj;|1;}}BM4 7GD;}DD|ЋL~֋DDž9>ك<݃Xɋٝݝ݅A݅ɋG F܍ɉ݅x݅p܍x݅`1;݅X܍`%مݝt&݅܍݅܍݅pݕ8݅܍h݅܍X݅܍Pݕ݅݅8ƍX܅ٸ݅݅܍݅܍݅܍x݅܍p܍`݅݅܍X܍܍xf݅܍`fQt&ϋfʋA9щʋыыЋ7֋ t=/=fBω9t&)9|3Bf9‰2ϋ׋Nj׋Ћt=A=f@9&&9x}щxFx1;t2مdم`ݝݝO݅܍@݅܍݅܍0݅܍(݅܍ ݝ݅܍݅܍Dݕ݅݅ōX܅ظHffQ@T݅H\щTH;tT݅݅JݝLDD0ɋMݝ1݅ݝۅ݅ݝD8ۅ݅\݅܍ݝ܍݅ݝݕݕ݅ɋ@܍0݅܍(݅܍ ܍݅܍ݕ݅[݅ōX܅(غ݅܍@݅܍݅܍0݅܍(݅܍ ݝ݅܍݅܍Dݕ݅,؋HffA\܅ٽfff fd܅٭d۝`٭f`܅ٽfff fd܅٭d۝`٭f`xGu9ݝ9AAA Gݝݝݝݝ_ 1;t~UD ݅ݝ݅ˋ@D܍܍݅݅݅݅݅܍D\;tq1;tمdم`ݝpݝx)݅܍݅܍݅܍݅܍݅܍݅܍Dݕ݅xōX܅ظHffQ@T݅pH\щTH;toT݅݅JݝLDD0ɋMݝ݅݅1݅ݝۅ܍@݅ݝ܍D(ۅ݅\܍݅܍ݝ܍݅܍ݝݝ݅xÍX܅غ݅܍@݅܍݅܍݅܍݅܍݅܍Dݕ݅xx؋HffAٽfff fd܅٭d۝`٭f`Bٽfff fd܅٭d۝`٭f`ٽfff fd٭d۝`٭f`ٽfff fd܅٭d۝`٭f`xu9A9 GݝݝA1;t݅ˋ@D܍D܍݅D\;t~1;tمdم`ݝݝ ݅(܍P݅h܍H݅`܍@݅8݅X܍0Dݕ݅ 8čX܅8ظHffQ@T݅H\щTH;t]TD(݅`J݅XLDM݅hˋ1ݝXۅ܍P@ݝh܍HD ۅ݅h\܍@ݕ`܍8݅0ݝ(ݝ݅ X܅ٺ݅(܍P@݅h܍H݅`܍@݅8݅X܍0Dݕ݅ ؋HffAٽfff fd܅٭d۝`٭f`ٽfff fd܅٭d۝`٭f`1;tمdم`ݝݝں݅܍@݅݅݅܍Dݕ݅ظHf݅fQH@T\щTH;tɋTݝJ܍݅LDMۅ1D D܍ۅ݅\݅ɋ@݅ݝݝ݅XčX܅txٽfff fd܅٭d۝`٭f`ÍX܅uظ3ٽfff fd܅٭d۝`٭f`1;t2مdم`ݝݝں݅܍@݅Dݕ݅ظHf݅fQH@T\щTH;tSʋTݝJ܍݅LDMۅ1DD܍ۅ\ɋ@ݕ݅ÍX܅ٽfff fd܅٭d۝`٭f`(X܅u2ظQuVظٽfff fd܅٭d۝`٭f`ٽfff fd٭d۝`٭f`-Pu0VM(QU$RuV ȨDž 98DNjT‰T 9~888Op88@Az @@@p@Oz @HEr99 ШDž 9t&8DLʋ‹Ћ‹Ƌыʋʋ‹Ћ‹ыTʉT 9888zq @@yr@@r H@P9E9 ШDž 98|D‹Ћʋыȋȋ‹Ћ򋕼֋֋ЋT‰T 9Dž 9~w=Q`fOt&DTN 9Oex|t|8TxLtMʋD`ADD|ʋ‹Ћ‹ƋыxЋ4 x΋|ʋ‹Ћ‹NjыtЍ TЁ~/f=`fW(f`fOf7؋88J@z@@A@VEq 99 Ȩ Dž 98| DЋ‹ȋʋ‹򋵴DЋ D9:Dž 9f==i`fBTD z~ 9H8LDMʋD`ADDʋ‹Ћ‹Ƌ4 ΋ʋ‹Ћ‹ TЁf=`TfGDNO 9`frf7i8@J:@@AV|E9x9 ШDž 98|T Ƌ|xȋ|xDȉD 9l Dž 9f==`fATD4Q P9W8XL|MȋDdd`BdDыX‹ȋʋʋ‰XЋ‹Ƌ򋵠ƍtӽXX$XJf=`fA`frXf1Dž 9T(f=>=J`fA(Dȋ 98M4Xt D\ ` H\LX׋ ʉXLΊӽXXXLf=`fA`fQXf뮋@8@8tU9q9p ȨDž 98L|tƋptȋpDȉD 9~Dž 9T4f=`fB4Dȋ 9]X8L| MȋDdd`BdDϋʋXȋʋ‹X‹ƍtӽXXcXك$ك<ݝ0ݝ8ڹ݅P܍`݅X݅H݅h܍@Dݕ݅80ظf݅0\f~;x4nݝP܍`݅PD ܍X݅h݅HݝhD݅@ݕ݅8čXù܅txٽf f܅٭۝٭qÍX܅uظ1ٽf f܅٭۝٭Z;݅`ɋ݅Xʋ݅h܍H݅@Lك<wظfك$֋4QAUuUEM} VuM2UMUM=~gMfM|uEFMU?MȋEAM9E1^_]ËUf:>u}fwh= Uf됋Mf눐&UWVS,;[ MUك8~كɃؾ1V$ݜh@~؋U u B~ ErzHBUBDž\ XTP LH Ή p9 4?xtG9 )ыEItɋ zx~xΉ׋x1|9}O1Q\ƋVXWTƋ|VP@;|tDž񋽼9ك<݃XɋWٝݝ&9txƉ݅h\Xݝ@݅݅pݝ(݅݅xݝݝ8\X݅ݝݝ0ݝ ݅݅ݝAA1ɋݝݝGݝ;݅X\݅ݝDX݅D ݝD DD><ݝ~DTP$Lȉ$$܍@͋H\݅8˃܍ ݅܍0݅(݅݅܍݅݅܍8݅܍@܍ ݅0݅܍(܍݅܍\;݅TPݝ݅݅ݝ`݅݅ݝ@TݝPݝAݝxݝ݅BʋT1ݝhݝX݅݅ݝAݝPݝ8ݝ;مݝ4݅܍xH݅p܍݅܍hݕX݅܍`݅H܍X݅܍P݅܍@݅0܍8ݝPDݝ݅݅XōX܅P܅|ظffNfЊH=$=%fWffwu9P9L ȨpDž9(frNDž9~fH=1fBTD9c($(GD$׋@(<Nj‹TDȋ(@$<TDH<fH==%fBffODž95~fH=fATD9(GD(׋@TDȋ(@TDH afH==JfB=ffffO P:e9Tt TWle{ VdTTHUWVSL3E M[- hpP pp@qE)‰uBƉf~|4B0؋94~TMt4TT\9x}0hlj0wxFx9xtAt9tL9t VE1e[^_]Ít&ٽf f٭۝٭ RD덉'UWVS[1 MDžhDžlu Udك8~كɃؿW$~ MF~ANBBݝB ʋVwʉUݝɋBݝݝJODžt< ҍ|I89t|$}t@t9t^t)EItɋt4W<QGN1ɉ9}F1ҍ&fV%DfW%DA;|ËDžx 849xك<݃XɋمL݅@Ht&؋fA;<D܍D܍f8܍ˁ܍ɋĉ<'H ظ5fA;<$؋\9x}4w4AxAx89xt@t9td9t V6=1e[^_]ٽf f'٭۝٭ Rx<뉍v'UWVS[ñ M$Dž`Dždu U Tك8~كɃؿW$N ~F~@pBBݝB ʋUݝʋJVuݝNݝDžt ҍ|pM)Ή49tU)lh\tAt9tt)E(Itɋtt4H$ODžl0x txN49l|&hlxAl9lGxl)EItɋllы0@1ɉ9};1ҍt&1D1DxA;|ˋDžp V4,9p ك@ك<݃Xʋ0zLٝHٝDݝ8P1ɋ,x׉N@ݝA1ݝ;PمDمHݝX݅Dž`݅܍݅݅܍݅XXظd`dtDTx>t׉;P݅|4ۅ1ݝD ݅D ݝD݅xݝxD ݝ݅D D(‹݅܍ؾD(݅܍о݅܍Ⱦ݅݅x܍݅܍݅݅܍Ľ݅x܍܍Ⱦ݅܍ؾ܍݅܍о܍݅܍܍݅\;B݅hݝ݅݅pݝ݅ݝ`݅xݝ݅ݝݝ݅Fʋ1ݝݝݝx݅݅݅ݝBݝpݝhݝXݝ;݅DݝD݅ɋݝD݅D D(ݝD݅D ݝD(LMݝP1ۅۅ\݅ϋ܍܍x݅܍܍݅܍܍݅܍݅p܍x݅܍h݅܍`݅X܍`݅܍݅܍݅܍p݅܍h݅P܍XD\;)݅݅݅ȿ݅пݝ(݅ؿݝ ݝFFݝݝF1ݝHݝ@ݝ8ݝ0;ممݝݝ݅(ɋ݅H܍ ݅@܍݅8܍݅0܍D݅ܥōXܥ غȋЋȽ Ľщ;݅8݅0݅H܍(݅@݅ ݝHݝ@ɋD ݅Hݝ8܍݅@܍݅8܍D(ݝ0ݝܥ݅NčX܅ܥٹ݅(ɋ݅H܍ ݅@܍݅8܍݅0܍D݅ܥ!ȃȽĽʉ;Vt&;ؽمم݅ؼݝݝݝE؋ܽȽȽωF;ؽ Dݝݝp ݝHDݝDݝhD ݝ@DݝDݝ`Dɋ ݝػݝ8DDݝлݝDDݝXDɋ ݝȻݝ0D D ݝD ݝPD ݝݝ(Dݝݝ DDD ݝݝݝU 21ҊR$݅ ݅ݝ݅܍ݕ݅(݅ݝ݅0݅8ݝ݅܍ݕ݅݅H݅@ݕxݝ݅܍݅ػ݅X݅Pݕhݝp݅л܍p݅Ȼ݅h݅`ݝ`݅܍`ݕX݅p݅x݅݅pݕHݝP݅h܍P݅ȿ݅п݅`݅݅ؿ݅ݕ8ݝ@݅X܍@݅P݅݅ݕ(ݝ0݅H܍0݅@݅݅ݕݝ ݅8܍ ݅0݅݅ݝ݅(܍݅ ݅݅݅݅݅ܥ݅݅܍݅܍݅܍݅܍݅܍݅ػ܍x݅л܍p݅Ȼ܍h݅܍`݅p܍X݅h܍P݅`܍H݅X܍@݅P܍8݅H܍0݅@܍(݅8܍ ݅0܍݅(܍݅ ݅݅݅݅݅ܥfظܽȽϋȽʉF;ؽT;Լ}1E ‹Լ)1P$ȽǃJu1;M}uTTA9|}~5uԼ4ʋMDIu苽 9м}̼н։̼̽‹мG  мԽ9мȽF99t Q1e[^_]ٽf fܥ٭۝٭)ٽf f܅ܥ٭۝٭t&݅܍݅܍܍݅܍݅܍݅܍݅ػ܍x݅л܍p݅Ȼ܍h݅܍`݅p܍X݅h܍P݅`܍H݅X܍@݅P܍8݅H܍0݅@܍(݅8܍ ݅0܍݅(܍܍ ܍ݕٽ܍fʶ ܍f܍ݝݝݝܥ٭۝٭ WOX{нnEн8'UWVSlE$u }M t1[þxB~VNUF FJVMAVDžU)׃Dž9M)ΉJB9:)E(ItɋNjM }}~lj~Dž9M)ʉ  |xplhd`\XTPL 11Ɋ@1Ҋ,1ҋ <1Ɋ7(1Ҋ 8 7$1Ҋ41ҊDž 9Pt&@<($H0D84 ,@,<(1P81$17417$ (LX0,T8Ƌ`ȋ\Ћhы@Ћ<d‹HpHDlH‹ыX΋PBq@@@W H~@$}9 P9 Ȩv Dž 9ox|t|8LxDtы|‹ȋƋʋxȋʋ ы| xы‹ȋNjtLщL 9@u<9@ MF 9 p91PDž;}}Z4H441;U}t&B;U|UF9|tuD1ҋp`Gʉ99}BUʋD )‹1`Ju1;U}}D44B9|DžU 9}3}DU 4 DDJuu lE()ǃ9\}hdA\;0G\l!9\#X`AX9X[,9t Wq(9Pt PRoq1e[^_]ËE FRp y򉕬#f @$E9 :9 ШDž 98֋DNjT‰T 9~׃J $@u9 98 Ш'Dž 9@xxxt`DT։ 9814xtUы|D1ɋ` D|LxϋxDӽxxx888V8O8z@@p r@@@r @Ox$E9 J9 ШDž 98DLʋ‹Ћ‹Ƌыʋʋ‹Ћ‹NjыTʉT 9888q @@zVy@@@HF $E9 J9 Ш5Dž 9 8|D‹Ћʋыȋȋ‹Ћ֋֋ЋT‰T 9Dž 9Њt`2TD 9px|t|8TxL1tUыD1ɋ` DL|ʋ‹Ћ‹ƋыxЋ4 x֋|ʋ‹Ћ‹NjtэD88@>Px@@r@Au$9 J 9 Ш)Dž 98| DЋ‹ȋʋ‹򋵴DЋ D9:HDž 9'Њt`2TD 98LDDE11ɋ` DLʋ‹Ћ‹Ƌ4 ֋ʋ‹Ћ‹ D8@Q9@@rH$E9 9 ШDž 98|T ƋȋDȉD 9lDž 9xxxЊt`DTЉ 98x8L|DMȋ11ɋ` DLыx‹ȋʋʋ‰xЋ‹Ƌ򋵸Ƌ‹Dӽxxx%@8@:$VE9 9 ШMDž 9q8L|‹ЋȋDȉD 9~Dž 9xxxЊ`TD 9G8xL| DMȋ11ɋ` DLϋʋxȋʋ‹x‹DӽxxBxЊT`UDž 9Ѓxt`TD 9A8|t UЋD11`DD򋵐ȋDx~Dž 9ы xt`TD 98‹L|E1D1`DDȋDx PZP ,9t WZ@v'UWVSL3E UxH xpJxH[k"B1҉U)@9~Mt|D6=@_DžDž EM)9t)׋9~UDžD:9p|$K@9,)E Itɋ1;}ك$@;|Dž9atE}|1;M;p!Ш1GAP$;M;p!Шu;;p!Ш)ȋuG)AƋ0%P$;;p!ШuE9;p!ШtYʋt)HƋAG0%P$֋E9;p!Шu1ɋU;t&1BGGݝݝBB;|w݅ˋ܍DD ŋ܍݅D\;|؋ ;)E)ȃ\ 1;| ك$ك@كҋ9|%# G9 )EItɋ׋΋1ω9}`1ɍt&1P1$׋$1$׋$$B;|Dž @΋9ك@ك<݃Xʋqٝٝݝ݅F݅ɋAG܍݅x 1;݅p܍x݅`ɋ݅X܍`Wممݝ(ݝ &݅܍Dž݅܍݅p݅܍hݝH݅Xݝ@݅܍Pݝܥ(݅ ݅HƍX܅@܅ܥ(nں݅݅܍݅܍݅܍x݅܍p܍`݅܍XɊɃ܍܍x݅܍`ʈʈЋ:֋;ɋ݅hDݝDDݝDDݝݝDʋ1ɉ1݅2܍݅PD̋Tۅۅ\ݕ݅ ܥ(dX܅ܥ(Dž݅܍݅܍݅p݅܍hݝH݅Xݝ@݅܍Pݝܥ(݅ ;݅݅܍݅܍݅܍x݅܍p܍`݅܍XɊɃ܍܍x݅܍`ʈʋ:֋;~;مم݅ݝݝݝ3&A;ʋDʋݝ8DDDݝ0DD1P܍$܍˃܍܍x܍pݝ܍h݅8܍`ݕ8݅0܍X܍Pݝ0݅ܥ݅܅܅8܅0ܥظA;v1ҊR$17D$$9\}Ɖ֋G9RG99t R>1e[^_]Ít&܅Hٽf f܅@܅ܥ(٭۝٭Hٽf f܅ܥ(٭۝٭ٽf f܅܅8܅0ܥ٭۝٭ R= vUWVuEU Eȋ}N EċFMOEM؋FEF EFEFEFEFrEuBrE܉uMBɃr MԋUOʉhE؃EElp9E|1t&űMFu9ME})UHШt׋ŰupMϋlu}~hlHtu܋UE܉xM‹}UMxu11Ҋ}Mx1uE>%1ME1MuEMMEuEыEċMMuEƋMEԋuE֋MUʋUϋM׋Mԉ}U}NjEԉ}‹x`ϋtd9MDMDDMЋuuETUX49P}\׋}ϋL}ȋP‹M֋LUMTщLM`ЋdEEdNjUЉMM`UЋtM9} U11ɋ`\dEXM1ҊdL1Ҋ`P1ҊM\TXMыMUȋuMDT‹XuE֋Puƍ4U\‹LUЋUЋUD EDMut&htaM1ҋd`M@1Ҋ%Nj@M@ЊMȋUtJ}uEƋxHUuMűMFu9Mĸ1^_]Ë}뺉UWVS,;[ñM} Uك4~كɃؾ1V$ݜh@~؋Gw WEwHWEx|Dž\ XTP LH >9|$ @9 )EItɋ֋񋽜|щ1҉x9}w1ɐ1P$\1$1$X׋|$1$T׋x$$PB;|Dž ΋9ك@ك<݃XʋQٝٝݝ9tlj݅h\Xݝ@݅݅pݝ(݅݅xݝݝ8\X݅ݝݝ0ݝ ݅݅ݝAA1ɋݝݝGݝ;~}݅X\݅ݝD݅ݝD X1ݝDD D17DTP$Lȉ$$܍@͋H\݅8̃܍ ݅܍0݅(݅݅܍݅݅܍8݅܍@܍ ݅0݅܍(܍݅܍\;݅TPݝ݅݅ݝ`݅݅ݝxݝ@݅ݝhݝX݅݅ݝɋݝPݝ8ݝFAFʉ,1ݝݝݝ;ممݝݝR݅܍xH݅p܍݅܍hݕ`݅܍`݅H܍X݅܍P݅܍@݅0܍8ݝXDݝܥ݅݅`ōX܅X܅ܥعȋ>,,;݅PTݝp݅݅ݝD݅ʋP܍ݝD݅ݝʋT܍XDݝH݅p܍xˋPݝD ݕpݝD ݅ݝ0݅܍h̋H܍`݅H܍P݅܍@݅܍8ݝhݕ݅ܥd݅pōX܅h܅ܥؾ݅܍xH݅p܍݅܍hݕ`݅܍`݅H܍X݅܍P݅܍@݅0܍8ݝXDݝܥ݅)>,lj,;p&;مم݅ݝݝݝ9؋,B;\T1֋XDݝыPݝы\ݝDTݝ DPݝD\ݝDTݝDPݝDɋ\ݝݝDDݝݝDTPݝDݝD,ݝP$L݅p݅hݝH݅ ܍HݕP݅xݝ@݅܍@݅ݝ8݅܍8݅ݝ0݅܍0݅ݝ(݅܍(݅݅݅݅ݝ ݅܍ ݅݅݅ݝ݅܍݅ݝ݅܍݅ݝ݅܍݅ݝ݅܍݅݅݅݅݅݅ܥ8݅P݅ ܍H݅܍@݅܍8݅܍0݅܍(݅܍ ݅܍݅܍݅܍݅܍݅݅݅݅݅݅ܥظ,Ɖ,B;I,1Ɋ Q$L1$1$\$$XT\PXL\T\PL@9@9l9t R{)1e[^_]܅`ٽfff fd܅X܅ܥ٭d۝`٭f`t&܅pٽfff fd܅h܅ܥ٭d۝`٭f`&݅ ٽf܍Hff݅܍Pϵ ܍@݅܍8݅܍0݅܍(݅܍ ݅܍݅܍݅܍݅܍܍܍ݕ܍ݕ܍ݕ܍ݕݕܥfd٭d۝`٭f` )R'SvUWVSLD[AM} U ك4~كɃؾ1V$ݜ@~؋WMGUw AwOBDž << ؽؽ ԽΉܽ p9|*@9)EItɋ΋֋Ήн1։̽ƉȽ$91ҋ1P1$ ΋$1$΋н$1$΋̽$1$Ƚϊ$$A;$nDž9ܼك@ك<݃XʍAٝٝݝ9tܼܼ݅ ݝ݅8݅ ݝо݅X݅(ݝ ܼݝݝF݅0ݝݝ݅@Bʋ ݝݝؾݝȾ݅P݅H݅`ݝFݝݝݝݝB1;݅݅݅ݝݝɋԽ1܍ɉ1܍Ⱦ݅Խ݅D D DݝD ݝD ݝ݅D(΋݅܍D(݅܍ؾ݅܍о݅݅܍݅܍݅݅܍݅܍܍о݅܍܍Ⱦ݅܍ؾ܍݅܍܍݅\ܽ;<݅h݅p݅x݅ݝݝ݅݅ʋݝݝ݅݅ݝݝ݅݅ݝݝxAݝpݝhG1ݝмݝȼݝݝ;DD݅м݅ȼݝмݝȼDDD ݅݅ݝݝD(D(ɋԽݝ`TD P$ȉ$$܍ϋ\݅΃܍݅м܍݅܍݅݅݅ȼ܍x݅܍p݅h݅м܍܍܍݅܍܍܍݅ȼ܍܍p݅܍x݅`܍hD\;Pܼ݅݅݅ȿ݅пݝ8݅ؿݝ0ݝ(BBݝ ݝBɋ1҉ݝXݝPݝHݝ@;ممݝݝ݅8ɋ݅X܍0݅P܍(݅H܍ ݅@܍D݅ܥōXܥp عȋ1ܽ;݅H݅@݅X܍8݅P݅0ݝXݝPɋD ݅XݝH܍(݅P܍ ݅H܍D(ݝ@ݝܥ݅:čX܅ܥqپ݅8ɋ݅X܍0݅P܍(݅H܍ ݅@܍D݅ܥ ܽ;Ht&;مم݅ݝݝݝK؋ؽB; ɋ1ݝPDݝы ݝxDݝݝDDɋ ݝpݝHDDɋݝݝػDDɋ ݝhݝ@DDݝݝлDD ݝ`ݝ8D D ɋݝݝȻD D ݝXݝ0Dݝ(ݝ DDD ݝݝݝP$݅ݕ݅ ݝ݅܍݅(݅ݝ݅0݅8ݝ݅܍ݕ݅݅H݅@ݕݝ݅܍݅݅X݅Pݕݝ݅ػ܍݅л݅h݅`ݝx݅Ȼ܍xݕp݅x݅x݅݅pݕ`ݝh݅p܍h݅ȿ݅п݅h݅݅ؿ݅ݕPݝX݅`܍X݅X݅݅ݕ@ݝH݅P܍H݅H݅݅ݕ0ݝ8݅@܍8݅8݅݅ݝ(݅0܍(݅(݅ ݅݅݅݅ܥ݅݅܍݅܍݅܍݅܍݅܍݅܍݅ػ܍݅л܍݅Ȼ܍x݅x܍p݅p܍h݅h܍`݅`܍X݅X܍P݅P܍H݅H܍@݅@܍8݅8܍0݅0܍(݅(݅ ݅݅݅݅ܥظؽƉB;f&1Ɋ Q$1$1$\ܽ$$\ЋЊ$$\Ɖ F9+@9x 9t V1e[^_]ٽf fܥ٭۝٭Zٽf f܅ܥ٭۝٭S݅܍݅܍܍݅܍݅܍݅܍݅܍݅ػ܍݅л܍݅Ȼ܍x݅x܍p݅p܍h݅h܍`݅`܍X݅X܍P݅P܍H݅H܍@݅@܍8݅8܍0݅0܍(܍(܍ ݕ ٽ܍fʵ ܍f܍ݝݝݝܥ٭۝٭- R&nt&UWVSE u Mt1[A<xB~NE~ VM~PFq @ɋNjDž ~9|%# B9 )EItɋ΋~<x|`plhdL\XTP1ɋ @11Ɋ,1 <1Ɋ7(18 $1Ɋ 041Ɋ Dž ‹9F<(D04@<8$ H@(,,181$141$P LX0,T8Ƌ`ȋ\Ћhы@Ћ<d‹HpHDlH‹ ыX΋Pم`مXݝݝم\ؾ݅܍@X݅܍8݅܍0݅܍(݅܍ Dݕ݅X݅\Ɖ;hd݅݅ݝD D(݅ݝݝݝ݅ɋ1ݕP$1$܍@݅$܍8ɋX\܍0˃݅܍(݅܍ ݕ݅!ÍX܅ٽfff fd܅٭d۝`٭f`čX܅uyغЋ2X݅\։ٽfff fd܅٭d۝`٭f`Q1;hم`مXݝݝم\پ݅@ɋX݅܍8݅܍0݅܍(D݅غЋ2X݅\։;h݅DD ݅ݝݕ݅ʋ1ݝP$1$܍@݅8$ɋX\̃܍0݅܍(ݕ݅WX¾܅ٽfff fd܅٭d۝`٭f`čXútLٽfff fd٭d۝`٭f`1;hم`مXݝݝpم\ؾ݅@ɋX݅܍8݅܍0D݅ptغЋ2X݅\։;hLDD݅݅ݕݝ1P$1$܍@݅8$ɋX\˃܍0݅pX¾ٽfff fd٭d۝`٭f`;ÍXºtLٽfff fd٭d۝`٭f`8u ظ0ٽfff fd٭d۝`٭f`PP}(WU RMQTVE P}W UWVS\U XTrJ rBu[՗}Džvru$҃HUu))A@M EuNʉ@^Eu=Mu19}B9|Dž9|$@9)E(ItɋDž 9}$TB9|܋WDžPE9Pb DžLu9L MILL})׃L1Ƀ1L1Ҋ(1$171ɉ 1Ҋ1Ҋ ~<>DzA@OD@xA <8Ox40A,B< 0D49^ "u9P9L ШDž91D1NJT  DыLыLщ9~$L11ɉL( 2$1Ɋ 02 1ҋ1ҊN 1ҊHD@BQ <8HB40Q O,ȃ1cSX} A }9P9L ШDž9~n:TD9J11Ҋ9DTNj<D׋|HtЊH'(u9P9L Ȩ2Dž9 (( $$1Ҋ1Ҋ 4ʋ0‹(<Ћ$8‹D@ƋTыʋ4ʋ0‹$<Ћ 8‹D(@TыTʉTǃ9/u9P9L ȨDž9 $($(1 1:$<(Nj8׋@ЋDʋ4 T‹$<‹ 8ЋD(@֋4ЋT‰T׃9Dž9ЊHt2TDƉ9& (( $$11 4 ʋ0‹(<Ћ$8‹D@Ƌ,ыЋT4 ֋4ʋ0‹$<Ћ 8‹D(@Nj,эTDHE9P9L ШDž9n ((1$$1<( ׋$8NjDϋ@ʋT<׋$< <׋8NjD(@Ƌ|׋|ƒ9oDž9NЊHt2TD9  $($(11 4 ʋ0‹(<Ћ$8‹D@ƋT4 ֋4ʋ0‹$<Ћ 8‹D(@T DHBCE9P9L Ш%Dž9@1ҋ($(1Ҋ7($@D‹<T<Nj(8E΋ыU)‰EEJuG;}|}U)9}vU؋EŠOPNM΋MTuE܉ ȋP4M)ϋ NO"F" ȈFM܋EO uE})9Eu܃}UM؋u)Љ1M̊21D2UӥM苍 ":# Ȉ:M؋UuE]uuMODž}9}MuU)։Hu}9}}pEU؋E‹HϋuЋM)΋11AMM ЈU؋ыU׉Nuʋ}F9lEM)ȉ|9EtM܋E}M<u؋Έ‹|4 U)Љ1M̊F1҉ӥMꋍ ъ"W" ˆWU؋MuUu)89U}k}Mu7lj}׋U)׋7"18" ȋ1M΋MOuŋu]}}0MN/u9}ruUME)MuMu9u}; >8E΋ыU)ŠEEJuG;}|}U)9}CU؋EŠ/0.΋MTE܉ ȋ04M)ϋ ./"F" ȈFM܋EO uuU)11x9BBhȊM⋍ UWVS(EM xpqpQp[ÅQ},qH p RpuكĞݝ=uM~ك݅ɃݝغR1$݅9}M@9|؋}1<ʉ9}ك̞@;|Dž9MU()։|Dž9iك$ك̞ٝ`ٝ\݃Xك܍l@;|؋DE D9:;E$|;}4uAu91;}|‹ٽم\f ݅PمLfEuEu٭٭A;|؋HFJH9E$9|#|9}4A9p9t P,x9t Q 1e[^_]مhمhUWVS[JUu֋E<DžHDžDDž@Dž0Dž,…4HErv #<1ҍ Bȃ~ɋ}9}k}DDDEuhEuQEuBEuC}fM fMm]mu}| FpE1Ƀ1a V1<L41T ы ֋1T ы ֋1T ы ֋1T ы ֋1T ы ֋1T щ4 򋵼7~40w)ƈ 71uE x9u1;U$9|IF9)t9t Qk1e[^_]à M$QjURMQFP迤MUу M9|넉9zjURMQFR(MEM9|J"<<< <<+<9EkUWV} Mu WA}UEV FOw u9~EM9~U))1~p11~B1ɋUpu F4EuuP8>9,54Dž(d9(o uu Dž Dž ։t=11ҊM ӥpt plt l$$U$xtn jPRQQ7}${ jPRtQV xttDž,1x(Dž Dž0HD-v890(00 0|PDLHL~H(B,$瀀 ‹(က% ( ‰% Nj(%(( Nj %Nj    % 0 ‹|  D ʋWH$,G $ Љ $ Ё $$ $  Ƌ%‰ % ׋ ω ȋ ȋ0 Nj|TH DB Q ,(( %, ʋ, ʉ Љʉҋ ы  ‰ ʉ ꉅ ы  щက %  ʋ|0 DDr JH2JP ΉzP0pPL ʉL0LLH HHL l4  Dt0DH DH8L90000 TH|(QD%,~Ɖ(( (瀀က ‰(% Ή((%  ׋ က ׉ Ήɋ| ΋ 0H %Dw Q, Ћ ΁ ΋  ȋ Ƌ %က ׉  0 ȋ| L ϋHDA ,w , ρꉵ( ׁ怀, щы  ΋   ցက  ɋ0 ʉ|L ƋD ȋ2zJH2zr ΋0p ׉T ‰|0 l ʉL ȉD0 QϾ0 jU$R8@PPQU Rz M$Z jE$PPRVQO NPF jM$QPVxWR }$ ju$VPWtRP y jj8JRGWlQ xtE|4AM򉵔‰$ (8)׉<$E 1ҊM <0L,ӥ,,,t LM$jPW$VR-$8DžXɋTp TXXЋ|% lj% NjD% $%$ ҉$$ Ћ|$$ NjL ω0p<< !ȉ!׉ 0l# Ƌp!1 U }4E}L~B E1ɉd9vU DtU | t ‰9}򋵰9};} ;} <9}ЋD9}DE T|E ;tT};};};D}D9}ЋD 9}D ;d[MDžl`9luXEU4DžLы}4U6} M> U UEɍDٝGٝA}UDž\9\EمEtċ\مEt\Et\\ED؋\\DDٝٝDDEt ممEt مE؋\\E؋\\Et\\ Euh؋\\ \9}hɋ\DDD D ESESNً\\ \9|؋}Dž9UMxE U։ A@ˉ1ٝٝٝ|ٝ;M\م|Eم|ٝEEٝDDٝDDٝ|مٝٝEt مم|Etم|EEٝdمًdEtمٝ`مEt مɉdممdEممEuممEuممم`EH\م0مXZXم`zX ممR EممEم\مE2م\مE0م\ مEt مɋمEɋ\dمdEyʋ\EYً\ \\;$\@BٝB@̋ٝٝ@ B ٝٝDمٝٝٝDD D EtمEuممE5ٕd͋dم|E+ٕE1E1ٕ*]3ڋ|مB̋TمEˋT &ʋTmEٕdɋ\م0م`مXZX̉zP dمd;xDDٝtمEممtEtمtمEtم|EtEt͋EٝdˋdEhٝddq;x:DD ٝpD DE E Etɋ\مpTEpDEٝddQ|;xbDDDDEEE|مtE`EFˋ\TE@ٝdɋdET,ٝdd|M9@U99t Qje1[^_]ٕd[EٕddDE gOٕd RiU ֋u1҃9|JEusEusEusٝddB9DDEtEtEtEt닐UWVS[W1UEMUu}ENjE}|y|…u Ћu 1҉}t e[^_]Íy ‰E)RThEtlj1ɋETЉTAƒ~ E}MMMUFF|F EDž\E9\~kt&Et\Eً\DEu9\\E\9?\DEt눍؋\TE\9~ōt&U9\\DE E ً\E ݝ`\`dLNjU}BU4}MEXE1;M|&bݝ`}`dTA;M}>UXEtݝ`}`dTA;M|‹|DžxM9xMUFD|ċMUt}Dȋ|FEF EDž\E9\~tEt\Eً\EDE\E\M\9\DExsɋ\vBE؋\E؋\ETM\9kE9\n\DElElً\Elً\EȋMBBB E1;M~L}EtEDEu4\;M6DEt먉ɋ\E;T;M~;MZDEEEBϋE41;U }fM fMlEEEmmɋMщ\Eɋ\EmmEB;UtEыMЋEEgEgEcɋEmmɋMщ\E]XO}UE MUx]E!x}E9x|9xMUFDLĉplEFTF E1;M~REtEuDE\;MDEtUE؋\ElTo;MDEEEݝ``dD<1;M}fE fE8EuXEuXm]m}u4A;M}BlpuEtEtEt릃 UR2`1ҍe[^_]DUWVS[(UEMUu}ENjE}EyE…u Ћu 1҉}t e[^_]Íy ‰E)R _Etlj1ɋEt&TЉTAƒ~ E}MMMUFF|FEDžlE9l~kt&EtlEًlD(Eu9l\El9?lD Et눍؋lTEl9~ōt&U9llD EEًlEݝplptLNj}MG}4΃}EU1ɉEh;M|&bݝp}ptTA;M}>UhEtݝp}ptTA;M|‹MEM9M MUFD|ċMU|}Dȋ|FEFEDžlE9l~x t&EtlEًlED(ElE\Ml9lD ExsɋlxDE؋lE؋lETMl9jE9lklD EiEiًlEiًlEȋMBBBE1;M~LEtED(Eu6\;M8D Et먍t&ɋlE8T;M~;MUD EEE=ϋE41;U|l&EEEɋMщlEɋlEȋEB;U|EыMЋEEsEsEoɋEɋMщlEiɋlEȋEB;Ui}M}MMU}]EU!9UEU9UEFLċ|FTxlFE1;M~`EtEuɋlD(E؋l\;MD EtOSE؋lEqY_;MD EEEݝplpt|41;M|.nEuLEuLًuA;M}BxluEtEtEt벃 URV1ҍe[^_]a4UWVS[UEMUu}ENjE}EyE…u Ћu 1҉}t e[^_]Íy )QUEt1ҍ &DЉDBȃ~E<ЋU}U1FFTF EDžpE9p~gEtpEcًpDEu9p\Ep9?pDEt눍؋pTEp9~ōt&E9ppDEEًpEٝtpt MG4EDžpU}d9p|-ٝtpEtEAp9}zpdEt빍&ɋp>ZE؋pE}E}9}t&ME}TFLĉUFT̋|ȉMdF EDžpE9p~m&EtpEًpDEu9p\Ep9?pDEt눍؋pTEp9~ōt&U9ppDEEًpEٝtpt MBBB EaDžpu9p~rEtpEًpdDEuPpd\up9RpDEzuɋpW؋pdTup9~E9plpDEEًpEٝtpdt4E41;U|WEuyEu}EMщpEuًpEEB;U{MEEdEtEtEt؋EMщpEt؋pEEB;U|}U ׋}MMUM]E!}E9} M9M;E}ULDF|ȉEFMpF E1;}~tEtEٕtptDEٝtptT;}DEtɋpdsAE*؋pE*%ٕt\ٝt'gF.;}DEEEٝtpt 1;M|:zEuXEuXٝt}tA;M}BEpu}EtEtEt릃 MQjM1ҍe[^_]U2UWVS[7UMuUEEuu <@ENjE l}ypl… 1Ҩt e[^_]Íy Q:LEtӉ1ɍTȉTAƒ~pExUFF|F FFEE"Dž<x9<EE؋<E<DE<\x<9ɋҍ M~qU1U)&Љ9>;M~@uEЉMMt>M)ȋM΋E 9ʋ4u;U~9щUU MMuÐ}}}MGI}EM9EKuB~Bu(^_]É'UWV(E}wO0uwtMEOXF~MuUE9uNu)u܋UFJu܋MuAFU؋UGMu9UEMtˋU܋u uU UEu]EEEm؋u UFuEu]EE/E؋EE9|cEEEEEB ;Uɋu uuEu]EEmEmEmEcEeEB ;Ue x(^_]Í'UWV,E}OM􋇈MUw0EE9uUVUM}AOM}9uEMt݋U MUҍ<}M}1҉E܉MEuLEMtPыE) Eu]EEuEtEMut&UUM}AOM}9u7E@~Eu,^_]ÐUWV0E}G0uWwtEUOXVEUFUME9UJU/}؋EGU}؋u}HBFGMEԉUu}9MEMtŋE؋}M U܍4NjE9MEu ]EEEv؋M EU܋yMEu ]EE/E؋}9|dt&EEEEEA49ɋU UЋUЋUEu]EEmEmEmEcEeEA49em0^_]UWV0EuNM􋆈MU~0EE9}UWUMuANMu9}EMt݋U MUҍ4ȉu4Eu؉E1ɋu!EuP؋UENUtPȋU)Eu]EEuEtًUENUuMMMuANMu9}3E@~E}0^_]ÐUWVpEUuz0NU}M}uUuډUߋDL}Eȋ|M}ċD }EM9}O}}MGI}EM9E`EMtڋu U}MFU}MMEM&}Mu1Ҋ91U1Ҋ>uE܊U>%ƋE 8EȁϋMEU؋Uċ UMԋMMEЋEEMŰU)ʉ!ыU)ʉEUE)‹M!Љ))ʉ!))!ЋU)lj)Љ!ЋU))!׋U׉)!)8)׋U؉!NjE))Ή!u E؋u)։ʉ!u)E}̋EЉu)NjuЉ!}))!)Ɖ)!ЋU))Љ!ЋU))Ɖ!ыUщ)ʉ!Ѝ))ЋU!))Ή!u2U}M}u+MȋUċuE}MȉUĉuE}}MGI}EM9EuB~B}p^_]UWVlEU}Er0OUuMuU}ډuUD|Eĉ}ȋD |EE}M9ExHE}MGI}uM9uOEMtڋu}M EwyUU4G҉uMEMt&u}U~ VE}EUM4xvEUЋMBA@}ȋuĉEUЉM̋UMGFB9M}ȉuĉUM})EItU }MuЋM1ҋ}̊Uu11Ɋ}U1ҊuUUĊ 1ҊE)ƉUU!u)Ɖ8ȋU0)Ћu!)})!})Ɖ)ʉ!Ћ8)lj)Љ!)Ɖ,0)0!,)!,)Ǎ0)։!))!1ҍ9E}̋u MЋFuȊ1ɉU1Ҋ}U1ҊuUUĊ 1ҊE)ƉUU!u)Ɖ ȋU)Ћu!)})!})Ɖ)ʉ!Ћ )lj)Љ!)Ɖ)!)!)Ǎ0)։!)u )!1ҍ9MЉE܋F1ɉUuȋ}1Ҋ}U1ҊuUUĊ 1ҊE)ƉUU!u)ƉȋU)Ћu!)})!})Ɖ)ʉ!Ћ)lj)Љ!)Ɖ)!)!)Ǎ0)։!)u )!1ҍ9}̉E؋MЋF 1ɉx1Ҋt}u1ҊtpUĊ 1ҊExl)Ƌx!t)Ɖȋp)Ћp!)l)!l)Ɖ)ʉ!Ћ)lj)Љ!)Ƌ)!)Ɖ!))׉!))!э<1u}ԉD9u+}U u1ɋ}̊1Ҋ}ȉd1Ҋ}`UĊ 1Ҋd)\!ljd)`)Ƌ`!)\)!\)lj)ʉ!Ћ)Ɖ)Љ!)lj)!)!)lj)!)E)!ыU)Ѝ<1M!TU؋Eԋu))P}!u)΋)!lj)Nj)!‹T)׋)‰!‰)ыP)׉!׋P׉ʉ)!׋))Љ!))Ή!֋Uu܋}؉uEԋu}F}E؋uDMԉU9EuMFAu}̋EȋUċuMЋMG@BF}9MEȉUĉuSD^_]ÍvUWVHEU}r0O}uMMuԉUމu؋E؋ }EЉM9}O}M̋uANM̋Eu9EYEMtڋE űU8҉uMuUuȉMĉU&Uu1}Њ1ɉEu1E}Ԋ MM%ƊU%)ʉljыE!ыU)ʉUEE)‹M!Љ))ʉ!))!ЋU)lj)Љ!ЋU))!׋U׉)!)8)׋U!))Ή!}uȉMuMM U܃U܋M̋uANM̋Eu9E}B~uJH^_]Ë}H^_]ÍvUWVEMy0uAqtQX}EUFV >E̋NUċFU}ЉMȉE9U|>vuMFAuEЋUȋ}ċuM̋M@BGFE9MUȉ}ĉu^MU)ыEItuM} EЍqủU BEȉMvMЋuAFŰEȋ}ĉMЋMuB@GAuỦEȉ}ĉM9uMu)EItU}M 4WUЉu1Mf} }MuMuUUuЋMFIuЋ}M9}EB~Bu<^_]É'UWVE(؋EE9?EEu]EEEEEE]EEuEE]EEcEEu]EEu]EEEuEEEu]EEEًUEAEEˍ;M]]]ЉUU UЋUЋUEu]EEu]EEu]EEu]EEOEOJt&;#Hv/~# K5X^_]Ít&'UWV8EU}r0OUuMM}u}މuU􋷜Eڋ U܋EM9E}HEt&M؋EAHM؋uE9uYEMtڋU }؋E2 u}uЋu[vEEu]EEEE ENEUыEUEu ]EEu]EEu]EEu]EE1E1,E EN[vMMM؋EAHM؋uE9uE@~Uԋ}8^_]}8^_]ÐU1WVUS[bL׉Uu UM9w-P~WRRPV|R~xWQme[^_]ÍvPNQURWQV|RFxPMQ8e[^_]UVS[·Mu Uw,PBPPPB|PBxPVQpe[^]ÍvPBPPPB|PBxPVQe[^]Í&UWVS[GUuz UDJ@I@HD)ω8>9,54Dž(d9(o uu Dž Dž ։t=11ҊM ӥpt plt l$$U$xtn jPRQQs}${ jPRtQVsxttDž,1x(Dž Dž0HD-v890(00 0|PDLHL~H(B,$瀀 ‹(က% (!‰% Nj(%(( Nj!%Nj   !% 0 !‹|  D!ʋWH$,G $ Љ $ Ё $$!$ !Ƌ%‰ % ׋!ω ȋ ȋ0!Nj|TH!DB Q ,(( %, ʋ,!ʉ Љʉҋ ы !‰ ʉ ꉅ ы !щက % !ʋ|0#DDr JH2JP!ΉzP0pPL!ʉL0LLH!HHL !l4!!Dt0DH DH8L90000 TH|(QD%,~Ɖ(( (瀀က !‰(% Ή((% ! ׋ က!׉ Ήɋ| ΋!0H!%Dw Q, Ћ ΁ !΋  ȋ!Ƌ %က ׉ !0 ȋ| !L!ϋHDA ,w , ρꉵ( ׁ怀,!щы  ΋ !  ցက  ! ɋ0 ʉ|L!ƋD!ȋ2zJH2zr!΋0p!׉T!‰|0!l !ʉL!ȉD0 QOo0 jU$R8@PPQU R< M$Z jE$PPRVQ< NPF jM$QPVxWR< }$ ju$VPWtRPk< y jj8JRGWlQ@< xtE|4AM򉵔‰$ (8)׉<$E 1ҊM <0L,ӥ,,,t LM$jPW$VRf$8DžXɋTp TXXЋ|% lj% NjD%!$%$ ҉$$ Ћ|$$!!NjL !ω0p< U }4E}L~B E1ɉd9vU DtU | t ‰9~򋵰9~;~ ;~ <9~ЋD9~DE T|E ;tT~;~;~;D~D9~ЋD 9~D ;d[MDžl`9luXEU4DžLы}4%UWVS[7UMuUEEuu <@ENjE l}ypl… 1Ҩt e[^_]Íy Q:EtӉ1ɍTȉTAƒ~pExUFF|F FFEE0Dž<x9<+EE؋<Eʋ<DEʋ<\x<9ɋt&}U ׋}MhUh]E!|E9hl9hMF|FɋW XOGLTPWF FFE E1;x~lEutEutEtDE\;xDEtEtEt2E E u_I3;p.DE/E.EEݝ@@DD<1;p}fE fE@EutEutEutm]m}u4A;p}[XTLȋPEtEtEtEt늃 UR1ҍe[^_]%UWVS[÷UMuUEEuu <@ENjEt}yxt… 1Ҩt e[^_]Íy QEtӉ1ɍTȉTAƒ~xE}UFF|FF F(EE-DžLE9L(vEE؋LEʋLD8EʋL\EL9ɋLD0EiEiEiڋLEiɋLD8EeɋLT\DžLE9L[ɋx9L8LD0EAE+E؋LEݝPLPTLNj}MG}4΃}U1ɋ}U;x|&ZݝPPTTA;x}6UEtݝPPTTA;x|ʋE1ɋ}E;x|&ZݝPPTTA;x}6UEtݝPPTTA;x|ʋtDžp}9pXE|vM|FFɋPx lhHPdUMxFF F(EEdDžLE9L_EE؋LEʋLED8EʋLE\ML9ɋLD0EcEcEcڋLEcɋLED8E_ɋLETV[-DžLE9L'ɋx9LoLD0EEE؋LEn؋LEȋMBBBB B(EE1;M~WEu_Eu_Eu_D8Euc\;MwD0EtEtEtEtD8EtT1;M~;xD0E+EEEϋE41;x||1EEEʋEʋMщLE؋LEȋEB;xlEыMыhdEQESESEOɋEʋMщLEKًLEȋEB;xKt&}M}MpUp]E!}E9pt9pUF|FɋGO\G LXG`FF F(E6E1;M~sEu{Eu{EtʋLD8EʋL\;MD0EtEtEt6EE `lV@;x D0E!E E EݝPLPT|41;x|;EulEulEul؋uA;x}_`L΋\XEtEtEtEt뒃 MQ1ҍe[^_]9UWVS[wUMuUEEuu <@ENjE E}Ey}… 1Ҩt e[^_]Íy QEtӉ1ɍTȉTAƒ~E1EFFTF FFE<E1DžPE9P,&EE؋PEʋPDEʋP\EP9ɋPDEiEiEiڋPEiɋPDEeɋPT\DžPE9P[ɋE9PPDEEE؋PEٝTPT MG4DžPUM}9P|'_ٝTPTU@P9}:PEtٝTPTU@P9|DžPUM}9P|'_ٝTPTU@P9}:PEtٝTPTU@P9|Ƌ}Dž|}9|MMvM}FFɋxPxtH xp}UxF FFEuE1;M~aEuiEuiEuiʋEDEumʋE\;MDEtEtEtEtɋEDEtɋETrQ;%1;Md;MDEEEEٝTETEBBB BBE5E1;u~WEu_Eu_Eu_DEuc\;uwDEtEtEtEtDEtT1;u~;uXDEgEQE;E ٝTTE41;U|~+EEEʋEʋMщPE؋PEEB;UxEMtpETEVEVERɋEʋMщPENًPEEB;UNMU<ыM}|U|]EU!9|EA}9|MF|FɋOldGW Oh`PF FFEqEc1;}XEEEٝTPTDEٝTPTT;}DEeEeEeEeٕT\ٕT:D);}"DE#E"E EٝTPT 1;u|DEu{Eu{Eu{ٝTETF;u}elPhd`EtEtEtEt냃 uV1ҍe[^_]-UWVS[Êu EuME}EMUM}EfUƉE 1fUč8mUmك(ك̞MQ$mە\m˃ك,ۅ\]E]κ?]m]E`ك0ɉd]]E]]m]݅`]m]tZ؍e[^_]Ë\}JF׍4}1;}tuUҍ Uux vMUtMEE u EEE EE ʋx E E E EE E E EE E E M\G;}&؍UPPR}W-e[^_]Ðt&\}JF׍4‹E1;}p}uUҍ Uu| vMt&UpMEE u EEE EE ʋ| E E E EE E E EE E E M\G;}&UWVS['u EV>FU}EfUƉE fUmUmكĞMQ$mUmU$$ʃ˃]]]]]tI؍e[^_]J}׋F}41;}tuUҍ Uu| vMUtMEE u EE E ʋ| E M EE M EE M\G;},؍UPPR}We[^_]J}׋F14E;}p}uUҍ Uux vMUpMEE u EE E ʋx E M EE M EE M\G;}, UWVS<[êM EyEqttn؍e[^_]}fUƶ fUm]mƋU׋ym]mM΍19}\A9|uPPVMQe[^_]}fUƶ fUm]mƋU׋ym]mM΍<19}t&ϋTT̉DA9|떐UWVS[çU EzH(U lp0H,)׋P8UU}ҋx4HPHHlEʃʃÉUɋM̉ű7ʋM ɃʋD4]EʍN]]͋U]Nʋu]E̍͋uNu]EI9uEI]EI EHɋMUÃEHUUMË EH UEHMEHEH EHEHEH M]E̋E ̋T]ʍωh]ʉ΋M]ʋE΋ ɋE̓]EȋhH΋Mɍ]EJ]EJEJ UBU9MbEHɋuM<EHEH E4EOM EOEO }EJEJEJ MĔ[^_]ËlU|8xXˍ`] hm]E]΍X]E] `]΍8͍h ɋM ɋDɍ UˍqIɋMENʍENݝpEܭpN ݝpEʋMIM9M<EHɋM|ÃEH|xMË EH xEHEHEH EHEHEH ˍX܍p̍̍8ˍ`]hm]E]ɍX]E]]mɍ]ɍ8͍`̍hϋE ϋT̍h̉‹MʋE EhMEHAɍJEJݝpEJ ܭpݝpɉMʋM9MEHɋuM<EHEH E4EO EOEO }EJEJEJ ܍p'UWVSt[ÚMUUEq,AكĞكÉ0ݝhݝ`ككݝXݝP]t&}݅pOp݅p݅pG`݅pOhG ܍pGPOXGHO@G8܍pG܍pG(܍pG0܍pG܍pG]EMMME܅h]̋u܍XU0݅p̅NpEF`]݅`܍P݅pM܍XM݅pENXN@]EFF8M܍pݕ@܍p]M݅pNhF FPFݝxEFH܍p܍pF0M܍pFF(M܍pM݅`܍XE܍P܅hM܍XMEME uEU0}fML H4fMt&Em]mE}M8܅xT}]-Ĭ[^_]؁Ĭ[^_]ÍvUWVS[Wك̞E },GM4PE}(UGE]T@`@pݝ@(ݝpݝ@H@hݝݝ@0ݝݝx@P@Xݝݝ@@ݝ0ݝ@8@ݝhݝ@@كĞݝ8ݝP@ كݝكݝHكݝ@Bp]ݝ`B`݅ݝBHݝB(ݝݝBhݝpBPݝB0ݝhBݝ BXݝB8ݝXBݝB@ݝ(BݝB ݝU܍HɉHU܍H]ݝxt&݅p݅݅݅8M݅M܅M܅M܅hM܅MMM܅܅x܅MMM܅M܅0M܅M݅܍H܅PEM]E݅܍@MME]EʋU0EME݅(E݅pM݅`]EMMݝXE݅M܅M]M݅܅܅XM܅MM܅hM܅M܅݅MM܅ M܅܍HEMM܅݅܍@EMEM܅x܅P݅xM݅xM܅xME}ȋuE0}fMƉ dLfMmUmƋTEE͋UmUmɉЉ)u؉!!P)!ȋM !!]<E r܅X΋zEɋR ̉U΋d!ƒ# ]ɉ rMzʉJ ƋddL,EuU8܅PHu] Ĭ[^_]؁Ĭ[^_]UWVS[gك̞E M,U(ABE4Eݝp 2كЉݝPك]ݝHكݝ@كݝ8كݝ0كݝ(}݅p܏݅p݅p܇݅p݅p܍pG(܏Gx܏܇܍pGhOpGXOPGH܍pG ܍pG0܍pG`܍pG@܍p܍pG܍pݝEGG8܍pMGMMM܅܅Pݝŋu݅p܎܍8܆܍p݅@Fx]݅H܍(܍0܍pEFXM݅@܍pMF0܍pME]܍(܍8EF(݅pMNPF M]܍8EMݕ݅p]M݅p܎݅p܎FhNpݝxE܆FH܍p܍pF`܍pF@MF܍p܍pF8FM܍pFMM܅Pݝh݅H̋U0݅@ʅ܍0܍(܍8EM݅@MM܅hݝh܍(܍8EM܍8Mݝ`EME/UuE0}fM\ $fM݅mUm EɋUmUmɉݝEEu)]EE!!܅hɋ]E܅x)!M !!]݅x܅<z2ݝx݅h܅`ݝh݅`ݝ`$\!ƒ! ‰z2$$݅pMu8܅PMݝp0[^_]؁[^_]ÉUWVS[נك̞E M,U(ABE4Eݝp LكĞكݝ0ݝ(ككÉݝPݝHككݝ@ݝ8]}݅p܏݅p݅p܇݅p݅p܍pG(܏Gx܏܇܍pGhOpGXOPGH܍pG ܍pG0܍pG`܍pG@܍p܍pG܍pݝEGG8܍pMGMMM܅܅0ݝŋu݅p܎܍H܆܍p݅PFx]݅(܍8܍@܍pEM݅PM܍H܍8MEF(FX]E݅pNPMF ܍pF0M܍p]܍HEMݕ݅p]M݅p܎݅p܎FhNpݝxE܆FH܍p܍pF`܍pF@MF܍p܍pF8FM܍pFMM܅0ݝh݅(̋U0݅P̅܍@܍8܍HEM݅PM܍H܍8M܅hݝhE܍HMMݝ`EME;UuE0}fM\ $fM݅E ݅m]mݝEEɋUmUmɉЉ)]EEu؉!]E܅x!)܅h!]݅x܅M !!ݝx݅h܅`<z2Bݝh$\!ƒ! ݅`͉z$2̓$ݝ`Z݅pMu8܅0Mݝp[^_]؁[^_]Ð&UWVS['ك̞E M,AE4M(AEݝp:كЉ ݝPك]ݝHكݝ@كݝ8كݝ0كݝX}݅p܏݅p݅p܇݅p݅p܍pG(܏Gx܏܇܍pGhOpGXOPGH܍pG ܍pG0܍pG`܍pG@܍p܍pG܍pݝEGG8܍pMGMMM܅܅Pݝŋu݅p܎܍8܆܍p݅@Fx]݅H܍X܍0܍pEFXM݅@܍pMF0܍pME]܍X܍8EF(݅pMNPF M]܍8EMݕ ݅p]M݅p܎݅p܎FhNpݝxE܆FH܍p܍pF`܍pF@MF܍p܍pF8FM܍pFMM܅Pݝh݅H̋U0݅@ʅ܍0܍X܍8EM݅@MM܅hݝh܍X܍8EM܍8Mݝ`EME7UE},fM( }0fM݅mUmEɋMmUmɉݝEE+ȋu]EE!Љ I!]E܅xɉ܅h+!ЋU ]݅x܅ !!ݝx݅h܅` (,!ƃݝh݅`! ƋQʋݝ`VAQ V FAQVF(O(݅p}M8܅P }ݝp([^_]؁[^_]Ív'UWVS[Çك̞E M,U(ABE4Eݝp \كĞكݝ(ݝXككÉݝPݝHككݝ@ݝ8]}݅p܏݅p݅p܇݅p݅p܍pG(܏Gx܏܇܍pGhOpGXOPGH܍pG ܍pG0܍pG`܍pG@܍p܍pG܍pݝEGG8܍pMGMMM܅܅(ݝ]݅Xˋu݅P܍@܍8݅p܍HE܎M݅P܆M܍pFxM܍pEFX]܍8܍HEF(݅pMNP܍pF MF0܍H]E܍pMݕ݅p]M݅p܎݅p܎FhNpݝxE܆FH܍p܍pF`܍pF@MF܍p܍pF8FM܍pFMM܅(ݝh݅X̋U0݅P΅܍@܍8܍HEM݅PMM܅hݝh܍8܍HEM܍HMݝ`EMECUuE0}fM4 $fM݅E ݅m]mݝEEɋUmUmɉЉ)]EEu؉!]E܅x!)܅h!]݅x܅M !!ݝx݅h܅`<z2BBBݝh$4!ƒ! ݅`Ήz$2΃ $ݝ`ZZZ݅pMu8܅(Mݝp[^_]؁[^_]ÐUWVS[njك̞E M,AE4HM(AE]LكĞ]كɉD]ك]كݝxكݝpكݝhE}EH0}EH8E@H@ݝXEHX@EMEHH܅XHP@@(ݝXEHx@ MM܅XݝXE܈MMM܅XݝXE܈MMMM܅XݝXEH`ME܈MME܈MMMEHhME܈MMEHpM܅XݝX܅XݝX܅XݝX܅XݝX܅XEݝX܍xE]E܍h܍pE]EE]܍xE]܍hE]]E]E]܍x]E]ɋuݝ`EN8E]EN0FN@EFNHݝPENXFEMF(NP܅PF ݝPENxMM܅PݝPE܎MMM܅PݝPE܎MMMM܅PݝPEN`ME܎MME܎MMMENhME܎MMENpM܅PݝP΋U0܅PݝP܅PݝP܅PݝP܅PEݝP܍xE]E܍h܍pE]EE]܍xE]܍hE]]E]܍xE]U}fMU }0fM݅Xm]m݅PMm]m+Lȋu!Љ!Љ+H!ЋU !!݅XE uU܊ !ݝXEEȃ! ]EE]EE]E܅`]݅PEݝPEE]E] uFOu.E}M8ED}]y؁Ĵ[^_]؁Ĵ[^_]Ð&UWVS[Ç} u(EGM,+GU4 E8 E E ك̞AF@D] كĞ]ك‰<]ك]كݝxكݝpكݝht&MEI0EI8EAI@ݝPEIXAEMEIH܅PIPAA(ݝPEIxA MM܅PݝPE܉MMM܅PݝPE܉MMMM܅PݝPEI`ME܉MME܉MMMEIhME܉MMEIpM܅PݝP܅PݝP܅PݝP܅PݝP܅PEݝP܍xE]E܍h܍pEE]E]E]E]܍x܍h]E]E]܍x]E]ɋUݝXEJ8E]EJ0BJ@EBJHݝHEJXBEMB(JP܅HB ݝHEJxMM܅HݝHE܊MMM܅HݝHE܊MMMM܅HݝHEJ`ME܊MME܊MMMEJhME܊MMEJpM܅HݝH΋U0܅HݝH܅HݝH܅HݝH܅HEݝH܍xE]E܍h܍pEE]E]E]E]܍x܍h]E]܍xE]uE}ufU扅d }0fU݅Pm]m݅HMm]m+Dȋu!Љ!Љ+@!ЋU !!݅PE uQݝPEEȈUߋd!ƒ!]EE ]EE]E܅X]݅HEݝHEE]E]M߈JdOdE}u8E<}]{7 }8WR}0WQV\$$uVMQU REP e[^_]؍e[^_]Ð&UWVS[~ك̞E u(E,U4@LvE]HLكĞ]كɉD]ك]ك]كݝxكݝp}uEO0uEO8EGO@ݝXEOXGEMEOH܅XOPGG(ݝXEOxG MM܅XݝXE܏MMM܅XݝXE܏MMMM܅XݝXEO`ME܏MME܏MMMEOhME܏MMEOpM܅XݝX܅XݝX܅XݝX܅XݝX܅XEݝXME]E܍p܍xE]EE]ME]܍pE]]E]E]M]E]ɋMݝ`EI8E]EI0AI@EAIHݝPEIXAEMA(IP܅PA ݝPEIxMM܅PݝPE܉MMM܅PݝPE܉MMMM܅PݝPEI`ME܉MME܉MMMEIhME܉MMEIpM܅PݝP΋U0܅PݝP܅PݝP܅PݝP܅PEݝPME]E܍p܍xE]EE]ME]܍pE]]E]ME]M}fU才l }0fU݅Xm]m݅Pum]mv+LBu!ȉ!ȉ+H!ȋM !!l ?JRUދU!ƒ! Š?JEވB݅XEݝXEE]EE]EEE؃OE]E܅`]݅PEݝPEE]E]E}U8ED}]`؁ļ[^_]؁ļ[^_] UWVS[çv} u(EGM,+GU4 E8 E E ك̞AFHL]كĞ]ك‰D]ك]ك]كݝxكݝp&EMEH0MEH8E@H@ݝXEHX@EMEHH܅XHP@@(ݝXEHx@ MM܅XݝXE܈MMM܅XݝXE܈MMMM܅XݝXEH`ME܈MME܈MMMEHhME܈MMEHpM܅XݝX܅XݝX܅XݝX܅XݝX܅XEݝXME]E܍p܍xEE]E]E]E]M܍p]E]E]M]E]ɋUݝ`EJ8E]EJ0BJ@EBJHݝPEJXBEMB(JP܅PB ݝPEJxMM܅PݝPE܊MMM܅PݝPE܊MMMM܅PݝPEJ`ME܊MME܊MMMEJhME܊MMEJpM܅PݝP΋U0܅PݝP܅PݝP܅PݝP܅PEݝPME]E܍p܍xEE]E]E]E]M܍p]E]ME]-u}fU扵l }0fU t&݅Xm]m݅PUm]m+LЋu!ȉ!ȉ+H!ȋM !!݅XE ?JMߊJMފJUݝXEEȈM݋l!ƒ!]EE ]EE]E܅`?]݅PEݝPEE]E] E߈BMވJE݈BE؃OEE}U8ED}]l7 }8WR}0WQV\$$URMQE PuV e[^_]؍e[^_]É'UWVS<[o},M4GEU}(Gك̞E E‰7 @0ݝ@XݝݝX@x݀ݝXݝ݀@8ݝݝ@@`ݝݝH݀݀ݝ8ݝ@@@ݝݝ@h݀ݝݝ@H@ݝݝH@p@Pݝ@ ݝhݝ(كĞ@(ݝpكݝݝ@ككݝ8كݝ(كݝ B0ݝݝBXݝPBxݝP݂ݝ݂ݝB8ݝBݝB`݅ݝ0݂ݝ0݂UݝB@܍@ݝBUݝBh܍8ݝ݂ݝBHݝxBݝ@BpݝBPݝ`B ݝ B(ݝE܍ ]]܍ݕx܍@ݝp݅x݅p܍ ܍8ݝhݝ`݅ʋM݅݅‰M݅X݅݅܅܅݅hݝ݅X܅H܅(܅ݝ݅܅ݝ݅܅ݝ݅H݅8݅݅݅݅܅ݝ܅ݝ܅ݝ܅ݝ݅܅܅pݝ݅܍8݅@]E܍ ݅܍(EE]EE]܍8EE]EE]]E]EE]EE]EE݅݅]EE]݅E܅ݝ]݅܅݅xݝ݅P܅@݅`܅܅ ݝ݅P܅ݝ݅܅ݝ݅܅ݝ݅0݅0݅݅݅݅܅ݝ͋U0݅Ʌ܅ݝ݅x܍8܅ݝ܅ݝ݅܅܅pݝ݅@܍ ݅܍(݅x݅p܍8݅h݅x݅p݅`]܅`E]݅pE]܅`݅`u}fU։ }0fU݅m]m݅Mm]m։݅E+ȋuݝEE!Љ!Љ]EE+!ЋU ]EE!! ]E܅U!ƒ!f1 ]݅ݝE̋Ẽf2OEE](ɋ}M8܅p}t L؁<[^_]Ít&'UWVS<[weu UM}(F+F E8 E EE h u,GFE4ك̞y B0ݝBXݝ(ݝBx݂ݝ ݝ8݂B8ݝݝBB`ݝ@ݝh݂݂ݝݝB@BݝݝBh݂ݝXݝXBHBݝݝBpBPݝHB ݝݝPكĞB(ݝكݝ0ݝككݝكݝxكݝpA0ݝݝAXݝAxݝ݁ݝ(݁ݝA8ݝAݝ0A`݅0ݝ`݁ݝ݁UݝA@܍ݝAUݝAh܍ݝP݁ݝHAHݝAݝApݝ8APݝA ݝ@A(ݝ E܍p]]܍ U4ݕx܍ݝp݅x݅p܍p܍ݝhݝ`݅ɋE݅(݅‰E݅݅݅܅@܅݅ݝ݅ ܅܅P܅ݝ݅8܅ݝ݅܅ݝ݅h݅݅݅X݅X݅H܅ݝ܅ݝ܅ݝ܅ݝ݅0܅܅ݝ݅0܍݅]E܍p݅0܍xEE]EE]܍EE]EE]]E]EE]EE]EE݅݅]EE]݅E܅0ݝ]݅܅݅ݝ݅܅݅܅܅@ݝ݅܅ݝ݅(܅ݝ݅܅ݝ݅`݅݅݅P݅H݅8܅ݝ΋M0݅ Ʌ܅ݝ݅x܍܅ݝ܅ݝ݅ ܅܅ݝ݅܍p݅ ܍x݅x݅p܍݅h݅x݅p݅`]܅`E]݅pE]܅`݅`}}fU։ }0fU ݅m]m݅Mm]m։݅E+ȋuݝEE!!Љ]EE+!ЋU ]EE!! ]E܅U!ƒ! f]݅fIݝE̋ũfOuEfJ]؋uM8܅uB7 u8Vu4Vu0VE,PW\$$QRU REPe[^_]؍e[^_]Ðt&UWVS<[Ç[},M4GEU}(Gك̞E E‰U @0ݝ0@Xݝݝ8@x݀ݝݝ@݀@8ݝݝ@@`ݝݝ(݀݀ݝݝ @@@ݝݝ@h݀ݝݝp@H@ݝXݝ@p@Pݝ@ ݝHݝكĞ@(ݝكݝPݝككݝxكݝhكݝ`B0ݝ(ݝBXݝ0Bxݝ݂ݝ8݂ݝB8ݝBݝB`݅Pݝ ݂ݝ݂UݝB@܍ݝBUݝBh܍xݝ݂ݝXBHݝPBݝBpݝBPݝ@B ݝB(ݝHE܍`]]܍HU܍ݝxE݅x܍`܍xݝpݝh݅0݅݅݅ݝ݅8܅܅݅X܅݅H܅ݝ݅܅܅ݝ݅@܅ݝ݅܅ݝ݅(݅݅ ݅݅p݅܅ݝ܅ݝ܅ݝ܅ݝ݅P܅܅ݝ݅P܍x݅]E܍`݅P܍hEE]EE]܍xEE]EE]]E]EE]EE]EE݅(݅]EE]݅E܅ݝ]݅܅݅Pݝ݅0܅݅@܅܅ݝ݅܅ݝ݅8܅ݝ݅܅ݝ݅ ݅݅݅݅X݅܅ݝ͋U0݅HɅ܅ݝE܍x܅ݝ܅ݝ݅H܅܅ݝ݅܍`݅H܍hE݅x܍x݅pE݅x݅h]܅hE]݅xE]܅h݅hu؋E}։dfM։ }0fM݅m]m݅Mm]m։݅E+ȋuݝEE!Љ I!]EE+!ЋU ]EE!!]E܅ d!ƒ]݅! f1EݝE]f2fqfrfqfrOɋ}U8܅}t .؁<[^_]Í'UWVS<[Qu UM}(F+F E8 E EE ~ u,GFك̞u4 B0ݝBXݝ@ݝBx݂ݝ0ݝ8݂B8ݝݝBB`ݝhݝ݂݂ݝݝB@BݝݝBh݂ݝݝXBHBݝݝBpBPݝpB ݝݝPكĞB(ݝPكݝ0ݝHككݝ8كݝ(كݝA0ݝݝ AXݝAxݝ݁ݝ(݁ݝA8ݝAݝXA`݅0ݝ݁ݝ݁UݝA@܍HݝAUݝAh܍8ݝx݁ݝHAHݝAݝApݝ`APݝA ݝ@A(ݝ E܍]]܍ U4ݕx܍Hݝp݅x݅p܍܍8ݝhݝ`݅ɋE݅@݅‰E݅݅݅܅h܅݅ݝ݅0܅܅P܅ݝ݅8܅ݝ݅܅ݝ݅݅݅݅݅X݅p܅ݝ܅ݝ܅ݝ܅ݝ݅0܅܅Pݝ݅0܍8݅H]E܍݅0܍(EE]EE]܍8EE]EE]]E]EE]EE]EE݅݅]EE]݅E܅Xݝ]݅ ܅݅ݝ݅܅݅܅܅@ݝ݅܅ݝ݅(܅ݝ݅܅ݝ݅݅݅݅x݅H݅`܅ݝ΋M0݅ Ʌ܅ݝ݅x܍8܅ݝ܅ݝ݅ ܅܅Pݝ݅H܍݅ ܍(݅x݅p܍8݅h݅x݅p݅`]܅`E]݅pE]܅`݅`u؋}0}։fU։ fU݅m]m֋݅Mm]m։݅Eʋu)؉!ݝEE!]EE)!ЋU !!]EE؁L[^_]Í&UWVSL[>},M4GEU}(Gك̞E E‰Q @0ݝ@XݝݝP@x݀ݝHݝ݀@8ݝݝ@@`ݝݝ݀݀ݝ(ݝ@@@ݝݝ@h݀ݝݝ@H@ݝpݝ8@p@Pݝ@ ݝ`ݝكĞ@(ݝ@كݝݝ8ككݝ0كݝ(كݝ B0ݝݝBXݝHBxݝ@݂ݝ݂ݝB8ݝBݝB`݅ݝ݂ݝ ݂UݝB@܍8ݝxBUݝBh܍0ݝ݂ݝBHݝhBݝ0BpݝBPݝXB ݝB(ݝE܍ ݝxݝp܍ݕh܍8ݝ`݅h݅`܍ ܍0ݝXݝP݅ʋM݅݅‰M݅P݅݅p܅܅݅`ݝ݅H܅8܅܅ݝ݅܅ݝ݅܅ݝ݅݅(݅݅݅݅܅ݝ܅ݝ܅ݝ܅ݝ݅܅܅@ݝ݅܍0݅8]E܍ ݅܍(EE]EE]܍0݅xE]EE]]E]EE]E܅p]݅p܅p݅݅]E݅p]݅xE܅ݝ]݅܅݅hݝ݅H܅0݅X܅܅ݝ݅@܅ݝ݅܅ݝ݅܅ݝ݅݅ ݅݅݅݅܅ݝ͋U0݅Ʌ܅ݝ݅h܍0܅ݝ܅ݝ݅܅܅@ݝ݅8܍ ݅܍(݅h݅`܍0݅X݅h݅`݅P]܅PE]݅`E]܅P݅Pu}fUƉ }0fU݅m]m݅Mm]mƉ݅E+ȋuݝEE!!Љ]EE+!ЋU ]EE!! ]E܅U!ƒ! ‹]݅ċIݝE̋uOuEˉJ]#ɋ}M8܅@}t 8؁L[^_]ÐUWVSL[W4},M4GEU}(Gك̞E E‰f @0ݝ(@Xݝݝ0@x݀ݝݝ8݀@8ݝݝ@@`ݝݝ ݀݀ݝxݝ@@@ݝݝ@h݀ݝݝX@H@ݝPݝ@p@Pݝ@ ݝ@ݝhكĞ@(ݝكݝHݝككݝكݝكݝB0ݝ ݝBXݝ(Bxݝ݂ݝ0݂ݝB8ݝBݝB`݅Hݝ݂ݝp݂UݝB@܍ݝBUݝBh܍ݝ݂ݝPBHݝHBݝBpݝBPݝ8B ݝ`B(ݝ@E܍]ݝx܍@ݕp܍ݝh݅p݅h܍܍ݝ`ݝX݅(݅݅݅ݝ݅0܅܅݅P܅݅@܅ݝ݅܅h܅ݝ݅8܅ݝ݅܅ݝ݅ ݅x݅݅݅X݅܅ݝ܅ݝ܅ݝ܅ݝ݅H܅܅ݝ݅H܍݅]E܍݅H܍EE]EE]܍EE]EE]]E]EE]E܅x]݅x܅x݅ ݅]E݅x]݅E܅ݝ]݅܅݅Hݝ݅(܅݅8܅܅`ݝ݅܅ݝ݅0܅ݝ݅܅ݝ݅݅p݅݅݅P݅܅ݝ͋U0݅@Ʌ܅ݝ݅p܍܅ݝ܅ݝ݅@܅܅ݝ݅܍݅@܍݅p݅h܍݅`݅p݅h݅X]܅XE]݅hE]܅X݅XuȋE}Ɖ fMƉ }0fM݅m]m݅Mm]mƉ݅E+ȋuݝEE!Љ I!]EE+!ЋU ]EE!!]E܅ !ƒ]݅! ‹1EݝE]2qrqr O ɋ}E8܅}t ؁L[^_]ÍUWVS\[Ç*},M4GEU}(Gك̞E E‰ @0ݝ@Xݝݝ(@x݀ݝHݝ݀@8ݝhݝx@@`ݝݝ݀݀ݝ ݝ@@@ݝXݝ@h݀ݝݝ@H@ݝHݝ8@p@Pݝ@ ݝ8ݝكĞ@(ݝ0كݝݝككݝكݝكݝB0ݝݝBXݝ Bxݝ@݂ݝ݂ݝ`B8ݝpBݝB`݅ݝ݂ݝ݂UݝB@܍ݝPBUݝBh܍ݝ݂ݝBHݝ@Bݝ(BpݝBPݝ0B ݝB(ݝE܍ݝxݝp܍ݕh܍ݝ`݅h݅`܍܍ݝXݝP݅݅݅x݅Xݝ݅(܅܅݅H܅݅8܅8ݝ݅H܅܅ݝ݅܅ݝ݅h܅ݝ݅݅ ݅݅݅݅܅ݝ܅ݝ܅ݝ܅ݝ݅܅܅0ݝ݅܍݅]E܍݅܍EE]EE]܍݅xE]EE]]E]EE]E܅p]݅p܅p݅݅p]E݅p]݅PE܅ݝ]݅܅݅@ݝ݅ ܅(݅0܅܅ݝ݅@܅ݝ݅܅ݝ݅`܅ݝ݅݅݅݅݅݅܅ݝ͋U0݅Ʌ܅ݝ݅h܍܅ݝ܅ݝ݅܅܅0ݝ݅܍݅܍݅h݅`܍݅X݅h݅`݅P]܅PE]݅`E]܅P݅P/}ȋuE0}fMƉ fM݅m]mƋ݅Um]mƉ݅Eыu)؉!ݝEE!]EE)!ȋM !!]EE< r]E܅zR U]!ƒ݅#E ݝ rEMzˉJ ʋ˃]ɋEu8܅0Et ؁\[^_]ÐUWVS[Ú u M EAtV8UEك0ك49E݃Xك<} EW< 1;U}huu؋EuB;U}AuE<}4tu>1}>uB;U|؋}MG}9Mr؃[^_]É}fE fEm]mE4뤍&'UWVS[zE Eك8PtH8MU9UP}f9׃!׋Uf>}f9׃!׋Uf>֍<EԋMM9E~E9E}5M0U$҉M܉U؉U)fƒ!‹EfNjUIu}uE,M MЍGNU׉EuMFu9M(^_]ÉUWVEU9U}M(U 4} FuEMPUE~eUu,UU u҉U}~'Ƌ}$E0UEt&ffEJuMuU}M܉MuMEuuEFu9EW^_]Ðt&UWVEU9UU}(E }4uMUE~YU uuU荴&}~}$U1E0JuMEuU,MMuMEuu}Fu9}e^_]ÍvUWVEU9U}M(U 4} FuEMUE~dUu,UU uU}~%Ƌ}$E0UEvEJuMuU}M܉MuMEuuEFu9EX^_]ÍUWVEU9U}M(U 4} FuEMUE~iUu,UU uU}~*Ƌ}$E0UEvf%EJuMuU}M܉MuMEuuEFu9ES^_]ÐUWVEU9U}M(U 4} uEMUE~cUu,U܋U uU}~#Ƌ}$E0UEEJuMuU}M܉MuMEuuEFu9EY^_]ÐUWVEU9UME(U u<x}U<ME~guu}~7M0ɉ։MM !ЈU֋U$Iu؋EM,} 4HEuMEEu}UG}9UY^_]Í&UWVEU9UME(U u<}U<ME~pM,uuM}~9։NjU0MU !ЈU֋U$IuًuE M}MuMUEuMUAM9UP^_]Í&'UWVS[ÊEU9Uك(ك$}M(U  U4}u <UM}}܋E~L}0UE$Jt*tu\E$Juېt&؋MU uыE,M܍NjU0E$UEM) !fU֋UIuڋEu,U MT$+مE*%UWVS[nE UكĞHx xJuHJP҉|Lݝ`|x t04pσ 8~ك݅`Ƀݝ`؉R$ܽ`E}Džl9lݝ@ݝ@ݝݝGݝGݝ|$lGl9ll)EItɋl84 1ҋ4<$9}-1x׋$tB9|1;4}jt&xt݅݅ L݅L݅݅ L݅L@;4|֋0,݃Xك<ɋ40(ݝ@ٝ<\,ɋ 1ݝ;\م<݅@ݝPp݅܍܍܍Eu݅PEG؋;\cx݅DЋt܍ݝDɋ܍܍΋pݝ݅܍݅݅݅܍܍TEu݅PEٽf f٭٭ R||ٽf f٭٭;\;4݅܍݅@م<9ٽf f٭٭B;4܍x݅Ћt܍܍ɋp܍EuEVB;4t؋,(F=E1ɊK1҉M>11HDžL1U?UĉỦU1҉M=MEMEEMEEEȉUԉ% U ȋL 0 ЋTE щ F~hDž4tJ4q0P8x‰XT\D)dU<}L#h1Ʌy1t&XTu1Ҋ19<}Ah9|ˋhtwX1U1T1144E4!Ƌ4$E$!1$$ 9}  @9|DG8xl z\lj80\ uV躇M;t Q裇1e[^_]ظe[^_]6uV?;UB1҈>9tuN=+UB1҈=9 u܉uEM1T؋D ‰L؉ ʋL؋E ʉF~S\ELh1yKXT11Ɋ 7ƃ ȁM ֋ЋUGh9|h@0XT11Ɋ7 Ɖʃ ȋM ֋u‹4! 4# 1;@@;@|DPhW\QEP輇 jPVMQXR8WRjPVMQTR8xWR 7XTu1Ҋ19<}Ah9|vXT11Ɋ7 Ɖʃ ȋU UȉGh9|Yh UR膄UWVS[|LMU كByB y ByByB$zqƒ9 Шu9N Ȩtظe[^_]ۅɋuۅʋكĞɋƉEٽfɵ f}٭۝٭٭۝٭)9~;~B= ׋ υ5|LLЉ jRQVWOj׋QVPWSOjQVRW+OjVRPWOǃ 9f>f%f9fщƁPf HdPfA%ddfA%ddfAd%PfP`fP`f^%‹Pf&ɋ}E F@@B@YR;u|ZY^_] UWVPPu~ VNF F1E}9| D&ɋ}E F@@ @B@@(YR;u|ZY^_]É'UWVPPu~ VNF F1E}9| J&ɋ}E F@@ @0@B@@(@8YR;u|ZY^_]ÍvUWVPPu~ VNF F1E}9| F&ɋ}E F@BYR@BYR;u|ZY^_]Í&UWVPPu~ VNF F1E}9| O&ɋ}E F@@B@ YR@B@(YR;u|ZY^_]Í&'UWVPPu~ VNF F1E}9| X&ɋ}E F@@0@B@ @8YR@B@(@@YR;u|ZY^_]Ðt&UWVPPu~ VNF F1E}9| a&ɋ}E F@@0@H@B@ @8@PYR@B@(@@@XYR;u|ZY^_]ÍUWVPPu~ VNF F1E}9| T&ɋ}E F@BYR@BYR@BY R ;u|ZY^_]É'UWVPPu~ VNF F1E}9| `&ɋ}E F@ @B@(YR@B@0YR@B@8Y R ;u|ZY^_]Í'UWVPPu~ VNF F1E}9| l&ɋ}E F@ @@@B@(@HYR@B@0@PYR@B@8@XY R ;u|ZY^_]ÐUWVPPu~ VNF F1E}9| x&ɋ}E F@ @@@`@B@(@H@hYR@B@0@P@pYR@B@8@X@xY R ;u|ZY^_]ÐUWVS EU xp JplxpLBhtz BJ[,HDžxEE\كĞ04ك̞ۅtݝ`E ܍`ݕPW$ٽfɶ Hf٭۝$٭ŋ$$) EʋE ٭۝٭٭۝٭);p~pl9~1&٭ە٭0݅`٭۝|٭}ۅىuEu Bۅ|݅`Eu |A|t9~9|~|ݝXW$ɋh٭۝٭ƍw hh~hwp =M@U1҉8|Q$ɋh9,]}Sٽf f݅X٭ە٭,ݝXB9|؍$1ٔ@~݅1U9}@@9|؅yt)ʋ,ttT9~ ,)‰ThDžTT0|) hD|1쉽t싽9XU t&D9v0)щ8|<~$ Q0RP8Q T0;|<(Wv R0QR8Q(T0;| }|)~g؍e[^_]ك Pݝ^݅tكĞ4v1;|QDžxPPEEEm]mU܃逋E!ƒ#E ‹EFu;}EEEuEAEuEAEuEAEuEA EuEA(EuEA0EuE]A8E6UɋuEm]mE!ƒ!u ‹Eu%&EEM)EE8֋UDEu@uDEu EDEuuD EuED(EuuD0EuuD8EE уU!ʃ!E ʋMЈAM;}}T1EEu GI;}|U܋}!A!} u!ƒ"7 ˆ7UEu‹}M UMm؃t[^_]1E шL>/ E}!Ѓ!׉Eԉ}ЋMԋEЋ} MԋMUԈ9 E}!Ѓ!׉Ẻ}ȋŰEȋ} ‰ŨU"D9! ˆT9| UWVS[×M$E UEUEw AEuDDAEu""AEuM(E Dž4u(FEu4DD4M(AEu4 ""4u(FEu44EuEOM,E}4U܉}UE؉Du M,EEFFFEE,Uu)‰U9u~u}EE}9}M̋U,ʾ})ыE ủ}Eu)} lj}ЋM̋uDEu)} lj}ЋM̋uDEu)} lj}ЋM̋uDEu)ыU UЋM̋uM9u$U9U}l},EߍOɺ} ׋ủ}ԉ0}EuuЋ0 ƉuˋŰ}ݝ8BIU9}݅8|ɋEЋMUЋ}ЋU!!‰}ЊMԋuЃ }ԋu!" ʈEU9UكԢ}ك]]ككp]]كك]ٝ|كPكĞɋ}̋ufU ٝxٝt fUiUɋE؋}EEEEm]mu!!ƋE GLA@ݕPݕXݕ`ݕhEuEAHEu EݝhAPEuEAXEu Eݝ`A`Eu EݝXAhEu م|ݝPApEu مxAxE{܅h܅`܅X܅Pm]mU܋E؃逋}!!NjE LGỦ}ȃ}U9}AEEEEEEEEEuEAEuEAEuEAEuE]A EuE]A(Eu م|]A0Eu مx]A8EU܋EEEEE؅tm]m}!!NjE }܅h܅`܅X܅P؅tt&u9u!ŰME E̋}䉕0DEu @0űUDEu0 0E̋}DEu00ű}D Eu00EUD(Eu00Eű0D0EuűE}D8uE% ЋU!ƒ!} ˆ7GM̉}ȃM̋U9U}̋MEDžHuF;uEMDEuH@H}̃;}xE̋uDEuH HŨ;UGű}DEuHHŨ;UE}D EuHHM̃;ME}D(EuHHũ;uEMD0EuHHE̋})ǹ)HuHHU!!ʉHuH }!ƒ 7u! ˆ7UM}ʋED4ljUu[^_]cDžH.1Dž41UWVM U}u M$]EɋM(]E]E~aMUMMM1;U|-&B;U}UMEtɉ ɋEUMt؃^_]Ð&UWVE U$M(}u ]E]E]@E]BE]AEɋEU]EEMEMMEM1;U|T&\ ;U}@UMEu U͋MDEuɉ TˋMUMt؃^_]Ít&'UWV(E U$M(uuЋ} ]E]]@E]BE]A]@E]BE]AM]IEUEuMEuE؉M1;U| rE t\;U}\UʋEЋMUuEuMEDEuuEDEt\ tʋMЋuU׉MMt c؃(^_]Ít&'UWV0E U$M(}u ]]]@]B]A]@]BE]A]@E]BE]ԋUAUȋU]̅MM1;U|#Eʉ \\\ ;U}rEMEEEuMDEEuEDEuED Et\\\ ;U|MЋUE<4O؃0^_]Í'UWVS|[zM E uEM$U]uEEE}(EEMUM&U,1EDž|u,|M)|9|~|1E;|}EU,ڍJ&U ‰xEUEux GI;||ʋM؋U!!֊U u!u" шE;}كԢ}ك]]ككp]]كك]]كPكĞɋufU] ]fUU؋uEEm]mE!ƒ!u ‹Ë0A FUUEuEA$EuEA(EuEA,EuEA0EuE]A4EuEA8EuE]A шL>$E}!Ѓ!׉Ẻ}ȋM̋Eȋ} M̋MÜ9$E}!Ѓ!׉Eĉ}UċE} ‰UăU"D9! ˆT9&UWVS[ÇM$E UEك$}u(UE$AEuDDAEu""AEuEDžlFEulDDlFEul""lFEullEE3M,pUȃluEUĉpE M,11]@]@]@]qEu,UE)U9E~EM1EM9U,)M EEMEu) ƋEEDEu) ƋEEDEu) ƋEED Eu) ƃ;}N;}}[U,ڍJ&E ЉhEEEEu EEEʋUGIU]]];}|UȋE!M! u!u" ʈ;}كԢ}كككpˋU]]]]كككPكĞˍ fU]ɶ ]]]fUg܅`Em]mUE!ƒ#E ‹EˆFA EݕxݕXEuEA$EEuEA(EEu EݝXA,EEuEA0EEuEA4EEu EݝxA8EEuEA ˆ>u܋E}ƋUp u܉M؁Ĝ[^_]Džtl1pDžl01UWVS[EEPH @Hzr$MxlJ}uxM}:}@X@`]@P]Rݝ8ݝ(*݅8EM݅(1EDȉDBȃ~MUщUM}1;M}كĞfEƴ fEmUmƋEP$ɋ}EAZZZ ;M|1;Mك(}fEƋUݝxك̞ ݝpكТك,ݝhكĞك0ݝPfEmUmƋEP$}݅x݅pʉAEɃ݅h܅PZZZ ;M\}fUƶ fUmUmƋ}$)M}$$L}\كĞMݝ`XE}܅`Dž$$)$$U$1;Mt}AUt&}\@J @J@J A;M|Nj\$UF$ ׉\$~U1;E|ȋt̋LЋTԉ }. ɋ ‹U@;E|EEMmUmƋ}})MEE~ }׋\։\U}XEL؃ MQd1e[^_]؋(؋Т؋,؋0؋̞܅`؍e[^_]Ðt&UWVS[EDž|PH MzJ$Mxlr}ux}:@` }@Xݝp@H@Pɍݝhݝ`Rݝ(݅(EU1EDȉDBȃ~M}UME‰U}1;M}كĞfEƋU fEmUmƋEP$ɋ}܅hAZZZ ;M|1;Mك̞}كТݝXك,fEƋU ݝPݝHك(كĞك0fEmUmƋEP$}݅XA܅h݅H݅PZZZ ;M]}fUƶ fU݅pm]mƋM$)ʋ}M$ϋ$|M<0كĞ}ݝ@8E݅p}]܅@]Dž$$)$$$|1;Mt}t}<}Љ@JA@ @;MJ J0>JJJ(J8\>|<$UF$ ׉<$G||Lԋ|̋Dȉ tЉEU19}4ME ɋ E M@9|݅p܅`E|mUmݝp}})|||~ LLLL\>MkEEЋuMmUm<΋uĉ}}UM]ȍ \1[^_]Í&UWVSd[ÚExH @HwWuUuM]@PVMwU]@`}UfUE fUm]mU?كĞu]R$mȋU}BXɅ]Ee]E1EmUmUR$REEuEʍuL MM ELLLL L(LLL L(>\>\>M6EEЋuMmUm<΋uĉ}}UM]ȍ d1[^_]ÍvUWVS\[ښExH @HwWuUuM]@PVMwU]@`}UfUE fUm]mUUكĞu]R$EUE}BX]EuEmUmEP$}EEEEɍL L L(LLL(^L0LLL0^LL8LL8^ M!EEЋuMmUm<΋uĉ}}UM]ȍ \1[^_]Í'UWVS u|[FuPHFHplUNݝ`L~ PFPF`ɉpɋB HDݝX@,c|Hx tUكĞٽHfݝ8 4f٭ەT٭E݅8ۅT@XɁDPTp 1;|x٭ە٭P$鋽P݅8LL܅` ˋl F;|݅XTlL٭ە٭l)4؁@,1e[^_]Åu?1;}xt ɋl @;|TNTPp1;4|$&٭ە٭P$鋽P݅8ዽtLL܅` ˋx ʋlA;lPTPpϋt1ҍ ;xxt|=٭ە٭W$鋅l݅8܅`ʃL x B;| Wݝw݅||c |V&UWVS }|[Èw}GHO VݝhGPFPݝ`tG`VpHGLݝXD@,AH|DɉxكĞٽDfݝ( $f݅X٭۝T٭M݅XAXۅT݅(ݝ8uB؋H193x݅8 ɋp @9|vNmTPȋt΋H1ɋH٭ە٭P$݅(܅hL LLP LLLʋx܍8TT܍8ˋp\N2݅X܅`TL٭ە٭ݝX)$p ‰p&@,11e[^_]ËPT‹Ptȉ4Hx ƅҋ4xI1ҋH٭ە٭P$݅(܅hLL Lʋ4݅8TD ˋp܍8\N_ HQF|| |Wv&'UWVS }|[Xw}GHO FHpGݝhGPVDItݝ`V@G`<ݝX8,@| @ϋ݅P٭۝٭V$ܭP݅L LL(LL0L8Lʋx݅(PPPʍ܍0 @܍(܍0@@Z܍(@݅P܍0܅h܍(܍0ݝPZZB9 @Rv|| |W覽UWVS4[Ú}GHO]GPq]G`uq]G QEԉUȋGUEЋ}EfME̵ EfMmUmMGX}]Q1$EكĞ];ME}bmUmكĞEP$ʃEL E LA;M|E؋UЋE4UmUmMOK؃41[^_] UWVSL[JuFHFV ]FPHx]F`M}NU]ԋ8M̋H}}EfUMĶ EfUMm]mUFXu]uR1$EكĞ;u]E]EmUmكĞUR$}EEˍW̋}EɃL LLL LL \F;ujEE؋uMmUm\>\> M5EEԋu܋MmUm|mediaLib_jnicom/sun/medialib/mlib/mediaLibExceptioncom/sun/medialib/mlib/mediaLibImageColormap(ILjava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V,%&%&X&&%\^[^^^s^[^\J^m]9^(^^m]]c__R_A_0__^_^^^^^A?@?channelswidthheightstride[BpaddingsbitoffsetformatLjava/lang/Object;dataleftPaddingtopPaddingsubsampleBitsHsubsampleBitsVprecisionBitsvis_width_bitsvis_height_bitsdataH_float[FdataV_floatdataH_double[DdataV_doubleintypeouttypemethodlutlengthindexsizenormal_table[Idimdouble_lutrmapgmapbmapmaplengthv8plusav9av8plusbv9bn] 99K ?0C787L8L88739\::&:&:::w<<k>]>O>A>3>%>> >==p<==???p>>=EEEEE{EpEeEZEOEDE6E?CG????????b[QGy??UUUUUU???UUUUUU?$I$I??qq??F]tE?UUUUUU?;;?$I$I????qq?(??aa?F]tE?d! YB?UUUUUU?{Gz?;;?h/?$I$I?{a??B!??|??AA?qq?к?(?AA???aa?}A_З?F]tE?ll?d! YB?W+ɕ?UUUUUU?9/?{Gz??;;?x+R?h/?)A?$I$I?p}?{a? 'u_[??;ڼOqɐ?B!?AA?? ?|?g1??ہv`?AA? V،?qq??к?O贁N?(?cj`?AA?3??H??k?aa??}A_Ї?ȤxL?F]tE?p \?ll?hh?d! YB?X`?W+Ʌ?X0Ҏ?UUUUUU??9/?[R֯?{Gz?beF??,?;;?88?x+R?+J#?h/?,Mɂ?)A?5'Ps?$I$I?x!?p}?ρ?{a?? 'u_[?55??k?;ڼOqɀ?h ?B!?Mb?AA?@ ??? ?qBJeD?|?~?g1~?t:W~?~?}?ہv`}?4,Tw}?AA}?tn }? V|?^|?qq|??|?|?+{?к{?3=l}{?O贁N{?ـl@6 {?(z?ppz?cj`z?mЦmz?AAz?Kzz?3y?Ny?y?r py?Hy?)I y?x?0x?kx?Jx?aax?ݾzek?кk?#+k?3=l}k?ek?O贁Nk?JH7k?ـl@6 k?"1K k?(j?^j?ppj?/j?cj`j?Y0Qj?mЦmj?JhAWj?AAj?Ň*,j?Kzj?j?3i?-hki?Ni?U$i?i? /i?r pi?w \i?Hi?,4i?)I i?ՐO i?h??7zRh?0h?:bοh?kh?h?Jh?'th?aah?xOh?ݾzd?[R֯d?Jvd?gв9d?H"d?{Gzd?f`Y4md?`d?vSd?beFd?M0':d?%f-d?QY^& d?d?feтd??c?Bc?,c?uc?{c?U)#`c?;;c?"z8$c?c,c?f"c?88c?E[uc?Hic?*_]c?x+Rc?FyFc?W[:c?j\/c?+J#c?Xwc?0 c?`*c?h/b?KNb?K%b?P- b?,Mb?7Zb?@+b?b?)Ab?[rb?b?MΡ8}b?5'Psb?'|hb?p"^b?w~Sb?$I$Ib?[`>b?߼xV4b?*"*b?x!b?UHyb?g G b?  b?p}a?Le[? [?к[?y[?#+[?@@[?3=l}[?4kq[?e[?[M-Z[?O贁N[?p B[?JH7[?#j_+[?ـl@6 [?`߻[?"1K [?,*Z?(Z? *2Z?^Z?vZ?ppZ?AZ?/Z?@Z?cj`Z?FZ?Y0QZ?V uwZ?mЦmZ?ߏObZ?JhAWZ?7& nLZ?AAZ?-C6Z?Ň*,Z?/uz!Z?KzZ?.5 Z?Z?rY?3Y? _}Y?-hkY?@Ӝ4Y?NY?-&vY?U$Y?t$ۣY?Y?p?`Y? /Y?Rf{Y?r pY?lfY?w \Y?s RY?HY?Ygз>Y?,4Y?td*Y?)I Y?AR!Y?ՐO Y?Ρ~Y?X?<X??7zRX?CX?0X?OdX?:bοX?@@X?kX?4\9X?X?KtOX?JX?n@}X?'tX?\jX?aaX??XX?xOX?)QΠEX?ݾzW?}A_W?p!W?|.W?37iW?"W?^cW?9kW?ÉCW?ȤxLW?W? ƚyW?VopW?4gW?|"P_W?muVW?tKNW?F]tEW?X ^=W?A4W?p,W?F($W? .sW? |mxW?fh) W?p \W?C$CV?``V?t(V?7CkV?&V?aȁ&V?);V?llV?xNWV?= IV?@@V?rST?T?[R֯T?\[(T?JvT?ڛT?gв9T?cT?H"T?%qT?{GzT?&UtT?f`Y4mT?GJgT?`T?pPZT?vST?CgMT?beFT?"[Í@T?M0':T?R3T?%f-T?M 'T?QY^& T?!sbT?T?:z T?feтT?@@T??S?trPS?BS?Q֩\S?,S?=S?uS?S?{S?:U\yS?U)#`S?\iLS?;;S?-S?"z8$S?n"ZS?c,S?!S?f"S?~+S?88S?D'$H{S?E[uS?soS?HiS?(bcS?*_]S?[WS?x+RS?ؒILS?FyFS?vF@S?W[:S?ƿ}5S?j\/S?2d)S?+J#S?}Ӝ+S?XwS?sa{S?0 S?,\RnS?`*S?D$R?h/R?WR?KNR?1^R?K%R?<=R?P- R?gYR?,MR?xR?7ZR?T]}R?@+R?wR?R? IR?)AR?70ؘR?[rR?5ݧR?R?~SR?MΡ8}R?PxR?5'PsR?pnR?'|hR?ricR?p"^R?!QuXR?w~SR?_NR?$I$IR?ԗCR?[`>R?9R?߼xV4R?ŏU*/R?*"*R?$R?x!R?[ R?UHyR?6^R?g G R?ml;!2R?  R?OQ?p}Q?dkQ?L@>>o@@@?@p?+k/k/..----H033w33n2n2n224A:999877H76;]o]onCnumumuml;llkj#j#j#jipۤۤMgpt9 Cc!B AAAAA@@a@??B >>>>>===8<8<Cyyyyyxx1xRwRwCnvnvnvnvnvuut t tzJJJz7ZYYYYY29GG##_>$ؗҜ<|N6VN\SȞq:Π?7OBaa/_Q|}r gg\ `""(;f?;f5656665888886:A@i><-;8XG!FFFEXG+EXGGGGzGoGdGJJlJEJJJJ*@+ P@E \= wk_TJ@7.& }|zyxvutsqponmlkjihgfedcba`__^]\[[ZYXXWVVUTTSRRQQPOONNMMLLKKJJIIHHGGFFEEDDDCCBBBAA@@@???>>===<<<;;;::::9998888777666655554444333322221111100000////......-----,,,,,,+++++U?3*n$U/@7+l %  3 r  :I`8mP4yfR@. ~rf[PE:0& ztnhb\WQLGB=83.)%  ~|ywusqomkigeca_][YXVTRPOMKJHFECB@>=;:8754210.-+*)'&%#"!     ;<@<<<<<=q= =l0=@=hP=`=op=s==q==o===========+=s>>r> >q>>>> >$>(>,>0>д4>8>ϼ<>@>D>H>L>P>T>X>\>`>d>h> l>p> t>x> |>s>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> >> >>> >> >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>s> |>x> t>p> l>h>d>`>\>X>T>P>L>H>D>@>ϼ<>8>д4>0>,>(>$> >>>>q> >r>>s>+===========o==q==s=op=`=hP=@=l0= =q==<<<<@<<;th\PD8, ȾP ==>0>`>>>>>>? ??$?0?>>>>p>@>>=@=@@pؾ(4@LXdp|xl`TH<0$ о`0<== >P>>>>>>>?? ?,?8?D?P?\?h?t??t?h?\?P?D?8?,? ???>>>>>>P> >==<0`о $0<HT`lx|pdXL@4(ؾp@@@==>@>p>>>>>>???(?4?@?L?X?d?p?|?x?l?`?T?H?>>>>`>0>>== PȾ ,8DP\ht?@@@@@@@AA A0A@APA`ApAAAAAAAAAAAAAAAAABBB BBBBB B$B(B,B0B4B8BC?C@CACBCCCDCECFCGCHCICJCKCLCMCNCOCPCQCRCSCTCUCVCWCXCYCZC[C\C]C^C_C`CaCbCcCdCeCfCgChCiCjCkClCmCnCoCpCqCrCsCtCuCvCwCxCyCzC{C|C}C~CCC?@@@@@@ @"@$@&@(@*@,@.@0@1@2@3@4@5@6@7@8@9@:@;@<@=@>@?@@@@@A@A@B@B@C@C@D@D@E@E@F@F@G@G@H@H@I@I@J@J@K@K@L@L@M@M@N@N@O@O@P@@P@P@P@Q@@Q@Q@Q@R@@R@R@R@S@@S@S@S@T@@T@T@T@U@@U@U@U@V@@V@V@V@W@@W@W@W@X@@X@X@X@Y@@Y@Y@Y@Z@@Z@Z@Z@[@@[@[@[@\@@\@\@\@]@@]@]@]@^@@^@^@^@_@@_@_@_@`@ `@@`@``@`@`@`@`@a@ a@@a@`a@a@a@a@a@b@ b@@b@`b@b@b@b@b@c@ c@@c@`c@c@c@c@c@d@ d@@d@`d@d@d@d@d@e@ e@@e@`e@e@e@e@e@f@ f@@f@`f@f@f@f@f@g@ g@@g@`g@g@g@g@g@h@ h@@h@`h@h@h@h@h@i@ i@@i@`i@i@i@i@i@j@ j@@j@`j@j@j@j@j@k@ k@@k@`k@k@k@k@k@l@ l@@l@`l@l@l@l@l@m@ m@@m@`m@m@m@m@m@n@ n@@n@`n@n@n@n@n@o@ o@@o@`o@o@o@o@o@p@*YYXXXXX+eGw@P">bRC.f?B.f?xjsT= C?̬UU?%UUUUU?X?77I  V{-Y1_IpA}l N?Z ?53=?;h!?aw>,?̻.7?nC?Tr_N?tӰY?< e?)kp?6r+{?țuE?f?>?9ǩ?lX?lE?Gr+?]9#;?2?ũ_?f?>#(0?Q[?*^?1l*?6?-B?.1ON?QȘZ?qTf?{Q}?kJ?1 ?2?1Lp!?2;.?duR? u?Ev?/eT'?ѭ?L%?AL6?#FG?![W?Dh?61y?fNJ?XxЛ?۠*B?xֺ?D2?f^j?6w?,(? [?/S%?Ͱ77?_+H?RDZ?pvk?PNޟ}?"z3?p? SM?𣂑?ոKt?##c?^?e]{f ?z?3-J0? FB?]%>U?R`Jg?X0y?Y.S?yUk?n5!?zӿk?t'<:?Z?"L?f)?Rl ?O3?IF?:YrY?*)nl?G^v?I'?J0?o?K?i? [?=Aa(?Rݛ?,e-?z_@?"xGT?KW.g?3Ɉ{?m?QW@?i ?um.?]U ?t[[?|J-?h+S? /?֡C?X?bvl??u`q]?2?:|?`!?Xd?_{3?}kg?)F&?;3;??.P?vmg$9e?LQz?@wy?ڐ?n|?g-H??'Za?*A?k7+%?trH:?@En[vP?Dy2f?{?<$ؑ?ؐ?Ւ6?q+?#*k?q`͸)a#LwiK<K<@~dG<3u㚼4Y 3alX2e>D8`<]D뚽ݪbIZZy-ә<>495{< 5y.aSH< sgyyetbBlTEEdB$buEBhڿ<~NEڜB f#E>B@-Z(~EBtSקI~EB@|҄~EʡBF~EWCv=~E1* C`W2}EC ²q}Eb$C!}EQ0CPy|EivDD^{uHLdEPHD*:'Fn)cEƝMDfT9ß?bEYRD֩"RaEWD‚Qc`E>[D³Ԏp_E`D6¿è{^EeD^è]EpjD\EWoDʈ[EEtDkchZE9yDR,ñYE4~DNYbXED Ä{WEDX!qVE~DFYAdUE>/Dz&jTTE介Djʕ0CSE_KDƒ"/REݐD1DqÚQErDw;1OE DbNE6D;(zME@D!,TLEߝD ~xOKE"D} ïÁbJE$D&ÜÚE)D*ⱖU=ED=j-p'EuÙ(ù?D?Ev%ۊDr@EKU"à׵D/BEo$ &D`CEzZvDDEËtȭDEE%yDFE G{{rDHE]ËʥD/DAdUEYâF~D!qVEX¸D{WE ÚDbXEYN½4~DYE,R9yDhZEkc‚EtD[EʈWoD\EpjD]E^eD{^E6`Dp_EԎ>[DQc`EîWDRaE"ò֩YRD?bET9fƝMDn)cE'F*:PHDLdEuH^{>DD/dE(@bǖE?DeE'-Ö:DfEZZm6DgE…1D?ehEk§`-D9iEBf#E ڜBNE<~€hڿBuE$b€dBETEl>BZĒ('BEd RAE FH4AwE(zDAeE=@`E@@E?AEPA(E!B ƾY«EHcBRAdE|B'̄&E4BT“E%B$$&EDBhZtE2DD2tE7D<‹ñsE:fD1{DqQYEDw;1-XE緻D߂c=WEOaD;(EK7D+øn:=ERmEjød.E1:B)EE/,6' (EE_@:&EE3Vą%E;VEٱfĵY$E EWAs#EE}s{!EE E_E_E EE!Es{}E#EAsW EY$Efٱ;VE%EV3ÆE:&E@ĕ_E (E6'/,ÆE:B)E1F>Ezy*E˱9E+E-kfEZ,Ej ćcE.E_ ÁE?J/E( s|ED{0E Eɂ E1Eݫ _l6 ET2EQe aKl ET4EO{ϛ E15ElÜME$\6EpķE7E9|ÇE98Eēò_E9E6Hz_Ea:EďÒE.EjhčL#D.x?E縿:D@ERi"ÚHDsAE̽CQDBE_"ĥiD CE-ĿOSDDE!D FE' wDGEp=jÏZDx,HEŪFD;IEtęæDGJEvý]DRKEKU΢ÕD[LEo$< D"cMEzü^DbhNEċК#DkOE%{ DmPE G{^DWlQE]В2DiREģ&é DdSE}A_D^TE}xԊDfDZE"ĦuDZEjpmD[E&jĞzkToD\EXĢFfȫDV]EXa"DC^E Ě[}Db_EZNVj٣D ?`E-RQ5DaElcLD@aEʈGD3bEB PDcE^= DgdE68'D-5eE3?sD fEĮ.v֎DfE" IJ):DUgES9 f%mDQhE&F *: EDiEvH ^{loD iE(@ b؁DJjE(-Ėæ~DCkEZZ V`yDkEtEìjV-DkJuEUS4(D~uEÜɎ3#DsUvEäDCvE aDSwE=(s“=D[wErfŸhDExE,Y¡ DxEL­DU*yER}@DȗyECøt4—CzEi(ChzEEØcsC2zEaè^KCE,{E+bxoC{EGqC2{Eð nC7|Eжm;CR|ERpLC'|E9ɊÐICw$}E#0ž#Cl྾C>>pC#?G\`Cf?»ziCu? 3=7Cg?foʿCAv?ڦ/'Cs?@ݼKC6@fi9C#@&1.!C5@`W/uC@H@GZ>ƟCr,[@poqLCTn@q [~C܀@K̽/6ilC@0Bw"YC@ߗDCd@(w.CϨ@$WC7@H6N!~Cz@8I۝_~C@hk]~C@Xrd#Y~CS@Ĺ䰱~CS+@Ꮎ/u~C`@TwcV~C/@yz6~CIAd貾RL~CR A\¿}C'A;`}CAlھ0}CA0 }Co!AL!^}CnY'AJ+6}CP-A  }CU3Aқ|C'h9AΨ|C?A"d|C@EAVY+]|CKA"3~b /|C7RA<1. {ClXAER{Cg^A& O{Ce]eAroX9gj{CUkAnb7{C#brAk<{CxAuaWzCAgzC$Aǐ!m]zCUAU$l$zCcAc&cyCPA?$)TyC!A*+@syC7A2W.(6yC~AK᥿z0xCQ7A2xC1A=5xxCOAo 77xCAs9wC~AYÿ0vnwCưAϿ@h)wC}rAVֿ0BavC$Aܿ0EavCܻAB㿳@GjTvCsAecI| vC]A~[KuC&A)9MvuCAVO*uCAtïQ/noC] BEQBp9oCץ Bx^F2rnC BxsJus~nC(B+NbVunCBzRmvlmC.$BLVbx`mCIB[ylCqBN_W{lCBcH|9lCBgH5~kCB(lrqkC)!Bp ~ kC\#Bty+֥jC%B5Byց>jC'Bt}}iC*B"UniCA,BKăiCN.B$WdhC=0B͇/hCW3B=ǚgCI5B[1;WgC7B5ƆfCq9B.W{fC&Br eCa@BXj,eC!CBXM<~dChEBbIdCGBy ߈cCqJBkmccC-pLB"У탋AbCNBK7YzbC{)QB}tbCSB [aCtUB{ZGaCMXBeɍ{`CZB_6&`C]BԴ­_C؂_BL(3_CaB_ƹ.m9^CYdBGBϏ=^C8fB@/]C88iB@@2;E]CkB98\ClnB!F@I\CpBޖt sBSK[CuB;ZC*wBhJZCwzB֒YCk|Bt GYCutBshXCB5AXC;B^8WC}B0'9WC!Bc$oVCBo$.VCGB(vUCB(- UC"҉BaPTCvBd+TC_BRهSC=BaRCB@o RtRC8BC 7QCB܂i^o^QC͒B\%PC2B[;FPCcBn6NǖEOCeB+OCsBTNC!KB.NClBK4MCSBJLC7B* ^_LCꇞBE~ dpKC؟B =KC)B]'JC{B-|/:JCͣB"FIC B5&HCUtBb{o^HCMȧBɲSGCB%T5GCqBB{tԟFCIǫBͰ FCGB%*XsECsB,{^DCʯB_DDC"B|%CCyB|zCCQҳBY w{BC+B $"wgUACFBx#PUHACݷB$EAB@C7B &[+@C\Bt'`x?C0BV(>ChHB*ޖ@>CBn+5<=CB,R=CL\BK.3i8CB`F7]i 8CJBŔ8v:tp7CB9 z6C| B/;ה.6C3iB]|R;X*C?GB~XSk)CPBTᙏ|I)Cw BUF(CpB Ww'CBBXO'C_7B(zYrES&CʚBZ%CAB[ԒS%CaB]v7$CHBL^ڌ#C(B~_|HT#CaB-`B"CBa{!C{SBr c}Z7S!CB6d' CB2aeC}Brf,PCpC?gwʼnգCc"Ch ]mC CgieJCC kC?7C|BlhCCbmCCFCLn@BCKCLoІCCp`:CiC\qΌC_CarzCCtC0CCFu3Cr C)v C# C&:w -$C CfIxW*)uC CVyC;6 Cfbz5C Cl{˹4gCD Cs|<CGCy}޾CC}~?WCCCWC?}~CC޾y}GCCRGB@+C!PkB+CGאOB,C"I]N Br5-CmMYB-CõK4B.CJB8'/CBdRI2B}/Cd HBmq0CɒFcoB1C ԀE BH1CVIO9DB1^2CBpKB3C@ÓAB3C^@؉BG4C6?d)BF4CmJ=,Bb5C]|<3iB.6Cה/;| Bz6C 9Btp7Cv:Ŕ8JB 8C]i`F7B>8Cm5YB Q9C •4 /Bs9CW3 Bs:Cs2[sB .;C:0B8;C^e/BiCޖ*hHB>CV(0B`x?Ct'\B@C[+ &7BB@CEA$ݷBHACPUx#FBUACwg $"+B{BCwY QҳBCC|zyBCC|%"BDDC_ʯB^DC,{sBXsEC*%GB FCͰIǫBԟFCtB{qBT5GC%BSGCɲMȧB^HCob{UtBHC5& BFIC"ͣB:JC/-|{BJC]')B=KC ؟BKCdpE~ ꇞB_LC^* 7BLCJSBMC4KlBNC.!KBNCTsB+OCeBEOCNǖn6cB;FPC[2BPC\%͒Bo^QCi^܂BQC 7C8BRtRCo @BaRC=BهSCR_BTC+dvBTCPa"҉B UC(-BUCv(GB$.VCoBVC$oc!B'9WC0}BWC^8;BAXC5BXCshutBGYC tk|BYC֒wzBJZCh*wBZC;uBK[CSt sBޖpBI\C@!FlnB\C89kB;E]C2@@88iB]C/@8fB=^CϏGBYdB9^C.m_ƹaB3_C(L؂_B­_CԴ]B&`C6_ZB{`CɍeMXBGaCZ{tUB[aC SBbC}t{)QBYzbCK7NBAbC탋"У-pLBmccCkqJBcC߈y GBIdCbhEB~dCB fCG&jCց5By%B֥jCy+t\#B kC ~p)!BrqkC(lBkCH5~gB9lCH|cBlCW{N_qBlCy[IB`mCbxLV.$BlmCmvzRBnCbVu+N(B~nCusxsJ BnC2rx^Fץ B9oCpEQB] BoC/nK>^uBoCPmN:`B@KpCףkeY6NBpC'iRl2o?B\pCl8h.eAUqCyf*fPAqCd&@ArCb #6AVrCaH1ArCLA_1A?rCc]|O7APsC![*50BAsC6YڕrRAjsC W hAAtCUr ?AtCֲSܣA׻ɿxAwC0G@H@uC/`W5@C.!&1#@9Cfi6@KC@ݼs?'C/ڦAv?Cʿfog?7C3= u?iCz»f?`C\G#?Cp>>Cl྾>CC>l>C@?pCǠ?ǻܿCR?Bz C'A@3=,C1@f5JCkR@&/iCvs@@]-Cg8@fiKCĚ@&.qC_@`׽ӯC= @GZԲC{@poqC@q1 ]C^b@KL/6CG@h0BzCAߗ~iC!A(䓾w WC'AWEC!AHⶾN!2CM*A8ɾC[2Ahkݾ$pC;AXd#+6~C;DAĹ1~CLA/8~CuUATwc>~Cg^Ay&zE~C.gAd2RK.u~C&oA\?QiY~CoxAMW<~CՀAlZ0]#~ChFAh0 d~ChALwj;}C1AJp}CAu}C)Aқ{}}CAΨԀZ}C.A袿뵃6}CAVY}C?A"~bP|CͭAм1.|CO]Aſ\|CA& Ͽv|CAroؿ9gL|CG An"|CA{C[Aa4{CA{CܢAǐr{CJAU C{ClAc{CA?$zCASA*zCVA2 WzC)AK%zYOzCtA+߲zC/A1=yCAo7 yC;Bs=|yC8BYC0FyCB׻IstyCBOxCje BVV0xC( B\0bxC(4BBc@(xCfBeicxwCB~p[ wCsB)9wrwCzB~V4wCNBtïvC߽Bֲ[vCU. BruvC"B 4vC%Bە6uC'B)5!uC)B}ݗczluCq,BLA*(uC.BHtCta1B 7tC3BצVtCbU6By4tC8Bl8 sCM;BRl'%~sC-=BdYף{4sCI@BNPrCBBK/rC)JEBFQRrCGBy^2TrCoNJBxsuqCLB+bVjqCVOBzmqC QBLbTpCqbTB{pCVBNW*pCqYBH)oC[BH5oC^B(q3oCaB ~ƒnCcBy+ފnCG(fB5B„5nChBt}vmCCkB"³mCmBK>1mCbpB$WdlCirB/cCB@@A2LccCGB9C8bCHB!FF@1bC}BHޖ·%bCqBSK¡aC;BM;NaCBhP¡`CޡBR¹u`Cn0BtU 7`CrBXsh_CԥB5Zj+_C 'B^8] ^CyB_0@L^Ca̩Bcb$o]CNBod½j]CerB(gv\CŭB(-j\C BalP#\ClBdo+ˠ[CJBRr,[CBtfZChB@wo \CZC(BCy 7YC\B܂|i^šWYCdB\%XCB[¢iXC Bn6NWC9bB㈃zyWCBۄT•WC B.'VC`BK4/ VCBֈJ¯UC B*^§UC_BE~dpTCBҌ TC B]'jSC _B-|/L&SCHB"ѐ©RC B5&„*RC^Bb{oܫQC(BДɲ²,QC| B%PC^BB{t,PC&BИͰ3OCz B%* +OC^B,{dNCB_М@'NC_ B|%¡MC^B|z…!MCֳBYϠwLC B $wgLC$^BxPUTKC8B̤EAQKC=B [+׊JC2]BtJCBVȨ}~ICB HC[Bn5MpHCFB‡GCBK3M`GCGYBe^ FCB:‚NFCBsECUBW:ECB ‚DCBm¤%DCQB`F]iWCC1BŔv:CCB wBCKB/ACB]|hACBJȽm@CDB6­M@CB^s?CB@0?C@CTBO9VIV>C5BԀ =CCC;7CC~Xk:7CRCx6CCFº6C6C  |5CPH CB+4CE C(zrE]Q4C C 53C< CԒ µ$3C; Cv7 ݍ2C CL ¯1C/ C~| *_1C C-B P0C{C "/0CN!Cr }Z Ÿ/CC6' .C&lC2a ¢d.CNCr, (-CHC?w ^1-C[C ] C,CCge+CC !b+COHC|Bh*CVCb+*C*CL@')C3CL;(C5C`X(ClzC\„'ClCazº'C6C§&CbCF3M%C#C)«F%CEC&: è$C-ICfIW*– $CCV#l#CMCfb5m"C-Cl˹s."C}Cs<6!C:oCy޾¸ CC}?O CCCO C?}C C޾y:oC6!C<s}Cs."C˹l-Cm"C5fbMC#l#CVC $CW*fI-ICè$C &:ECF%C)#CM%C3FbC&C±6C'CzalC'C\lzCX(C`©5C;(CL3C')C@L*C+*C¯bVC*Ch|BOHC!b+C· C+CegCC,C ] ’[C^1-Cw ?HC(-C, rNCd.C 2a&lC.C' …6C/C}Z r N!C"/0C {CP0CB - C*_1C| ~/ C1C •L Cݍ2Cv7 ; C$3CԒ < C53C › C]Q4CrE(zE C+4CƒBPH C|5C³ 6C6CF¾Cx6C«RC:7Ck~XC7C;>hCb8C![C8CGžC9C"I] CG:CmcCq:Cõ´7C9?;CC;CBdRLjCaCVIO9TBơ>C@XB3dCGB1mCKmBmC"£CkBvmC}thB5nC5BG(fBފnCy+¢cBnC ~aBq3oC(^BoCH5[B)oCHqYB*pCWNVB{pCqbTBTpCbL QBqCmzVOBjqCbV+LBqCuxsoNJBTrC2y^GBRrCFQ)JEBrC/KBBrCPNI@B{4sCףdY-=B%~sC'RlM;B sCl88B4tCybU6BVtCצ3B7tC ta1BtCH.B*(uCLAq,BzluCc}ݗ)BuC!)5'BuC6ە%B4vC "BuvCrU. B[vCֲ߽BvCïtNB4wCV~zBrwC)9wsB wC[~pBxwCceifB(xC@Bc(4BbxC0\( BxC0VVje BxCOBtyCs׻IBFyC0YC8B|yCs=;ByC o7AyC=1/AzC߲+tAYOzCzK%)AzCW2 VAzC*ASAzC?$A{CclAC{CU JAr{CǐܢA{CA4{Ca[A{C뿌A"|CnG AL|C9groؿAv|C& ϿA\|CſO]A|C1.мͭAP|C~b"?A}CVYA6}C뵃袿.AZ}CԀΨA}}C{қ)A}CuA}CpJ1A;}CjLwhA~C0 dhhFA#~C0]lZՀA<~CWMoxAiY~CQ\?&oA.u~CRKd2.gA~CzEy&g^A~Cc>TwuUA~C/8LA~C1Ĺ;DA6~Cd#+X;ApC$hkݾ[2AC8ɾM*A2CN!Hⶾ!AECW񤾉'AWCw (䓾!A~iCߗAzC0BhG@C/6KL^b@]C q1@Cqpo{@ԲCZG= @Cӯ`׽_@qC.&Ě@KCfig8@-C@]vs@C/i&kR@CJf1@C3=,'A@Cz BR?CܿǻǠ?Cp@?Cl>>C@?1?S?vu?W?:?? ?3?\???|?c?5Jo?c2\?H?3??#?V>>>>'j>]Vd>CF>0&>>> =x==z=*T=g.=x=q<"j\53XY5&)U5wR4O4 LF4nJ4H3Gr3hE*3D2C2eCO2B2B1dCp1C$1D0eE0uF?0jG/h`I/UJU/JL/m@O.5Qh.+T.s W-Zw-# ]&-{a,e,,i1,m+q+5v9+z**?=*))J@)((Tv?(m' d'_ Z=' Q&!H&i!?9&!7%".%s"%3%"$$#$|# ,$##,$ |#$$#$"3%%s"%."%7!9&?i!&H!&Q ='Z_ 'd 'm?(vT((@)J))=*?**z9+v5+q+m1,i,,e,a{&-] #w-Z-W s.T+h.Q5.O@m/LJU/JU/I`h/Gj?0Fu0Ee0D$1Cp1Cd1B2BO2Ce2C2D*3Ehr3G3H4JnF4L 4O4Rw5U)&Y5X35\>5_H3!6dRb6h]6mgD6rq"7w{a7}Y7 7 8r U8& 8 8 9D =9 u9 9g 9 : N: : G : : ;#v L;+1 |;(3;4<;ADg>&>0F>Cd>V]>j'>>>>V?#?3?H?\?2co?J5?c?|???\?3? ??:?W?uv?S?1?@@ ?ac?&??%?gu?]>x>n>=c>W`>L:f> @K>m3.>&> =T = j= K=> ,t= S= 0=* =y < < =K =j = T = >&.>3mK>@ f>:L>`W>c=>n>x>]>'?R#?}6?8I?[?l?4`|?d???E?,?a?v?1??<?ug?%??&?ca? @1RssT5"FkfJ.+R{s nfJSXu9J;+% S~~~p~ X~;@~k(~q~\~F~./~_~~}r}+]}_G}2}f}K}2{/}hv}p|k| e|C `|{ Z||| Ti\| OV<|% IC|^ C1{ <{ 6 { 0{G *r{ #N{ *{ {6 zr z z soz* bHzh S!z Cy 3y$$ycyVy+y#ydxxx(Rxj&xww2{{wusnnwkb?wcUw@[IvR=vJ1vA%NvT9v0u(u%ulSu utAttMttassus;>ssyrprch^r`%rWqCzPqqHxqg@=q%]9qqT1pJ*p @#PpV7p-o#o> ]oon'nudn%nmamfm%mOllbl? !l k k0!vZk!lk!bj#"Wju"MLj"Bj#8ij#-i##:i$ha$h$ih%#hY%g%g%OgS&g&f&xfN'0f'e'eJ(Ve( e(ydH)oyd)d/d)YcG*Oc*DPc*9cG+/b+$nb+"bI,a,a,>aK-`-`-W`N. `._.n_R/ _/^0^W05^0]1z]]1pH]1e\ 2[\c2PY\2F \3;[k31h[3'[4Zr4uZ4$Z"5Y{5Y5/Y+6X6X69X47W7W7AW>8V8V8GVH9U9yU9oMUR:eT:[T;R QT\;HS;>S<4SSg<*!R>UQ|>DP>JP.?QTP?WO?]O8@dRO@jN@qNCAwONA~MAMMBKMBLBzLWCqFLCiKD`KaDWAKDOJEFJjE>;JE5IF-IsF%4IFH$GH|G ,HGG,H |GH$GHF4I%sFI-FI5E;J>jEJFEJODAKWaDK`DKiCFLqWCLzBLBKMMBMAM~AONwCANq@Nj@ROd8@O]?OW?TPQ.?PJ>PD|>UQ>#>Q8=Q2q=UR-=R'!;SH\;QT R;T[:TeR:MUo9Uy9UH9GV8V8V>8AW7W7W479X6X6X+6/Y5Y{5Y"5$Z4uZr4Z4['3h[1k3[;3 \F2Y\Pc2\[ 2\e1H]p]1]z1]05^W0^0^/ _R/n_._. `N.W`-`-`K->a,a,aI,"b+nb$+b/G+c9*PcD*cOG*cY)/dd)ydoH)dy( e(VeJ(e'e'0fN'xf&f&gS&Og%g%gY%#h%ih$ha$h$:i##i-j#i8#jB"LjMu"jW#"jb!kl!Zkv0!k k !l? blllO%mfmmam%ndnun'no]o >o#o-p7VPp#@ p*Jp1Tqq9]%=q@gxqHqqPzCqW%r`^rhcrprys>s;usssatMtttAtu Sulu%u(u0v9TNv%Av1Jv=RvI[@wUc?wbknwnsuw{{2ww&xjRx(xxxdy#+yVyycy$$y3 yC !zSh Hzb* ozs z z r z6 { *{ N{# r{*G {0 { 6 {< {1C^ |CI% <|VO \|iT |||Z{ |`C |e |k|p}vh/}{2K}f}}2}G_}]+}r}~~_/~.F~\~q~~(k~@;~X ~p~~~S %+;J9uXSJfn s{R+.JfkF"5TssR1 `b##dj.)k{A3v[#A}GRy"rng?a UH9;s-B T~}~+ uP~s m!~ e~ ]~K U~ Ljv~ C=c~& :O~o 1;~ (&~ ~L d} :} }* }u } } nr}UFZ}A}(}7}||b|gy=|m|`k|MSN|F1|9|4,j{H{'{{j{{[{;{T{gzHz?*z zrz,tOz{e+zUzF|yj6ay'Fy +uyYOy)yyJxxx<zfxb>xKx.r3wawOw">pws-Eww vhvv smv^`@vMv:uU'uxuf]uM S.u @u .tE!t!qt!At?"t"s"qs8#c}s#UKs#Gs3$m9r$Y,r$Fr.%2Nr%r% q*&q~&}q&Hq&'q{'p'p#(}qpx(h;p(Tp!)?ou)+o)^o*z&os*rn*kn+c}nq+]Cn+V n,Pmp,pJm,[D\m-F>"mo-19l-4l.0pln.+5l.'k/#kn/ k/Dk0km0sj0]j1HNjm13j1i2 im2 Ti2 i3 hm3 h3 Wh4 hm4s g4^ g5H Ugm53 g5f6fm6Of6 f7em7e7Ee8em8s!d8^$|d9I(8dl94,c90c: 4lck:9'c:>b;Cbj;HXb;Mby'`f>_>_?Q_d? _?^ @y^a@1^@k] AW]^ABW]A.]B\ZB{\B1\C[VC[C S[C [QD"ZD|,tZDh7)ZKEUBYEBNYE.YGYEFeXFpXF|dX>GXGWGW6H2WHVHV.IrLVI`UINU$JGdX|FXpFXeEFGYY.EYNBEYBUKE)Z7hDtZ,|DZ"QD [CS[ C[VC[C1\B{\ZB\B].AW]B^A]W A]k@1^a@y^ @^? _d?Q_?_>_f>'`y>n`r#=`l8h=`eM=Aa_b:'c9k:lc4 :c09c,4l98d(I9|d$^8d!sm8e8Ee7em7e7 f6Ofm6f6f5g 3m5Ug H5g ^4g sm4h 4Wh 3h m3h 3i 2Ti m2i 2i1j3m1NjH1j]0jsm0k0Dk/k n/k#/k'.5l+n.pl0.l4-l91o-"m>F-\mD[,mJpp,mP, nV+Cn]q+}nc+nk*nrs*&oz*^o)o+u)o?!)pT(;phx(qp}#(p'p{'q&'Hq&}q~&q*&q %r%Nr2.%rF$r,Y$r9m3$sG#KsU#}sc8#sq"s"t?"At!qt!tE!t. u@ .uSM ]ufuxu'Uu:vM@v`^mvs vvhv wEw-spw>"wOwaw3r.xK>xbfxz<xxxJy)yOyYuy+ yF'ya6jy|FzU+ze{Ozt,rzz z*?zHzg{T;{[{{{j{{'{H{j,4|91|FN|SMk|`|m|=yg|b||}7(}A}Z}FUr}n } }u }* } }: }dL ~ &~( ;~1o O~:& c~=C v~jL ~UK ~] ~e ~!ms ~Pu+ ~}~T B-s;9HU a?gnr"yRG}A#[v3A{k).jd##b` @N{5c {R"H2{443|3032H265V6B5{54<Hд?EPUUU?9B.?v6 wV9 i< 5x? B _E H eK <|N oQ ^T IW L0Z ] _ >b ge sh Ak  n {p s Sv y D{ y~ ) oփ T $ Ƌ e  ,  J C՝ \ a cߧ Z Ѭ oF  & z I 5a EĽ ~$ i4 + x E  ]P 1  K ú  N ]{ v  N N7 V r Ռ Ѻ  "  + T m z   0b`(t YQ P= ! z ??ozh^B./'_ VӹO ~dE"H#, %n&>'n3)**?????Xt?B?0 ?6e?A???Eø?m?T^?'??~?*?R?q?+M?&?0????ZR?#????!\?'???%@?$??\g??A)?Z??G?L?0?a?_??wv??,?Ȇ??9???B?3??G??#?@H??)?D?X?M?=???2??j?$?Bt???#a?X?.?R%?K?>r?o?r?I? ?q/?T?y???_? ?01?[U?^y?7??t??+?(N?q????[????a?S??y?? ?-*?$K?k??@???2? ?>y|6>՟jV=f >+ ?W>ƈ[^>Eo\FL>/T,X>L \om>0S:zb>Ǔ O>|si\a>d3j>酲j%M>úbxa>zz[>)U]H>ܥY#4o> z>i>GP>G2k>\6t|>)l>C:|>YHȆ>$D2Hi>&Ȏ֊s>7cO>3/c>t~5c m>5DSx>CVj>zzk>C&{y>RUWk>O\>krN>Z5>stu>x_?3->QH,y>^S>#\>ȚS>qzQZu>wЀ>oO؋C>iﮂ>(9>TlZEW>J9>8W>BĮ^>rqJz׎>wQw>YWm>c'bC>VK(؀>X>L!\>&uY">V~ >Ea> 1 AS>™5#i>/#~>1kb҂>~o㴃>xv>5DS>X G>wX:>((b>3>&l|Z>iux>H>!,w>(N>5c>/0@>>L$>7>G.HՃ>VP>]~9>~ߒœ>$`}>15^>4{>->${4>jmU2>[P>gә>?xb> YV؟>T/wё>* 4r{>_y>|>rb:>訖>85Cj>> U>iK->U>ON9_>oO؋C>zQ=>l>-6ǝ>[a>gyi ->;w-s>QE%e>#(s~>^&1>k>g >ܞ삍>΂$>O>ͳͳ bbiDDKc=*ddb # $ $ p{ ccHddSyEzS, @?@p>@@@@@@@@$@@c-KKW-ooo[$l7H5JnY"}Fm9A   !!""##$$%%%&&'''(()))**+++,,,---...///000111222333444555566677778889999:::;;;;<<<<====>>>>????@@AABBCCDDEEFFGGGHHIIJJKKKLLMMMNNOOOPPQQQRRSSSTTUUUVVVWWWXXYYYZZZ[[[\\\]]]^^^___```aaabbbcccdddeeefffgggghhhiiijjjkkkklllmmmmnnnoooopppqqqqrrrsssstttuuuuvvvvwwwwxxxyyyyzzzz{{{{||||}}}}~~~~%%%%%%%%%%%%ffffffffffffffffffffffffffffffffffffffffffffffff>?((Q]]iQ;N9DD9DD9D@D9D9D9D@DY@(#)mediaLib:0250:20060825:386mediaLib:0250:20060825:386F2k?c}ؿc}?F2k@ `P0pH(hX8xD$dT4t L,l\<|B"bR2r J*jZ:zF&fV6vN.n^>~A!aQ1q I)iY9yE%eU5u M-m]=}C#cS3s K+k[;{G'gW7wO/o_??;f?c}? iAT!>CT!>DT!>"DT!>*DT!>s,DT!>,DT!>-DT!y>-DT!i>-DT!Y>-DT!I>-DT!9>-DT!)>-DT!>-DT! >-DT!=-DT!=ϱ/?`!) I 2J21/*>DB>@>>>SPDFLLoIDFB????O??>>L>*>%I>>9==.==ى=%I===p=9c=6W=L=1 C=.:=C2=*= #=ى=&=%I== ==!==><<<9x;>`;;ֹ;;.;s;;;e;;%;9;x;8p;\;g;b;t;;6;+;{; h;! ;;ud;;;(;E;; ; ;e;07;1 ;;;0;;i;dR;>;.;D!;;;a ; ;A ;c;C;; ,;:;AL;+`;v;j;;Jȩ;?; ;/;U;~;ϩ; ף;^;7;1k;; ؟;f;L;ى;Ȝ; ;pL;萚;#י;;h;&;.;O;%; ;E;;;%I;;;8Z;$;z;5x;Rڍ;=;;;4p;؊;B;A;;;;g;ن;K;7;4;;!;0;o;;!;;;;;;~;};|;{;-#z;/y;>x;@Nw;>`v;tu;t;ՠs;ֹr;q;p;p;.o;Pn;sm;l;k;dj;j;e9i;eh;sg;f;me;%e;3Yd;9c;b;xa;5a;8p`;_;\^;'^;g];\;b[;/[;tZ;BY;Y;ZJX;6W;DV;+V;xU;{T;3T; hS;R;! R;TaQ;P; P;udO;N;N;'qM;L;x)L;(K;J;EJ;4I;I;yjH; G;2G; F;tE;eE;D;07D;>C;1 C;xB;A;PRA;@; 0@;0?;+?;>;=;i=;S<;dR<;A;;>;;V:;.:;9;D!9;Û8;8;7;7;16;a 6;H5; 5;:4;A 4;3;c3;|2;C2;1;1;0; ,0;"/;:/;>.;AL.;-;+`-;,;v,;,;j+;+;*;/9*;J);W);?(;y(; (;{';/';&;U&;%;~%;%;ϩ$;)@$; #;rn#;^#;Ϟ";7";:!;1k!;!; ;< ; ;zt;f;ͮ;L;;ى;");;i; ;ߪ;pL;s;;3;#;z;;;h;A;&;uZ;.;P;O;;%;H; ;;E;;;E;;;%I;;; P;;̫;8Z; ;$;g;z;;5x;);R ; ;= ; ; ;U ; ;Q ;4p ;k$ ; ;͍ ;B ;t ;A ;\d ; ;;;?;;p;g;3 ;;#;K;<;7;|y;4;;;^e;!;;0;U;o;u;;NJ;!;6;;(C;;";; @;)00vbQ@/ AAE3DOROQQPO%W]0W[[.Y0We X\PKMURB8@3A`FPK2@PZp_2Pe`ou20PP2$ 2P/6< 2CIN2 W]c2l@s0y02 2 2 202220{2p 2`0"2` `%2 28(2?pek,200p/2820`B2`0M2$5;S2Qhn\262@2 K2Q2Z20b2psx|P@ P0 q2w2P22 `@#p2+5@pHNX@ck r~PP@@@t2@|22 2@2@$24<B0MW`_dp`|p p2 @>`02&@DgP2)`Fi0p\P]]0^W XXY33333P3P33W3 _3c3h3m3u3`z30305P55@544`404044@4@455 5p5 5 55p55 55 5p$5%5&5(555 5`"55@5505@55065p75850515`2535@*5p+5,5`.5P4@44444`44@4p44`4@4@4p4444P4404`44`4@PPP0@`@@Pp`@ 55555555@66` 606p55@5 5p "P$%))+, g6ph6j6k6PM6N6 P6Q6m6 r6`v6P{6S6 X6P\6@a6 76 <6A6G66$6*606p]Э0Sp0A},@',P-<P0K OOppp0P`$P$p$$p$$$0$%%%%%0 % %!%"%@$% 3333@333@3@33303333303P333P3p3334 4 44 44!4'4-43494`?4_40g4n4v4}4@404444`404''''@'0'Л'`'''''PK0LpMOY']'b'pg'0l'o's'0x'`|'`''0' 1:P@E@333033333P3p3333`33 3-43494`?433@3444`404PE4K4R40Y433333P3P33W3 _3c3h3m3u3`z303p555 5p555@5p55505p555P555@555P550555`5и5@55@55@5л5`55505о555@5555P5555P5555`555@555P55 555`55@55055055@55p555@5:DDD D 7xbx.oaoo\boo^_o7‘ґ"2BRbr`pq7 0x`77P7`7(7@777pX77`7P77677`7@7x77p4P@74`7 7h- 4p`zp4 76677P770`76077P40 77@47@@`6677p7`p7p P7u 777 7D70GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux)GCC: (GNU) 3.3.1 (SuSE Linux).symtab.strtab.shstrtab.hash.dynsym.dynstr.gnu.version.gnu.version_d.gnu.version_r.rel.dyn.rel.plt.text.rodata.data.dynamic.got.bss.comment!  )D1o^_^_4>oaaMo\b\b@\ bbx.e x inM5t66 B |77 7777777O8&8*8. Y8vt ^_a\bb  6 7 7 770BTh *AXn$=Rdw (Kn #9Oew 5Hc{.D[p8Qj*B[t ' ; R f }       ( F ] q        " 6 M a t         ' ? T t      9 [ }    !5Qj 8Sk/BUex #?Xo7Vr3Lcv 2Ol 0Pp$<Qd}2Jc{/Qh 5Ncw6Ww<^ (D[r;Xu*EZt1Lc ) H i     !)!H!h!!!!!"("G"h""""""#/#G#b#|###### $#$=$W$o$$$$$$ %"%<%R%q%%%%%&$&A&]&}&&&&&','C'Y'o''''''((6(N(f(}(((((()2)O)l))))))*<*\*y*****+,+I+f++++++ ,C,f,,,,, -/-O-q------...M.l....../+/B/b/05 /` /va j0p/ 06 00R 1' ?1@ l1pu 1 1 ;2 i2 2@L 2 2PX@ '3` 3  3@ $4@E W4 4  50' H5} 5 E  5  6- G6 v6З0 7@ D7 l7+ 7 7 f8P 80 94 9Y 9 ^ : @: :p :0I  ;P 7;p `; ; ;_ ;n (<  <  =@A I=a z= = 0 =@  $>2>&B>0=  s>7 > >O ? y?  ? ?` @t A@ x@0 A% >AR oA`uB A@ A *Bp0 SB zBp Bj5 vCp% C C[ %D30 DPg Ed zE FP :F kF`8 F  G [G GG G` HHP0 H I ?I IWA I JP VJ@ J070 KPX LK KK  KP0 K LpV .LXALZ5 L L`' M VMP M@ NN Nk Np O0 O On 7Psg P  Q Q` Q0 Q R _R  R N R 3SpTA ]SP9 S Sp# S TP CT TT0qg *U XU  UUP Up& V AVP13 Vp 5W bW  W x W tXo5 X 'Y SY` ~Y)0 Z AZ Z` Z [ 8[0 f[* [ \@ \P \0S@ \. ] ]l5 ^p C^ n^( ^0 z_Кt _p" _ `P >`rB ````0 PaP a  b b03 b  b0% cph5 c  c`0 d'"d`0 Td|dd d d d ke5 e, fPca f fp! f\5 mgvgg  g5 gz Fh}E uh4 h&hP4 h@ #i |i b0 iX i7 jj Bj0 tju jp jCj0 kЯ Hk@ya k% kp  2l0 bl0M l0 lp l m  Hmi mU@ m "n0 {n n n o{0 oea ,p cpP pp$ qQA =qLqp'0 q  r Hr {r` r0 r s  s0s@p s2 mediaLibwrapper_Image.cmediaLibwrapper_util.cmlib_c_ImageAdd.cmlib_c_ImageAnd.cmlib_c_ImageBlend.cmlib_c_ImageChannelExtract.cmlib_c_ImageChannelInsert.cmlib_c_ImageClear.cmlib_c_ImageClrOrdDither8x8.cmlib_c_ImageClrOrdDitherMxN.cmlib_c_ImageColorConvert.cmlib_c_ImageColorErrDiff3x3.cmlib_c_ImageConstAdd.cmlib_c_ImageConstAnd.cmlib_c_ImageConstOr.cmlib_c_ImageConstSub.cmlib_c_ImageConstXor.cmlib_c_ImageCopy.cmlib_c_ImageDataTypeConvert.cmlib_c_ImageFilteredSubsample_Fp.cmlib_c_ImageFilteredSubsample.cmlib_c_ImageFlipY.cmlib_c_ImageHistogram2.cmlib_c_ImageLookUp.cmlib_c_ImageMax.cmlib_c_ImageMean.cmlib_c_ImageMin.cmlib_c_ImageMulShift.cmlib_c_ImageNot.cmlib_c_ImageOr.cmlib_c_ImagePolynomialWarp.cmlib_c_ImageScale_Fp.cmlib_c_ImageSub.cmlib_c_ImageSubsampleAverage.cmlib_c_ImageSubsampleAverage_S16.cmlib_c_ImageSubsampleAverage_U16.cmlib_c_ImageSubsampleAverage_U8.cmlib_c_ImageThresh1.cmlib_c_ImageThresh1_S16.cmlib_c_ImageThresh1_S32.cmlib_c_ImageThresh1_U16.cmlib_c_ImageThresh1_U8.cmlib_c_ImageThresh2.cmlib_c_ImageThresh3.cmlib_c_ImageThresh4.cmlib_c_ImageThresh5.cmlib_c_ImageXor.cmlib_c_ImageZoomTranslate.cmlib_ImageAbs_Fp_Inp.cmlib_ImageAbs_Fp.cmlib_ImageAbs_Inp.cmlib_ImageAbs.cmlib_ImageAdd_Fp_Inp.cmlib_ImageAdd_Fp.cmlib_ImageAdd_Inp.cmlib_ImageAffine_Fp.cmlib_ImageAffine.cmlib_ImageAffineTable_Fp.cmlib_ImageAffineTable.cmlib_ImageAffineTable_u16_3nw.cmlib_ImageAffineTable_u16ext.cmlib_ImageAffineTable_u16nw.cmlib_ImageAnd_Bit.cmlib_ImageAnd_Inp.cmlib_ImageBlend_Fp_Inp.cmlib_ImageBlend_Fp.cmlib_ImageBlend_Inp.cmlib_ImageBlendMulti.cmlib_ImageClear_Fp.cmlib_ImageColorConvert_Fp.cmlib_ImageColorErrorDiffusionMxN.cmlib_ImageColorTrue2IndexInit.cmlib_ImageColorTrue2Index.cmlib_ImageConstAdd_Fp_Inp.cmlib_ImageConstAdd_Fp.cmlib_ImageConstAdd_Inp.cmlib_ImageConstAnd_Bit.cmlib_ImageConstAnd_Inp.cmlib_ImageConstDiv_Fp.cmlib_ImageConstDiv.cmlib_ImageConstDivShift.cmlib_ImageConstMul_Fp.cmlib_ImageConstMul.cmlib_ImageConstOr_Bit.cmlib_ImageConstOr_Inp.cmlib_ImageConstSub_Fp_Inp.cmlib_ImageConstSub_Fp.cmlib_ImageConstSub_Inp.cmlib_ImageConstXor_Bit.cmlib_ImageConstXor_Inp.cmlib_ImageConv2x2_Fp.cmlib_ImageConv2x2.cmlib_ImageConv_32ext.cmlib_ImageConv_32nw.cmlib_ImageConv3x3_Fp.cmlib_ImageConv3x3.cmlib_ImageConv4x4_Fp.cmlib_ImageConv4x4.cmlib_ImageConv5x5_Fp.cmlib_ImageConv5x5.cmlib_ImageConv7x7_Fp.cmlib_ImageConv7x7.cmlib_ImageConvClearEdge_Fp.cmlib_ImageConvCopyEdge_Fp.cmlib_ImageConv_D64ext.cmlib_ImageConv_D64nw.cmlib_ImageConv_F32ext.cmlib_ImageConv_F32nw.cmlib_ImageConvKernelConvert.cmlib_ImageConvMxN_Fp.cmlib_ImageConvMxN.cmlib_ImageConvolveMxN_Fp.cmlib_ImageConvolveMxN.cmlib_ImageConv_u16ext.cmlib_ImageConv_u16nw.cmlib_ImageCopy_Bit.cmlib_ImageCreate.cmlib_ImageDFT.cmlib_ImageDilate4_Fp.cmlib_ImageDilate4.cmlib_ImageDilate8_Fp.cmlib_ImageDilate8.cmlib_ImageDiv_Fp.cmlib_ImageDivShift.cmlib_ImageDivTables.cmlib_ImageErode4_Fp.cmlib_ImageErode4.cmlib_ImageErode8_Fp.cmlib_ImageErode8.cmlib_ImageExp_Fp.cmlib_ImageExp.cmlib_ImageExpTbl.cmlib_ImageExtrema2_Fp.cmlib_ImageExtrema2.cmlib_ImageExtremaLocations_Fp.cmlib_ImageExtremaLocations.cmlib_ImageFilteredSubsam_D64_f.cmlib_ImageFilteredSubsam_F32_f.cmlib_ImageFilteredSubsample_D64.cmlib_ImageFilteredSubsample_F32.cmlib_ImageFilteredSubsample_S16.cmlib_ImageFilteredSubsample_S32.cmlib_ImageFilteredSubsample_U16.cmlib_ImageFilteredSubsample_U8.cmlib_ImageFilteredSubsam_S16_f.cmlib_ImageFilteredSubsam_S32_f.cmlib_ImageFilteredSubsam_U16_f.cmlib_ImageFilteredSubsam_U8_f.cmlib_ImageFilters.cmlib_ImageFlipAntiDiag_Fp.cmlib_ImageFlipAntiDiag.cmlib_ImageFlipMainDiag_Fp.cmlib_ImageFlipMainDiag.cmlib_ImageFlipX_Fp.cmlib_ImageFlipX.cmlib_ImageFlipY_Bit.cmlib_ImageFlipY_f.cmlib_ImageFlipY_Fp.cmlib_ImageGradient3x3_Fp.cmlib_ImageGradient3x3.cmlib_ImageGradientMxN_Fp.cmlib_ImageGradientMxN.cmlib_ImageGridWarp_Fp.cmlib_ImageGridWarp.cmlib_ImageGridWarpTable_Fp.cmlib_ImageGridWarpTable.cmlib_ImageHistogram2_f.cmlib_ImageInvert_Fp_Inp.cmlib_ImageInvert_Fp.cmlib_ImageInvert_Inp.cmlib_ImageInvert.cmlib_ImageLog_Fp.cmlib_ImageLog.cmlib_ImageLogTbl.cmlib_ImageLookUp2.cmlib_ImageLookUp_64.cmlib_ImageLookUp_Bit.cmlib_ImageMaxFilter3x3_Fp.cmlib_ImageMaxFilter3x3.cmlib_ImageMaxFilter5x5_Fp.cmlib_ImageMaxFilter5x5.cmlib_ImageMaxFilter7x7_Fp.cmlib_ImageMaxFilter7x7.cmlib_ImageMax_Fp_Inp.cmlib_ImageMax_Fp.cmlib_ImageMaximum_Fp.cmlib_ImageMax_Inp.cmlib_ImageMean_Fp.cmlib_ImageMedianFilter3x3_Fp.cmlib_ImageMedianFilter3x3.cmlib_ImageMedianFilter5x5_Fp.cmlib_ImageMedianFilter5x5.cmlib_ImageMedianFilter7x7_Fp.cmlib_ImageMedianFilter7x7.cmlib_ImageMedianFilterMxN_Fp.cmlib_ImageMedianFilterMxN.cmlib_ImageMinFilter3x3_Fp.cmlib_ImageMinFilter3x3.cmlib_ImageMinFilter5x5_Fp.cmlib_ImageMinFilter5x5.cmlib_ImageMinFilter7x7_Fp.cmlib_ImageMinFilter7x7.cmlib_ImageMin_Fp_Inp.cmlib_ImageMin_Fp.cmlib_ImageMinimum_Fp.cmlib_ImageMin_Inp.cmlib_ImageMul_Fp_Inp.cmlib_ImageMul_Fp.cmlib_ImageMulShift_Inp.cmlib_ImageNot_Bit.cmlib_ImageNot_Inp.cmlib_ImageOr_Bit.cmlib_ImageOr_Inp.cmlib_ImagePolynomialWarp_0.cmlib_ImagePolynomialWarp_1.cmlib_ImagePolynomialWarp_BC_S32.cmlib_ImagePolynomialWarp_BL_S32.cmlib_ImagePolynomialWarp_Fp.cmlib_ImagePolynomialWarp_NN_Fp.cmlib_ImagePolynomialWarp_NN.cmlib_ImagePolynomialWarpTable_Fp.cmlib_ImagePolynomialWarpTable.cmlib_ImagePolynomialWarpTools.cmlib_ImagePolynomWarpTable_0_Fp.cmlib_ImagePolynomWarpTable_0.cmlib_ImageReformat.cmlib_ImageRotate180_Fp.cmlib_ImageRotate180.cmlib_ImageRotate270_Fp.cmlib_ImageRotate270.cmlib_ImageRotate90_Fp.cmlib_ImageRotate90.cmlib_ImageScale2.cmlib_ImageScale_Fp_Inp.cmlib_ImageScanPoly.cmlib_ImageSConv3x3_Fp.cmlib_ImageSConv3x3.cmlib_ImageSConv5x5_Fp.cmlib_ImageSConv5x5.cmlib_ImageSConv7x7_Fp.cmlib_ImageSConv7x7.cmlib_ImageSConv_D64ext.cmlib_ImageSConv_D64nw.cmlib_ImageSConv_F32ext.cmlib_ImageSConv_F32nw.cmlib_ImageSConvKernelConvert.cmlib_ImageSobel_Fp.cmlib_ImageSobel.cmlib_ImageSqrtTable_U16.cmlib_ImageSub_Fp.cmlib_ImageSubsampleAverage_Fp.cmlib_ImageSubsampleAverage_S32.cmlib_ImageSubsampleBinaryToGray.cmlib_ImageThresh1_Fp.cmlib_ImageThresh2_Fp.cmlib_ImageThresh3_Fp.cmlib_ImageThresh4_Fp.cmlib_ImageThresh5_Fp.cmlib_ImageThresh_Fp_Inp.cmlib_ImageThresh_Inp.cmlib_ImageXor_Bit.cmlib_ImageXor_Inp.cmlib_ImageZoom_BL_S32.cmlib_ImageZoomClipping.cmlib_ImageZoomEdge.cmlib_ImageZoom_NN.cmlib_ImageZoomTranslate_Fp.cmlib_ImageZoomTranslateTable_Fp.cmlib_ImageZoomTranslateTable.cmlib_ImageZoomTranslateToGray.cmlib_ImageZoomTransTable_16ext.cmlib_ImageZoomTransTable_16nw.cmlib_ImageZoomTransTable_32ext.cmlib_ImageZoomTransTable_32nw.cmlib_ImageZoomTransTable_8ext.cmlib_ImageZoomTransTable_8nw.cmlib_ImageZoomTransTable_D64ext.cmlib_ImageZoomTransTable_D64nw.cmlib_ImageZoomTransTable_F32ext.cmlib_ImageZoomTransTable_F32nw.cmlib_ImageZoomTransTable_u16ext.cmlib_ImageZoomTransTable_u16nw.cmlib_sys.cmlib_version.cmlib_SignalFFT_Frw.cmlib_SignalFFT_Inv.cmlib_SignalFFT_D64.cmlib_SignalFFT_D64Disp.cmlib_SignalFFT_D64Disp_8.cmlib_SignalFFT_D64Disp_24.cmlib_SignalFFT_D64Disp_32.cmlib_SignalFFT_D64_1.cmlib_SignalFFT_D64_2.cmlib_SignalFFT_D64_3.cmlib_c_ImageAbs.cmlib_c_ImageAffine_BC.cmlib_c_ImageAffine_BC_S16.cmlib_c_ImageAffine_BC_U16.cmlib_c_ImageAffine_BL.cmlib_c_ImageAffine_BL_S16.cmlib_c_ImageAffine_BL_U16.cmlib_c_ImageAffineIndex_BC.cmlib_c_ImageAffineIndex_BL.cmlib_c_ImageAffine_NN.cmlib_c_ImageChannelExtract_f.cmlib_c_ImageChannelInsert_f.cmlib_c_ImageClrOrdDitherMxN_Bit.cmlib_c_ImageColorErrDiffMxN_Bit.cmlib_c_ImageConvClearEdge.cmlib_c_ImageConvCopyEdge.cmlib_c_ImageConv_f.cmlib_c_ImageConvVersion.cmlib_c_ImageDataTypeConvert_f.cmlib_c_ImageDilate4_16nw.cmlib_c_ImageDilate4_8nw.cmlib_c_ImageDilate4_u16nw.cmlib_c_ImageDivShiftU8.cmlib_c_ImageErode4_16nw.cmlib_c_ImageErode4_8nw.cmlib_c_ImageErode4_u16nw.cmlib_c_ImageExtrema2.cmlib_c_ImageFlipMainDiag_f.cmlib_c_ImageGetAffineFunc.cmlib_c_ImageGetZoomFunc.cmlib_c_ImageGradient3x3Func.cmlib_c_ImageGradientMxNFunc.cmlib_c_ImageLookUp_f.cmlib_c_ImageMaxFilter3x3_16nw.cmlib_c_ImageMaxFilter3x3_8nw.cmlib_c_ImageMaxFilter3x3_u16nw.cmlib_c_ImageMaxFilter5x5_16nw.cmlib_c_ImageMaxFilter5x5_8nw.cmlib_c_ImageMaxFilter5x5_u16nw.cmlib_c_ImageMaxFilter7x7_16nw.cmlib_c_ImageMaxFilter7x7_8nw.cmlib_c_ImageMaxFilter7x7_u16nw.cmlib_c_ImageMedianFilterFunc.cmlib_c_ImageMinFilter3x3_16nw.cmlib_c_ImageMinFilter3x3_8nw.cmlib_c_ImageMinFilter3x3_u16nw.cmlib_c_ImageMinFilter5x5_16nw.cmlib_c_ImageMinFilter5x5_8nw.cmlib_c_ImageMinFilter5x5_u16nw.cmlib_c_ImageMinFilter7x7_16nw.cmlib_c_ImageMinFilter7x7_8nw.cmlib_c_ImageMinFilter7x7_u16nw.cmlib_c_ImagePolynomialWarp_BC.cmlib_c_ImagePolynomialWarp_BL.cmlib_c_ImagePolynomialWarp_Call.cmlib_c_ImageScale2Func.cmlib_c_ImageSConv_16ext.cmlib_c_ImageSConv_16nw.cmlib_c_ImageSConv_8ext.cmlib_c_ImageSConv_8nw.cmlib_c_ImageSConv_u16ext.cmlib_c_ImageSConv_u16nw.cmlib_c_ImageSobel.cmlib_c_ImageVisVersion.cmlib_c_ImageZoom_BC.cmlib_c_ImageZoom_BC_S16.cmlib_c_ImageZoom_BC_S32.cmlib_c_ImageZoom_BC_U16.cmlib_c_ImageZoom_BL.cmlib_c_ImageZoom_BL_S16.cmlib_c_ImageZoom_BL_U16.cmlib_c_ImageZoom_NN_f.cmlib_ImageAffine_BC_D64.cmlib_ImageAffine_BC_F32.cmlib_ImageAffine_BC_S32.cmlib_ImageAffine_BL_D64.cmlib_ImageAffine_BL_F32.cmlib_ImageAffine_BL_S32.cmlib_ImageAffineEdge.cmlib_ImageAffine_NN_Bit.cmlib_ImageAffine_NN.cmlib_ImageAffineTable_16_3nw.cmlib_ImageAffineTable_16ext.cmlib_ImageAffineTable_16nw.cmlib_ImageAffineTable_32_3nw.cmlib_ImageAffineTable_32ext.cmlib_ImageAffineTable_32nw.cmlib_ImageAffineTable_8_3nw.cmlib_ImageAffineTable_8ext.cmlib_ImageAffineTable_8nw.cmlib_ImageAffineTable_D64_3nw.cmlib_ImageAffineTable_D64ext.cmlib_ImageAffineTable_D64nw.cmlib_ImageAffineTable_F32_3nw.cmlib_ImageAffineTable_F32ext.cmlib_ImageAffineTable_F32nw.cmlib_ImageClipping.cmlib_ImageConv_16ext.cmlib_ImageConv_16nw.cmlib_ImageConv2x2_f.cmlib_ImageConv3x3_Bit.cmlib_ImageConv_8ext.cmlib_ImageConv_8nw.cmlib_ImageConvClearEdge_Bit.cmlib_ImageConvCopyEdge_Bit.cmlib_ImageConvMxN_ext.cmlib_ImageDilate4_1nw.cmlib_ImageDilate4_32nw.cmlib_ImageDilate4_D64.cmlib_ImageDilate4_F32.cmlib_ImageErode4_1nw.cmlib_ImageErode4_32nw.cmlib_ImageErode4_D64.cmlib_ImageErode4_F32.cmlib_ImageFlipMainDiag_Bit.cmlib_ImageFlipRotate.cmlib_ImageMaxFilter3x3_1nw.cmlib_ImageMaxFilter3x3_32nw.cmlib_ImageMaxFilter3x3_D64.cmlib_ImageMaxFilter3x3_F32.cmlib_ImageMaxFilter5x5_32nw.cmlib_ImageMaxFilter5x5_D64.cmlib_ImageMaxFilter5x5_F32.cmlib_ImageMaxFilter7x7_32nw.cmlib_ImageMaxFilter7x7_D64.cmlib_ImageMaxFilter7x7_F32.cmlib_ImageMedianFilter3x3Func.cmlib_ImageMedianFilter5x5Func.cmlib_ImageMedianFilterFunc.cmlib_ImageMedianFilterMxNFunc.cmlib_ImageMinFilter3x3_1nw.cmlib_ImageMinFilter3x3_32nw.cmlib_ImageMinFilter3x3_D64.cmlib_ImageMinFilter3x3_F32.cmlib_ImageMinFilter5x5_32nw.cmlib_ImageMinFilter5x5_D64.cmlib_ImageMinFilter5x5_F32.cmlib_ImageMinFilter7x7_32nw.cmlib_ImageMinFilter7x7_D64.cmlib_ImageMinFilter7x7_F32.cmlib_ImagePolynomialWarp_0_Fp.cmlib_ImagePolynomialWarp_BC_Fp.cmlib_ImagePolynomialWarp_BL_Fp.cmlib_ImagePolynomialWarp_Call_Fp.cmlib_ImagePolynomialWarp_NN_2_Fp.cmlib_ImagePolynomialWarp_NN_2.cmlib_ImagePolynomialWarp_NN_3_Fp.cmlib_ImagePolynomialWarp_NN_3.cmlib_ImagePolynomialWarp_NN_4_Fp.cmlib_ImagePolynomialWarp_NN_4.cmlib_ImagePolynomialWarp_NN_5_Fp.cmlib_ImagePolynomialWarp_NN_5.cmlib_ImagePolynomialWarpTable_f.cmlib_ImagePolynomialWarpTable_ft.cmlib_ImagePolynomialWarpTable_it.cmlib_ImageReformat_f1.cmlib_ImageReformat_f2.cmlib_ImageReformat_fp.cmlib_ImageSConv_32ext.cmlib_ImageSConv_32nw.cmlib_ImageSubsamBinToGray2x2.cmlib_ImageSubsamBinToGray4x4.cmlib_ImageSubsampleAverage_D64.cmlib_ImageSubsampleAverage_F32.cmlib_ImageThresh1_D64.cmlib_ImageThresh1_F32.cmlib_ImageZoom_BC_Fp_D64.cmlib_ImageZoom_BC_Fp.cmlib_ImageZoom_BL_Fp_D64.cmlib_ImageZoom_BL_Fp.cmlib_ImageAffineTable_F32nw_2.cJava_com_sun_medialib_mlib_Image_ColorConvert2_1FpJava_com_sun_medialib_mlib_Image_ConstDiv__Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_Thresh5__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3I_3IJava_com_sun_medialib_mlib_Image_MinFilter7x7Java_com_sun_medialib_mlib_Image_Not__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_GradientMxNJava_com_sun_medialib_mlib_Image_ConvMxN_1FpJava_com_sun_medialib_mlib_Image_Gradient3x3Java_com_sun_medialib_mlib_Image_BlendJava_com_sun_medialib_mlib_Image_IFFT_12___3D_3DJava_com_sun_medialib_mlib_Image_Exp_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_MaxFilter7x7Java_com_sun_medialib_mlib_Image_MaxFilter5x5Java_com_sun_medialib_mlib_Image_Rotate180Java_com_sun_medialib_mlib_Image_SubsampleBinaryToGrayJava_com_sun_medialib_mlib_Image_SConv7x7_1FpJava_com_sun_medialib_mlib_Image_ConstAdd_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_Conv3x3Java_com_sun_medialib_mlib_Image_ConstMul__Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_ZoomTranslate_1FpJava_com_sun_medialib_mlib_Image_Abs__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Abs_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_ConvolveMxNJava_com_sun_medialib_mlib_Image_Xor__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_PolynomialWarp2_1FpJava_com_sun_medialib_mlib_Image_Conv7x7Java_com_sun_medialib_mlib_Image_MinFilter5x5Java_com_sun_medialib_mlib_Image_ColorConvert1Java_com_sun_medialib_mlib_Image_Add_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Gradient3x3_1FpJava_com_sun_medialib_mlib_Image_Erode8Java_com_sun_medialib_mlib_Image_MinFilter3x3Java_com_sun_medialib_mlib_Image_Log_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_ConstSub__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_FilteredSubsample_1FpJava_com_sun_medialib_mlib_Image_Log__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Mul_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_SConvKernelConvertJava_com_sun_medialib_mlib_Image_Scale_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3DJava_com_sun_medialib_mlib_Image_Erode4_1FpJava_com_sun_medialib_mlib_Image_Add_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Erode8_1FpJava_com_sun_medialib_mlib_Image_ReformatJava_com_sun_medialib_mlib_Image_Rotate90Java_com_sun_medialib_mlib_Image_Dilate4Java_com_sun_medialib_mlib_Image_SubsampleAverage_1FpJava_com_sun_medialib_mlib_Image_FFT_12___3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_SobelJava_com_sun_medialib_mlib_Image_ColorTrue2IndexInitJava_com_sun_medialib_mlib_Image_Invert_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Max__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_PolynomialWarp_1FpJava_com_sun_medialib_mlib_Image_ColorTrue2IndexJava_com_sun_medialib_mlib_Image_ConstAdd__Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_Blend1_1FpJava_com_sun_medialib_mlib_Image_Conv4x4SUNW_1.1.1_01sinf@@GLIBC_2.0Java_com_sun_medialib_mlib_Image_PolynomialWarp2Java_com_sun_medialib_mlib_Image_AffineTable2_1FpJava_com_sun_medialib_mlib_Image_SubsampleAverageJava_com_sun_medialib_mlib_Image_Rotate270_1FpJava_com_sun_medialib_mlib_Image_And__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_BlendMulti_1FpJava_com_sun_medialib_mlib_Image_Erode4Java_com_sun_medialib_mlib_Image_ExtremaLocations_1FpJava_com_sun_medialib_mlib_Image_PolynomialWarpTable2_1FpJava_com_sun_medialib_mlib_Image_IFFT_12___3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_Max_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_initIDsJava_com_sun_medialib_mlib_Image_GradientMxN_1FpJava_com_sun_medialib_mlib_Image_Thresh4_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_Dilate8_1FpJava_com_sun_medialib_mlib_Image_Conv7x7_1FpJava_com_sun_medialib_mlib_Image_Div_1FpJava_com_sun_medialib_mlib_Image_FlipXJava_com_sun_medialib_mlib_Image_ConstAdd__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_Thresh2_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3DJava_com_sun_medialib_mlib_Image_MedianFilterMxNJava_com_sun_medialib_mlib_Image_AffineJava_com_sun_medialib_mlib_Image_Scale2__Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3DJava_com_sun_medialib_mlib_Image_Mul_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Thresh1_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3D_3DJava_com_sun_medialib_mlib_Image_Thresh1__Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3I_3IJava_com_sun_medialib_mlib_Image_GridWarp_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3F_3FDDIIIIIIIIJava_com_sun_medialib_mlib_Image_ZoomTranslateTableJava_com_sun_medialib_mlib_Image_IFFT_11___3D_3DJava_com_sun_medialib_mlib_Image_Or__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Conv3x3_1FpJava_com_sun_medialib_mlib_Image_Abs_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_ColorErrorDiffusionMxNexpf@@GLIBC_2.0Java_com_sun_medialib_mlib_Image_MaxFilter5x5_1FpJava_com_sun_medialib_mlib_Image_Exp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_And__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_GridWarpTable_1FpJava_com_sun_medialib_mlib_Image_FlipY_1FpJava_com_sun_medialib_mlib_Image_Log_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_SConv7x7Java_com_sun_medialib_mlib_Image_Affine2Java_com_sun_medialib_mlib_Image_Exp__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_FlipAntiDiagJava_com_sun_medialib_mlib_Image_Or__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_GridWarpTable2Java_com_sun_medialib_mlib_Image_ZoomTranslateToGraySUNW_1.1Java_com_sun_medialib_mlib_Image_Conv5x5Java_com_sun_medialib_mlib_Image_Blend2Java_com_sun_medialib_mlib_Image_ClearJava_com_sun_medialib_mlib_Image_getVersionmemmove@@GLIBC_2.0Java_com_sun_medialib_mlib_Image_Scale2__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3DJava_com_sun_medialib_mlib_Image_DataTypeConvertJava_com_sun_medialib_mlib_Image_ConvolveMxN_1FpJava_com_sun_medialib_mlib_Image_ColorOrderedDither8x8Java_com_sun_medialib_mlib_Image_ColorOrderedDitherMxNJava_com_sun_medialib_mlib_Image_ConstDiv__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3DSUNW_1.0.1Java_com_sun_medialib_mlib_Image_ConstXor__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_Thresh2_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3DJava_com_sun_medialib_mlib_Image_Histogram2Java_com_sun_medialib_mlib_Image_Add__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_ExtremaLocationsJava_com_sun_medialib_mlib_Image_Thresh3__Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3IJava_com_sun_medialib_mlib_Image_Thresh4_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_ConstOr__Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_ConstOr__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_Dilate4_1FpJava_com_sun_medialib_mlib_Image_Blend1Java_com_sun_medialib_mlib_Image_Conv5x5_1FpJava_com_sun_medialib_mlib_Image_Exp_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Invert_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Rotate270Java_com_sun_medialib_mlib_Image_ConstSub__Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_SConv5x5Java_com_sun_medialib_mlib_Image_PolynomialWarpJava_com_sun_medialib_mlib_Image_MaxFilter7x7_1FpJava_com_sun_medialib_mlib_Image_MedianFilter7x7Java_com_sun_medialib_mlib_Image_Extrema2Java_com_sun_medialib_mlib_Image_LookUp2Java_com_sun_medialib_mlib_Image_ConstSub_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3DSUNW_1.1.2Java_com_sun_medialib_mlib_Image_Thresh4__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3I_3I_3IJava_com_sun_medialib_mlib_Image_Extrema2_1FpJava_com_sun_medialib_mlib_Image_Log__Lcom_sun_medialib_mlib_mediaLibImage_2strstr@@GLIBC_2.0Java_com_sun_medialib_mlib_Image_Dilate8Java_com_sun_medialib_mlib_Image_MedianFilterMxN_1FpJava_com_sun_medialib_mlib_Image_Affine_1FpJava_com_sun_medialib_mlib_Image_MulShift__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2IJava_com_sun_medialib_mlib_Image_ConstAnd__Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_AffineTableJava_com_sun_medialib_mlib_Image_FFT_12___3D_3DJava_com_sun_medialib_mlib_Image_Thresh5__Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3I_3IJava_com_sun_medialib_mlib_Image_GridWarp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3F_3FDDIIIIIIIIJava_com_sun_medialib_mlib_Image_Thresh3_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3DJava_com_sun_medialib_mlib_Image_FourierTransformJava_com_sun_medialib_mlib_Image_BlendMultiJava_com_sun_medialib_mlib_Image_Sobel_1FpJava_com_sun_medialib_mlib_Image_Min_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_MeanJava_com_sun_medialib_mlib_Image_Abs__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Conv4x4_1FpJava_com_sun_medialib_mlib_Image_FlipYJava_com_sun_medialib_mlib_Image_FFT_13___3D_3DJava_com_sun_medialib_mlib_Image_MaxFilter3x3Java_com_sun_medialib_mlib_Image_Min_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_FilteredSubsampleJava_com_sun_medialib_mlib_Image_Max_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_GridWarp2_1FpJava_com_sun_medialib_mlib_Image_SConv3x3_1FpJava_com_sun_medialib_mlib_Image_MinFilter5x5_1FpJava_com_sun_medialib_mlib_Image_ConstSub_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_Thresh3__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3IJava_com_sun_medialib_mlib_Image_MedianFilter3x3Java_com_sun_medialib_mlib_Image_Clear_1FpJava_com_sun_medialib_mlib_Image_Min__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Max__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_PolynomialWarpTable2Java_com_sun_medialib_mlib_Image_MedianFilter5x5_1FpJava_com_sun_medialib_mlib_Image_FlipX_1FpJava_com_sun_medialib_mlib_Image_Rotate90_1FpJava_com_sun_medialib_mlib_Image_Thresh4__Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3I_3I_3Isqrtf@@GLIBC_2.0realloc@@GLIBC_2.0strcat@@GLIBC_2.0Java_com_sun_medialib_mlib_Image_ConstDiv_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_ConstDiv_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_Invert__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Add__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_DivShiftJava_com_sun_medialib_mlib_Image_FlipMainDiagJava_com_sun_medialib_mlib_Image_initinterptableIDsJava_com_sun_medialib_mlib_Image_Thresh2__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3IJava_com_sun_medialib_mlib_Image_Invert__Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Submemcpy@@GLIBC_2.0Java_com_sun_medialib_mlib_Image_MinFilter7x7_1Fpsqrt@@GLIBC_2.0Java_com_sun_medialib_mlib_Image_CopyJava_com_sun_medialib_mlib_Image_ColorDitherInitJava_com_sun_medialib_mlib_Image_IFFT_13___3D_3DJava_com_sun_medialib_mlib_Image_ConstMul_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_Not__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_MinFilter3x3_1FpJava_com_sun_medialib_mlib_Image_Thresh1__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3I_3IJava_com_sun_medialib_mlib_Image_Conv2x2_1FpJava_com_sun_medialib_mlib_Image_MedianFilter5x5Java_com_sun_medialib_mlib_Image_Scale_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3DSUNW_1.0memalign@@GLIBC_2.0Java_com_sun_medialib_mlib_Image_ChannelInsertJava_com_sun_medialib_mlib_Image_ColorConvert2Java_com_sun_medialib_mlib_Image_Thresh5_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3D_3DJava_com_sun_medialib_mlib_Image_ZoomTranslateJava_com_sun_medialib_mlib_Image_PolynomialWarpTablecosf@@GLIBC_2.0Java_com_sun_medialib_mlib_Image_PolynomialWarpTable_1FpJava_com_sun_medialib_mlib_Image_FFT_11___3D_3DJava_com_sun_medialib_mlib_Image_ConstAdd_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_Sub_1FpJava_com_sun_medialib_mlib_Image_GridWarpTable2_1FpJava_com_sun_medialib_mlib_Image_AffineTable2free@@GLIBC_2.0Java_com_sun_medialib_mlib_Image_GridWarp2Java_com_sun_medialib_mlib_Image_FlipAntiDiag_1FpJava_com_sun_medialib_mlib_Image_Blend_1FpJava_com_sun_medialib_mlib_Image_ColorErrorDiffusion3x3memset@@GLIBC_2.0Java_com_sun_medialib_mlib_Image_Blend2_1FpJava_com_sun_medialib_mlib_Image_ColorConvert1_1FpJava_com_sun_medialib_mlib_Image_Thresh5_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3D_3DJava_com_sun_medialib_mlib_Image_initcolormapIDsJava_com_sun_medialib_mlib_Image_MedianFilter3x3_1FpJava_com_sun_medialib_mlib_Image_ChannelExtractJava_com_sun_medialib_mlib_Image_Rotate180_1FpJava_com_sun_medialib_mlib_Image_ConvMxNJava_com_sun_medialib_mlib_Image_GridWarpTableJava_com_sun_medialib_mlib_Image_FFT_13___3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_Conv2x2Java_com_sun_medialib_mlib_Image_Thresh2__Lcom_sun_medialib_mlib_mediaLibImage_2_3I_3IJava_com_sun_medialib_mlib_Image_SConv5x5_1FpJava_com_sun_medialib_mlib_Image_ConstXor__Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_ConstMul_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3DJava_com_sun_medialib_mlib_Image_ZoomTranslateTable_1FpJava_com_sun_medialib_mlib_Image_FlipMainDiag_1FpJava_com_sun_medialib_mlib_Image_Mean_1FpJava_com_sun_medialib_mlib_Image_Xor__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_Thresh1_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3D_3DJava_com_sun_medialib_mlib_Image_IFFT_11___3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_ConstAnd__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3IJava_com_sun_medialib_mlib_Image_MedianFilter7x7_1FpJava_com_sun_medialib_mlib_Image_SConv3x3log@@GLIBC_2.0Java_com_sun_medialib_mlib_Image_Min__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Java_com_sun_medialib_mlib_Image_MaxFilter3x3_1FpJava_com_sun_medialib_mlib_Image_AffineTable_1FpJava_com_sun_medialib_mlib_Image_ConvKernelConvertJava_com_sun_medialib_mlib_Image_Affine2_1FpJava_com_sun_medialib_mlib_Image_FFT_11___3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_IFFT_13___3D_3D_3D_3DJava_com_sun_medialib_mlib_Image_ConstMul__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2_3Dstrcpy@@GLIBC_2.0Java_com_sun_medialib_mlib_Image_Thresh3_1Fp__Lcom_sun_medialib_mlib_mediaLibImage_2_3D_3DJava_com_sun_medialib_mlib_Image_MulShift__Lcom_sun_medialib_mlib_mediaLibImage_2Lcom_sun_medialib_mlib_mediaLibImage_2Ijai-core-1.1.4/src/share/mediaLib/mlibwrapper_jai.jar0000644000175000017500000013054510473721517022343 0ustar mathieumathieuPK5 META-INF/PKPK5META-INF/MANIFEST.MF}0&}5FԉM02aK9K蕴eE`\n } j/E٣.yP0 ;:s.գ$uwRؔCAƻ06@FmmߡE_H Q4 q>%z7#JI'PKy0PK4%com/sun/medialib/mlib/JAIUtil$1.classePJ@=Wjmo**Һhp]JA-T'$WR\~xS:0= ET˨XG†m ; +!f7[O ^2 Ձ.}{E޸IXY6a RZJ+;/+Zءh[B~?PK=PK4%com/sun/medialib/mlib/JAIUtil$3.class}RmOA~B8 p(``LbR IE?lkv"'?QĹRb%7s;7<ϋ?lᑃiܴQrE lv;➅ gR9CZ{0ylS*qFoR$SP֚]~ν gpZqRfi"í, \BJYJ,x `DRÑֱfXD[?j })DƪnaZaa9#Ϥz#9RBawsɈ 2{'ZS_>2jZ}a&95}Cj%Z~6ܵZl<3LwDrYð1Cӂxc*kA lDTL7j^$ U&O7zl~&;Q Yq81@3 w%LxX}֐ s'8UPtg. ɳ=>Z2 1<"t%Ͻ́fr_sl:]jry="zg}z44\ }7oPSay @~8PUi^]?y4|ȶq%"u4y?[[?D 4*he+ŸipHnIB,BUDIK?=vmr*^AzFb+NwQ|>}`3II$%[ a|dZAuYPKPK4#com/sun/medialib/mlib/Image$1.class}Rn@=<-%Pq,*!HBA R7h ?S >Aڤ*K={|lu,8EPְcETK4\a(ޕL1j +f2{ w| d=~m 7f0:@L3V=HLγ&b]55VNØ'2~.E_*)r R~_W'2 ok0q7LDFB;Y1&V 81Nˉ;P2ڏ<<ѽ連a:8?T2,0(z۩)<+⸺yfP;2MFjn1L{"°>IЉZs)xPizW.=)itSvw`o:=Kk1 ZhqCSp45B.?B5v86~dLq4KD3 wXWX߰h RV9f(PKO PK4#com/sun/medialib/mlib/Image$2.classTOW=ey B|4iiBizpkP\z\C(ȇ*)5RzUg 6Ԓg7f?/~Ћ ѡ N]#B >~{b2.+° 1 Weƽ2&dLʘq]ƴ2>𡄏%DWMt1:o3qP1-ͤb¾ǒd)3CM3oZR\lC؆azLJO^M9HULK3=i:y|PLkuafh3f]tm1 *qӻ'nX§*Pymcj vIXQVSI3k"sw& :b gvrttMV|9mizČ}e뛛žgML[# qHK 37 ]Op=As'lSռZv^mT TRwAWvI02nkEcO_AaBr͔ljpE$ʙE>qO nZ€y;m+ʔ32pT|b*Qб7>9ʄ{tG*F2툢c RJw^Sk{Jqж *,2l@RukM[_ZRLh2_} lD1QhA-?m֠yYw$}W'DYd%=/_~Hwq*k@x;D|=T.=wa0=,k@.o?7#xLJ_|q1ˏv|DZ4PK7*DPK4#com/sun/medialib/mlib/Image$3.classePJ@=WbZJ EpYX(-TO!LI&2WR\~xS:0}O=:(n`M ,2T憡>1L0R,~~DLQga{WE` 4t 2W:\X$) G.q K3b&y$}/ay~wЃHm+*AYz-4@(f3]F PN6KίmFK)DYe.mE-z(Ҹ(_"!d펰 w-mfŗ oװO!?PKPY;PK4#com/sun/medialib/mlib/Image$4.classePJ@=WjZJ EqW *'$L +]).?J)]hsg'>mѨic - ,3T.抡=2L04R4~^HLQa!W{ͅo 4N/ndGD?9Peϵd!~IH$F9,3O/z$ Cs-CgI%O4;rAbv?9,1"bLt,κ-U0pGf6զH||p[VZw_ִc*,_.ESPKm<PK4#com/sun/medialib/mlib/Image$5.class}RQOAq V")lQ{!I&UInKڻ6{W7d?e+%Uldwv'',ظf"e:-ei +WMdLe`mWX.!iHي~ϫKJ0WZX8m7tiW>SQUDS0 VZKcwqaePGJ6H=;aѓ %*InWWvC(pPQ}CxNA A tg2$}_ݶ0],\u ݞVRc*)O݈L40*>eXj܉`y^2"#[P.m3L5ex°1׈jcPX4 " Mmz-Mwo:}ҙag1Dx/V}}ɩ{yE AKdC`@X dxY.ΏP X #?PK$?1qPKs3#com/sun/medialib/mlib/Image$6.class}R]OA=C[vYV[n+`L * /d.n+5~_?xb$3w{νs@l\31c:f-yY M,X6p G |%Cr;I~ӫJ\TI0/TNDK8 םF[ AԮ|HkuYlpL0^CUO:3yc.1, 2C5RzG@3zDEUjqz*Ι+O#6lq]YgȺMĉqNWwn$yn0!ŢC63ZT~]1?vIa*n3LA_ߊ`y^2 s#--o3et°:׀CPX8 "Cr6 &%KEj;7ĝBhoj/3RfD*X=7ڥ<,!GHCxD7y 3 %9+=0nFiPKqҠqPKmW-#com/sun/medialib/mlib/Image$7.class}RjA=$vkXj-T) Q })1Ln6_>| QDAĻiB ]ع3{{|bu \X԰#m kyYNJU 5i0@2|_2wZ50SxPϹӤLLu|\Gnrn?u j\XFFuQL\ 5s5L䱮ᖉ`Ȍn|<+)jR-ŰQjP{ &lMpAdH-:ݯ!} A MU(vgJQuQ{F4a tũ1Gqee5TүoG3XVeܜu^\.Y^t;Y\MZi'gM#5jVNY]4{[bǚbu E>  C^ zӔھu1eORw=r@Y r'\hHMJn;_<^$MdMdM>1z-QyyߘUԫ2ynJT Y('Y&HT,d!|p*1:zmGvcSs۩Tv}n;>fdQ7q\Gcqq)رhQ*oUӫlyDJJExah7% k+EJ6o ʬ4noM^I-~Q7dJIz_yrJИ)Ov؎dJ ૘ZL@qd ~dڟ+Z>#YNjv?au 4zF5tw@dEjK*ئ^$J]h h:&T\3H>;+~+tzrbASn#CTRV9& 6Rii=yQG-2*sfi7l '|^t"oBNdk6/eI/k&cX-;&GF,/`sZ-wKeq|9va#W.ny5iDZY(X0"{Nг0˪>AX8M.Uxzs1&L/Wk⸇@!ɹ7ۯk:| PUp f.99±p$7zĄZSU{$rhh5ynx J8^=kOb]~1W#XI:B_k7";K. iD=Rt.+XI.δt*#[xSlFKRάd\Q&f44iJk=F&CW1|3}hi^1+{4E'pv:M渴&w]ْ:zV8rr6尞X1.DMWhh>'@m8[ʕv*C u6 }lg]B`'cw .͘=q\˸2} !gA!u /f4!˘qFc2ΘoK-퇥yB =.#92++OQi?*bIQigI}зww -R_. W #*Hk_'z7P}B'~AƇaGM (f>*V?c$og|\OO!Nx#{ ? ~@DŽ>.qg=)sB ! ~D/?*bk0'N/|JyJП3 E/I|Iej;}Uڿ*_3~- a.|E_M?`BH _ߌ?}'SƟ 3RWk0~QmE%uz|BAC0F1l3. Y>Z>"IIve1!xθ`Ec"N S)#e*aPyDB :pbxh>[֥?ƥ^{^KEaht@>UUJ9_0CI1#3c8{,cp B4H,ġWGR&miQX~$:MS=͉ӏG ޜ8 Ykg,8 9ÁĹ\^8ٶGaWyB̜P?q,\DHS ]CP%z;¥sp@r<YX3D b.gi[ka>1X  pq72T73FS4-\Oc=xǧ`LR,E4!·r>ϑ:'HxhIX Jf{sFa¢-9tjap &9d.Ŏ&s-Ns}ݥyXuR0E%(st z0v*VڭstPݺ:@~?>TwG_<f<<%oV+l֑B.AQQQ.VS +՝J( .M2KI]_ABaGG% mOOJ|xJsJ<4/%|Q;.%^AC߇Q~$wM~/YB,~E~CG={_COzh*ggt4$\P}Q6qbv=׀Q}I2O?#?7%ޖ%DvcI2y%#_ChD߷]u'hC}t>@C| SgQ_ /Wk wG?cI|YPKXPK91-com/sun/medialib/mlib/mediaLibException.classSkoG=Nɣ!&MKp6< $:u@xnjS{ Ѫj%cHTGU= 8^sg{gzZ Z0܈nH<}zBbRbJbZbF".1+qNbNb^bAbQ|E,X>\Xe_HXh(jTpDBDFw7K*k߳c9R^qsw\[\[.d@KqF9ūv:G#z|u R=:HflO8\$U(wuƱN҃m9w0LXɨD~홰0r<#'ȩ0r: #a@GCٱPvM\1-DyկbygGMpU\~B>V*@5c{}PLr:k疊r^ޑp[lףab,!09^|9ľm3 0dӛ ޞv3Ckэ>C /Z`a/o`n 79 u(Th@pPa)DXIu90I&J}CCSZ82"qOiSUרyk UFŸ0v{{GH>ÚH\Tb\ +8ثZݪخ>!6+E.im:"'ȴ/ȷas^:6WxmCJ:&6˻4nAwd\}瓇G>{|Š_ozΪ?PfX/4b)x?VC@D 5/PKfDrPKT39com/sun/medialib/mlib/mediaLibExceptionStrings.properties_k0Sõݦ6*˗!Os4i=$wgsxm"yuk`EЅ:gX댣 'т"W#zM]~z3&/$H֬' ֡aVdE9WEyUpB%+IdaB#4-l48 yBȸ6Ž+> 8Sߘ W乱CM +vX$iupcPim">R=ҡQ.A|zb'lN̥ȧ\qHPpI/1(wy}z-! :2_x`D~ll=n$c#}暸(RkjOl:g=/~9+ۗ.Чwi|w2ojcܿ=&Ǟ_ݫ<? PKCmPK81(com/sun/medialib/mlib/mediaLibI18N.classuPJ@=i6FU"v!mwBŅ V'P$itěic9s_o0- ETJZ5T29& !ғJk)D2H&R{ػS#wFvۿ)5I eVzҿ88;چ șтPNa.?m"c5+;5ac K Mvvn75ܥ?ABhh*Md^$UŮ?]^X6GX<905` /36'~wuOPK_x,PK1}~3)com/sun/medialib/mlib/mediaLibImage.class| xo\B@Ha)d,HPIH.I0 d TԊR,bӢT(ֵjPqZΙ777Q[}>Oμs̙;^|G(ݸDĺC1Ĥ3/.L2$Iw& L$1H2^Lz2 s1$S]$,& a20.#(KfL0dLadL^jLmSL4$SL6ŹnS%rݨSLXk|7ƋLſZӹR`B.4eLQ1&[.Džn1W0)e2Ir&y)*(䲊K,pXE5 ՘֍y<B.2E%X"hbb&K,Q'Yr)WHe\L3UL6 S45V@kxa@@L*Kkk J2PUQH U$h͟ k^UcY+/m,]PtPuimŠϦkJIZXZ^^U[A9HAAFܼ3:d646,nQ NRɟ!E¢5({nv~ԬlVHse-8@t hrNsVSf7-`r^v~QF.O͟1{\AzA,Yy`YuB>75kJO.z 6M49fA3 r^)Sg  erbO>/;tL,(.ȥ٨rꄉBhTdiSf80XE);mR]:g7=gO(83=ĦG)~"Ѵ1UUbSZ/oLUǎ16T3/P_T::"\U7(: &.lj,l&w2xy@Bd_[dD['lf~lA~t9mJtͰ1P4峻 jhT(*7FV͛}עʬ'JS%˲Z2z2j6V}St&fEHz2C3C¢,;Iq'I<zPrSx,?xi$ 4{HL1`Xiae] )B;# &׆L{TוqsYuMd6 rbVj/i۠\ -+R$Q 4S&rMbW?-!1uL䜢L ~FI*[A0> ]{i"j 48&bM$rF.q P`u杙ϤߨgcLWV((@/pªe@ˡ)eN$Nºil5@h Ń+܃+\j&+dr qi#ML63x>&O t SC[:Vq_=x/ztVz<8? 9zp_eg&o2y #V)ΪHCe2QN vS:#<4w;=Q&p[_|G8xOL&3aB9oKz}5iBDE tm-%P_[Z]__W73ƒأE&zZ]KQBp ;հl.4d ?y>-fA.3j 3G3I =yIeЮR49lGA'P*h *􈟓{ -EiP' Vw3?q|[ZZr;:<+SÜ'9O@h24wv+aLfS}}eMRqicrUCr-XuuE(c?R1 }Z-(u+}vxn_y:'9O0=xc%&hL}-l_0zwOJᝃ+. Գ2cS4֐GɅC4i+,騷kՄ7Y>5x$c<͜J))e Q/ -+T9g}Q}}0\]|~orܐ\ PD>>5yX]agkxquB+nZ{Fiò7avy uMiBXZ0[eP*.ex+*)W8J)i_*N5q#~'07WS,~ V_K_omauT_V%ol5G )79f)Ae<~Eu%a5!o¸[økC{¸qׅƽ/Ľ?-} `P0wGwg0!0wa=7P؋Lj>N?@6)uD&jjIivۅ>jUX? $xT:Hv< ߺ=A4&w  P$b`:.L)Nxrkg)w )*kއ[bk ^/xkHo|&5oRЉZF.()[QJ8s#xS|w'Q|-0E\%~/1FYe"R+Z1Zئn%J ).8cnth@A he+ÑD2 1XJe(q'[dbV&e>k%GD)«L^+.mltjzr ˕+VK,+",1,0?Ȉu#lo[.*&g陵.w`c !R 1Re&&+ŘlBT*sq2+eT TY_*5C%Ց?nqM8{}&{=d=ΞMM.&\B6lrd9dd%:*ɍdMn&B69d-i:Dzt!`GW L_ڮ)kj}]Q=RAnWԈ}튚O=ܮ)ʗ7FrF|+D;o}xQW[DD;=qm[4UT>@G_Ӽ>ܟqwmiNb/鯇k_UabT^PNN?8X:!VLc8Zoۿ'M &ЛӨ:D܏pVGQ q?'@1j/3G4c"9u{8|l;nE_f߽'1).\jAV%wD.dżZQ˰=O RW`zj5r*;SA{O|vLNSrΕ!\H L>\t"~I o@ ջ-HVF/7@=EUzr1]݆ 1_}nu X%n\7{V} ['p$vO} g,^Wq\} /K8Q!&"Y=,~[cb~ F T?iu'N˻0,7/-nvi5ԝJMU&`T$ԾʗH~]i_[N^#^tjCQՐv0aO >-ș;2KtZ{5UE׀X4ɚ )gip<-ZgҼ8_Jjx4jX'.zcI- <͇G͏W!xMwxW!2hh WD'o" D],Ԃi^~///6ॳ$vEtz[!ۈ=M̈z5 <( oy-BdwoFz|wHcFkA㵽~7 =D/brGGO0 ihm6&hspv!fhPXZ%nѪxHcZ kxV[?i 85s* ][""]"zhW^evi@m k׊*1^^Lnp/B?'8N{_[?fe߅bfَ󝝝~[5OM:яQT;ɼGmbwRlm`n5b>\=mJ 崋p<<|/rtw)ۅyi;PJ7L-| ]B'm`/}=.v ӡh?fX|ScX4rF pz.HG t^#^{z=7[ EiaTn~^.TN~~m"#Z%­!HqrqFю>D3N >EhCwi}NۯhrПHC-3o.qZH^+|HK7_Kj/{C6xsaFlFL+pm7gr(SWkH t0BH=Ezf]1[Oz<./ӓpXp)آn=-«v,9{%j/siLYZɵcL;Vҥ9[+==g|u崶X#9y6s,s<3KQdԧ|sEdbL\XF}.nKU/=z=o{Jwj:%N?A߀ 01H:%XDsK8/շOsI~3״?{;9\&8~*-.=þ+V9 Gc*GNՌT<- yѷd] [ߋȍL? ),b,yr/b6/Cح?c&oMm'|kZ+.Ke9pY.3.u쟇 ,Se>18F&,L5&Ș )(3rQaa1 K(µ d:c6a1cQǍR7Qr2xØ 0*w _ ߷FuC-'`$X.,j V7`?ʒPY@GP'TT TT/T/TT?TT& ToTTG18AA!AAuG j|P=@ɆJt6q*[33<fF0#,Ts̙,F9,2/Rs.2Z 7Xgќf4`Y:7⠹/8d6 M8a.\" s€S<>Q蜬^5\l u:6tpd:2鯀JaC/c1$qRj ~I`u V9X?1&>ik/lI%Y.5|:%üyb+5BOsrkPl^9u*7o@y#̛ 5Xak̵ż Xd; ;imE0l!ߍWvB9W ݉}=Q< ڰluÇ˗ndހe1䗾++._mosUlPC2]m/㼒OqTi$cֹK&sXnfENt4Ds|U>,42ک i5Yߙ x|LM8[ߎ&6?>4_#N6x0R̷0|L2E>yQ1?ĥGl VRBL_~mh&%!|-l~Ib_LIVȷ|<+M̳ej  fS^Z~22(1xTZXlMr+ 7Yٸ͚$kg|bMkteShXO&7mlKB}JtBs0$F! D'd:C&gҤ\z-4[.)͑fV}}|ii#b`u XI|S[Gtsd1n;/H$AU2«Cz͎cq_Z%Hauj{k!YjVbᵖ"Z&[,R1úL̷kbuۺNcX׋Ǭ3֍2^g:T}aq) ~DL{+azayIfn-bݍf~笭=xǺ'Dgmi1I9bnIrI)qffÙr%g Nn QiNIsHCҝ!!g;3q>}IHs)qݩNlU,Rp.]zw7]>:?K/7oeo9Y鐽si[M[M\k^KzKz;i2QQpZ$#@4ZuUzCt$}[=:51:.>7YRBHlNǭK֧J%JmSIVZRY(J ]ΑtsKۉS#"2"z]ʌbln]kuKO-Kߑ]Q0\FWW :aˋ).(ru;;tSФDdsWDz+=]MBq_v,#u6XL3r4@*4$ґDxDgadߞVOZ4pFkk;i84dL\04U4}* 3HEt[>߁lcT%#PKF`#RPK;1/com/sun/medialib/mlib/mediaLibImageCanvas.classkpzZKayȲ&"`681ؼk-e% 8'mސ>IHLgZO$LcS&ׇNI2mCC'2qwWq3C{ιw=wO~8PolZQ]*vUJmR#R'*ࠔla)Sl+0TC>DeS`*T#.$TIt&)HK=Rl dJiQя<"Ǘc<+ߘ ,oս}K݉ȧDGc҈j>ݝ$(SYIOIJ]Qa&]Yw5ʚNn#n8aFTsoHMдVYOYDٗ03{n읞;L:ČiOT+vEO͙R,rfl\luCJD{-UWHz̈́K%4 *I3՛H82fɘ1{L^]FP'F&fSZiL9{^0K-ݗrkLdiNh5y Zc VFÀHq<>D|EW񤆕Xa3TGgd,.,G 4Ì6->ypQ*,l&ә~kv EuR풆54S1LkM\lf-^Ѐe/kxPJatҨ ;*^UA=;6y\e$goT~It6q3+Tc(@^(13KF!t'2iPvjBV~*3wR|QR2R)Z l=9l-Ir3&Xajluo4cjdŮL:53|ZbL%uYՌ5wPy;ϑ?7ܐ1͔—3/ck,)G"q:`MhrS[:z)!V¶f$6wG.L?uo\ 5|?u|:ʋHG!( W1 ]p^'~o=Xv/R8yyWsEn]Uڜ8}9|UC3KaWa | oC[Vj+ .ax2@ˬ`GF7FlĦpn*Gt|R9y Ӆ8Ѷ w5Kg XLUwN+ѭ\,J&O/z/\s|qB ]]/us]h҃O)Qw*3"-̌x7#+'x,]==9C⪘JPysJMu v7X=|iƯ;݃hOz[]r$ 7h{ PTQ2>h]GزO֒!ܷ[ztg! p+&̷CZ6Q6&cWQp0V!)$wa&Q1̇e"1\4 o-;EK.W G%E@h{[JĀߡkcPVd _‹_b~ǒ{7`}} =3+sγ#ƻNBL$'I|D"$ID :=…B!$1?&e}0OBI>Y(HgUz`<'ewN<有h #%#%/J?'8us`.]჋!xy\KE)f2,,sBJ1[BYD0KqR,CX?W <S}8ՄpVj©!pj g#l&-pg<@8 p g##&i$2.?2vVZ,Dn b!8rHZ!RRB\^_yH|Sʇ<&t!&)y-LRUꁁ@SK8Wykc b1&Vbj#=ĴKAbjG8BLǰAh#0D )ILqb“.S; ďPKpPK:11com/sun/medialib/mlib/mediaLibImageColormap.class|xUl9垒{osNH`7 H A vbQ,O˳<{<=ޛp} 33{fI8A`PL5Ȅw|0{(װ@GX(LE |)Z# ;Q|/DqPŏ $/UCހ*E+DABEDE!iD&BT4DÚ04(%B|O* j$TX* PHI(lUIF2IFIJrT+4$A%UI!58QH Wt%A ؼ˃j!xE? Q-( DQ("Q+Dԥ 顐cPUk%@FA yӪJT+=fNq86JS[U__`1E54̝I55M ո̂%M2ꚥaI3U-$@g"F}r`VSbܖVݰYv5vYC+VL5tʐ& nqubcCFO@J3#6~:ÝIz 5xܸcݛw& Q''">N]|z]Ӝڨjg }&4|C 6x0g7E S5q9ў8xnrGi6T ȑl]}͸% g4Nvx ;kJLk#{}u #} .]3ٙ w.{"3No8qq؆?3LjMP_P*ÔFw}$Ok]J}r8ڱ;h?mDÒƦZg,0ɍ5QŶB|jƺHgbMu:#ni~i7L8Cgyج$':StLmhl6cs^WtRCg؂ŢEzMt9Oi\lwq}k[XSart8;Ե'&5ŬavVTew zmWW14K&Qg`b.i^XWؓ$;H-.xJ&T5qvz73 !T; Ũv{6$2i3qҒ !{iU ꪫ?vNKKTa҅v!ZcfS<*v91hA)DUM&{Iz6AӦȳj6&隝?s_ؚ[j9 $t95"qpAɀ-ƀ0πZQԉ9^7p](%0>q$q؏1`h h7ZĜuހ $6 h(z4źF7̫i4HO؆ \)3T aM:]Ig3``TQCBzǟGxT?oȒsk1E /gcID0Q^^`. _:fQ:B3H2@^t<jrc8Omjpzh6: ᳚FqqȮgqxR-o];͓G|d/?`I/=qBCDVUR>Ac0sK+ILr[jbgFC9q5v]un=?ŭBw^֧53nr-qt΀O ˰}f\,l>bؾ ݌q }Q\{%/k_K۵l>+vrj7~U9Z}"=QHANE;^;ADrij9 Z]Hb4 w{ikϻh.yKc3O*'*j@m)߫+{qG|K֭"r`uEQ@w:ꄍP%K>^J%jR j^#fI$,:#Q"Y8'm{2QYO+D԰=%, K W5- -ͅ!3dօ!]U( U!Zj( 6"` Xs`rVGRC U,Z)P Գ/d⺩"Z7hY XF("WT 6kJ2+#Ų6[i8o+M!G8poȴB8`6 ZyyuΥZ7_뺣$W}B5_k9Z{G<;? 6;؀wmBѝQuj@"J ºppfX%oj `&Ǽzj ֯^#vRk{54հв"9bi"@hUDmT$-iA[x]<_ONӢxJ"'9q8E3+*Z5jml0tZ-Mȼ:* :bQG['L,-/f?2$ݹ+sE> -f⟺bU/ (6N.uFpQTwCm;ՅC5<<<P OA?x3P4ς"O<_c<ď_xO~ûp7$|a|?Dω _0|Eפ7|Od$?O,%K(Zp_O?I 9~NzӟI_FHHMzRғEH6 RƦ$2UAl>̚> g 2]CưXo#vRv lĞ"kd Letv 9Y|'"R/#*r BNb~?iO%5r,埒3d,,&JBrt)D+T@vUғj]r Y#r1 $\E?ϝKل!0RID sRQRRQŁ%"¢0nVG-y5`=Y"%hb"KjɊ*A\t`7WK*nSvdWG$wi1^]cOp#V{,9嫬Yj%@8k|m rv#<Ǒ}0<#0 S}PERr?IvC3y .%4p[QTPS@uL4c&Nb7Nk[EfG6eb6$  A| D>| _B|p<`"f`.9gVRrp#U`3UꃻlCuxI '#?gBWI?'M'e4LF 2fSL8f hhYK ZDnnZBaڃSyFK4/iiGI;C{2ڏ:OJ0:D๤LyI~ٯ8Vlt_Dw뒙b8K٬I$i%/nKN3䒈P"Z`{S(>ĂᠥYX ceb6^p6( % _X$9P|YdJm**}7t`sK˰n!M=R$qd 8[YFĬ`-peKZ"qW)WOm)78kȝn:AgxNk@P%2܍x=FLm "a!VW0h5?1YiCY\q!֙S'VK1(xF&h!AZ@pBf1-Z8^g1Gtfeľ],{}ڶKQ+ꑽGI˥N7tr1&ݚnn7O`g P^{`{*to/k{v>=#x}ٗ4^c[}DaIdI~!C!R@N,Ye+d :Dv yyyȗ"?d4i7ncAL:g }|;ħsgp ~2㕼͋|.ky7z7+b~_o|_ʟg ~|)FR=TZWIU~_#Ư7H5|z97-r~wW~7^y6fIfX")$𦮹 %f'RvT9P΁,$@ݿgɟDw?EnhSNrVv^j˞X3GPrqrkgtcIlk[g! ? ? wA  g`F` ?XƟ~./2lN*<߆&-7I*Hks45{sSYNj:?n6=9AƱS9^j6N*4WatԓY@uDs^ CEFrelY"5)(5nnR&GOb+$݄dU0Vƥ :4o%M󒳞2mغ.ʄ. ==몠: c*`Zr=mi#.:1sҳ9vz6M:l;=(=˿?A?B'?~]( L8̐$'ɰLJ).R:)k4*v) HWʄGlx\ʃ ^Tx_WF$I'A I %I;-BR/sbrԇ\##2A:H[HIǓǤAIi(yNN^FפRԃ|+CU'͕zX:\*'Ht4ΐ&Ji2+MCrTdf; u 7cEtwvݲ햝<`%D&6tq\ [WqZD"̈w[vmRw^I4{UdHT؋-d1G ȼ0t-(U;e-mI=ct`OvӀXns? c*N~FM6nٱ[vqܲ]\eG-;'rKi.HR-R)/-";KK-OC<r)[@btK-/E rt+-ny5ס[ny[.G<rk-ס[G܀nr薷[ގnyt;-Br[nF܂n&t˛-7[>n!tG-nt'-F_M"]&]m'&0?ꛙe.Ej~5ɷ_Mگ&jYsȓL7ݯQGɗ׎ݰSކa%;坉6:[ǩ?O8mˀѝ -z+9J{+gJ:>7=Iy't\:)ᾑ􈾑k]=r9jyZ} pDBmo#s7[m?<[mO3[mO;8|}).R(0E w{)ػHUDg}&6]$k x7]FHv"w!$yҿ1#}eG?!g0JI_cJ?p\ WJ[ev v2xD6q9 ^ ) _|/g9&L =K?<;M#r'SB ;!r1!rOHErY!':y0"!7ˣvy GKcx<)O!SL|9("Jj4E!9ht@ˋxT:Cnt|mO2L>+C/ϥWzUn+Bz|%_FW_*|}M-_C?/7Hgy)ә%odfI 166@ oeS;Ly;Yɪl|[*a+>J~]/`'i[~_`O/W]MQ~D^5y=ĺ &{_7|+UȷJ#94Q,rՕ hrP.hr mPK^\O/TVӫ5tޮl)7|e;)wqlMSvY<aLyT+*IZymTnU^g]ofO+b9(_tG@@p%?@ˋHxe A7P ^h G`\Ĵ s/;p6͌ .}=3Ù  gJΔ(<90j¨3mV'#^Ȏl/|dG>>y r0 g~@n(maa㱳vt ,ǍV9mLZ,sueGd'~\OQzύi?~nLs=e zύj?~nLscʳzNpL7 [k'h@e_\@1 a@wb@ a@oa*rT,UZU&UU.QM^MTSm5 ԎDV;څd]Ig9F-$ej1j)V{FLGV FJnVGhJqt:Pѹt@EUtZK7 j#}O=~IRϢ?`ݪd\:+Xz![^ɞSc׳젺n`F.۸“[y:xNQǪ;`~>J}ORglI^>+9h ls2k*4E:΂6lG;%!*o)&Dڣ~vEsQU~]b#};bK 5,w9In] y ,N-%=baWV;Hló|+];13J?ۣ xE-tQWĶ({{N,jO4D!A{&>xx8!>xx8+5>xx8'3>_C0pqCpLr]Ы޻ϸ-wA_o_lx{gA(@]<۠i i:(h)0HKZmf_@MB2tl^\ ĖN/ Ǐ+"Ƀ7@*NI9v=*٥l]v|ܝ4Iy# Ĩ jֆ[a},vibYy/z0wt/j/!keZC9Bf@|c}ć@ jݡV=z7 h-Z_0C@ hԆ2m8pv\ÕZ\ A)M wi=txDOh'sIv2U{Z|͆/^Z=ZiI74_wXҡIЩ@N@'b_ݎE P>=q `H Ŀ.T@YԈ%T&;bp{=XnL\do>hfvR}bNj9̈́nj71PmW+F.L'lmb57Ǡ .9 U?7k ?~r6ogX{}v.5Cvh+v)F`v%_av5mܢ{MW -h7 ov vFm$_Aiw!=dLv^Ҡ!M\m/Y=HVk]{<=J/'ghDO^S7_^/ժ kڇ>d$RBEM;$=8^W\8&"R#9$B2$N SH9dt$jB 7=P*Nx ^w9XK|LFNnoYz∀_ڈnI"g6Vl.wK[zViK}YEL񨌽!/.RĴ;~v n#X&F6k 2׷{ÇD H!#rΝ \0I,G~tQ UIz tZ:j3,nKyis~Khl#9=HXHԇ_^$ldnTe'|#e~R@HY2IAUD, %f IJǦH6ޟRaiH] [ʰ,QeZ!QeY)šR")VR"% )bY!"!+eob,,W+R*R,] ūKO@E X5P"y-g]sJ*Ls]yG0-X# fz`fTr[`MkjXSRꑨTwrd+TN,'ٳ$rZN=YNm9h3%T-,1᮶v**,?4vOf"[``LۘP~ͫܝX݅n'VPv79 ~]ߕac(|[cl/x ?5*cQwFe)*"bt2:ʴL+ˮZsη >Ő],Q$TL Z8%y%'fSј(QC5FcYRVm } Y-.auV]SI nM[W+kr\T)>JuʕIt+ ׺_41{<2ΈE oj/vT f: ^ns`/ xxyDn+rA $n$@;IhBD~;96[I~WNoζ'^IO}Xv=OlNo[?<Rċ49N>R"q+4@ $;&r3 ~3@pbz7$.x5g]d44 aH iF .tHt3|)/Y 0Rkaa뛠Q g[&T0o ߠE=Wǯ{ᠾIK$#9ä?Jd~'dԟ&>]H._!+Wɥkd:Vl$mS#)!y/Ogs*_Ѐпw~HW:M?DO%K N/7z-Ao3nO6~â0SF:}Ƞ/m#gd/\GOFGFL60ѝ%,dlu5JYOvѓ 7zla'e85Rc Į2l1m0FHv1m3Fی1.c{Ԙ7c{՘2f'kcQ93pը1'<ͨY)w5x4N|ćK4>Xn!3Iۍsry\YY`P όg%+|W1X*0ҍkuZzVkfFؤclV[jfmMɸEخcܦ=oܡ}cܩ=XNf(ۏ0PK9 i 7PoPK<14com/sun/medialib/mlib/mediaLibImageInterpTable.classV_lSenj;`+2 6 p`0A]ﶋ];1!7ߌ&2zTÒ~s_! O`@~pX?E?Qxɏ:>ㄎ:hՑґ1Dp3Q<=2Z p/ع_X&ғ.LvIBp|'|N~\e8Vi{b"P²Ȥ | :ǡ($YK]\#Vp阮>+a>k}Scikb >Vv;M߻A&exT3`O{a^&{1BJb1I~>RYu.>R[UD& ÒX]N$Ji<;]xhqzONTn$?}*lixwI,>I< &=$l2vNF\WUr+X%$ZY  G@25CT.etbL{!k)r hA#asf򶔹N~V~RF+ؕMR9\jOK{w_ `ܱ+ұO8vcڱkr{]Xn ek81nU[b}aG6Ҫr8㖊RW c{EV.].}q[E\cGE^.}[.qI2(K1>hO Re/( Ke|;+Hr]r鏌]]V& no!j SzS3t_Bt73W`u17ՍWQ}ި2qW,FF=3Fݲ #nz'cZ=< c:@fX;6Aq3PWa.#$V+nz|x̀Rw 9jkOgKFjͰPfa*`$R|qޮ)VTpF-"0s 7(A1$ Gq(5PUA,))N|`:N|}^o˯yɅUF+$/ȏ$x(PR-ާ:̐Oi Q?Q#~V/wi%i|D!ZMubl c6ZN :^m4mQL9BޠNzUF;:=*( ƓY-v]k0uΎ~w";F-D?5 ล[,nwrW[%F9;2nMWo\Y\RmrrO?SVyMm=^0f(jšZդjr o ͡:HH|nzi/R2k2(ȿrn51E G:40Fz(^㘦4ޤ!G>a|L#lL+e'oS&ȤI)3Yh/J* PKw ] PK<1/com/sun/medialib/mlib/mediaLibImageJPanel.class{pTWǿ'7 f44 $GH A&{,dwf,bVkUK htt@ґ8:UG?uq:>kܻ&A}o}Tr ;TbvIiڤ.= >q@T9t8]pDG* )Q`*V1=RWAT1CbrX4/I) !`P'T)i? bX]X`2fFF_2f 93zZ5ޒK$cF@l&SYE#^$ўޔ;7R@A1Qg{*[;])G`pVQpnIDLs4nuv+{fg2#eZ/|SժYf3`2|F4X"nƉonfLU6&h׀eG‹ ڢM4>3Dܹ3ivɤIQݿ:nF6n͎_G?'yB㶖 ^o=ږLv Qy󧅰m7ja{ qOHJJ1T G?1 JҰk|J򧥝'<"xDTFBCId[x-  OI -f,L[i tI5|,|N/Y ¸!4hhC_԰UJMhN4lC5 ^p m*NFYfa׿Le/r00v({+d˜R3+S+IӪ:;Yq&7`՞g nV-Q٘sG3_mGJٮ*8Sz3 v)=ѯ6 =r]:e6#-3Xg^2+u}enuE@>ehҦ>4Nf VJzk瀙ޕ=@ʌ"t٧#K%,lX^`U}Z>\]GNlpun9+ @5Qx_:ᤔG ,˚8]:8d]_.c~K7cQT`A# t2Ye=/kGV`d\Y3CS —YYN }rum!.lil:I50FxnfMf\J.7Y0Q7s 촅Kw9EtŮa@'1̩qۻ Ê-aw1OWt揢eDFq=-b{Z4Ҹ_w:U]̕@HZ)"+ț\Y2îCcXjYķLlsƖ~,X1mҽӕjZ=-I+>_Ykr+fƋXqnVA uuutW0?GocI*xg[8w{{p^ Z<)Sb;{%,.G $q_pM1^auؒ:z*;'DF]LsNYaW}22:>GXANݽG~G8omupCyԖs)2Z {u N܀'#uߐO֡<1wyׇ4A?+dOp7 9Cx#c^c OƆq,_g:WƯyv"~Ow>$L`RIja$%S I+a ;usI֛Ow_?t? S1m#LL-X&vSb֋h.:`Ctqa3O>bjUM!PK8*PK5 META-INF/PK5y0=META-INF/MANIFEST.MFPK4~:,=%com/sun/medialib/mlib/JAIUtil$1.classPK4=%com/sun/medialib/mlib/JAIUtil$2.classPK4<%=com/sun/medialib/mlib/JAIUtil$3.classPK4#gcom/sun/medialib/mlib/JAIUtil.classPK4O #A com/sun/medialib/mlib/Image$1.classPK47*D# com/sun/medialib/mlib/Image$2.classPK4PY;#com/sun/medialib/mlib/Image$3.classPK4m<#zcom/sun/medialib/mlib/Image$4.classPK4$?1q#com/sun/medialib/mlib/Image$5.classPKs3qҠq#scom/sun/medialib/mlib/Image$6.classPKmW-u(Ga#com/sun/medialib/mlib/Image$7.classPK4T,75!Icom/sun/medialib/mlib/Image.classPK5X%(com/sun/medialib/mlib/Constants.classPK91fDr-A.com/sun/medialib/mlib/mediaLibException.classPKT3Cm91com/sun/medialib/mlib/mediaLibExceptionStrings.propertiesPK81_x,(4com/sun/medialib/mlib/mediaLibI18N.classPK1}~3F`#R)5com/sun/medialib/mlib/mediaLibImage.classPK;1p/KYcom/sun/medialib/mlib/mediaLibImageCanvas.classPK:19 i 7Po1bcom/sun/medialib/mlib/mediaLibImageColormap.classPK<1w ] 4com/sun/medialib/mlib/mediaLibImageInterpTable.classPK<18*/com/sun/medialib/mlib/mediaLibImageJPanel.classPKũjai-core-1.1.4/src/share/classes/0000755000175000017500000000000011633360404016406 5ustar mathieumathieujai-core-1.1.4/src/share/classes/com/0000755000175000017500000000000011633360404017164 5ustar mathieumathieujai-core-1.1.4/src/share/classes/com/sun/0000755000175000017500000000000011633360404017771 5ustar mathieumathieujai-core-1.1.4/src/share/classes/com/sun/media/0000755000175000017500000000000011633360404021050 5ustar mathieumathieujai-core-1.1.4/src/share/classes/com/sun/media/jai/0000755000175000017500000000000011633360406021615 5ustar mathieumathieujai-core-1.1.4/src/share/classes/com/sun/media/jai/util/0000755000175000017500000000000011633360406022572 5ustar mathieumathieujai-core-1.1.4/src/share/classes/com/sun/media/jai/util/SunTileScheduler.java0000644000175000017500000017276310203035544026671 0ustar mathieumathieu/* * $RCSfile: SunTileScheduler.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:02 $ * $State: Exp $ */ package com.sun.media.jai.util; import java.awt.Point; import java.awt.RenderingHints; import java.awt.image.Raster; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.lang.ref.WeakReference; import java.lang.reflect.Method; import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Set; import java.util.Vector; import javax.media.jai.OpImage; import javax.media.jai.PlanarImage; import javax.media.jai.TileCache; import javax.media.jai.TileComputationListener; import javax.media.jai.TileRequest; import javax.media.jai.TileScheduler; import javax.media.jai.remote.SerializableRenderedImage; import javax.media.jai.util.ImagingException; import javax.media.jai.util.ImagingListener; import com.sun.media.jai.util.ImageUtil; /** * A class representing a request for non-prefetch background computation * of tiles. The object stores the image, the indices of all tiles being * requested, and references to all listeners associated with the request. * * TileRequest methods are not commented. */ class Request implements TileRequest { private final TileScheduler scheduler; final PlanarImage image; final List indices; final Set listeners; final Hashtable tileStatus; /** * Constructs a Request. * * @param scheduler The scheduler processing this request. * @param image The image for which tiles are being computed. * @param tileIndices The indices of the tiles to be computed. * @param tileListeners The listeners to be notified of tile * computation, cancellation, or failure. * * @exception IllegalArgumentException if scheduler, * image, or tileIndices is * null or if tileIndices is * zero-length. */ Request(TileScheduler scheduler, PlanarImage image, Point[] tileIndices, TileComputationListener[] tileListeners) { // Save a reference to the scheduler. if(scheduler == null) { throw new IllegalArgumentException(); // Internal error - no message. } this.scheduler = scheduler; // Save a reference to the image. if(image == null) { throw new IllegalArgumentException(); // Internal error - no message. } this.image = image; // Ensure there is at least one tile in the request. if(tileIndices == null || tileIndices.length == 0) { // If this happens it is an internal programming error. throw new IllegalArgumentException(); // Internal error - no message. } // Save the tile indices. indices = Arrays.asList(tileIndices); // Save references to the listeners, if any. if(tileListeners != null) { int numListeners = tileListeners.length; if(numListeners > 0) { listeners = new HashSet(numListeners); for(int i = 0; i < numListeners; i++) { listeners.add(tileListeners[i]); } } else { listeners = null; } } else { listeners = null; } // Initialize status table. tileStatus = new Hashtable(tileIndices.length); } // --- TileRequest implementation --- public PlanarImage getImage() { return image; } public Point[] getTileIndices() { return (Point[])indices.toArray(new Point[0]); } public TileComputationListener[] getTileListeners() { return (TileComputationListener[]) listeners.toArray(new TileComputationListener[0]); } public boolean isStatusAvailable() { return true; } public int getTileStatus(int tileX, int tileY) { Point p = new Point(tileX, tileY); int status; if(tileStatus.containsKey(p)) { status = ((Integer)tileStatus.get(p)).intValue(); } else { status = TileRequest.TILE_STATUS_PENDING; } return status; } public void cancelTiles(Point[] tileIndices) { // Forward the call to the scheduler. scheduler.cancelTiles(this, tileIndices); } } /** A job to put in a job queue. */ interface Job { /** Computes the job required. */ void compute(); /** Returns true if the job is not done. */ boolean notDone(); /** Returns the image for which tiles are being computed. */ PlanarImage getOwner(); /** * Returns true if and only if the job should block the * thread which processes it. In this case the scheduler and the * processing thread must communicate using wait() and * notify(). */ boolean isBlocking(); /** Returns the first exception encountered or null. */ Exception getException(); } /** * A Job which computes a single tile at a time for a * non-prefetch background job queued by the version of scheduleTiles() * which returns a TileRequest. This Job * notifies all TileComputationListeners of all * TileRequests with which this tile is associated of * whether the tile was computed or the computation failed. */ final class RequestJob implements Job { final SunTileScheduler scheduler; // the TileScheduler final PlanarImage owner; // the image this tile belongs to final int tileX; // tile's X index final int tileY; // tile's Y index final Raster[] tiles; // the computed tiles final int offset; // offset into arrays boolean done = false; // flag indicating completion status Exception exception = null; // Any exception that might have occured // during computeTile /** Constructor. */ RequestJob(SunTileScheduler scheduler, PlanarImage owner, int tileX, int tileY, Raster[] tiles, int offset) { this.scheduler = scheduler; this.owner = owner; this.tileX = tileX; this.tileY = tileY; this.tiles = tiles; this.offset = offset; } /** * Tile computation. Does the actual call to getTile(). */ public void compute() { // Get the Request List. List reqList; synchronized(scheduler.tileRequests) { // Initialize the tile ID. Object tileID = SunTileScheduler.tileKey(owner, tileX, tileY); // Remove the List of Requests from the request Map. reqList = (List)scheduler.tileRequests.remove(tileID); // Remove the tile Job from the job Map. scheduler.tileJobs.remove(tileID); } // Check whether reqList is valid in case job was cancelled while // blocking on the tileRequests Map above. // XXX Do not need empty check in next line? if(reqList != null && !reqList.isEmpty()) { // Update tile status to "processing". Point p = new Point(tileX, tileY); Integer tileStatus = new Integer(TileRequest.TILE_STATUS_PROCESSING); Iterator reqIter = reqList.iterator(); while(reqIter.hasNext()) { Request r = (Request)reqIter.next(); r.tileStatus.put(p, tileStatus); } try { tiles[offset] = owner.getTile(tileX, tileY); } catch (Exception e) { exception = e; } finally { // Extract the Set of all TileComputationListeners. int numReq = reqList.size(); Set listeners = SunTileScheduler.getListeners(reqList); // XXX Do not need empty check in next line. if(listeners != null && !listeners.isEmpty()) { // Get TileRequests as an array for later use. TileRequest[] requests = (TileRequest[])reqList.toArray(new TileRequest[0]); // Update tile status as needed. tileStatus = new Integer(exception == null ? TileRequest.TILE_STATUS_COMPUTED : TileRequest.TILE_STATUS_FAILED); for(int i = 0; i < numReq; i++) { ((Request)requests[i]).tileStatus.put(p, tileStatus); } // Create an Iterator over the listeners. Iterator iter = listeners.iterator(); // Notify listeners. if(exception == null) { // Tile computation successful. while(iter.hasNext()) { TileComputationListener listener = (TileComputationListener)iter.next(); listener.tileComputed(scheduler, requests, owner, tileX, tileY, tiles[offset]); } } else { // Tile computation unsuccessful. while(iter.hasNext()) { TileComputationListener listener = (TileComputationListener)iter.next(); listener.tileComputationFailure(scheduler, requests, owner, tileX, tileY, exception); } } } } } // Set the flag indicating job completion. done = true; } /** * Returns true if the job is not done; that is, * the tile is not computed and no exceptions have occurred. */ public boolean notDone() { return !done; } /** Returns the image for which the tile is being computed. */ public PlanarImage getOwner() { return owner; } /** Always returns true. */ public boolean isBlocking() { return false; } /** Returns any encountered exception or null. */ public Exception getException() { return exception; } /** Returns a string representation of the class object. */ public String toString() { String tString = "null"; if (tiles[offset] != null) { tString = tiles[offset].toString(); } return getClass().getName() + "@" + Integer.toHexString(hashCode()) + ": owner = " + owner.toString() + " tileX = " + Integer.toString(tileX) + " tileY = " + Integer.toString(tileY) + " tile = " + tString; } } /** * A Job which computes one or more tiles at a time for either * a prefetch job or a blocking job. */ final class TileJob implements Job { final SunTileScheduler scheduler; // the TileScheduler final boolean isBlocking; // whether the job is blocking final PlanarImage owner; // the image this tile belongs to final Point[] tileIndices; // the tile indices final Raster[] tiles; // the computed tiles final int offset; // offset into arrays final int numTiles; // number of elements to use in indices array boolean done = false; // flag indicating completion status Exception exception = null; // The first exception that might have // occured during computeTile /** Constructor. */ TileJob(SunTileScheduler scheduler, boolean isBlocking, PlanarImage owner, Point[] tileIndices, Raster[] tiles, int offset, int numTiles) { this.scheduler = scheduler; this.isBlocking = isBlocking; this.owner = owner; this.tileIndices = tileIndices; this.tiles = tiles; this.offset = offset; this.numTiles = numTiles; } /** * Tile computation. Does the actual calls to getTile(). */ public void compute() { exception = scheduler.compute(owner, tileIndices, tiles, offset, numTiles, null); done = true; } /** * Returns true if the job is not done; that is, * the tile is not computed and no exceptions have occurred. */ public boolean notDone() { return !done; } /** Returns the image for which tiles are being computed. */ public PlanarImage getOwner() { return owner; } /** Returns true if and only if there is a listener. */ public boolean isBlocking() { return isBlocking; } /** Returns any encountered exception or null. */ public Exception getException() { return exception; } } /** * Worker thread that takes jobs from the tile computation queue and does * the actual computation. */ class WorkerThread extends Thread { /** Object indicating the the thread should exit. */ public static final Object TERMINATE = new Object(); /** The scheduler that spawned this thread. */ SunTileScheduler scheduler; /** Whether this is a prefetch thread. */ boolean isPrefetch; /** Constructor. */ public WorkerThread(ThreadGroup group, SunTileScheduler scheduler, boolean isPrefetch) { super(group, group.getName() + group.activeCount()); this.scheduler = scheduler; this.isPrefetch = isPrefetch; setDaemon(true); start(); } /** Does the tile computation. */ public void run() { LinkedList jobQueue = scheduler.getQueue(isPrefetch); while(true) { Object dequeuedObject = null; // Check the job queue. synchronized(jobQueue) { if(jobQueue.size() > 0) { // Remove the first job. dequeuedObject = jobQueue.removeFirst(); } else { try { // Wait for a notify() on the queue. jobQueue.wait(); continue; } catch(InterruptedException ie) { // Ignore: should never happen. } } } if(dequeuedObject == TERMINATE || getThreadGroup() == null || getThreadGroup().isDestroyed()) { // Remove WorkerThread from appropriate Vector. Vector threads; synchronized(threads = scheduler.getWorkers(isPrefetch)) { threads.remove(this); } // Exit the thread. return; } Job job = (Job)dequeuedObject; // Execute tile job. if (job != null) { job.compute(); // Notify the scheduler only if the Job is blocking. if(job.isBlocking()) { synchronized(scheduler) { scheduler.notify(); } } } } // infinite loop } } /** * This is Sun Microsystems' reference implementation of the * javax.media.jai.TileScheduler interface. It provides * a mechanism for scheduling tile calculation. Multi-threading is * used whenever possible. * * @see javax.media.jai.TileScheduler */ public final class SunTileScheduler implements TileScheduler { /** The default number of worker threads. */ private static final int NUM_THREADS_DEFAULT = 2; /** The default number of worker threads. */ private static final int NUM_PREFETCH_THREADS_DEFAULT = 1; /** The instance counter. It is used to compose the name of the * ThreadGroup. */ private static int numInstances = 0; /** The tile schedular name. It is used to compose the name of the * ThreadGroup. */ private static String name = JaiI18N.getString("SunTileSchedulerName"); /** The root ThreadGroup, which holds two sub-groups: * the ThreadGroup for the standard jobs, and the ThreadGroup for * the prefetch jobs. */ private ThreadGroup rootGroup; /** The ThreadGroup contains all the standard jobs. */ private ThreadGroup standardGroup; /** The ThreadGroup contains all the prefetch jobs. */ private ThreadGroup prefetchGroup; /** The worker thread parallelism. */ private int parallelism = NUM_THREADS_DEFAULT; /** The processing thread parallelism. */ private int prefetchParallelism = NUM_PREFETCH_THREADS_DEFAULT; /** The worker thread priority. */ private int priority = Thread.NORM_PRIORITY; /** The prefetch thread priority. */ private int prefetchPriority = Thread.MIN_PRIORITY; /** A job queue for tiles waiting to be computed by the worker threads. */ private LinkedList queue = null; /** A job queue for tiles waiting to be computed by prefetch workers. */ private LinkedList prefetchQueue = null; /** * A Vector of WorkerThreads that persist * to do the actual tile computation for normal processing. This * variable should never be set to null. */ private Vector workers = new Vector(); /** * A Vector of WorkerThreads that persist * to do the actual tile computation for prefetch processing. This * variable should never be set to null. */ private Vector prefetchWorkers = new Vector(); /** * The effective number of worker threads; may differ from * workers.size() due to latency. This value should * equal the size of workers less the number of * WorkerThread.TERMINATEs in queue. */ private int numWorkerThreads = 0; /** * The effective number of prefetch worker threads; may differ from * prefetchWorkers.size() due to latency. This value should * equal the size of prefetchWorkers less the number of * WorkerThread.TERMINATEs in prefetchQueue. */ private int numPrefetchThreads = 0; /** * Map of tiles currently being computed. The key is * created from the image and tile indices by the tileKey() * method. Each key is mapped to an Object[1] which may * contain null, a Raster, or an indefinite * Object which represent, respectively, that the tile is * being computed, the tile itself, and that the tile computation failed. */ private Map tilesInProgress = new HashMap(); /** * Map of tiles to Requests. The key is * created from the image and tile indices by the tileKey() * method. Each key is mapped to a List of * Request for the tile. If there is no mapping for the * tile, then there are no current requests. If a mapping exists, it * should always be non-null and the List value should * have size of at least unity. */ Map tileRequests = new HashMap(); /** * Map of tiles to Jobs.The key is * created from the image and tile indices by the tileKey() * method. Each key is mapped to a Job for the tile. If * there is no mapping for the tile, then there is no enqueued * RequestJob. */ Map tileJobs = new HashMap(); /** The name of this instance. */ private String nameOfThisInstance; /** * Returns the hash table "key" as a Object for this * tile. For PlanarImage and * SerializableRenderedImage, the key is generated by * the method ImageUtilgenerateID(Object) . For the * other cases, a Long object is returned. * The upper 32 bits for this Long is the tile owner's * hash code, and the lower 32 bits is the tile's index. */ static Object tileKey(PlanarImage owner, int tileX, int tileY) { long idx = tileY * (long)owner.getNumXTiles() + tileX; BigInteger imageID = (BigInteger)owner.getImageID(); byte[] buf = imageID.toByteArray(); int length = buf.length; byte[] buf1 = new byte[length + 8]; System.arraycopy(buf, 0, buf1, 0, length); for (int i = 7, j = 0; i >= 0; i--, j += 8) buf1[length++] = (byte)(idx >> j); return new BigInteger(buf1); } /** * Returns all TileComputationListeners for the supplied * List of Requests. */ static Set getListeners(List reqList) { // Extract the Set of all TileComputationListeners. int numReq = reqList.size(); HashSet listeners = null; for(int j = 0; j < numReq; j++) { Request req = (Request)reqList.get(j); // XXX Do not need empty check in next line. if(req.listeners != null && !req.listeners.isEmpty()) { if(listeners == null) { listeners = new HashSet(); } listeners.addAll(req.listeners); } } return listeners; } /** * Converts the supplied Exception's stack trace * to a String. */ private static String getStackTraceString(Throwable e) { ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); PrintStream printStream = new PrintStream(byteStream); e.printStackTrace(printStream); printStream.flush(); String stackTraceString = byteStream.toString(); printStream.close(); return stackTraceString; } /** * Constructor. * * @param parallelism The number of worker threads to do tile computation. * If this number is less than 1, no multi-threading is used. * @param priority The priority of worker threads. * @param prefetchParallelism The number of threads to do prefetching. * If this number is less than 1, no multi-threading is used. * @param prefetchPriority The priority of prefetch threads. */ public SunTileScheduler(int parallelism, int priority, int prefetchParallelism, int prefetchPriority) { // Create queues and set parallelism and priority to default values. this(); setParallelism(parallelism); setPriority(priority); setPrefetchParallelism(prefetchParallelism); setPrefetchPriority(prefetchPriority); } /** * Constructor. Processing and prefetch queues are created and all * parallelism and priority values are set to default values. */ public SunTileScheduler() { queue = new LinkedList(); prefetchQueue = new LinkedList(); nameOfThisInstance = name + numInstances; rootGroup = new ThreadGroup(nameOfThisInstance); rootGroup.setDaemon(true); standardGroup = new ThreadGroup(rootGroup, nameOfThisInstance + "Standard"); standardGroup.setDaemon(true); prefetchGroup = new ThreadGroup(rootGroup, nameOfThisInstance + "Prefetch"); prefetchGroup.setDaemon(true); numInstances++; } /** * Tile computation. Does the actual calls to getTile(). */ Exception compute(PlanarImage owner, Point[] tileIndices, Raster[] tiles, int offset, int numTiles, Request request) { Exception exception = null; int j = offset; if(request == null || request.listeners == null) { for(int i = 0; i < numTiles; i++, j++) { Point p = tileIndices[j]; try { tiles[j] = owner.getTile(p.x, p.y); } catch (Exception e) { exception = e; // Abort the remaining tiles in the job. break; } } } else { // listeners present Request[] reqs = new Request[] {request}; for(int i = 0; i < numTiles; i++, j++) { Point p = tileIndices[j]; // Update tile status to "processing". Integer tileStatus = new Integer(TileRequest.TILE_STATUS_PROCESSING); request.tileStatus.put(p, tileStatus); try { tiles[j] = owner.getTile(p.x, p.y); Iterator iter = request.listeners.iterator(); while(iter.hasNext()) { // Update tile status to "computed". tileStatus = new Integer(TileRequest.TILE_STATUS_COMPUTED); request.tileStatus.put(p, tileStatus); TileComputationListener listener = (TileComputationListener)iter.next(); listener.tileComputed(this, reqs, owner, p.x, p.y, tiles[j]); } } catch (Exception e) { exception = e; // Abort the remaining tiles in the job. break; } /* XXX try { List reqList; synchronized(tileRequests) { Long tileID = tileKey(owner, p.x, p.y); reqList = (List)tileRequests.remove(tileID); tileJobs.remove(tileID); } if(reqList != null) { tiles[j] = owner.getTile(p.x, p.y); TileRequest[] reqs = (TileRequest[])reqList.toArray(new TileRequest[0]); Set listeners = getListeners(reqList); if(listeners != null) { Iterator iter = listeners.iterator(); while(iter.hasNext()) { TileComputationListener listener = (TileComputationListener)iter.next(); listener.tileComputed(this, reqs, owner, p.x, p.y, tiles[j]); } } } } catch (Exception e) { exception = e; // Abort the remaining tiles in the job. break; } */ } } // If an exception occured, notify listeners that all remaining // tiles in the job have failed. if(exception != null && request != null && request.listeners != null) { int lastOffset = j; int numFailed = numTiles - (lastOffset - offset); // Mark all tiles starting with the one which generated the // Exception as "failed". for(int i = 0, k = lastOffset; i < numFailed; i++) { Integer tileStatus = new Integer(TileRequest.TILE_STATUS_FAILED); request.tileStatus.put(tileIndices[k++], tileStatus); } // Notify listeners. Request[] reqs = new Request[] {request}; for(int i = 0, k = lastOffset; i < numFailed; i++) { Point p = tileIndices[k++]; Iterator iter = request.listeners.iterator(); while(iter.hasNext()) { TileComputationListener listener = (TileComputationListener)iter.next(); listener.tileComputationFailure(this, reqs, owner, p.x, p.y, exception); } } } /* XXX if(exception != null) { int numFailed = numTiles - (j - offset); for(int i = 0; i < numFailed; i++) { Point p = tileIndices[j++]; Long tileID = tileKey(owner, p.x, p.y); List reqList = (List)tileRequests.remove(tileID); tileJobs.remove(tileID); if(reqList != null) { TileRequest[] reqs = (TileRequest[])reqList.toArray(new TileRequest[0]); Set listeners = getListeners(reqList); if(listeners != null) { Iterator iter = listeners.iterator(); while(iter.hasNext()) { TileComputationListener listener = (TileComputationListener)iter.next(); listener.tileComputationFailure(this, reqs, owner, p.x, p.y, exception); } } } } } */ return exception; } /** * Schedules a single tile for computation. * * @param owner The image the tiles belong to. * @param tileX The tile's X index. * @param tileY The tile's Y index. * * @exception IllegalArgumentException if owner is * null. * * @return The computed tile */ // // This method blocks on the 'tilesInProgress' Map to avoid simultaneous // computation of the same tile in two or more different threads. The idea // is to release the resources of all but one thread so that the computation // occurs more quickly. The synchronization variable is an Object[] of length // unity. The computed tile is passed from the computing thread to the // waiting threads via the contents of this Object[]. Thus this method does // not depend on the TileCache to transfer the data. // public Raster scheduleTile(OpImage owner, int tileX, int tileY) { if (owner == null) { throw new IllegalArgumentException(JaiI18N.getString("SunTileScheduler1")); } // Eventual tile to be returned. Raster tile = null; // Get the tile's unique ID. Object tileID = tileKey(owner, tileX, tileY); // Set the computation flag and initialize or retrieve the tile cache. boolean computeTile = false; Object[] cache = null; synchronized(tilesInProgress) { if(computeTile = !tilesInProgress.containsKey(tileID)) { // Computing: add tile ID to the map. tilesInProgress.put(tileID, cache = new Object[1]); } else { // Waiting: get tile cache from the Map. cache = (Object[])tilesInProgress.get(tileID); } } if(computeTile) { try { try { // Attempt to compute the tile. tile = owner.computeTile(tileX, tileY); } catch (OutOfMemoryError e) { // Empty the cache and call System.gc() TileCache tileCache = owner.getTileCache(); if(tileCache != null) { tileCache.flush(); System.gc(); //slow } // Re-attempt to compute the tile. tile = owner.computeTile(tileX, tileY); } } catch(Throwable e) { // Re-throw the Error or Exception. if(e instanceof Error) { throw (Error)e; } else if(e instanceof RuntimeException) { sendExceptionToListener(JaiI18N.getString("SunTileScheduler6"), e); // throw (RuntimeException)e; } else { String message = JaiI18N.getString("SunTileScheduler6"); sendExceptionToListener(message, new ImagingException(message, e)); /* throw new RuntimeException(e.getMessage()+"\n"+ getStackTraceString(e)); */ } } finally { synchronized(cache) { // Always set the cached tile to a non-null value. cache[0] = tile != null ? tile : new Object(); // Notify the thread(s). cache.notifyAll(); synchronized(tilesInProgress) { // Remove the tile ID from the Map. tilesInProgress.remove(tileID); } } } } else { synchronized(cache) { // Check the cache: a null value indicates computation is // still in progress. if(cache[0] == null) { // Wait for the computation to complete. try { cache.wait(); // XXX Should there be a timeout? } catch(Exception e) { // XXX What response here? } } // Set the result only if cache contains a Raster. if(cache[0] instanceof Raster) { tile = (Raster)cache[0]; } else { throw new RuntimeException(JaiI18N.getString("SunTileScheduler5")); } } } return tile; } /** * General purpose method for job creation and queueing. Note that * the returned value should be ignored if the listener * parameter is non-null. * * @param owner The image for which tile computation jobs will be queued. * @param tileIndices The indices of the tiles to be computed. * @param isPrefetch Whether the operation is a prefetch. * @param listener A TileComputationListener of the * processing. May be null. * * @return The computed tiles. This value is meaningless if * listener is non-null. */ // The allowable arguments are constained as follows: // A) owner and tileIndices non-null. // B) (isBlocking,isPrefetch) in {(true,false),(false,false),(false,true)} // C) listeners != null <=> (isBlocking,isPrefetch) == (false,false) // The returned value is one of: // Raster[] <=> (isBlocking,isPrefetch) == (true,false) // Integer <=> (isBlocking,isPrefetch) == (false,false) // (Raster[])null <=> (isBlocking,isPrefetch) == (false,true) private Object scheduleJob(PlanarImage owner, Point[] tileIndices, boolean isBlocking, boolean isPrefetch, TileComputationListener[] listeners) { if(owner == null || tileIndices == null) { // null parameters throw new IllegalArgumentException(); // coding error - no message } else if((isBlocking || isPrefetch) && listeners != null) { // listeners for blocking or prefetch job throw new IllegalArgumentException(); // coding error - no message } else if(isBlocking && isPrefetch) { throw new IllegalArgumentException(); // coding error - no message } int numTiles = tileIndices.length; Raster[] tiles = new Raster[numTiles]; Object returnValue = tiles; int numThreads = 0; Job[] jobs = null; int numJobs = 0; synchronized(getWorkers(isPrefetch)) { numThreads = getNumThreads(isPrefetch); if(numThreads > 0) { // worker threads exist if(numTiles <= numThreads || // no more tiles than threads (!isBlocking && !isPrefetch)) { // non-blocking, non-prefetch jobs = new Job[numTiles]; if(!isBlocking && !isPrefetch) { Request request = new Request(this, owner, tileIndices, listeners); // Override return value. returnValue = request; // Queue all tiles as single-tile jobs. while(numJobs < numTiles) { Point p = tileIndices[numJobs]; Object tileID = tileKey(owner, p.x, p.y); synchronized(tileRequests) { List reqList = null; if(tileRequests.containsKey(tileID)) { // This tile is already queued in a // non-blocking, non-prefetch job. reqList = (List)tileRequests.get(tileID); reqList.add(request); numTiles--; } else { // This tile has not yet been queued. reqList = new ArrayList(); reqList.add(request); tileRequests.put(tileID, reqList); jobs[numJobs] = new RequestJob(this, owner, p.x, p.y, tiles, numJobs); tileJobs.put(tileID, jobs[numJobs]); addJob(jobs[numJobs++], false); } } } } else { // numTiles <= numThreads while(numJobs < numTiles) { jobs[numJobs] = new TileJob(this, isBlocking, owner, tileIndices, tiles, numJobs, 1); addJob(jobs[numJobs++], isPrefetch); } } } else { // more tiles than worker threads // Set the fraction of unqueued tiles to be processed by // each worker thread. float frac = 1.0F/(2.0F*numThreads); // Set the minimum number of tiles each thread may process. // If there is only one thread this will equal the total // number of tiles. int minTilesPerThread = numThreads == 1 ? numTiles : Math.min(Math.max(1, (int)(frac*numTiles/2.0F + 0.5F)), numTiles); // Allocate the maximum possible number of multi-tile jobs. // This will be larger than the actual number of jobs but // a more precise calcuation is not possible and a dynamic // storage object such as a Collection would not be useful // since as calculated maxNumJobs = 4*numThreads if the // preceeding values of "frac" and "minTilesPerThread" are // 1/(2*numThreads) and frac*numTiles/2, respectively. int maxNumJobs = numThreads == 1 ? 1 : (int)((float)numTiles/(float)minTilesPerThread+0.5F); jobs = new TileJob[maxNumJobs]; // Set the number of enqueued tiles and the number left. int numTilesQueued = 0; int numTilesLeft = numTiles - numTilesQueued; // Assign a number of tiles to each thread determined by // the number of remaining tiles, the fraction of remaining // tiles to be processed and the minimum chunk size. while(numTilesLeft > 0) { // Set the number of tiles to the pre-calculated // fraction of tiles yet to be computed. int numTilesInThread = (int)(frac*numTilesLeft + 0.5F); // Ensure that the number to be processed is at // least the minimum chunk size. if(numTilesInThread < minTilesPerThread) { numTilesInThread = minTilesPerThread; } // Clamp number of tiles in thread to number unqueued. if(numTilesInThread > numTilesLeft) { numTilesInThread = numTilesLeft; } // Decrement the count of remaining tiles. Note that // this value will be non-negative due to the clamping // above. numTilesLeft -= numTilesInThread; // If the number left is smaller than the minimum chunk // size then process these tiles in the current job. if(numTilesLeft < minTilesPerThread) { numTilesInThread += numTilesLeft; numTilesLeft = 0; } // Create a job to process the number of tiles needed. jobs[numJobs] = new TileJob(this, isBlocking, owner, tileIndices, tiles, numTilesQueued, numTilesInThread); // Queue the job and increment the job count. addJob(jobs[numJobs++], isPrefetch); // Increment the count of tiles queued. numTilesQueued += numTilesInThread; } } // SingleTile vs. MultiTile Jobs } // numThreads > 0 } // end synchronized block if(numThreads != 0) { // If blocking, wait until all tiles have been computed. // There is no 'else' block for non-blocking as in that // case we just want to continue. if(isBlocking) { LinkedList jobQueue = getQueue(isPrefetch); for (int i = 0; i < numJobs; i++) { synchronized(this) { while (jobs[i].notDone()) { try { wait(); } catch(InterruptedException ie) { // Ignore: should never happen. } } } // XXX: should we re-throw the exception or // should we reschedule this job ?? krishnag Exception e = jobs[i].getException(); if (e != null) { // Throw a RuntimeException with the Exception's // message concatenated with the stack trace. String message = JaiI18N.getString("SunTileScheduler7"); sendExceptionToListener(message, new ImagingException(message, e)); /* throw new RuntimeException(e.getMessage()+"\n"+ getStackTraceString(e)); */ } } } } else { // numThreads == 0 Request request = null; if(!isBlocking && !isPrefetch) { request = new Request(this, owner, tileIndices, listeners); returnValue = request; } // no workers; sequentially compute tiles in main thread Exception e = compute(owner, tileIndices, tiles, 0, numTiles, request); // Throw a RuntimeException with the Exception's // message concatenated with the stack trace. if(e != null) { String message = JaiI18N.getString("SunTileScheduler7"); sendExceptionToListener(message, new ImagingException(message, e)); /* throw new RuntimeException(e.getMessage()+"\n"+ getStackTraceString(e)); */ } } return returnValue; } /** * Schedules multiple tiles of an image for computation. * * @param owner The image the tiles belong to. * @param tileIndices An array of tile X and Y indices. * * @return An array of computed tiles. */ public Raster[] scheduleTiles(OpImage owner, Point tileIndices[]) { if (owner == null || tileIndices == null) { throw new IllegalArgumentException(JaiI18N.getString("SunTileScheduler0")); } return (Raster[])scheduleJob(owner, tileIndices, true, false, null); } /** * Schedule a list of tiles for computation. The supplied listeners * will be notified after each tile has been computed. This * method ideally should be non-blocking. If the TileScheduler * implementation uses multithreading, it is at the discretion of the * implementation which thread invokes the * TileComputationListener methods. */ public TileRequest scheduleTiles(PlanarImage target, Point[] tileIndices, TileComputationListener[] tileListeners) { if (target == null || tileIndices == null) { throw new IllegalArgumentException(JaiI18N.getString("SunTileScheduler4")); } return (TileRequest)scheduleJob(target, tileIndices, false, false, tileListeners); } /** * Issues an advisory cancellation request to the * TileScheduler stating that the indicated tiles of the * specified image should not be processed. The handling of this request * is at the discretion of the scheduler which may cancel tile processing * in progress and remove tiles from its internal queue, remove tiles from * the queue but not terminate current processing, or simply do nothing. * *

In the Sun Microsystems reference implementation of * TileScheduler the second tile cancellation option is * implemented, i.e., tiles are removed from the internal queue but * computation already in progress is not terminated. If there is at * least one worker thread this method should be non-blocking. Any tiles * allowed to complete computation subsequent to this call are complete * and will be treated as if they had not been cancelled, e.g., with * respect to caching, notification of registered listeners, etc. * Furthermore, cancelling a tile request in no way invalidates the tile * as a candidate for future recomputation. */ public void cancelTiles(TileRequest request, Point[] tileIndices) { if(request == null) { throw new IllegalArgumentException(JaiI18N.getString("SunTileScheduler3")); } Request req = (Request)request; synchronized(tileRequests) { // Save the list of all tile indices in this request. List reqIndexList = req.indices; // Initialize the set of tile indices to cancel. Point[] indices; if(tileIndices != null && tileIndices.length > 0) { // Create a Set from the supplied indices. List tileIndexList = Arrays.asList(tileIndices); // Retain only indices which were actually in the request. tileIndexList.retainAll(reqIndexList); indices = (Point[])tileIndexList.toArray(new Point[0]); } else { indices = (Point[])reqIndexList.toArray(new Point[0]); } // Cache the count. int numTiles = indices.length; // Cache status value. Integer tileStatus = new Integer(TileRequest.TILE_STATUS_CANCELLED); // Loop over tile indices to be cancelled. for(int i = 0; i < numTiles; i++) { Point p = indices[i]; // Get the tile's ID. Object tileID = tileKey(req.image, p.x, p.y); // Get the list of requests for this tile. List reqList = (List)tileRequests.get(tileID); // If there are none, proceed to next index. if(reqList == null) { continue; } // Remove this Request from the Request List for this tile. reqList.remove(req); // If the request list is now empty, dequeue the job and // remove the tile from the hashes. if(reqList.isEmpty()) { synchronized(queue) { Object job = tileJobs.remove(tileID); if(job != null) { queue.remove(job); } } tileRequests.remove(tileID); } // Update tile status to "cancelled". req.tileStatus.put(p, tileStatus); // Notify any listeners. if(req.listeners != null) { TileRequest[] reqArray = new TileRequest[] {req}; Iterator iter = req.listeners.iterator(); while(iter.hasNext()) { TileComputationListener listener = (TileComputationListener)iter.next(); listener.tileCancelled(this, reqArray, req.image, p.x, p.y); } } } } } /** * Prefetchs a list of tiles of an image. * * @param owner The image the tiles belong to. * @param tileIndices An array of tile X and Y indices. */ public void prefetchTiles(PlanarImage owner, Point[] tileIndices) { if (owner == null || tileIndices == null) { throw new IllegalArgumentException(JaiI18N.getString("SunTileScheduler0")); } scheduleJob(owner, tileIndices, false, true, null); } /** * Suggests to the scheduler the degree of parallelism to use in * processing invocations of scheduleTiles(). For * example, this might set the number of threads to spawn. It is * legal to implement this method as a no-op. * *

In the Sun Microsystems reference implementation of TileScheduler * this method sets the number of worker threads actually used for tile * computation. Ideally this number should equal the number of processors * actually available on the system. It is the responsibility of the * application to set this value as the number of processors is not * available via the virtual machine. A parallelism value of zero * indicates that all tile computation will be effected in the primary * thread. A parallelism value of N indicates that there will be * N worker threads in addition to the primary scheduler thread. * In JAI the parallelism defaults to a value of 2 unless explicity set * by the application. * * @param parallelism The suggested degree of parallelism. * @throws IllegalArgumentException if parallelism * is negative. */ public void setParallelism(int parallelism) { if (parallelism < 0) { throw new IllegalArgumentException(JaiI18N.getString("SunTileScheduler2")); } this.parallelism = parallelism; } /** * Returns the degree of parallelism of the scheduler. */ public int getParallelism() { return parallelism; } /** * Identical to setParallelism() but applies only to * prefetchTiles(). */ public void setPrefetchParallelism(int parallelism) { if (parallelism < 0) { throw new IllegalArgumentException(JaiI18N.getString("SunTileScheduler2")); } prefetchParallelism = parallelism; } /** * Identical to getParallelism() but applies only to * prefetchTiles(). */ public int getPrefetchParallelism() { return prefetchParallelism; } /** * Suggests to the scheduler the priority to assign to processing * effected by scheduleTiles(). For example, this might * set thread priority. Values outside of the accepted priority range * will be clamped to the nearest extremum. An implementation may clamp * the prefetch priority to less than the scheduling priority. It is * legal to implement this method as a no-op. * *

In the Sun Microsystems reference implementation of TileScheduler * this method sets the priority of the worker threads used for tile * computation. Its initial value is Thread.NORM_PRIORITY. * * @param priority The suggested priority. */ public void setPriority(int priority) { this.priority = Math.max(Math.min(priority, Thread.MAX_PRIORITY), Thread.MIN_PRIORITY); } /** * Returns the priority of scheduleTiles() processing. */ public int getPriority() { return priority; } /** * Identical to setPriority() but applies only to * prefetchTiles(). * *

In the Sun Microsystems reference implementation of * TileScheduler, this method sets the priority of any threads * spawned to prefetch tiles. Its initial value is * Thread.MIN_PRIORITY. */ public void setPrefetchPriority(int priority) { prefetchPriority = Math.max(Math.min(priority, Thread.MAX_PRIORITY), Thread.MIN_PRIORITY); } /** * Identical to getPriority() but applies only to * prefetchTiles(). */ public int getPrefetchPriority() { return prefetchPriority; } /** Recreate the ThreadGroupis and WorkThreads. * This happens in the case of applet: the java plugin will exist after * the termination of the applet so that JAI and SunTileScheduler will * also exist. However, the ThreadGroups are destroyed. * Thus, the old workers should be terminated and new i * ThreadGroup and workers should be created. */ // private synchronized void createThreadGroup(boolean isPrefetch) { private void createThreadGroup(boolean isPrefetch) { if (rootGroup == null || rootGroup.isDestroyed()) { rootGroup = new ThreadGroup(nameOfThisInstance); rootGroup.setDaemon(true); } if (isPrefetch && (prefetchGroup == null || prefetchGroup.isDestroyed())) { prefetchGroup = new ThreadGroup(rootGroup, nameOfThisInstance + "Prefetch"); prefetchGroup.setDaemon(true); } if (!isPrefetch && (standardGroup == null || standardGroup.isDestroyed())) { standardGroup = new ThreadGroup(rootGroup, nameOfThisInstance + "Standard"); standardGroup.setDaemon(true); } Vector thr = getWorkers(isPrefetch); int size = thr.size(); for(int i = size - 1; i >= 0; i--) { Thread t = (Thread)thr.get(i); if (!t.isAlive()) thr.remove(t); } if (isPrefetch) numPrefetchThreads = thr.size(); else numWorkerThreads = thr.size(); } /** * Returns the effective number of threads of the specified type. * This method also updates the number and priority of threads of * the specified type according to the global settings. This method * may add WorkerThread.TERMINATEs to the appropriate * queue if there are too many effective threads. */ private int getNumThreads(boolean isPrefetch) { createThreadGroup(isPrefetch); // Local variables. Vector thr = getWorkers(isPrefetch); int nthr; int prll; int prty; // Set local variables depending on the thread type. if(isPrefetch) { nthr = numPrefetchThreads; prll = prefetchParallelism; prty = prefetchPriority; } else { nthr = numWorkerThreads; prll = parallelism; prty = priority; } // Update priority if it has changed. if(nthr > 0 && ((Thread)thr.get(0)).getPriority() != prty) { int size = thr.size(); for(int i = 0; i < size; i++) { Thread t = (Thread)thr.get(i); if (t != null && t.getThreadGroup() != null) { t.setPriority(prty); } } } if(nthr < prll) { // Not enough processing threads. // Add more threads at current priority. while(nthr < prll) { Thread t = new WorkerThread(isPrefetch ? prefetchGroup : standardGroup, this, isPrefetch); t.setPriority(prty); thr.add(t); nthr++; } } else { // Too many processing threads: queue WorkerThread.TERMINATEs. // WorkerThread will remove itself later from the appropriate // Vector. while(nthr > prll) { addJob(WorkerThread.TERMINATE, isPrefetch); nthr--; } } // Update the number of effective threads. if(isPrefetch) { numPrefetchThreads = nthr; } else { numWorkerThreads = nthr; } return nthr; } /** Returns the appropriate worker list. */ Vector getWorkers(boolean isPrefetch) { return isPrefetch ? workers : prefetchWorkers; } /** Returns the appropriate queue. */ LinkedList getQueue(boolean isPrefetch) { return isPrefetch ? prefetchQueue : queue; } /** Append a job to the appropriate queue. */ private void addJob(Object job, boolean isPrefetch) { if(job == null || (job != WorkerThread.TERMINATE && !(job instanceof Job))) { // Programming error: deliberately no message. throw new IllegalArgumentException(); } LinkedList jobQueue; synchronized(jobQueue = getQueue(isPrefetch)) { if(isPrefetch || jobQueue.isEmpty() || job instanceof RequestJob) { // Append job to queue. jobQueue.addLast(job); } else { // If the queue is non-empty or the job is a TileJob // insert the job after the last TileJob in the queue. boolean inserted = false; for(int idx = jobQueue.size() - 1; idx >= 0; idx--) { if(jobQueue.get(idx) instanceof TileJob) { jobQueue.add(idx+1, job); inserted = true; break; } } if(!inserted) { jobQueue.addFirst(job); } } jobQueue.notify(); } } /** Queue WorkerThread.TERMINATEs to all workers. */ protected void finalize() throws Throwable { terminateAll(false); terminateAll(true); super.finalize(); } /** Queue WorkerThread.TERMINATEs to all appropriate workers. */ private void terminateAll(boolean isPrefetch) { synchronized(getWorkers(isPrefetch)) { int numThreads = isPrefetch ? numPrefetchThreads : numWorkerThreads; for(int i = 0; i < numThreads; i++) { addJob(WorkerThread.TERMINATE, isPrefetch); if(isPrefetch) { numPrefetchThreads--; } else { numWorkerThreads--; } } } } void sendExceptionToListener(String message, Throwable e) { ImagingListener listener = ImageUtil.getImagingListener((RenderingHints)null); listener.errorOccurred(message, e, this, false); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/util/AreaOpPropertyGenerator.java0000644000175000017500000000556110203035544030221 0ustar mathieumathieu/* * $RCSfile: AreaOpPropertyGenerator.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:59 $ * $State: Exp $ */ package com.sun.media.jai.util; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.AreaOpImage; import javax.media.jai.PlanarImage; import javax.media.jai.RenderedOp; import javax.media.jai.ROI; import javax.media.jai.ROIShape; public class AreaOpPropertyGenerator extends PropertyGeneratorImpl { /** Constructor. */ public AreaOpPropertyGenerator() { super(new String[] {"ROI"}, new Class[] {ROI.class}, new Class[] {RenderedOp.class}); } /** * Returns the specified property in the rendered layer. * * @param name Property name. * @param opNode Operation node. */ public Object getProperty(String name, Object opNode) { validate(name, opNode); if(opNode instanceof RenderedOp && name.equalsIgnoreCase("roi")) { RenderedOp op = (RenderedOp)opNode; ParameterBlock pb = op.getParameterBlock(); // Retrieve the rendered source image and its ROI. PlanarImage src = (PlanarImage)pb.getRenderedSource(0); Object roiProperty = src.getProperty("ROI"); if(roiProperty == null || roiProperty == java.awt.Image.UndefinedProperty || !(roiProperty instanceof ROI)) { return java.awt.Image.UndefinedProperty; } ROI roi = (ROI)roiProperty; // Determine the effective destination bounds. Rectangle dstBounds = null; PlanarImage dst = op.getRendering(); if(dst instanceof AreaOpImage && ((AreaOpImage)dst).getBorderExtender() == null) { AreaOpImage aoi = (AreaOpImage)dst; dstBounds = new Rectangle(aoi.getMinX() + aoi.getLeftPadding(), aoi.getMinY() + aoi.getTopPadding(), aoi.getWidth() - aoi.getLeftPadding() - aoi.getRightPadding(), aoi.getHeight() - aoi.getTopPadding() - aoi.getBottomPadding()); } else { dstBounds = dst.getBounds(); } // If necessary, clip the ROI to the destination bounds. // XXX Is this desirable? if(!dstBounds.contains(roi.getBounds())) { roi = roi.intersect(new ROIShape(dstBounds)); } return roi; } return java.awt.Image.UndefinedProperty; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/util/SunTileCache.java0000644000175000017500000010772210336237403025754 0ustar mathieumathieu/* * $RCSfile: SunTileCache.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-11-15 01:50:59 $ * $State: Exp $ */ package com.sun.media.jai.util; import java.awt.RenderingHints; import java.util.Collections; import java.util.Comparator; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.Observable; import java.util.Vector; import java.util.Enumeration; import java.util.Hashtable; import java.util.SortedSet; import java.util.TreeSet; import java.awt.Point; import java.awt.image.Raster; import java.awt.image.RenderedImage; import javax.media.jai.EnumeratedParameter; import javax.media.jai.TileCache; import javax.media.jai.util.ImagingListener; /** * This is Sun Microsystems' reference implementation of the * javax.media.jai.TileCache interface. It provides a * central location for images to cache computed tiles, and is used as * the default tile cache mechanism when no other tile cache objects * are specified. * *

In this implementation, the cache size is limited by the memory * capacity, which may be set at construction time or using the * setMemoryCapacity(long) method. The tile capacity * is not used. Different images may have very different tile sizes. * Therefore, the memory usage for a specific tile capacity may vary * greatly depends on the type of images involved. In fact, the tile * capacity is rather meaningless. * * @see javax.media.jai.TileCache * */ // // NOTE: code is inlined for performance reasons // public final class SunTileCache extends Observable implements TileCache, CacheDiagnostics { /** The default memory capacity of the cache (16 MB). */ private static final long DEFAULT_MEMORY_CAPACITY = 16L * 1024L * 1024L; /** The default hashtable capacity (heuristic) */ private static final int DEFAULT_HASHTABLE_CAPACITY = 1009; // prime number /** The hashtable load factor */ private static final float LOAD_FACTOR = 0.5F; /** * The tile cache. * A Hashtable is used to cache the tiles. The "key" is a * Object determined based on tile owner's UID if any or * hashCode if the UID doesn't exist, and tile index. The * "value" is a SunCachedTile. */ private Hashtable cache; /** * Sorted (Tree) Set used with tile metrics. * Adds another level of metrics used to determine * which tiles are removed during memoryControl(). */ private SortedSet cacheSortedSet; /** The memory capacity of the cache. */ private long memoryCapacity; /** The amount of memory currently being used by the cache. */ private long memoryUsage = 0; /** The amount of memory to keep after memory control */ private float memoryThreshold = 0.75F; /** A indicator for tile access time. */ private long timeStamp = 0; /** Custom comparator used to determine tile cost or * priority ordering in the tile cache. */ private Comparator comparator = null; /** Pointer to the first (newest) tile of the linked SunCachedTile list. */ private SunCachedTile first = null; /** Pointer to the last (oldest) tile of the linked SunCachedTile list. */ private SunCachedTile last = null; /** Tile count used for diagnostics */ private long tileCount = 0; /** Cache hit count */ private long hitCount = 0; /** Cache miss count */ private long missCount = 0; /** Diagnostics enable/disable */ private boolean diagnostics = false; // diagnostic actions // !!! If actions are changed in any way (removal, modification, addition) // then the getCachedTileActions() method below should be changed to match. private static final int ADD = 0; private static final int REMOVE = 1; private static final int REMOVE_FROM_FLUSH = 2; private static final int REMOVE_FROM_MEMCON = 3; private static final int UPDATE_FROM_ADD = 4; private static final int UPDATE_FROM_GETTILE = 5; private static final int ABOUT_TO_REMOVE = 6; /** * Returns an array of EnumeratedParameters corresponding * to the numeric values returned by the getAction() * method of the CachedTile implementation used by * SunTileCache. The "name" of each * EnumeratedParameter provides a brief string * describing the numeric action value. */ public static EnumeratedParameter[] getCachedTileActions() { return new EnumeratedParameter[] { new EnumeratedParameter("add", ADD), new EnumeratedParameter("remove", REMOVE), new EnumeratedParameter("remove_by_flush", REMOVE_FROM_FLUSH), new EnumeratedParameter("remove_by_memorycontrol", REMOVE_FROM_MEMCON), new EnumeratedParameter("timestamp_update_by_add", UPDATE_FROM_ADD), new EnumeratedParameter("timestamp_update_by_gettile", UPDATE_FROM_GETTILE), new EnumeratedParameter("preremove", ABOUT_TO_REMOVE) }; } /** * No args constructor. Use the DEFAULT_MEMORY_CAPACITY of 16 Megs. */ public SunTileCache() { this(DEFAULT_MEMORY_CAPACITY); } /** * Constructor. The memory capacity should be explicitly specified. * * @param memoryCapacity The maximum cache memory size in bytes. * * @throws IllegalArgumentException If memoryCapacity * is less than 0. */ public SunTileCache(long memoryCapacity) { if (memoryCapacity < 0) { throw new IllegalArgumentException(JaiI18N.getString("SunTileCache")); } this.memoryCapacity = memoryCapacity; // try to get a prime number (more efficient?) // lower values of LOAD_FACTOR increase speed, decrease space efficiency cache = new Hashtable(DEFAULT_HASHTABLE_CAPACITY, LOAD_FACTOR); } /** * Adds a tile to the cache. * *

If the specified tile is already in the cache, it will not be * cached again. If by adding this tile, the cache exceeds the memory * capacity, older tiles in the cache are removed to keep the cache * memory usage under the specified limit. * * @param owner The image the tile blongs to. * @param tileX The tile's X index within the image. * @param tileY The tile's Y index within the image. * @param tile The tile to be cached. */ public void add(RenderedImage owner, int tileX, int tileY, Raster tile) { add(owner, tileX, tileY, tile, null); } /** * Adds a tile to the cache with an associated tile compute cost. * *

If the specified tile is already in the cache, it will not be * cached again. If by adding this tile, the cache exceeds the memory * capacity, older tiles in the cache are removed to keep the cache * memory usage under the specified limit. * * @param owner The image the tile blongs to. * @param tileX The tile's X index within the image. * @param tileY The tile's Y index within the image. * @param tile The tile to be cached. * @param tileCacheMetric Metric for prioritizing tiles */ public synchronized void add(RenderedImage owner, int tileX, int tileY, Raster tile, Object tileCacheMetric) { if ( memoryCapacity == 0 ) { return; } // This tile is not in the cache; create a new SunCachedTile. // else just update. (code inlined for performance). Object key = SunCachedTile.hashKey(owner, tileX, tileY); SunCachedTile ct = (SunCachedTile) cache.get(key); if ( ct != null ) { // tile is cached, inlines update() ct.timeStamp = timeStamp++; if (ct != first) { // Bring this tile to the beginning of the list. if (ct == last) { last = ct.previous; last.next = null; } else { ct.previous.next = ct.next; ct.next.previous = ct.previous; } ct.previous = null; ct.next = first; first.previous = ct; first = ct; } hitCount++; if ( diagnostics ) { ct.action = UPDATE_FROM_ADD; setChanged(); notifyObservers(ct); } } else { // create a new tile ct = new SunCachedTile(owner, tileX, tileY, tile, tileCacheMetric); // Don't cache tile if adding it would provoke memoryControl() // which would in turn only end up removing the tile. if(memoryUsage + ct.memorySize > memoryCapacity && ct.memorySize > (long)(memoryCapacity * memoryThreshold)) { return; } ct.timeStamp = timeStamp++; ct.previous = null; ct.next = first; if (first == null && last == null) { first = ct; last = ct; } else { first.previous = ct; first = ct; // put this tile at the top of the list } // add to tile cache if ( cache.put(ct.key, ct) == null ) { memoryUsage += ct.memorySize; tileCount++; //missCount++; Not necessary? if ( cacheSortedSet != null ) { cacheSortedSet.add(ct); } if ( diagnostics ) { ct.action = ADD; setChanged(); notifyObservers(ct); } } // Bring memory usage down to memoryThreshold % of memory capacity. if (memoryUsage > memoryCapacity) { memoryControl(); } } } /** * Removes a tile from the cache. * *

If the specified tile is not in the cache, this method * does nothing. */ public synchronized void remove(RenderedImage owner, int tileX, int tileY) { if ( memoryCapacity == 0 ) { return; } Object key = SunCachedTile.hashKey(owner, tileX, tileY); SunCachedTile ct = (SunCachedTile) cache.get(key); if ( ct != null ) { // Notify observers that a tile is about to be removed. // It is possible that the tile will be removed from the // cache before the observers get notified. This should // be ok, since a hard reference to the tile will be // kept for the observers, so the garbage collector won't // remove the tile until the observers release it. ct.action = ABOUT_TO_REMOVE; setChanged(); notifyObservers(ct); ct = (SunCachedTile) cache.remove(key); // recalculate memoryUsage only if tile is actually removed if ( ct != null ) { memoryUsage -= ct.memorySize; tileCount--; if ( cacheSortedSet != null ) { cacheSortedSet.remove(ct); } if ( ct == first ) { if ( ct == last ) { first = null; // only one tile in the list last = null; } else { first = ct.next; first.previous = null; } } else if ( ct == last ) { last = ct.previous; last.next = null; } else { ct.previous.next = ct.next; ct.next.previous = ct.previous; } // Notify observers that a tile has been removed. // If the core's hard references go away, the // soft references will be garbage collected. // Usually, by the time the observers are notified // the ct owner and tile are nulled by the GC, so // we can't really tell which op was removed // This occurs when OpImage's finalize method is // invoked. This code works ok when remove is // called directly. (by flush() for example). // If the soft references are GC'd, the timeStamp // will no longer be contiguous, it will be // unique, so this is ok. if ( diagnostics ) { ct.action = REMOVE; setChanged(); notifyObservers(ct); } ct.previous = null; ct.next = null; ct = null; } } } /** * Retrieves a tile from the cache. * *

If the specified tile is not in the cache, this method * returns null. If the specified tile is in the * cache, its last-access time is updated. * * @param owner The image the tile blongs to. * @param tileX The tile's X index within the image. * @param tileY The tile's Y index within the image. */ public synchronized Raster getTile(RenderedImage owner, int tileX, int tileY) { Raster tile = null; if ( memoryCapacity == 0 ) { return null; } Object key = SunCachedTile.hashKey(owner, tileX, tileY); SunCachedTile ct = (SunCachedTile)cache.get(key); if ( ct == null ) { missCount++; } else { // found tile in cache tile = (Raster) ct.getTile(); // Update last-access time. (update() inlined for performance) ct.timeStamp = timeStamp++; if (ct != first) { // Bring this tile to the beginning of the list. if (ct == last) { last = ct.previous; last.next = null; } else { ct.previous.next = ct.next; ct.next.previous = ct.previous; } ct.previous = null; ct.next = first; first.previous = ct; first = ct; } hitCount++; if ( diagnostics ) { ct.action = UPDATE_FROM_GETTILE; setChanged(); notifyObservers(ct); } } return tile; } /** * Retrieves a contiguous array of all tiles in the cache which are * owned by the specified image. May be null if there * were no tiles in the cache. The array contains no null entries. * * @param owner The RenderedImage to which the tiles belong. * @return An array of all tiles owned by the specified image or * null if there are none currently in the cache. */ public synchronized Raster[] getTiles(RenderedImage owner) { Raster[] tiles = null; if ( memoryCapacity == 0 ) { return null; } int size = Math.min(owner.getNumXTiles() * owner.getNumYTiles(), (int)tileCount); if ( size > 0 ) { int minTx = owner.getMinTileX(); int minTy = owner.getMinTileY(); int maxTx = minTx + owner.getNumXTiles(); int maxTy = minTy + owner.getNumYTiles(); // arbitrarily set a temporary vector size Vector temp = new Vector(10, 20); for (int y = minTy; y < maxTy; y++) { for (int x = minTx; x < maxTx; x++) { // inline this method //Raster raster = getTile(owner, x, y); //************************ Raster raster = null; Object key = SunCachedTile.hashKey(owner, x, y); SunCachedTile ct = (SunCachedTile)cache.get(key); if ( ct == null ) { raster = null; missCount++; } else { // found tile in cache raster = (Raster) ct.getTile(); // Update last-access time. (update() inlined for performance) ct.timeStamp = timeStamp++; if (ct != first) { // Bring this tile to the beginning of the list. if (ct == last) { last = ct.previous; last.next = null; } else { ct.previous.next = ct.next; ct.next.previous = ct.previous; } ct.previous = null; ct.next = first; first.previous = ct; first = ct; } hitCount++; if ( diagnostics ) { ct.action = UPDATE_FROM_GETTILE; setChanged(); notifyObservers(ct); } } //************************ if ( raster != null ) { temp.add(raster); } } } int tmpsize = temp.size(); if ( tmpsize > 0 ) { tiles = (Raster[])temp.toArray(new Raster[tmpsize]); } } return tiles; } /** * Removes all the tiles that belong to a RenderedImage * from the cache. * * @param owner The image whose tiles are to be removed from the cache. */ public void removeTiles(RenderedImage owner) { if ( memoryCapacity > 0 ) { int minTx = owner.getMinTileX(); int minTy = owner.getMinTileY(); int maxTx = minTx + owner.getNumXTiles(); int maxTy = minTy + owner.getNumYTiles(); for (int y=minTy; yRenderedImage that the tile belongs to. * @param tileIndices An array of Points containing the * tileX and tileY indices for each tile. * @param tiles The array of tile Rasters containing tile data. * @param tileCacheMetric Object which provides an ordering metric * associated with the RenderedImage owner. * @since 1.1 */ public synchronized void addTiles(RenderedImage owner, Point[] tileIndices, Raster[] tiles, Object tileCacheMetric) { if ( memoryCapacity == 0 ) { return; } // this just inlines the add routine (no sync overhead for each call). for ( int i = 0; i < tileIndices.length; i++ ) { int tileX = tileIndices[i].x; int tileY = tileIndices[i].y; Raster tile = tiles[i]; Object key = SunCachedTile.hashKey(owner, tileX, tileY); SunCachedTile ct = (SunCachedTile) cache.get(key); if ( ct != null ) { // tile is cached, inlines update() ct.timeStamp = timeStamp++; if (ct != first) { // Bring this tile to the beginning of the list. if (ct == last) { last = ct.previous; last.next = null; } else { ct.previous.next = ct.next; ct.next.previous = ct.previous; } ct.previous = null; ct.next = first; first.previous = ct; first = ct; } hitCount++; if ( diagnostics ) { ct.action = UPDATE_FROM_ADD; setChanged(); notifyObservers(ct); } } else { // create a new tile ct = new SunCachedTile(owner, tileX, tileY, tile, tileCacheMetric); // Don't cache tile if adding it would provoke memoryControl() // which would in turn only end up removing the tile. if(memoryUsage + ct.memorySize > memoryCapacity && ct.memorySize > (long)(memoryCapacity * memoryThreshold)) { return; } ct.timeStamp = timeStamp++; ct.previous = null; ct.next = first; if (first == null && last == null) { first = ct; last = ct; } else { first.previous = ct; first = ct; // put this tile at the top of the list } // add to tile cache if ( cache.put(ct.key, ct) == null ) { memoryUsage += ct.memorySize; tileCount++; //missCount++; Not necessary? if ( cacheSortedSet != null ) { cacheSortedSet.add(ct); } if ( diagnostics ) { ct.action = ADD; setChanged(); notifyObservers(ct); } } // Bring memory usage down to memoryThreshold % of memory capacity. if (memoryUsage > memoryCapacity) { memoryControl(); } } } } /** * Returns an array of tile Rasters from the cache. * Any or all of the elements of the returned array may be null * if the corresponding tile is not in the cache. * * @param owner The RenderedImage that the tile belongs to. * @param tileIndices An array of Points containing the * tileX and tileY indices for each tile. * @since 1.1 */ public synchronized Raster[] getTiles(RenderedImage owner, Point[] tileIndices) { if ( memoryCapacity == 0 ) { return null; } Raster[] tiles = new Raster[tileIndices.length]; for ( int i = 0; i < tiles.length; i++ ) { int tileX = tileIndices[i].x; int tileY = tileIndices[i].y; Object key = SunCachedTile.hashKey(owner, tileX, tileY); SunCachedTile ct = (SunCachedTile)cache.get(key); if ( ct == null ) { tiles[i] = null; missCount++; } else { // found tile in cache tiles[i] = (Raster) ct.getTile(); // Update last-access time. (update() inlined for performance) ct.timeStamp = timeStamp++; if (ct != first) { // Bring this tile to the beginning of the list. if (ct == last) { last = ct.previous; last.next = null; } else { ct.previous.next = ct.next; ct.next.previous = ct.previous; } ct.previous = null; ct.next = first; first.previous = ct; first = ct; } hitCount++; if ( diagnostics ) { ct.action = UPDATE_FROM_GETTILE; setChanged(); notifyObservers(ct); } } } return tiles; } /** Removes -ALL- tiles from the cache. */ public synchronized void flush() { // // It is necessary to clear all the elements // from the old cache in order to remove dangling // references, due to the linked list. In other // words, it is possible to reache the object // through 2 paths so the object does not // become weakly reachable until the reference // to it in the hash map is null. It is not enough // to just set the object to null. // Enumeration keys = cache.keys(); // all keys in Hashtable // reset counters before diagnostics hitCount = 0; missCount = 0; while (keys.hasMoreElements()) { Object key = keys.nextElement(); SunCachedTile ct = (SunCachedTile) cache.remove(key); // recalculate memoryUsage only if tile is actually removed if ( ct != null ) { memoryUsage -= ct.memorySize; tileCount--; if ( ct == first ) { if ( ct == last ) { first = null; // only one tile in the list last = null; } else { first = ct.next; first.previous = null; } } else if ( ct == last ) { last = ct.previous; last.next = null; } else { ct.previous.next = ct.next; ct.next.previous = ct.previous; } ct.previous = null; ct.next = null; // diagnostics if ( diagnostics ) { ct.action = REMOVE_FROM_FLUSH; setChanged(); notifyObservers(ct); } } } if ( memoryCapacity > 0 ) { cache = new Hashtable(DEFAULT_HASHTABLE_CAPACITY, LOAD_FACTOR); } if ( cacheSortedSet != null ) { cacheSortedSet.clear(); cacheSortedSet = Collections.synchronizedSortedSet( new TreeSet(comparator) ); } // force reset after diagnostics tileCount = 0; timeStamp = 0; memoryUsage = 0; // no System.gc() here, it's too slow and may occur anyway. } /** * Returns the cache's tile capacity. * *

This implementation of TileCache does not use * the tile capacity. This method always returns 0. */ public int getTileCapacity() { return 0; } /** * Sets the cache's tile capacity to the desired number of tiles. * *

This implementation of TileCache does not use * the tile capacity. The cache size is limited by the memory * capacity only. This method does nothing and has no effect on * the cache. * * @param tileCapacity The desired tile capacity for this cache * in number of tiles. */ public void setTileCapacity(int tileCapacity) { } /** Returns the cache's memory capacity in bytes. */ public long getMemoryCapacity() { return memoryCapacity; } /** * Sets the cache's memory capacity to the desired number of bytes. * If the new memory capacity is smaller than the amount of memory * currently being used by this cache, tiles are removed from the * cache until the memory usage is less than the specified memory * capacity. * * @param memoryCapacity The desired memory capacity for this cache * in bytes. * * @throws IllegalArgumentException If memoryCapacity * is less than 0. */ public void setMemoryCapacity(long memoryCapacity) { if (memoryCapacity < 0) { throw new IllegalArgumentException(JaiI18N.getString("SunTileCache")); } else if ( memoryCapacity == 0 ) { flush(); } this.memoryCapacity = memoryCapacity; if ( memoryUsage > memoryCapacity ) { memoryControl(); } } /** Enable Tile Monitoring and Diagnostics */ public void enableDiagnostics() { diagnostics = true; } /** Turn off diagnostic notification */ public void disableDiagnostics() { diagnostics = false; } public long getCacheTileCount() { return tileCount; } public long getCacheMemoryUsed() { return memoryUsage; } public long getCacheHitCount() { return hitCount; } public long getCacheMissCount() { return missCount; } /** * Reset hit and miss counters. * * @since 1.1 */ public void resetCounts() { hitCount = 0; missCount = 0; } /** * Set the memory threshold value. * * @since 1.1 */ public void setMemoryThreshold(float mt) { if ( mt < 0.0F || mt > 1.0F ) { throw new IllegalArgumentException(JaiI18N.getString("SunTileCache")); } else { memoryThreshold = mt; memoryControl(); } } /** * Returns the current memoryThreshold. * * @since 1.1 */ public float getMemoryThreshold() { return memoryThreshold; } /** Returns a string representation of the class object. */ public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()) + ": memoryCapacity = " + Long.toHexString(memoryCapacity) + " memoryUsage = " + Long.toHexString(memoryUsage) + " #tilesInCache = " + Integer.toString(cache.size()); } /** Returns the Object that represents the actual cache. */ public Object getCachedObject() { return cache; } /** * Removes tiles from the cache based on their last-access time * (old to new) until the memory usage is memoryThreshold % of that of the * memory capacity. */ public synchronized void memoryControl() { if ( cacheSortedSet == null ) { standard_memory_control(); } else { custom_memory_control(); } } // time stamp based memory control (LRU) private final void standard_memory_control() { long limit = (long)(memoryCapacity * memoryThreshold); while( memoryUsage > limit && last != null ) { SunCachedTile ct = (SunCachedTile) cache.get(last.key); if ( ct != null ) { ct = (SunCachedTile) cache.remove(last.key); memoryUsage -= last.memorySize; tileCount--; last = last.previous; if (last != null) { last.next.previous = null; last.next = null; } else { first = null; } // diagnostics if ( diagnostics ) { ct.action = REMOVE_FROM_MEMCON; setChanged(); notifyObservers(ct); } } } } // comparator based memory control (TreeSet) private final void custom_memory_control() { long limit = (long)(memoryCapacity * memoryThreshold); Iterator iter = cacheSortedSet.iterator(); SunCachedTile ct; while( iter.hasNext() && (memoryUsage > limit) ) { ct = (SunCachedTile) iter.next(); memoryUsage -= ct.memorySize; tileCount--; // remove from sorted set try { iter.remove(); } catch(ConcurrentModificationException e) { ImagingListener listener = ImageUtil.getImagingListener((RenderingHints)null); listener.errorOccurred(JaiI18N.getString("SunTileCache0"), e, this, false); // e.printStackTrace(); } // remove tile from the linked list if ( ct == first ) { if ( ct == last ) { first = null; last = null; } else { first = ct.next; if ( first != null ) { first.previous = null; first.next = ct.next.next; } } } else if ( ct == last ) { last = ct.previous; if ( last != null ) { last.next = null; last.previous = ct.previous.previous; } } else { SunCachedTile ptr = first.next; while( ptr != null ) { if ( ptr == ct ) { if ( ptr.previous != null ) { ptr.previous.next = ptr.next; } if ( ptr.next != null ) { ptr.next.previous = ptr.previous; } break; } ptr = ptr.next; } } // remove reference in the hashtable cache.remove(ct.key); // diagnostics if ( diagnostics ) { ct.action = REMOVE_FROM_MEMCON; setChanged(); notifyObservers(ct); } } // If the custom memory control didn't release sufficient // number of tiles to satisfy the memory limit, fallback // to the standard memory controller. if ( memoryUsage > limit ) { standard_memory_control(); } } /** * The Comparator is used to produce an * ordered list of tiles based on a user defined * compute cost or priority metric. This determines * which tiles are subject to "ordered" removal * during a memory control operation. * * @since 1.1 */ public synchronized void setTileComparator(Comparator c) { comparator = c; if ( comparator == null ) { // turn of comparator if ( cacheSortedSet != null ) { cacheSortedSet.clear(); cacheSortedSet = null; } } else { // copy tiles from hashtable to sorted tree set cacheSortedSet = Collections.synchronizedSortedSet( new TreeSet(comparator) ); Enumeration keys = cache.keys(); while( keys.hasMoreElements() ) { Object key = keys.nextElement(); Object ct = cache.get(key); cacheSortedSet.add(ct); } } } /** * Return the current comparator * * @since 1.1 */ public Comparator getTileComparator() { return comparator; } // test public void dump() { System.out.println("first = " + first); System.out.println("last = " + last); Iterator iter = cacheSortedSet.iterator(); int k = 0; while( iter.hasNext() ) { SunCachedTile ct = (SunCachedTile) iter.next(); System.out.println(k++); System.out.println(ct); } } void sendExceptionToListener(String message, Exception e) { ImagingListener listener = ImageUtil.getImagingListener((RenderingHints)null); listener.errorOccurred(message, e, this, false); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/util/SimpleCMYKColorSpace.java0000644000175000017500000000567210375417505027344 0ustar mathieumathieu/* * $RCSfile: SimpleCMYKColorSpace.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.5 $ * $Date: 2006-02-17 19:08:53 $ * $State: Exp $ */ package com.sun.media.jai.util; import java.awt.color.ColorSpace; /** * Singleton class representing a simple, mathematically defined CMYK * color space. */ public final class SimpleCMYKColorSpace extends ColorSpace { private static ColorSpace theInstance = null; private ColorSpace csRGB; /** The exponent for gamma correction. */ private static final double power1 = 1.0 / 2.4; public static final synchronized ColorSpace getInstance() { if(theInstance == null) { theInstance = new SimpleCMYKColorSpace(); } return theInstance; } private SimpleCMYKColorSpace() { super(TYPE_CMYK, 4); csRGB = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB); } public boolean equals(Object o) { return o != null && o instanceof SimpleCMYKColorSpace; } public float[] toRGB(float[] colorvalue) { float C = colorvalue[0]; float M = colorvalue[1]; float Y = colorvalue[2]; float K = colorvalue[3]; float K1 = 1.0F - K; // Convert from CMYK to linear RGB. float[] rgbvalue = new float[] {K1*(1.0F - C), K1*(1.0F - M), K1*(1.0F - Y)}; // Convert from linear RGB to sRGB. for (int i = 0; i < 3; i++) { float v = rgbvalue[i]; if (v < 0.0F) v = 0.0F; if (v < 0.0031308F) { rgbvalue[i] = 12.92F * v; } else { if (v > 1.0F) v = 1.0F; rgbvalue[i] = (float)(1.055 * Math.pow(v, power1) - 0.055); } } return rgbvalue; } public float[] fromRGB(float[] rgbvalue) { // Convert from sRGB to linear RGB. for (int i = 0; i < 3; i++) { if (rgbvalue[i] < 0.040449936F) { rgbvalue[i] /= 12.92F; } else { rgbvalue[i] = (float)(Math.pow((rgbvalue[i] + 0.055)/1.055, 2.4)); } } // Convert from linear RGB to CMYK. float C = 1.0F - rgbvalue[0]; float M = 1.0F - rgbvalue[1]; float Y = 1.0F - rgbvalue[2]; float K = Math.min(C, Math.min(M, Y)); // If K == 1.0F, then C = M = Y = 1.0F. if(K != 1.0F) { float K1 = 1.0F - K; C = (C - K)/K1; M = (M - K)/K1; Y = (Y - K)/K1; } else { C = M = Y = 0.0F; } return new float[] {C, M, Y, K}; } public float[] toCIEXYZ(float[] colorvalue) { return csRGB.toCIEXYZ(toRGB(colorvalue)); } public float[] fromCIEXYZ(float[] xyzvalue) { return fromRGB(csRGB.fromCIEXYZ(xyzvalue)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/util/MathJAI.java0000644000175000017500000000215210203035544024644 0ustar mathieumathieu/* * $RCSfile: MathJAI.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:01 $ * $State: Exp $ */ package com.sun.media.jai.util; /** * A utility class to contain miscellaneous static methods. */ public class MathJAI { /** * Calculate the smallest positive power of 2 greater than or equal to * the provided parameter. * * @param n The value for which the next power of 2 is to be found. * @return The smallest power of 2 >= n. */ public static final int nextPositivePowerOf2(int n) { if(n < 2) { return 2; } int power = 1; while(power < n) { power <<= 1; } return power; } /** * Determine whether the parameter is equal to a positive power of 2. * * @param n The value to check. * @return Whether n is a positive power of 2. */ public static final boolean isPositivePowerOf2(int n) { return n == nextPositivePowerOf2(n); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/util/Service.java0000644000175000017500000003437010203035544025036 0ustar mathieumathieu/* * $RCSfile: Service.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:02 $ * $State: Exp $ */ // This code is copied from the javax.media.imageio.spi // // Original SCCS ID: @(#)Service.java 1.4 00/05/23 package com.sun.media.jai.util; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import java.util.Set; import java.util.TreeSet; /** * A simple service-provider lookup mechanism. A service is a * well-known set of interfaces and (usually abstract) classes. A service * provider is a specific implementation of a service. The classes in a * provider typically implement the interfaces and subclass the classes defined * in the service itself. Service providers may be installed in an * implementation of the Java platform in the form of extensions, that is, jar * files placed into any of the usual extension directories. Providers may * also be made available by adding them to the applet or application class * path or by some other platform-specific means. * *

In this lookup mechanism a service is represented by an interface or an * abstract class. (A concrete class may be used, but this is not * recommended.) A provider of a given service contains one or more concrete * classes that extend this service class with data and code specific to * the provider. This provider class will typically not be the entire * provider itself but rather a proxy that contains enough information to * decide whether the provider is able to satisfy a particular request together * with code that can create the actual provider on demand. The details of * provider classes tend to be highly service-specific; no single class or * interface could possibly unify them, so no such class has been defined. The * only requirement enforced here is that provider classes must have a * zero-argument constructor so that they may be instantiated during lookup. * *

A service provider identifies itself by placing a provider-configuration * file in the resource directory META-INF/services. The file's name * should consist of the fully-qualified name of the abstract service class. * The file should contain a list of fully-qualified concrete provider-class * names, one per line. Space and tab characters surrounding each name, as * well as blank lines, are ignored. The comment character is '#' * (0x23); on each line all characters following the first comment * character are ignored. The file must be encoded in UTF-8. * *

If a particular concrete provider class is named in more than one * configuration file, or is named in the same configuration file more than * once, then the duplicates will be ignored. The configuration file naming a * particular provider need not be in the same jar file or other distribution * unit as the provider itself. The provider must be accessible from the same * class loader that was initially queried to locate the configuration file; * note that this is not necessarily the class loader that found the file. * *

Example: Suppose we have a service class named * java.io.spi.CharCodec. It has two abstract methods: * *

 *   public abstract CharEncoder getEncoder(String encodingName);
 *   public abstract CharDecoder getDecoder(String encodingName);
 * 
* * Each method returns an appropriate object or null if it cannot * translate the given encoding. Typical CharCodec providers will * support more than one encoding. * *

If sun.io.StandardCodec is a provider of the CharCodec * service then its jar file would contain the file * META-INF/services/java.io.spi.CharCodec. This file would contain * the single line: * *

 *   sun.io.StandardCodec    # Standard codecs for the platform
 * 
* * To locate an encoder for a given encoding name, the internal I/O code would * do something like this: * *
 *   CharEncoder getEncoder(String encodingName) {
 *       Iterator ps = Service.providers(CharCodec.class);
 *       while (ps.hasNext()) {
 *           CharCodec cc = (CharCodec)ps.next();
 *           CharEncoder ce = cc.getEncoder(encodingName);
 *           if (ce != null)
 *               return ce;
 *       }
 *       return null;
 *   }
 * 
* * The provider-lookup mechanism always executes in the security context of the * caller. Trusted system code should typically invoke the methods in this * class from within a privileged security context. * * @since 1.3 */ public final class Service { private static final String prefix = "META-INF/services/"; private Service() { } private static void fail(Class service, String msg) throws ServiceConfigurationError { throw new ServiceConfigurationError(service.getName() + ": " + msg); } private static void fail(Class service, URL u, int line, String msg) throws ServiceConfigurationError { fail(service, u + ":" + line + ": " + msg); } /** * Parse a single line from the given configuration file, adding the name * on the line to both the names list and the returned set iff the name is * not already a member of the returned set. */ private static int parseLine(Class service, URL u, BufferedReader r, int lc, List names, Set returned) throws IOException, ServiceConfigurationError { String ln = r.readLine(); if (ln == null) { return -1; } int ci = ln.indexOf('#'); if (ci >= 0) ln = ln.substring(0, ci); ln = ln.trim(); int n = ln.length(); if (n != 0) { if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0)) fail(service, u, lc, "Illegal configuration-file syntax"); if (!Character.isJavaIdentifierStart(ln.charAt(0))) fail(service, u, lc, "Illegal provider-class name: " + ln); for (int i = 1; i < n; i++) { char c = ln.charAt(i); if (!Character.isJavaIdentifierPart(c) && (c != '.')) fail(service, u, lc, "Illegal provider-class name: " + ln); } if (!returned.contains(ln)) { names.add(ln); returned.add(ln); } } return lc + 1; } /** * Parse the content of the given URL as a provider-configuration file. * * @param service * The service class for which providers are being sought; * used to construct error detail strings * * @param url * The URL naming the configuration file to be parsed * * @param returned * A Set containing the names of provider classes that have already * been returned. This set will be updated to contain the names * that will be yielded from the returned Iterator. * * @return A (possibly empty) Iterator that will yield the * provider-class names in the given configuration file that are * not yet members of the returned set * * @throws ServiceConfigurationError * If an I/O error occurs while reading from the given URL, or * if a configuration-file format error is detected */ private static Iterator parse(Class service, URL u, Set returned) throws ServiceConfigurationError { InputStream in = null; BufferedReader r = null; ArrayList names = new ArrayList(); try { in = u.openStream(); r = new BufferedReader(new InputStreamReader(in, "utf-8")); int lc = 1; while ((lc = parseLine(service, u, r, lc, names, returned)) >= 0); } catch (IOException x) { fail(service, ": " + x); } finally { try { if (r != null) r.close(); if (in != null) in.close(); } catch (IOException y) { fail(service, ": " + y); } } return names.iterator(); } /** * Private inner class implementing fully-lazy provider lookup */ private static class LazyIterator implements Iterator { Class service; ClassLoader loader; Enumeration configs = null; Iterator pending = null; Set returned = new TreeSet(); String nextName = null; private LazyIterator(Class service, ClassLoader loader) { this.service = service; this.loader = loader; } public boolean hasNext() throws ServiceConfigurationError { if (nextName != null) { return true; } if (configs == null) { try { String fullName = prefix + service.getName(); if (loader == null) configs = ClassLoader.getSystemResources(fullName); else configs = loader.getResources(fullName); } catch (IOException x) { fail(service, ": " + x); } } while ((pending == null) || !pending.hasNext()) { if (!configs.hasMoreElements()) { return false; } pending = parse(service, (URL)configs.nextElement(), returned); } nextName = (String)pending.next(); return true; } public Object next() throws ServiceConfigurationError { if (!hasNext()) { throw new NoSuchElementException(); } String cn = nextName; nextName = null; try { return Class.forName(cn, true, loader).newInstance(); } catch (ClassNotFoundException x) { fail(service, "Provider " + cn + " not found"); } catch (Exception x) { fail(service, "Provider " + cn + " could not be instantiated: " + x); } return null; /* This cannot happen */ } public void remove() { throw new UnsupportedOperationException(); } } /** * Locates and incrementally instantiates the available providers of a * given service using the given class loader. * *

This method transforms the name of the given service class into a * provider-configuration filename as described above and then uses the * getResources method of the given class loader to find all * available files with that name. These files are then read and parsed to * produce a list of provider-class names. The iterator that is returned * uses the given class loader to lookup and then instantiate each element * of the list. * *

Because it is possible for extensions to be installed into a running * Java virtual machine, this method may return different results each time * it is invoked.

* * @param service * The service's abstract service class * * @param loader * The class loader to be used to load provider-configuration files * and instantiate provider classes, or null if the system * class loader (or, failing that the bootstrap class loader) is to * be used * * @return An Iterator that yields provider objects for the given * service, in some arbitrary order. The iterator will throw a * ServiceConfigurationError if a provider-configuration * file violates the specified format or if a provider class cannot * be found and instantiated. * * @throws ServiceConfigurationError * If a provider-configuration file violates the specified format * or names a provider class that cannot be found and instantiated * * @see #providers(java.lang.Class) * @see #installedProviders(java.lang.Class) */ public static Iterator providers(Class service, ClassLoader loader) throws ServiceConfigurationError { return new LazyIterator(service, loader); } /** * Locates and incrementally instantiates the available providers of a * given service using the context class loader. This convenience method * is equivalent to * *

     *   ClassLoader cl = Thread.currentThread().getContextClassLoader();
     *   return Service.providers(service, cl);
     * 
* * @param service * The service's abstract service class * * @return An Iterator that yields provider objects for the given * service, in some arbitrary order. The iterator will throw a * ServiceConfigurationError if a provider-configuration * file violates the specified format or if a provider class cannot * be found and instantiated. * * @throws ServiceConfigurationError * If a provider-configuration file violates the specified format * or names a provider class that cannot be found and instantiated * * @see #providers(java.lang.Class, java.lang.ClassLoader) */ public static Iterator providers(Class service) throws ServiceConfigurationError { ClassLoader cl = Thread.currentThread().getContextClassLoader(); return Service.providers(service, cl); } /** * Locates and incrementally instantiates the available providers of a * given service using the extension class loader. This convenience method * simply locates the extension class loader, call it * extClassLoader, and then does * *
     *   return Service.providers(service, extClassLoader);
     * 
* * If the extension class loader cannot be found then the system class * loader is used; if there is no system class loader then the bootstrap * class loader is used. * * @param service * The service's abstract service class * * @return An Iterator that yields provider objects for the given * service, in some arbitrary order. The iterator will throw a * ServiceConfigurationError if a provider-configuration * file violates the specified format or if a provider class cannot * be found and instantiated. * * @throws ServiceConfigurationError * If a provider-configuration file violates the specified format * or names a provider class that cannot be found and instantiated * * @see #providers(java.lang.Class, java.lang.ClassLoader) */ public static Iterator installedProviders(Class service) throws ServiceConfigurationError { ClassLoader cl = ClassLoader.getSystemClassLoader(); if (cl != null) cl = cl.getParent(); return Service.providers(service, cl); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/util/ServiceConfigurationError.java0000644000175000017500000000202410203035544030567 0ustar mathieumathieu/* * $RCSfile: ServiceConfigurationError.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:02 $ * $State: Exp $ */ // This code is copied from the javax.media.imageio.spi // // Original SCCS ID: @(#)Service.java 1.4 00/05/23 package com.sun.media.jai.util; /** * Error thrown when something goes wrong while looking up service providers. * In particular, this error will be thrown in the following situations: * *
    *
  • A concrete provider class cannot be found, *
  • A concrete provider class cannot be instantiated, *
  • The format of a provider-configuration file is illegal, or *
  • An IOException occurs while reading a provider-configuration file. *
* * @since 1.3 */ class ServiceConfigurationError extends Error { /** * Constructs a new instance with the specified detail string. */ public ServiceConfigurationError(String msg) { super(msg); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/util/InterpAverage.java0000644000175000017500000000513210346160414026167 0ustar mathieumathieu/* * $RCSfile: InterpAverage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-12-09 01:42:36 $ * $State: Exp $ */package com.sun.media.jai.util; import javax.media.jai.Interpolation; /** * An Interpolation class which performs simple averaging of * all pixels within a specified neighborhood. It is used by the * "SubsampleAverage" operation implementations. * * @since JAI 1.1.2 */ public class InterpAverage extends Interpolation { /** * Creates an InterpAverage instance having the supplied * dimensions. The left and top padding are * (blockX - 1)/2 and * (blockY - 1)/2, respectively. The * subsampleBitsH and subsampleBitsV instance * variables are set to 32. * * @param blockX The width of the interpolation block. * @param blockY The height of the interpolation block. * * @throws IllegalArgumentException if either parameter is non-positive. */ public InterpAverage(int blockX, int blockY) { super(blockX, blockY, (blockX - 1)/2, blockX - 1 - (blockX - 1)/2, (blockY - 1)/2, blockY - 1 - (blockY - 1)/2, 32, 32); if(blockX <= 0 || blockY <= 0) { throw new IllegalArgumentException("blockX <= 0 || blockY <= 0"); } } /** * Returns the average of all elements in samples; * xfrac is ignored. */ public int interpolateH(int[] samples, int xfrac) { int numSamples = samples.length; double total = 0.0; for(int i = 0; i < numSamples; i++) { total += samples[i]/numSamples; } return (int)(total + 0.5); } /** * Returns the average of all elements in samples; * xfrac is ignored. */ public float interpolateH(float[] samples, float xfrac) { int numSamples = samples.length; float total = 0.0F; for(int i = 0; i < numSamples; i++) { total += samples[i]/numSamples; } return total; } /** * Returns the average of all elements in samples; * xfrac is ignored. */ public double interpolateH(double[] samples, float xfrac) { int numSamples = samples.length; double total = 0.0; for(int i = 0; i < numSamples; i++) { total += samples[i]/numSamples; } return total; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/util/CaselessStringArrayTable.java0000644000175000017500000000744110203035544030335 0ustar mathieumathieu/* * $RCSfile: CaselessStringArrayTable.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:59 $ * $State: Exp $ */ package com.sun.media.jai.util; import java.util.Hashtable; import javax.media.jai.util.CaselessStringKey; /** * A class that maps an array of Strings or * CaselessStringKeys into the array indices and * vice versa (all in a caseless fashion). * * This is used to map source names and parameter names to their * indices in a case insensitive fashion. */ public class CaselessStringArrayTable implements java.io.Serializable { private CaselessStringKey[] keys; private Hashtable indices; /** * Constructor for an array of CaselessStringKeys. */ public CaselessStringArrayTable() { this((CaselessStringKey[])null); } /** * Constructor for an array of CaselessStringKeys. */ public CaselessStringArrayTable(CaselessStringKey[] keys) { this.keys = keys; this.indices = new Hashtable(); if (keys != null) for (int i = 0; i < keys.length; i++) { this.indices.put(keys[i], new Integer(i)); } } /** * Constructor for an array of Strings. */ public CaselessStringArrayTable(String[] keys) { this(toCaselessStringKey(keys)); } /** * Map an array of Strings to CaselessStringKeys. */ private static CaselessStringKey[] toCaselessStringKey(String strings[]) { if (strings == null) return null; CaselessStringKey[] keys = new CaselessStringKey[strings.length]; for (int i = 0; i < strings.length; i++) keys[i] = new CaselessStringKey(strings[i]); return keys; } /** * Get the index of the specified key. * * @throws IllegalArgumentException if the key is null or * if the key is not found. */ public int indexOf(CaselessStringKey key) { if (key == null) { throw new IllegalArgumentException( JaiI18N.getString("CaselessStringArrayTable0")); } Integer i = (Integer)indices.get(key); if (i == null) { throw new IllegalArgumentException(key.getName() + " - " + JaiI18N.getString("CaselessStringArrayTable1")); } return i.intValue(); } /** * Get the index of the specified key. * * @throws IllegalArgumentException if the key is null or * if the key is not found. */ public int indexOf(String key) { return indexOf(new CaselessStringKey(key)); } /** * Get the String corresponding to the index i. * * @throws ArrayIndexOutOfBoundsException if i is out of range. */ public String getName(int i) { if (keys == null) throw new ArrayIndexOutOfBoundsException(); return keys[i].getName(); } /** * Get the CaselessStringKey corresponding to the * index i. * * @throws ArrayIndexOutOfBoundsException if i is out of range. */ public CaselessStringKey get(int i) { if (keys == null) throw new ArrayIndexOutOfBoundsException(); return keys[i]; } /** * Tests if this table contains the specified key. * * @return true if the key is present. false otherwise. */ public boolean contains(CaselessStringKey key) { if (key == null) { throw new IllegalArgumentException( JaiI18N.getString("CaselessStringArrayTable0")); } return indices.get(key) != null; } /** * Tests if this table contains the specified key. * * @return true if the key is present. false otherwise. */ public boolean contains(String key) { return contains(new CaselessStringKey(key)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/util/PolyWarpSolver.java0000644000175000017500000005352010665376131026420 0ustar mathieumathieu/* * $RCSfile: PolyWarpSolver.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2007-08-29 23:08:09 $ * $State: Exp $ */ package com.sun.media.jai.util; import java.util.Random; /** * A utility class to fit a polynomial to a set of corresponding * points in the source and destination images of a warp. The core is * based on a public-domain Fortran utility routine for singular-value * decomposition. * * @since EA2 * */ public class PolyWarpSolver { private static double sign(double a, double b) { a = Math.abs(a); if (b >= 0.0F) { return a; } else { return -a; } } private static final double square(double x) { return x*x; } private static final double sqrt(double x) { return (double)Math.sqrt(x); } private static final double hypot(double x, double y) { double xabs = Math.abs(x); double yabs = Math.abs(y); if (xabs > yabs) { return xabs*sqrt(square(yabs/xabs) + 1.0F); } else if (yabs != 0.0F) { return yabs*sqrt(square(xabs/yabs) + 1.0F); } else { return xabs; } } /* Multiply A * B^T */ public static double[][] matmul_t(double[][] A, double[][] B) { int rowsA = A.length; int colsA = A[0].length; int rowsB = B[0].length; int colsB = B.length; // Must have colsA == rowsB double[][] out = new double[rowsA][colsB]; for (int i = 0; i < rowsA; i++) { double[] outi = out[i]; double[] Ai = A[i]; for (int j = 0; j < colsB; j++) { double tmp = 0.0F; for (int k = 0; k < colsA; k++) { tmp += Ai[k]*B[j][k]; } outi[j] = tmp; } } return out; } /** * Performs Singular-Value Decomposition on a given matrix. The * number of rows of the matrix must be greater than or equal to * the number of columns. * *

When the routine completes, the product U*diag(W)*V^T * will be equal to the input matrix A. U will be column-orthogonal * and V will be fully orthogonal. The elements of W will be positive * or zero. * *

From the comments in the original Fortran version contained * in the Eispack library: * *

     * c     this subroutine is a translation of the algol procedure svd,
     * c     num. math. 14, 403-420(1970) by golub and reinsch.
     * c     handbook for auto. comp., vol ii-linear algebra, 134-151(1971).
     * c     Questions and comments should be directed to Alan K. Cline,
     * c     Pleasant Valley Software, 8603 Altus Cove, Austin, TX 78759.
     * c     Electronic mail to cline@cs.utexas.edu.
     * c
     * c     this version dated january 1989. (for the IBM 3090vf)
     * 
* * @param a the input matrix to be decomposed of size m x n. * @param w an empty vector of length n to be filled in. * @param u an empty matrix of size m x n to be filled in. * @param v an empty matrix of size n x n to be filled in. * @return true if convergence is acheived within 30 iterations. */ private static boolean SVD(double[][] a, double[] w, double [][] u, double[][] v) { int i, j, k, l, m, n, i1, k1, l1, mn, its; double c, f, g, h, s, x, y, z, tst1, tst2, scale; double fabs, gabs, habs; l = 0; l1 = 0; m = a.length; n = a[0].length; double[] rv1 = new double[n]; for (i = 0; i < m; i++) { for (j = 0; j < n; j++) { u[i][j] = a[i][j]; } } g = 0.0F; scale = 0.0F; x = 0.0F; for (i = 0; i < n; i++) { l = i + 1; rv1[i] = scale*g; g = 0.0F; s = 0.0F; scale = 0.0F; if (i < m) { for (k = i; k < m; k++) { scale += Math.abs(u[k][i]); } if (scale != 0.0F) { for (k = i; k < m; k++) { u[k][i] /= scale; s += square(u[k][i]); } f = u[i][i]; g = -sign(sqrt(s), f); h = f*g - s; u[i][i] = f - g; for (j = l; j < n; j++) { s = 0.0F; for (k = i; k < m; k++) { s += u[k][i]*u[k][j]; } f = s/h; for (k = i; k < m; k++) { u[k][j] += f*u[k][i]; } } for (k = i; k < m; k++) { u[k][i] *= scale; } } } w[i] = scale*g; g = 0.0F; s = 0.0F; scale = 0.0F; if ((i < m) && (i != n - 1)) { for (k = l; k < n; k++) { scale += Math.abs(u[i][k]); } if (scale != 0.0F) { for (k = l; k < n; k++) { u[i][k] /= scale; s += square(u[i][k]); } f = u[i][l]; g = -sign(sqrt(s), f); h = f*g - s; u[i][l] = f - g; for (k = l; k < n; k++) { rv1[k] = u[i][k]/h; } for (j = l; j < m; j++) { s = 0.0F; for (k = l; k < n; k++) { s += u[j][k]*u[i][k]; } for (k = l; k < n; k++) { u[j][k] += s*rv1[k]; } } for (k = l; k < n; k++) { u[i][k] *= scale; } } } x = Math.max(x, Math.abs(w[i]) + Math.abs(rv1[i])); } for (i = n - 1; i >= 0; i--) { if (i != n - 1) { if (g != 0.0F) { for (j = l; j < n; j++) { v[j][i] = (u[i][j]/u[i][l])/g; } for (j = l; j < n; j++) { s = 0.0F; for (k = l; k < n; k++) { s += u[i][k]*v[k][j]; } for (k = l; k < n; k++) { v[k][j] += s*v[k][i]; } } } for (j = l; j < n; j++) { v[i][j] = v[j][i] = 0.0F; } } v[i][i] = 1.0F; g = rv1[i]; l = i; } mn = Math.min(m, n); for (i = mn - 1; i >= 0; i--) { l = i + 1; g = w[i]; if (i != n - 1) { for (j = l; j < n; j++) { u[i][j] = 0.0F; } } if (g != 0.0F) { if (i != mn - 1) { for (j = l; j < n; j++) { s = 0.0F; for (k = l; k < m; k++) { s += u[k][i]*u[k][j]; } // 440 f = (s/u[i][i])/g; for (k = i; k < m; k++) { u[k][j] += f*u[k][i]; } } } for (j = i; j < m; j++) { u[j][i] /= g; } } else { for (j = i; j < m; j++) { u[j][i] = 0.0F; } } u[i][i] += 1.0F; } tst1 = x; for (k = n - 1; k >= 0; k--) { k1 = k - 1; its = 0; while (true) { boolean flag = true; for (l = k; l >= 0; l--) { l1 = l - 1; tst2 = tst1 + Math.abs(rv1[l]); if (tst2 == tst1) { flag = false; break; } tst2 = tst1 + Math.abs(w[l1]); if (tst2 == tst1) { flag = true; break; } } if (flag) { c = 0.0F; s = 1.0F; for (i = l; i < k + 1; i++) { f = s*rv1[i]; rv1[i] *= c; tst2 = tst1 + Math.abs(f); if (tst2 != tst1) { g = w[i]; h = hypot(f, g); w[i] = h; c = g/h; s = -f/h; for (j = 0; j < m; j++) { y = u[j][l1]; z = u[j][i]; u[j][l1] = y*c + z*s; u[j][i] = -y*s + z*c; } } } } z = w[k]; if (l == k) { if (z < 0.0F) { w[k] = -z; for (j = 0; j < n; j++) { v[j][k] = -v[j][k]; } } break; } if (its == 30) { return false; } ++its; x = w[l]; y = w[k1]; g = rv1[k1]; h = rv1[k]; f = 0.5F*(((g + z)/h)*((g - z)/y) + y/h - h/y); g = hypot(f, 1.0F); f = x - (z/x)*z + (h/x)*(y/(f + sign(g,f)) - h); c = 1.0F; s = 1.0F; for (i1 = l; i1 <= k1; i1++) { i = i1 + 1; g = rv1[i]; y = w[i]; h = s*g; g = c*g; z = hypot(f, h); rv1[i1] = z; c = f/z; s = h/z; f = x*c + g*s; g = -x*s + g*c; h = y*s; y = y*c; for (j = 0; j < n; j++) { x = v[j][i1]; z = v[j][i]; v[j][i1] = x*c + z*s; v[j][i] = -x*s + z*c; } z = hypot(f, h); w[i1] = z; if (z != 0.0F) { c = f/z; s = h/z; } f = c*g + s*y; x = -s*g + c*y; for (j = 0; j < m; j++) { y = u[j][i1]; z = u[j][i]; u[j][i1] = y*c + z*s; u[j][i] = -y*s + z*c; } } rv1[l] = 0.0F; rv1[k] = f; w[k] = x; } } return true; } /** * * @param sourceCoords a double array containing the source coordinates * x_0, y_0, x_1, y_1, ... * @param destCoords a double array containing the source coordinates * x_0, y_0, x_1, y_1, ... * @return the best-fit coefficients for a bivariate polynomial of the * given degree mapping the destination points into the source * points. The coefficients for the X polynomial are returned * first, followed by those for the Y polynomial. */ public static float[] getCoeffs(float[] sourceCoords, int sourceOffset, float[] destCoords, int destOffset, int numCoords, float preScaleX, float preScaleY, float postScaleX, float postScaleY, int degree) { int i, j, k; int equations = numCoords/2; /* for (i = 0; i < numCoords; i++) { System.out.println("sourceCoords[" + i + "] = " + sourceCoords[i + sourceOffset]); System.out.println("destCoords[" + i + "] = " + destCoords[i + destOffset]); } */ // Number of unknowns int unknowns = (degree + 1)*(degree + 2)/2; float[] out = new float[2*unknowns]; // Special case for 3-point affine mapping if ((degree == 1) && (numCoords == 3)) { double x0, x1, x2, y0, y1, y2; double u0, u1, u2, v0, v1, v2; x0 = sourceCoords[0]/postScaleX; y0 = sourceCoords[1]/postScaleY; x1 = sourceCoords[2]/postScaleX; y1 = sourceCoords[3]/postScaleY; x2 = sourceCoords[4]/postScaleX; y2 = sourceCoords[5]/postScaleY; u0 = destCoords[0]*preScaleX; v0 = destCoords[1]*preScaleY; u1 = destCoords[2]*preScaleX; v1 = destCoords[3]*preScaleY; u2 = destCoords[4]*preScaleX; v2 = destCoords[5]*preScaleY; double v0mv1 = v0 - v1; double v1mv2 = v1 - v2; double v2mv0 = v2 - v0; double u1mu0 = u1 - u0; double u2mu1 = u2 - u1; double u0mu2 = u0 - u2; double u1v2mu2v1 = u1*v2 - u2*v1; double u2v0mu0v2 = u2*v0 - u0*v2; double u0v1mu1v0 = u0*v1 - u1*v0; double invdet = 1.0F/(u0*(v1mv2) + v0*(u2mu1) + (u1v2mu2v1)); out[0] = (float)(((v1mv2)*x0 + (v2mv0)*x1 + (v0mv1)*x2)*invdet); out[1] = (float)(((u2mu1)*x0 + (u0mu2)*x1 + (u1mu0)*x2)*invdet); out[2] = (float)(((u1v2mu2v1)*x0 + (u2v0mu0v2)*x1 + (u0v1mu1v0)*x2)*invdet); out[3] = (float)(((v1mv2)*y0 + (v2mv0)*y1 + (v0mv1)*y2)*invdet); out[4] = (float)(((u2mu1)*y0 + (u0mu2)*y1 + (u1mu0)*y2)*invdet); out[5] = (float)(((u1v2mu2v1)*y0 + (u2v0mu0v2)*y1 + (u0v1mu1v0)*y2)*invdet); return out; } double[][] A = new double[equations][unknowns]; /* Fill in A with: 1 x_0 y_0 ... x_0*y_0^(n-1) y_0^n 1 x_1 y_1 ... x_1*y_1^(n-1) y_1^n ... 1 x_(k-1) y_(k-1) ... x_(k-1)*y_(k-1)^(n-1) y_(k-1)^n The height of the matrix is equal to the number of equations The width of the matrix is equal to the number of unknowns */ double[] xpow = new double[degree + 1]; double[] ypow = new double[degree + 1]; for (i = 0; i < equations; i++) { double[] Ai = A[i]; double x = destCoords[2*i + destOffset]*preScaleX; double y = destCoords[2*i + 1 + destOffset]*preScaleY; double xtmp = 1.0F; double ytmp = 1.0F; for (int d = 0; d <= degree; d++) { xpow[d] = xtmp; ypow[d] = ytmp; xtmp *= x; ytmp *= y; } int index = 0; for (int deg = 0; deg <= degree; deg++) { for (int ydeg = 0; ydeg <= deg; ydeg++) { Ai[index++] = xpow[deg - ydeg]*ypow[ydeg]; } } } double[][] V = new double[unknowns][unknowns]; double[] W = new double[unknowns]; double[][] U = new double[equations][unknowns]; SVD(A, W, U, V); // Multiply the columns of V by the inverted diagonal entries of W for (j = 0; j < unknowns; j++) { double winv = W[j]; if (winv != 0.0) { winv = 1.0F/winv; } for (i = 0; i < unknowns; i++) { V[i][j] *= winv; } } // Multiply V by U^T double[][] VWINVUT = matmul_t(V, U); // unknowns x equations // Multiply VWINVUT by source coords to yield output coefficients for (i = 0; i < unknowns; i++) { double tmp0 = 0; double tmp1 = 0; for (j = 0; j < equations; j++) { double val = VWINVUT[i][j]; tmp0 += val*sourceCoords[2*j + sourceOffset]/postScaleX; tmp1 += val*sourceCoords[2*j + 1 + sourceOffset]/postScaleY; } out[i] = (float)tmp0; out[i + unknowns] = (float)tmp1; } return out; } /* Code for unit test */ private static Random myRandom = new Random(0); private static double c0[] = new double[6]; private static double c1[] = new double[6]; private static double preScaleX; private static double preScaleY; private static double postScaleX; private static double postScaleY; private static double noise = 0.0F; private static float xpoly(float x, float y) { x *= (float) preScaleX; y *= (float) preScaleY; return (float)(postScaleX * (c0[0] + c0[1]*x + c0[2]*y + c0[3]*x*x + c0[4]*x*y + c0[5]*y*y + myRandom.nextDouble()*noise)); } private static float ypoly(float x, float y) { x *= (float) preScaleX; y *= (float) preScaleY; return (float)(postScaleY * (c1[0] + c1[1]*x + c1[2]*y + c1[3]*x*x + c1[4]*x*y + c1[5]*y*y + myRandom.nextDouble()*noise)); } private static void doTest(int equations, boolean print) { // Make up a pair of random polynomials for (int i = 0; i < 6; i++) { c0[i] = myRandom.nextDouble()*100.0F; c1[i] = myRandom.nextDouble()*100.0F; } // Make up random pre- and postScales: preScaleX = myRandom.nextDouble() + 2.0; // Value between 2 and 3 preScaleY = myRandom.nextDouble() + 3.0; // Value between 3 and 4 postScaleX = myRandom.nextDouble() + 4.0; // Value between 4 and 5 postScaleY = myRandom.nextDouble() + 5.0; // Value between 5 and 6 // Make up random destination coordinates float[] destCoords = new float[2*equations]; for (int i = 0; i < 2*equations; i++) { destCoords[i] = myRandom.nextFloat()*100.0F; } // Compute corresponding source coordinates float[] sourceCoords = new float[2*equations]; for (int i = 0; i < equations; i++) { sourceCoords[2*i] = xpoly(destCoords[2*i], destCoords[2*i + 1]); sourceCoords[2*i + 1] = ypoly(destCoords[2*i], destCoords[2*i + 1]); } // Recover the polynomials using the SVD algorithm float[] coeffs = getCoeffs(sourceCoords, 0, destCoords, 0, sourceCoords.length, (float)preScaleX, (float)preScaleY, (float)postScaleX, (float)postScaleY, 2); // Print the results if (print) { System.out.println("Using " + equations + " equations:"); for (int i = 0; i < 6; i++) { System.out.println("c0[" + i + "] = " + c0[i] + ", recovered as " + coeffs[i] + " (ratio = " + (c0[i]/coeffs[i]) + ")"); System.out.println("c1[" + i + "] = " + c1[i] + ", recovered as " + coeffs[i + 6] + " (ratio = " + (c1[i]/coeffs[i + 6]) + ")"); } } } public static void main(String[] args) { for (int times = 0; times < 3; times ++) { doTest(6 + 50*times, true); System.out.println(); } int trials = 10000; int points = 6; long startTime = System.currentTimeMillis(); for (int times = 0; times < trials; times ++) { doTest(points, false); } long endTime = System.currentTimeMillis(); System.out.println("Did " + trials + " " + points + "-point solutions in " + ((endTime - startTime)/1000.0F) + " seconds."); System.out.println("Rate = " + (trials*1000.F/(endTime - startTime)) + " trials/second"); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/util/ImagingListenerImpl.java0000644000175000017500000000534310203035544027337 0ustar mathieumathieu/* * $RCSfile: ImagingListenerImpl.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:00 $ * $State: Exp $ */ package com.sun.media.jai.util; import java.lang.ref.SoftReference; import javax.media.jai.OperationRegistry; import javax.media.jai.util.ImagingListener; /** * A simply implementation of the interface ImagingListener. * In the method errorOccurred, only the message and the * stack trace of the provided Throwable is printed * into the stream System.err. This keeps the * backward compatibility. * *

This class is a singleton that has only one instance. This single * instance can be retrieved by calling the static method * getInstance. * * @see ImagingListener * * @since JAI 1.1.2 */ public class ImagingListenerImpl implements ImagingListener { private static SoftReference reference = new SoftReference(null); /** * Retrieves the unique instance of this class the construction of * which is deferred until the first invocation of this method. */ public static ImagingListenerImpl getInstance() { synchronized(reference) { Object referent = reference.get(); ImagingListenerImpl listener; if (referent == null) { // First invocation or SoftReference has been cleared. reference = new SoftReference(listener = new ImagingListenerImpl()); } else { // SoftReference has not been cleared. listener = (ImagingListenerImpl)referent; } return listener; } } /** * The constructor. */ private ImagingListenerImpl() {} public synchronized boolean errorOccurred(String message, Throwable thrown, Object where, boolean isRetryable) throws RuntimeException { // Silent the RuntimeException occuring in any OperationRegistry // and rethrown all the other RuntimeExceptions. if (thrown instanceof RuntimeException && !(where instanceof OperationRegistry)) throw (RuntimeException)thrown; System.err.println("Error: " + message); System.err.println("Occurs in: " + ((where instanceof Class) ? ((Class)where).getName() : where.getClass().getName())); thrown.printStackTrace(System.err); return false; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/util/ImageUtil.java0000644000175000017500000017107510460237310025322 0ustar mathieumathieu/* * $RCSfile: ImageUtil.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2006-07-21 20:53:28 $ * $State: Exp $ */ package com.sun.media.jai.util; import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.image.ColorModel; import java.awt.image.ComponentSampleModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferInt; import java.awt.image.DataBufferShort; import java.awt.image.DataBufferUShort; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.Raster; import java.awt.image.SampleModel; import java.awt.image.SinglePixelPackedSampleModel; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderContext; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.math.BigInteger; import java.util.Arrays; import java.util.Map; import java.util.Vector; import javax.media.jai.DeferredData; import javax.media.jai.JAI; import javax.media.jai.KernelJAI; import javax.media.jai.PixelAccessor; import javax.media.jai.RasterAccessor; import javax.media.jai.PlanarImage; import javax.media.jai.UnpackedImageData; import javax.media.jai.util.ImagingException; import javax.media.jai.util.ImagingListener; import java.io.ByteArrayOutputStream; import java.io.PrintStream; public final class ImageUtil { /** The minimum value of a float. */ private static final float FLOAT_MIN = -Float.MAX_VALUE; /** The counter for images that use the method generateID to create * a UID. */ private static long counter; /** A constant used to extract a byte from a short or an int. */ public static final int BYTE_MASK = 0xFF; /** A constant used to extract an unsigned short from an int. */ public static final int USHORT_MASK = 0xFFFF; /** Clamps a number to the range supported by byte data type. */ public static final byte clampByte(int in) { return (in > 0xFF ? (byte)0xFF : (in >= 0 ? (byte)in : (byte)0)); } /** Clamps a number to the range supported by unsigned short data type. */ public static final short clampUShort(int in) { return (in > 0xFFFF ? (short)0xFFFF : (in >= 0 ? (short)in : (short)0)); } /** Clamps a number to the range supported by short data type. */ public static final short clampShort(int in) { return (in > Short.MAX_VALUE ? Short.MAX_VALUE : (in >= Short.MIN_VALUE ? (short)in : Short.MIN_VALUE)); } /** Clamps a number to the range supported by integer data type. */ public static final int clampInt(long in) { return (in > Integer.MAX_VALUE ? Integer.MAX_VALUE : (in >= Integer.MIN_VALUE ? (int)in : Integer.MIN_VALUE)); } /** Clamps a number to the range supported by float data type. */ public static final float clampFloat(double in) { return (in > Float.MAX_VALUE ? Float.MAX_VALUE : (in >= FLOAT_MIN ? (float)in : FLOAT_MIN)); } /** * Clamps and rounds a number to the range supported by * byte data type. The input number is float. */ public static final byte clampRoundByte(float in) { return (in > 0xFF ? (byte)0xFF : (in >= 0 ? (byte)(in + 0.5F) : (byte)0)); } /** * Clamps and rounds a number to the range supported by * byte data type. The input number is double. */ public static final byte clampRoundByte(double in) { return (in > 0xFF ? (byte)0xFF : (in >= 0 ? (byte)(in + 0.5) : (byte)0)); } /** * Clamps and rounds a number to the range supported by * unsigned short data type. The input number is float. */ public static final short clampRoundUShort(float in) { return (in > 0xFFFF ? (short)0xFFFF : (in >= 0 ? (short)(in + 0.5F) : (short)0)); } /** * Clamps and rounds a number to the range supported by * unsigned short data type. The input number is double. */ public static final short clampRoundUShort(double in) { return (in > 0xFFFF ? (short)0xFFFF : (in >= 0 ? (short)(in + 0.5) : (short)0)); } /** * Clamps and rounds a number to the range supported by * short data type. The input number is float. */ public static final short clampRoundShort(float in) { return (in > Short.MAX_VALUE ? Short.MAX_VALUE : (in >= Short.MIN_VALUE ? (short)Math.floor(in + 0.5F) : Short.MIN_VALUE)); } /** * Clamps and rounds a number to the range supported by * short data type. The input number is double. */ public static final short clampRoundShort(double in) { return (in > Short.MAX_VALUE ? Short.MAX_VALUE : (in >= Short.MIN_VALUE ? (short)Math.floor(in + 0.5) : Short.MIN_VALUE)); } /** * Clamps and rounds a number to the range supported by * integer data type. The input number is float. */ public static final int clampRoundInt(float in) { return (in > Integer.MAX_VALUE ? Integer.MAX_VALUE : (in >= Integer.MIN_VALUE ? (int)Math.floor(in + 0.5F) : Integer.MIN_VALUE)); } /** * Clamps and rounds a number to the range supported by * integer data type. The input number is double. */ public static final int clampRoundInt(double in) { return (in > Integer.MAX_VALUE ? Integer.MAX_VALUE : (in >= Integer.MIN_VALUE ? (int)Math.floor(in + 0.5) : Integer.MIN_VALUE)); } /** Clamps a positive number to the range supported by byte data type. */ public static final byte clampBytePositive(int in) { return (in > 0xFF ? (byte)0xFF : (byte)in); } /** Clamps a negative number to the range supported by byte data type. */ public static final byte clampByteNegative(int in) { return (in < 0 ? (byte)0 : (byte)in); } /** * Clamps a positive number to the range supported by * unsigned short data type. */ public static final short clampUShortPositive(int in) { return (in > 0xFFFF ? (short)0xFFFF : (short)in); } /* * Clamps a negative number to the range supported by * unsigned short data type. */ public static final short clampUShortNegative(int in) { return (in < 0 ? (short)0 : (short)in); } public static final void copyRaster(RasterAccessor src, RasterAccessor dst) { int srcPixelStride = src.getPixelStride(); int srcLineStride = src.getScanlineStride(); int[] srcBandOffsets = src.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstLineStride = dst.getScanlineStride(); int[] dstBandOffsets = dst.getBandOffsets(); int width = dst.getWidth() * dstPixelStride; int height = dst.getHeight() * dstLineStride; int bands = dst.getNumBands(); switch (dst.getDataType()) { case DataBuffer.TYPE_BYTE: byte[][] bSrcData = src.getByteDataArrays(); byte[][] bDstData = dst.getByteDataArrays(); for (int b = 0; b < bands; b++) { byte[] s = bSrcData[b]; byte[] d = bDstData[b]; int heightEnd = dstBandOffsets[b] + height; for (int dstLineOffset = dstBandOffsets[b], srcLineOffset = srcBandOffsets[b]; dstLineOffset < heightEnd; dstLineOffset += dstLineStride, srcLineOffset += srcLineStride) { int widthEnd = dstLineOffset + width; for (int dstPixelOffset = dstLineOffset, srcPixelOffset = srcLineOffset; dstPixelOffset < widthEnd; dstPixelOffset += dstPixelStride, srcPixelOffset += srcPixelStride) { d[dstPixelOffset] = s[srcPixelOffset]; } } } break; case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: short[][] sSrcData = src.getShortDataArrays(); short[][] sDstData = dst.getShortDataArrays(); for (int b = 0; b < bands; b++) { short[] s = sSrcData[b]; short[] d = sDstData[b]; int heightEnd = dstBandOffsets[b] + height; for (int dstLineOffset = dstBandOffsets[b], srcLineOffset = srcBandOffsets[b]; dstLineOffset < heightEnd; dstLineOffset += dstLineStride, srcLineOffset += srcLineStride) { int widthEnd = dstLineOffset + width; for (int dstPixelOffset = dstLineOffset, srcPixelOffset = srcLineOffset; dstPixelOffset < widthEnd; dstPixelOffset += dstPixelStride, srcPixelOffset += srcPixelStride) { d[dstPixelOffset] = s[srcPixelOffset]; } } } break; case DataBuffer.TYPE_INT: int[][] iSrcData = src.getIntDataArrays(); int[][] iDstData = dst.getIntDataArrays(); for (int b = 0; b < bands; b++) { int[] s = iSrcData[b]; int[] d = iDstData[b]; int heightEnd = dstBandOffsets[b] + height; for (int dstLineOffset = dstBandOffsets[b], srcLineOffset = srcBandOffsets[b]; dstLineOffset < heightEnd; dstLineOffset += dstLineStride, srcLineOffset += srcLineStride) { int widthEnd = dstLineOffset + width; for (int dstPixelOffset = dstLineOffset, srcPixelOffset = srcLineOffset; dstPixelOffset < widthEnd; dstPixelOffset += dstPixelStride, srcPixelOffset += srcPixelStride) { d[dstPixelOffset] = s[srcPixelOffset]; } } } break; case DataBuffer.TYPE_FLOAT: float[][] fSrcData = src.getFloatDataArrays(); float[][] fDstData = dst.getFloatDataArrays(); for (int b = 0; b < bands; b++) { float[] s = fSrcData[b]; float[] d = fDstData[b]; int heightEnd = dstBandOffsets[b] + height; for (int dstLineOffset = dstBandOffsets[b], srcLineOffset = srcBandOffsets[b]; dstLineOffset < heightEnd; dstLineOffset += dstLineStride, srcLineOffset += srcLineStride) { int widthEnd = dstLineOffset + width; for (int dstPixelOffset = dstLineOffset, srcPixelOffset = srcLineOffset; dstPixelOffset < widthEnd; dstPixelOffset += dstPixelStride, srcPixelOffset += srcPixelStride) { d[dstPixelOffset] = s[srcPixelOffset]; } } } break; case DataBuffer.TYPE_DOUBLE: double[][] dSrcData = src.getDoubleDataArrays(); double[][] dDstData = dst.getDoubleDataArrays(); for (int b = 0; b < bands; b++) { double[] s = dSrcData[b]; double[] d = dDstData[b]; int heightEnd = dstBandOffsets[b] + height; for (int dstLineOffset = dstBandOffsets[b], srcLineOffset = srcBandOffsets[b]; dstLineOffset < heightEnd; dstLineOffset += dstLineStride, srcLineOffset += srcLineStride) { int widthEnd = dstLineOffset + width; for (int dstPixelOffset = dstLineOffset, srcPixelOffset = srcLineOffset; dstPixelOffset < widthEnd; dstPixelOffset += dstPixelStride, srcPixelOffset += srcPixelStride) { d[dstPixelOffset] = s[srcPixelOffset]; } } } break; } if (dst.isDataCopy()) { dst.clampDataArrays(); dst.copyDataToRaster(); } } /** * Determines whether two SampleModels are "equal", i.e., * assignment-compatible. This signifies that the two SampleModels * are either the very same object or are two different objects * with identical characteristics. */ public boolean areEqualSampleModels(SampleModel sm1, SampleModel sm2) { if(sm1 == sm2) { // Identical objects. return true; } else if(sm1.getClass() == sm2.getClass() && sm1.getDataType() == sm2.getDataType() && sm1.getTransferType() == sm2.getTransferType() && sm1.getWidth() == sm2.getWidth() && sm1.getHeight() == sm2.getHeight()) { // At this point all common attributes are equivalent. Next test // those specific to the known direct subclasses of SampleModel. // Subclasses which are not known will always return false. if(sm1 instanceof ComponentSampleModel) { ComponentSampleModel csm1 = (ComponentSampleModel)sm1; ComponentSampleModel csm2 = (ComponentSampleModel)sm2; return csm1.getPixelStride() == csm2.getPixelStride() && csm1.getScanlineStride() == csm2.getScanlineStride() && Arrays.equals(csm1.getBankIndices(), csm2.getBankIndices()) && Arrays.equals(csm1.getBandOffsets(), csm2.getBandOffsets()); } else if(sm1 instanceof MultiPixelPackedSampleModel) { MultiPixelPackedSampleModel mpp1 = (MultiPixelPackedSampleModel)sm1; MultiPixelPackedSampleModel mpp2 = (MultiPixelPackedSampleModel)sm2; return mpp1.getPixelBitStride() == mpp2.getPixelBitStride() && mpp1.getScanlineStride() == mpp2.getScanlineStride() && mpp1.getDataBitOffset() == mpp2.getDataBitOffset(); } else if(sm1 instanceof SinglePixelPackedSampleModel) { SinglePixelPackedSampleModel spp1 = (SinglePixelPackedSampleModel)sm1; SinglePixelPackedSampleModel spp2 = (SinglePixelPackedSampleModel)sm2; return spp1.getScanlineStride() == spp2.getScanlineStride() && Arrays.equals(spp1.getBitMasks(), spp2.getBitMasks()); } } return false; } /// ---- BEGIN Binary data handling methods ---- /** * Check whether a SampleModel represents a binary * data set, i.e., a single band of data with one bit per pixel * packed into a MultiPixelPackedSampleModel. */ public static boolean isBinary(SampleModel sm) { return sm instanceof MultiPixelPackedSampleModel && ((MultiPixelPackedSampleModel)sm).getPixelBitStride() == 1 && sm.getNumBands() == 1; } /** * For the case of binary data (isBinary() returns * true), return the binary data as a packed byte array. * The data will be packed as eight bits per byte with no bit offset, * i.e., the first bit in each image line will be the left-most of the * first byte of the line. The line stride in bytes will be * (int)((getWidth()+7)/8). The length of the returned * array will be the line stride multiplied by getHeight() * * @return the binary data as a packed array of bytes with zero offset * of null if the data are not binary. * @throws IllegalArgumentException if isBinary() returns * false with the SampleModel of the * supplied Raster as argument. */ public static byte[] getPackedBinaryData(Raster raster, Rectangle rect) { SampleModel sm = raster.getSampleModel(); if(!isBinary(sm)) { throw new IllegalArgumentException(JaiI18N.getString("ImageUtil0")); } int rectX = rect.x; int rectY = rect.y; int rectWidth = rect.width; int rectHeight = rect.height; DataBuffer dataBuffer = raster.getDataBuffer(); int dx = rectX - raster.getSampleModelTranslateX(); int dy = rectY - raster.getSampleModelTranslateY(); MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)sm; int lineStride = mpp.getScanlineStride(); int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy); int bitOffset = mpp.getBitOffset(dx); int numBytesPerRow = (rectWidth + 7)/8; if(dataBuffer instanceof DataBufferByte && eltOffset == 0 && bitOffset == 0 && numBytesPerRow == lineStride && ((DataBufferByte)dataBuffer).getData().length == numBytesPerRow*rectHeight) { return ((DataBufferByte)dataBuffer).getData(); } byte[] binaryDataArray = new byte[numBytesPerRow*rectHeight]; int b = 0; if(bitOffset == 0) { if(dataBuffer instanceof DataBufferByte) { byte[] data = ((DataBufferByte)dataBuffer).getData(); int stride = numBytesPerRow; int offset = 0; for(int y = 0; y < rectHeight; y++) { System.arraycopy(data, eltOffset, binaryDataArray, offset, stride); offset += stride; eltOffset += lineStride; } } else if(dataBuffer instanceof DataBufferShort || dataBuffer instanceof DataBufferUShort) { short[] data = dataBuffer instanceof DataBufferShort ? ((DataBufferShort)dataBuffer).getData() : ((DataBufferUShort)dataBuffer).getData(); for(int y = 0; y < rectHeight; y++) { int xRemaining = rectWidth; int i = eltOffset; while(xRemaining > 8) { short datum = data[i++]; binaryDataArray[b++] = (byte)((datum >>> 8) & 0xFF); binaryDataArray[b++] = (byte)(datum & 0xFF); xRemaining -= 16; } if(xRemaining > 0) { binaryDataArray[b++] = (byte)((data[i] >>> 8) & 0XFF); } eltOffset += lineStride; } } else if(dataBuffer instanceof DataBufferInt) { int[] data = ((DataBufferInt)dataBuffer).getData(); for(int y = 0; y < rectHeight; y++) { int xRemaining = rectWidth; int i = eltOffset; while(xRemaining > 24) { int datum = data[i++]; binaryDataArray[b++] = (byte)((datum >>> 24) & 0xFF); binaryDataArray[b++] = (byte)((datum >>> 16) & 0xFF); binaryDataArray[b++] = (byte)((datum >>> 8) & 0xFF); binaryDataArray[b++] = (byte)(datum & 0xFF); xRemaining -= 32; } int shift = 24; while(xRemaining > 0) { binaryDataArray[b++] = (byte)((data[i] >>> shift) & 0xFF); shift -= 8; xRemaining -= 8; } eltOffset += lineStride; } } } else { // bitOffset != 0 if(dataBuffer instanceof DataBufferByte) { byte[] data = ((DataBufferByte)dataBuffer).getData(); if((bitOffset & 7) == 0) { int stride = numBytesPerRow; int offset = 0; for(int y = 0; y < rectHeight; y++) { System.arraycopy(data, eltOffset, binaryDataArray, offset, stride); offset += stride; eltOffset += lineStride; } } else { // bitOffset % 8 != 0 int leftShift = bitOffset & 7; int rightShift = 8 - leftShift; for(int y = 0; y < rectHeight; y++) { int i = eltOffset; int xRemaining = rectWidth; while(xRemaining > 0) { if(xRemaining > rightShift) { binaryDataArray[b++] = (byte)(((data[i++]&0xFF) << leftShift) | ((data[i]&0xFF) >>> rightShift)); } else { binaryDataArray[b++] = (byte)((data[i]&0xFF) << leftShift); } xRemaining -= 8; } eltOffset += lineStride; } } } else if(dataBuffer instanceof DataBufferShort || dataBuffer instanceof DataBufferUShort) { short[] data = dataBuffer instanceof DataBufferShort ? ((DataBufferShort)dataBuffer).getData() : ((DataBufferUShort)dataBuffer).getData(); for(int y = 0; y < rectHeight; y++) { int bOffset = bitOffset; for(int x = 0; x < rectWidth; x += 8, bOffset += 8) { int i = eltOffset + bOffset/16; int mod = bOffset % 16; int left = data[i] & 0xFFFF; if(mod <= 8) { binaryDataArray[b++] = (byte)(left >>> (8 - mod)); } else { int delta = mod - 8; int right = data[i+1] & 0xFFFF; binaryDataArray[b++] = (byte)((left << delta) | (right >>> (16 - delta))); } } eltOffset += lineStride; } } else if(dataBuffer instanceof DataBufferInt) { int[] data = ((DataBufferInt)dataBuffer).getData(); for(int y = 0; y < rectHeight; y++) { int bOffset = bitOffset; for(int x = 0; x < rectWidth; x += 8, bOffset += 8) { int i = eltOffset + bOffset/32; int mod = bOffset % 32; int left = data[i]; if(mod <= 24) { binaryDataArray[b++] = (byte)(left >>> (24 - mod)); } else { int delta = mod - 24; int right = data[i+1]; binaryDataArray[b++] = (byte)((left << delta) | (right >>> (32 - delta))); } } eltOffset += lineStride; } } } return binaryDataArray; } /** * Returns the binary data unpacked into an array of bytes. * The line stride will be the width of the Raster. * * @throws IllegalArgumentException if isBinary() returns * false with the SampleModel of the * supplied Raster as argument. */ public static byte[] getUnpackedBinaryData(Raster raster, Rectangle rect) { SampleModel sm = raster.getSampleModel(); if(!isBinary(sm)) { throw new IllegalArgumentException(JaiI18N.getString("ImageUtil0")); } int rectX = rect.x; int rectY = rect.y; int rectWidth = rect.width; int rectHeight = rect.height; DataBuffer dataBuffer = raster.getDataBuffer(); int dx = rectX - raster.getSampleModelTranslateX(); int dy = rectY - raster.getSampleModelTranslateY(); MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)sm; int lineStride = mpp.getScanlineStride(); int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy); int bitOffset = mpp.getBitOffset(dx); byte[] bdata = new byte[rectWidth*rectHeight]; int maxY = rectY + rectHeight; int maxX = rectX + rectWidth; int k = 0; if(dataBuffer instanceof DataBufferByte) { byte[] data = ((DataBufferByte)dataBuffer).getData(); for(int y = rectY; y < maxY; y++) { int bOffset = eltOffset*8 + bitOffset; for(int x = rectX; x < maxX; x++) { byte b = data[bOffset/8]; bdata[k++] = (byte)((b >>> (7 - bOffset & 7)) & 0x0000001); bOffset++; } eltOffset += lineStride; } } else if(dataBuffer instanceof DataBufferShort || dataBuffer instanceof DataBufferUShort) { short[] data = dataBuffer instanceof DataBufferShort ? ((DataBufferShort)dataBuffer).getData() : ((DataBufferUShort)dataBuffer).getData(); for(int y = rectY; y < maxY; y++) { int bOffset = eltOffset*16 + bitOffset; for(int x = rectX; x < maxX; x++) { short s = data[bOffset/16]; bdata[k++] = (byte)((s >>> (15 - bOffset % 16)) & 0x0000001); bOffset++; } eltOffset += lineStride; } } else if(dataBuffer instanceof DataBufferInt) { int[] data = ((DataBufferInt)dataBuffer).getData(); for(int y = rectY; y < maxY; y++) { int bOffset = eltOffset*32 + bitOffset; for(int x = rectX; x < maxX; x++) { int i = data[bOffset/32]; bdata[k++] = (byte)((i >>> (31 - bOffset % 32)) & 0x0000001); bOffset++; } eltOffset += lineStride; } } return bdata; } /** * Sets the supplied Raster's data from an array * of packed binary data of the form returned by * getPackedBinaryData(). * * @throws IllegalArgumentException if isBinary() returns * false with the SampleModel of the * supplied Raster as argument. */ public static void setPackedBinaryData(byte[] binaryDataArray, WritableRaster raster, Rectangle rect) { SampleModel sm = raster.getSampleModel(); if(!isBinary(sm)) { throw new IllegalArgumentException(JaiI18N.getString("ImageUtil0")); } int rectX = rect.x; int rectY = rect.y; int rectWidth = rect.width; int rectHeight = rect.height; DataBuffer dataBuffer = raster.getDataBuffer(); int dx = rectX - raster.getSampleModelTranslateX(); int dy = rectY - raster.getSampleModelTranslateY(); MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)sm; int lineStride = mpp.getScanlineStride(); int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy); int bitOffset = mpp.getBitOffset(dx); int b = 0; if(bitOffset == 0) { if(dataBuffer instanceof DataBufferByte) { byte[] data = ((DataBufferByte)dataBuffer).getData(); if(data == binaryDataArray) { // Optimal case: simply return. return; } int stride = (rectWidth + 7)/8; int offset = 0; for(int y = 0; y < rectHeight; y++) { System.arraycopy(binaryDataArray, offset, data, eltOffset, stride); offset += stride; eltOffset += lineStride; } } else if(dataBuffer instanceof DataBufferShort || dataBuffer instanceof DataBufferUShort) { short[] data = dataBuffer instanceof DataBufferShort ? ((DataBufferShort)dataBuffer).getData() : ((DataBufferUShort)dataBuffer).getData(); for(int y = 0; y < rectHeight; y++) { int xRemaining = rectWidth; int i = eltOffset; while(xRemaining > 8) { data[i++] = (short)(((binaryDataArray[b++] & 0xFF) << 8) | (binaryDataArray[b++] & 0xFF)); xRemaining -= 16; } if(xRemaining > 0) { data[i++] = (short)((binaryDataArray[b++] & 0xFF) << 8); } eltOffset += lineStride; } } else if(dataBuffer instanceof DataBufferInt) { int[] data = ((DataBufferInt)dataBuffer).getData(); for(int y = 0; y < rectHeight; y++) { int xRemaining = rectWidth; int i = eltOffset; while(xRemaining > 24) { data[i++] = (int)(((binaryDataArray[b++] & 0xFF) << 24) | ((binaryDataArray[b++] & 0xFF) << 16) | ((binaryDataArray[b++] & 0xFF) << 8) | (binaryDataArray[b++] & 0xFF)); xRemaining -= 32; } int shift = 24; while(xRemaining > 0) { data[i] |= (int)((binaryDataArray[b++] & 0xFF) << shift); shift -= 8; xRemaining -= 8; } eltOffset += lineStride; } } } else { // bitOffset != 0 int stride = (rectWidth + 7)/8; int offset = 0; if(dataBuffer instanceof DataBufferByte) { byte[] data = ((DataBufferByte)dataBuffer).getData(); if((bitOffset & 7) == 0) { for(int y = 0; y < rectHeight; y++) { System.arraycopy(binaryDataArray, offset, data, eltOffset, stride); offset += stride; eltOffset += lineStride; } } else { // bitOffset % 8 != 0 int rightShift = bitOffset & 7; int leftShift = 8 - rightShift; int leftShift8 = 8 + leftShift; int mask = (byte)(255< 0) { byte datum = binaryDataArray[b++]; if (xRemaining > leftShift8) { // when all the bits in this BYTE will be set // into the data buffer. data[i] = (byte)((data[i] & mask ) | ((datum&0xFF) >>> rightShift)); data[++i] = (byte)((datum & 0xFF) << leftShift); } else if (xRemaining > leftShift) { // All the "leftShift" high bits will be set // into the data buffer. But not all the // "rightShift" low bits will be set. data[i] = (byte)((data[i] & mask ) | ((datum&0xFF) >>> rightShift)); i++; data[i] = (byte)((data[i] & mask1) | ((datum & 0xFF) << leftShift)); } else { // Less than "leftShift" high bits will be set. int remainMask = (1 << leftShift - xRemaining) - 1; data[i] = (byte)((data[i] & (mask | remainMask)) | (datum&0xFF) >>> rightShift & ~remainMask); } xRemaining -= 8; } eltOffset += lineStride; } } } else if(dataBuffer instanceof DataBufferShort || dataBuffer instanceof DataBufferUShort) { short[] data = dataBuffer instanceof DataBufferShort ? ((DataBufferShort)dataBuffer).getData() : ((DataBufferUShort)dataBuffer).getData(); int rightShift = bitOffset & 7; int leftShift = 8 - rightShift; int leftShift16 = 16 + leftShift; int mask = (short)(~(255 << leftShift)); int mask1 = (short)(65535 << leftShift); int mask2 = (short)~mask1; for(int y = 0; y < rectHeight; y++) { int bOffset = bitOffset; int xRemaining = rectWidth; for(int x = 0; x < rectWidth; x += 8, bOffset += 8, xRemaining -= 8) { int i = eltOffset + (bOffset >> 4); int mod = bOffset & 15; int datum = binaryDataArray[b++] & 0xFF; if(mod <= 8) { // This BYTE is set into one SHORT if (xRemaining < 8) { // Mask the bits to be set. datum &= 255 << 8 - xRemaining; } data[i] = (short)((data[i] & mask) | (datum << leftShift)); } else if (xRemaining > leftShift16) { // This BYTE will be set into two SHORTs data[i] = (short)((data[i] & mask1) | ((datum >>> rightShift)&0xFFFF)); data[++i] = (short)((datum << leftShift)&0xFFFF); } else if (xRemaining > leftShift) { // This BYTE will be set into two SHORTs; // But not all the low bits will be set into SHORT data[i] = (short)((data[i] & mask1) | ((datum >>> rightShift)&0xFFFF)); i++; data[i] = (short)((data[i] & mask2) | ((datum << leftShift)&0xFFFF)); } else { // Only some of the high bits will be set into // SHORTs int remainMask = (1 << leftShift - xRemaining) - 1; data[i] = (short)((data[i] & (mask1 | remainMask)) | ((datum >>> rightShift)&0xFFFF & ~remainMask)); } } eltOffset += lineStride; } } else if(dataBuffer instanceof DataBufferInt) { int[] data = ((DataBufferInt)dataBuffer).getData(); int rightShift = bitOffset & 7; int leftShift = 8 - rightShift; int leftShift32 = 32 + leftShift; int mask = 0xFFFFFFFF << leftShift; int mask1 = ~mask; for(int y = 0; y < rectHeight; y++) { int bOffset = bitOffset; int xRemaining = rectWidth; for(int x = 0; x < rectWidth; x += 8, bOffset += 8, xRemaining -= 8) { int i = eltOffset + (bOffset >> 5); int mod = bOffset & 31; int datum = binaryDataArray[b++] & 0xFF; if(mod <= 24) { // This BYTE is set into one INT int shift = 24 - mod; if (xRemaining < 8) { // Mask the bits to be set. datum &= 255 << 8 - xRemaining; } data[i] = (data[i] & (~(255 << shift))) | (datum << shift); } else if (xRemaining > leftShift32) { // All the bits of this BYTE will be set into two INTs data[i] = (data[i] & mask) | (datum >>> rightShift); data[++i] = datum << leftShift; } else if (xRemaining > leftShift) { // This BYTE will be set into two INTs; // But not all the low bits will be set into INT data[i] = (data[i] & mask) | (datum >>> rightShift); i++; data[i] = (data[i] & mask1) | (datum << leftShift); } else { // Only some of the high bits will be set into INT int remainMask = (1 << leftShift - xRemaining) - 1; data[i] = (data[i] & (mask | remainMask)) | (datum >>> rightShift & ~remainMask); } } eltOffset += lineStride; } } } } /** * Copies data into the packed array of the Raster * from an array of unpacked data of the form returned by * getUnpackedBinaryData(). * *

If the data are binary, then the target bit will be set if * and only if the corresponding byte is non-zero. * * @throws IllegalArgumentException if isBinary() returns * false with the SampleModel of the * supplied Raster as argument. */ public static void setUnpackedBinaryData(byte[] bdata, WritableRaster raster, Rectangle rect) { SampleModel sm = raster.getSampleModel(); if(!isBinary(sm)) { throw new IllegalArgumentException(JaiI18N.getString("ImageUtil0")); } int rectX = rect.x; int rectY = rect.y; int rectWidth = rect.width; int rectHeight = rect.height; DataBuffer dataBuffer = raster.getDataBuffer(); int dx = rectX - raster.getSampleModelTranslateX(); int dy = rectY - raster.getSampleModelTranslateY(); MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)sm; int lineStride = mpp.getScanlineStride(); int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy); int bitOffset = mpp.getBitOffset(dx); int k = 0; if(dataBuffer instanceof DataBufferByte) { byte[] data = ((DataBufferByte)dataBuffer).getData(); for(int y = 0; y < rectHeight; y++) { int bOffset = eltOffset*8 + bitOffset; for(int x = 0; x < rectWidth; x++) { if(bdata[k++] != (byte)0) { data[bOffset/8] |= (byte)(0x00000001 << (7 - bOffset & 7)); } bOffset++; } eltOffset += lineStride; } } else if(dataBuffer instanceof DataBufferShort || dataBuffer instanceof DataBufferUShort) { short[] data = dataBuffer instanceof DataBufferShort ? ((DataBufferShort)dataBuffer).getData() : ((DataBufferUShort)dataBuffer).getData(); for(int y = 0; y < rectHeight; y++) { int bOffset = eltOffset*16 + bitOffset; for(int x = 0; x < rectWidth; x++) { if(bdata[k++] != (byte)0) { data[bOffset/16] |= (short)(0x00000001 << (15 - bOffset % 16)); } bOffset++; } eltOffset += lineStride; } } else if(dataBuffer instanceof DataBufferInt) { int[] data = ((DataBufferInt)dataBuffer).getData(); for(int y = 0; y < rectHeight; y++) { int bOffset = eltOffset*32 + bitOffset; for(int x = 0; x < rectWidth; x++) { if(bdata[k++] != (byte)0) { data[bOffset/32] |= (int)(0x00000001 << (31 - bOffset % 32)); } bOffset++; } eltOffset += lineStride; } } } /** Fill the specified rectangle of raster with the provided * background values. Suppose the raster is initialized to 0. Thus, * for binary data, if the provided background values are 0, do nothing. */ public static void fillBackground(WritableRaster raster, Rectangle rect, double[] backgroundValues) { rect = rect.intersection(raster.getBounds()); int numBands = raster.getSampleModel().getNumBands(); SampleModel sm = raster.getSampleModel(); PixelAccessor accessor = new PixelAccessor(sm, null); if (isBinary(sm)) { //fill binary data byte value = (byte)(((int)backgroundValues[0]) & 1); if (value == 0) return; int rectX = rect.x; int rectY = rect.y; int rectWidth = rect.width; int rectHeight = rect.height; int dx = rectX - raster.getSampleModelTranslateX(); int dy = rectY - raster.getSampleModelTranslateY(); DataBuffer dataBuffer = raster.getDataBuffer(); MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)sm; int lineStride = mpp.getScanlineStride(); int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy); int bitOffset = mpp.getBitOffset(dx); switch(sm.getDataType()) { case DataBuffer.TYPE_BYTE: { byte[] data = ((DataBufferByte)dataBuffer).getData(); int bits = bitOffset & 7; int otherBits = (bits == 0) ? 0: 8 - bits; byte mask = (byte)(255 >> bits); int lineLength = (rectWidth - otherBits) / 8; int bits1 = (rectWidth - otherBits) & 7; byte mask1 = (byte)(255 << (8 - bits1)); // If operating within a single byte, merge masks into one // and don't apply second mask after while loop if (lineLength == 0) { mask &= mask1; bits1 = 0; } for (int y = 0; y < rectHeight; y++) { int start = eltOffset; int end = start + lineLength; if (bits != 0) data[start++] |= mask; while (start < end) data[start++] = (byte)255; if (bits1 != 0) data[start] |= mask1; eltOffset += lineStride; } break; } case DataBuffer.TYPE_USHORT: { short[] data = ((DataBufferUShort)dataBuffer).getData(); int bits = bitOffset & 15; int otherBits = (bits == 0) ? 0: 16 - bits; short mask = (short)(65535 >> bits); int lineLength = (rectWidth - otherBits) / 16; int bits1 = (rectWidth - otherBits) & 15; short mask1 = (short)(65535 << (16 - bits1)); // If operating within a single byte, merge masks into one // and don't apply second mask after while loop if (lineLength == 0) { mask &= mask1; bits1 = 0; } for (int y = 0; y < rectHeight; y++) { int start = eltOffset; int end = start + lineLength; if (bits != 0) data[start++] |= mask; while (start < end) data[start++] = (short)0xFFFF; if (bits1 != 0) data[start++] |= mask1; eltOffset += lineStride; } break; } case DataBuffer.TYPE_INT: { int[] data = ((DataBufferInt)dataBuffer).getData(); int bits = bitOffset & 31; int otherBits = (bits == 0) ? 0: 32 - bits; int mask = 0xFFFFFFFF >> bits; int lineLength = (rectWidth - otherBits) / 32; int bits1 = (rectWidth - otherBits) & 31; int mask1 = 0xFFFFFFFF << (32 - bits1); // If operating within a single byte, merge masks into one // and don't apply second mask after while loop if (lineLength == 0) { mask &= mask1; bits1 = 0; } for (int y = 0; y < rectHeight; y++) { int start = eltOffset; int end = start + lineLength; if (bits != 0) data[start++] |= mask; while (start < end) data[start++] = 0xFFFFFFFF; if (bits1 != 0) data[start++] |= mask1; eltOffset += lineStride; } break; } } } else { int srcSampleType = accessor.sampleType == PixelAccessor.TYPE_BIT ? DataBuffer.TYPE_BYTE : accessor.sampleType; UnpackedImageData uid = accessor.getPixels(raster, rect, srcSampleType, false); rect = uid.rect; int lineStride = uid.lineStride; int pixelStride = uid.pixelStride; switch(uid.type) { case DataBuffer.TYPE_BYTE: byte[][] bdata = uid.getByteData(); for (int b = 0; b < accessor.numBands; b++) { byte value = (byte)backgroundValues[b]; byte[] bd = bdata[b]; int lastLine = uid.bandOffsets[b] + rect.height * lineStride; for (int lo = uid.bandOffsets[b]; lo < lastLine; lo += lineStride) { int lastPixel = lo + rect.width * pixelStride; for (int po = lo; po < lastPixel; po += pixelStride) { bd[po] = value; } } } break; case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: short[][] sdata = uid.getShortData(); for (int b = 0; b < accessor.numBands; b++) { short value = (short)backgroundValues[b]; short[] sd = sdata[b]; int lastLine = uid.bandOffsets[b] + rect.height * lineStride; for (int lo = uid.bandOffsets[b]; lo < lastLine; lo += lineStride) { int lastPixel = lo + rect.width * pixelStride; for (int po = lo; po < lastPixel; po += pixelStride) { sd[po] = value; } } } break; case DataBuffer.TYPE_INT: int[][] idata = uid.getIntData(); for (int b = 0; b < accessor.numBands; b++) { int value = (int)backgroundValues[b]; int[] id = idata[b]; int lastLine = uid.bandOffsets[b] + rect.height * lineStride; for (int lo = uid.bandOffsets[b]; lo < lastLine; lo += lineStride) { int lastPixel = lo + rect.width * pixelStride; for (int po = lo; po < lastPixel; po += pixelStride) { id[po] = value; } } } break; case DataBuffer.TYPE_FLOAT: float[][] fdata = uid.getFloatData(); for (int b = 0; b < accessor.numBands; b++) { float value = (float)backgroundValues[b]; float[] fd = fdata[b]; int lastLine = uid.bandOffsets[b] + rect.height * lineStride; for (int lo = uid.bandOffsets[b]; lo < lastLine; lo += lineStride) { int lastPixel = lo + rect.width * pixelStride; for (int po = lo; po < lastPixel; po += pixelStride) { fd[po] = value; } } } break; case DataBuffer.TYPE_DOUBLE: double[][] ddata = uid.getDoubleData(); for (int b = 0; b < accessor.numBands; b++) { double value = backgroundValues[b]; double[] dd = ddata[b]; int lastLine = uid.bandOffsets[b] + rect.height * lineStride; for (int lo = uid.bandOffsets[b]; lo < lastLine; lo += lineStride) { int lastPixel = lo + rect.width * pixelStride; for (int po = lo; po < lastPixel; po += pixelStride) { dd[po] = value; } } } break; } } } /** When the destination rectangle is not the same as the image bounds, * should fill the border. */ public static void fillBordersWithBackgroundValues(Rectangle outerRect, Rectangle innerRect, WritableRaster raster, double[] backgroundValues) { int outerMaxX = outerRect.x + outerRect.width; int outerMaxY = outerRect.y + outerRect.height; int innerMaxX = innerRect.x + innerRect.width; int innerMaxY = innerRect.y + innerRect.height; if (outerRect.x < innerRect.x) { Rectangle rect = new Rectangle(outerRect.x, innerRect.y, innerRect.x - outerRect.x, outerMaxY - innerRect.y); fillBackground(raster, rect, backgroundValues); } if (outerRect.y < innerRect.y) { Rectangle rect = new Rectangle(outerRect.x, outerRect.y, innerMaxX - outerRect.x, innerRect.y - outerRect.y); fillBackground(raster, rect, backgroundValues); } if (outerMaxX > innerMaxX) { Rectangle rect = new Rectangle(innerMaxX, outerRect.y, outerMaxX - innerMaxX, innerMaxY - outerRect.y); fillBackground(raster, rect, backgroundValues); } if (outerMaxY > innerMaxY) { Rectangle rect = new Rectangle(innerRect.x, innerMaxY, outerMaxX - innerRect.x, outerMaxY - innerMaxY); fillBackground(raster, rect, backgroundValues); } } /// ---- END Binary data handling methods ---- /** * Given a kernel and the gain (sharpness) factor of an * UnsharpMask operation, compute a modified kernel that * would be equivalent to the specified unsharp operation. * * for UnsharpMask function we have the following formula: * * dst(i,j) = src(i,j) + gain * * (src(i,j) - SUM SUM K(l,m) * src(i+l,j+m)) * l m * * Which can be written as : * * dst(i,j) = SUM SUM Q(l,m) * src(i+l,j+m), * l m * * where Q(0,0) = 1 + gain * (1 - K(0,0)), and * Q(l,m) = - gain * K(l,m) otherwise * * @param kernel the unsharp mask kernel * @param gain the unsharp mask gain (sharpness) factor. * * @return an equivalent convolution KernelJAI */ public static KernelJAI getUnsharpMaskEquivalentKernel( KernelJAI kernel, float gain) { int width = kernel.getWidth(); int height = kernel.getHeight(); int xOrigin = kernel.getXOrigin(); int yOrigin = kernel.getYOrigin(); float oldData[] = kernel.getKernelData(); float newData[] = new float[oldData.length]; int k; for (k = 0; k < width*height; k++) newData[k] = -gain * oldData[k]; k = yOrigin*width + xOrigin; newData[k] = 1.0f + gain * (1.0f - oldData[k]); return new KernelJAI(width, height, xOrigin, yOrigin, newData); } /** * Retrieve the indices of a set of tiles in row-major order with * the given tile index bounds in x and y. */ public static final Point[] getTileIndices(int txmin, int txmax, int tymin, int tymax) { if(txmin > txmax || tymin > tymax) { return null; } Point[] tileIndices = new Point[(txmax - txmin + 1)*(tymax - tymin + 1)]; int k = 0; for (int tj = tymin; tj <= tymax; tj++) { for (int ti = txmin; ti <= txmax; ti++) { tileIndices[k++] = new Point(ti, tj); } } return tileIndices; } /// Method for handling DeferrdData objects in ParameterBlocks. /** * If any DeferredData components are detected, * the argument is cloned and the DeferredData * object is replaced with what its getData() returns. */ public static Vector evaluateParameters(Vector parameters) { if(parameters == null) { throw new IllegalArgumentException(); } Vector paramEval = parameters; int size = parameters.size(); for(int i = 0; i < size; i++) { Object element = parameters.get(i); if(element instanceof DeferredData) { if(paramEval == parameters) { paramEval = (Vector)parameters.clone(); } paramEval.set(i, ((DeferredData)element).getData()); } } return paramEval; } /** * If any DeferredData parameters are detected, * a new ParameterBlock is constructed and the * DeferredData object is replaced with what its * getData() returns. */ public static ParameterBlock evaluateParameters(ParameterBlock pb) { if(pb == null) { throw new IllegalArgumentException(); } Vector parameters = pb.getParameters(); Vector paramEval = evaluateParameters(parameters); return paramEval == parameters ? pb : new ParameterBlock(pb.getSources(), paramEval); } /** * Derive a compatible ColorModel for the supplied * SampleModel using the method specified via the * OpImage configuration Map. * * @return a compatible ColorModel or null. */ public static ColorModel getCompatibleColorModel(SampleModel sm, Map config) { ColorModel cm = null; if(config == null || !Boolean.FALSE.equals( config.get(JAI.KEY_DEFAULT_COLOR_MODEL_ENABLED))) { // Set the default ColorModel if(config != null && config.containsKey(JAI.KEY_DEFAULT_COLOR_MODEL_METHOD)) { // Attempt to retrieve the default CM Method. Method cmMethod = (Method)config.get(JAI.KEY_DEFAULT_COLOR_MODEL_METHOD); // Check method compatibility. Class[] paramTypes = cmMethod.getParameterTypes(); if((cmMethod.getModifiers() & Modifier.STATIC) != Modifier.STATIC) { // Method must be static. throw new RuntimeException(JaiI18N.getString("ImageUtil1")); } else if(cmMethod.getReturnType() != ColorModel.class) { // Method must return a ColorModel. throw new RuntimeException(JaiI18N.getString("ImageUtil2")); } else if(paramTypes.length != 1 || !paramTypes[0].equals(SampleModel.class)) { // Unique Method parameter must be a SampleModel. throw new RuntimeException(JaiI18N.getString("ImageUtil3")); } // Set the default ColorModel. try { // Attempt to use the supplied Method. Object[] args = new Object[] {sm}; cm = (ColorModel)cmMethod.invoke(null, args); } catch(Exception e) { String message = JaiI18N.getString("ImageUtil4") + cmMethod.getName(); sendExceptionToListener(message , new ImagingException(message, e)); /* // XXX Is this a reasonable Exception to throw? throw new RuntimeException(cmMethod.getName()+" "+ e.getMessage()); */ } } else { // No default method hint set. // Use PlanarImage method. cm = PlanarImage.createColorModel(sm); } } return cm; } /** * Converts the supplied Exception's stack trace * to a String. */ public static String getStackTraceString(Exception e) { ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); PrintStream printStream = new PrintStream(byteStream); e.printStackTrace(printStream); printStream.flush(); String stackTraceString = byteStream.toString(); printStream.close(); return stackTraceString; } public static ImagingListener getImagingListener(RenderingHints hints) { ImagingListener listener = null; if (hints != null) listener = (ImagingListener)hints.get(JAI.KEY_IMAGING_LISTENER); if (listener == null) listener = JAI.getDefaultInstance().getImagingListener(); return listener; } public static ImagingListener getImagingListener(RenderContext context) { return getImagingListener(context.getRenderingHints()); } /** * Generates a UID for the provided Object. * The counter for the objects that request an ID, the hashcode of the * class of the provided object, the hashcode of the provided object, * the current time in milli seconds, and a random number are * concatenated together in a BigInteger. This * BigInteger is returned as the unique ID. */ public static synchronized Object generateID(Object owner) { Class c = owner.getClass(); counter++; byte[] uid = new byte[32]; int k = 0; for (int i = 7, j = 0; i >=0; i--, j += 8) uid[k++] = (byte)(counter >> j); int hash = c.hashCode(); for (int i = 3, j = 0; i >= 0; i--, j += 8) uid[k++] = (byte)(hash >> j); hash = owner.hashCode(); for (int i = 3, j = 0; i >= 0; i--, j += 8) uid[k++] = (byte)(hash >> j); long time = System.currentTimeMillis(); for (int i = 7, j = 0; i >=0; i--, j += 8) uid[k++] = (byte)(time >> j); long rand = Double.doubleToLongBits(new Double(Math.random()).doubleValue()); for (int i = 7, j = 0; i >=0; i--, j += 8) uid[k++] = (byte)(rand>> j); return new BigInteger(uid); } static void sendExceptionToListener(String message, Exception e) { ImagingListener listener = getImagingListener((RenderingHints)null); listener.errorOccurred(message, e, ImageUtil.class, false); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/util/JDKWorkarounds.java0000644000175000017500000003610210203035544026300 0ustar mathieumathieu/* * $RCSfile: JDKWorkarounds.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:00 $ * $State: Exp $ */ package com.sun.media.jai.util; import java.awt.*; import java.awt.image.*; import java.util.*; import javax.media.jai.*; // Workaround Repository for JDK bugs. public final class JDKWorkarounds { private JDKWorkarounds() {} /** * Faster implementation of setRect for bilevel Rasters * with a SinglePixelPackedSampleModel and DataBufferByte. * Based on sun.awt.image.BytePackedRaster.setDataElements * (JDK1.3 beta version), with improvements. */ private static boolean setRectBilevel(WritableRaster dstRaster, Raster srcRaster, int dx, int dy) { int width = srcRaster.getWidth(); int height = srcRaster.getHeight(); int srcOffX = srcRaster.getMinX(); int srcOffY = srcRaster.getMinY(); int dstOffX = dx+srcOffX; int dstOffY = dy+srcOffY; int dminX = dstRaster.getMinX(); int dminY = dstRaster.getMinY(); int dwidth = dstRaster.getWidth(); int dheight = dstRaster.getHeight(); // Clip to dstRaster if (dstOffX + width > dminX + dwidth) { width = dminX + dwidth - dstOffX; } if (dstOffY + height > dminY + dheight) { height = dminY + dheight - dstOffY; } // // This implementation works but is not as efficient as the one // below which is commented out. In terms of performance, cobbling // a 1728x2376 bit image with 128x144 tiles took the following // amount of time for four cases: // // WritableRaster.setRect() 19756 // Aligned optimal case 5645 // Unaligned optimal case 6644 // Case using ImageUtil 7500 // // So this case gives intermediate speed performance closer to the // optimal case than to the JDK. It will likely use more memory // however. On the other hand this approach covers all data types. // Rectangle rect = new Rectangle(dstOffX, dstOffY, width, height); byte[] binaryData = ImageUtil.getPackedBinaryData(srcRaster, rect); ImageUtil.setPackedBinaryData(binaryData, dstRaster, rect); /* XXX BEGIN: Commented out as it gives vertical lines in cobbling data. This gives optimal performance for the case of byte-to-byte data. For non-byte data the sub-optimal solution above (using the ImageUtil packed routines) should be used. Note that this commented out section includes a few bug fixes compared with the original code in the previous SCCS version. bpb 6/21/2000 MultiPixelPackedSampleModel srcMPPSM = (MultiPixelPackedSampleModel)srcRaster.getSampleModel(); MultiPixelPackedSampleModel dstMPPSM = (MultiPixelPackedSampleModel)dstRaster.getSampleModel(); DataBufferByte srcDBB = (DataBufferByte)srcRaster.getDataBuffer(); DataBufferByte dstDBB = (DataBufferByte)dstRaster.getDataBuffer(); byte[] srcData = srcDBB.getData(); byte[] dstData = dstDBB.getData(); int srcTransX = srcRaster.getSampleModelTranslateX(); int srcTransY = srcRaster.getSampleModelTranslateY(); int srcDataBitOffset = srcMPPSM.getDataBitOffset(); int srcScanlineStride = srcMPPSM.getScanlineStride(); int srcYOffset = (srcOffY - srcTransY)*srcScanlineStride; int srcXOffset = srcDataBitOffset + (srcOffX - srcTransX); int dstTransX = dstRaster.getSampleModelTranslateX(); int dstTransY = dstRaster.getSampleModelTranslateY(); int dstDataBitOffset = dstMPPSM.getDataBitOffset(); int dstScanlineStride = dstMPPSM.getScanlineStride(); int dstYOffset = (dstOffY - dstTransY)*dstScanlineStride; int dstXOffset = dstDataBitOffset + (dstOffX - dstTransX); int inbit = srcYOffset*8 + srcXOffset; int outbit = dstYOffset*8 + dstXOffset; if ((inbit & 7) == (outbit & 7)) { // Aligned case int copybits = width; int bits = inbit & 7; if (bits != 0) { // Copy partial bytes on left int inbyte = inbit >> 3; int outbyte = outbit >> 3; int mask = 0xff >> bits; bits = 8 - bits; if (copybits < bits) { mask &= (mask << (8 - copybits)); bits = copybits; } for (int j = 0; j < height; j++) { int element = dstData[outbyte]; element &= ~mask; element |= (srcData[inbyte] & mask); dstData[outbyte] = (byte) element; inbyte += srcScanlineStride; outbyte += dstScanlineStride; } inbit += bits; outbit += bits; copybits -= bits; } if (copybits >= 8) { // Copy whole bytes int inbyte = inbit >> 3; int outbyte = outbit >> 3; int copybytes = copybits >> 3; if (copybytes == srcScanlineStride && srcScanlineStride == dstScanlineStride) { System.arraycopy(srcData, inbyte, dstData, outbyte, srcScanlineStride*height); } else { for (int j = 0; j < height; j++) { System.arraycopy(srcData, inbyte, dstData, outbyte, copybytes); inbyte += srcScanlineStride; outbyte += dstScanlineStride; } } bits = copybytes * 8; inbit += bits; outbit += bits; copybits -= bits; } if (copybits > 0) { // Copy partial bytes on right int inbyte = inbit >> 3; int outbyte = outbit >> 3; int mask = (0xff00 >> copybits) & 0xff; for (int j = 0; j < height; j++) { int element = dstData[outbyte]; element &= ~mask; element |= (srcData[inbyte] & mask); dstData[outbyte] = (byte) element; inbyte += srcScanlineStride; outbyte += dstScanlineStride; } } } else { // Unaligned case for (int j = 0; j < height; j++) { int save_inbit = inbit; int save_outbit = outbit; int copybits = width; int inbyte, outbyte; int mask; int bits = outbit & 7; if (bits > 0) { inbyte = inbit >> 8; outbyte = outbit >> 8; mask = 0xff >> bits; if (copybits < bits) { mask &= mask << (8 - copybits); bits = copybits; } int element = dstData[outbyte]; element &= ~mask; element |= (srcData[inbyte] & mask); dstData[outbyte] = (byte) element; inbit += bits; outbit += bits; copybits -= bits; } if (copybits == 0) { continue; } int shift0 = inbit & 7; int shift1 = 7 - shift0; int mask1 = 0xff >>> shift1; inbyte = inbit >> 3; outbyte = outbit >> 3; int srcData0 = srcData[inbyte]; int lastIndex = srcData.length - 1; while (copybits >= 8 && inbyte < lastIndex) { int srcData1 = srcData[inbyte + 1]; int val = (srcData0 << shift0) | ((srcData1 >>> shift1) & mask1); srcData0 = srcData1; dstData[outbyte] = (byte)val; ++inbyte; ++outbyte; inbit += 8; outbit += 8; copybits -= 8; } if (copybits > 0) { mask = (0xff00 >> copybits) & 0xff; int element = dstData[outbyte]; element &= ~mask; element |= ((srcData[inbyte] << shift0) & mask); dstData[outbyte] = (byte)(element & 0xFF); } inbit = save_inbit + 8*srcScanlineStride; outbit = save_outbit + 8*dstScanlineStride; } } XXX END */ return true; } // Workarounds for WritableRaster.setRect bug (4250270) in JDK 1.2. // Also filed as bug 4250273 against JAI. public static void setRect(WritableRaster dstRaster, Raster srcRaster) { setRect(dstRaster, srcRaster, 0, 0); } public static void setRect(WritableRaster dstRaster, Raster srcRaster, int dx, int dy) { // Special case for bilevel Rasters SampleModel srcSampleModel = srcRaster.getSampleModel(); SampleModel dstSampleModel = dstRaster.getSampleModel(); if (srcSampleModel instanceof MultiPixelPackedSampleModel && dstSampleModel instanceof MultiPixelPackedSampleModel) { MultiPixelPackedSampleModel srcMPPSM = (MultiPixelPackedSampleModel)srcSampleModel; MultiPixelPackedSampleModel dstMPPSM = (MultiPixelPackedSampleModel)dstSampleModel; DataBuffer srcDB = srcRaster.getDataBuffer(); DataBuffer dstDB = srcRaster.getDataBuffer(); if (srcDB instanceof DataBufferByte && dstDB instanceof DataBufferByte && srcMPPSM.getPixelBitStride() == 1 && dstMPPSM.getPixelBitStride() == 1) { if (setRectBilevel(dstRaster, srcRaster, dx, dy)) { return; } } } // Use the regular JDK routines for everything else except // float and double images. int dataType = dstRaster.getSampleModel().getDataType(); if (dataType != DataBuffer.TYPE_FLOAT && dataType != DataBuffer.TYPE_DOUBLE) { dstRaster.setRect(dx, dy, srcRaster); return; } int width = srcRaster.getWidth(); int height = srcRaster.getHeight(); int srcOffX = srcRaster.getMinX(); int srcOffY = srcRaster.getMinY(); int dstOffX = dx+srcOffX; int dstOffY = dy+srcOffY; int dminX = dstRaster.getMinX(); int dminY = dstRaster.getMinY(); int dwidth = dstRaster.getWidth(); int dheight = dstRaster.getHeight(); // Clip to dstRaster if (dstOffX + width > dminX + dwidth) { width = dminX + dwidth - dstOffX; } if (dstOffY + height > dminY + dheight) { height = dminY + dheight - dstOffY; } 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); dstRaster.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); dstRaster.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); dstRaster.setPixels(dstOffX, dstOffY+startY, width, 1, dData); } break; } } /** * Workaround for JDK 1.3 bug 4326636 (bpb 30 March 2000). * * Check whether the given SampleModel and ColorModel are compatible. * * This is required because in JDK 1.3 the ComponentColorModel * implementation of isCompatibleSampleModel() only checks whether * the SampleModel is a ComponentSampleModel with the same transferType * as the ColorModel. No check of the number of components or bit * depth is effected. * * @throws IllegalArgumentException if either parameter is null. */ public static boolean areCompatibleDataModels(SampleModel sm, ColorModel cm) { if(sm == null || cm == null) { throw new IllegalArgumentException(JaiI18N.getString("JDKWorkarounds0")); } // Call the method we should be using instead of this workaround. // This checks the compatibility of the transferType and possibly // other quantities. if(!cm.isCompatibleSampleModel(sm)) { return false; } // This if-block adds the tests performed in // ComponentColorModel.isCompatibleRaster() but not in // ComponentColorModel.isCompatibleSampleModel(). // These tests might duplicate the implementation of some // subclasses of ComponentColorModel. if(cm instanceof ComponentColorModel) { // Check the number of samples per pixel. int numBands = sm.getNumBands(); if (numBands != cm.getNumComponents()) { return false; } // Check adequate depth. This should work for // FloatDoubleColorModel as well because // SampleModel.getSampleSize() should return 32 or 64 as // it gets the size from the DataBuffer object and // ColorModel.getComponentSize() returns the number of bits // which are set to 32 or 64 as a function of the transferType. for (int b = 0; b < numBands; b++) { if (sm.getSampleSize(b) < cm.getComponentSize(b)) { return false; } } } // Got this far so return true. return true; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/util/RWLock.java0000644000175000017500000003767510203035544024612 0ustar mathieumathieu/* * $RCSfile: RWLock.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:01 $ * $State: Exp $ */ package com.sun.media.jai.util; import java.util.LinkedList; import java.util.ListIterator; /** * A class that provides a classic reader/writer lock functionality. * This implementation is based on the JavaWorld article by Tark Modi * titled "Lock on to an alternate synchronized mechanism" with some * minor modifications. * *

This lock provides the following functionality :

    * *
  • Allows multiple threads read access while only a single * thread can have a write access.
  • * *
  • Allows a thread owning a read lock to "upgrade" to a write * lock.
  • * *
  • Allows a thread owning a write lock to "downgrade" to a read * lock.
  • * *
  • Allows the option to either block or specify a waitTime on * certain requests. * *
*/ public final class RWLock { // Should a read lock be allowed to upgrade to a write lock if the // thread requesting the write lock already owns the read lock. private boolean allowUpgrades; // Lock Types private static int READ = 1; private static int WRITE = 2; private static int NOT_FOUND = -1; // A list of threads that are waiting to acquire a lock, waiting to // upgrade their lock or have been granted locks. private WaitingList waitingList = new WaitingList(); private class WaitingList extends LinkedList { // Return index of the first writer in the list. int indexOfFirstWriter() { ListIterator iter = listIterator(0); int index = 0; while (iter.hasNext()) { if (((ReaderWriter)iter.next()).lockType == WRITE) return index; index++; } return NOT_FOUND; } // Return the index of the last thread granted a lock int indexOfLastGranted() { ListIterator iter = listIterator(size()); int index = size()-1; while (iter.hasPrevious()) { if (((ReaderWriter)iter.previous()).granted == true) return index; index--; } return NOT_FOUND; } // Return index of the element for this thread int findMe() { return indexOf(new ReaderWriter()); } } // Element stored in the waiting list. private class ReaderWriter { Thread key; // set equal to the Thread ID int lockType; // READ or WRITE int lockCount; // # of times lock was taken boolean granted; // Has this thread been granted the lock ReaderWriter() { this(0); } ReaderWriter(int type) { key = Thread.currentThread(); lockType = type; lockCount = 0; granted = false; } /** * Two elements are equal if their keys are equal. Note that * this does not make any instanceof checks since it assumes * that this implementation uses the this list a certain way. */ public boolean equals(Object o) { return key == ((ReaderWriter)o).key; } } /** * Constructor. * * @param should upgrades of read locks to write locks be * allowed ? */ public RWLock(boolean allowUpgrades) { this.allowUpgrades = allowUpgrades; } /** * Constructor. Equivalent to RWLock(true) * which creates an upgradable reader-writer lock. */ public RWLock() { this(true); } /** * Tries to obtain a read lock within the specified time. * * @param waitTime the time to wait in milliseconds while trying * to obtain the read lock. A negative value indicates a * blocking wait. * * @return true if the read lock was obtained without timing out. */ public synchronized boolean forReading(int waitTime) { ReaderWriter element = null; // Is there a node for this thread already in the list? // If not, create a new one. int index = waitingList.findMe(); if (index != NOT_FOUND) { element = (ReaderWriter)waitingList.get(index); } else { element = new ReaderWriter(READ); waitingList.add(element); } // If a lock has already been granted once just increment the // count and return. It does not matter whether the lock granted // initially was a READ or WRITE lock. if (element.lockCount > 0) { element.lockCount++; return true; } long startTime = System.currentTimeMillis(); long endTime = waitTime + startTime; do { int nextWriter = waitingList.indexOfFirstWriter(); index = waitingList.findMe(); // If there is no writer in front of me I get a // read lock. Otherwise, I wait... if ((nextWriter == NOT_FOUND) || (nextWriter > index)) { element.lockCount++; element.granted = true; return true; } // Non-blocking version, just return. Do not need notifyAll // here since we added and removed the new element within the // same synchronized block and so no other thread ever saw // it. if (waitTime == 0) { waitingList.remove(element); return false; } // Now wait for the lock. try { // Negative wait time indicates a blocking wait. if (waitTime < 0) { wait(); } else { long delta = endTime - System.currentTimeMillis(); if (delta > 0) wait(delta); } } catch (InterruptedException e) { // Should never be here. // These messages are for debugging purposes only System.err.println(element.key.getName() + " : interrupted while waiting for a READ lock!"); } } while ((waitTime < 0) || (endTime > System.currentTimeMillis())); // Could not get the lock and timed out. waitingList.remove(element); // Important to notify all threads of our removal. notifyAll(); // Failed to get lock. return false; } /** * Tries to obtain a read lock. Equivalent to forReading(-1) * which will go into a blocking wait attempting to get a * read lock. * * @return true, always. */ public synchronized boolean forReading() { return forReading(-1); } /** * Tries to obtain a write lock withing the specified time. If the * current thread owns a read lock, then it is upgraded to a write * lock if upgrades are allowed, else, throws an UpgradeNotAllowed * exception. If the lock is not owned by the current thread then * it waits for a write lock for the specified time. * * @param waitTime the time to wait in milliseconds while trying * to obtain the write lock. A negative value indicates a * blocking wait. * * @return true if the write lock was obtained without timing out. * * @throws UpgradeNotAllowed if current thread owns a read lock * and upgrades are not allowed. */ public synchronized boolean forWriting(int waitTime) throws UpgradeNotAllowed { ReaderWriter element = null; // Is there a node for this thread already in the list? // If not, create a new one. int index = waitingList.findMe(); if (index != NOT_FOUND) { element = (ReaderWriter)waitingList.get(index); } else { element = new ReaderWriter(WRITE); waitingList.add(element); } // If the thread has a READ lock, we need to upgrade if ((element.granted == true) && (element.lockType == READ)) { try { if (!upgrade(waitTime)) return false; } catch (LockNotHeld e) { return false; } } // If a lock has already been granted once just increment the // count and return. At this point the thread either had a WRITE // lock or was upgraded to have one. if (element.lockCount > 0) { element.lockCount++; return true; } long startTime = System.currentTimeMillis(); long endTime = waitTime + startTime; do { // If there are any readers in front of me // I have to wait... index = waitingList.findMe(); // If I am the first one in the list I get the lock. if (index == 0) { element.lockCount++; element.granted = true; return true; } // Non-blocking version, just return. Do not need notifyAll // here since we added and removed the new element within the // same synchronized block and so no other thread ever saw // it. if (waitTime == 0) { waitingList.remove(element); return false; } // Now wait for the lock. try { // Negative wait time indicates a blocking wait. if (waitTime < 0) { wait(); } else { long delta = endTime - System.currentTimeMillis(); if (delta > 0) wait(delta); } } catch (InterruptedException e) { // Should never be here. // These messages are for debugging purposes only System.err.println(element.key.getName() + " : interrupted while waiting for a WRITE lock!"); } } while ((waitTime < 0) || (endTime > System.currentTimeMillis())); // Could not get the lock and timed out. waitingList.remove(element); // Notify all threads of our removal. notifyAll(); // Failed to get the lock. return false; } /** * Tries to obtain a write lock. Equivalent to forWriting(-1) * which will go into a blocking wait attempting to get a * write lock. * * @return true, always. * * @throws UpgradeNotAllowed if current thread owns a read lock * and upgrades are not allowed. */ public synchronized boolean forWriting() throws UpgradeNotAllowed { return forWriting(-1); } /** * Try to upgrade a write lock within the specified amount of time. * If the current thread does not own the lock or if upgrades * are not allowed an exception is thrown. If the current thread * already owns a write lock, nothing happens. Otherwise, threads * already owning a read lock are given a higher priority in * receiving the write lock than those that own no lock. * * @param waitTime the time to wait in milliseconds while trying * to upgrade to a write lock. A negative value indicates a * blocking wait. * * @return true if the upgrade was performed without timing out. * * @throws LockNotHeld if the current thread does not own the lock * @throws UpgradeNotAllowed if the lock is not currently * held by the owner. */ public synchronized boolean upgrade(int waitTime) throws UpgradeNotAllowed, LockNotHeld { if (!allowUpgrades) throw new UpgradeNotAllowed(); // We should already be in the list. If not, it is an error. int index = waitingList.findMe(); if (index == NOT_FOUND) throw new LockNotHeld(); // Get the actual element. If the lock type is already WRITE, // just return. ReaderWriter element = (ReaderWriter)waitingList.get(index); if (element.lockType == WRITE) return true; // What is the index of the last granted lock? int lastGranted = waitingList.indexOfLastGranted(); // lastGranted can not be NOT_FOUND, after all we are // granted a READ lock! if (lastGranted == NOT_FOUND) throw new LockNotHeld(); // If we are not the last granted lock, then we will position // ourselves as such. if (index != lastGranted) { waitingList.remove(index); ListIterator iter = waitingList.listIterator(lastGranted); iter.add(element); } // We want new readers to think this is a write lock // This is important so that they block i.e. do not get granted. // Since we are now waiting for a write lock it is // important that we were after all granted read locks. element.lockType = WRITE; long startTime = System.currentTimeMillis(); long endTime = waitTime + startTime; do { index = waitingList.findMe(); if (index == 0) { return true; } // Non-blocking version. Do not need notifyAll here since // we changed the lock type back and forth within the same // synchronized block and so no other thread ever saw it. if (waitTime == 0) { // Back to READ type element.lockType = READ; // No need to readjust position since it does not matter // for already granted locks. return false; } // Now wait for the lock. try { // Negative wait time indicates a blocking wait. if (waitTime < 0) { wait(); } else { long delta = endTime - System.currentTimeMillis(); if (delta > 0) wait(delta); } } catch (InterruptedException e) { // Should never be here. // These messages are for debugging purposes only System.err.println(element.key.getName() + " : interrupted while waiting to UPGRADE lock!"); } } while ((waitTime < 0) || (endTime > System.currentTimeMillis())); // We failed to upgrade. Go back to original lock type element.lockType = READ; // Important to notify all threads that we are back // to being a READ lock. notifyAll(); // Failed to upgrade. return false; } /** * Tries to upgrade to a write lock. Equivalent to * upgrade(-1) which will go into a blocking wait * attempting to get a upgrade to a write lock. * * @return true, always. * * @throws LockNotHeld if some other thread owns the lock * @throws UpgradeNotAllowed if the lock is not currently * held by the owner. */ public synchronized boolean upgrade() throws UpgradeNotAllowed, LockNotHeld { return upgrade(-1); } /** * Tries to downgrade a write lock to a read lock. If the current * thread does not hold the lock an exception is thrown. If the * current thread already owns a read lock, nothing happens. If it * owns a write lock, then it is downgraded to a read lock. All * threads waiting for a read lock can now get it only if there are * no other threads waiting for a write lock ahead of them. * * @return true if the downgrade was performed successfully. * * @throws LockNotHeld if the current thread does not own the lock. */ public synchronized boolean downgrade() throws LockNotHeld { // We should already be in the list. If not, it is an error. int index = waitingList.findMe(); if (index == NOT_FOUND) throw new LockNotHeld(); // Get the element for this thread ReaderWriter e = (ReaderWriter)waitingList.get(index); // Downgrade the WRITE lock and notify all threads of the change. if (e.lockType == WRITE) { e.lockType = READ; notifyAll(); } return true; } /** * Tries to relinquish the ownership of a lock. If the * current thread does not hold the lock an exception is * thrown. Note that every call to forReading * and forWriting must have a corresponding * release() call. However, upgrade() * and downgrade() do not have corresponding * release() calls. * * @throws LockNotHeld if the current thread does not own the lock. */ public synchronized void release() throws LockNotHeld { // We should already be in the list. If not, it is an error. int index = waitingList.findMe(); if (index == NOT_FOUND) throw new LockNotHeld(); // Get the element for this thread ReaderWriter e = (ReaderWriter)waitingList.get(index); // If the lock count goes down to zero, // remove the lock and notify all threads of the change. if ((--e.lockCount) == 0) { waitingList.remove(index); notifyAll(); } } /** * The exception thrown when trying to upgrade a lock when * the lock does not allow upgrades. */ public class UpgradeNotAllowed extends RuntimeException { } /** * The exception thrown when trying to upgrade a lock when * the lock is not held by the current thread. */ public class LockNotHeld extends RuntimeException { } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/util/DataBufferUtils.java0000644000175000017500000003202010203035544026450 0ustar mathieumathieu/* * $RCSfile: DataBufferUtils.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:00 $ * $State: Exp $ */ package com.sun.media.jai.util; import java.awt.image.DataBuffer; import java.lang.reflect.Constructor; import java.lang.reflect.Method; /** * Utility class to enable compatibility with Java 2 1.4 real-valued * DataBuffer classes. Factory methods and data array accessors are * defined to use reflection. The core Java classes are given precedence * over their JAI equivalents. */ public final class DataBufferUtils { /** * Priority ordered array of DataBufferFloat class names. */ private static final String[] FLOAT_CLASS_NAMES = { "java.awt.image.DataBufferFloat", "javax.media.jai.DataBufferFloat", "com.sun.media.jai.codecimpl.util.DataBufferFloat" }; /** * Priority ordered array of DataBufferDouble class names. */ private static final String[] DOUBLE_CLASS_NAMES = { "java.awt.image.DataBufferDouble", "javax.media.jai.DataBufferDouble", "com.sun.media.jai.codecimpl.util.DataBufferDouble" }; /** * Classes to be used for DB float and double. */ private static Class floatClass = null; private static Class doubleClass = null; /** * Initialize float and double DB classes. */ static { floatClass = getDataBufferClass(DataBuffer.TYPE_FLOAT); doubleClass = getDataBufferClass(DataBuffer.TYPE_DOUBLE); } /** * Return the class for the specified data type. * * @param dataType The data type from among * DataBuffer.TYPE_*. */ private static final Class getDataBufferClass(int dataType) { // Set the array of class names. String[] classNames = null; switch(dataType) { case DataBuffer.TYPE_FLOAT: classNames = FLOAT_CLASS_NAMES; break; case DataBuffer.TYPE_DOUBLE: classNames = DOUBLE_CLASS_NAMES; break; default: throw new IllegalArgumentException("dataType == "+dataType+"!"); } // Initialize the return value. Class dataBufferClass = null; // Loop over the class names array in priority order. for(int i = 0; i < classNames.length; i++) { try { // Attempt to get the class. dataBufferClass = Class.forName(classNames[i]); // Break if the class was found. if(dataBufferClass != null) { break; } } catch(ClassNotFoundException e) { // Ignore the exception. } } // Throw an exception if no class was found. if(dataBufferClass == null) { throw new RuntimeException (JaiI18N.getString("DataBufferUtils0")+" "+ (String)(dataType == DataBuffer.TYPE_FLOAT ? "DataBufferFloat" : "DataBufferDouble")); } return dataBufferClass; } /** * Construct a DataBuffer of the requested type * using the parameters with the specified types and values. */ private static final DataBuffer constructDataBuffer(int dataType, Class[] paramTypes, Object[] paramValues) { Class dbClass = null; switch(dataType) { case DataBuffer.TYPE_FLOAT: dbClass = floatClass; break; case DataBuffer.TYPE_DOUBLE: dbClass = doubleClass; break; default: throw new IllegalArgumentException("dataType == "+dataType+"!"); } DataBuffer dataBuffer = null; try { Constructor constructor = dbClass.getConstructor(paramTypes); dataBuffer = (DataBuffer)constructor.newInstance(paramValues); } catch(Exception e) { throw new RuntimeException(JaiI18N.getString("DataBufferUtils1")); } return dataBuffer; } /** * Invoke the DataBuffer method of the specified name * using the parameters with the specified types and values. */ private static final Object invokeDataBufferMethod(DataBuffer dataBuffer, String methodName, Class[] paramTypes, Object[] paramValues) { if(dataBuffer == null) { throw new IllegalArgumentException("dataBuffer == null!"); } Class dbClass = dataBuffer.getClass(); Object returnValue = null; try { Method method = dbClass.getMethod(methodName, paramTypes); returnValue = method.invoke(dataBuffer, paramValues); } catch(Exception e) { throw new RuntimeException(JaiI18N.getString("DataBufferUtils2")+ " \""+methodName+"\"."); } return returnValue; } public static final DataBuffer createDataBufferFloat(float[][] dataArray, int size) { return constructDataBuffer(DataBuffer.TYPE_FLOAT, new Class[] {float[][].class, int.class}, new Object[] {dataArray, new Integer(size)}); } public static final DataBuffer createDataBufferFloat(float[][] dataArray, int size, int[] offsets) { return constructDataBuffer(DataBuffer.TYPE_FLOAT, new Class[] {float[][].class, int.class, int[].class}, new Object[] {dataArray, new Integer(size), offsets}); } public static final DataBuffer createDataBufferFloat(float[] dataArray, int size) { return constructDataBuffer(DataBuffer.TYPE_FLOAT, new Class[] {float[].class, int.class}, new Object[] {dataArray, new Integer(size)}); } public static final DataBuffer createDataBufferFloat(float[] dataArray, int size, int offset) { return constructDataBuffer(DataBuffer.TYPE_FLOAT, new Class[] {float[].class, int.class, int.class}, new Object[] {dataArray, new Integer(size), new Integer(offset)}); } public static final DataBuffer createDataBufferFloat(int size) { return constructDataBuffer(DataBuffer.TYPE_FLOAT, new Class[] {int.class}, new Object[] {new Integer(size)}); } public static final DataBuffer createDataBufferFloat(int size, int numBanks) { return constructDataBuffer(DataBuffer.TYPE_FLOAT, new Class[] {int.class, int.class}, new Object[] {new Integer(size), new Integer(numBanks)}); } public static final float[][] getBankDataFloat(DataBuffer dataBuffer) { return (float[][])invokeDataBufferMethod(dataBuffer, "getBankData", null, null); } public static final float[] getDataFloat(DataBuffer dataBuffer) { return (float[])invokeDataBufferMethod(dataBuffer, "getData", null, null); } public static final float[] getDataFloat(DataBuffer dataBuffer, int bank) { return (float[])invokeDataBufferMethod(dataBuffer, "getData", new Class[] {int.class}, new Object[] {new Integer(bank)}); } public static final DataBuffer createDataBufferDouble(double[][] dataArray, int size) { return constructDataBuffer(DataBuffer.TYPE_DOUBLE, new Class[] {double[][].class, int.class}, new Object[] {dataArray, new Integer(size)}); } public static final DataBuffer createDataBufferDouble(double[][] dataArray, int size, int[] offsets) { return constructDataBuffer(DataBuffer.TYPE_DOUBLE, new Class[] {double[][].class, int.class, int[].class}, new Object[] {dataArray, new Integer(size), offsets}); } public static final DataBuffer createDataBufferDouble(double[] dataArray, int size) { return constructDataBuffer(DataBuffer.TYPE_DOUBLE, new Class[] {double[].class, int.class}, new Object[] {dataArray, new Integer(size)}); } public static final DataBuffer createDataBufferDouble(double[] dataArray, int size, int offset) { return constructDataBuffer(DataBuffer.TYPE_DOUBLE, new Class[] {double[].class, int.class, int.class}, new Object[] {dataArray, new Integer(size), new Integer(offset)}); } public static final DataBuffer createDataBufferDouble(int size) { return constructDataBuffer(DataBuffer.TYPE_DOUBLE, new Class[] {int.class}, new Object[] {new Integer(size)}); } public static final DataBuffer createDataBufferDouble(int size, int numBanks) { return constructDataBuffer(DataBuffer.TYPE_DOUBLE, new Class[] {int.class, int.class}, new Object[] {new Integer(size), new Integer(numBanks)}); } public static final double[][] getBankDataDouble(DataBuffer dataBuffer) { return (double[][])invokeDataBufferMethod(dataBuffer, "getBankData", null, null); } public static final double[] getDataDouble(DataBuffer dataBuffer) { return (double[])invokeDataBufferMethod(dataBuffer, "getData", null, null); } public static final double[] getDataDouble(DataBuffer dataBuffer, int bank) { return (double[])invokeDataBufferMethod(dataBuffer, "getData", new Class[] {int.class}, new Object[] {new Integer(bank)}); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/util/CacheDiagnostics.java0000644000175000017500000000233710203035544026627 0ustar mathieumathieu/* * $RCSfile: CacheDiagnostics.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:59 $ * $State: Exp $ */ /** * Diagnostics Interface for SunTileCache. These routines apply to the * tile cache, not the cached tile. All methods are implicitly public. * * @since JAI 1.1 */ package com.sun.media.jai.util; public interface CacheDiagnostics { /** Enable diagnostic monitoring of the tile cache. */ void enableDiagnostics(); /** Disable diagnostic monitoring of the tile cache. */ void disableDiagnostics(); /** Returns the total number of tiles in a particular cache. */ long getCacheTileCount(); /** Returns the total memory used in a particular cache. */ long getCacheMemoryUsed(); /** * Returns the number of times this tile was requested when * it was in the tile cache. */ long getCacheHitCount(); /** * Returns the number of times this tile was requested when * it was not in the tile cache. */ long getCacheMissCount(); /** Resets the hit and miss counts to zero. */ void resetCounts(); // resets hit,miss counts } jai-core-1.1.4/src/share/classes/com/sun/media/jai/util/com.sun.media.jai.util.properties0000644000175000017500000000377010203035544031067 0ustar mathieumathieu# # $RCSfile: com.sun.media.jai.util.properties,v $ # # Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. # # Use is subject to license terms. # # $Revision: 1.1 $ # $Date: 2005-02-11 04:57:02 $ # $State: Exp $ # CaselessStringArrayTable0=Can not look up a null key. CaselessStringArrayTable1=Could not find the key. DataBufferUtils0=Cannot find class for DataBufferUtils1=Cannot construct DataBuffer. DataBufferUtils2=Cannot invoke DataBuffer method Generic0=The input argument(s) may not be null. ImageUtil0=The supplied Raster does not represent a binary data set. ImageUtil1=Default ColorModel method is non-static. ImageUtil2=Default ColorModel method return type is not ColorModel. ImageUtil3=Default ColorModel method does not accept a single parameter of class SampleModel. ImageUtil4=Exception occurs when generate a compatible color model for a sample model. JDKWorkarounds0=SampleModel and ColorModel parameters must be non-null. PropertyGeneratorImpl0=The parameter(s) may not be null. PropertyGeneratorImpl1=The parameter arrays may not be zero length. PropertyGeneratorImpl2=The property name and class array lengths must be equal. PropertyGeneratorImpl3=The class of the operation node is not supported. PropertyGeneratorImpl4=Property classes cannot correspond to a primitive type. PropertyUtil0=The property name prefix may not be null. SunTileCache=Tile cache memory capacity must be greater than or equal to 0. SunTileCache0=ConcurrentModificationException occurs when remove tiles from cache. SunTileScheduler0=All parameters must be non-null. SunTileScheduler1=The image parameter must be non-null. SunTileScheduler2=The parallelism must be non-negative. SunTileScheduler3=The request parameter must be non-null. SunTileScheduler4=The target and tileIndices parameters must be non-null. SunTileScheduler5=Waiting thread received a null tile. SunTileScheduler6=Problem occurs when computing a tile by the owner. SunTileScheduler7=Exception occurs when computing tiles. SunTileSchedulerName=SunTileScheduler jai-core-1.1.4/src/share/classes/com/sun/media/jai/util/DisposableNullOpImage.java0000644000175000017500000000427310444643225027627 0ustar mathieumathieu/* * $RCSfile: DisposableNullOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2006-06-17 00:02:27 $ * $State: Exp $ */ package com.sun.media.jai.util; import java.awt.image.RenderedImage; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Method; import java.util.Map; import javax.media.jai.ImageLayout; import javax.media.jai.NullOpImage; import javax.media.jai.PlanarImage; import javax.media.jai.RenderedImageAdapter; /** * NullOpImage subclass which conditionally forwards * {@link #dispose()} to its source. The call will be forwarded if the * source is a PlanarImage or a RenderedImage * wrapped by RenderedImageAdapter and which has a * dispose() method with no parameters. In the former case * the call is forwarded directly, and in the latter via reflection. * * @since JAI 1.1.3 */ public class DisposableNullOpImage extends NullOpImage { public DisposableNullOpImage(RenderedImage source, ImageLayout layout, Map configuration, int computeType) { super(source, layout, configuration, computeType); } public synchronized void dispose() { PlanarImage src = getSource(0); if(src instanceof RenderedImageAdapter) { // Use relection to invoke dispose(); RenderedImage trueSrc = ((RenderedImageAdapter)src).getWrappedImage(); Method disposeMethod = null; try { Class cls = trueSrc.getClass(); disposeMethod = cls.getMethod("dispose", null); if(!disposeMethod.isAccessible()) { AccessibleObject.setAccessible(new AccessibleObject[] { disposeMethod }, true); } disposeMethod.invoke(trueSrc, null); } catch(Exception e) { // Ignore it. } } else { // Invoke dispose() directly. src.dispose(); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/util/PropertyGeneratorImpl.java0000644000175000017500000002433510203035544027753 0ustar mathieumathieu/* * $RCSfile: PropertyGeneratorImpl.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:01 $ * $State: Exp $ */ package com.sun.media.jai.util; import javax.media.jai.PropertyGenerator; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; /** * This utility class provides an implementation of the * PropertyGenerator interface that is suitable for * extending. In addition to providing a no-arg constructor which * passes the appropriate arrays, subclasses need only override the * method getProperty(String,Object) in which the method * validate() should be invoked as the first statement. * * @see PropertyGenerator */ public abstract class PropertyGeneratorImpl implements PropertyGenerator { private String[] propertyNames; private Class[] propertyClasses; private Class[] supportedOpClasses; /** * Constructs a PropertyGeneratorImpl. All parameters are * saved by reference. * * @param propertyNames Names of emitted properties. * @param propertyClasses Classes of emitted properties. * @param supportedOpClasses Classes of supported op nodes. * @exception IllegalArgumentException if any of the array parameters * is null. * @exception IllegalArgumentException if any of the array parameters * has length zero. * @exception IllegalArgumentException if the lengths of the arrays * propertyNames and propertyClasses * are unequal. */ protected PropertyGeneratorImpl(String[] propertyNames, Class[] propertyClasses, Class[] supportedOpClasses) { if(propertyNames == null || propertyClasses == null || supportedOpClasses == null) { throw new IllegalArgumentException(JaiI18N.getString("PropertyGeneratorImpl0")); } else if(propertyNames.length == 0 || propertyClasses.length == 0 || supportedOpClasses.length == 0) { throw new IllegalArgumentException(JaiI18N.getString("PropertyGeneratorImpl1")); } else if(propertyNames.length != propertyClasses.length) { throw new IllegalArgumentException(JaiI18N.getString("PropertyGeneratorImpl2")); } for(int i = 0; i < propertyClasses.length; i++) { if(propertyClasses[i].isPrimitive()) { throw new IllegalArgumentException(JaiI18N.getString("PropertyGeneratorImpl4")); } } this.propertyNames = propertyNames; this.propertyClasses = propertyClasses; this.supportedOpClasses = supportedOpClasses; } /** * Returns an array of Strings naming properties emitted * by this property generator. * * @return an array of Strings that may be passed as parameter * names to the getProperty() method. */ public String[] getPropertyNames() { return propertyNames; } /** * Returns the class expected to be returned by a request for * the property with the specified name. If this information * is unavailable, null will be returned indicating * that getProperty(propertyName).getClass() should * be executed instead. A null value might * be returned for example to prevent generating the value of * a deferred property solely to obtain its class. * * @return The Class expected to be return by a * request for the value of this property or null. * @exception IllegalArgumentException if propertyName * is null. */ public Class getClass(String propertyName) { if(propertyName == null) { throw new IllegalArgumentException(JaiI18N.getString("PropertyGeneratorImpl0")); } // Linear search as there are likely few properties. int numProperties = propertyNames.length; for(int i = 0; i < numProperties; i++) { if(propertyName.equalsIgnoreCase(propertyNames[i])) { return propertyClasses[i]; } } // XXX Should an IllegalArgumentException be thrown if this property // is not emitted by this PropertyGenerator? return null; } /** * Determines whether the specified Object will * be recognized by getProperty(String,Object). * * @exception IllegalArgumentException if opNode * is null. */ public boolean canGenerateProperties(Object opNode) { if(opNode == null) { throw new IllegalArgumentException(JaiI18N.getString("PropertyGeneratorImpl0")); } int numClasses = supportedOpClasses.length; if(numClasses == 1) { return supportedOpClasses[0].isInstance(opNode); } else { // Linear search as there are likely few supported classes. for(int i = 0; i < numClasses; i++) { if(supportedOpClasses[i].isInstance(opNode)) { return true; } } } return false; } /** * Computes the value of a property relative to an environment * of pre-existing properties. The case of the supplied * String is ignored. * *

In the case of an OperationNode in a chain of * operations these properties may be emitted by the sources of the * node in a chain or the parameters of that operation. The information * requisite to compute the requested property must be available via the * supplied OperationNode. It is legal to call * getProperty() on the operation's sources. * * @param name the name of the property, as a String. * @param op the Object from which properties will * be generated. * @return the value of the property, as an Object or the * value java.awt.Image.UndefinedProperty. * @exception IllegalArgumentException if name or * opNode is null. * @exception IllegalArgumentException if opNode is * not an instance of a supported class for this method, i.e., * canGenerateProperties(opNode) returns * false. */ public abstract Object getProperty(String name, Object opNode); /** * Computes the value of a property relative to an environment * of pre-existing properties emitted by the sources of * a RenderedOp, and the parameters of that operation. * *

The operation name, sources, and ParameterBlock * of the RenderedOp being processed may be obtained by * means of the op.getOperationName, * op.getSources(), and op.getParameterBlock() * methods. It is legal to call getProperty() on the * operation's sources. * * @param name the name of the property, as a String. * @param op the RenderedOp representing the operation. * @return the value of the property, as an Object or the * value java.awt.Image.UndefinedProperty. * * @deprecated as of Java(tm) Advanced Imaging 1.1. Use * getProperty(String,Object) instead. * @exception IllegalArgumentException if name or * op is null. */ public Object getProperty(String name, RenderedOp op) { return getProperty(name, (Object)op); } /** * Computes the value of a property relative to an environment * of pre-existing properties emitted by the sources of * a RenderableOp, and the parameters of that operation. * *

The operation sources and ParameterBlock of the * RenderableOp being processed may be obtained by * means of the op.getSources() and * op.getParameterBlock() methods. It is legal to call * getProperty() on the operation's sources. * * @param name the name of the property, as a String. * @param op the RenderableOp representing the operation. * @return the value of the property, as an Object or the * value java.awt.Image.UndefinedProperty. * @exception IllegalArgumentException if name or * op is null. * * @deprecated as of Java(tm) Advanced Imaging 1.1. Use * getProperty(String,Object) instead. */ public Object getProperty(String name, RenderableOp op) { return getProperty(name, (Object)op); } /** * Throws an exception if the arguments are illegal for * getProperty(String,Object). * * @param name the name of the property, as a String. * @param op the Object from which properties will * be generated. * @exception IllegalArgumentException if name or * opNode is null. * @exception IllegalArgumentException if opNode is * not an instance of a supported class for this method, i.e., * canGenerateProperties(opNode) returns * false. */ protected void validate(String name, Object opNode) { if(name == null) { throw new IllegalArgumentException(JaiI18N.getString("PropertyGeneratorImpl0")); } else if(!canGenerateProperties(opNode)) { throw new IllegalArgumentException(JaiI18N.getString("PropertyGeneratorImpl3")); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/util/CaselessStringKeyHashtable.java0000644000175000017500000001710410203035544030650 0ustar mathieumathieu/* * $RCSfile: CaselessStringKeyHashtable.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:59 $ * $State: Exp $ */ package com.sun.media.jai.util; import java.util.Hashtable; import java.util.Map; import javax.media.jai.util.CaselessStringKey; /** * A Hashtable where the keys are CaselessStringKeys. */ public class CaselessStringKeyHashtable extends Hashtable implements Cloneable, java.io.Serializable { /** * Constructs a new, empty CaselessStringKeyHashtable. */ public CaselessStringKeyHashtable() { super(); } /** * Constructs a new CaselessStringKeyHashtable with the same * mappings as the given Map. * * @param t the map whose mappings are to be placed in this map. */ public CaselessStringKeyHashtable(Map t) { super(t); } /** * Returns a clone of the CaselessStringKeyHashtable * as an Object. */ public Object clone() { return super.clone(); } /** * Tests if the specified String is a key in this * hashtable. The key is wrapped by a CaselessStringKey * before being looked up. * * @param key possible key. * * @return true if and only if the specified object * is a key in this hashtable, as determined by the * equals method; false otherwise. */ public boolean containsKey(String key) { return super.containsKey(new CaselessStringKey(key)); } /** * Tests if the specified CaselessStringKey is a key in * this hashtable. * * @param key possible key. * * @return true if and only if the specified object * is a key in this hashtable, as determined by the * equals method; false otherwise. */ public boolean containsKey(CaselessStringKey key) { return super.containsKey(key); } /** * Allow only String and CaselessStringKey * keys to be looked up. This always throws an IllegalArgumentException. * * @param key possible key. * * @return always throws IllegalArgumentException. */ public boolean containsKey(Object key) { throw new IllegalArgumentException(); } /** * Returns the value to which the specified key is mapped in this * hashtable. The key is wrapped by a CaselessStringKey * before being looked up. * * @param key a key in the hashtable. * * @return the value to which the key is mapped in this hashtable; * null if the key is not mapped to any value in * this hashtable. * @see #put(String, Object) */ public Object get(String key) { return super.get(new CaselessStringKey(key)); } /** * Returns the value to which the specified key is mapped in this * hashtable. * * @param key a key in the hashtable. * * @return the value to which the key is mapped in this hashtable; * null if the key is not mapped to any value in * this hashtable. * @see #put(CaselessStringKey, Object) */ public Object get(CaselessStringKey key) { return super.get(key); } /** * Allow only String and CaselessStringKey keys to be looked up. * This always throws an IllegalArgumentException. * * @param key possible key. * * @return always throws IllegalArgumentException. * @see #put(Object, Object) */ public Object get(Object key) { throw new IllegalArgumentException(); } /** * Maps the specified key to the specified * value in this hashtable. Neither the key nor * the value can be null. The key is wrapped by a * CaselessStringKey before mapping the key to the * value.

* * The value can be retrieved by calling the get method * with a key that is equal to the original key. * * @param key the hashtable key. * @param value the value. * @return the previous value of the specified key in this hashtable, * or null if it did not have one. * @exception IllegalArgumentException if the key or value is * null. * @see Object#equals(Object) * @see #get(String) */ public Object put(String key, Object value) { if ( key == null || value == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return super.put(new CaselessStringKey(key), value); } /** * Maps the specified key to the specified * value in this hashtable. Neither the key nor the * value can be null.

* * The value can be retrieved by calling the get method * with a key that is equal to the original key. * * @param key the hashtable key. * @param value the value. * @return the previous value of the specified key in this hashtable, * or null if it did not have one. * @exception IllegalArgumentException if the key or value is * null. * @see Object#equals(Object) * @see #get(CaselessStringKey) */ public Object put(CaselessStringKey key, Object value) { if ( key == null || value == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return super.put(key, value); } /** * Allow only String and CaselessStringKey keys to be mapped. * This always throws an IllegalArgumentException. * * @param key possible key. * @param value the value. * * @return always throws IllegalArgumentException. * @see #get(Object) */ public Object put(Object key, Object value) { throw new IllegalArgumentException(); } /** * Removes the key (and its corresponding value) from this * hashtable. This method does nothing if the key is not in the * hashtable. The key is wrapped by a CaselessStringKey * before trying to remove the entry. * * @param key the key that needs to be removed. * @return the value to which the key had been mapped in this hashtable, * or null if the key did not have a mapping. */ public Object remove(String key) { return super.remove(new CaselessStringKey(key)); } /** * Removes the key (and its corresponding value) from this * hashtable. This method does nothing if the key is not in the * hashtable. * * @param key the key that needs to be removed. * @return the value to which the key had been mapped in this hashtable, * or null if the key did not have a mapping. */ public Object remove(CaselessStringKey key) { return super.remove(key); } /** * Allow only String and CaselessStringKey keys to be removed. * This always throws an IllegalArgumentException. * * @param key possible key. * * @return always throws IllegalArgumentException. * @see #get(Object) */ public Object remove(Object key) { throw new IllegalArgumentException(); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/util/JaiI18N.java0000644000175000017500000000074310203035544024536 0ustar mathieumathieu/* * $RCSfile: JaiI18N.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:00 $ * $State: Exp $ */ package com.sun.media.jai.util; import com.sun.media.jai.util.PropertyUtil; class JaiI18N { static String packageName = "com.sun.media.jai.util"; public static String getString(String key) { return PropertyUtil.getString(packageName, key); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/util/PropertyUtil.java0000644000175000017500000001462010203035544026114 0ustar mathieumathieu/* * $RCSfile: PropertyUtil.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:01 $ * $State: Exp $ */ package com.sun.media.jai.util; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.io.IOException; import java.util.Hashtable; import java.util.Iterator; import java.util.Vector; import java.util.PropertyResourceBundle; import java.util.ResourceBundle; import java.util.StringTokenizer; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.net.URL; import java.security.AccessController; import java.security.PrivilegedAction; public class PropertyUtil { private static Hashtable bundles = new Hashtable(); private static String propertiesDir = "javax/media/jai"; public static InputStream getFileFromClasspath(String path) throws IOException, FileNotFoundException { InputStream is; final String pathFinal = path; final String sep = File.separator; String tmpHome = null; try { tmpHome = System.getProperty("java.home"); } catch(Exception e) { tmpHome = null; // Redundant } final String home = tmpHome; final String urlHeader = tmpHome == null ? null : home + sep + "lib" + sep; if(home != null) { String libExtPath = urlHeader + "ext" + sep + path; File libExtFile = new File(libExtPath); try { if (libExtFile.exists()) { is = new FileInputStream(libExtFile); if (is != null) { return is; } } } catch (java.security.AccessControlException e) { // When the files are packed into jar files, the // permission to access these files in a security environment // isn't granted in the policy files in most of the cases. // Thus, this java.security.AccessControlException is // thrown. To continue the searching in the jar files, // catch this exception and do nothing here. // The fix of 4531516. } } is = PropertyUtil.class.getResourceAsStream("/" + path); if (is != null) { return is; } // The above call doesn't work if the jai is an installed extension // in the main jre/lib directory (as of 5-21-1999 the javaplugin // doesn't look in the ext diretory which is a bug). We'll // try to load the file from either $java_home/ext/jai_core.jar // or $java_home/jai_core.jar. The ext is where it should be // when the bug finally gets fixed. PrivilegedAction p = new PrivilegedAction() { public Object run() { String localHome = null; String localUrlHeader = null; if(home != null) { localHome = home; localUrlHeader = urlHeader; } else { localHome = System.getProperty("java.home"); localUrlHeader = localHome + sep + "lib" + sep; } String filenames[] = { localUrlHeader + "ext" + sep + "jai_core.jar", localUrlHeader + "ext" + sep + "jai_codec.jar", localUrlHeader + "jai_core.jar", localUrlHeader + "jai_codec.jar" }; for (int i = 0; i < filenames.length; i++) { try { InputStream tmpIS = getFileFromJar(filenames[i],pathFinal); if (tmpIS != null) { return tmpIS; } } catch (Exception e) { } } return null; } }; return (InputStream)AccessController.doPrivileged(p); } private static InputStream getFileFromJar(String jarFilename, String path) throws Exception { // Look in jar file JarFile f = null; try { f = new JarFile(jarFilename); } catch (Exception e) { } JarEntry ent = f.getJarEntry(path); if (ent != null) { return f.getInputStream(ent); } return null; } /** Get bundle from .properties files in javax/media/jai dir. */ private static ResourceBundle getBundle(String packageName) { ResourceBundle bundle = null; InputStream in = null; try { in = getFileFromClasspath(propertiesDir + "/" + packageName + ".properties"); if (in != null) { bundle = new PropertyResourceBundle(in); bundles.put(packageName, bundle); return bundle; } } catch (Exception e) { e.printStackTrace(); } return null; } public static String getString(String packageName, String key) { ResourceBundle b = (ResourceBundle)bundles.get(packageName); if (b == null) { b = getBundle(packageName); } return b.getString(key); } /** * Utility method to search the full list of property names for * matches. If propertyNames is null * then null is returned. * * @exception IllegalArgumentException if prefix is * null and propertyNames is * non-null. */ public static String[] getPropertyNames(String[] propertyNames, String prefix) { if (propertyNames == null) { return null; } else if(prefix == null) { throw new IllegalArgumentException(JaiI18N.getString("PropertyUtil0")); } prefix = prefix.toLowerCase(); Vector names = new Vector(); for (int i = 0; i < propertyNames.length; i++) { if (propertyNames[i].toLowerCase().startsWith(prefix)) { names.addElement(propertyNames[i]); } } if (names.size() == 0) { return null; } // Copy the strings from the Vector over to a String array. String prefixNames[] = new String[names.size()]; int count = 0; for (Iterator it = names.iterator(); it.hasNext(); ) { prefixNames[count++] = (String)it.next(); } return prefixNames; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/util/SunCachedTile.java0000644000175000017500000001421410203035544026104 0ustar mathieumathieu/* * $RCSfile: SunCachedTile.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:02 $ * $State: Exp $ */ package com.sun.media.jai.util; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; import java.lang.reflect.Method; import java.math.BigInteger; import javax.media.jai.CachedTile; import javax.media.jai.PlanarImage; import javax.media.jai.remote.SerializableRenderedImage; /** * Information associated with a cached tile. * *

This class is used by SunTileCache to create an object that * includes all the information associated with a tile, and is put * into the tile cache. * *

It also serves as a double linked list. * * @see SunTileCache * */ final class SunCachedTile implements CachedTile { // Soft or Weak references need to be used, or the objects // never get garbage collected. The OpImage finalize // method calls removeTiles(). It was suggested, that // the owner be a weak reference. Raster tile; // the tile to be cached WeakReference owner; // the RenderedImage this tile belongs to int tileX; // tile X index int tileY; // tile Y index Object tileCacheMetric; // Metric for weighting tile computation cost long timeStamp; // the last time this tile is accessed Object key; // the key used to hash this tile long memorySize; // the memory used by this tile in bytes SunCachedTile previous; // the SunCachedTile before this tile SunCachedTile next; // the SunCachedTile after this tile int action = 0; // add, remove, update from tile cache /** * Constructor that takes a tile cache metric * @since 1.1 */ SunCachedTile(RenderedImage owner, int tileX, int tileY, Raster tile, Object tileCacheMetric) { this.owner = new WeakReference(owner); this.tile = tile; this.tileX = tileX; this.tileY = tileY; this.tileCacheMetric = tileCacheMetric; // may be null key = hashKey(owner, tileX, tileY); // tileMemorySize(Raster tile) inlined for performance DataBuffer db = tile.getDataBuffer(); memorySize = db.getDataTypeSize(db.getDataType()) / 8L * db.getSize() * db.getNumBanks(); } /** * Returns the hash table "key" as a Object for this * tile. For PlanarImage and * SerializableRenderedImage, the key is generated by * the method ImageUtilgenerateID(Object) . For the * other cases, a Long object is returned. * The upper 32 bits for this Long is the tile owner's * hash code, and the lower 32 bits is the tile's index. */ static Object hashKey(RenderedImage owner, int tileX, int tileY) { long idx = tileY * (long)owner.getNumXTiles() + tileX; BigInteger imageID = null; if (owner instanceof PlanarImage) imageID = (BigInteger)((PlanarImage)owner).getImageID(); else if (owner instanceof SerializableRenderedImage) imageID = (BigInteger)((SerializableRenderedImage)owner).getImageID(); if (imageID != null) { byte[] buf = imageID.toByteArray(); int length = buf.length; byte[] buf1 = new byte[length + 8]; System.arraycopy(buf, 0, buf1, 0, length); for (int i = 7, j = 0; i >= 0; i--, j += 8) buf1[length++] = (byte)(idx >> j); return new BigInteger(buf1); } idx = idx & 0x00000000ffffffffL; return new Long(((long)owner.hashCode() << 32) | idx); } /** * Special version of hashKey for use in SunTileCache.removeTiles(). * Minimizes the overhead of repeated calls to * hashCode and getNumTiles(). Note that this causes a * linkage between the CachedTile and SunTileCache classes * in that SunTileCache now has to understand how the * tileIndex is calculated. */ /* static Long hashKey(int ownerHashCode, int tileIndex) { long idx = (long)tileIndex; idx = idx & 0x00000000ffffffffL; return new Long(((long)ownerHashCode << 32) | idx); } */ /** Returns the owner's hash code. */ /* static long getOwnerHashCode(Long key) { return key.longValue() >>> 32; } */ /** Returns a string representation of the class object. */ public String toString() { RenderedImage o = (RenderedImage) getOwner(); String ostring = o == null ? "null" : o.toString(); Raster t = getTile(); String tstring = t == null ? "null" : t.toString(); return getClass().getName() + "@" + Integer.toHexString(hashCode()) + ": owner = " + ostring + " tileX = " + Integer.toString(tileX) + " tileY = " + Integer.toString(tileY) + " tile = " + tstring + " key = " + ((key instanceof Long)? Long.toHexString(((Long)key).longValue()) : key.toString()) + " memorySize = " + Long.toString(memorySize) + " timeStamp = " + Long.toString(timeStamp); } /** Returns the cached tile. */ public Raster getTile() { return tile; } /** Returns the owner of the cached tile. */ public RenderedImage getOwner() { return (RenderedImage)owner.get(); } /** Returns the current time stamp */ public long getTileTimeStamp() { return timeStamp; } /** Returns the tileCacheMetric object */ public Object getTileCacheMetric() { return tileCacheMetric; } /** Returns the tile memory size */ public long getTileSize() { return memorySize; } /** Returns information about the method that * triggered the notification event. */ public int getAction() { return action; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/util/Rational.java0000644000175000017500000001412710234540366025214 0ustar mathieumathieu/* * $RCSfile: Rational.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-04-29 23:19:18 $ * $State: Exp $ */ package com.sun.media.jai.util; /** * A class to perform Rational arithmetic. * * @since 1.0 */ public class Rational { public long num; public long denom; public Rational(long num, long denom) { this.num = num; this.denom = denom; } public Rational(Rational r) { this.num = r.num; this.denom = r.denom; } /** * Returns a Rational defined by a given number of terms * of a continued fraction: * * terms[0] + 1 * ------------------------- * terms[1] + 1 * -------------- * terms[2] + ... */ public static Rational createFromFrac(long[] terms, int len) { Rational r = new Rational(0, 1); for (int i = len - 1; i >= 0; i--) { r.add(terms[i]); if (i != 0) { r.invert(); } } return r; } private static final int MAX_TERMS = 20; /** * Returns a Rational that is within the given tolerance * of a given float value. */ public static Rational approximate(float f, float tol) { // Expand f as a continued fraction by repeatedly removing the integer // part and inverting. float rem = f; long[] d = new long[MAX_TERMS]; int index = 0; for (int i = 0; i < MAX_TERMS; i++) { int k = (int)Math.floor(rem); d[index++] = k; rem -= k; if (rem == 0) { break; } rem = 1.0F/rem; } // Evaluate with increasing number of terms until the tolerance // has been reached Rational r = null; for (int i = 1; i <= index; i++) { r = Rational.createFromFrac(d, i); if (Math.abs(r.floatValue() - f) < tol) { return r; } } return r; } /** * Returns a Rational that is within the given tolerance * of a given double value. */ public static Rational approximate(double f, double tol) { // Expand f as a continued fraction by repeatedly removing the integer // part and inverting. double rem = f; long[] d = new long[MAX_TERMS]; int index = 0; for (int i = 0; i < MAX_TERMS; i++) { long k = (long)Math.floor(rem); d[index++] = k; rem -= k; if (rem == 0) { break; } rem = 1.0F/rem; } // Evaluate with increasing number of terms until the tolerance // has been reached Rational r = null; for (int i = 1; i <= index; i++) { r = Rational.createFromFrac(d, i); if (Math.abs(r.doubleValue() - f) < tol) { return r; } } return r; } private static long gcd(long m, long n) { if (m < 0) { m = -m; } if (n < 0) { n = -n; } while (n > 0) { long tmp = m % n; m = n; n = tmp; } return m; } /** Reduces the internal representation to lowest terms. */ private void normalize() { if (denom < 0) { num = -num; denom = -denom; } long gcd = gcd(num, denom); if (gcd > 1) { num /= gcd; denom /= gcd; } } /** * Adds an integer to this Rational value. */ public void add(long i) { num += i*denom; normalize(); } /** * Adds an integer to this Rational value. */ public void add(Rational r) { num = num * r.denom + r.num * denom; denom *= r.denom; normalize(); } /** * Subtracts an int from this Rational value. */ public void subtract(long i) { num -= i*denom; normalize(); } /** * Subtracts an integer to this Rational value. */ public void subtract(Rational r) { num = num * r.denom - r.num * denom; denom *= r.denom; normalize(); } /** * Multiplies an integer to this Rational value. */ public void multiply(long i) { num *= i; normalize(); } /** * Multiplies a Rational to this Rational value. */ public void multiply(Rational r) { num *= r.num; denom *= r.denom; normalize(); } /** * Inverts this Rational value. */ public void invert() { long tmp = num; num = denom; denom = tmp; } /** * Returns the approximate float value of this Rational. */ public float floatValue() { return (float)num/denom; } /** * Returns the approximate double value of this Rational. */ public double doubleValue() { return (double)num/denom; } /** * Returns this Rational as a String in the form '###/###'. */ public String toString() { return num + "/" + denom; } /** * Returns the ceil (equivalent of Math.ceil()) */ public static int ceil(long num, long denom) { int ret = (int)(num/denom); if (num > 0) { if ((num % denom) != 0) { ret += 1; } } return ret; } /** * Returns the floor (equivalent of Math.floor()) */ public static int floor(long num, long denom) { int ret = (int)(num/denom); if (num < 0) { if ((num % denom) != 0) { ret -= 1; } } return ret; } /** * Prints out rational approximations of a floating point argument * with 1 to 8 digits of accuracy. */ public static void main(String[] args) { float f = Float.parseFloat(args[0]); for (int i = 1; i < 15; i++) { Rational r = Rational.approximate(f, (float)Math.pow(10, -i)); System.out.println(r + " = " + r.floatValue()); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/util/PlanarImageProducer.java0000644000175000017500000000526210203035544027320 0ustar mathieumathieu/* * $RCSfile: PlanarImageProducer.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:01 $ * $State: Exp $ */ package com.sun.media.jai.util; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.ImageConsumer; import java.awt.image.ImageProducer; import java.awt.image.Raster; import java.util.Vector; import javax.media.jai.PlanarImage; public class PlanarImageProducer implements ImageProducer { PlanarImage im; Vector consumers = new Vector(); public PlanarImageProducer(PlanarImage im) { this.im = im.createSnapshot(); } public void addConsumer(ImageConsumer ic) { if (!consumers.contains(ic)) { consumers.add(ic); } produceImage(); } public boolean isConsumer(ImageConsumer ic) { return consumers.contains(ic); } public void removeConsumer(ImageConsumer ic) { consumers.remove(ic); } public void requestTopDownLeftRightResend(ImageConsumer ic) { startProduction(ic); } public void startProduction(ImageConsumer ic) { if (!consumers.contains(ic)) { consumers.add(ic); } produceImage(); } private synchronized void produceImage() { int numConsumers = consumers.size(); int minX = im.getMinX(); int minY = im.getMinY(); int width = im.getWidth(); int height = im.getHeight(); int numBands = im.getSampleModel().getNumBands(); int scansize = width*numBands; ColorModel colorModel = im.getColorModel(); int[] pixels = new int[scansize]; Rectangle rect = new Rectangle(minX, minY, width, 1); for (int i = 0; i < numConsumers; i++) { ImageConsumer ic = (ImageConsumer)consumers.elementAt(i); ic.setHints(ImageConsumer.COMPLETESCANLINES | ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.SINGLEFRAME); } for (int y = minY; y < minY + height; y++) { rect.y = y; Raster row = im.getData(rect); row.getPixels(minX, y, width, 1, pixels); for (int i = 0; i < numConsumers; i++) { ImageConsumer ic = (ImageConsumer)consumers.elementAt(i); ic.setPixels(0, y - minY, width, 1, colorModel, pixels, 0, scansize); } } for (int i = 0; i < numConsumers; i++) { ImageConsumer ic = (ImageConsumer)consumers.elementAt(i); ic.imageComplete(ImageConsumer.STATICIMAGEDONE); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/remote/0000755000175000017500000000000011633360406023110 5ustar mathieumathieujai-core-1.1.4/src/share/classes/com/sun/media/jai/remote/JAIServerConfigurationSpi.java0000644000175000017500000000665310203035544030755 0ustar mathieumathieu/* * $RCSfile: JAIServerConfigurationSpi.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:49 $ * $State: Exp $ */ package com.sun.media.jai.remote; import javax.media.jai.JAI; // This uses the ImageIO idea of "services" to look for // concrete class that implement this interface. These concrete // classes must have been registered/listed in the // META-INF/services/com.sun.media.jai.remote.JAIServerConfigurationSpi file. /** *

An interface definition to aid in the automatic loading of * user-defined JAI based Remote Imaging server configuration logic. * *

All concrete classes that implement this * interface can register by listing themselves in the * "META-INF/services/com.sun.media.jai.remote.JAIServerConfigurationSpi" * file that can be found in the classpath (this file is often contained * in a jar file along with the class files). The file should contain * a list of fully-qualified concrete provider-class names, one per * line. Space and tab characters surrounding each name, as well as * blank lines, are ignored. The comment character is '#' * (0x23); on each line all characters following the first * comment character are ignored. The file must be encoded in UTF-8. * *

If a particular concrete provider class is named in more than one * configuration file, or is named in the same configuration file more * than once, then the duplicates will be ignored. The configuration * file naming a particular provider need not be in the same jar file or * other distribution unit as the provider itself. The provider must be * accessible from the same class loader that was initially queried to * locate the configuration file; note that this is not necessarily the * class loader that found the file. * *

All such concrete classes must have a zero-argument * constructor so that they may be instantiated during lookup. The * updateServer() method of all such registered * classes will be called with the default instance of the JAI * class. Note that this will take place after the JAI * OperationRegistry has been initialized with the * default JAI registry file (META-INF/javax.media.jai.registryFile.jai), * once all "META-INF/registryFile.jai"s found in the * classpath are loaded and the updateRegistry method of each * OperationRegistrySpi instance has been executed. There is * no guarantee of the order in which the updateServer() method * of each JAIServerConfigurationSpi instance will be invoked. * *

It is possible to provide arguments to a class implementing this * interface (or any other Service Provider Interface) using the standard * Java -D= mechanism on * the command line when starting an application. * * @see javax.media.jai.remote.JAIRMIDescriptor * @see javax.media.jai.OperationRegistry * @see javax.media.jai.OperationRegistry#writeExternal * @see javax.media.jai.OperationRegistrySpi * * @since JAI 1.1 */ public interface JAIServerConfigurationSpi { /** * This method will be called for all registered "service-providers" * of this interface just after the default JAI instance * has been constructed. */ public void updateServer(JAI jaiInstance); } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/0000755000175000017500000000000011633360406022672 5ustar mathieumathieujai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/StreamSegmentMapper.java0000644000175000017500000000626410203035544027462 0ustar mathieumathieu/* * $RCSfile: StreamSegmentMapper.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:33 $ * $State: Exp $ */ package com.sun.media.jai.codec; /** * An interface for use with the SegmentedSeekableStream * class. An instance of the StreamSegmentMapper * interface provides the location and length of a segment of a source * SeekableStream corresponding to the initial portion of * a desired segment of the output stream. * *

As an example, consider a mapping between a source * SeekableStream src and a SegmentedSeekableStream * dst comprising bytes 100-149 and 200-249 of the source * stream. The dst stream has a reference to an instance * mapper of StreamSegmentMapper. * *

A call to dst.seek(0); dst.read(buf, 0, 10) will * result in a call to mapper.getStreamSegment(0, 10), * returning a new StreamSegment with a starting * position of 100 and a length of 10 (or less). This indicates that * in order to read bytes 0-9 of the segmented stream, bytes 100-109 * of the source stream should be read. * *

A call to dst.seek(10); int nbytes = dst.read(buf, 0, * 100) is somewhat more complex, since it will require data * from both segments of src. The method * mapper.getStreamSegment(10, 100) will be called. This * method will return a new StreamSegment with a starting * position of 110 and a length of 40 (or less). The length is * limited to 40 since a longer value would result in a read past the * end of the first segment. The read will stop after the first 40 * bytes and an addition read or reads will be required to obtain the * data contained in the second segment. * *

This interface is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. */ public interface StreamSegmentMapper { /** * Returns a StreamSegment object indicating the * location of the initial portion of a desired segment in the * source stream. The length of the returned * StreamSegment may be smaller than the desired * length. * * @param pos The desired starting position in the * SegmentedSeekableStream, as a long. * @param length The desired segment length. */ StreamSegment getStreamSegment(long pos, int length); /** * Sets the values of a StreamSegment object * indicating the location of the initial portion of a desired * segment in the source stream. The length of the returned * StreamSegment may be smaller than the desired * length. * * @param pos The desired starting position in the * SegmentedSeekableStream, as a long. * @param length The desired segment length. * @param seg A StreamSegment object to be overwritten. */ void getStreamSegment(long pos, int length, StreamSegment seg); } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/FileSeekableStream.java0000644000175000017500000001414310336227056027231 0ustar mathieumathieu/* * $RCSfile: FileSeekableStream.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-11-15 00:39:10 $ * $State: Exp $ */ package com.sun.media.jai.codec; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; /** * A subclass of SeekableStream that takes its input * from a File or RandomAccessFile. * Backwards seeking is supported. The mark() and * reset() methods are supported. * *

This class is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. */ public class FileSeekableStream extends SeekableStream { private RandomAccessFile file; private long markPos = -1; // Base 2 logarithm of the cache page size private static final int PAGE_SHIFT = 9; // The page size, derived from PAGE_SHIFT private static final int PAGE_SIZE = 1 << PAGE_SHIFT; // Binary mask to find the offset of a pointer within a cache page private static final int PAGE_MASK = PAGE_SIZE - 1; // Number of pages to cache private static final int NUM_PAGES = 32; // Reads longer than this bypass the cache private static final int READ_CACHE_LIMIT = PAGE_SIZE; // The page cache private byte[][] pageBuf = new byte[PAGE_SIZE][NUM_PAGES]; // The index of the file page held in a given cache entry, // -1 = invalid. private int[] currentPage = new int[NUM_PAGES]; private long length = 0L; private long pointer = 0L; /** * Constructs a FileSeekableStream from a * RandomAccessFile. */ public FileSeekableStream(RandomAccessFile file) throws IOException { this.file = file; file.seek(0L); this.length = file.length(); // Allocate the cache pages and mark them as invalid for (int i = 0; i < NUM_PAGES; i++) { pageBuf[i] = new byte[PAGE_SIZE]; currentPage[i] = -1; } } /** * Constructs a FileSeekableStream from a * File. */ public FileSeekableStream(File file) throws IOException { this(new RandomAccessFile(file, "r")); } /** * Constructs a FileSeekableStream from a * String path name. */ public FileSeekableStream(String name) throws IOException { this(new RandomAccessFile(name, "r")); } /** Returns true since seeking backwards is supported. */ public final boolean canSeekBackwards() { return true; } /** * Returns the current offset in this stream. * * @return the offset from the beginning of the stream, in bytes, * at which the next read occurs. * @exception IOException if an I/O error occurs. */ public final long getFilePointer() throws IOException { return pointer; } public final void seek(long pos) throws IOException { if (pos < 0) { throw new IOException(JaiI18N.getString("FileSeekableStream0")); } pointer = pos; } public final int skip(int n) throws IOException { pointer += n; return n; } private byte[] readPage(long pointer) throws IOException { int page = (int)(pointer >> PAGE_SHIFT); for (int i = 0; i < NUM_PAGES; i++) { if (currentPage[i] == page) { return pageBuf[i]; } } // Use random replacement for now int index = (int)(Math.random()*NUM_PAGES); currentPage[index] = page; long pos = ((long)page) << PAGE_SHIFT; long remaining = length - pos; int len = PAGE_SIZE < remaining ? PAGE_SIZE : (int)remaining; file.seek(pos); file.readFully(pageBuf[index], 0, len); return pageBuf[index]; } /** Forwards the request to the real File. */ public final int read() throws IOException { if (pointer >= length) { return -1; } byte[] buf = readPage(pointer); return buf[(int)(pointer++ & PAGE_MASK)] & 0xff; } /** Forwards the request to the real File. */ public final int read(byte[] b, int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); } if ((off < 0) || (len < 0) || (off + len > b.length)) { throw new IndexOutOfBoundsException(); } if (len == 0) { return 0; } len = (int)Math.min((long)len, length - pointer); if (len <= 0) { return -1; } // If the read is large, don't bother to cache it. if (len > READ_CACHE_LIMIT) { file.seek(pointer); int nbytes = file.read(b, off, len); pointer += nbytes; return nbytes; } else { byte[] buf = readPage(pointer); // Compute length to end of page int remaining = PAGE_SIZE - (int)(pointer & PAGE_MASK); int newLen = len < remaining ? len : remaining; System.arraycopy(buf, (int)(pointer & PAGE_MASK), b, off, newLen); pointer += newLen; return newLen; } } /** Forwards the request to the real File. */ public final void close() throws IOException { file.close(); } /** * Marks the current file position for later return using * the reset() method. */ public synchronized final void mark(int readLimit) { markPos = pointer; } /** * Returns the file position to its position at the time of * the immediately previous call to the mark() * method. */ public synchronized final void reset() throws IOException { if (markPos != -1) { pointer = markPos; } } /** Returns true since marking is supported. */ public boolean markSupported() { return true; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/ImageEncodeParam.java0000644000175000017500000000114510203035544026651 0ustar mathieumathieu/* * $RCSfile: ImageEncodeParam.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:30 $ * $State: Exp $ */ package com.sun.media.jai.codec; import java.io.Serializable; /** * An empty (marker) interface to be implemented by all image encoder * parameter classes. * *

This interface is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. */ public interface ImageEncodeParam extends ImageDecodeParam, Cloneable, Serializable { } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/ImageCodec.java0000644000175000017500000007712510345425766025543 0ustar mathieumathieu/* * $RCSfile: ImageCodec.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.3 $ * $Date: 2005-12-07 00:25:26 $ * $State: Exp $ */ package com.sun.media.jai.codec; import java.awt.RenderingHints; import java.awt.Transparency; import java.awt.color.ColorSpace; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.ComponentColorModel; import java.awt.image.IndexColorModel; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.io.File; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; import com.sun.media.jai.codecimpl.BMPCodec; import com.sun.media.jai.codecimpl.FPXCodec; import com.sun.media.jai.codecimpl.GIFCodec; import com.sun.media.jai.codecimpl.JPEGCodec; import com.sun.media.jai.codecimpl.PNGCodec; import com.sun.media.jai.codecimpl.PNMCodec; import com.sun.media.jai.codecimpl.TIFFCodec; import com.sun.media.jai.codecimpl.WBMPCodec; import com.sun.media.jai.codecimpl.ImagingListenerProxy; import com.sun.media.jai.codecimpl.util.FloatDoubleColorModel; import com.sun.media.jai.util.SimpleCMYKColorSpace; /** * An abstract class allowing the creation of image decoders and * encoders. Instances of ImageCodec may be registered. * Once a codec has been registered, the format name associated with * it may be used as the name parameter in the * createImageEncoder() and createImageDecoder() * methods. * *

Additionally, subclasses of ImageCodec * are able to perform recognition of their particular format, * wither by inspection of a fixed-length file header or by * arbitrary access to the source data stream. * *

Format recognition is performed by two variants of the * isFormatRecognized() method. Which variant should be * called is determined by the output of the codec's * getNumHeaderBytes() method, which returns 0 if * arbitrary access to the stream is required, and otherwise returns * the number of header bytes required to recognize the format. * Each subclass of ImageCodec needs to implement only * one of the two variants. * *

This class is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. */ public abstract class ImageCodec { private static Hashtable codecs = new Hashtable(); /** Allow only subclasses to instantiate this class. */ protected ImageCodec() {} /** * Load the JPEG and PNM codecs. */ static { registerCodec(new BMPCodec()); registerCodec(new GIFCodec()); registerCodec(new FPXCodec()); registerCodec(new JPEGCodec()); registerCodec(new PNGCodec()); registerCodec(new PNMCodec()); registerCodec(new TIFFCodec()); registerCodec(new WBMPCodec()); } /** * Returns the ImageCodec associated with the given * name. null is returned if no codec is registered * with the given name. Case is not significant. * * @param name The name associated with the codec. * @return The associated ImageCodec, or null. */ public static ImageCodec getCodec(String name) { return (ImageCodec)codecs.get(name.toLowerCase()); } /** * Associates an ImageCodec with its format name, as * determined by its getFormatName() method. Case is * not significant. Any codec previously associated with the name * is discarded. * * @param codec The ImageCodec object to be registered. */ public static void registerCodec(ImageCodec codec) { codecs.put(codec.getFormatName().toLowerCase(), codec); } /** * Unregisters the ImageCodec object currently * responsible for handling the named format. Case is not * significant. * * @param name The name associated with the codec to be removed. */ public static void unregisterCodec(String name) { codecs.remove(name.toLowerCase()); } /** * Returns an Enumeration of all regstered * ImageCodec objects. */ public static Enumeration getCodecs() { return codecs.elements(); } /** * Returns an ImageEncoder object suitable for * encoding to the supplied OutputStream, using the * supplied ImageEncoderParam object. * * @param name The name associated with the codec. * @param dst An OutputStream to write to. * @param param An instance of ImageEncoderParam suitable * for use with the named codec, or null. * @return An instance of ImageEncoder, or null. */ public static ImageEncoder createImageEncoder(String name, OutputStream dst, ImageEncodeParam param) { ImageCodec codec = getCodec(name); if (codec == null) { return null; } return codec.createImageEncoder(dst, param); } /** * Returns an ImageDecoder object suitable for * decoding from the supplied InputStream, using the * supplied ImageDecodeParam object. * * @param name The name associated with the codec. * @param src An InputStream to read from. * @param param An instance of ImageDecodeParam suitable * for use with the named codec, or null. * @return An instance of ImageDecoder, or null. */ public static ImageDecoder createImageDecoder(String name, InputStream src, ImageDecodeParam param) { ImageCodec codec = getCodec(name); if (codec == null) { return null; } return codec.createImageDecoder(src, param); } /** * Returns an ImageDecoder object suitable for * decoding from the supplied File, using the * supplied ImageDecodeParam object. * * @param name The name associated with the codec. * @param src A File to read from. * @param param An instance of ImageDecodeParam suitable * for use with the named codec, or null. * @return An instance of ImageDecoder, or null. */ public static ImageDecoder createImageDecoder(String name, File src, ImageDecodeParam param) throws IOException { ImageCodec codec = getCodec(name); if (codec == null) { return null; } return codec.createImageDecoder(src, param); } /** * Returns an ImageDecoder object suitable for * decoding from the supplied SeekableStream, using the * supplied ImageDecodeParam object. * * @param name The name associated with the codec. * @param src A SeekableStream to read from. * @param param An instance of ImageDecodeParam suitable * for use with the named codec, or null. * @return An instance of ImageDecoder, or null. */ public static ImageDecoder createImageDecoder(String name, SeekableStream src, ImageDecodeParam param) { ImageCodec codec = getCodec(name); if (codec == null) { return null; } return codec.createImageDecoder(src, param); } private static String[] vectorToStrings(Vector nameVec) { int count = nameVec.size(); String[] names = new String[count]; for (int i = 0; i < count; i++) { names[i] = (String)nameVec.elementAt(i); } return names; } /** * Returns an array of Strings indicating the names * of registered ImageCodecs that may be appropriate * for reading the given SeekableStream. * *

If the src SeekableStream does * not support seeking backwards (that is, its * canSeekBackwards() method returns * false) then only FormatRecognizers * that require only a fixed-length header will be checked. * *

If the src stream does not support seeking * backwards, it must support marking, as determined by its * markSupported() method. * * @param src A SeekableStream which optionally supports * seeking backwards. * @return An array of Strings. * * @throws IllegalArgumentException if src supports * neither seeking backwards nor marking. */ public static String[] getDecoderNames(SeekableStream src) { if (!src.canSeekBackwards() && !src.markSupported()) { throw new IllegalArgumentException(JaiI18N.getString("ImageCodec2")); } Enumeration enumeration = codecs.elements(); Vector nameVec = new Vector(); String opName = null; while (enumeration.hasMoreElements()) { ImageCodec codec = (ImageCodec)enumeration.nextElement(); int bytesNeeded = codec.getNumHeaderBytes(); if ((bytesNeeded == 0) && !src.canSeekBackwards()) { continue; } try { if (bytesNeeded > 0) { src.mark(bytesNeeded); byte[] header = new byte[bytesNeeded]; src.readFully(header); src.reset(); if (codec.isFormatRecognized(header)) { nameVec.add(codec.getFormatName()); } } else { long pointer = src.getFilePointer(); src.seek(0L); if (codec.isFormatRecognized(src)) { nameVec.add(codec.getFormatName()); } src.seek(pointer); } } catch (IOException e) { ImagingListenerProxy.errorOccurred(JaiI18N.getString("ImageCodec3"), e, ImageCodec.class, false); // e.printStackTrace(); } } return vectorToStrings(nameVec); } /** * Returns an array of Strings indicating the names * of registered ImageCodecs that may be appropriate * for writing the given RenderedImage, using the * optional ImageEncodeParam, which may be * null. * * @param im A RenderedImage to be encodec. * @param param An ImageEncodeParam, or null. * @return An array of Strings. */ public static String[] getEncoderNames(RenderedImage im, ImageEncodeParam param) { Enumeration enumeration = codecs.elements(); Vector nameVec = new Vector(); String opName = null; while (enumeration.hasMoreElements()) { ImageCodec codec = (ImageCodec)enumeration.nextElement(); if (codec.canEncodeImage(im, param)) { nameVec.add(codec.getFormatName()); } } return vectorToStrings(nameVec); } /** * Returns the name of this image format. * * @return A String containing the name of the * image format supported by this codec. */ public abstract String getFormatName(); /** * Returns the number of bytes of header needed to recognize the * format, or 0 if an arbitrary number of bytes may be needed. * The default implementation returns 0. * *

The return value must be a constant for all instances of * each particular subclass of ImageCodec. * *

Although it is legal to always return 0, in some cases * processing may be more efficient if the number of bytes needed * is known in advance. */ public int getNumHeaderBytes() { return 0; } /** * Returns true if the format is recognized in the * initial portion of a stream. The header will be passed in as a * byte array of length getNumHeaderBytes(). * This method should be called only if getNumHeaderBytes() * returns a value greater than 0. * *

The default implementation throws an exception to indicate * that it should never be called. * * @param header An array of bytes containing the input * stream header. * @return true if the format is recognized. */ public boolean isFormatRecognized(byte[] header) { throw new RuntimeException(JaiI18N.getString("ImageCodec0")); } /** * Returns true if the format is recognized in the * input data stream. This method should be called only if * getNumHeaderBytesNeeded() returns 0. * *

The source SeekableStream is guaranteed to * support seeking backwards, and should be seeked to 0 prior * to calling this method. * *

The default implementation throws an exception to indicate * that it should never be called. * * @param src A SeekableStream containing the input * data. * @return true if the format is recognized. */ public boolean isFormatRecognized(SeekableStream src) throws IOException { throw new RuntimeException(JaiI18N.getString("ImageCodec1")); } /** * Returns a Class object indicating the proper * subclass of ImageEncodeParam to be used with this * ImageCodec. If encoding is not supported by this * codec, null is returned. If encoding is * supported, but a parameter object is not used during encoding, * Object.class is returned to signal this fact. */ protected abstract Class getEncodeParamClass(); /** * Returns a Class object indicating the proper * subclass of ImageDecodeParam to be used with this * ImageCodec. If encoding is not supported by this * codec, null is returned. If decoding is * supported, but a parameter object is not used during decoding, * Object.class is returned to signal this fact. */ protected abstract Class getDecodeParamClass(); /** * In a concrete subclass of ImageCodec, returns an * implementation of the ImageEncoder interface * appropriate for that codec. * * @param dst An OutputStream to write to. * @param param An instance of ImageEncoderParam * suitable for use with the ImageCodec * subclass, or null. * @return An instance of ImageEncoder. */ protected abstract ImageEncoder createImageEncoder(OutputStream dst, ImageEncodeParam param); /** * Returns true if the given image and encoder param * object are suitable for encoding by this ImageCodec. * For example, some codecs may only deal with images with a certain * number of bands; an attempt to encode an image with an unsupported * number of bands will fail. * * @param im a RenderedImage whose ability to be encoded is to be * determined. * @param param a suitable ImageEncodeParam object, * or null. */ public abstract boolean canEncodeImage(RenderedImage im, ImageEncodeParam param); /** * Returns an implementation of the ImageDecoder * interface appropriate for that codec. Subclasses of * ImageCodec may override this method if they wish * to accept data directly from an InputStream; * otherwise, this method will convert the source into a * backwards-seekable SeekableStream and call the * appropriate version of createImageDecoder for that * data type. * *

Instances of ImageCodec that do not require * the ability to seek backwards in their source * SeekableStream should override this method in * order to avoid the default call to * SeekableStream.wrapInputStream(src, true). * * @param dst An InputStream to read from. * @param param An instance of ImageDecodeParam * suitable for use with the ImageCodec * subclass, or null. * @return An instance of ImageDecoder. */ protected ImageDecoder createImageDecoder(InputStream src, ImageDecodeParam param) { SeekableStream stream = SeekableStream.wrapInputStream(src, true); return createImageDecoder(stream, param); } /** * Returns an implementation of the ImageDecoder * interface appropriate for that codec. Subclasses of * ImageCodec may override this method if they wish * to accept data directly from a File; * otherwise, this method will convert the source into a * SeekableStream and call the appropriate * version of createImageDecoder for that data type. * * @param dst A File to read from. * @param param An instance of ImageDecodeParam * suitable for use with the ImageCodec * subclass, or null. * @return An instance of ImageDecoder. */ protected ImageDecoder createImageDecoder(File src, ImageDecodeParam param) throws IOException { return createImageDecoder(new FileSeekableStream(src), param); } /** * In a concrete subclass of ImageCodec, returns an * implementation of the ImageDecoder interface * appropriate for that codec. * * @param dst A SeekableStream to read from. * @param param An instance of ImageDecodeParam * suitable for use with the ImageCodec * subclass, or null. * @return An instance of ImageDecoder. */ protected abstract ImageDecoder createImageDecoder(SeekableStream src, ImageDecodeParam param); // ColorModel utility functions private static final byte[][] grayIndexCmaps = { null, // 1 bit { (byte)0x00, (byte)0xff }, // 2 bits { (byte)0x00, (byte)0x55, (byte)0xaa, (byte)0xff }, null, // 4 bits { (byte)0x00, (byte)0x11, (byte)0x22, (byte)0x33, (byte)0x44, (byte)0x55, (byte)0x66, (byte)0x77, (byte)0x88, (byte)0x99, (byte)0xaa, (byte)0xbb, (byte)0xcc, (byte)0xdd, (byte)0xee, (byte)0xff } }; /** * A convenience methods to create an instance of * IndexColorModel suitable for the given 1-banded * SampleModel. * * @param sm a 1-banded SampleModel. * @param blackIsZero true if the gray ramp should * go from black to white, falseotherwise. */ public static ColorModel createGrayIndexColorModel(SampleModel sm, boolean blackIsZero) { if (sm.getNumBands() != 1) { throw new IllegalArgumentException(); } int sampleSize = sm.getSampleSize(0); byte[] cmap = null; if (sampleSize < 8) { cmap = grayIndexCmaps[sampleSize]; if (!blackIsZero) { int length = cmap.length; byte[] newCmap = new byte[length]; for (int i = 0; i < length; i++) { newCmap[i] = cmap[length - i - 1]; } cmap = newCmap; } } else { cmap = new byte[256]; if (blackIsZero) { for (int i = 0; i < 256; i++) { cmap[i] = (byte)i; } } else { for (int i = 0; i < 256; i++) { cmap[i] = (byte)(255 - i); } } } return new IndexColorModel(sampleSize, cmap.length, cmap, cmap, cmap); } private static final int[] GrayBits8 = { 8 }; private static final ComponentColorModel colorModelGray8 = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), GrayBits8, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE); private static final int[] GrayAlphaBits8 = { 8, 8 }; private static final ComponentColorModel colorModelGrayAlpha8 = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), GrayAlphaBits8, true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE); private static final int[] GrayBits16 = { 16 }; private static final ComponentColorModel colorModelGray16 = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), GrayBits16, false, false, Transparency.OPAQUE, DataBuffer.TYPE_USHORT); private static final int[] GrayAlphaBits16 = { 16, 16 }; private static final ComponentColorModel colorModelGrayAlpha16 = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), GrayAlphaBits16, true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_USHORT); private static final int[] GrayBits32 = { 32 }; private static final ComponentColorModel colorModelGray32 = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), GrayBits32, false, false, Transparency.OPAQUE, DataBuffer.TYPE_INT); private static final int[] GrayAlphaBits32 = { 32, 32 }; private static final ComponentColorModel colorModelGrayAlpha32 = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), GrayAlphaBits32, true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_INT); private static final int[] RGBBits8 = { 8, 8, 8 }; private static final ComponentColorModel colorModelRGB8 = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), RGBBits8, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE); private static final int[] RGBABits8 = { 8, 8, 8, 8 }; private static final ComponentColorModel colorModelRGBA8 = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), RGBABits8, true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE); private static final int[] RGBBits16 = { 16, 16, 16 }; private static final ComponentColorModel colorModelRGB16 = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), RGBBits16, false, false, Transparency.OPAQUE, DataBuffer.TYPE_USHORT); private static final int[] RGBABits16 = { 16, 16, 16, 16 }; private static final ComponentColorModel colorModelRGBA16 = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), RGBABits16, true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_USHORT); private static final int[] RGBBits32 = { 32, 32, 32 }; private static final ComponentColorModel colorModelRGB32 = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), RGBBits32, false, false, Transparency.OPAQUE, DataBuffer.TYPE_INT); private static final int[] RGBABits32 = { 32, 32, 32, 32 }; private static final ComponentColorModel colorModelRGBA32 = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), RGBABits32, true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_INT); /** * A convenience method to create an instance of * ComponentColorModel suitable for use with the * given SampleModel. The SampleModel * should have a data type of DataBuffer.TYPE_BYTE, * TYPE_USHORT, or TYPE_INT and between * 1 and 4 bands. Depending on the number of bands of the * SampleModel, either a gray, gray+alpha, rgb, or * rgb+alpha ColorModel is returned. */ public static ColorModel createComponentColorModel(SampleModel sm) { int type = sm.getDataType(); int bands = sm.getNumBands(); ComponentColorModel cm = null; if (type == DataBuffer.TYPE_BYTE) { switch (bands) { case 1: cm = colorModelGray8; break; case 2: cm = colorModelGrayAlpha8; break; case 3: cm = colorModelRGB8; break; case 4: cm = colorModelRGBA8; break; } } else if (type == DataBuffer.TYPE_USHORT) { switch (bands) { case 1: cm = colorModelGray16; break; case 2: cm = colorModelGrayAlpha16; break; case 3: cm = colorModelRGB16; break; case 4: cm = colorModelRGBA16; break; } } else if (type == DataBuffer.TYPE_INT) { switch (bands) { case 1: cm = colorModelGray32; break; case 2: cm = colorModelGrayAlpha32; break; case 3: cm = colorModelRGB32; break; case 4: cm = colorModelRGBA32; break; } } else if (type == DataBuffer.TYPE_FLOAT && bands >= 1 && bands <= 4) { ColorSpace cs = bands <= 2 ? ColorSpace.getInstance(ColorSpace.CS_GRAY) : ColorSpace.getInstance(ColorSpace.CS_sRGB); boolean hasAlpha = bands % 2 == 0; cm = new FloatDoubleColorModel(cs, hasAlpha, false, hasAlpha ? Transparency.TRANSLUCENT : Transparency.OPAQUE, DataBuffer.TYPE_FLOAT); } return cm; } /** * A convenience method to create an instance of * ComponentColorModel suitable for use with the * given SampleModel and . The * SampleModel * should have a data type of DataBuffer.TYPE_BYTE, * TYPE_USHORT, or TYPE_INT and between * 1 and 4 bands. Depending on the number of bands of the * SampleModel, either a gray, gray+alpha, rgb, or * rgb+alpha ColorModel is returned. */ public static ColorModel createComponentColorModel(SampleModel sm, ColorSpace cp) { if (cp == null) return createComponentColorModel(sm); int type = sm.getDataType(); int bands = sm.getNumBands(); ComponentColorModel cm = null; int[] bits = null; int transferType = -1; boolean hasAlpha = (bands % 2 == 0); if (cp instanceof SimpleCMYKColorSpace) hasAlpha = false; int transparency = hasAlpha ? Transparency.TRANSLUCENT : Transparency.OPAQUE; if (type == DataBuffer.TYPE_BYTE) { transferType = DataBuffer.TYPE_BYTE; switch (bands) { case 1: bits = GrayBits8; break; case 2: bits = GrayAlphaBits8; break; case 3: bits = RGBBits8; break; case 4: bits = RGBABits8; break; } } else if (type == DataBuffer.TYPE_USHORT) { transferType = DataBuffer.TYPE_USHORT; switch (bands) { case 1: bits = GrayBits16; break; case 2: bits = GrayAlphaBits16; break; case 3: bits = RGBBits16; break; case 4: bits = RGBABits16; break; } } else if (type == DataBuffer.TYPE_INT) { transferType = DataBuffer.TYPE_INT; switch (bands) { case 1: bits = GrayBits32; break; case 2: bits = GrayAlphaBits32; break; case 3: bits = RGBBits32; break; case 4: bits = RGBABits32; break; } } if (type == DataBuffer.TYPE_FLOAT && bands >= 1 && bands <= 4) { cm = new FloatDoubleColorModel(cp, hasAlpha, false, transparency, DataBuffer.TYPE_FLOAT); } else { cm = new ComponentColorModel(cp, bits, hasAlpha, false, transparency, transferType); } return cm; } /** * Tests whether the color indices represent a gray-scale image. * * @param r The red channel color indices. * @param g The green channel color indices. * @param b The blue channel color indices. * @return If all the indices have 256 entries, and are identical mappings, * return true; otherwise, return false. */ public static boolean isIndicesForGrayscale(byte[] r, byte[] g, byte[] b) { if (r.length != g.length || r.length != b.length) return false; int size = r.length; if (size != 256) return false; for (int i = 0; i < size; i++) { byte temp = (byte) i; if (r[i] != temp || g[i] != temp || b[i] != temp) return false; } return true; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/com.sun.media.jai.codec.properties0000644000175000017500000000531310203035544031262 0ustar mathieumathieu# # $RCSfile: com.sun.media.jai.codec.properties,v $ # # Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. # # Use is subject to license terms. # # $Revision: 1.1 $ # $Date: 2005-02-11 04:55:34 $ # $State: Exp $ # BMPEncodeParam0=Unsupported version number specified for BMP file. FileSeekableStream0=pos < 0. FileCacheSeekableStream0=pos < 0. ImageCodec0=Method unimplemented, should be implemented by subclass. ImageCodec1=Method unimplemented, should be implemented by subclass. ImageCodec2=src must support seeking backwards or marking. ImageCodec3=IOException occurs when search for propriate codecs. JPEGEncodeParam0=A quantization table has not been set for this component. MemoryCacheSeekableStream0=pos < 0. PNGDecodeParam0=User exponent must not be negative. PNGDecodeParam1=Display exponent must not be negative. PNGEncodeParam0=Bad palette length. PNGEncodeParam1=Not divisible by 3. PNGEncodeParam2=Bit depth not equal to 1, 2, 4, or 8. PNGEncodeParam3=RGB palette has not been set. PNGEncodeParam4=background palette index has not been set. PNGEncodeParam5=Palette transparency has not been set. PNGEncodeParam6=Background gray level has not been set. PNGEncodeParam7=Transparent gray value has not been set. PNGEncodeParam8=Bit shift has not been set. PNGEncodeParam9=RGB background color has not been set. PNGEncodeParam10=Transparent RGB value has not been set. PNGEncodeParam11=Grayscale bit depth has not been set. PNGEncodeParam12=Chromaticity has not been set. PNGEncodeParam13=Gamma has not been set. PNGEncodeParam14=Palette histogram has not been set. PNGEncodeParam15=ICC profile has not been set. PNGEncodeParam16=Physical dimension information has not been set. PNGEncodeParam17=Suggested palette information has not been set. PNGEncodeParam18=Significant bits values have not been set. PNGEncodeParam19=sRGB rendereding intent has not been set. PNGEncodeParam20=Uncompressed text strings have not been set. PNGEncodeParam21=Modification time has not been set. PNGEncodeParam22=Compressed text strings have not been set. PNGEncodeParam23='unsetBackground' not implemented by the superclass 'PNGEncodeParam'. PNGEncodeParam24='isBackgroundSet' not implemented by the superclass 'PNGEncodeParam'. SeekableOutputStream0=The constructor RandomAccessFile parameter cannot be null. SegmentedSeekableStream0=Source stream does not support seeking backwards. TIFFDirectory0=Unsupported TIFFField tag. TIFFDirectory1=Bad endianness tag (not 0x4949 or 0x4d4d). TIFFDirectory2=Bad magic number, should be 42. TIFFDirectory3=Directory number too large. TIFFDirectory4=- Ignoring this tag due to invalid data type. TIFFEncodeParam0=Unsupported compression scheme specified. TIFFEncodeParam1=Illegal DEFLATE compression level specified. jai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/ForwardSeekableStream.java0000644000175000017500000000650310203035544027747 0ustar mathieumathieu/* * $RCSfile: ForwardSeekableStream.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:30 $ * $State: Exp $ */ package com.sun.media.jai.codec; import java.io.InputStream; import java.io.IOException; /** * A subclass of SeekableStream that may be used * to wrap a regular InputStream efficiently. * Seeking backwards is not supported. * *

This class is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. */ public class ForwardSeekableStream extends SeekableStream { /** The source InputStream. */ private InputStream src; /** The current position. */ long pointer = 0L; /** The marked position. */ long markPos = -1L; /** * Constructs a InputStreamForwardSeekableStream from a * regular InputStream. */ public ForwardSeekableStream(InputStream src) { this.src = src; } /** Forwards the request to the real InputStream. */ public final int read() throws IOException { int result = src.read(); if (result != -1) { ++pointer; } return result; } /** Forwards the request to the real InputStream. */ public final int read(byte[] b, int off, int len) throws IOException { int result = src.read(b, off, len); if (result != -1) { pointer += result; } return result; } /** Forwards the request to the real InputStream. */ public final long skip(long n) throws IOException { long skipped = src.skip(n); pointer += skipped; return skipped; } /** Forwards the request to the real InputStream. */ public final int available() throws IOException { return src.available(); } /** Forwards the request to the real InputStream. */ public final void close() throws IOException { src.close(); } /** Forwards the request to the real InputStream. */ public synchronized final void mark(int readLimit) { markPos = pointer; src.mark(readLimit); } /** Forwards the request to the real InputStream. */ public synchronized final void reset() throws IOException { if (markPos != -1) { pointer = markPos; } src.reset(); } /** Forwards the request to the real InputStream. */ public boolean markSupported() { return src.markSupported(); } /** Returns false since seking backwards is not supported. */ public final boolean canSeekBackwards() { return false; } /** Returns the current position in the stream (bytes read). */ public final long getFilePointer() { return (long)pointer; } /** * Seeks forward to the given position in the stream. * If pos is smaller than the current position * as returned by getFilePointer(), nothing * happens. */ public final void seek(long pos) throws IOException { while (pos - pointer > 0) { pointer += src.skip(pos - pointer); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/ImageEncoderImpl.java0000644000175000017500000000537210203035544026702 0ustar mathieumathieu/* * $RCSfile: ImageEncoderImpl.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:31 $ * $State: Exp $ */ package com.sun.media.jai.codec; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.io.IOException; import java.io.OutputStream; import com.sun.media.jai.codecimpl.SingleTileRenderedImage; /** * A partial implementation of the ImageEncoder interface useful for * subclassing. * *

This class is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. */ public abstract class ImageEncoderImpl implements ImageEncoder { /** The OutputStream associcted with this ImageEncoder. */ protected OutputStream output; /** The ImageEncodeParam object associcted with this ImageEncoder. */ protected ImageEncodeParam param; /** * Constructs an ImageEncoderImpl with a given OutputStream * and ImageEncoderParam instance. */ public ImageEncoderImpl(OutputStream output, ImageEncodeParam param) { this.output = output; this.param = param; } /** * Returns the current parameters as an instance of the * ImageEncodeParam interface. Concrete implementations of this * interface will return corresponding concrete implementations of * the ImageEncodeParam interface. For example, a JPEGImageEncoder * will return an instance of JPEGEncodeParam. */ public ImageEncodeParam getParam() { return param; } /** * Sets the current parameters to an instance of the * ImageEncodeParam interface. Concrete implementations * of ImageEncoder may throw a RuntimeException if the * params argument is not an instance of the appropriate * subclass or subinterface. For example, a JPEGImageEncoder * will expect param to be an instance of JPEGEncodeParam. */ public void setParam(ImageEncodeParam param) { this.param = param; } /** Returns the OutputStream associated with this ImageEncoder. */ public OutputStream getOutputStream() { return output; } /** * Encodes a Raster with a given ColorModel and writes the output * to the OutputStream associated with this ImageEncoder. */ public void encode(Raster ras, ColorModel cm) throws IOException { RenderedImage im = new SingleTileRenderedImage(ras, cm); encode(im); } /** * Encodes a RenderedImage and writes the output to the * OutputStream associated with this ImageEncoder. */ public abstract void encode(RenderedImage im) throws IOException; } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/FileCacheSeekableStream.java0000644000175000017500000002653010203035544030150 0ustar mathieumathieu/* * $RCSfile: FileCacheSeekableStream.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:29 $ * $State: Exp $ */ package com.sun.media.jai.codec; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.IOException; import java.io.RandomAccessFile; import java.lang.reflect.Method; import java.util.HashSet; import java.util.Iterator; /** * A subclass of SeekableStream that may be used to wrap * a regular InputStream. Seeking backwards is supported * by means of a file cache. In circumstances that do not allow the * creation of a temporary file (for example, due to security * consideration or the absence of local disk), the * MemoryCacheSeekableStream class may be used instead. * *

The mark() and reset() methods are * supported. * *

This class is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. */ public final class FileCacheSeekableStream extends SeekableStream { /** A thread to clean up all temporary files on VM exit (VM 1.3+) */ private static TempFileCleanupThread cleanupThread = null; /** The source stream. */ private InputStream stream; /** The cache File. */ private File cacheFile; /** The cache as a RandomAcessFile. */ private RandomAccessFile cache; /** The length of the read buffer. */ private int bufLen = 1024; /** The read buffer. */ private byte[] buf = new byte[bufLen]; /** Number of bytes in the cache. */ private long length = 0; /** Next byte to be read. */ private long pointer = 0; /** True if we've encountered the end of the source stream. */ private boolean foundEOF = false; // Create the cleanup thread. Use reflection to preserve compile-time // compatibility with JDK 1.2. static { try { Method shutdownMethod = Runtime.class.getDeclaredMethod("addShutdownHook", new Class[] {Thread.class}); cleanupThread = new TempFileCleanupThread(); shutdownMethod.invoke(Runtime.getRuntime(), new Object[] {cleanupThread}); } catch(Exception e) { // Reset the Thread to null if Method.invoke failed. cleanupThread = null; } } /** * Constructs a MemoryCacheSeekableStream that takes * its source data from a regular InputStream. * Seeking backwards is supported by means of an file cache. * *

An IOException will be thrown if the * attempt to create the cache file fails for any reason. */ public FileCacheSeekableStream(InputStream stream) throws IOException { this.stream = stream; this.cacheFile = File.createTempFile("jai-FCSS-", ".tmp"); cacheFile.deleteOnExit(); this.cache = new RandomAccessFile(cacheFile, "rw"); // Add cache file to cleanup thread for deletion at VM shutdown. if(cleanupThread != null) { cleanupThread.addFile(this.cacheFile); } } /** * Ensures that at least pos bytes are cached, * or the end of the source is reached. The return value * is equal to the smaller of pos and the * length of the source file. */ private long readUntil(long pos) throws IOException { // We've already got enough data cached if (pos < length) { return pos; } // pos >= length but length isn't getting any bigger, so return it if (foundEOF) { return length; } long len = pos - length; cache.seek(length); while (len > 0) { // Copy a buffer's worth of data from the source to the cache // bufLen will always fit into an int so this is safe int nbytes = stream.read(buf, 0, (int)Math.min(len, (long)bufLen)); if (nbytes == -1) { foundEOF = true; return length; } cache.setLength(cache.length() + nbytes); cache.write(buf, 0, nbytes); len -= nbytes; length += nbytes; } return pos; } /** * Returns true since all * FileCacheSeekableStream instances support seeking * backwards. */ public boolean canSeekBackwards() { return true; } /** * Returns the current offset in this file. * * @return the offset from the beginning of the file, in bytes, * at which the next read occurs. */ public long getFilePointer() { return pointer; } /** * Sets the file-pointer offset, measured from the beginning of this * file, at which the next read occurs. * * @param pos the offset position, measured in bytes from the * beginning of the file, at which to set the file * pointer. * @exception IOException if pos is less than * 0 or if an I/O error occurs. */ public void seek(long pos) throws IOException { if (pos < 0) { throw new IOException(JaiI18N.getString("FileCacheSeekableStream0")); } pointer = pos; } /** * Reads the next byte of data from the input stream. The value byte is * returned as an int in the range 0 to * 255. If no byte is available because the end of the stream * has been reached, the value -1 is returned. This method * blocks until input data is available, the end of the stream is detected, * or an exception is thrown. * * @return the next byte of data, or -1 if the end of the * stream is reached. * @exception IOException if an I/O error occurs. */ public int read() throws IOException { long next = pointer + 1; long pos = readUntil(next); if (pos >= next) { cache.seek(pointer++); return cache.read(); } else { return -1; } } /** * Reads up to len bytes of data from the input stream into * an array of bytes. An attempt is made to read as many as * len bytes, but a smaller number may be read, possibly * zero. The number of bytes actually read is returned as an integer. * *

This method blocks until input data is available, end of file is * detected, or an exception is thrown. * *

If b is null, a * NullPointerException is thrown. * *

If off is negative, or len is negative, or * off+len is greater than the length of the array * b, then an IndexOutOfBoundsException is * thrown. * *

If len is zero, then no bytes are read and * 0 is returned; otherwise, there is an attempt to read at * least one byte. If no byte is available because the stream is at end of * file, the value -1 is returned; otherwise, at least one * byte is read and stored into b. * *

The first byte read is stored into element b[off], the * next one into b[off+1], and so on. The number of bytes read * is, at most, equal to len. Let k be the number of * bytes actually read; these bytes will be stored in elements * b[off] through b[off+k-1], * leaving elements b[off+k] through * b[off+len-1] unaffected. * *

In every case, elements b[0] through * b[off] and elements b[off+len] through * b[b.length-1] are unaffected. * *

If the first byte cannot be read for any reason other than end of * file, then an IOException is thrown. In particular, an * IOException is thrown if the input stream has been closed. * * @param b the buffer into which the data is read. * @param off the start offset in array b * at which the data is written. * @param len the maximum number of bytes to read. * @return the total number of bytes read into the buffer, or * -1 if there is no more data because the end of * the stream has been reached. * @exception IOException if an I/O error occurs. */ public int read(byte[] b, int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); } if ((off < 0) || (len < 0) || (off + len > b.length)) { throw new IndexOutOfBoundsException(); } if (len == 0) { return 0; } long pos = readUntil(pointer + len); // len will always fit into an int so this is safe len = (int)Math.min((long)len, pos - pointer); if (len > 0) { cache.seek(pointer); cache.readFully(b, off, len); pointer += len; return len; } else { return -1; } } /** * Closes this stream and releases any system resources * associated with the stream. * * @throws IOException if an I/O error occurs. */ public void close() throws IOException { super.close(); cache.close(); cacheFile.delete(); // Remove cache file from list of files to delete at VM shutdown. if(cleanupThread != null) { cleanupThread.removeFile(this.cacheFile); } } } /** * A singleton Thread passed to * Runtime.addShutdownHook() which removes any still extant * files created by File.createTempFile() in the * FileCacheSeekableStream constructor. */ class TempFileCleanupThread extends Thread { /** * A Set of temporary Files. */ private HashSet tempFiles = null; TempFileCleanupThread() { super(); setPriority(MIN_PRIORITY); } /** * Deletes all Files in the internal cache. */ public void run() { if(tempFiles != null && tempFiles.size() > 0) { Iterator fileIter = tempFiles.iterator(); while(fileIter.hasNext()) { try { File file = (File)fileIter.next(); file.delete(); } catch(Exception e) { // Ignore } } } } /** * Add a file to be deleted at shutdown. */ synchronized void addFile(File file) { if(tempFiles == null) { tempFiles = new HashSet(); } tempFiles.add(file); } /** * Remove a file to be deleted at shutdown. */ synchronized void removeFile(File file) { tempFiles.remove(file); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/PNGEncodeParam.java0000644000175000017500000013632010203035544026257 0ustar mathieumathieu/* * $RCSfile: PNGEncodeParam.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:32 $ * $State: Exp $ */ package com.sun.media.jai.codec; import java.awt.image.ColorModel; import java.awt.image.IndexColorModel; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.color.ICC_Profile; import java.awt.color.ICC_ProfileGray; import java.awt.color.ICC_ProfileRGB; import java.util.Date; import java.util.Vector; /** * An instance of ImageEncodeParam for encoding images in * the PNG format. * *

This class is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. */ public abstract class PNGEncodeParam implements ImageEncodeParam { /** Constant for use with the sRGB chunk. */ public static final int INTENT_PERCEPTUAL = 0; /** Constant for use with the sRGB chunk. */ public static final int INTENT_RELATIVE = 1; /** Constant for use with the sRGB chunk. */ public static final int INTENT_SATURATION = 2; /** Constant for use with the sRGB chunk. */ public static final int INTENT_ABSOLUTE = 3; /** Constant for use in filtering. */ public static final int PNG_FILTER_NONE = 0; /** Constant for use in filtering. */ public static final int PNG_FILTER_SUB = 1; /** Constant for use in filtering. */ public static final int PNG_FILTER_UP = 2; /** Constant for use in filtering. */ public static final int PNG_FILTER_AVERAGE = 3; /** Constant for use in filtering. */ public static final int PNG_FILTER_PAETH = 4; /** * Returns an instance of PNGEncodeParam.Palette, * PNGEncodeParam.Gray, or * PNGEncodeParam.RGB appropriate for encoding * the given image. * *

If the image has an IndexColorModel, an * instance of PNGEncodeParam.Palette is returned. * Otherwise, if the image has 1 or 2 bands an instance of * PNGEncodeParam.Gray is returned. In all other * cases an instance of PNGEncodeParam.RGB is * returned. * *

Note that this method does not provide any guarantee that * the given image will be successfully encoded by the PNG * encoder, as it only performs a very superficial analysis of * the image structure. */ public static PNGEncodeParam getDefaultEncodeParam(RenderedImage im) { ColorModel colorModel = im.getColorModel(); if (colorModel instanceof IndexColorModel) { return new PNGEncodeParam.Palette(); } SampleModel sampleModel = im.getSampleModel(); int numBands = sampleModel.getNumBands(); if (numBands == 1 || numBands == 2) { return new PNGEncodeParam.Gray(); } else { return new PNGEncodeParam.RGB(); } } public static class Palette extends PNGEncodeParam { /** Constructs an instance of PNGEncodeParam.Palette. */ public Palette() {} // bKGD chunk private boolean backgroundSet = false; /** * Suppresses the 'bKGD' chunk from being output. */ public void unsetBackground() { backgroundSet = false; } /** * Returns true if a 'bKGD' chunk will be output. */ public boolean isBackgroundSet() { return backgroundSet; } /** * Sets the desired bit depth for a palette image. The bit * depth must be one of 1, 2, 4, or 8, or else an * IllegalArgumentException will be thrown. */ public void setBitDepth(int bitDepth) { if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4 && bitDepth != 8) { throw new IllegalArgumentException(JaiI18N.getString("PNGEncodeParam2")); } this.bitDepth = bitDepth; bitDepthSet = true; } // PLTE chunk private int[] palette = null; private boolean paletteSet = false; /** * Sets the RGB palette of the image to be encoded. * The rgb parameter contains alternating * R, G, B values for each color index used in the image. * The number of elements must be a multiple of 3 between * 3 and 3*256. * *

The 'PLTE' chunk will encode this information. * * @param rgb An array of ints. */ public void setPalette(int[] rgb) { if (rgb.length < 1*3 || rgb.length > 256*3) { throw new IllegalArgumentException(JaiI18N.getString("PNGEncodeParam0")); } if ((rgb.length % 3) != 0) { throw new IllegalArgumentException(JaiI18N.getString("PNGEncodeParam1")); } palette = (int[])(rgb.clone()); paletteSet = true; } /** * Returns the current RGB palette. * *

If the palette has not previously been set, or has been * unset, an IllegalStateException will be thrown. * * @throws IllegalStateException if the palette is not set. * * @return An array of ints. */ public int[] getPalette() { if (!paletteSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam3")); } return (int[])(palette.clone()); } /** * Suppresses the 'PLTE' chunk from being output. */ public void unsetPalette() { palette = null; paletteSet = false; } /** * Returns true if a 'PLTE' chunk will be output. */ public boolean isPaletteSet() { return paletteSet; } // bKGD chunk private int backgroundPaletteIndex; /** * Sets the palette index of the suggested background color. * *

The 'bKGD' chunk will encode this information. */ public void setBackgroundPaletteIndex(int index) { backgroundPaletteIndex = index; backgroundSet = true; } /** * Returns the palette index of the suggested background color. * *

If the background palette index has not previously been * set, or has been unset, an * IllegalStateException will be thrown. * * @throws IllegalStateException if the palette index is not set. */ public int getBackgroundPaletteIndex() { if (!backgroundSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam4")); } return backgroundPaletteIndex; } // tRNS chunk private int[] transparency; /** * Sets the alpha values associated with each palette entry. * The alpha parameter should have as many entries * as there are RGB triples in the palette. * *

The 'tRNS' chunk will encode this information. */ public void setPaletteTransparency(byte[] alpha) { transparency = new int[alpha.length]; for (int i = 0; i < alpha.length; i++) { transparency[i] = alpha[i] & 0xff; } transparencySet = true; } /** * Returns the alpha values associated with each palette entry. * *

If the palette transparency has not previously been * set, or has been unset, an * IllegalStateException will be thrown. * * @throws IllegalStateException if the palette transparency is * not set. */ public byte[] getPaletteTransparency() { if (!transparencySet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam5")); } byte[] alpha = new byte[transparency.length]; for (int i = 0; i < alpha.length; i++) { alpha[i] = (byte)transparency[i]; } return alpha; } } public static class Gray extends PNGEncodeParam { /** Constructs an instance of PNGEncodeParam.Gray. */ public Gray() {} // bKGD chunk private boolean backgroundSet = false; /** * Suppresses the 'bKGD' chunk from being output. */ public void unsetBackground() { backgroundSet = false; } /** * Returns true if a 'bKGD' chunk will be output. */ public boolean isBackgroundSet() { return backgroundSet; } /** * Sets the desired bit depth for a grayscale image. The bit * depth must be one of 1, 2, 4, 8, or 16. * *

When encoding a source image of a greater bit depth, * pixel values will be clamped to the smaller range after * shifting by the value given by getBitShift(). * When encoding a source image of a smaller bit depth, pixel * values will be shifted and left-filled with zeroes. */ public void setBitDepth(int bitDepth) { if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4 && bitDepth != 8 && bitDepth != 16) { throw new IllegalArgumentException(); } this.bitDepth = bitDepth; bitDepthSet = true; } // bKGD chunk private int backgroundPaletteGray; /** * Sets the suggested gray level of the background. * *

The 'bKGD' chunk will encode this information. */ public void setBackgroundGray(int gray) { backgroundPaletteGray = gray; backgroundSet = true; } /** * Returns the suggested gray level of the background. * *

If the background gray level has not previously been * set, or has been unset, an * IllegalStateException will be thrown. * * @throws IllegalStateException if the background gray level * is not set. */ public int getBackgroundGray() { if (!backgroundSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam6")); } return backgroundPaletteGray; } // tRNS chunk private int[] transparency; /** * Sets the gray value to be used to denote transparency. * *

Setting this attribute will cause the alpha channel * of the input image to be ignored. * *

The 'tRNS' chunk will encode this information. */ public void setTransparentGray(int transparentGray) { transparency = new int[1]; transparency[0] = transparentGray; transparencySet = true; } /** * Returns the gray value to be used to denote transparency. * *

If the transparent gray value has not previously been * set, or has been unset, an * IllegalStateException will be thrown. * * @throws IllegalStateException if the transparent gray value * is not set. */ public int getTransparentGray() { if (!transparencySet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam7")); } int gray = transparency[0]; return gray; } private int bitShift; private boolean bitShiftSet = false; /** * Sets the desired bit shift for a grayscale image. * Pixels in the source image will be shifted right by * the given amount prior to being clamped to the maximum * value given by the encoded image's bit depth. */ public void setBitShift(int bitShift) { if (bitShift < 0) { throw new RuntimeException(); } this.bitShift = bitShift; bitShiftSet = true; } /** * Returns the desired bit shift for a grayscale image. * *

If the bit shift has not previously been set, or has been * unset, an IllegalStateException will be thrown. * * @throws IllegalStateException if the bit shift is not set. */ public int getBitShift() { if (!bitShiftSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam8")); } return bitShift; } /** * Suppresses the setting of the bit shift of a grayscale image. * Pixels in the source image will not be shifted prior to encoding. */ public void unsetBitShift() { bitShiftSet = false; } /** * Returns true if the bit shift has been set. */ public boolean isBitShiftSet() { return bitShiftSet; } /** * Returns true if the bit depth has been set. */ public boolean isBitDepthSet() { return bitDepthSet; } } public static class RGB extends PNGEncodeParam { /** Constructs an instance of PNGEncodeParam.RGB. */ public RGB() {} // bKGD chunk private boolean backgroundSet = false; /** * Suppresses the 'bKGD' chunk from being output. */ public void unsetBackground() { backgroundSet = false; } /** * Returns true if a 'bKGD' chunk will be output. */ public boolean isBackgroundSet() { return backgroundSet; } /** * Sets the desired bit depth for an RGB image. The bit * depth must be 8 or 16. */ public void setBitDepth(int bitDepth) { if (bitDepth != 8 && bitDepth != 16) { throw new RuntimeException(); } this.bitDepth = bitDepth; bitDepthSet = true; } // bKGD chunk private int[] backgroundRGB; /** * Sets the RGB value of the suggested background color. * The rgb parameter should have 3 entries. * *

The 'bKGD' chunk will encode this information. */ public void setBackgroundRGB(int[] rgb) { if (rgb.length != 3) { throw new RuntimeException(); } backgroundRGB = rgb; backgroundSet = true; } /** * Returns the RGB value of the suggested background color. * *

If the background color has not previously been set, or has been * unset, an IllegalStateException will be thrown. * * @throws IllegalStateException if the background color is not set. */ public int[] getBackgroundRGB() { if (!backgroundSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam9")); } return backgroundRGB; } // tRNS chunk private int[] transparency; /** * Sets the RGB value to be used to denote transparency. * *

Setting this attribute will cause the alpha channel * of the input image to be ignored. * *

The 'tRNS' chunk will encode this information. */ public void setTransparentRGB(int[] transparentRGB) { transparency = (int[])(transparentRGB.clone()); transparencySet = true; } /** * Returns the RGB value to be used to denote transparency. * *

If the transparent color has not previously been set, * or has been unset, an IllegalStateException * will be thrown. * * @throws IllegalStateException if the transparent color is not set. */ public int[] getTransparentRGB() { if (!transparencySet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam10")); } return (int[])(transparency.clone()); } } protected int bitDepth; protected boolean bitDepthSet = false; /** * Sets the desired bit depth of an image. */ public abstract void setBitDepth(int bitDepth); /** * Returns the desired bit depth for a grayscale image. * *

If the bit depth has not previously been set, or has been * unset, an IllegalStateException will be thrown. * * @throws IllegalStateException if the bit depth is not set. */ public int getBitDepth() { if (!bitDepthSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam11")); } return bitDepth; } /** * Suppresses the setting of the bit depth of a grayscale image. * The depth of the encoded image will be inferred from the source * image bit depth, rounded up to the next power of 2 between 1 * and 16. */ public void unsetBitDepth() { bitDepthSet = false; } private boolean useInterlacing = false; /** * Turns Adam7 interlacing on or off. */ public void setInterlacing(boolean useInterlacing) { this.useInterlacing = useInterlacing; } /** * Returns true if Adam7 interlacing will be used. */ public boolean getInterlacing() { return useInterlacing; } // bKGD chunk - delegate to subclasses // In JAI 1.0, 'backgroundSet' was private. The JDK 1.2 compiler // was lenient and incorrectly allowed this variable to be // accessed from the subclasses. The JDK 1.3 compiler correctly // flags this as a use of a non-static variable in a static // context. Changing 'backgroundSet' to protected would have // solved the problem, but would have introduced a visible API // change. Thus we are forced to adopt the solution of placing a // separate private variable in each subclass and providing // separate implementations of 'unsetBackground' and // 'isBackgroundSet' in each concrete subclass. /** * Suppresses the 'bKGD' chunk from being output. * For API compatibility with JAI 1.0, the superclass * defines this method to throw a RuntimeException; * accordingly, subclasses must provide their own implementations. */ public void unsetBackground() { throw new RuntimeException(JaiI18N.getString("PNGEncodeParam23")); } /** * Returns true if a 'bKGD' chunk will be output. * For API compatibility with JAI 1.0, the superclass * defines this method to throw a RuntimeException; * accordingly, subclasses must provide their own implementations. */ public boolean isBackgroundSet() { throw new RuntimeException(JaiI18N.getString("PNGEncodeParam24")); } // cHRM chunk private float[] chromaticity = null; private boolean chromaticitySet = false; /** * Sets the white point and primary chromaticities in CIE (x, y) * space. * *

The chromaticity parameter should be a * float array of length 8 containing the white point * X and Y, red X and Y, green X and Y, and blue X and Y values in * order. * *

The 'cHRM' chunk will encode this information. */ public void setChromaticity(float[] chromaticity) { if (chromaticity.length != 8) { throw new IllegalArgumentException(); } this.chromaticity = (float[])(chromaticity.clone()); chromaticitySet = true; } /** * A convenience method that calls the array version. */ public void setChromaticity(float whitePointX, float whitePointY, float redX, float redY, float greenX, float greenY, float blueX, float blueY) { float[] chroma = new float[8]; chroma[0] = whitePointX; chroma[1] = whitePointY; chroma[2] = redX; chroma[3] = redY; chroma[4] = greenX; chroma[5] = greenY; chroma[6] = blueX; chroma[7] = blueY; setChromaticity(chroma); } /** * Returns the white point and primary chromaticities in * CIE (x, y) space. * *

See the documentation for the setChromaticity * method for the format of the returned data. * *

If the chromaticity has not previously been set, or has been * unset, an IllegalStateException will be thrown. * * @throws IllegalStateException if the chromaticity is not set. */ public float[] getChromaticity() { if (!chromaticitySet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam12")); } return (float[])(chromaticity.clone()); } /** * Suppresses the 'cHRM' chunk from being output. */ public void unsetChromaticity() { chromaticity = null; chromaticitySet = false; } /** * Returns true if a 'cHRM' chunk will be output. */ public boolean isChromaticitySet() { return chromaticitySet; } // gAMA chunk private float gamma; private boolean gammaSet = false; /** * Sets the file gamma value for the image. * *

The 'gAMA' chunk will encode this information. */ public void setGamma(float gamma) { this.gamma = gamma; gammaSet = true; } /** * Returns the file gamma value for the image. * *

If the file gamma has not previously been set, or has been * unset, an IllegalStateException will be thrown. * * @throws IllegalStateException if the gamma is not set. */ public float getGamma() { if (!gammaSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam13")); } return gamma; } /** * Suppresses the 'gAMA' chunk from being output. */ public void unsetGamma() { gammaSet = false; } /** * Returns true if a 'gAMA' chunk will be output. */ public boolean isGammaSet() { return gammaSet; } // hIST chunk private int[] paletteHistogram = null; private boolean paletteHistogramSet = false; /** * Sets the palette histogram to be stored with this image. * The histogram consists of an array of integers, one per * palette entry. * *

The 'hIST' chunk will encode this information. */ public void setPaletteHistogram(int[] paletteHistogram) { this.paletteHistogram = (int[])(paletteHistogram.clone()); paletteHistogramSet = true; } /** * Returns the palette histogram to be stored with this image. * *

If the histogram has not previously been set, or has been * unset, an IllegalStateException will be thrown. * * @throws IllegalStateException if the histogram is not set. */ public int[] getPaletteHistogram() { if (!paletteHistogramSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam14")); } return paletteHistogram; } /** * Suppresses the 'hIST' chunk from being output. */ public void unsetPaletteHistogram() { paletteHistogram = null; paletteHistogramSet = false; } /** * Returns true if a 'hIST' chunk will be output. */ public boolean isPaletteHistogramSet() { return paletteHistogramSet; } // iCCP chunk private byte[] ICCProfileData = null; private boolean ICCProfileDataSet = false; private String ICCProfileName = null; /** * Sets the ICC profile data to be stored with this image. * The profile is represented in raw binary form. * *

The 'iCCP' chunk will encode this information. */ public void setICCProfileData(byte[] ICCProfileData) { this.ICCProfileData = (byte[])(ICCProfileData.clone()); ICCProfileDataSet = true; ICC_Profile profile = ICC_Profile.getInstance(this.ICCProfileData); if (!(profile instanceof ICC_ProfileRGB || profile instanceof ICC_ProfileGray)) return; //Set gamma try { if (profile instanceof ICC_ProfileRGB) setGamma(((ICC_ProfileRGB)profile).getGamma(ICC_ProfileRGB.REDCOMPONENT)); else if (profile instanceof ICC_ProfileGray) setGamma(((ICC_ProfileGray)profile).getGamma()); } catch(Exception e) { //Gamma is not defined. TRC is defined. So ignore. } if (profile instanceof ICC_ProfileGray) return; // Set cHRM float[] chrom = new float[8]; float[] whitePoint = ((ICC_ProfileRGB)profile).getMediaWhitePoint(); if (whitePoint == null) return; float sum = whitePoint[0] + whitePoint[1] + whitePoint[2]; chrom[0] = whitePoint[0] / sum; chrom[1] = whitePoint[1] / sum; float[][] temp = ((ICC_ProfileRGB)profile).getMatrix(); if (temp == null) return; for (int i = 0; i < 3; i++) { sum = temp[0][i] + temp[1][i] + temp[2][i]; chrom[2 + (i << 1 )] = temp[0][i] / sum; chrom[3 + (i << 1)] = temp[1][i] / sum; } setChromaticity(chrom); } /** * Returns the ICC profile data to be stored with this image. * *

If the ICC profile has not previously been set, or has been * unset, an IllegalStateException will be thrown. * * @throws IllegalStateException if the ICC profile is not set. */ public byte[] getICCProfileData() { if (!ICCProfileDataSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam15")); } return (byte[])(ICCProfileData.clone()); } /** * Suppresses the 'iCCP' chunk from being output. */ public void unsetICCProfileData() { ICCProfileData = null; ICCProfileDataSet = false; ICCProfileName = null; } /** * Sets the ICC profile name. */ public void setICCProfileName(String name) { if (!ICCProfileDataSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam15")); } this.ICCProfileName = name; } /** Returns the ICC profile name. */ public String getICCProfileName() { if (!ICCProfileDataSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam15")); } return ICCProfileName; } /** * Returns true if a 'iCCP' chunk will be output. */ public boolean isICCProfileDataSet() { return ICCProfileDataSet; } // pHYS chunk private int[] physicalDimension = null; private boolean physicalDimensionSet = false; /** * Sets the physical dimension information to be stored with this * image. The physicalDimension parameter should be a 3-entry * array containing the number of pixels per unit in the X * direction, the number of pixels per unit in the Y direction, * and the unit specifier (0 = unknown, 1 = meters). * *

The 'pHYS' chunk will encode this information. */ public void setPhysicalDimension(int[] physicalDimension) { this.physicalDimension = (int[])(physicalDimension.clone()); physicalDimensionSet = true; } /** * A convenience method that calls the array version. */ public void setPhysicalDimension(int xPixelsPerUnit, int yPixelsPerUnit, int unitSpecifier) { int[] pd = new int[3]; pd[0] = xPixelsPerUnit; pd[1] = yPixelsPerUnit; pd[2] = unitSpecifier; setPhysicalDimension(pd); } /** * Returns the physical dimension information to be stored * with this image. * *

If the physical dimension information has not previously * been set, or has been unset, an * IllegalStateException will be thrown. * * @throws IllegalStateException if the physical dimension information * is not set. */ public int[] getPhysicalDimension() { if (!physicalDimensionSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam16")); } return (int[])(physicalDimension.clone()); } /** * Suppresses the 'pHYS' chunk from being output. */ public void unsetPhysicalDimension() { physicalDimension = null; physicalDimensionSet = false; } /** * Returns true if a 'pHYS' chunk will be output. */ public boolean isPhysicalDimensionSet() { return physicalDimensionSet; } // sPLT chunk private PNGSuggestedPaletteEntry[] suggestedPalette = null; private boolean suggestedPaletteSet = false; /** * Sets the suggested palette information to be stored with this * image. The information is passed to this method as an array of * PNGSuggestedPaletteEntry objects. * *

The 'sPLT' chunk will encode this information. */ public void setSuggestedPalette(PNGSuggestedPaletteEntry[] palette) { suggestedPalette = (PNGSuggestedPaletteEntry[])(palette.clone()); suggestedPaletteSet = true; } /** * Returns the suggested palette information to be stored with this * image. * *

If the suggested palette information has not previously * been set, or has been unset, an * IllegalStateException will be thrown. * * @throws IllegalStateException if the suggested palette * information is not set. */ public PNGSuggestedPaletteEntry[] getSuggestedPalette() { if (!suggestedPaletteSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam17")); } return (PNGSuggestedPaletteEntry[])(suggestedPalette.clone()); } /** * Suppresses the 'sPLT' chunk from being output. */ public void unsetSuggestedPalette() { suggestedPalette = null; suggestedPaletteSet = false; } /** * Returns true if a 'sPLT' chunk will be output. */ public boolean isSuggestedPaletteSet() { return suggestedPaletteSet; } // sBIT chunk private int[] significantBits = null; private boolean significantBitsSet = false; /** * Sets the number of significant bits for each band of the image. * *

The number of entries in the significantBits * array must be equal to the number of output bands in the image: * 1 for a gray image, 2 for gray+alpha, 3 for index or truecolor, * and 4 for truecolor+alpha. * *

The 'sBIT' chunk will encode this information. */ public void setSignificantBits(int[] significantBits) { this.significantBits = (int[])(significantBits.clone()); significantBitsSet = true; } /** * Returns the number of significant bits for each band of the image. * *

If the significant bits values have not previously been * set, or have been unset, an IllegalStateException * will be thrown. * * @throws IllegalStateException if the significant bits values are * not set. */ public int[] getSignificantBits() { if (!significantBitsSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam18")); } return (int[])significantBits.clone(); } /** * Suppresses the 'sBIT' chunk from being output. */ public void unsetSignificantBits() { significantBits = null; significantBitsSet = false; } /** * Returns true if an 'sBIT' chunk will be output. */ public boolean isSignificantBitsSet() { return significantBitsSet; } // sRGB chunk private int SRGBIntent; private boolean SRGBIntentSet = false; /** * Sets the sRGB rendering intent to be stored with this image. * The legal values are 0 = Perceptual, 1 = Relative Colorimetric, * 2 = Saturation, and 3 = Absolute Colorimetric. Refer to the * PNG specification for information on these values. * *

The 'sRGB' chunk will encode this information. */ public void setSRGBIntent(int SRGBIntent) { this.SRGBIntent = SRGBIntent; SRGBIntentSet = true; } /** * Returns the sRGB rendering intent to be stored with this image. * *

If the sRGB intent has not previously been set, or has been * unset, an IllegalStateException will be thrown. * * @throws IllegalStateException if the sRGB intent is not set. */ public int getSRGBIntent() { if (!SRGBIntentSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam19")); } return SRGBIntent; } /** * Suppresses the 'sRGB' chunk from being output. */ public void unsetSRGBIntent() { SRGBIntentSet = false; } /** * Returns true if an 'sRGB' chunk will be output. */ public boolean isSRGBIntentSet() { return SRGBIntentSet; } // tEXt chunk private String[] text = null; private boolean textSet = false; /** * Sets the textual data to be stored in uncompressed form with this * image. The data is passed to this method as an array of * Strings. * *

The 'tEXt' chunk will encode this information. */ public void setText(String[] text) { this.text = text; textSet = true; } /** * Returns the text strings to be stored in uncompressed form with this * image as an array of Strings. * *

If the text strings have not previously been set, or have been * unset, an IllegalStateException will be thrown. * * @throws IllegalStateException if the text strings are not set. */ public String[] getText() { if (!textSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam20")); } return text; } /** * Suppresses the 'tEXt' chunk from being output. */ public void unsetText() { text = null; textSet = false; } /** * Returns true if a 'tEXt' chunk will be output. */ public boolean isTextSet() { return textSet; } // tIME chunk private Date modificationTime; private boolean modificationTimeSet = false; /** * Sets the modification time, as a Date, to be * stored with this image. The internal storage format will use * UTC regardless of how the modificationTime * parameter was created. * *

The 'tIME' chunk will encode this information. */ public void setModificationTime(Date modificationTime) { this.modificationTime = modificationTime; modificationTimeSet = true; } /** * Returns the modification time to be stored with this image. * *

If the bit depth has not previously been set, or has been * unset, an IllegalStateException will be thrown. * * @throws IllegalStateException if the bit depth is not set. */ public Date getModificationTime() { if (!modificationTimeSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam21")); } return modificationTime; } /** * Suppresses the 'tIME' chunk from being output. */ public void unsetModificationTime() { modificationTime = null; modificationTimeSet = false; } /** * Returns true if a 'tIME' chunk will be output. */ public boolean isModificationTimeSet() { return modificationTimeSet; } // tRNS chunk boolean transparencySet = false; /** * Suppresses the 'tRNS' chunk from being output. */ public void unsetTransparency() { transparencySet = false; } /** * Returns true if a 'tRNS' chunk will be output. */ public boolean isTransparencySet() { return transparencySet; } // zTXT chunk private String[] zText = null; private boolean zTextSet = false; /** * Sets the text strings to be stored in compressed form with this * image. The data is passed to this method as an array of * Strings. * *

The 'zTXt' chunk will encode this information. */ public void setCompressedText(String[] text) { this.zText = text; zTextSet = true; } /** * Returns the text strings to be stored in compressed form with * this image as an array of Strings. * *

If the compressed text strings have not previously been * set, or have been unset, an IllegalStateException * will be thrown. * * @throws IllegalStateException if the compressed text strings are * not set. */ public String[] getCompressedText() { if (!zTextSet) { throw new IllegalStateException(JaiI18N.getString("PNGEncodeParam22")); } return zText; } /** * Suppresses the 'zTXt' chunk from being output. */ public void unsetCompressedText() { zText = null; zTextSet = false; } /** * Returns true if a 'zTXT' chunk will be output. */ public boolean isCompressedTextSet() { return zTextSet; } // Other chunk types Vector chunkType = new Vector(); Vector chunkData = new Vector(); /** * Adds a private chunk, in binary form, to the list of chunks to * be stored with this image. * * @param type a 4-character String giving the chunk type name. * @param data an array of bytes containing the * chunk data. */ public synchronized void addPrivateChunk(String type, byte[] data) { chunkType.add(type); chunkData.add((byte[])data.clone()); } /** * Returns the number of private chunks to be written to the * output file. */ public synchronized int getNumPrivateChunks() { return chunkType.size(); } /** * Returns the type of the private chunk at a given index, as a * 4-character String. The index must be smaller * than the return value of getNumPrivateChunks. */ public synchronized String getPrivateChunkType(int index) { return (String)chunkType.elementAt(index); } /** * Returns the data associated of the private chunk at a given * index, as an array of bytes. The index must be * smaller than the return value of * getNumPrivateChunks. */ public synchronized byte[] getPrivateChunkData(int index) { return (byte[])chunkData.elementAt(index); } /** * Remove all private chunks associated with this parameter instance * whose 'safe-to-copy' bit is not set. This may be advisable when * transcoding PNG images. */ public synchronized void removeUnsafeToCopyPrivateChunks() { Vector newChunkType = new Vector(); Vector newChunkData = new Vector(); int len = getNumPrivateChunks(); for (int i = 0; i < len; i++) { String type = getPrivateChunkType(i); char lastChar = type.charAt(3); if (lastChar >= 'a' && lastChar <= 'z') { newChunkType.add(type); newChunkData.add(getPrivateChunkData(i)); } } chunkType = newChunkType; chunkData = newChunkData; } /** * Remove all private chunks associated with this parameter instance. */ public synchronized void removeAllPrivateChunks() { chunkType = new Vector(); chunkData = new Vector(); } /** * An abs() function for use by the Paeth predictor. */ private static final int abs(int x) { return (x < 0) ? -x : x; } /** * The Paeth predictor routine used in PNG encoding. This routine * is included as a convenience to subclasses that override the * filterRow method. */ public static final int paethPredictor(int a, int b, int c) { int p = a + b - c; int pa = abs(p - a); int pb = abs(p - b); int pc = abs(p - c); if ((pa <= pb) && (pa <= pc)) { return a; } else if (pb <= pc) { return b; } else { return c; } } /** * Performs filtering on a row of an image. This method may be * overridden in order to provide a custom algorithm for choosing * the filter type for a given row. * *

The method is supplied with the current and previous rows * of the image. For the first row of the image, or of an * interlacing pass, the previous row array will be filled with * zeros as required by the PNG specification. * *

The method is also supplied with five scratch arrays. * These arrays may be used within the method for any purpose. * At method exit, the array at the index given by the return * value of the method should contain the filtered data. The * return value will also be used as the filter type. * *

The default implementation of the method performs a trial * encoding with each of the filter types, and computes the sum of * absolute values of the differences between the raw bytes of the * current row and the predicted values. The index of the filter * producing the smallest result is returned. * *

As an example, to perform only 'sub' filtering, this method * could be implemented (non-optimally) as follows: * *

     * for (int i = bytesPerPixel; i < bytesPerRow + bytesPerPixel; i++) {
     *     int curr = currRow[i] & 0xff;
     *     int left = currRow[i - bytesPerPixel] & 0xff;
     *     scratchRow[PNG_FILTER_SUB][i] = (byte)(curr - left);
     * }
     * return PNG_FILTER_SUB;
     * 
* * @param currRow The current row as an array of bytes * of length at least bytesPerRow + bytesPerPixel. * The pixel data starts at index bytesPerPixel; * the initial bytesPerPixel bytes are zero. * @param prevRow The current row as an array of bytes * The pixel data starts at index bytesPerPixel; * the initial bytesPerPixel bytes are zero. * @param scratchRows An array of 5 byte arrays of * length at least bytesPerRow + * bytesPerPixel, useable to hold temporary results. * The filtered row will be returned as one of the entries * of this array. The returned filtered data should start * at index bytesPerPixel; The initial * bytesPerPixel bytes are not used. * @param bytesPerRow The number of bytes in the image row. * This value will always be greater than 0. * @param bytesPerPixel The number of bytes representing a single * pixel, rounded up to an integer. This is the 'bpp' parameter * described in the PNG specification. * * @return The filter type to be used. The entry of * scratchRows[] at this index holds the * filtered data. */ public int filterRow(byte[] currRow, byte[] prevRow, byte[][] scratchRows, int bytesPerRow, int bytesPerPixel) { int[] filterBadness = new int[5]; for (int i = 0; i < 5; i++) { filterBadness[i] = Integer.MAX_VALUE; } { int badness = 0; for (int i = bytesPerPixel; i < bytesPerRow + bytesPerPixel; i++) { int curr = currRow[i] & 0xff; badness += curr; } filterBadness[0] = badness; } { byte[] subFilteredRow = scratchRows[1]; int badness = 0; for (int i = bytesPerPixel; i < bytesPerRow + bytesPerPixel; i++) { int curr = currRow[i] & 0xff; int left = currRow[i - bytesPerPixel] & 0xff; int difference = curr - left; subFilteredRow[i] = (byte)difference; badness += abs(difference); } filterBadness[1] = badness; } { byte[] upFilteredRow = scratchRows[2]; int badness = 0; for (int i = bytesPerPixel; i < bytesPerRow + bytesPerPixel; i++) { int curr = currRow[i] & 0xff; int up = prevRow[i] & 0xff; int difference = curr - up; upFilteredRow[i] = (byte)difference; badness += abs(difference); } filterBadness[2] = badness; } { byte[] averageFilteredRow = scratchRows[3]; int badness = 0; for (int i = bytesPerPixel; i < bytesPerRow + bytesPerPixel; i++) { int curr = currRow[i] & 0xff; int left = currRow[i - bytesPerPixel] & 0xff; int up = prevRow[i] & 0xff; int difference = curr - (left + up)/2;; averageFilteredRow[i] = (byte)difference; badness += abs(difference); } filterBadness[3] = badness; } { byte[] paethFilteredRow = scratchRows[4]; int badness = 0; for (int i = bytesPerPixel; i < bytesPerRow + bytesPerPixel; i++) { int curr = currRow[i] & 0xff; int left = currRow[i - bytesPerPixel] & 0xff; int up = prevRow[i] & 0xff; int upleft = prevRow[i - bytesPerPixel] & 0xff; int predictor = paethPredictor(left, up, upleft); int difference = curr - predictor; paethFilteredRow[i] = (byte)difference; badness += abs(difference); } filterBadness[4] = badness; } int filterType = 0; int minBadness = filterBadness[0]; for (int i = 1; i < 5; i++) { if (filterBadness[i] < minBadness) { minBadness = filterBadness[i]; filterType = i; } } if (filterType == 0) { System.arraycopy(currRow, bytesPerPixel, scratchRows[0], bytesPerPixel, bytesPerRow); } return filterType; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/BMPEncodeParam.java0000644000175000017500000000513010203035544026243 0ustar mathieumathieu/* * $RCSfile: BMPEncodeParam.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:29 $ * $State: Exp $ */ package com.sun.media.jai.codec; /** * An instance of ImageEncodeParam for encoding images in * the BMP format. * *

This class allows for the specification of various parameters * while encoding (writing) a BMP format image file. By default, the * version used is VERSION_3, no compression is used, and the data layout * is bottom_up, such that the pixels are stored in bottom-up order, the * first scanline being stored last. * *

This class is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. * */ public class BMPEncodeParam implements ImageEncodeParam { // version constants /** Constant for BMP version 2. */ public static final int VERSION_2 = 0; /** Constant for BMP version 3. */ public static final int VERSION_3 = 1; /** Constant for BMP version 4. */ public static final int VERSION_4 = 2; // Default values private int version = VERSION_3; private boolean compressed = false; private boolean topDown = false; /** * Constructs an BMPEncodeParam object with default values for parameters. */ public BMPEncodeParam() {} /** Sets the BMP version to be used. */ public void setVersion(int versionNumber) { checkVersion(versionNumber); this.version = versionNumber; } /** Returns the BMP version to be used. */ public int getVersion() { return version; } /** If set, the data will be written out compressed, if possible. */ public void setCompressed(boolean compressed) { this.compressed = compressed; } /** * Returns the value of the parameter compressed. */ public boolean isCompressed() { return compressed; } /** * If set, the data will be written out in a top-down manner, the first * scanline being written first. */ public void setTopDown(boolean topDown) { this.topDown = topDown; } /** * Returns the value of the topDown parameter. */ public boolean isTopDown() { return topDown; } // Method to check whether we can handle the given version. private void checkVersion(int versionNumber) { if ( !(versionNumber == VERSION_2 || versionNumber == VERSION_3 || versionNumber == VERSION_4) ) { throw new RuntimeException(JaiI18N.getString("BMPEncodeParam0")); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/ImageEncoder.java0000644000175000017500000000376310203035544026062 0ustar mathieumathieu/* * $RCSfile: ImageEncoder.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:31 $ * $State: Exp $ */ package com.sun.media.jai.codec; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.io.IOException; import java.io.OutputStream; /** * An interface describing objects that transform a BufferedImage or * Raster into an OutputStream. * *

This interface is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. */ public interface ImageEncoder { /** * Returns the current parameters as an instance of the * ImageEncodeParam interface. Concrete implementations of this * interface will return corresponding concrete implementations of * the ImageEncodeParam interface. For example, a JPEGImageEncoder * will return an instance of JPEGEncodeParam. */ public ImageEncodeParam getParam(); /** * Sets the current parameters to an instance of the * ImageEncodeParam interface. Concrete implementations * of ImageEncoder may throw a RuntimeException if the * params argument is not an instance of the appropriate * subclass or subinterface. For example, a JPEGImageEncoder * will expect param to be an instance of JPEGEncodeParam. */ public void setParam(ImageEncodeParam param); /** Returns the OutputStream associated with this ImageEncoder. */ public OutputStream getOutputStream(); /** * Encodes a Raster with a given ColorModel and writes the output * to the OutputStream associated with this ImageEncoder. */ public void encode(Raster ras, ColorModel cm) throws IOException; /** * Encodes a RenderedImage and writes the output to the * OutputStream associated with this ImageEncoder. */ public void encode(RenderedImage im) throws IOException; } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/TIFFEncodeParam.java0000644000175000017500000003636210336211466026376 0ustar mathieumathieu/* * $RCSfile: TIFFEncodeParam.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-11-14 22:44:06 $ * $State: Exp $ */ package com.sun.media.jai.codec; import java.awt.image.RenderedImage; import java.util.Collections; import java.util.Iterator; import java.util.zip.Deflater; /** * An instance of ImageEncodeParam for encoding images in * the TIFF format. * *

This class allows for the specification of encoding parameters. By * default, the image is encoded without any compression, and is written * out consisting of strips, not tiles. The particular compression scheme * to be used can be specified by using the setCompression() * method. The compression scheme specified will be honored only if it is * compatible with the type of image being written out. For example, * Group3 and Group4 compressions can only be used with Bilevel images. * Writing of tiled TIFF images can be enabled by calling the * setWriteTiled() method. * *

This class is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. * */ public class TIFFEncodeParam implements ImageEncodeParam { /** No compression. */ public static final int COMPRESSION_NONE = 1; /** Byte-oriented run-length encoding "PackBits" compression. */ public static final int COMPRESSION_PACKBITS = 32773; /** * Modified Huffman Compression (CCITT Run Length Encoding (RLE)). */ public static final int COMPRESSION_GROUP3_1D = 2; /** * CCITT T.4 bilevel compression (Group 3 facsimile compression). */ public static final int COMPRESSION_GROUP3_2D = 3; /** * CCITT T.6 bilevel compression (Group 4 facsimile compression). */ public static final int COMPRESSION_GROUP4 = 4; /** * LZW compression. *

Not supported. */ public static final int COMPRESSION_LZW = 5; /** * * JPEG-in-TIFF compression. */ public static final int COMPRESSION_JPEG_TTN2 = 7; /** * * DEFLATE lossless compression (also known as "Zip-in-TIFF"). */ public static final int COMPRESSION_DEFLATE = 32946; private int compression = COMPRESSION_NONE; private boolean reverseFillOrder = false; private boolean T4Encode2D = true; private boolean T4PadEOLs = false; private boolean writeTiled = false; private int tileWidth; private int tileHeight; private Iterator extraImages; private TIFFField[] extraFields; private boolean convertJPEGRGBToYCbCr = true; private JPEGEncodeParam jpegEncodeParam = null; private int deflateLevel = Deflater.DEFAULT_COMPRESSION; private boolean isLittleEndian = false; /** * Constructs a TIFFEncodeParam object with default values for * all parameters. */ public TIFFEncodeParam() {} /** * Returns the value of the compression parameter. */ public int getCompression() { return compression; } /** * Specifies the type of compression to be used. The compression type * specified will be honored only if it is compatible with the image * being written out. Currently only PackBits, JPEG, and DEFLATE * compression schemes are supported. * *

If compression is set to any value but * COMPRESSION_NONE and the OutputStream * supplied to the ImageEncoder is not a * SeekableOutputStream, then the encoder will use either * a temporary file or a memory cache when compressing the data * depending on whether the file system is accessible. Compression * will therefore be more efficient if a SeekableOutputStream * is supplied. * * @param compression The compression type. * @throws IllegalArgumentException if compression is * not one of the defined COMPRESSION_* constants. */ public void setCompression(int compression) { switch(compression) { case COMPRESSION_NONE: case COMPRESSION_GROUP3_1D: case COMPRESSION_GROUP3_2D: case COMPRESSION_GROUP4: case COMPRESSION_PACKBITS: case COMPRESSION_JPEG_TTN2: case COMPRESSION_DEFLATE: // Do nothing. break; default: throw new IllegalArgumentException(JaiI18N.getString("TIFFEncodeParam0")); } this.compression = compression; } /** * Returns value of flag indicating whether CCITT-compressed bilevel * data should be filled in reverse order. * * @see #setReverseFillOrder */ public boolean getReverseFillOrder() { return reverseFillOrder; } /** * Set value of flag indicating whether CCITT-compressed bilevel * data should be filled in reverse order. If true, * pixels are arranged within a byte such that pixels with lower * column values are stored in the lower order bits of the byte. * Thus true corresponds to TIFF FillOrder value 2 * and false to TIFF FillOrder 1. The default * value is false. */ public void setReverseFillOrder(boolean reverseFillOrder) { this.reverseFillOrder = reverseFillOrder; } /** * Returns value of flag indicating whether T4-compressed bilevel data * should be two-dimensionally encoded. * * @see #setT4Encode2D */ public boolean getT4Encode2D() { return T4Encode2D; } /** * Set value of flag indicating whether T4-compressed bilevel data * should be two-dimensionally encoded. If true the * data are two-dimensionally encoded; if false they * are one-dimensionally encoded. The default value is true. */ public void setT4Encode2D(boolean T4Encode2D) { this.T4Encode2D = T4Encode2D; } /** * Returns value of flag indicating whether T4-compressed bilevel data * should have the embedded EOL bit sequences padded to byte alignment. * * @see #setT4PadEOLs */ public boolean getT4PadEOLs() { return T4PadEOLs; } /** * Sets value of flag indicating whether T4-compressed bilevel data * should have the embedded EOL bit sequences padded to byte alignment. * If true, zero-valued bits are prepended to each EOL * bit sequence 0x001 such that the EOL is right-aligned * on a byte boundary: * *

     * xxxx-0000 0000-0001
     * 
* * where "x" denotes a value which could be either data or a fill bit * depending on the alignment of the data before the EOL. The default * value is false. */ public void setT4PadEOLs(boolean T4PadEOLs) { this.T4PadEOLs = T4PadEOLs; } /** * Returns the value of the writeTiled parameter. */ public boolean getWriteTiled() { return writeTiled; } /** * If set, the data will be written out in tiled format, instead of * in strips. * * @param writeTiled Specifies whether the image data should be * wriiten out in tiled format. */ public void setWriteTiled(boolean writeTiled) { this.writeTiled = writeTiled; } /** * Sets the dimensions of the tiles to be written. If either * value is non-positive, the encoder will use a default value. * *

If the data are being written as tiles, i.e., * getWriteTiled() returns true, then the * default tile dimensions used by the encoder are those of the tiles * of the image being encoded. * *

If the data are being written as strips, i.e., * getWriteTiled() returns false, the width * of each strip is always the width of the image and the default * number of rows per strip is 8. * *

If JPEG compession is being used, the dimensions of the strips or * tiles may be modified to conform to the JPEG-in-TIFF specification. * * @param tileWidth The tile width; ignored if strips are used. * @param tileHeight The tile height or number of rows per strip. */ public void setTileSize(int tileWidth, int tileHeight) { this.tileWidth = tileWidth; this.tileHeight = tileHeight; } /** * Retrieves the tile width set via setTileSize(). */ public int getTileWidth() { return tileWidth; } /** * Retrieves the tile height set via setTileSize(). */ public int getTileHeight() { return tileHeight; } /** * Sets an Iterator of additional images to be written * after the image passed as an argument to the ImageEncoder. * The methods on the supplied Iterator must only be invoked * by the ImageEncoder which will exhaust the available * values unless an error occurs. * *

The value returned by an invocation of next() on the * Iterator must return either a RenderedImage * or an Object[] of length 2 wherein the element at index * zero is a RenderedImage amd the other element is a * TIFFEncodeParam. If no TIFFEncodeParam is * supplied in this manner for an additional image, the parameters used * to create the ImageEncoder will be used. The extra * image Iterator set on any TIFFEncodeParam * of an additional image will in all cases be ignored. */ public synchronized void setExtraImages(Iterator extraImages) { this.extraImages = extraImages; } /** * Returns the additional image Iterator specified via * setExtraImages() or null if none was * supplied or if a null value was supplied. */ public synchronized Iterator getExtraImages() { return extraImages; } /** * Sets the compression level for DEFLATE-compressed data which should * either be java.util.Deflater.DEFAULT_COMPRESSION or a * value in the range [1,9] where larger values indicate more compression. * The default setting is Deflater.DEFAULT_COMPRESSION. This * setting is ignored if the compression type is not DEFLATE. * * @throws IllegalArgumentException if deflateLevel is * not in the range [1, 9] and is not * {@link Deflater#DEFAULT_COMPRESSION}. */ public void setDeflateLevel(int deflateLevel) { if(deflateLevel < 1 && deflateLevel > 9 && deflateLevel != Deflater.DEFAULT_COMPRESSION) { throw new IllegalArgumentException(JaiI18N.getString("TIFFEncodeParam1")); } this.deflateLevel = deflateLevel; } /** * Gets the compression level for DEFLATE compression. */ public int getDeflateLevel() { return deflateLevel; } /** * Sets flag indicating whether to convert RGB data to YCbCr when the * compression type is JPEG. The default value is true. * This flag is ignored if the compression type is not JPEG. */ public void setJPEGCompressRGBToYCbCr(boolean convertJPEGRGBToYCbCr) { this.convertJPEGRGBToYCbCr = convertJPEGRGBToYCbCr; } /** * Whether RGB data will be converted to YCbCr when using JPEG compression. */ public boolean getJPEGCompressRGBToYCbCr() { return convertJPEGRGBToYCbCr; } /** * Sets the JPEG compression parameters. These parameters are ignored * if the compression type is not JPEG. The argument may be * null to indicate that default compression parameters * are to be used. For maximum conformance with the specification it * is recommended in most cases that only the quality compression * parameter be set. * *

The writeTablesOnly and JFIFHeader * flags of the JPEGEncodeParam are ignored. The * writeImageOnly flag is used to determine whether the * JPEGTables field will be written to the TIFF stream: if * writeImageOnly is true, then the JPEGTables * field will be written and will contain a valid JPEG abbreviated * table specification datastream. In this case the data in each data * segment (strip or tile) will contain an abbreviated JPEG image * datastream. If the writeImageOnly flag is * false, then the JPEGTables field will not be written and * each data segment will contain a complete JPEG interchange datastream. */ public void setJPEGEncodeParam(JPEGEncodeParam jpegEncodeParam) { if(jpegEncodeParam != null) { jpegEncodeParam = (JPEGEncodeParam)jpegEncodeParam.clone(); jpegEncodeParam.setWriteTablesOnly(false); jpegEncodeParam.setWriteJFIFHeader(false); } this.jpegEncodeParam = jpegEncodeParam; } /** * Retrieves the JPEG compression parameters. */ public JPEGEncodeParam getJPEGEncodeParam() { if(jpegEncodeParam == null) { jpegEncodeParam = new JPEGEncodeParam(); jpegEncodeParam.setWriteTablesOnly(false); jpegEncodeParam.setWriteImageOnly(true); jpegEncodeParam.setWriteJFIFHeader(false); } return jpegEncodeParam; } /** * Sets an array of extra fields to be written to the TIFF Image File * Directory (IFD). Fields with tags equal to the tag of any * automatically generated fields are ignored. No error checking is * performed with respect to the validity of the field contents or * the appropriateness of the field for the image being encoded. * * @param extraFields An array of extra fields; the parameter is * copied by reference. */ public void setExtraFields(TIFFField[] extraFields) { this.extraFields = extraFields; } /** * Returns the value set by setExtraFields(). */ public TIFFField[] getExtraFields() { return extraFields; } /** * Sets a flag indicating whether the byte order used to write the * output stream is little endian. If true * multi-byte data units such as 16-bit and 32-bit integers and 32-bit * floating point values are written from the least to the most * significant byte; if false the order is from most to * least significant byte. The default value is false. */ public void setLittleEndian(boolean isLittleEndian) { this.isLittleEndian = isLittleEndian; } /** * Returns the value of the flag indicating whether the output stream * byte order is little endian. */ public boolean getLittleEndian() { return this.isLittleEndian; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/JPEGEncodeParam.java0000644000175000017500000002655410203035544026367 0ustar mathieumathieu/* * $RCSfile: JPEGEncodeParam.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:31 $ * $State: Exp $ */ package com.sun.media.jai.codec; import java.awt.image.RenderedImage; /** * A class which encapsulates the most common functionality required for * the parameters to a Jpeg encode operation. It does not include all of * the parameters of the com.sun.image.codec.jpeg classes. * Users needing that additional functionality should use those classes * directly, bearing in mind that they are part of an uncommitted non-core * interface that may be modified or removed in the future. * * This class makes very simple assumptions about the image colorspaces. * Images with a single band are assumed to be grayscale. * Images with three bands are assumed to be RGB and are encoded to YCbCr. * *

This class is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. */ public class JPEGEncodeParam implements ImageEncodeParam { private static int JPEG_MAX_BANDS = 3; private int[] hSamp; private int[] vSamp; private int[][] qTab; private int[] qTabSlot; private float qual; private int rstInterval; private boolean writeImageOnly; private boolean writeTablesOnly; private boolean writeJFIFHeader; private boolean qualitySet; private boolean[] qTabSet; /** * Constructs a JAI JPEGEncodeParam object with default settings. */ public JPEGEncodeParam() { // // Set all the defaults // hSamp = new int[JPEG_MAX_BANDS]; vSamp = new int[JPEG_MAX_BANDS]; qTabSlot = new int[JPEG_MAX_BANDS]; qTab = new int[JPEG_MAX_BANDS][]; qTabSet = new boolean[JPEG_MAX_BANDS]; // Y channel - full resolution sampling hSamp[0] = 1; vSamp[0] = 1; qTabSlot[0] = 0; qTab[0] = null; qTabSet[0] = false; // Cb channel - sample by 2 in each axis hSamp[1] = 2; vSamp[1] = 2; qTabSlot[1] = 1; qTab[1] = null; qTabSet[1] = false; // Cr channel - sample by 2 in each axis hSamp[2] = 2; vSamp[2] = 2; qTabSlot[2] = 1; qTab[2] = null; qTabSet[2] = false; qual = 0.75F; rstInterval = 0; writeImageOnly = false; writeTablesOnly = false; writeJFIFHeader = true; } /** * Sets the horizontal subsampling to be applied to an image band. * Defaults to 1 for grayscale and (1,2,2) for RGB. * @param component The band for which to set horizontal subsampling. * @param subsample The horizontal subsampling factor. */ public void setHorizontalSubsampling(int component, int subsample) { hSamp[component] = subsample; } /** * Get the horizontal subsampling factor for a band. * @param component The band of the image for which to retrieve subsampling. * @return The horizontal subsampling factor to be applied to this band */ public int getHorizontalSubsampling(int component) { return hSamp[component]; } /** * Sets the vertical subsampling to be applied to an image band. * Defaults to 1 for grayscale and (1,2,2) for RGB. * @param component The band for which to set vertical subsampling. * @param subsample The vertical subsampling factor. */ public void setVerticalSubsampling(int component, int subsample) { vSamp[component] = subsample; } /** * Get the vertical subsampling factor for a band. * @param component The band of the image for which to retrieve subsampling. * @return The vertical subsampling factor to be applied to this band */ public int getVerticalSubsampling(int component) { return vSamp[component]; } /** * Sets the quantization table to be used for luminance data. * This is a convenience method which explicitly sets the * contents of quantization table 0. The length of the table must be 64. * This disables any quality setting. * @param qTable Quantization table values in "zig-zag" order. */ public void setLumaQTable(int[] qTable) { setQTable(0, 0, qTable); qTabSet[0] = true; qualitySet = false; } /** * Sets the quantization table to be used for chrominance data. * This is a convenience method which explicitly sets the * contents of quantization table 1. The length of the table must be 64. * This method assumes that all chroma components will use the same table. * This disables any quality setting. * @param qTable Quantization table values in "zig-zag" order. */ public void setChromaQTable(int[] qTable) { setQTable(1, 1, qTable); setQTable(2, 1, qTable); qTabSet[1] = true; qTabSet[2] = true; qualitySet = false; } /** * Sets a quantization table to be used for a component. * This method allows up to four independent tables to be specified. * This disables any quality setting. * @param component The band to which this table applies. * @param tableSlot The table number that this table is assigned to (0 to 3). * @param qTable Quantization table values in "zig-zag" order. */ public void setQTable(int component, int tableSlot, int[]qTable) { qTab[component] = (int[])(qTable.clone()); qTabSlot[component] = tableSlot; qTabSet[component] = true; qualitySet = false; } /** * Tests if a Quantization table has been set. * @return Returns true is the specified quantization table has been set. */ public boolean isQTableSet(int component) { return qTabSet[component]; } /** * Retrieve the contents of the quantization table used for a component. * @param component The band to which this table applies. * @return The contents of the quantization table as a reference. * @throws IllegalStateException if table has not been previously set for this component. */ public int[] getQTable(int component) { if (!qTabSet[component]) { throw new IllegalStateException( JaiI18N.getString("JPEGEncodeParam0")); } return qTab[component]; } /** * Retrieve the quantization table slot used for a component. * @param component The band to which this table slot applies. * @return The table slot used for this band. * @throws IllegalStateException if table has not been previously set for this component. */ public int getQTableSlot(int component) { if (!qTabSet[component]) { throw new IllegalStateException( JaiI18N.getString("JPEGEncodeParam0")); } return qTabSlot[component]; } /** * Sets the restart interval in Minimum Coded Units (MCUs). * This can be useful in some environments to limit the effect * of bitstream errors to a single restart interval. * The default is zero (no restart interval markers). * @param restartInterval Number of MCUs between restart markers. */ public void setRestartInterval(int restartInterval) { rstInterval = restartInterval; } /** * Gets the restart interval in Minimum Coded Units (MCUs). * @return The restart interval in MCUs (0 if not set). */ public int getRestartInterval() { return rstInterval; } /** * This creates new quantization tables that replace the currently * installed quantization tables. * The created quantization table varies from very high * compression, very low quality, (0.0) to low compression, very * high quality (1.0) based on the quality parameter.

* At a quality level of 1.0 the table will be all 1's which will * lead to no loss of data due to quantization (however chrominace * subsampling, if used, and roundoff error in the DCT will still * degrade the image some what).

* The default setting is 0.75 which provides high quality while * insuring a good compression ratio. *

Some guidelines: 0.75 high quality
     *                 0.5  medium quality
     *                 0.25 low quality
     * 
* @param quality 0.0-1.0 setting of desired quality level. */ public void setQuality(float quality) { qual = quality; // Reset custom Q tables for (int i=0; iJPEGEncodeParam
object. */ public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { // this shouldn't happen, since we are Cloneable throw new InternalError(); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/SeekableOutputStream.java0000644000175000017500000000404110203035544027636 0ustar mathieumathieu/* * $RCSfile: SeekableOutputStream.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:32 $ * $State: Exp $ */ package com.sun.media.jai.codec; import java.io.FileDescriptor; import java.io.IOException; import java.io.OutputStream; import java.io.RandomAccessFile; /** * An OutputStream which can seek to an arbitrary offset. */ public class SeekableOutputStream extends OutputStream { private RandomAccessFile file; /** * Constructs a SeekableOutputStream from a * RandomAccessFile. Unless otherwise indicated, * all method invocations are fowarded to the underlying * RandomAccessFile. * * @param file The RandomAccessFile to which calls * will be forwarded. * @exception IllegalArgumentException if file is * null. */ public SeekableOutputStream(RandomAccessFile file) { if(file == null) { throw new IllegalArgumentException(JaiI18N.getString("SeekableOutputStream0")); } this.file = file; } public void write(int b) throws IOException { file.write(b); } public void write(byte b[]) throws IOException { file.write(b); } public void write(byte b[], int off, int len) throws IOException { file.write(b, off, len); } /** * Invokes getFD().sync() on the underlying * RandomAccessFile. */ public void flush() throws IOException { // Fix: 4636212. When this FIleDescriptor is not valid, do nothing. FileDescriptor fd = file.getFD(); if(fd.valid()) fd.sync(); } public void close() throws IOException { file.close(); } public long getFilePointer() throws IOException { return file.getFilePointer(); } public void seek(long pos) throws IOException { file.seek(pos); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/PNGDecodeParam.java0000644000175000017500000002770710203035544026255 0ustar mathieumathieu/* * $RCSfile: PNGDecodeParam.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:31 $ * $State: Exp $ */ package com.sun.media.jai.codec; /** * An instance of ImageDecodeParam for decoding images in * the PNG format. * * PNGDecodeParam allows several aspects of the decoding * process for PNG images to be controlled. By default, decoding produces * output images with the following properties: * *

Images with a bit depth of 8 or less use a * DataBufferByte to hold the pixel data. 16-bit images * use a DataBufferUShort. * *

Palette color images and non-transparent grayscale images with * bit depths of 1, 2, or 4 will have a * MultiPixelPackedSampleModel and an * IndexColorModel. For palette color images, the * ColorModel palette contains the red, green, blue, and * optionally alpha palette information. For grayscale images, the * palette is used to expand the pixel data to cover the range 0-255. * The pixels are stored packed 8, 4, or 2 to the byte. * *

All other images are stored using a * PixelInterleavedSampleModel with each sample of a pixel * occupying its own byte or short within * the DataBuffer. A ComponentColorModel is * used which simply extracts the red, green, blue, gray, and/or alpha * information from separate DataBuffer entries. * *

Five aspects of this process may be altered by means of methods * in this class. * *

setSuppressAlpha() prevents an alpha channel * from appearing in the output. * *

setExpandPalette() turns palette-color images into * 3-or 4-channel full-color images. * *

setOutput8BitGray() causes 1, 2, or 4 bit * grayscale images to be output in 8-bit form, using a * ComponentSampleModel and * ComponentColorModel. * *

setDecodingExponent() causes the output image to be * gamma-corrected using a supplied output gamma value. * *

setExpandGrayAlpha() causes 2-channel gray/alpha * (GA) images to be output as full-color (GGGA) images, which may * simplify further processing and display. * *

This class is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. */ public class PNGDecodeParam implements ImageDecodeParam { /** * Constructs a default instance of PNGDecodeParam. */ public PNGDecodeParam() {} private boolean suppressAlpha = false; /** * Returns true if alpha (transparency) will * be prevented from appearing in the output. */ public boolean getSuppressAlpha() { return suppressAlpha; } /** * If set, no alpha (transparency) channel will appear in the * output image. * *

The default is to allow transparency to appear in the * output image. */ public void setSuppressAlpha(boolean suppressAlpha) { this.suppressAlpha = suppressAlpha; } private boolean expandPalette = false; /** * Returns true if palette-color images will be expanded to * produce full-color output. */ public boolean getExpandPalette() { return expandPalette; } /** * If set, palette color images (PNG color type 3) will * be decoded into full-color (RGB) output images. The output * image may have 3 or 4 channels, depending on the presence of * transparency information. * *

The default is to output palette images using a single * channel. The palette information is used to construct the * output image's ColorModel. */ public void setExpandPalette(boolean expandPalette) { this.expandPalette = expandPalette; } private boolean output8BitGray = false; /** * Returns the current value of the 8-bit gray output parameter. */ public boolean getOutput8BitGray() { return output8BitGray; } /** * If set, grayscale images with a bit depth less than 8 * (1, 2, or 4) will be output in 8 bit form. The output values * will occupy the full 8-bit range. For example, gray values * 0, 1, 2, and 3 of a 2-bit image will be output as * 0, 85, 170, and 255. * *

The decoding of non-grayscale images and grayscale images * with a bit depth of 8 or 16 are unaffected by this setting. * *

The default is not to perform expansion. Grayscale images * with a depth of 1, 2, or 4 bits will be represented using * a MultiPixelPackedSampleModel and an * IndexColorModel. */ public void setOutput8BitGray(boolean output8BitGray) { this.output8BitGray = output8BitGray; } private boolean performGammaCorrection = false; /** * Returns true if gamma correction is to be performed * on the image data. The default is false. * *

If gamma correction is to be performed, the * getUserExponent() and * getDisplayExponent() methods are used in addition to * the gamma value stored within the file (or the default value of * 1/2.2 used if no value is found) to produce a single exponent * using the formula: *

     * decoding_exponent = user_exponent/(gamma_from_file * display_exponent)
     * 
*/ public boolean getPerformGammaCorrection() { return performGammaCorrection; } /** * Turns gamma corection of the image data on or off. */ public void setPerformGammaCorrection(boolean performGammaCorrection) { this.performGammaCorrection = performGammaCorrection; } private float userExponent = 1.0F; /** * Returns the current value of the user exponent parameter. * By default, the user exponent is equal to 1.0F. */ public float getUserExponent() { return userExponent; } /** * Sets the user exponent to a given value. The exponent * must be positive. If not, an * IllegalArgumentException will be thrown. * *

The output image pixels will be placed through a transformation * of the form: * *

     * sample = integer_sample / (2^bitdepth - 1.0)
     * decoding_exponent = user_exponent/(gamma_from_file * display_exponent)
     * output = sample ^ decoding_exponent
     * 
* * where gamma_from_file is the gamma of the file * data, as determined by the gAMA, sRGB, * and/or iCCP chunks, and display_exponent * is the exponent of the intrinsic transfer curve of the display, * generally 2.2. * *

Input files which do not specify any gamma are assumed to * have a gamma of 1/2.2; such images may be displayed * on a CRT with an exponent of 2.2 using the default user * exponent of 1.0. * *

The user exponent may be used in order to change the * effective gamma of a file. If a file has a stored gamma of * X, but the decoder believes that the true file gamma is Y, * setting a user exponent of Y/X will produce the same result * as changing the file gamma. * *

This parameter affects the decoding of all image types. * * @throws IllegalArgumentException if userExponent is * negative. */ public void setUserExponent(float userExponent) { if (userExponent <= 0.0F) { throw new IllegalArgumentException(JaiI18N.getString("PNGDecodeParam0")); } this.userExponent = userExponent; } private float displayExponent = 2.2F; /** * Returns the current value of the display exponent parameter. * By default, the display exponent is equal to 2.2F. */ public float getDisplayExponent() { return displayExponent; } /** * Sets the display exponent to a given value. The exponent * must be positive. If not, an * IllegalArgumentException will be thrown. * *

The output image pixels will be placed through a transformation * of the form: * *

     * sample = integer_sample / (2^bitdepth - 1.0)
     * decoding_exponent = user_exponent/(gamma_from_file * display_exponent)
     * output = sample ^ decoding_exponent
     * 
* * where gamma_from_file is the gamma of the file * data, as determined by the gAMA, sRGB, * and/or iCCP chunks, and user_exponent * is an additional user-supplied parameter. * *

Input files which do not specify any gamma are assumed to * have a gamma of 1/2.2; such images should be * decoding using the default display exponent of 2.2. * *

If an image is to be processed further before being displayed, * it may be preferable to set the display exponent to 1.0 in order * to produce a linear output image. * *

This parameter affects the decoding of all image types. * * @throws IllegalArgumentException if userExponent is * negative. */ public void setDisplayExponent(float displayExponent) { if (displayExponent <= 0.0F) { throw new IllegalArgumentException(JaiI18N.getString("PNGDecodeParam1")); } this.displayExponent = displayExponent; } private boolean expandGrayAlpha = false; /** * Returns the current setting of the gray/alpha expansion. */ public boolean getExpandGrayAlpha() { return expandGrayAlpha; } /** * If set, images containing one channel of gray and one channel of * alpha (GA) will be output in a 4-channel format (GGGA). This * produces output that may be simpler to process and display. * *

This setting affects both images of color type 4 (explicit * alpha) and images of color type 0 (grayscale) that contain * transparency information. * *

By default, no expansion is performed. */ public void setExpandGrayAlpha(boolean expandGrayAlpha) { this.expandGrayAlpha = expandGrayAlpha; } private boolean generateEncodeParam = false; private PNGEncodeParam encodeParam = null; /** * Returns true if an instance of * PNGEncodeParam will be available after an image * has been decoded via the getEncodeParam method. */ public boolean getGenerateEncodeParam() { return generateEncodeParam; } /** * If set, an instance of PNGEncodeParam will be * available after an image has been decoded via the * getEncodeParam method that encapsulates information * about the contents of the PNG file. If not set, this information * will not be recorded and getEncodeParam() will * return null. */ public void setGenerateEncodeParam(boolean generateEncodeParam) { this.generateEncodeParam = generateEncodeParam; } /** * If getGenerateEncodeParam() is true, * this method may be called after decoding has completed, and * will return an instance of PNGEncodeParam containing * information about the contents of the PNG file just decoded. */ public PNGEncodeParam getEncodeParam() { return encodeParam; } /** * Sets the current encoder param instance. This method is * intended to be called by the PNG decoder and will overwrite the * current instance returned by getEncodeParam. */ public void setEncodeParam(PNGEncodeParam encodeParam) { this.encodeParam = encodeParam; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/ByteArraySeekableStream.java0000644000175000017500000001673410203035544030254 0ustar mathieumathieu/* * $RCSfile: ByteArraySeekableStream.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:29 $ * $State: Exp $ */ package com.sun.media.jai.codec; import java.io.InputStream; import java.io.IOException; /** * A subclass of SeekableStream that takes input from an * array of bytes. Seeking backwards is supported. The * mark() and resest() methods are * supported. * *

This class is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. */ public class ByteArraySeekableStream extends SeekableStream { /** Array holding the source data. */ private byte[] src; /** The starting offset within the array. */ private int offset; /** The length of the valid segment of the array. */ private int length; /** The current output position. */ private int pointer; /** * Constructs a ByteArraySeekableStream taking * input from a given segment of an input byte array. */ public ByteArraySeekableStream(byte[] src, int offset, int length) throws IOException { this.src = src; this.offset = offset; this.length = length; } /** * Constructs a ByteArraySeekableStream taking * input from an entire input byte array. */ public ByteArraySeekableStream(byte[] src) throws IOException { this(src, 0, src.length); } /** * Returns the number of bytes that can be read from this input * stream without blocking. * The value returned is * Math.min(offset + length, src.length) - pos, * which is the number of bytes remaining to be read from the input buffer. * * @return the number of bytes that can be read from the input stream * without blocking. */ public synchronized int available() { ensureOpen(); return Math.min(offset + length, src.length) - pointer; } /** * Returns true since this object supports seeking * backwards. */ public boolean canSeekBackwards() { return true; } /** * Returns the current offset in this stream. * * @return the offset from the beginning of the stream, in bytes, * at which the next read occurs. */ public long getFilePointer() { return (long)pointer; } /** * Sets the offset, measured from the beginning of this * stream, at which the next read occurs. Seeking backwards is * allowed. * * @param pos the offset position, measured in bytes from the * beginning of the stream, at which to set the stream * pointer. */ public void seek(long pos) { pointer = (int)pos; } /** * Reads the next byte of data from the input array. The value byte is * returned as an int in the range 0 to * 255. If no byte is available because the end of the stream * has been reached, the value -1 is returned. */ public int read() { if (pointer < length + offset) { return (int)(src[pointer++ + offset] & 0xff); } else { return -1; } } /** * Copies up to len bytes of data from the input array into * an array of bytes. An attempt is made to copy as many as * len bytes, but a smaller number may be copied, possibly * zero. The number of bytes actually copied is returned as an integer. * *

If b is null, a * NullPointerException is thrown. * *

If off is negative, or len is negative, or * off+len is greater than the length of the array * b, then an IndexOutOfBoundsException is * thrown. * *

If len is zero, then no bytes are copied and * 0 is returned; otherwise, there is an attempt to copy at * least one byte. If no byte is available because the stream is at end of * stream, the value -1 is returned; otherwise, at least one * byte is copied into b. * *

The first byte copied is stored into element * b[off], the next one into b[off+1], * and so on. The number of bytes copied is, at most, equal to * len. Let k be the number of bytes actually * copied; these bytes will be stored in elements * b[off] through * b[off+k-1], leaving elements * b[off+k] through * b[off+len-1] unaffected. * *

In every case, elements b[0] through * b[off] and elements b[off+len] through * b[b.length-1] are unaffected. * * @param b the buffer into which the data is copied. * @param off the start offset in array b * at which the data is written. * @param len the maximum number of bytes to copy. * @return the total number of bytes read into the buffer, or * -1 if there is no more data because the end of * the stream has been reached. */ public int read(byte[] b, int off, int len) { if (b == null) { throw new NullPointerException(); } if ((off < 0) || (len < 0) || (off + len > b.length)) { throw new IndexOutOfBoundsException(); } if (len == 0) { return 0; } int oldPointer = pointer; pointer = Math.min(pointer + len, length + offset); if (pointer == oldPointer) { return -1; } else { System.arraycopy(src, oldPointer, b, off, pointer - oldPointer); return pointer - oldPointer; } } /** * Attempts to skip over n bytes of input discarding the * skipped bytes. *

* * This method may skip over some smaller number of bytes, possibly zero. * This may result from any of a number of conditions; reaching end of * stream before n bytes have been skipped is only one * possibility. This method never throws an EOFException. * The actual number of bytes skipped is returned. If n * is negative, no bytes are skipped. * * @param n the number of bytes to be skipped. * @return the actual number of bytes skipped. */ public int skipBytes(int n) { int oldPointer = pointer; pointer = Math.min(pointer + n, length + offset); return pointer - oldPointer; } /** Does nothing. */ public void close() { } /** Returns the number of valid bytes in the input array. */ public long length() { return length; } /** Check to make sure that the stream has not been closed */ private void ensureOpen() { /* This method does nothing for now. Once we add throws clauses * to the I/O methods in this class, it will throw an IOException * if the stream has been closed. */ } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/ImageDecoderImpl.java0000644000175000017500000001217110203035544026663 0ustar mathieumathieu/* * $RCSfile: ImageDecoderImpl.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:30 $ * $State: Exp $ */ package com.sun.media.jai.codec; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.io.InputStream; import java.io.IOException; /** * A partial implementation of the ImageDecoder interface * useful for subclassing. * *

This class is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. */ public abstract class ImageDecoderImpl implements ImageDecoder { /** * The SeekableStream associcted with this * ImageEncoder. */ protected SeekableStream input; /** * The ImageDecodeParam object associated with this * ImageEncoder. */ protected ImageDecodeParam param; /** * Constructs an ImageDecoderImpl with a given * SeekableStream and ImageDecodeParam * instance. */ public ImageDecoderImpl(SeekableStream input, ImageDecodeParam param) { this.input = input; this.param = param; } /** * Constructs an ImageDecoderImpl with a given * InputStream and ImageDecodeParam * instance. The input parameter will be used to * construct a ForwardSeekableStream; if the ability * to seek backwards is required, the caller should construct * an instance of SeekableStream and * make use of the other contructor. */ public ImageDecoderImpl(InputStream input, ImageDecodeParam param) { this.input = new ForwardSeekableStream(input); this.param = param; } /** * Returns the current parameters as an instance of the * ImageDecodeParam interface. Concrete * implementations of this interface will return corresponding * concrete implementations of the ImageDecodeParam * interface. For example, a JPEGImageDecoder will * return an instance of JPEGDecodeParam. */ public ImageDecodeParam getParam() { return param; } /** * Sets the current parameters to an instance of the * ImageDecodeParam interface. Concrete * implementations of ImageDecoder may throw a * RuntimeException if the param * argument is not an instance of the appropriate subclass or * subinterface. For example, a JPEGImageDecoder * will expect param to be an instance of * JPEGDecodeParam. */ public void setParam(ImageDecodeParam param) { this.param = param; } /** * Returns the SeekableStream associated with * this ImageDecoder. */ public SeekableStream getInputStream() { return input; } /** * Returns the number of pages present in the current stream. * By default, the return value is 1. Subclasses that deal with * multi-page formats should override this method. */ public int getNumPages() throws IOException { return 1; } /** * Returns a Raster that contains the decoded * contents of the SeekableStream associated * with this ImageDecoder. Only * the first page of a multi-page image is decoded. */ public Raster decodeAsRaster() throws IOException { return decodeAsRaster(0); } /** * Returns a Raster that contains the decoded * contents of the SeekableStream associated * with this ImageDecoder. * The given page of a multi-page image is decoded. If * the page does not exist, an IOException will be thrown. * Page numbering begins at zero. * * @param page The page to be decoded. */ public Raster decodeAsRaster(int page) throws IOException { RenderedImage im = decodeAsRenderedImage(page); return im.getData(); } /** * Returns a RenderedImage that contains the decoded * contents of the SeekableStream associated * with this ImageDecoder. Only * the first page of a multi-page image is decoded. */ public RenderedImage decodeAsRenderedImage() throws IOException { return decodeAsRenderedImage(0); } /** * Returns a RenderedImage that contains the decoded * contents of the SeekableStream associated * with this ImageDecoder. * The given page of a multi-page image is decoded. If * the page does not exist, an IOException will be thrown. * Page numbering begins at zero. * * @param page The page to be decoded. */ public abstract RenderedImage decodeAsRenderedImage(int page) throws IOException; } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/TIFFDirectory.java0000644000175000017500000005145210313631404026152 0ustar mathieumathieu/* * $RCSfile: TIFFDirectory.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.3 $ * $Date: 2005-09-19 21:52:04 $ * $State: Exp $ */ package com.sun.media.jai.codec; import java.io.IOException; import java.io.EOFException; import java.io.Serializable; import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; /** * A class representing an Image File Directory (IFD) from a TIFF 6.0 * stream. The TIFF file format is described in more detail in the * comments for the TIFFDescriptor class. * *

A TIFF IFD consists of a set of TIFFField tags. Methods are * provided to query the set of tags and to obtain the raw field * array. In addition, convenience methods are provided for acquiring * the values of tags that contain a single value that fits into a * byte, int, long, float, or double. * *

Every TIFF file is made up of one or more public IFDs that are * joined in a linked list, rooted in the file header. A file may * also contain so-called private IFDs that are referenced from * tag data and do not appear in the main list. * *

This class is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. * * @see javax.media.jai.operator.TIFFDescriptor * @see TIFFField */ public class TIFFDirectory extends Object implements Serializable { /** A boolean storing the endianness of the stream. */ boolean isBigEndian; /** The number of entries in the IFD. */ int numEntries; /** An array of TIFFFields. */ TIFFField[] fields; /** A Hashtable indexing the fields by tag number. */ Hashtable fieldIndex = new Hashtable(); /** The offset of this IFD. */ long IFDOffset = 8; /** The offset of the next IFD. */ long nextIFDOffset = 0; /** The default constructor. */ TIFFDirectory() {} private static boolean isValidEndianTag(int endian) { return ((endian == 0x4949) || (endian == 0x4d4d)); } /** * Constructs a TIFFDirectory from a SeekableStream. * The directory parameter specifies which directory to read from * the linked list present in the stream; directory 0 is normally * read but it is possible to store multiple images in a single * TIFF file by maintaing multiple directories. * * @param stream a SeekableStream to read from. * @param directory the index of the directory to read. */ public TIFFDirectory(SeekableStream stream, int directory) throws IOException { long global_save_offset = stream.getFilePointer(); long ifd_offset; // Read the TIFF header stream.seek(0L); int endian = stream.readUnsignedShort(); if (!isValidEndianTag(endian)) { throw new IllegalArgumentException(JaiI18N.getString("TIFFDirectory1")); } isBigEndian = (endian == 0x4d4d); int magic = readUnsignedShort(stream); if (magic != 42) { throw new IllegalArgumentException(JaiI18N.getString("TIFFDirectory2")); } // Get the initial ifd offset as an unsigned int (using a long) ifd_offset = readUnsignedInt(stream); for (int i = 0; i < directory; i++) { if (ifd_offset == 0L) { throw new IllegalArgumentException(JaiI18N.getString("TIFFDirectory3")); } stream.seek(ifd_offset); int entries = readUnsignedShort(stream); stream.skip(12*entries); ifd_offset = readUnsignedInt(stream); } stream.seek(ifd_offset); initialize(stream); stream.seek(global_save_offset); } /** * Constructs a TIFFDirectory by reading a SeekableStream. * The ifd_offset parameter specifies the stream offset from which * to begin reading; this mechanism is sometimes used to store * private IFDs within a TIFF file that are not part of the normal * sequence of IFDs. * * @param stream a SeekableStream to read from. * @param ifd_offset the long byte offset of the directory. * @param directory the index of the directory to read beyond the * one at the current stream offset; zero indicates the IFD * at the current offset. */ public TIFFDirectory(SeekableStream stream, long ifd_offset, int directory) throws IOException { long global_save_offset = stream.getFilePointer(); stream.seek(0L); int endian = stream.readUnsignedShort(); if (!isValidEndianTag(endian)) { throw new IllegalArgumentException(JaiI18N.getString("TIFFDirectory1")); } isBigEndian = (endian == 0x4d4d); // Seek to the first IFD. stream.seek(ifd_offset); // Seek to desired IFD if necessary. int dirNum = 0; while(dirNum < directory) { // Get the number of fields in the current IFD. int numEntries = readUnsignedShort(stream); // Skip to the next IFD offset value field. stream.seek(ifd_offset + 12*numEntries); // Read the offset to the next IFD beyond this one. ifd_offset = readUnsignedInt(stream); // Seek to the next IFD. stream.seek(ifd_offset); // Increment the directory. dirNum++; } initialize(stream); stream.seek(global_save_offset); } private static final int[] sizeOfType = { 0, // 0 = n/a 1, // 1 = byte 1, // 2 = ascii 2, // 3 = short 4, // 4 = long 8, // 5 = rational 1, // 6 = sbyte 1, // 7 = undefined 2, // 8 = sshort 4, // 9 = slong 8, // 10 = srational 4, // 11 = float 8 // 12 = double }; private void initialize(SeekableStream stream) throws IOException { long nextTagOffset; int i, j; IFDOffset = stream.getFilePointer(); numEntries = readUnsignedShort(stream); fields = new TIFFField[numEntries]; for (i = 0; i < numEntries; i++) { int tag = readUnsignedShort(stream); int type = readUnsignedShort(stream); int count = (int)(readUnsignedInt(stream)); int value = 0; // The place to return to to read the next tag nextTagOffset = stream.getFilePointer() + 4; try { // If the tag data can't fit in 4 bytes, the next 4 bytes // contain the starting offset of the data if (count*sizeOfType[type] > 4) { value = (int)(readUnsignedInt(stream)); stream.seek(value); } } catch (ArrayIndexOutOfBoundsException ae) { System.err.println(tag + " " + JaiI18N.getString("TIFFDirectory4")); // if the data type is unknown we should skip this TIFF Field stream.seek(nextTagOffset); continue; } fieldIndex.put(new Integer(tag), new Integer(i)); Object obj = null; try { switch (type) { case TIFFField.TIFF_BYTE: case TIFFField.TIFF_SBYTE: case TIFFField.TIFF_UNDEFINED: case TIFFField.TIFF_ASCII: byte[] bvalues = new byte[count]; stream.readFully(bvalues, 0, count); if (type == TIFFField.TIFF_ASCII) { // Can be multiple strings int index = 0, prevIndex = 0; Vector v = new Vector(); while (index < count) { while ((index < count) && (bvalues[index++] != 0)); // When we encountered zero, means one string has ended v.add(new String(bvalues, prevIndex, (index - prevIndex)) ); prevIndex = index; } count = v.size(); String strings[] = new String[count]; for (int c = 0 ; c < count; c++) { strings[c] = (String)v.elementAt(c); } obj = strings; } else { obj = bvalues; } break; case TIFFField.TIFF_SHORT: char[] cvalues = new char[count]; for (j = 0; j < count; j++) { cvalues[j] = (char)(readUnsignedShort(stream)); } obj = cvalues; break; case TIFFField.TIFF_LONG: long[] lvalues = new long[count]; for (j = 0; j < count; j++) { lvalues[j] = readUnsignedInt(stream); } obj = lvalues; break; case TIFFField.TIFF_RATIONAL: long[][] llvalues = new long[count][2]; for (j = 0; j < count; j++) { llvalues[j][0] = readUnsignedInt(stream); llvalues[j][1] = readUnsignedInt(stream); } obj = llvalues; break; case TIFFField.TIFF_SSHORT: short[] svalues = new short[count]; for (j = 0; j < count; j++) { svalues[j] = readShort(stream); } obj = svalues; break; case TIFFField.TIFF_SLONG: int[] ivalues = new int[count]; for (j = 0; j < count; j++) { ivalues[j] = readInt(stream); } obj = ivalues; break; case TIFFField.TIFF_SRATIONAL: int[][] iivalues = new int[count][2]; for (j = 0; j < count; j++) { iivalues[j][0] = readInt(stream); iivalues[j][1] = readInt(stream); } obj = iivalues; break; case TIFFField.TIFF_FLOAT: float[] fvalues = new float[count]; for (j = 0; j < count; j++) { fvalues[j] = readFloat(stream); } obj = fvalues; break; case TIFFField.TIFF_DOUBLE: double[] dvalues = new double[count]; for (j = 0; j < count; j++) { dvalues[j] = readDouble(stream); } obj = dvalues; break; default: System.err.println(JaiI18N.getString("TIFFDirectory0")); break; } fields[i] = new TIFFField(tag, type, count, obj); } catch(EOFException eofe) { // The TIFF 6.0 fields have tag numbers less than or equal // to 532 (ReferenceBlackWhite) or equal to 33432 (Copyright). // If there is an error reading a baseline tag, then re-throw // the exception and fail; otherwise continue with the next // field. if(tag <= 532 || tag == 33432) { throw eofe; } fieldIndex.remove(new Integer(tag)); // XXX Warning message } stream.seek(nextTagOffset); } // Read the offset of the next IFD. nextIFDOffset = readUnsignedInt(stream); } /** Returns the number of directory entries. */ public int getNumEntries() { return numEntries; } /** * Returns the value of a given tag as a TIFFField, * or null if the tag is not present. * *

Note that the value (data) of the TIFFField * will always be the actual field value regardless of the number of * bytes required for that value. This is the case despite the fact * that the TIFF IFD Entry corresponding to the field may * actually contain the offset to the field's value rather than * the value itself (the latter occurring if and only if the * value fits into 4 bytes). In other words, the value of the * field will already have been read from the TIFF stream.

*/ public TIFFField getField(int tag) { Integer i = (Integer)fieldIndex.get(new Integer(tag)); if (i == null) { return null; } else { return fields[i.intValue()]; } } /** * Returns true if a tag appears in the directory. */ public boolean isTagPresent(int tag) { return fieldIndex.containsKey(new Integer(tag)); } /** * Returns an ordered array of ints indicating the tag * values. */ public int[] getTags() { int[] tags = new int[fieldIndex.size()]; Enumeration enumeration = fieldIndex.keys(); int i = 0; while (enumeration.hasMoreElements()) { tags[i++] = ((Integer)enumeration.nextElement()).intValue(); } return tags; } /** * Returns an array of TIFFFields containing all the fields * in this directory. */ public TIFFField[] getFields() { return fields; } /** * Returns the value of a particular index of a given tag as a * byte. The caller is responsible for ensuring that the tag is * present and has type TIFFField.TIFF_SBYTE, TIFF_BYTE, or * TIFF_UNDEFINED. */ public byte getFieldAsByte(int tag, int index) { Integer i = (Integer)fieldIndex.get(new Integer(tag)); byte [] b = (fields[i.intValue()]).getAsBytes(); return b[index]; } /** * Returns the value of index 0 of a given tag as a * byte. The caller is responsible for ensuring that the tag is * present and has type TIFFField.TIFF_SBYTE, TIFF_BYTE, or * TIFF_UNDEFINED. */ public byte getFieldAsByte(int tag) { return getFieldAsByte(tag, 0); } /** * Returns the value of a particular index of a given tag as a * long. The caller is responsible for ensuring that the tag is * present and has type TIFF_BYTE, TIFF_SBYTE, TIFF_UNDEFINED, * TIFF_SHORT, TIFF_SSHORT, TIFF_SLONG or TIFF_LONG. */ public long getFieldAsLong(int tag, int index) { Integer i = (Integer)fieldIndex.get(new Integer(tag)); return (fields[i.intValue()]).getAsLong(index); } /** * Returns the value of index 0 of a given tag as a * long. The caller is responsible for ensuring that the tag is * present and has type TIFF_BYTE, TIFF_SBYTE, TIFF_UNDEFINED, * TIFF_SHORT, TIFF_SSHORT, TIFF_SLONG or TIFF_LONG. */ public long getFieldAsLong(int tag) { return getFieldAsLong(tag, 0); } /** * Returns the value of a particular index of a given tag as a * float. The caller is responsible for ensuring that the tag is * present and has numeric type (all but TIFF_UNDEFINED and * TIFF_ASCII). */ public float getFieldAsFloat(int tag, int index) { Integer i = (Integer)fieldIndex.get(new Integer(tag)); return fields[i.intValue()].getAsFloat(index); } /** * Returns the value of index 0 of a given tag as a float. The * caller is responsible for ensuring that the tag is present and * has numeric type (all but TIFF_UNDEFINED and TIFF_ASCII). */ public float getFieldAsFloat(int tag) { return getFieldAsFloat(tag, 0); } /** * Returns the value of a particular index of a given tag as a * double. The caller is responsible for ensuring that the tag is * present and has numeric type (all but TIFF_UNDEFINED and * TIFF_ASCII). */ public double getFieldAsDouble(int tag, int index) { Integer i = (Integer)fieldIndex.get(new Integer(tag)); return fields[i.intValue()].getAsDouble(index); } /** * Returns the value of index 0 of a given tag as a double. The * caller is responsible for ensuring that the tag is present and * has numeric type (all but TIFF_UNDEFINED and TIFF_ASCII). */ public double getFieldAsDouble(int tag) { return getFieldAsDouble(tag, 0); } // Methods to read primitive data types from the stream private short readShort(SeekableStream stream) throws IOException { if (isBigEndian) { return stream.readShort(); } else { return stream.readShortLE(); } } private int readUnsignedShort(SeekableStream stream) throws IOException { if (isBigEndian) { return stream.readUnsignedShort(); } else { return stream.readUnsignedShortLE(); } } private int readInt(SeekableStream stream) throws IOException { if (isBigEndian) { return stream.readInt(); } else { return stream.readIntLE(); } } private long readUnsignedInt(SeekableStream stream) throws IOException { if (isBigEndian) { return stream.readUnsignedInt(); } else { return stream.readUnsignedIntLE(); } } private long readLong(SeekableStream stream) throws IOException { if (isBigEndian) { return stream.readLong(); } else { return stream.readLongLE(); } } private float readFloat(SeekableStream stream) throws IOException { if (isBigEndian) { return stream.readFloat(); } else { return stream.readFloatLE(); } } private double readDouble(SeekableStream stream) throws IOException { if (isBigEndian) { return stream.readDouble(); } else { return stream.readDoubleLE(); } } private static int readUnsignedShort(SeekableStream stream, boolean isBigEndian) throws IOException { if (isBigEndian) { return stream.readUnsignedShort(); } else { return stream.readUnsignedShortLE(); } } private static long readUnsignedInt(SeekableStream stream, boolean isBigEndian) throws IOException { if (isBigEndian) { return stream.readUnsignedInt(); } else { return stream.readUnsignedIntLE(); } } // Utilities /** * Returns the number of image directories (subimages) stored in a * given TIFF file, represented by a SeekableStream. */ public static int getNumDirectories(SeekableStream stream) throws IOException{ long pointer = stream.getFilePointer(); // Save stream pointer stream.seek(0L); int endian = stream.readUnsignedShort(); if (!isValidEndianTag(endian)) { throw new IllegalArgumentException(JaiI18N.getString("TIFFDirectory1")); } boolean isBigEndian = (endian == 0x4d4d); int magic = readUnsignedShort(stream, isBigEndian); if (magic != 42) { throw new IllegalArgumentException(JaiI18N.getString("TIFFDirectory2")); } stream.seek(4L); long offset = readUnsignedInt(stream, isBigEndian); int numDirectories = 0; while (offset != 0L) { ++numDirectories; // EOFException means IFD was probably not properly terminated. try { stream.seek(offset); int entries = readUnsignedShort(stream, isBigEndian); stream.skip(12*entries); offset = readUnsignedInt(stream, isBigEndian); } catch(EOFException eof) { numDirectories--; break; } } stream.seek(pointer); // Reset stream pointer return numDirectories; } /** * Returns a boolean indicating whether the byte order used in the * the TIFF file is big-endian (i.e. whether the byte order is from * the most significant to the least significant) */ public boolean isBigEndian() { return isBigEndian; } /** * Returns the offset of the IFD corresponding to this * TIFFDirectory. */ public long getIFDOffset() { return IFDOffset; } /** * Returns the offset of the next IFD after the IFD corresponding to this * TIFFDirectory. */ public long getNextIFDOffset() { return nextIFDOffset; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/ImageDecoder.java0000644000175000017500000000573110203035544026045 0ustar mathieumathieu/* * $RCSfile: ImageDecoder.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:30 $ * $State: Exp $ */ package com.sun.media.jai.codec; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.io.InputStream; import java.io.IOException; /** * An interface describing objects that transform an InputStream into a * BufferedImage or Raster. * *

This interface is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. */ public interface ImageDecoder { /** * Returns the current parameters as an instance of the * ImageDecodeParam interface. Concrete implementations of this * interface will return corresponding concrete implementations of * the ImageDecodeParam interface. For example, a JPEGImageDecoder * will return an instance of JPEGDecodeParam. */ ImageDecodeParam getParam(); /** * Sets the current parameters to an instance of the * ImageDecodeParam interface. Concrete implementations * of ImageDecoder may throw a RuntimeException if the * param argument is not an instance of the appropriate * subclass or subinterface. For example, a JPEGImageDecoder * will expect param to be an instance of JPEGDecodeParam. */ void setParam(ImageDecodeParam param); /** Returns the SeekableStream associated with this ImageDecoder. */ SeekableStream getInputStream(); /** Returns the number of pages present in the current stream. */ int getNumPages() throws IOException; /** * Returns a Raster that contains the decoded contents of the * SeekableStream associated with this ImageDecoder. Only * the first page of a multi-page image is decoded. */ Raster decodeAsRaster() throws IOException; /** * Returns a Raster that contains the decoded contents of the * SeekableStream associated with this ImageDecoder. * The given page of a multi-page image is decoded. If * the page does not exist, an IOException will be thrown. * Page numbering begins at zero. * * @param page The page to be decoded. */ Raster decodeAsRaster(int page) throws IOException; /** * Returns a RenderedImage that contains the decoded contents of the * SeekableStream associated with this ImageDecoder. Only * the first page of a multi-page image is decoded. */ RenderedImage decodeAsRenderedImage() throws IOException; /** * Returns a RenderedImage that contains the decoded contents of the * SeekableStream associated with this ImageDecoder. * The given page of a multi-page image is decoded. If * the page does not exist, an IOException will be thrown. * Page numbering begins at zero. * * @param page The page to be decoded. */ RenderedImage decodeAsRenderedImage(int page) throws IOException; } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/StreamSegment.java0000644000175000017500000000315110203035544026305 0ustar mathieumathieu/* * $RCSfile: StreamSegment.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:33 $ * $State: Exp $ */ package com.sun.media.jai.codec; /** * A utility class representing a segment within a stream as a * long starting position and an int * length. * *

This class is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. */ public class StreamSegment { private long startPos = 0L; private int segmentLength = 0; /** * Constructs a StreamSegment. * The starting position and length are set to 0. */ public StreamSegment() {} /** * Constructs a StreamSegment with a * given starting position and length. */ public StreamSegment(long startPos, int segmentLength) { this.startPos = startPos; this.segmentLength = segmentLength; } /** * Returns the starting position of the segment. */ public final long getStartPos() { return startPos; } /** * Sets the starting position of the segment. */ public final void setStartPos(long startPos) { this.startPos = startPos; } /** * Returns the length of the segment. */ public final int getSegmentLength() { return segmentLength; } /** * Sets the length of the segment. */ public final void setSegmentLength(int segmentLength) { this.segmentLength = segmentLength; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/JPEGDecodeParam.java0000644000175000017500000000413010203035544026337 0ustar mathieumathieu/* * $RCSfile: JPEGDecodeParam.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:31 $ * $State: Exp $ */ package com.sun.media.jai.codec; /** * An instance of ImageDecodeParam for decoding images in * the JPEG format. * *

This class allows for the specification of whether to decode the * JPEG data into an image having a SampleModel which is a * ComponentSampleModel or a subclass thereof. By default * data are decoded into an image having a ComponentSampleModel. * *

This class is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. */ public class JPEGDecodeParam implements ImageDecodeParam { /** * Flag indicating whether to decode the data into an image with * a ComponentSampleModel. */ private boolean decodeToCSM = true; /** * Constructs a JPEGDecodeParam object with default * parameter values. */ public JPEGDecodeParam() { } /** * Sets the data formatting flag to the value of the supplied parameter. * If true the data will be decoded into an image which has * a SampleModel which is a ComponentSampleModel. * The default setting of this flag is true. If the flag is * set to false then memory may be saved during decoding but * the resulting image is not in that case guaranteed to have a * ComponentSampleModel. * * @param decodeToCSM true if a * ComponentSampleModel layout is preferred for the * decoded image. */ public void setDecodeToCSM(boolean decodeToCSM) { this.decodeToCSM = decodeToCSM; } /** * Returns the value of the ComponentSampleModel flag * which is by default true. */ public boolean getDecodeToCSM() { return decodeToCSM; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/PNMEncodeParam.java0000644000175000017500000000251510203035544026263 0ustar mathieumathieu/* * $RCSfile: PNMEncodeParam.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:32 $ * $State: Exp $ */ package com.sun.media.jai.codec; /** * An instance of ImageEncodeParam for encoding images in * the PNM format. * *

This class allows for the specification of whether to encode * in the ASCII or raw variants of the PBM, PGM, and PPM formats. * By default, raw encoding is used. * *

This class is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. */ public class PNMEncodeParam implements ImageEncodeParam { private boolean raw = true; /** * Constructs a PNMEncodeParam object with default values for parameters. */ public PNMEncodeParam() { } /** * Sets the representation to be used. If the raw * parameter is true, raw encoding will be used; * otherwise ASCII encoding will be used. * * @param raw true if raw format is to be used. */ public void setRaw(boolean raw) { this.raw = raw; } /** * Returns the value of the raw parameter. */ public boolean getRaw() { return raw; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/TIFFField.java0000644000175000017500000003360210203035544025227 0ustar mathieumathieu/* * $RCSfile: TIFFField.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:34 $ * $State: Exp $ */ package com.sun.media.jai.codec; import java.io.Serializable; /** * A class representing a field in a TIFF 6.0 Image File Directory. * *

The TIFF file format is described in more detail in the * comments for the TIFFDescriptor class. * *

A field in a TIFF Image File Directory (IFD). A field is defined * as a sequence of values of identical data type. TIFF 6.0 defines * 12 data types, which are mapped internally onto the Java datatypes * byte, int, long, float, and double. * *

This class is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. * * @see javax.media.jai.operator.TIFFDescriptor * @see TIFFDirectory */ public class TIFFField extends Object implements Comparable, Serializable { /** Flag for 8 bit unsigned integers. */ public static final int TIFF_BYTE = 1; /** Flag for null-terminated ASCII strings. */ public static final int TIFF_ASCII = 2; /** Flag for 16 bit unsigned integers. */ public static final int TIFF_SHORT = 3; /** Flag for 32 bit unsigned integers. */ public static final int TIFF_LONG = 4; /** Flag for pairs of 32 bit unsigned integers. */ public static final int TIFF_RATIONAL = 5; /** Flag for 8 bit signed integers. */ public static final int TIFF_SBYTE = 6; /** Flag for 8 bit uninterpreted bytes. */ public static final int TIFF_UNDEFINED = 7; /** Flag for 16 bit signed integers. */ public static final int TIFF_SSHORT = 8; /** Flag for 32 bit signed integers. */ public static final int TIFF_SLONG = 9; /** Flag for pairs of 32 bit signed integers. */ public static final int TIFF_SRATIONAL = 10; /** Flag for 32 bit IEEE floats. */ public static final int TIFF_FLOAT = 11; /** Flag for 64 bit IEEE doubles. */ public static final int TIFF_DOUBLE = 12; /** The tag number. */ int tag; /** The tag type. */ int type; /** The number of data items present in the field. */ int count; /** The field data. */ Object data; /** The default constructor. */ TIFFField() {} /** * Constructs a TIFFField with arbitrary data. The data * parameter must be an array of a Java type appropriate for the * type of the TIFF field. Since there is no available 32-bit * unsigned datatype, long is used. The mapping between types is * as follows: * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
TIFF type Java type
TIFF_BYTE byte
TIFF_ASCII String
TIFF_SHORT char
TIFF_LONG long
TIFF_RATIONAL long[2]
TIFF_SBYTE byte
TIFF_UNDEFINED byte
TIFF_SSHORT short
TIFF_SLONG int
TIFF_SRATIONAL int[2]
TIFF_FLOAT float
TIFF_DOUBLE double
* *

Note that the data parameter should always * be the actual field value regardless of the number of bytes * required for that value. This is the case despite the fact * that the TIFF IFD Entry corresponding to the field may * actually contain the offset to the field's value rather than * the value itself (the latter occurring if and only if the * value fits into 4 bytes).

*/ public TIFFField(int tag, int type, int count, Object data) { this.tag = tag; this.type = type; this.count = count; this.data = data; } /** * Returns the tag number, between 0 and 65535. */ public int getTag() { return tag; } /** * Returns the type of the data stored in the field. * For a TIFF6.0 file, the value will equal one of the * TIFF_ constants defined in this class. For future * revisions of TIFF, higher values are possible. * */ public int getType() { return type; } /** * Returns the number of data items present in the field. */ public int getCount() { return count; } /** * Returns the data as an uninterpreted array of bytes. * The type of the field must be one of TIFF_BYTE, TIFF_SBYTE, * or TIFF_UNDEFINED; * *

For data in TIFF_BYTE format, the application must take * care when promoting the data to longer integral types * to avoid sign extension. * *

A ClassCastException will be thrown if the field is not * of type TIFF_BYTE, TIFF_SBYTE, or TIFF_UNDEFINED. */ public byte[] getAsBytes() { return (byte[])data; } /** * Returns TIFF_SHORT data as an array of chars (unsigned 16-bit * integers). * *

A ClassCastException will be thrown if the field is not * of type TIFF_SHORT. */ public char[] getAsChars() { return (char[])data; } /** * Returns TIFF_SSHORT data as an array of shorts (signed 16-bit * integers). * *

A ClassCastException will be thrown if the field is not * of type TIFF_SSHORT. */ public short[] getAsShorts() { return (short[])data; } /** * Returns TIFF_SLONG data as an array of ints (signed 32-bit * integers). * *

A ClassCastException will be thrown if the field is not * of type TIFF_SLONG. */ public int[] getAsInts() { return (int[])data; } /** * Returns TIFF_LONG data as an array of longs (signed 64-bit * integers). * *

A ClassCastException will be thrown if the field is not * of type TIFF_LONG. */ public long[] getAsLongs() { return (long[])data; } /** * Returns TIFF_FLOAT data as an array of floats. * *

A ClassCastException will be thrown if the field is not * of type TIFF_FLOAT. */ public float[] getAsFloats() { return (float[])data; } /** * Returns TIFF_DOUBLE data as an array of doubles. * *

A ClassCastException will be thrown if the field is not * of type TIFF_DOUBLE. */ public double[] getAsDoubles() { return (double[])data; } /** * Returns TIFF_SRATIONAL data as an array of 2-element arrays of ints. * *

A ClassCastException will be thrown if the field is not * of type TIFF_SRATIONAL. */ public int[][] getAsSRationals() { return (int[][])data; } /** * Returns TIFF_RATIONAL data as an array of 2-element arrays of longs. * *

A ClassCastException will be thrown if the field is not * of type TIFF_RATTIONAL. */ public long[][] getAsRationals() { return (long[][])data; } /** * Returns data in TIFF_BYTE, TIFF_SBYTE, TIFF_UNDEFINED, TIFF_SHORT, * TIFF_SSHORT, or TIFF_SLONG format as an int. * *

TIFF_BYTE and TIFF_UNDEFINED data are treated as unsigned; * that is, no sign extension will take place and the returned * value will be in the range [0, 255]. TIFF_SBYTE data will * be returned in the range [-128, 127]. * *

A ClassCastException will be thrown if the field is not of * type TIFF_BYTE, TIFF_SBYTE, TIFF_UNDEFINED, TIFF_SHORT, * TIFF_SSHORT, or TIFF_SLONG. */ public int getAsInt(int index) { switch (type) { case TIFF_BYTE: case TIFF_UNDEFINED: return ((byte[])data)[index] & 0xff; case TIFF_SBYTE: return ((byte[])data)[index]; case TIFF_SHORT: return ((char[])data)[index] & 0xffff; case TIFF_SSHORT: return ((short[])data)[index]; case TIFF_SLONG: return ((int[])data)[index]; default: throw new ClassCastException(); } } /** * Returns data in TIFF_BYTE, TIFF_SBYTE, TIFF_UNDEFINED, TIFF_SHORT, * TIFF_SSHORT, TIFF_SLONG, or TIFF_LONG format as a long. * *

TIFF_BYTE and TIFF_UNDEFINED data are treated as unsigned; * that is, no sign extension will take place and the returned * value will be in the range [0, 255]. TIFF_SBYTE data will * be returned in the range [-128, 127]. * *

A ClassCastException will be thrown if the field is not of * type TIFF_BYTE, TIFF_SBYTE, TIFF_UNDEFINED, TIFF_SHORT, * TIFF_SSHORT, TIFF_SLONG, or TIFF_LONG. */ public long getAsLong(int index) { switch (type) { case TIFF_BYTE: case TIFF_UNDEFINED: return ((byte[])data)[index] & 0xff; case TIFF_SBYTE: return ((byte[])data)[index]; case TIFF_SHORT: return ((char[])data)[index] & 0xffff; case TIFF_SSHORT: return ((short[])data)[index]; case TIFF_SLONG: return ((int[])data)[index]; case TIFF_LONG: return ((long[])data)[index]; default: throw new ClassCastException(); } } /** * Returns data in any numerical format as a float. Data in * TIFF_SRATIONAL or TIFF_RATIONAL format are evaluated by * dividing the numerator into the denominator using * double-precision arithmetic and then truncating to single * precision. Data in TIFF_SLONG, TIFF_LONG, or TIFF_DOUBLE * format may suffer from truncation. * *

A ClassCastException will be thrown if the field is * of type TIFF_UNDEFINED or TIFF_ASCII. */ public float getAsFloat(int index) { switch (type) { case TIFF_BYTE: return ((byte[])data)[index] & 0xff; case TIFF_SBYTE: return ((byte[])data)[index]; case TIFF_SHORT: return ((char[])data)[index] & 0xffff; case TIFF_SSHORT: return ((short[])data)[index]; case TIFF_SLONG: return ((int[])data)[index]; case TIFF_LONG: return ((long[])data)[index]; case TIFF_FLOAT: return ((float[])data)[index]; case TIFF_DOUBLE: return (float)((double[])data)[index]; case TIFF_SRATIONAL: int[] ivalue = getAsSRational(index); return (float)((double)ivalue[0]/ivalue[1]); case TIFF_RATIONAL: long[] lvalue = getAsRational(index); return (float)((double)lvalue[0]/lvalue[1]); default: throw new ClassCastException(); } } /** * Returns data in any numerical format as a float. Data in * TIFF_SRATIONAL or TIFF_RATIONAL format are evaluated by * dividing the numerator into the denominator using * double-precision arithmetic. * *

A ClassCastException will be thrown if the field is of * type TIFF_UNDEFINED or TIFF_ASCII. */ public double getAsDouble(int index) { switch (type) { case TIFF_BYTE: return ((byte[])data)[index] & 0xff; case TIFF_SBYTE: return ((byte[])data)[index]; case TIFF_SHORT: return ((char[])data)[index] & 0xffff; case TIFF_SSHORT: return ((short[])data)[index]; case TIFF_SLONG: return ((int[])data)[index]; case TIFF_LONG: return ((long[])data)[index]; case TIFF_FLOAT: return ((float[])data)[index]; case TIFF_DOUBLE: return ((double[])data)[index]; case TIFF_SRATIONAL: int[] ivalue = getAsSRational(index); return (double)ivalue[0]/ivalue[1]; case TIFF_RATIONAL: long[] lvalue = getAsRational(index); return (double)lvalue[0]/lvalue[1]; default: throw new ClassCastException(); } } /** * Returns a TIFF_ASCII data item as a String. * *

A ClassCastException will be thrown if the field is not * of type TIFF_ASCII. */ public String getAsString(int index) { return ((String[])data)[index]; } /** * Returns a TIFF_SRATIONAL data item as a two-element array * of ints. * *

A ClassCastException will be thrown if the field is not * of type TIFF_SRATIONAL. */ public int[] getAsSRational(int index) { return ((int[][])data)[index]; } /** * Returns a TIFF_RATIONAL data item as a two-element array * of ints. * *

A ClassCastException will be thrown if the field is not * of type TIFF_RATIONAL. */ public long[] getAsRational(int index) { return ((long[][])data)[index]; } /** * Compares this TIFFField with another * TIFFField by comparing the tags. * *

Note: this class has a natural ordering that is inconsistent * with equals(). * * @throws IllegalArgumentException if the parameter is null. * @throws ClassCastException if the parameter is not a * TIFFField. */ public int compareTo(Object o) { if(o == null) { throw new IllegalArgumentException(); } int oTag = ((TIFFField)o).getTag(); if(tag < oTag) { return -1; } else if(tag > oTag) { return 1; } else { return 0; } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/SegmentedSeekableStream.java0000644000175000017500000003743110203035544030262 0ustar mathieumathieu/* * $RCSfile: SegmentedSeekableStream.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:33 $ * $State: Exp $ */ package com.sun.media.jai.codec; import java.io.IOException; /** * An implementation of the StreamSegmentMapper interface * that requires an explicit list of the starting locations and * lengths of the source segments. */ class StreamSegmentMapperImpl implements StreamSegmentMapper { private long[] segmentPositions; private int[] segmentLengths; public StreamSegmentMapperImpl(long[] segmentPositions, int[] segmentLengths) { this.segmentPositions = (long[])segmentPositions.clone(); this.segmentLengths = (int[])segmentLengths.clone(); } public StreamSegment getStreamSegment(long position, int length) { int numSegments = segmentLengths.length; for (int i = 0; i < numSegments; i++) { int len = segmentLengths[i]; if (position < len) { return new StreamSegment(segmentPositions[i] + position, Math.min(len - (int)position, length)); } position -= len; } return null; } public void getStreamSegment(long position, int length, StreamSegment seg) { int numSegments = segmentLengths.length; for (int i = 0; i < numSegments; i++) { int len = segmentLengths[i]; if (position < len) { seg.setStartPos(segmentPositions[i] + position); seg.setSegmentLength(Math.min(len - (int)position, length)); return; } position -= len; } seg.setStartPos(-1); seg.setSegmentLength(-1); return; } } /** * An implementation of the StreamSegmentMapper interface * for segments of equal length. */ class SectorStreamSegmentMapper implements StreamSegmentMapper { long[] segmentPositions; int segmentLength; int totalLength; int lastSegmentLength; public SectorStreamSegmentMapper(long[] segmentPositions, int segmentLength, int totalLength) { this.segmentPositions = (long[])segmentPositions.clone(); this.segmentLength = segmentLength; this.totalLength = totalLength; this.lastSegmentLength = totalLength - (segmentPositions.length - 1)*segmentLength; } public StreamSegment getStreamSegment(long position, int length) { int index = (int) (position/segmentLength); // Compute segment length int len = (index == segmentPositions.length - 1) ? lastSegmentLength : segmentLength; // Compute position within the segment position -= index*segmentLength; // Compute maximum legal length len -= position; if (len > length) { len = length; } return new StreamSegment(segmentPositions[index] + position, len); } public void getStreamSegment(long position, int length, StreamSegment seg) { int index = (int) (position/segmentLength); // Compute segment length int len = (index == segmentPositions.length - 1) ? lastSegmentLength : segmentLength; // Compute position within the segment position -= index*segmentLength; // Compute maximum legal length len -= position; if (len > length) { len = length; } seg.setStartPos(segmentPositions[index] + position); seg.setSegmentLength(len); } } /** * A SegmentedSeekableStream provides a view of a * subset of another SeekableStream consiting of a series * of segments with given starting positions in the source stream and * lengths. The resulting stream behaves like an ordinary * SeekableStream. * *

For example, given a SeekableStream containing * data in a format consisting of a number of sub-streams stored in * non-contiguous sectors indexed by a directory, it is possible to * construct a set of SegmentedSeekableStreams, one for * each sub-stream, that each provide a view of the sectors comprising * a particular stream by providing the positions and lengths of the * stream's sectors as indicated by the directory. The complex * multi-stream structure of the original stream may be ignored by * users of the SegmentedSeekableStream, who see a * separate SeekableStream for each sub-stream and do not * need to understand the directory structure at all. * *

For further efficiency, a directory structure such as in the * example described above need not be fully parsed in order to build * a SegmentedSeekableStream. Instead, the * StreamSegmentMapper interface allows the association * between a desired region of the output and an input segment to be * provided dynamically. This mapping might be computed by reading * from a directory in piecemeal fashion in order to avoid consuming * memory resources. * *

It is the responsibility of the user of this class to determine * whether backwards seeking should be enabled. If the source stream * supports only forward seeking, backwards seeking must be disabled * and the StreamSegmentMapper must be monotone; that is, * forward motion in the destination must always result in forward * motion within the source. If the source stream supports backwards * seeking, there are no restrictions on the * StreamSegmentMapper and backwards seeking may always * be enabled for the SegmentedSeekableStream. * *

This class is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. */ public class SegmentedSeekableStream extends SeekableStream { private SeekableStream stream; private StreamSegmentMapper mapper; private long pointer = 0; private boolean canSeekBackwards; /** * Constructs a SegmentedSeekableStream * given a SeekableStream as input, * an instance of StreamSegmentMapper, * and a boolean indicating whether the * output SegmentedSeekableStream should * support seeking backwards. If canSeekBackwards * is true, the source stream must itself * support seeking backwards. * * @param stream A source SeekableStream * @param mapper An instance of the StreamSegmentMapper * interface. * @param canSeekBackwards true if the ability to * seek backwards is desired. */ public SegmentedSeekableStream(SeekableStream stream, StreamSegmentMapper mapper, boolean canSeekBackwards) { this.stream = stream; this.mapper = mapper; this.canSeekBackwards = canSeekBackwards; if (canSeekBackwards && !stream.canSeekBackwards()) { throw new IllegalArgumentException(JaiI18N.getString("SegmentedSeekableStream0")); } } /** * Constructs a SegmentedSeekableStream given a * SeekableStream as input, a list of the starting * positions and lengths of the segments of the source stream, and * a boolean indicating whether the output * SegmentedSeekableStream should support seeking * backwards. If canSeekBakckwards is * true, the source stream must itself support * seeking backwards. * * @param stream A source SeekableStream * @param segmentPositions An array of longs * giving the starting positions of the segments in the * source stream. * @param segmentLengths An array of ints * giving the lengths of segments in the source stream. * @param canSeekBackwards true if the ability to * seek backwards is desired. */ public SegmentedSeekableStream(SeekableStream stream, long[] segmentPositions, int[] segmentLengths, boolean canSeekBackwards) { this(stream, new StreamSegmentMapperImpl(segmentPositions, segmentLengths), canSeekBackwards); } /** * Constructs a SegmentedSeekableStream given a * SeekableStream as input, a list of the starting * positions of the segments of the source stream, the common * length of each segment, the total length of the segments and * a boolean indicating whether the output * SegmentedSeekableStream should support seeking * backwards. If canSeekBakckwards is * true, the source stream must itself support * seeking backwards. * *

This constructor is useful for selecting substreams * of sector-oriented file formats in which each segment * of the substream (except possibly the final segment) * occupies a fixed-length sector. * * @param stream A source SeekableStream * @param segmentPositions An array of longs * giving the starting positions of the segments in the * source stream. * @param segmentLength The common length of each segment. * @param totalLength The total length of the source segments. * @param canSeekBackwards true if the ability to * seek backwards is desired. */ public SegmentedSeekableStream(SeekableStream stream, long[] segmentPositions, int segmentLength, int totalLength, boolean canSeekBackwards) { this(stream, new SectorStreamSegmentMapper(segmentPositions, segmentLength, totalLength), canSeekBackwards); } /** * Returns the current offset in this stream. * * @return the offset from the beginning of the stream, in bytes, * at which the next read occurs. */ public long getFilePointer() { return (long)pointer; } /** * Returns true if seeking backwards is supported. * Support is determined by the value of the * canSeekBackwards parameter at construction time. */ public boolean canSeekBackwards() { return canSeekBackwards; } /** * Sets the offset, measured from the beginning of this * stream, at which the next read occurs. * *

If canSeekBackwards() returns false, * then setting pos to an offset smaller than * the current value of getFilePointer() will have * no effect. * * @param pos the offset position, measured in bytes from the * beginning of the stream, at which to set the stream * pointer. * @exception IOException if pos is less than * 0 or if an I/O error occurs. */ public void seek(long pos) throws IOException { if (pos < 0) { throw new IOException(); } pointer = pos; } private StreamSegment streamSegment = new StreamSegment(); /** * Reads the next byte of data from the input stream. The value byte is * returned as an int in the range 0 to * 255. If no byte is available because the end of the stream * has been reached, the value -1 is returned. This method * blocks until input data is available, the end of the stream is detected, * or an exception is thrown. * * @return the next byte of data, or -1 if the end of the * stream is reached. * @exception IOException if an I/O error occurs. */ public int read() throws IOException { mapper.getStreamSegment(pointer, 1, streamSegment); stream.seek(streamSegment.getStartPos()); int val = stream.read(); ++pointer; return val; } /** * Reads up to len bytes of data from the input stream into * an array of bytes. An attempt is made to read as many as * len bytes, but a smaller number may be read, possibly * zero. The number of bytes actually read is returned as an integer. * *

This method blocks until input data is available, end of stream is * detected, or an exception is thrown. * *

If b is null, a * NullPointerException is thrown. * *

If off is negative, or len is negative, or * off+len is greater than the length of the array * b, then an IndexOutOfBoundsException is * thrown. * *

If len is zero, then no bytes are read and * 0 is returned; otherwise, there is an attempt to read at * least one byte. If no byte is available because the stream is at end of * stream, the value -1 is returned; otherwise, at least one * byte is read and stored into b. * *

The first byte read is stored into element b[off], the * next one into b[off+1], and so on. The number of bytes read * is, at most, equal to len. Let k be the number of * bytes actually read; these bytes will be stored in elements * b[off] through b[off+k-1], * leaving elements b[off+k] through * b[off+len-1] unaffected. * *

In every case, elements b[0] through * b[off] and elements b[off+len] through * b[b.length-1] are unaffected. * *

If the first byte cannot be read for any reason other than end of * stream, then an IOException is thrown. In particular, an * IOException is thrown if the input stream has been closed. * * @param b the buffer into which the data is read. * @param off the start offset in array b * at which the data is written. * @param len the maximum number of bytes to read. * @return the total number of bytes read into the buffer, or * -1 if there is no more data because the end of * the stream has been reached. * @exception IOException if an I/O error occurs. */ public int read(byte[] b, int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); } if ((off < 0) || (len < 0) || (off + len > b.length)) { throw new IndexOutOfBoundsException(); } if (len == 0) { return 0; } mapper.getStreamSegment(pointer, len, streamSegment); stream.seek(streamSegment.getStartPos()); int nbytes = stream.read(b, off, streamSegment.getSegmentLength()); pointer += nbytes; return nbytes; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/FPXDecodeParam.java0000644000175000017500000000244010203035544026251 0ustar mathieumathieu/* * $RCSfile: FPXDecodeParam.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:29 $ * $State: Exp $ */ package com.sun.media.jai.codec; /** * An instance of ImageDecodeParam for decoding images * in the FlashPIX format. * *

This class is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. */ public class FPXDecodeParam implements ImageDecodeParam { private int resolution = -1; /** Constructs a default instance of FPXDecodeParam. */ public FPXDecodeParam() {} /** * Constructs an instance of FPXDecodeParam * to decode a given resolution. * * @param resolution The resolution number to be decoded. */ public FPXDecodeParam(int resolution) { this.resolution = resolution; } /** * Sets the resolution to be decoded. * * @param resolution The resolution number to be decoded. */ public void setResolution(int resolution) { this.resolution = resolution; } /** * Returns the resolution to be decoded. */ public int getResolution() { return resolution; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/JaiI18N.java0000644000175000017500000000075710203035544024643 0ustar mathieumathieu/* * $RCSfile: JaiI18N.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:31 $ * $State: Exp $ */ package com.sun.media.jai.codec; import com.sun.media.jai.codecimpl.util.PropertyUtil; class JaiI18N { static String packageName = "com.sun.media.jai.codec"; public static String getString(String key) { return PropertyUtil.getString(packageName, key); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/MemoryCacheSeekableStream.java0000644000175000017500000002152710336751442030553 0ustar mathieumathieu/* * $RCSfile: MemoryCacheSeekableStream.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-11-17 00:47:30 $ * $State: Exp $ */ package com.sun.media.jai.codec; import java.io.InputStream; import java.io.IOException; import java.util.Vector; /** * A subclass of SeekableStream that may be used to wrap * a regular InputStream. Seeking backwards is supported * by means of an in-memory cache. For greater efficiency, * FileCacheSeekableStream should be used in * circumstances that allow the creation of a temporary file. * *

The mark() and reset() methods are * supported. * *

This class is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. */ public final class MemoryCacheSeekableStream extends SeekableStream { /** The source input stream. */ private InputStream src; /** Position of first unread byte. */ private long pointer = 0; /** Log_2 of the sector size. */ private static final int SECTOR_SHIFT = 9; /** The sector size. */ private static final int SECTOR_SIZE = 1 << SECTOR_SHIFT; /** A mask to determine the offset within a sector. */ private static final int SECTOR_MASK = SECTOR_SIZE - 1; /** A Vector of source sectors. */ private Vector data = new Vector(); /** Number of sectors stored. */ int sectors = 0; /** Number of bytes read. */ int length = 0; /** True if we've previously reached the end of the source stream */ boolean foundEOS = false; /** * Constructs a MemoryCacheSeekableStream that takes * its source data from a regular InputStream. * Seeking backwards is supported by means of an in-memory cache. */ public MemoryCacheSeekableStream(InputStream src) { this.src = src; } /** Forwards the request to the real InputStream. */ public final int available() throws IOException { return src.available(); } /** * Ensures that at least pos bytes are cached, * or the end of the source is reached. The return value * is equal to the smaller of pos and the * length of the source stream. */ private long readUntil(long pos) throws IOException { // We've already got enough data cached if (pos < length) { return pos; } // pos >= length but length isn't getting any bigger, so return it if (foundEOS) { return length; } int sector = (int)(pos >> SECTOR_SHIFT); // First unread sector int startSector = length >> SECTOR_SHIFT; // Read sectors until the desired sector for (int i = startSector; i <= sector; i++) { byte[] buf = new byte[SECTOR_SIZE]; data.addElement(buf); // Read up to SECTOR_SIZE bytes int len = SECTOR_SIZE; int off = 0; while (len > 0) { int nbytes = src.read(buf, off, len); // Found the end-of-stream if (nbytes == -1) { foundEOS = true; return length; } off += nbytes; len -= nbytes; // Record new data length length += nbytes; } } return length; } /** * Returns true since all * MemoryCacheSeekableStream instances support seeking * backwards. */ public boolean canSeekBackwards() { return true; } /** * Returns the current offset in this file. * * @return the offset from the beginning of the file, in bytes, * at which the next read occurs. */ public long getFilePointer() { return pointer; } /** * Sets the file-pointer offset, measured from the beginning of this * file, at which the next read occurs. * * @param pos the offset position, measured in bytes from the * beginning of the file, at which to set the file * pointer. * @exception IOException if pos is less than * 0 or if an I/O error occurs. */ public void seek(long pos) throws IOException { if (pos < 0) { throw new IOException(JaiI18N.getString("MemoryCacheSeekableStream0")); } pointer = pos; } /** * Reads the next byte of data from the input stream. The value byte is * returned as an int in the range 0 to * 255. If no byte is available because the end of the stream * has been reached, the value -1 is returned. This method * blocks until input data is available, the end of the stream is detected, * or an exception is thrown. * * @return the next byte of data, or -1 if the end of the * stream is reached. */ public int read() throws IOException { long next = pointer + 1; long pos = readUntil(next); if (pos >= next) { byte[] buf = (byte[])data.elementAt((int)(pointer >> SECTOR_SHIFT)); return buf[(int)(pointer++ & SECTOR_MASK)] & 0xff; } else { return -1; } } /** * Reads up to len bytes of data from the input stream into * an array of bytes. An attempt is made to read as many as * len bytes, but a smaller number may be read, possibly * zero. The number of bytes actually read is returned as an integer. * *

This method blocks until input data is available, end of file is * detected, or an exception is thrown. * *

If b is null, a * NullPointerException is thrown. * *

If off is negative, or len is negative, or * off+len is greater than the length of the array * b, then an IndexOutOfBoundsException is * thrown. * *

If len is zero, then no bytes are read and * 0 is returned; otherwise, there is an attempt to read at * least one byte. If no byte is available because the stream is at end of * file, the value -1 is returned; otherwise, at least one * byte is read and stored into b. * *

The first byte read is stored into element b[off], the * next one into b[off+1], and so on. The number of bytes read * is, at most, equal to len. Let k be the number of * bytes actually read; these bytes will be stored in elements * b[off] through b[off+k-1], * leaving elements b[off+k] through * b[off+len-1] unaffected. * *

In every case, elements b[0] through * b[off] and elements b[off+len] through * b[b.length-1] are unaffected. * *

If the first byte cannot be read for any reason other than end of * file, then an IOException is thrown. In particular, an * IOException is thrown if the input stream has been closed. * * @param b the buffer into which the data is read. * @param off the start offset in array b * at which the data is written. * @param len the maximum number of bytes to read. * @return the total number of bytes read into the buffer, or * -1 if there is no more data because the end of * the stream has been reached. */ public int read(byte[] b, int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); } if ((off < 0) || (len < 0) || (off + len > b.length)) { throw new IndexOutOfBoundsException(); } if (len == 0) { return 0; } long pos = readUntil(pointer + len); // End-of-stream if (pos <= pointer) { return -1; } byte[] buf = (byte[])data.elementAt((int)(pointer >> SECTOR_SHIFT)); int nbytes = Math.min((int)(pos < pointer + len ? pos - pointer : len), SECTOR_SIZE - (int)(pointer & SECTOR_MASK)); System.arraycopy(buf, (int)(pointer & SECTOR_MASK), b, off, nbytes); pointer += nbytes; return nbytes; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/SeekableStream.java0000644000175000017500000011230410203035544026417 0ustar mathieumathieu/* * $RCSfile: SeekableStream.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:33 $ * $State: Exp $ */ package com.sun.media.jai.codec; import java.io.DataInput; import java.io.DataInputStream; import java.io.EOFException; import java.io.InputStream; import java.io.IOException; /** * An abstract subclass of java.io.InputStream that * allows seeking within the input, similar to the * RandomAccessFile class. Additionally, the * DataInput interface is supported and extended to * include support for little-endian representations of fundamental * data types. * *

In addition to the familiar methods from * InputStream, the methods * getFilePointer(), seek(), are defined as * in the RandomAccessFile class. The * canSeekBackwards() method will return * true if it is permissible to seek to a position * earlier in the stream than the current value of * getFilePointer(). Some subclasses of * SeekableStream guarantee the ability to seek backwards * while others may not offer this feature in the interest of * providing greater efficiency for those users who do not require it. * *

The DataInput interface is supported as well. * This included the skipBytes() and * readFully() methods and a variety of read * methods for various data types. * *

A number of concrete subclasses of SeekableStream * are supplied in the com.sun.media.jai.codec package. * *

Three classes are provided for the purpose of adapting a * standard InputStream to the * SeekableStream interface. * ForwardSeekableStream does not allows seeking * backwards, but is inexpensive to use. * FileCacheSeekableStream maintains a copy of all of the * data read from the input in a temporary file; this file will be * discarded automatically when the FileCacheSeekableStream is * finalized, or when the JVM exits normally. * FileCacheSeekableStream is intended to be reasonably * efficient apart from the unavoidable use of disk space. In * circumstances where the creation of a temporary file is not * possible, MemoryCacheSeekableStream may be used. * MemoryCacheSeekableStream creates a potentially large * in-memory buffer to store the stream data and so should be * avoided when possible. * *

The FileSeekableStream class wraps a * File or RandomAccessFile. It forwards * requests to the real underlying file. It performs a limited amount * of caching in order to avoid excessive I/O costs. * *

The SegmentedSeekableStream class performs a * different sort of function. It creates a * SeekableStream from another * SeekableStream by selecting a series of portions or * "segments". Each segment starts at a specified location within the * source SeekableStream and extends for a specified * number of bytes. The StreamSegmentMapper interface * and StreamSegment class may be * used to compute the segment positions dynamically. * *

A convenience methods, wrapInputStream is provided * to construct a suitable SeekableStream instance whose * data is supplied by a given InputStream. The caller, * by means of the canSeekBackwards parameter, determines * whether support for seeking backwards is required. * * @see java.io.DataInput * @see java.io.InputStream * @see java.io.RandomAccessFile * @see ByteArraySeekableStream * @see FileCacheSeekableStream * @see FileSeekableStream * @see ForwardSeekableStream * @see MemoryCacheSeekableStream * @see SegmentedSeekableStream * @see StreamSegment * @see StreamSegmentMapper * *

This class is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. */ public abstract class SeekableStream extends InputStream implements DataInput { /** * Returns a SeekableStream that will read from a * given InputStream, optionally including support * for seeking backwards. This is a convenience method that * avoids the need to instantiate specific subclasses of * SeekableStream depending on the current security * model. * * @param is An InputStream. * @param canSeekBackwards true if the ability to seek * backwards in the output is required. * @return An instance of SeekableStream. */ public static SeekableStream wrapInputStream(InputStream is, boolean canSeekBackwards) { SeekableStream stream = null; if (canSeekBackwards) { try { stream = new FileCacheSeekableStream(is); } catch (Exception e) { stream = new MemoryCacheSeekableStream(is); } } else { stream = new ForwardSeekableStream(is); } return stream; } // Methods from InputStream /** * Reads the next byte of data from the input stream. The value byte is * returned as an int in the range 0 to * 255. If no byte is available because the end of the stream * has been reached, the value -1 is returned. This method * blocks until input data is available, the end of the stream is detected, * or an exception is thrown. * *

A subclass must provide an implementation of this method. * * @return the next byte of data, or -1 if the end of the * stream is reached. * @exception IOException if an I/O error occurs. */ public abstract int read() throws IOException; /** * Reads up to len bytes of data from the input stream into * an array of bytes. An attempt is made to read as many as * len bytes, but a smaller number may be read, possibly * zero. The number of bytes actually read is returned as an integer. * *

This method blocks until input data is available, end of stream is * detected, or an exception is thrown. * *

If b is null, a * NullPointerException is thrown. * *

If off is negative, or len is negative, or * off+len is greater than the length of the array * b, then an IndexOutOfBoundsException is * thrown. * *

If len is zero, then no bytes are read and * 0 is returned; otherwise, there is an attempt to read at * least one byte. If no byte is available because the stream is at end of * stream, the value -1 is returned; otherwise, at least one * byte is read and stored into b. * *

The first byte read is stored into element b[off], the * next one into b[off+1], and so on. The number of bytes read * is, at most, equal to len. Let k be the number of * bytes actually read; these bytes will be stored in elements * b[off] through b[off+k-1], * leaving elements b[off+k] through * b[off+len-1] unaffected. * *

In every case, elements b[0] through * b[off] and elements b[off+len] through * b[b.length-1] are unaffected. * *

If the first byte cannot be read for any reason other than end of * stream, then an IOException is thrown. In particular, an * IOException is thrown if the input stream has been closed. * *

A subclass must provide an implementation of this method. * * @param b the buffer into which the data is read. * @param off the start offset in array b * at which the data is written. * @param len the maximum number of bytes to read. * @return the total number of bytes read into the buffer, or * -1 if there is no more data because the end of * the stream has been reached. * @exception IOException if an I/O error occurs. */ public abstract int read(byte[] b, int off, int len) throws IOException; // Implemented in InputStream: // // public int read(byte[] b) throws IOException { // public long skip(long n) throws IOException // public int available) throws IOException // public void close() throws IOException; /** Marked position */ protected long markPos = -1L; /** * Marks the current file position for later return using * the reset() method. */ public synchronized void mark(int readLimit) { try { markPos = getFilePointer(); } catch (IOException e) { markPos = -1L; } } /** * Returns the file position to its position at the time of * the immediately previous call to the mark() * method. */ public synchronized void reset() throws IOException { if (markPos != -1) { seek(markPos); } } /** * Returns true if marking is supported. * Marking is automatically supported for SeekableStream * subclasses that support seeking backeards. Subclasses that do * not support seeking backwards but do support marking must override * this method. */ public boolean markSupported() { return canSeekBackwards(); } /** * Returns true if this object supports calls to * seek(pos) with an offset pos smaller * than the current offset, as returned by getFilePointer. */ public boolean canSeekBackwards() { return false; } /** * Returns the current offset in this stream. * * @return the offset from the beginning of the stream, in bytes, * at which the next read occurs. * @exception IOException if an I/O error occurs. */ public abstract long getFilePointer() throws IOException; /** * Sets the offset, measured from the beginning of this * stream, at which the next read occurs. * *

If canSeekBackwards() returns false, * then setting pos to an offset smaller than * the current value of getFilePointer() will have * no effect. * * @param pos the offset position, measured in bytes from the * beginning of the stream, at which to set the stream * pointer. * @exception IOException if pos is less than * 0 or if an I/O error occurs. */ public abstract void seek(long pos) throws IOException; // Methods from RandomAccessFile /** * Reads b.length bytes from this stream into the byte * array, starting at the current stream pointer. This method reads * repeatedly from the stream until the requested number of bytes are * read. This method blocks until the requested number of bytes are * read, the end of the stream is detected, or an exception is thrown. * * @param b the buffer into which the data is read. * @exception EOFException if this stream reaches the end before reading * all the bytes. * @exception IOException if an I/O error occurs. */ public final void readFully(byte[] b) throws IOException { readFully(b, 0, b.length); } /** * Reads exactly len bytes from this stream into the byte * array, starting at the current stream pointer. This method reads * repeatedly from the stream until the requested number of bytes are * read. This method blocks until the requested number of bytes are * read, the end of the stream is detected, or an exception is thrown. * * @param b the buffer into which the data is read. * @param off the start offset of the data. * @param len the number of bytes to read. * @exception EOFException if this stream reaches the end before reading * all the bytes. * @exception IOException if an I/O error occurs. */ public final void readFully(byte[] b, int off, int len) throws IOException { int n = 0; do { int count = this.read(b, off + n, len - n); if (count < 0) throw new EOFException(); n += count; } while (n < len); } // Methods from DataInput, plus little-endian versions /** * Attempts to skip over n bytes of input discarding the * skipped bytes. *

* * This method may skip over some smaller number of bytes, possibly zero. * This may result from any of a number of conditions; reaching end of * stream before n bytes have been skipped is only one * possibility. This method never throws an EOFException. * The actual number of bytes skipped is returned. If n * is negative, no bytes are skipped. * * @param n the number of bytes to be skipped. * @return the actual number of bytes skipped. * @exception IOException if an I/O error occurs. */ public int skipBytes(int n) throws IOException { if (n <= 0) { return 0; } return (int)skip((long)n); } /** * Reads a boolean from this stream. This method reads a * single byte from the stream, starting at the current stream pointer. * A value of 0 represents * false. Any other value represents true. * This method blocks until the byte is read, the end of the stream * is detected, or an exception is thrown. * * @return the boolean value read. * @exception EOFException if this stream has reached the end. * @exception IOException if an I/O error occurs. */ public final boolean readBoolean() throws IOException { int ch = this.read(); if (ch < 0) throw new EOFException(); return (ch != 0); } /** * Reads a signed eight-bit value from this stream. This method reads a * byte from the stream, starting from the current stream pointer. * If the byte read is b, where * 0 <= b <= 255, * then the result is: *

     *     (byte)(b)
     * 
*

* This method blocks until the byte is read, the end of the stream * is detected, or an exception is thrown. * * @return the next byte of this stream as a signed eight-bit * byte. * @exception EOFException if this stream has reached the end. * @exception IOException if an I/O error occurs. */ public final byte readByte() throws IOException { int ch = this.read(); if (ch < 0) throw new EOFException(); return (byte)(ch); } /** * Reads an unsigned eight-bit number from this stream. This method reads * a byte from this stream, starting at the current stream pointer, * and returns that byte. *

* This method blocks until the byte is read, the end of the stream * is detected, or an exception is thrown. * * @return the next byte of this stream, interpreted as an unsigned * eight-bit number. * @exception EOFException if this stream has reached the end. * @exception IOException if an I/O error occurs. */ public final int readUnsignedByte() throws IOException { int ch = this.read(); if (ch < 0) throw new EOFException(); return ch; } /** * Reads a signed 16-bit number from this stream. * The method reads two * bytes from this stream, starting at the current stream pointer. * If the two bytes read, in order, are * b1 and b2, where each of the two values is * between 0 and 255, inclusive, then the * result is equal to: *

     *     (short)((b1 << 8) | b2)
     * 
*

* This method blocks until the two bytes are read, the end of the * stream is detected, or an exception is thrown. * * @return the next two bytes of this stream, interpreted as a signed * 16-bit number. * @exception EOFException if this stream reaches the end before reading * two bytes. * @exception IOException if an I/O error occurs. */ public final short readShort() throws IOException { int ch1 = this.read(); int ch2 = this.read(); if ((ch1 | ch2) < 0) throw new EOFException(); return (short)((ch1 << 8) + (ch2 << 0)); } /** * Reads a signed 16-bit number from this stream in little-endian order. * The method reads two * bytes from this stream, starting at the current stream pointer. * If the two bytes read, in order, are * b1 and b2, where each of the two values is * between 0 and 255, inclusive, then the * result is equal to: *

     *     (short)((b2 << 8) | b1)
     * 
*

* This method blocks until the two bytes are read, the end of the * stream is detected, or an exception is thrown. * * @return the next two bytes of this stream, interpreted as a signed * 16-bit number. * @exception EOFException if this stream reaches the end before reading * two bytes. * @exception IOException if an I/O error occurs. */ public final short readShortLE() throws IOException { int ch1 = this.read(); int ch2 = this.read(); if ((ch1 | ch2) < 0) throw new EOFException(); return (short)((ch2 << 8) + (ch1 << 0)); } /** * Reads an unsigned 16-bit number from this stream. This method reads * two bytes from the stream, starting at the current stream pointer. * If the bytes read, in order, are * b1 and b2, where * 0 <= b1, b2 <= 255, * then the result is equal to: *

     *     (b1 << 8) | b2
     * 
*

* This method blocks until the two bytes are read, the end of the * stream is detected, or an exception is thrown. * * @return the next two bytes of this stream, interpreted as an * unsigned 16-bit integer. * @exception EOFException if this stream reaches the end before reading * two bytes. * @exception IOException if an I/O error occurs. */ public final int readUnsignedShort() throws IOException { int ch1 = this.read(); int ch2 = this.read(); if ((ch1 | ch2) < 0) throw new EOFException(); return (ch1 << 8) + (ch2 << 0); } /** * Reads an unsigned 16-bit number from this stream in little-endian order. * This method reads * two bytes from the stream, starting at the current stream pointer. * If the bytes read, in order, are * b1 and b2, where * 0 <= b1, b2 <= 255, * then the result is equal to: *

     *     (b2 << 8) | b1
     * 
*

* This method blocks until the two bytes are read, the end of the * stream is detected, or an exception is thrown. * * @return the next two bytes of this stream, interpreted as an * unsigned 16-bit integer. * @exception EOFException if this stream reaches the end before reading * two bytes. * @exception IOException if an I/O error occurs. */ public final int readUnsignedShortLE() throws IOException { int ch1 = this.read(); int ch2 = this.read(); if ((ch1 | ch2) < 0) throw new EOFException(); return (ch2 << 8) + (ch1 << 0); } /** * Reads a Unicode character from this stream. This method reads two * bytes from the stream, starting at the current stream pointer. * If the bytes read, in order, are * b1 and b2, where * 0 <= b1, b2 <= 255, * then the result is equal to: *

     *     (char)((b1 << 8) | b2)
     * 
*

* This method blocks until the two bytes are read, the end of the * stream is detected, or an exception is thrown. * * @return the next two bytes of this stream as a Unicode character. * @exception EOFException if this stream reaches the end before reading * two bytes. * @exception IOException if an I/O error occurs. */ public final char readChar() throws IOException { int ch1 = this.read(); int ch2 = this.read(); if ((ch1 | ch2) < 0) throw new EOFException(); return (char)((ch1 << 8) + (ch2 << 0)); } /** * Reads a Unicode character from this stream in little-endian order. * This method reads two * bytes from the stream, starting at the current stream pointer. * If the bytes read, in order, are * b1 and b2, where * 0 <= b1, b2 <= 255, * then the result is equal to: *

     *     (char)((b2 << 8) | b1)
     * 
*

* This method blocks until the two bytes are read, the end of the * stream is detected, or an exception is thrown. * * @return the next two bytes of this stream as a Unicode character. * @exception EOFException if this stream reaches the end before reading * two bytes. * @exception IOException if an I/O error occurs. */ public final char readCharLE() throws IOException { int ch1 = this.read(); int ch2 = this.read(); if ((ch1 | ch2) < 0) throw new EOFException(); return (char)((ch2 << 8) + (ch1 << 0)); } /** * Reads a signed 32-bit integer from this stream. This method reads 4 * bytes from the stream, starting at the current stream pointer. * If the bytes read, in order, are b1, * b2, b3, and b4, where * 0 <= b1, b2, b3, b4 <= 255, * then the result is equal to: *

     *     (b1 << 24) | (b2 << 16) + (b3 << 8) + b4
     * 
*

* This method blocks until the four bytes are read, the end of the * stream is detected, or an exception is thrown. * * @return the next four bytes of this stream, interpreted as an * int. * @exception EOFException if this stream reaches the end before reading * four bytes. * @exception IOException if an I/O error occurs. */ public final int readInt() throws IOException { int ch1 = this.read(); int ch2 = this.read(); int ch3 = this.read(); int ch4 = this.read(); if ((ch1 | ch2 | ch3 | ch4) < 0) throw new EOFException(); return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0)); } /** * Reads a signed 32-bit integer from this stream in little-endian order. * This method reads 4 * bytes from the stream, starting at the current stream pointer. * If the bytes read, in order, are b1, * b2, b3, and b4, where * 0 <= b1, b2, b3, b4 <= 255, * then the result is equal to: *

     *     (b4 << 24) | (b3 << 16) + (b2 << 8) + b1
     * 
*

* This method blocks until the four bytes are read, the end of the * stream is detected, or an exception is thrown. * * @return the next four bytes of this stream, interpreted as an * int. * @exception EOFException if this stream reaches the end before reading * four bytes. * @exception IOException if an I/O error occurs. */ public final int readIntLE() throws IOException { int ch1 = this.read(); int ch2 = this.read(); int ch3 = this.read(); int ch4 = this.read(); if ((ch1 | ch2 | ch3 | ch4) < 0) throw new EOFException(); return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + (ch1 << 0)); } /** * Reads an unsigned 32-bit integer from this stream. This method reads 4 * bytes from the stream, starting at the current stream pointer. * If the bytes read, in order, are b1, * b2, b3, and b4, where * 0 <= b1, b2, b3, b4 <= 255, * then the result is equal to: *

     *     (b1 << 24) | (b2 << 16) + (b3 << 8) + b4
     * 
*

* This method blocks until the four bytes are read, the end of the * stream is detected, or an exception is thrown. * * @return the next four bytes of this stream, interpreted as a * long. * @exception EOFException if this stream reaches the end before reading * four bytes. * @exception IOException if an I/O error occurs. */ public final long readUnsignedInt() throws IOException { long ch1 = this.read(); long ch2 = this.read(); long ch3 = this.read(); long ch4 = this.read(); if ((ch1 | ch2 | ch3 | ch4) < 0) throw new EOFException(); return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0)); } private byte[] ruileBuf = new byte[4]; /** * Reads an unsigned 32-bit integer from this stream in little-endian * order. This method reads 4 * bytes from the stream, starting at the current stream pointer. * If the bytes read, in order, are b1, * b2, b3, and b4, where * 0 <= b1, b2, b3, b4 <= 255, * then the result is equal to: *

     *     (b4 << 24) | (b3 << 16) + (b2 << 8) + b1
     * 
*

* This method blocks until the four bytes are read, the end of the * stream is detected, or an exception is thrown. * * @return the next four bytes of this stream, interpreted as a * long. * @exception EOFException if this stream reaches the end before reading * four bytes. * @exception IOException if an I/O error occurs. */ public final long readUnsignedIntLE() throws IOException { this.readFully(ruileBuf); long ch1 = (long)(ruileBuf[0] & 0xff); long ch2 = (long)(ruileBuf[1] & 0xff); long ch3 = (long)(ruileBuf[2] & 0xff); long ch4 = (long)(ruileBuf[3] & 0xff); return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + (ch1 << 0)); } /** * Reads a signed 64-bit integer from this stream. This method reads eight * bytes from the stream, starting at the current stream pointer. * If the bytes read, in order, are * b1, b2, b3, * b4, b5, b6, * b7, and b8, where: *

     *     0 <= b1, b2, b3, b4, b5, b6, b7, b8 <=255,
     * 
*

* then the result is equal to: *

     *     ((long)b1 << 56) + ((long)b2 << 48)
     *     + ((long)b3 << 40) + ((long)b4 << 32)
     *     + ((long)b5 << 24) + ((long)b6 << 16)
     *     + ((long)b7 << 8) + b8
     * 
*

* This method blocks until the eight bytes are read, the end of the * stream is detected, or an exception is thrown. * * @return the next eight bytes of this stream, interpreted as a * long. * @exception EOFException if this stream reaches the end before reading * eight bytes. * @exception IOException if an I/O error occurs. */ public final long readLong() throws IOException { return ((long)(readInt()) << 32) + (readInt() & 0xFFFFFFFFL); } /** * Reads a signed 64-bit integer from this stream in little-endian * order. This method reads eight * bytes from the stream, starting at the current stream pointer. * If the bytes read, in order, are * b1, b2, b3, * b4, b5, b6, * b7, and b8, where: *

     *     0 <= b1, b2, b3, b4, b5, b6, b7, b8 <=255,
     * 
*

* then the result is equal to: *

     *     ((long)b1 << 56) + ((long)b2 << 48)
     *     + ((long)b3 << 40) + ((long)b4 << 32)
     *     + ((long)b5 << 24) + ((long)b6 << 16)
     *     + ((long)b7 << 8) + b8
     * 
*

* This method blocks until the eight bytes are read, the end of the * stream is detected, or an exception is thrown. * * @return the next eight bytes of this stream, interpreted as a * long. * @exception EOFException if this stream reaches the end before reading * eight bytes. * @exception IOException if an I/O error occurs. */ public final long readLongLE() throws IOException { int i1 = readIntLE(); int i2 = readIntLE(); return ((long)i2 << 32) + (i1 & 0xFFFFFFFFL); } /** * Reads a float from this stream. This method reads an * int value, starting at the current stream pointer, * as if by the readInt method * and then converts that int to a float * using the intBitsToFloat method in class * Float. *

* This method blocks until the four bytes are read, the end of the * stream is detected, or an exception is thrown. * * @return the next four bytes of this stream, interpreted as a * float. * @exception EOFException if this stream reaches the end before reading * four bytes. * @exception IOException if an I/O error occurs. */ public final float readFloat() throws IOException { return Float.intBitsToFloat(readInt()); } /** * Reads a float from this stream in little-endian order. * This method reads an * int value, starting at the current stream pointer, * as if by the readInt method * and then converts that int to a float * using the intBitsToFloat method in class * Float. *

* This method blocks until the four bytes are read, the end of the * stream is detected, or an exception is thrown. * * @return the next four bytes of this stream, interpreted as a * float. * @exception EOFException if this stream reaches the end before reading * four bytes. * @exception IOException if an I/O error occurs. */ public final float readFloatLE() throws IOException { return Float.intBitsToFloat(readIntLE()); } /** * Reads a double from this stream. This method reads a * long value, starting at the current stream pointer, * as if by the readLong method * and then converts that long to a double * using the longBitsToDouble method in * class Double. *

* This method blocks until the eight bytes are read, the end of the * stream is detected, or an exception is thrown. * * @return the next eight bytes of this stream, interpreted as a * double. * @exception EOFException if this stream reaches the end before reading * eight bytes. * @exception IOException if an I/O error occurs. */ public final double readDouble() throws IOException { return Double.longBitsToDouble(readLong()); } /** * Reads a double from this stream in little-endian order. * This method reads a * long value, starting at the current stream pointer, * as if by the readLong method * and then converts that long to a double * using the longBitsToDouble method in * class Double. *

* This method blocks until the eight bytes are read, the end of the * stream is detected, or an exception is thrown. * * @return the next eight bytes of this stream, interpreted as a * double. * @exception EOFException if this stream reaches the end before reading * eight bytes. * @exception IOException if an I/O error occurs. */ public final double readDoubleLE() throws IOException { return Double.longBitsToDouble(readLongLE()); } /** * Reads the next line of text from this stream. This method successively * reads bytes from the stream, starting at the current stream pointer, * until it reaches a line terminator or the end * of the stream. Each byte is converted into a character by taking the * byte's value for the lower eight bits of the character and setting the * high eight bits of the character to zero. This method does not, * therefore, support the full Unicode character set. * *

A line of text is terminated by a carriage-return character * ('\r'), a newline character ('\n'), a * carriage-return character immediately followed by a newline character, * or the end of the stream. Line-terminating characters are discarded and * are not included as part of the string returned. * *

This method blocks until a newline character is read, a carriage * return and the byte following it are read (to see if it is a newline), * the end of the stream is reached, or an exception is thrown. * * @return the next line of text from this stream, or null if end * of stream is encountered before even one byte is read. * @exception IOException if an I/O error occurs. */ public final String readLine() throws IOException { StringBuffer input = new StringBuffer(); int c = -1; boolean eol = false; while (!eol) { switch (c = read()) { case -1: case '\n': eol = true; break; case '\r': eol = true; long cur = getFilePointer(); if ((read()) != '\n') { seek(cur); } break; default: input.append((char)c); break; } } if ((c == -1) && (input.length() == 0)) { return null; } return input.toString(); } /** * Reads in a string from this stream. The string has been encoded * using a modified UTF-8 format. *

* The first two bytes are read, starting from the current stream * pointer, as if by * readUnsignedShort. This value gives the number of * following bytes that are in the encoded string, not * the length of the resulting string. The following bytes are then * interpreted as bytes encoding characters in the UTF-8 format * and are converted into characters. *

* This method blocks until all the bytes are read, the end of the * stream is detected, or an exception is thrown. * * @return a Unicode string. * @exception EOFException if this stream reaches the end before * reading all the bytes. * @exception IOException if an I/O error occurs. * @exception UTFDataFormatException if the bytes do not represent * valid UTF-8 encoding of a Unicode string. */ public final String readUTF() throws IOException { return DataInputStream.readUTF(this); } /** * Releases any system resources associated with this stream * by calling the close() method. */ protected void finalize() throws Throwable { super.finalize(); close(); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/ImageDecodeParam.java0000644000175000017500000000111710203035544026636 0ustar mathieumathieu/* * $RCSfile: ImageDecodeParam.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:30 $ * $State: Exp $ */ package com.sun.media.jai.codec; import java.io.Serializable; /** * An empty (marker) interface to be implemented by all image decoder * parameter classes. * *

This interface is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. */ public interface ImageDecodeParam extends Cloneable, Serializable { } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/PNGSuggestedPaletteEntry.java0000644000175000017500000000206310203035544030370 0ustar mathieumathieu/* * $RCSfile: PNGSuggestedPaletteEntry.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:32 $ * $State: Exp $ */ package com.sun.media.jai.codec; import java.io.Serializable; /** * A class representing the fields of a PNG suggested palette entry. * *

This class is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. */ public class PNGSuggestedPaletteEntry implements Serializable { /** The name of the entry. */ public String name; /** The depth of the color samples. */ public int sampleDepth; /** The red color value of the entry. */ public int red; /** The green color value of the entry. */ public int green; /** The blue color value of the entry. */ public int blue; /** The alpha opacity value of the entry. */ public int alpha; /** The probable frequency of the color in the image. */ public int frequency; } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codec/TIFFDecodeParam.java0000644000175000017500000001265310203035544026353 0ustar mathieumathieu/* * $RCSfile: TIFFDecodeParam.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:34 $ * $State: Exp $ */ package com.sun.media.jai.codec; /** * An instance of ImageDecodeParam for decoding images in * the TIFF format. * *

To determine the number of images present in a TIFF file, use * the getNumPages() method on the * ImageDecoder object that will be used to perform the * decoding. The desired page number may be passed as an argument to * the ImageDecoder.decodeAsRaster)() or * decodeAsRenderedImage() methods. * *

For TIFF Palette color images, the colorMap always has entries * of short data type, the color Black being represented by 0,0,0 and * White by 65536,65536,65536. In order to display these images, the * default behavior is to dither the short values down to 8 bits. * The dithering is done by calling the decode16BitsTo8Bits * method for each short value that needs to be dithered. The method has * the following implementation: * * byte b; * short s; * s = s & 0xffff; * b = (byte)((s >> 8) & 0xff); * * If a different algorithm is to be used for the dithering, this class * should be subclassed and an appropriate implementation should be * provided for the decode16BitsTo8Bits method in the subclass. * *

If the palette contains image data that is signed short, as specified * by the SampleFormat tag, the dithering is done by calling * decodeSigned16BitsTo8Bits instead. The method has the * following implementation: * * byte b; * short s; * b = (byte)((s + Short.MIN_VALUE) >> 8); * * In order to use a different algorithm for the dithering, this class * should be subclassed and the method overridden. * *

If it is desired that the Palette be decoded such that the output * image is of short data type and no dithering is performed, the * setDecodePaletteAsShorts method should be used. * *

This class is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. * * @see TIFFDirectory */ public class TIFFDecodeParam implements ImageDecodeParam { private boolean decodePaletteAsShorts = false; private Long ifdOffset = null; private boolean convertJPEGYCbCrToRGB = true; /** Constructs a default instance of TIFFDecodeParam. */ public TIFFDecodeParam() { } /** * If set, the entries in the palette will be decoded as shorts * and no short to byte lookup will be applied to them. */ public void setDecodePaletteAsShorts(boolean decodePaletteAsShorts) { this.decodePaletteAsShorts = decodePaletteAsShorts; } /** * Returns true if palette entries will be decoded as * shorts, resulting in an output image with short datatype. */ public boolean getDecodePaletteAsShorts() { return decodePaletteAsShorts; } /** * Returns an unsigned 8 bit value computed by dithering the unsigned * 16 bit value. Note that the TIFF specified short datatype is an * unsigned value, while Java's short datatype is a * signed value. Therefore the Java short datatype cannot * be used to store the TIFF specified short value. A Java * int is used as input instead to this method. The method * deals correctly only with 16 bit unsigned values. */ public byte decode16BitsTo8Bits(int s) { return (byte)((s >> 8) & 0xffff); } /** * Returns an unsigned 8 bit value computed by dithering the signed * 16 bit value. This method deals correctly only with values in the * 16 bit signed range. */ public byte decodeSigned16BitsTo8Bits(short s) { return (byte)((s + Short.MIN_VALUE) >> 8); } /** * Sets the offset in the stream from which to read the image. There * must be an Image File Directory (IFD) at that position or an error * will occur. If setIFDOffset() is never invoked then * the decoder will assume that the TIFF stream is at the beginning of * the 8-byte image header. If the directory offset is set and a page * number is supplied to the TIFF ImageDecoder then the * page will be the zero-relative index of the IFD in linked list of * IFDs beginning at the specified offset with a page of zero indicating * the directory at that offset. */ public void setIFDOffset(long offset) { ifdOffset = new Long(offset); } /** * Returns the value set by setIFDOffset() or * null if no value has been set. */ public Long getIFDOffset() { return ifdOffset; } /** * Sets a flag indicating whether to convert JPEG-compressed YCbCr data * to RGB. The default value is true. This flag is * ignored if the image data are not JPEG-compressed. */ public void setJPEGDecompressYCbCrToRGB(boolean convertJPEGYCbCrToRGB) { this.convertJPEGYCbCrToRGB = convertJPEGYCbCrToRGB; } /** * Whether JPEG-compressed YCbCr data will be converted to RGB. */ public boolean getJPEGDecompressYCbCrToRGB() { return convertJPEGYCbCrToRGB; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/0000755000175000017500000000000011633360405023235 5ustar mathieumathieujai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/DFTOpImage.java0000644000175000017500000004441110203035544025756 0ustar mathieumathieu/* * $RCSfile: DFTOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:22 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Image; import java.awt.Rectangle; import java.awt.geom.Point2D; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.text.NumberFormat; import java.util.Arrays; import java.util.Locale; import java.util.Map; import javax.media.jai.EnumeratedParameter; import javax.media.jai.ImageLayout; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.RasterFactory; import javax.media.jai.UntiledOpImage; import javax.media.jai.operator.DFTDescriptor; import com.sun.media.jai.util.JDKWorkarounds; import com.sun.media.jai.util.MathJAI; /** * An OpImage implementing the forward and inverse discrete * Fourier transform (DFT) operations as described in * javax.media.jai.operator.DFTDescriptor and * javax.media.jai.operator.IDFTDescriptor. * *

The DFT operation is implemented using a one-dimensional decimation * in time fast Fourier transform (FFT) which is applied successively to the * rows and the columns of the image. All image dimensions are enlarged to the * next positive power of 2 greater than or equal to the respective dimension * unless the dimension is unity in which case it is not modified. Source * image values are padded with zeros when the dimension is smaller than the * output power-of-2 dimension. * * @since EA3 * * @see javax.media.jai.UntiledOpImage * @see javax.media.jai.operator.DFTDescriptor * @see javax.media.jai.operator.IDFTDescriptor * */ public class DFTOpImage extends UntiledOpImage { /** The Fast Fourier Transform object. */ FFT fft; /** Flag indicating whether the source image is complex. */ protected boolean complexSrc; /** Flag indicating whether the destination image is complex. */ protected boolean complexDst; /** * Override the dimension specification for the destination such that it * has width and height which are equal to non-negative powers of 2. */ private static ImageLayout layoutHelper(ImageLayout layout, RenderedImage source, EnumeratedParameter dataNature) { // Create an ImageLayout or clone the one passed in. ImageLayout il = layout == null ? new ImageLayout() : (ImageLayout)layout.clone(); // Force the origin to coincide with that of the source. il.setMinX(source.getMinX()); il.setMinY(source.getMinY()); // Recalculate the non-unity dimensions to be a positive power of 2. // XXX This calculation should not be effected if an implementation // of the FFT which supports arbitrary dimensions is used. int currentWidth = il.getWidth(source); int currentHeight = il.getHeight(source); int newWidth; int newHeight; if(currentWidth == 1 && currentHeight == 1) { newWidth = newHeight = 1; } else if(currentWidth == 1 && currentHeight > 1) { newWidth = 1; newHeight = MathJAI.nextPositivePowerOf2(currentHeight); } else if(currentWidth > 1 && currentHeight == 1) { newWidth = MathJAI.nextPositivePowerOf2(currentWidth); newHeight = 1; } else { // Neither dimension equal to unity. newWidth = MathJAI.nextPositivePowerOf2(currentWidth); newHeight = MathJAI.nextPositivePowerOf2(currentHeight); } il.setWidth(newWidth); il.setHeight(newHeight); // Set the complex flags for source and destination. boolean isComplexSource = !dataNature.equals(DFTDescriptor.REAL_TO_COMPLEX); boolean isComplexDest = !dataNature.equals(DFTDescriptor.COMPLEX_TO_REAL); // Initialize the SampleModel creation flag. boolean createNewSampleModel = false; // Determine the number of required bands. SampleModel srcSampleModel = source.getSampleModel(); int requiredNumBands = srcSampleModel.getNumBands(); if(isComplexSource && !isComplexDest) { requiredNumBands /= 2; } else if(!isComplexSource && isComplexDest) { requiredNumBands *= 2; } // Set the number of bands. SampleModel sm = il.getSampleModel(source); int numBands = sm.getNumBands(); if(numBands != requiredNumBands) { numBands = requiredNumBands; createNewSampleModel = true; } // Force the image to contain floating point data. int dataType = sm.getTransferType(); if(dataType != DataBuffer.TYPE_FLOAT && dataType != DataBuffer.TYPE_DOUBLE) { dataType = DataBuffer.TYPE_FLOAT; createNewSampleModel = true; } // Create a new SampleModel for the destination if necessary. if(createNewSampleModel) { sm = RasterFactory.createComponentSampleModel(sm, dataType, newWidth, newHeight, numBands); il.setSampleModel(sm); // Clear the ColorModel mask if needed. ColorModel cm = il.getColorModel(null); if(cm != null && !JDKWorkarounds.areCompatibleDataModels(sm, cm)) { // Clear the mask bit if incompatible. il.unsetValid(ImageLayout.COLOR_MODEL_MASK); } } return il; } /** * Constructs a DFTOpImage object. * *

The image dimensions are the respective next positive powers of 2 * greater than or equal to the dimensions of the source image. The tile * grid layout, SampleModel, and ColorModel may optionally be specified * by an ImageLayout object. * * @param source A RenderedImage. * @param layout An ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param fft The Fast Fourier Transform object. * * @see DFTDescriptor. */ public DFTOpImage(RenderedImage source, Map config, ImageLayout layout, EnumeratedParameter dataNature, FFT fft) { super(source, config, layoutHelper(layout, source, dataNature)); // Cache the FFT object. this.fft = fft; // Set the complex flags for source and destination. complexSrc = !dataNature.equals(DFTDescriptor.REAL_TO_COMPLEX); complexDst = !dataNature.equals(DFTDescriptor.COMPLEX_TO_REAL); } /** * Computes the source point corresponding to the supplied point. * * @param destPt the position in destination image coordinates * to map to source image coordinates. * * @return null. * * @throws IllegalArgumentException if destPt is * null. * * @since JAI 1.1.2 */ public Point2D mapDestPoint(Point2D destPt) { if (destPt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return null; } /** * Computes the destination point corresponding to the supplied point. * * @return null. * * @throws IllegalArgumentException if sourcePt is * null. * * @since JAI 1.1.2 */ public Point2D mapSourcePoint(Point2D sourcePt) { if (sourcePt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return null; } /** * Calculate the discrete Fourier transform of the source image. * * @param source The source Raster; should be the whole image. * @param dest The destination WritableRaster; should be the whole image. * @param destRect The destination Rectangle; should be the image bounds. */ protected void computeImage(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; // Degenerate case. if(destRect.width == 1 && destRect.height == 1) { int nDstBands = sampleModel.getNumBands(); double[] srcPixel = new double[source.getSampleModel().getNumBands()]; source.getPixel(destRect.x, destRect.y, srcPixel); if(complexSrc && complexDst) { // Complex -> Complex dest.setPixel(destRect.x, destRect.y, srcPixel); } else if(complexSrc) { // Complex -> Real. for(int i = 0; i < nDstBands; i++) { // Set destination to real part. dest.setSample(destRect.x, destRect.y, i, srcPixel[2*i]); } } else if(complexDst) { // Real -> Complex for(int i = 0; i < nDstBands; i++) { // Set destination real part to source. dest.setSample(destRect.x, destRect.y, i, i % 2 == 0 ? srcPixel[i/2] : 0.0); } } else { // Real -> Real. // NB This statement should be unreachable. throw new RuntimeException(JaiI18N.getString("DFTOpImage1")); } return; } // Initialize to first non-unity length to be encountered. fft.setLength(destRect.width > 1 ? getWidth() : getHeight()); // Get some information about the source image. int srcWidth = source.getWidth(); int srcHeight = source.getHeight(); int srcX = source.getMinX(); int srcY = source.getMinY(); // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); RasterAccessor srcAccessor = new RasterAccessor(source, new Rectangle(srcX, srcY, srcWidth, srcHeight), formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor dstAccessor = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); // Set data type flags. int srcDataType = srcAccessor.getDataType(); int dstDataType = dstAccessor.getDataType(); // Set pixel and line strides. int srcPixelStride = srcAccessor.getPixelStride(); int srcScanlineStride = srcAccessor.getScanlineStride(); int dstPixelStride = dstAccessor.getPixelStride(); int dstScanlineStride = dstAccessor.getScanlineStride(); int dstPixelStrideImag = 1; int dstLineStrideImag = destRect.width; if(complexDst) { dstPixelStrideImag = dstPixelStride; dstLineStrideImag = dstScanlineStride; } // Set indices and strides for image bands (real/imaginary). int srcBandIndex = 0; int srcBandStride = complexSrc ? 2 : 1; int dstBandIndex = 0; int dstBandStride = complexDst ? 2 : 1; // Get the number of components. int numComponents = (complexDst ? dest.getSampleModel().getNumBands() / 2 : dest.getSampleModel().getNumBands()); // Loop over the components. for(int comp = 0; comp < numComponents; comp++) { // Get the real source data for this component. Object srcReal = srcAccessor.getDataArray(srcBandIndex); // Get the imaginary source data for this component if present. Object srcImag = null; if(complexSrc) { srcImag = srcAccessor.getDataArray(srcBandIndex+1); } // Specify the destination components. Object dstReal = dstAccessor.getDataArray(dstBandIndex); Object dstImag = null; if(complexDst) { dstImag = dstAccessor.getDataArray(dstBandIndex+1); } else { // Need to allocate an array for the entire band anyway // even though the destination is real because it is needed // for storage of the result of the row transforms. if(dstDataType == DataBuffer.TYPE_FLOAT) { dstImag = new float[destRect.width*destRect.height]; } else { dstImag = new double[destRect.width*destRect.height]; } } if(destRect.width > 1) { // Set the FFT length. fft.setLength(getWidth()); // Initialize the source offsets for this component. int srcOffsetReal = srcAccessor.getBandOffset(srcBandIndex); int srcOffsetImag = 0; if(complexSrc) { srcOffsetImag = srcAccessor.getBandOffset(srcBandIndex+1); } // Initialize destination offsets and strides. int dstOffsetReal = dstAccessor.getBandOffset(dstBandIndex); int dstOffsetImag = 0; if(complexDst) { dstOffsetImag = dstAccessor.getBandOffset(dstBandIndex+1); } // Perform the row transforms. for(int row = 0; row < srcHeight; row++) { // Set the input data of the FFT. fft.setData(srcDataType, srcReal, srcOffsetReal, srcPixelStride, srcImag, srcOffsetImag, srcPixelStride, srcWidth); // Calculate the DFT of the row. fft.transform(); // Get the output data of the FFT. fft.getData(dstDataType, dstReal, dstOffsetReal, dstPixelStride, dstImag, dstOffsetImag, dstPixelStrideImag); // Increment the data offsets. srcOffsetReal += srcScanlineStride; srcOffsetImag += srcScanlineStride; dstOffsetReal += dstScanlineStride; dstOffsetImag += dstLineStrideImag; } } if(destRect.width == 1) { // destRect.height > 1 // NB 1) destRect.height has to be greater than one or this // would be the degenerate case of a single point which is // handled above. 2) There is no need to do setLength() on // the FFT object here as the length will already have been // set to the maximum of destRect.width amd destRect.height // which must be destRect.height. // Initialize the source offsets for this component. int srcOffsetReal = srcAccessor.getBandOffset(srcBandIndex); int srcOffsetImag = 0; if(complexSrc) { srcOffsetImag = srcAccessor.getBandOffset(srcBandIndex+1); } // Initialize destination offsets and strides. int dstOffsetReal = dstAccessor.getBandOffset(dstBandIndex); int dstOffsetImag = 0; if(complexDst) { dstOffsetImag = dstAccessor.getBandOffset(dstBandIndex+1); } // Set the input data of the FFT. fft.setData(srcDataType, srcReal, srcOffsetReal, srcScanlineStride, srcImag, srcOffsetImag, srcScanlineStride, srcHeight); // Calculate the DFT of the column. fft.transform(); // Get the output data of the FFT. fft.getData(dstDataType, dstReal, dstOffsetReal, dstScanlineStride, dstImag, dstOffsetImag, dstLineStrideImag); } else if(destRect.height > 1) { // destRect.width > 1 // Reset the FFT length. fft.setLength(getHeight()); // Initialize destination offsets and strides. int dstOffsetReal = dstAccessor.getBandOffset(dstBandIndex); int dstOffsetImag = 0; if(complexDst) { dstOffsetImag = dstAccessor.getBandOffset(dstBandIndex+1); } // Perform the column transforms. for(int col = 0; col < destRect.width; col++) { // Set the input data of the FFT. fft.setData(dstDataType, dstReal, dstOffsetReal, dstScanlineStride, dstImag, dstOffsetImag, dstLineStrideImag, destRect.height); // Calculate the DFT of the column. fft.transform(); // Get the output data of the FFT. fft.getData(dstDataType, dstReal, dstOffsetReal, dstScanlineStride, complexDst ? dstImag : null, dstOffsetImag, dstLineStrideImag); // Increment the data offset. dstOffsetReal += dstPixelStride; dstOffsetImag += dstPixelStrideImag; } } // Increment the indices of the real bands in both images. srcBandIndex += srcBandStride; dstBandIndex += dstBandStride; } if (dstAccessor.needsClamping()) { dstAccessor.clampDataArrays(); } // Make sure that the output data is copied to the destination. dstAccessor.copyDataToRaster(); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ColorQuantizerOpImage.java0000644000175000017500000002306410240004133030312 0ustar mathieumathieu/* * $RCSfile: ColorQuantizerOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-05-10 01:03:22 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.IndexColorModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.util.Map; import javax.media.jai.ImageLayout; import javax.media.jai.LookupTableJAI; import javax.media.jai.OpImage; import javax.media.jai.PixelAccessor; import javax.media.jai.PointOpImage; import javax.media.jai.ROI; import javax.media.jai.RasterFactory; import javax.media.jai.UnpackedImageData; /** * An OpImage implementing the color quantization operation as * described in javax.media.jai.operator.ColorQuantizerDescriptor. * *

This OpImage generates an optimal lookup table from the * source RGB image. This lookup table can also be used as a parameter of * operators such as "errordiffusion" to convert the source image into * a color-indexed image. * *

This OpImage contains the pixels of the result images * from the nearest distance classification based on the lookup table * generated from this OpImage. * * @see javax.media.jai.KernelJAI * @see javax.media.jai.LookupTableJAI * * @JAI 1.1.2 * */ abstract class ColorQuantizerOpImage extends PointOpImage { /** * Variables used in the optimized case of 3-band byte to 1-band byte * with a ColorCube color map and a Floyd-Steinberg kernel. */ private static final int NBANDS = 3; private static final int NGRAYS = 256; /** Cache the PixelAccessor for computation. */ protected PixelAccessor srcPA; /** Cache the source type. */ protected int srcSampleType; protected boolean isInitialized = false; /** Cache the PixelAccessor for computation. */ protected PixelAccessor destPA; /** * The color map which maps the ErrorDiffusionOpImage to * its source. */ protected LookupTableJAI colorMap; /** * The expected maximum number of color, that is, the expected size of * the lookup table. */ protected int maxColorNum; /** The subsample rate in the x direction. */ protected int xPeriod; /** The subsample rate in y direction. */ protected int yPeriod; /** The ROI used to define the data set for training. */ protected ROI roi; /** * The number of bands in the source image. */ private int numBandsSource; /** * Whether to check for skipped tiles. */ protected boolean checkForSkippedTiles = false; /** Used by the subclasses to define the start pixel position. */ final static int startPosition(int pos, int start, int period) { int t = (pos - start) % period; return t == 0 ? pos : pos + (period - t); } /** * Force the destination image to be single-banded. */ private static ImageLayout layoutHelper(ImageLayout layout, RenderedImage source) { // Create or clone the layout. ImageLayout il = layout == null ? new ImageLayout() : (ImageLayout)layout.clone(); // Force the destination and source origins and dimensions to coincide. il.setMinX(source.getMinX()); il.setMinY(source.getMinY()); il.setWidth(source.getWidth()); il.setHeight(source.getHeight()); // Get the SampleModel. SampleModel sm = il.getSampleModel(source); // Make sure that this OpImage is single-banded. if (sm.getNumBands() != 1) { sm = RasterFactory.createComponentSampleModel(sm, sm.getTransferType(), sm.getWidth(), sm.getHeight(), 1); il.setSampleModel(sm); } il.setColorModel(null); return il; } /** * Constructs a ColorQuantizerOpImage object. * *

The image dimensions are derived from the source image. The tile * grid layout, SampleModel, and ColorModel may optionally be specified * by an ImageLayout object. * * @param source A RenderedImage. * @param config The rendering hints. * @param layout An ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param maxColorNum The expected maximum number of colors. */ public ColorQuantizerOpImage(RenderedImage source, Map config, ImageLayout layout, int maxColorNum, ROI roi, int xPeriod, int yPeriod) { super(source, layoutHelper(layout, source), config, true); // Get the source sample model. SampleModel srcSampleModel = source.getSampleModel(); // Cache the number of bands in the source. numBandsSource = srcSampleModel.getNumBands(); this.maxColorNum = maxColorNum; this.xPeriod = xPeriod; this.yPeriod = yPeriod; this.roi = roi; this.checkForSkippedTiles = xPeriod > tileWidth || yPeriod > tileHeight; } protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { if (colorMap == null) train(); if(!isInitialized) { srcPA = new PixelAccessor(getSourceImage(0)); srcSampleType = srcPA.sampleType == PixelAccessor.TYPE_BIT ? DataBuffer.TYPE_BYTE : srcPA.sampleType; isInitialized = true; } UnpackedImageData uid = srcPA.getPixels(sources[0], destRect, srcSampleType, false); Rectangle rect = uid.rect; byte[][] data = uid.getByteData(); int srcLineStride = uid.lineStride; int srcPixelStride = uid.pixelStride; byte[] rBand = data[0]; byte[] gBand = data[1]; byte[] bBand = data[2]; int lastLine = rect.height * srcLineStride + uid.bandOffsets[0]; if (destPA == null) destPA = new PixelAccessor(this); UnpackedImageData destUid = destPA.getPixels(dest, destRect, sampleModel.getDataType(), false); int destLineOffset = destUid.bandOffsets[0]; int destLineStride = destUid.lineStride; byte[] d = destUid.getByteData(0); int[] currentPixel = new int[3]; for (int lo = uid.bandOffsets[0]; lo < lastLine; lo += srcLineStride) { int lastPixel = lo + rect.width * srcPixelStride - uid.bandOffsets[0]; int dstPixelOffset = destLineOffset; for (int po = lo - uid.bandOffsets[0]; po < lastPixel; po += srcPixelStride) { d[dstPixelOffset] = findNearestEntry(rBand[po + uid.bandOffsets[0]] & 0xff, gBand[po + uid.bandOffsets[1]] & 0xff, bBand[po + uid.bandOffsets[2]] & 0xff); dstPixelOffset += destUid.pixelStride; } destLineOffset += destLineStride; } } /** Returns one of the available statistics as a property. */ public Object getProperty(String name) { int numBands = sampleModel.getNumBands(); if (name.equals("JAI.LookupTable") || name.equals("LUT")) { if (colorMap == null) train(); return colorMap; } return super.getProperty(name); } protected abstract void train(); public ColorModel getColorModel() { if (colorMap == null) train(); if (colorModel == null) colorModel = new IndexColorModel(8, colorMap.getByteData(0).length, colorMap.getByteData(0), colorMap.getByteData(1), colorMap.getByteData(2)); return colorModel; } protected byte findNearestEntry(int r, int g, int b) { byte[] red = colorMap.getByteData(0); byte[] green = colorMap.getByteData(1); byte[] blue = colorMap.getByteData(2); int index = 0; int dr = r - (red[0] & 0xFF); int dg = g - (green[0] & 0xFF); int db = b - (blue[0] & 0xFF); int minDistance = dr * dr + dg * dg + db * db; // Find the distance to each entry and set the result to // the index which is closest to the argument. for(int i = 1; i < red.length; i++) { dr = r - (red[i] & 0xFF); int distance = dr * dr; if (distance > minDistance) continue; dg = g - (green[i] & 0xFF); distance += dg * dg; if (distance > minDistance) continue; db = b - (blue[i] & 0xFF); distance += db * db; if(distance < minDistance) { minDistance = distance; index = i; } } return (byte)index; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/SubtractFromConstCRIF.java0000644000175000017500000000314710203035544030166 0ustar mathieumathieu/* * $RCSfile: SubtractFromConstCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:45 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "SubtractFromConst" operation in * the rendered and renderable image layers. * * @see javax.media.jai.operator.SubtractFromConstDescriptor * @see SubtractFromConstOpImage * * * @since EA2 */ public class SubtractFromConstCRIF extends CRIFImpl { /** Constructor. */ public SubtractFromConstCRIF() { super("subtractfromconst"); } /** * Creates a new instance of SubtractFromConstOpImage * in the rendered layer. * * @param args The source image and the constants. * @param hints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new SubtractFromConstOpImage(args.getRenderedSource(0), renderHints, layout, (double[])args.getObjectParameter(0)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/Convolve3x3OpImage.java0000644000175000017500000004240210203035544027470 0ustar mathieumathieu/* * $RCSfile: Convolve3x3OpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:19 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class to perform a 3x3 convolution on a source image. * *

This class implements a convolution operation. Convolution is a * spatial operation that computes each output sample by multiplying * elements of a kernel with the samples surrounding a particular * source sample. * *

For each destination sample, the kernel is rotated 180 degrees * and its "key element" is placed over the source pixel corresponding * with the destination pixel. The kernel elements are multiplied * with the source pixels under them, and the resulting products are * summed together to produce the destination sample value. * *

Convolution, or any neighborhood operation, leaves a band of * pixels around the edges undefined, i.e., for a 3x3 kernel, only * four kernel elements and four source pixels contribute to the * destination pixel located at (0,0). Such pixels are not includined * in the destination image. A BorderOpImage may be used to add an * appropriate border to the source image in order to avoid shrinkage * of the image boundaries. * *

The Kernel cannot be bigger in any dimension than the image data. * * * @see KernelJAI */ final class Convolve3x3OpImage extends AreaOpImage { /** * The 3x3 kernel with which to do the convolve operation. */ protected KernelJAI kernel; float tables[][] = new float[9][256]; /** * Creates a Convolve3x3OpImage given a ParameterBlock containing the image * source and a pre-rotated convolution kernel. The image dimensions * are derived * from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout * object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param kernel the pre-rotated convolution KernelJAI. * @param cobbleSources a boolean indicating whether computeRect() * expects contiguous sources. */ public Convolve3x3OpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, KernelJAI kernel) { super(source, layout, config, true, extender, kernel.getLeftPadding(), kernel.getRightPadding(), kernel.getTopPadding(), kernel.getBottomPadding()); this.kernel = kernel; if ((kernel.getWidth() != 3) || (kernel.getHeight() != 3) || (kernel.getXOrigin() != 1) || (kernel.getYOrigin() != 1)) { throw new RuntimeException(JaiI18N.getString("Convolve3x3OpImage0")); } if (sampleModel.getDataType() == DataBuffer.TYPE_BYTE) { float kdata[] = kernel.getKernelData(); float k0 = kdata[0], k1 = kdata[1], k2 = kdata[2], k3 = kdata[3], k4 = kdata[4], k5 = kdata[5], k6 = kdata[6], k7 = kdata[7], k8 = kdata[8]; for (int j = 0; j < 256; j++) { byte b = (byte)j; float f = (float)j; tables[0][b+128] = k0*f+0.5f; tables[1][b+128] = k1*f; tables[2][b+128] = k2*f; tables[3][b+128] = k3*f; tables[4][b+128] = k4*f; tables[5][b+128] = k5*f; tables[6][b+128] = k6*f; tables[7][b+128] = k7*f; tables[8][b+128] = k8*f; } } } /** * Performs convolution on a specified rectangle. The sources are * cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); Raster source = sources[0]; Rectangle srcRect = mapDestRect(destRect, 0); RasterAccessor srcAccessor = new RasterAccessor(source,srcRect, formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor dstAccessor = new RasterAccessor(dest,destRect, formatTags[1], getColorModel()); switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(srcAccessor,dstAccessor); break; case DataBuffer.TYPE_SHORT: shortLoop(srcAccessor,dstAccessor); break; case DataBuffer.TYPE_INT: intLoop(srcAccessor,dstAccessor); break; default: String className = this.getClass().getName(); throw new RuntimeException(JaiI18N.getString("Convolve3x3OpImage1")); } // If the RasterAccessor object set up a temporary buffer for the // op to write to, tell the RasterAccessor to write that data // to the raster no that we're done with it. if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } private void byteLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); // cache these out to avoid an array access per kernel value float t0[] = tables[0], t1[] = tables[1], t2[] = tables[2], t3[] = tables[3], t4[] = tables[4], t5[] = tables[5], t6[] = tables[6], t7[] = tables[7], t8[] = tables[8]; float kdata[] = kernel.getKernelData(); byte dstDataArrays[][] = dst.getByteDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); byte srcDataArrays[][] = src.getByteDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); // precalcaculate offsets int centerScanlineOffset = srcScanlineStride; int bottomScanlineOffset = srcScanlineStride*2; int middlePixelOffset = dnumBands; int rightPixelOffset = dnumBands*2; for (int k = 0; k < dnumBands; k++) { byte dstData[] = dstDataArrays[k]; byte srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { float f = t0[128+srcData[srcPixelOffset]] + t1[128+srcData[srcPixelOffset + middlePixelOffset]] + t2[128+srcData[srcPixelOffset + rightPixelOffset]] + t3[128+srcData[srcPixelOffset + centerScanlineOffset]]+ t4[128+srcData[srcPixelOffset + centerScanlineOffset + middlePixelOffset]]+ t5[128+srcData[srcPixelOffset + centerScanlineOffset + rightPixelOffset]] + t6[128+srcData[srcPixelOffset + bottomScanlineOffset]] + t7[128+srcData[srcPixelOffset + bottomScanlineOffset + middlePixelOffset]] + t8[128+srcData[srcPixelOffset + bottomScanlineOffset + rightPixelOffset]]; // do the clamp int val = (int) f; if (val < 0) { val = 0; } else if (val > 255) { val = 255; } dstData[dstPixelOffset] = (byte)(val); srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } private void shortLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); // precalcaculate offsets int centerScanlineOffset = srcScanlineStride; int bottomScanlineOffset = srcScanlineStride*2; int middlePixelOffset = dnumBands; int rightPixelOffset = dnumBands*2; float kdata[] = kernel.getKernelData(); float k0 = kdata[0], k1 = kdata[1], k2 = kdata[2], k3 = kdata[3], k4 = kdata[4], k5 = kdata[5], k6 = kdata[6], k7 = kdata[7], k8 = kdata[8]; for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { float f = k0*srcData[srcPixelOffset] + k1*srcData[srcPixelOffset + middlePixelOffset] + k2*srcData[srcPixelOffset + rightPixelOffset] + k3*srcData[srcPixelOffset + centerScanlineOffset] + k4*srcData[srcPixelOffset + centerScanlineOffset + middlePixelOffset] + k5*srcData[srcPixelOffset + centerScanlineOffset + rightPixelOffset] + k6*srcData[srcPixelOffset + bottomScanlineOffset] + k7*srcData[srcPixelOffset + bottomScanlineOffset + middlePixelOffset] + k8*srcData[srcPixelOffset + bottomScanlineOffset + rightPixelOffset]; int val = (int)f; if (val < Short.MIN_VALUE) { val = Short.MIN_VALUE; } else if (val > Short.MAX_VALUE) { val = Short.MAX_VALUE; } dstData[dstPixelOffset] = (short)val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } private void intLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); int dstDataArrays[][] = dst.getIntDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcDataArrays[][] = src.getIntDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); // precalcaculate offsets int centerScanlineOffset = srcScanlineStride; int bottomScanlineOffset = srcScanlineStride*2; int middlePixelOffset = dnumBands; int rightPixelOffset = dnumBands*2; float kdata[] = kernel.getKernelData(); float k0 = kdata[0], k1 = kdata[1], k2 = kdata[2], k3 = kdata[3], k4 = kdata[4], k5 = kdata[5], k6 = kdata[6], k7 = kdata[7], k8 = kdata[8]; for (int k = 0; k < dnumBands; k++) { int dstData[] = dstDataArrays[k]; int srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { float f = k0*srcData[srcPixelOffset] + k1*srcData[srcPixelOffset + middlePixelOffset] + k2*srcData[srcPixelOffset + rightPixelOffset] + k3*srcData[srcPixelOffset + centerScanlineOffset] + k4*srcData[srcPixelOffset + centerScanlineOffset + middlePixelOffset] + k5*srcData[srcPixelOffset + centerScanlineOffset + rightPixelOffset] + k6*srcData[srcPixelOffset + bottomScanlineOffset] + k7*srcData[srcPixelOffset + bottomScanlineOffset + middlePixelOffset] + k8*srcData[srcPixelOffset + bottomScanlineOffset + rightPixelOffset]; dstData[dstPixelOffset] = (int)f; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } // public static OpImage createTestImage(OpImageTester oit) { // float data[] = {0.05f,0.10f,0.05f, // 0.10f,0.40f,0.10f, // 0.05f,0.10f,0.05f}; // KernelJAI kJAI = new KernelJAI(3,3,1,1,data); // return new Convolve3x3OpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource()), // kJAI); // } // // Calls a method on OpImage that uses introspection, to make this // // class, discover it's createTestImage() call, call it and then // // benchmark the performance of the created OpImage chain. // public static void main(String args[]) { // String classname = "com.sun.media.jai.opimage.Convolve3x3OpImage"; // OpImageTester.performDiagnostics(classname,args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/WarpBilinearOpImage.java0000644000175000017500000006146010203035544027723 0ustar mathieumathieu/* * $RCSfile: WarpBilinearOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:47 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.IndexColorModel; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.PlanarImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; import javax.media.jai.Warp; import javax.media.jai.WarpOpImage; import javax.media.jai.iterator.RandomIter; import javax.media.jai.iterator.RandomIterFactory; /** * An OpImage implementing the general "Warp" operation as * described in javax.media.jai.operator.WarpDescriptor. * It supports the bilinear interpolation. * * @since EA2 * @see javax.media.jai.Warp * @see javax.media.jai.WarpOpImage * @see javax.media.jai.operator.WarpDescriptor * @see WarpRIF * */ final class WarpBilinearOpImage extends WarpOpImage { /** Color table representing source's IndexColorModel. */ private byte[][] ctable = null; /** * Constructs a WarpBilinearOpImage. * * @param source The source image. * @param extender A BorderExtender, or null. * @param layout The destination image layout. * @param warp An object defining the warp algorithm. * @param interp An object describing the interpolation method. */ public WarpBilinearOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, Warp warp, Interpolation interp, double[] backgroundValues) { super(source, layout, config, false, extender, interp, warp, backgroundValues); /* * If the source has IndexColorModel, get the RGB color table. * Note, in this case, the source should have an integral data type. * And dest always has data type byte. */ ColorModel srcColorModel = source.getColorModel(); if (srcColorModel instanceof IndexColorModel) { IndexColorModel icm = (IndexColorModel)srcColorModel; ctable = new byte[3][icm.getMapSize()]; icm.getReds(ctable[0]); icm.getGreens(ctable[1]); icm.getBlues(ctable[2]); } } /** Warps a rectangle. */ protected void computeRect(PlanarImage[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); RasterAccessor d = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); switch (d.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(sources[0], d); break; case DataBuffer.TYPE_USHORT: computeRectUShort(sources[0], d); break; case DataBuffer.TYPE_SHORT: computeRectShort(sources[0], d); break; case DataBuffer.TYPE_INT: computeRectInt(sources[0], d); break; case DataBuffer.TYPE_FLOAT: computeRectFloat(sources[0], d); break; case DataBuffer.TYPE_DOUBLE: computeRectDouble(sources[0], d); break; } if (d.isDataCopy()) { d.clampDataArrays(); d.copyDataToRaster(); } } private void computeRectByte(PlanarImage src, RasterAccessor dst) { RandomIter iter; if(extender != null) { Rectangle bounds = new Rectangle(src.getMinX(), src.getMinY(), src.getWidth() + 1, src.getHeight() + 1); iter = RandomIterFactory.create(src.getExtendedData(bounds, extender), bounds); } else { iter = RandomIterFactory.create(src, src.getBounds()); } int minX = src.getMinX(); int maxX = src.getMaxX() - (extender != null ? 0 : 1); // Right padding int minY = src.getMinY(); int maxY = src.getMaxY() - (extender != null ? 0 : 1); // Bottom padding int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int lineStride = dst.getScanlineStride(); int pixelStride = dst.getPixelStride(); int[] bandOffsets = dst.getBandOffsets(); byte[][] data = dst.getByteDataArrays(); float[] warpData = new float[2 * dstWidth]; int lineOffset = 0; byte[] backgroundByte = new byte[dstBands]; for (int i = 0; i < dstBands; i++) backgroundByte[i] = (byte)backgroundValues[i]; if (ctable == null) { // source does not have IndexColorModel for (int h = 0; h < dstHeight; h++) { int pixelOffset = lineOffset; lineOffset += lineStride; warp.warpRect(dst.getX(), dst.getY()+h, dstWidth, 1, warpData); int count = 0; for (int w = 0; w < dstWidth; w++) { float sx = warpData[count++]; float sy = warpData[count++]; int xint = floor(sx); int yint = floor(sy); float xfrac = sx - xint; float yfrac = sy - yint; if (xint < minX || xint >= maxX || yint < minY || yint >= maxY) { /* Fill with a background color. */ if (setBackground) { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = backgroundByte[b]; } } } else { for (int b = 0; b < dstBands; b++) { int s00 = iter.getSample(xint, yint, b) & 0xFF; int s01 = iter.getSample(xint+1, yint, b) & 0xFF; int s10 = iter.getSample(xint, yint+1, b) & 0xFF; int s11 = iter.getSample(xint+1, yint+1, b) & 0xFF; float s0 = (s01 - s00) * xfrac + s00; float s1 = (s11 - s10) * xfrac + s10; float s = (s1 - s0) * yfrac + s0; data[b][pixelOffset+bandOffsets[b]] = (byte)s; } } pixelOffset += pixelStride; } } } else { // source has IndexColorModel for (int h = 0; h < dstHeight; h++) { int pixelOffset = lineOffset; lineOffset += lineStride; warp.warpRect(dst.getX(), dst.getY()+h, dstWidth, 1, warpData); int count = 0; for (int w = 0; w < dstWidth; w++) { float sx = warpData[count++]; float sy = warpData[count++]; int xint = floor(sx); int yint = floor(sy); float xfrac = sx - xint; float yfrac = sy - yint; if (xint < minX || xint >= maxX || yint < minY || yint >= maxY) { /* Fill with a background color. */ if (setBackground) { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = backgroundByte[b]; } } } else { for (int b = 0; b < dstBands; b++) { byte[] t = ctable[b]; int s00 = t[iter.getSample(xint, yint, 0) & 0xFF] & 0xFF; int s01 = t[iter.getSample(xint+1, yint, 0) & 0xFF] & 0xFF; int s10 = t[iter.getSample(xint, yint+1, 0) & 0xFF] & 0xFF; int s11 = t[iter.getSample(xint+1, yint+1, 0) & 0xFF] & 0xFF; float s0 = (s01 - s00) * xfrac + s00; float s1 = (s11 - s10) * xfrac + s10; float s = (s1 - s0) * yfrac + s0; data[b][pixelOffset+bandOffsets[b]] = (byte)s; } } pixelOffset += pixelStride; } } } } private void computeRectUShort(PlanarImage src, RasterAccessor dst) { RandomIter iter; if(extender != null) { Rectangle bounds = new Rectangle(src.getMinX(), src.getMinY(), src.getWidth() + 1, src.getHeight() + 1); iter = RandomIterFactory.create(src.getExtendedData(bounds, extender), bounds); } else { iter = RandomIterFactory.create(src, src.getBounds()); } int minX = src.getMinX(); int maxX = src.getMaxX() - (extender != null ? 0 : 1); // Right padding int minY = src.getMinY(); int maxY = src.getMaxY() - (extender != null ? 0 : 1); // Bottom padding int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int lineStride = dst.getScanlineStride(); int pixelStride = dst.getPixelStride(); int[] bandOffsets = dst.getBandOffsets(); short[][] data = dst.getShortDataArrays(); float[] warpData = new float[2 * dstWidth]; int lineOffset = 0; short[] backgroundUShort = new short[dstBands]; for (int i = 0; i < dstBands; i++) backgroundUShort[i] = (short)backgroundValues[i]; for (int h = 0; h < dstHeight; h++) { int pixelOffset = lineOffset; lineOffset += lineStride; warp.warpRect(dst.getX(), dst.getY()+h, dstWidth, 1, warpData); int count = 0; for (int w = 0; w < dstWidth; w++) { float sx = warpData[count++]; float sy = warpData[count++]; int xint = floor(sx); int yint = floor(sy); float xfrac = sx - xint; float yfrac = sy - yint; if (xint < minX || xint >= maxX || yint < minY || yint >= maxY) { /* Fill with a background color. */ if (setBackground) { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = backgroundUShort[b]; } } } else { for (int b = 0; b < dstBands; b++) { int s00 = iter.getSample(xint, yint, b) & 0xFFFF; int s01 = iter.getSample(xint+1, yint, b) & 0xFFFF; int s10 = iter.getSample(xint, yint+1, b) & 0xFFFF; int s11 = iter.getSample(xint+1, yint+1, b) & 0xFFFF; float s0 = (s01 - s00) * xfrac + s00; float s1 = (s11 - s10) * xfrac + s10; float s = (s1 - s0) * yfrac + s0; data[b][pixelOffset+bandOffsets[b]] = (short)s; } } pixelOffset += pixelStride; } } } private void computeRectShort(PlanarImage src, RasterAccessor dst) { RandomIter iter; if(extender != null) { Rectangle bounds = new Rectangle(src.getMinX(), src.getMinY(), src.getWidth() + 1, src.getHeight() + 1); iter = RandomIterFactory.create(src.getExtendedData(bounds, extender), bounds); } else { iter = RandomIterFactory.create(src, src.getBounds()); } int minX = src.getMinX(); int maxX = src.getMaxX() - (extender != null ? 0 : 1); // Right padding int minY = src.getMinY(); int maxY = src.getMaxY() - (extender != null ? 0 : 1); // Bottom padding int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int lineStride = dst.getScanlineStride(); int pixelStride = dst.getPixelStride(); int[] bandOffsets = dst.getBandOffsets(); short[][] data = dst.getShortDataArrays(); float[] warpData = new float[2 * dstWidth]; int lineOffset = 0; short[] backgroundShort = new short[dstBands]; for (int i = 0; i < dstBands; i++) backgroundShort[i] = (short)backgroundValues[i]; for (int h = 0; h < dstHeight; h++) { int pixelOffset = lineOffset; lineOffset += lineStride; warp.warpRect(dst.getX(), dst.getY()+h, dstWidth, 1, warpData); int count = 0; for (int w = 0; w < dstWidth; w++) { float sx = warpData[count++]; float sy = warpData[count++]; int xint = floor(sx); int yint = floor(sy); float xfrac = sx - xint; float yfrac = sy - yint; if (xint < minX || xint >= maxX || yint < minY || yint >= maxY) { /* Fill with a background color. */ if (setBackground) { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = backgroundShort[b]; } } } else { for (int b = 0; b < dstBands; b++) { int s00 = iter.getSample(xint, yint, b); int s01 = iter.getSample(xint+1, yint, b); int s10 = iter.getSample(xint, yint+1, b); int s11 = iter.getSample(xint+1, yint+1, b); float s0 = (s01 - s00) * xfrac + s00; float s1 = (s11 - s10) * xfrac + s10; float s = (s1 - s0) * yfrac + s0; data[b][pixelOffset+bandOffsets[b]] = (short)s; } } pixelOffset += pixelStride; } } } private void computeRectInt(PlanarImage src, RasterAccessor dst) { RandomIter iter; if(extender != null) { Rectangle bounds = new Rectangle(src.getMinX(), src.getMinY(), src.getWidth() + 1, src.getHeight() + 1); iter = RandomIterFactory.create(src.getExtendedData(bounds, extender), bounds); } else { iter = RandomIterFactory.create(src, src.getBounds()); } int minX = src.getMinX(); int maxX = src.getMaxX() - (extender != null ? 0 : 1); // Right padding int minY = src.getMinY(); int maxY = src.getMaxY() - (extender != null ? 0 : 1); // Bottom padding int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int lineStride = dst.getScanlineStride(); int pixelStride = dst.getPixelStride(); int[] bandOffsets = dst.getBandOffsets(); int[][] data = dst.getIntDataArrays(); float[] warpData = new float[2 * dstWidth]; int lineOffset = 0; int[] backgroundInt = new int[dstBands]; for (int i = 0; i < dstBands; i++) backgroundInt[i] = (int)backgroundValues[i]; for (int h = 0; h < dstHeight; h++) { int pixelOffset = lineOffset; lineOffset += lineStride; warp.warpRect(dst.getX(), dst.getY()+h, dstWidth, 1, warpData); int count = 0; for (int w = 0; w < dstWidth; w++) { float sx = warpData[count++]; float sy = warpData[count++]; int xint = floor(sx); int yint = floor(sy); float xfrac = sx - xint; float yfrac = sy - yint; if (xint < minX || xint >= maxX || yint < minY || yint >= maxY) { /* Fill with a background color. */ if (setBackground) { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = backgroundInt[b]; } } } else { for (int b = 0; b < dstBands; b++) { int s00 = iter.getSample(xint, yint, b); int s01 = iter.getSample(xint+1, yint, b); int s10 = iter.getSample(xint, yint+1, b); int s11 = iter.getSample(xint+1, yint+1, b); float s0 = (s01 - s00) * xfrac + s00; float s1 = (s11 - s10) * xfrac + s10; float s = (s1 - s0) * yfrac + s0; data[b][pixelOffset+bandOffsets[b]] = (int)s; } } pixelOffset += pixelStride; } } } private void computeRectFloat(PlanarImage src, RasterAccessor dst) { RandomIter iter; if(extender != null) { Rectangle bounds = new Rectangle(src.getMinX(), src.getMinY(), src.getWidth() + 1, src.getHeight() + 1); iter = RandomIterFactory.create(src.getExtendedData(bounds, extender), bounds); } else { iter = RandomIterFactory.create(src, src.getBounds()); } int minX = src.getMinX(); int maxX = src.getMaxX() - (extender != null ? 0 : 1); // Right padding int minY = src.getMinY(); int maxY = src.getMaxY() - (extender != null ? 0 : 1); // Bottom padding int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int lineStride = dst.getScanlineStride(); int pixelStride = dst.getPixelStride(); int[] bandOffsets = dst.getBandOffsets(); float[][] data = dst.getFloatDataArrays(); float[] warpData = new float[2 * dstWidth]; int lineOffset = 0; float[] backgroundFloat = new float[dstBands]; for (int i = 0; i < dstBands; i++) backgroundFloat[i] = (float)backgroundValues[i]; for (int h = 0; h < dstHeight; h++) { int pixelOffset = lineOffset; lineOffset += lineStride; warp.warpRect(dst.getX(), dst.getY()+h, dstWidth, 1, warpData); int count = 0; for (int w = 0; w < dstWidth; w++) { float sx = warpData[count++]; float sy = warpData[count++]; int xint = floor(sx); int yint = floor(sy); float xfrac = sx - xint; float yfrac = sy - yint; if (xint < minX || xint >= maxX || yint < minY || yint >= maxY) { /* Fill with a background color. */ if (setBackground) { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = backgroundFloat[b]; } } } else { for (int b = 0; b < dstBands; b++) { float s00 = iter.getSampleFloat(xint, yint, b); float s01 = iter.getSampleFloat(xint+1, yint, b); float s10 = iter.getSampleFloat(xint, yint+1, b); float s11 = iter.getSampleFloat(xint+1, yint+1, b); float s0 = (s01 - s00) * xfrac + s00; float s1 = (s11 - s10) * xfrac + s10; float s = (s1 - s0) * yfrac + s0; data[b][pixelOffset+bandOffsets[b]] = s; } } pixelOffset += pixelStride; } } } private void computeRectDouble(PlanarImage src, RasterAccessor dst) { RandomIter iter; if(extender != null) { Rectangle bounds = new Rectangle(src.getMinX(), src.getMinY(), src.getWidth() + 1, src.getHeight() + 1); iter = RandomIterFactory.create(src.getExtendedData(bounds, extender), bounds); } else { iter = RandomIterFactory.create(src, src.getBounds()); } int minX = src.getMinX(); int maxX = src.getMaxX() - (extender != null ? 0 : 1); // Right padding int minY = src.getMinY(); int maxY = src.getMaxY() - (extender != null ? 0 : 1); // Bottom padding int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int lineStride = dst.getScanlineStride(); int pixelStride = dst.getPixelStride(); int[] bandOffsets = dst.getBandOffsets(); double[][] data = dst.getDoubleDataArrays(); float[] warpData = new float[2 * dstWidth]; int lineOffset = 0; for (int h = 0; h < dstHeight; h++) { int pixelOffset = lineOffset; lineOffset += lineStride; warp.warpRect(dst.getX(), dst.getY()+h, dstWidth, 1, warpData); int count = 0; for (int w = 0; w < dstWidth; w++) { float sx = warpData[count++]; float sy = warpData[count++]; int xint = floor(sx); int yint = floor(sy); float xfrac = sx - xint; float yfrac = sy - yint; if (xint < minX || xint >= maxX || yint < minY || yint >= maxY) { /* Fill with a background color. */ if (setBackground) { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = backgroundValues[b]; } } } else { for (int b = 0; b < dstBands; b++) { double s00 = iter.getSampleDouble(xint, yint, b); double s01 = iter.getSampleDouble(xint+1, yint, b); double s10 = iter.getSampleDouble(xint, yint+1, b); double s11 = iter.getSampleDouble(xint+1, yint+1, b); double s0 = (s01 - s00) * xfrac + s00; double s1 = (s11 - s10) * xfrac + s10; double s = (s1 - s0) * yfrac + s0; data[b][pixelOffset+bandOffsets[b]] = s; } } pixelOffset += pixelStride; } } } /** Returns the "floor" value of a float. */ private static final int floor(float f) { return f >= 0 ? (int)f : (int)f - 1; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/AbsoluteOpImage.java0000644000175000017500000005044710203035544027125 0ustar mathieumathieu/* * $RCSfile: AbsoluteOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:11 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PointOpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage implementing the "Absolute" operation as * described in javax.media.jai.operator.AbsoluteDescriptor. * *

This OpImage takes the absolute value of the pixel * values of an image. The operation is done on a per-band basis. * For an integral number, its absolute value is taken as x = (~x) +1, * where ~x is the 1's complement of that number. * If the number is the maximum negative of its type, then it will remain * the same value. * * @since EA2 * @see javax.media.jai.operator.AbsoluteDescriptor * @see AbsoluteCRIF * */ final class AbsoluteOpImage extends PointOpImage { /** * Constructs an AbsoluteOpImage. * *

The layout parameter may optionally contains the * tile grid layout, sample model, and/or color model. The image * dimension is set to the same values as that of the source image. * *

The image layout of the source image is used as the fall-back * for the image layout of the destination image. Any layout parameters * not specified in the layout argument are set to the * same value as that of the source. * * @param source The source image. * @param layout The destination image layout. */ public AbsoluteOpImage(RenderedImage source, Map config, ImageLayout layout) { super(source, layout, config, true); // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * Map the pixels inside a specified rectangle whose value is within a * rang to a constant on a per-band basis. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); RasterAccessor src = new RasterAccessor(sources[0], destRect, formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor dst = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); if(dst.isBinary()) { byte[] dstBits = dst.getBinaryDataArray(); System.arraycopy(src.getBinaryDataArray(), 0, dstBits, 0, dstBits.length); dst.copyBinaryDataToRaster(); return; } /* Find out what kind of data type is used to store the image */ switch ( dst.getDataType() ) { case DataBuffer.TYPE_BYTE: byteAbsolute(dst.getNumBands(), dst.getWidth(), dst.getHeight(), src.getScanlineStride(), src.getPixelStride(), src.getBandOffsets(), src.getByteDataArrays(), dst.getScanlineStride(), dst.getPixelStride(), dst.getBandOffsets(), dst.getByteDataArrays()); break; case DataBuffer.TYPE_SHORT: shortAbsolute(dst.getNumBands(), dst.getWidth(), dst.getHeight(), src.getScanlineStride(), src.getPixelStride(), src.getBandOffsets(), src.getShortDataArrays(), dst.getScanlineStride(), dst.getPixelStride(), dst.getBandOffsets(), dst.getShortDataArrays()); break; case DataBuffer.TYPE_USHORT: ushortAbsolute(dst.getNumBands(), dst.getWidth(), dst.getHeight(), src.getScanlineStride(), src.getPixelStride(), src.getBandOffsets(), src.getShortDataArrays(), dst.getScanlineStride(), dst.getPixelStride(), dst.getBandOffsets(), dst.getShortDataArrays()); break; case DataBuffer.TYPE_INT: intAbsolute(dst.getNumBands(), dst.getWidth(), dst.getHeight(), src.getScanlineStride(), src.getPixelStride(), src.getBandOffsets(), src.getIntDataArrays(), dst.getScanlineStride(), dst.getPixelStride(), dst.getBandOffsets(), dst.getIntDataArrays()); break; case DataBuffer.TYPE_FLOAT: floatAbsolute(dst.getNumBands(), dst.getWidth(), dst.getHeight(), src.getScanlineStride(), src.getPixelStride(), src.getBandOffsets(), src.getFloatDataArrays(), dst.getScanlineStride(), dst.getPixelStride(), dst.getBandOffsets(), dst.getFloatDataArrays()); break; case DataBuffer.TYPE_DOUBLE: doubleAbsolute(dst.getNumBands(), dst.getWidth(), dst.getHeight(), src.getScanlineStride(), src.getPixelStride(), src.getBandOffsets(), src.getDoubleDataArrays(), dst.getScanlineStride(), dst.getPixelStride(), dst.getBandOffsets(), dst.getDoubleDataArrays()); break; } if (dst.needsClamping()) { dst.clampDataArrays(); } dst.copyDataToRaster(); } private void byteAbsolute(int numBands, int dstWidth, int dstHeight, int srcScanlineStride, int srcPixelStride, int[] srcBandOffsets, byte[][] srcData, int dstScanlineStride, int dstPixelStride, int[] dstBandOffsets, byte[][] dstData) { for (int band = 0; band < numBands; band++) { byte[] src = srcData[band]; byte[] dst = dstData[band]; int pixelValue; int srcLineOffset = srcBandOffsets[band]; int dstLineOffset = dstBandOffsets[band]; for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; for (int w = 0; w < dstWidth; w++) { // For byte data, this is a straight copy. dst[dstPixelOffset] = src[srcPixelOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcLineOffset += srcScanlineStride; dstLineOffset += dstScanlineStride; } } } private void shortAbsolute(int numBands, int dstWidth, int dstHeight, int srcScanlineStride, int srcPixelStride, int[] srcBandOffsets, short[][] srcData, int dstScanlineStride, int dstPixelStride, int[] dstBandOffsets, short[][] dstData) { for (int band= 0; band < numBands; band++) { short[] src = srcData[band]; short[] dst = dstData[band]; short pixelValue; int srcLineOffset = srcBandOffsets[band]; int dstLineOffset = dstBandOffsets[band]; for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; for (int w = 0; w < dstWidth; w++) { pixelValue = src[srcPixelOffset]; if ( (pixelValue != Short.MIN_VALUE) && (pixelValue & Short.MIN_VALUE) != 0 ) { // It is not 0x8000 and its sign bit is set. dst[dstPixelOffset] = (short)-src[srcPixelOffset]; } else { // It is either the minimum of short, i.e. 0x8000, // or a positive number; dst[dstPixelOffset] = src[srcPixelOffset]; } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcLineOffset += srcScanlineStride; dstLineOffset += dstScanlineStride; } } } private void ushortAbsolute(int numBands, int dstWidth, int dstHeight, int srcScanlineStride, int srcPixelStride, int[] srcBandOffsets, short[][] srcData, int dstScanlineStride, int dstPixelStride, int[] dstBandOffsets, short[][] dstData) { for (int band= 0; band < numBands; band++) { short[] src = srcData[band]; short[] dst = dstData[band]; int srcLineOffset = srcBandOffsets[band]; int dstLineOffset = dstBandOffsets[band]; for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; for (int w = 0; w < dstWidth; w++) { // For unsigned short data, this is a straight copy. dst[dstPixelOffset] = src[srcPixelOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcLineOffset += srcScanlineStride; dstLineOffset += dstScanlineStride; } } } private void intAbsolute(int numBands, int dstWidth, int dstHeight, int srcScanlineStride, int srcPixelStride, int[] srcBandOffsets, int[][] srcData, int dstScanlineStride, int dstPixelStride, int[] dstBandOffsets, int[][] dstData) { for (int band= 0; band < numBands; band++) { int[] src = srcData[band]; int[] dst = dstData[band]; int pixelValue; int srcLineOffset = srcBandOffsets[band]; int dstLineOffset = dstBandOffsets[band]; for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; for (int w = 0; w < dstWidth; w++) { pixelValue = src[srcPixelOffset]; if ( (pixelValue != Integer.MIN_VALUE) && (pixelValue & Integer.MIN_VALUE) != 0 ) { // It is not 0x80000000 and its sign bit is set. dst[dstPixelOffset] = -src[srcPixelOffset]; } else { // It is either the minimum of int, i.e. 0x80000000, // or a positive number; dst[dstPixelOffset] = src[srcPixelOffset]; } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcLineOffset += srcScanlineStride; dstLineOffset += dstScanlineStride; } } } private void floatAbsolute(int numBands, int dstWidth, int dstHeight, int srcScanlineStride, int srcPixelStride, int[] srcBandOffsets, float[][] srcData, int dstScanlineStride, int dstPixelStride, int[] dstBandOffsets, float[][] dstData) { for (int band= 0; band < numBands; band++) { float[] src = srcData[band]; float[] dst = dstData[band]; int srcLineOffset = srcBandOffsets[band]; int dstLineOffset = dstBandOffsets[band]; for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; // as per Math.abs() for (int w = 0; w < dstWidth; w++) { if ( src[srcPixelOffset] <= 0.0F ) { dst[dstPixelOffset] = 0.0F - src[srcPixelOffset]; } else { dst[dstPixelOffset] = src[srcPixelOffset]; } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcLineOffset += srcScanlineStride; dstLineOffset += dstScanlineStride; } } } private void doubleAbsolute(int numBands, int dstWidth, int dstHeight, int srcScanlineStride, int srcPixelStride, int[] srcBandOffsets, double[][] srcData, int dstScanlineStride, int dstPixelStride, int[] dstBandOffsets, double[][] dstData) { for (int band= 0; band < numBands; band++) { double[] src = srcData[band]; double[] dst = dstData[band]; int srcLineOffset = srcBandOffsets[band]; int dstLineOffset = dstBandOffsets[band]; for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; // as per Math.abs() for (int w = 0; w < dstWidth; w++) { if ( src[srcPixelOffset] <= 0.0D ) { dst[dstPixelOffset] = 0.0D - src[srcPixelOffset]; } else { dst[dstPixelOffset] = src[srcPixelOffset]; } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcLineOffset += srcScanlineStride; dstLineOffset += dstScanlineStride; } } } // public static void main(String args[]) { // System.out.println( "AbsoluteOpImage Test"); // ImageLayout layout; // OpImage src, dst; // Rectangle rect = new Rectangle(0, 0, 5, 5); // System.out.println("1. PixelInterleaved byte 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 800, 800, 0, 0, 200, 200, // DataBuffer.TYPE_BYTE, 3, false); // src = OpImageTester.createRandomOpImage(layout); // dst = new AbsoluteOpImage(src, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("2. Banded byte 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 800, 800, 0, 0, 200, 200, // DataBuffer.TYPE_BYTE, 3, true); // src = OpImageTester.createRandomOpImage(layout); // dst = new AbsoluteOpImage(src, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("3. PixelInterleaved int 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 512, 512, 0, 0, 200, 200, // DataBuffer.TYPE_INT, 3, false); // src = OpImageTester.createRandomOpImage(layout); // dst = new AbsoluteOpImage(src, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("4. Banded int 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 512, 512, 0, 0, 200, 200, // DataBuffer.TYPE_INT, 3, true); // src = OpImageTester.createRandomOpImage(layout); // dst = new AbsoluteOpImage(src, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("5. PixelInterleaved float 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 512, 512, 0, 0, 200, 200, // DataBuffer.TYPE_FLOAT, 3, false); // src = OpImageTester.createRandomOpImage(layout); // dst = new AbsoluteOpImage(src, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("6. Banded float 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 512, 512, 0, 0, 200, 200, // DataBuffer.TYPE_FLOAT, 3, true); // src = OpImageTester.createRandomOpImage(layout); // dst = new AbsoluteOpImage(src, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("7. PixelInterleaved double 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 512, 512, 0, 0, 200, 200, // DataBuffer.TYPE_DOUBLE, 3, false); // src = OpImageTester.createRandomOpImage(layout); // dst = new AbsoluteOpImage(src, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("8. Banded double 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 512, 512, 0, 0, 200, 200, // DataBuffer.TYPE_DOUBLE, 3, true); // src = OpImageTester.createRandomOpImage(layout); // dst = new AbsoluteOpImage(src, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MultiplyConstCRIF.java0000644000175000017500000000307210203035544027367 0ustar mathieumathieu/* * $RCSfile: MultiplyConstCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:37 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "MultiplyConst" operation in the rendered * and renderable image layers. * * @see javax.media.jai.operator.MultiplyConstDescriptor * @see MultiplyConstOpImage * * * @since EA2 */ public class MultiplyConstCRIF extends CRIFImpl { /** Constructor. */ public MultiplyConstCRIF() { super("multiplyconst"); } /** * Creates a new instance of MultiplyConstOpImage in the * rendered layer. * * @param args The source image and the constants. * @param hints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new MultiplyConstOpImage(args.getRenderedSource(0), renderHints, layout, (double[])args.getObjectParameter(0)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/SubsampleBinaryToGrayOpImage.java0000644000175000017500000005211210203035544031564 0ustar mathieumathieu/* * $RCSfile: SubsampleBinaryToGrayOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:44 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.Point2D; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.Point; import java.util.Hashtable; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.IndexColorModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferInt; import java.awt.image.DataBufferUShort; import java.awt.image.IndexColorModel; import java.awt.image.SampleModel; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.PixelInterleavedSampleModel; import java.awt.image.SinglePixelPackedSampleModel; import java.awt.image.BandedSampleModel; import java.awt.image.PixelInterleavedSampleModel; import java.awt.image.renderable.ParameterBlock; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.util.JDKWorkarounds; import javax.media.jai.ImageLayout; import java.util.Map; import javax.media.jai.GeometricOpImage; import javax.media.jai.JAI; import javax.media.jai.PackedImageData; import javax.media.jai.PixelAccessor; import javax.media.jai.PlanarImage; /** * A class extending GeometricOpImage to * subsample binary images to gray scale images. Image scaling operations * require rectilinear backwards mapping and padding by the resampling * filter dimensions. * *

When applying scale factors of scaleX, scaleY to a source image * with width of src_width and height of src_height, the resulting image * is defined to have the following bounds: * * * dst minX = floor(src minX * scaleX) * dst minY = floor(src minY * scaleY) * dst width = floor(src width * scaleX) * dst height = floor(src height * scaleY) * * * @see ScaleOpImage * */ public class SubsampleBinaryToGrayOpImage extends GeometricOpImage { /** The horizontal scale factor. */ protected float scaleX; /** The vertical scale factor. */ protected float scaleY; /** Cached value equal to 1/scaleX. */ protected float invScaleX; /** Cached value equal to 1/scaleY. */ protected float invScaleY; /** Used to determine whether a float is close to an int */ private float floatTol; /** same as ceil(invScaleX), ceil(invScaleY) */ private int blockX; private int blockY; /** destination image width */ private int dWidth; /** destination image height*/ private int dHeight; /** the 1st pixel location for destination pixels, i.e., * the source pixel matrix * Note the index runs from 0..dstWidth-1 and 0..dstHeight-1 * [yValues[j] yValues[j]+blockY-1] by [xValues[i] xValues[i]+blockX-1] * will be condensed to form pixel ith pixel in row j */ private int[] xValues; private int[] yValues; // a look up table; lut[i] counts 1s in binary expression of i private int[] lut = new int[256]; /** * Convert from number of bits on count to gray value, with * scaling, i.e. if invScaleX,Y=3,3, then the possible bit * counts are 0..9, hence the lookup tables are [0..9] * 255/9. */ protected byte[] lutGray; // package accessible for SubsampleBinaryToGrayOpImage4x4, etc... static ImageLayout layoutHelper(RenderedImage source, float scaleX, float scaleY, ImageLayout il, Map config) { ImageLayout layout = (il == null) ? new ImageLayout() : (ImageLayout)il.clone(); // to compute dWidth and dHeight // fTol and dWi, dHi must be the same as in computeDestInfo(..) // due to static method, a few lines of coding are repeated int srcWidth = source.getWidth(); int srcHeight= source.getHeight(); float f_dw = scaleX * srcWidth; float f_dh = scaleY * srcHeight; float fTol = .1F * Math.min(scaleX/(f_dw+1.0F),scaleY/(f_dh+1.0F)); int dWi = (int)(f_dw); int dHi = (int)(f_dh); // let it be int in the almost int case // espacially in the true int case with float calculation errors if(Math.abs(Math.round(f_dw)-f_dw) < fTol){ dWi = Math.round(f_dw); } if(Math.abs(Math.round(f_dh)-f_dh) < fTol){ dHi= Math.round(f_dh); } // Set the top left coordinate of the destination layout.setMinX((int)(scaleX * source.getMinX())); layout.setMinY((int)(scaleY * source.getMinY())); layout.setWidth(dWi); layout.setHeight(dHi); // sample model SampleModel sm = layout.getSampleModel(null); if (sm == null || sm.getDataType() != DataBuffer.TYPE_BYTE || !(sm instanceof PixelInterleavedSampleModel || sm instanceof SinglePixelPackedSampleModel && sm.getNumBands()==1)){ // Width and height will be corrected in OpImage.layoutHelper sm = new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE, 1, 1, 1, 1, new int[] {0}); } layout.setSampleModel(sm); ColorModel cm = layout.getColorModel(null); if(cm == null || !JDKWorkarounds.areCompatibleDataModels(sm, cm)) { layout.setColorModel(ImageUtil.getCompatibleColorModel(sm, config)); } return layout; } // Since this operation deals with packed bits in a binary image, we // do not need to expand the IndexColorModel private static Map configHelper(Map configuration) { Map config; if (configuration == null) { config = new RenderingHints(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.FALSE); } else { config = configuration; if (!config.containsKey(JAI.KEY_REPLACE_INDEX_COLOR_MODEL)) { RenderingHints hints = (RenderingHints)configuration; config = (RenderingHints)hints.clone(); config.put(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.FALSE); } } return config; } /** * Constructs a SubsampleBinaryToGrayOpImage * from a RenderedImage source, x and y scale * object. The image dimensions are determined by forward-mapping * the source bounds, and are passed to the superclass constructor * by means of the layout parameter. Other fields of * the layout are passed through unchanged. If * layout is null, a new * ImageLayout will be constructor to hold the bounds * information. * * The float rounding errors, such as 1.2 being * internally represented as 1.200001, are dealt with * the floatTol, which is set up so that only 1/10 of pixel * error will occur at the end of a line, which yields correct * results with Math.round() operation. * The repeatability is guaranteed with a one-time computed * table xvalues and yvalues. * * @param layout an ImageLayout optionally containing * the tile grid layout, SampleModel, and * ColorModel, or null. * @param source a RenderedImage. * from this OpImage, or null. If * null, no caching will be performed. * @param cobbleSources a boolean indicating whether * computeRect expects contiguous sources. * @param extender a BorderExtender, or null. * @param interp an Interpolation object to use for * resampling. * @param scaleX scale factor along x axis. * @param scaleY scale factor along y axis. * * @throws IllegalArgumentException if combining the * source bounds with the layout parameter results in negative * output width or height. */ public SubsampleBinaryToGrayOpImage(RenderedImage source, ImageLayout layout, Map config, float scaleX, float scaleY){ super(vectorize(source), layoutHelper(source, scaleX, scaleY, layout, config), configHelper(config), true, // cobbleSources, null, // extender null, // interpolation null); this.scaleX = scaleX; this.scaleY = scaleY; int srcMinX = source.getMinX(); int srcMinY = source.getMinY(); int srcWidth = source.getWidth(); int srcHeight= source.getHeight(); // compute floatTol, invScaleX, blockX, dWidth, dHeight,... computeDestInfo(srcWidth, srcHeight); if (extender == null) { computableBounds = new Rectangle(0, 0, dWidth, dHeight); } else { // If extender is present we can write the entire destination. computableBounds = getBounds(); } // these can be delayed, such as placed in computeRect() buildLookupTables(); // compute the begining bit position of each row and column computeXYValues(srcWidth, srcHeight, srcMinX, srcMinY); } /** * Computes the source point corresponding to the supplied point. * * @param destPt the position in destination image coordinates * to map to source image coordinates. * * @return a Point2D of the same class as * destPt. * * @throws IllegalArgumentException if destPt is * null. * * @since JAI 1.1.2 */ public Point2D mapDestPoint(Point2D destPt) { if (destPt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } Point2D pt = (Point2D)destPt.clone(); pt.setLocation(destPt.getX()/scaleX, destPt.getY()/scaleY); return pt; } /** * Computes the destination point corresponding to the supplied point. * * @param sourcePt the position in source image coordinates * to map to destination image coordinates. * * @return a Point2D of the same class as * sourcePt. * * @throws IllegalArgumentException if sourcePt is * null. * * @since JAI 1.1.2 */ public Point2D mapSourcePoint(Point2D sourcePt) { if (sourcePt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } Point2D pt = (Point2D)sourcePt.clone(); pt.setLocation(sourcePt.getX()*scaleX, sourcePt.getY()*scaleY); return pt; } /** * Returns the minimum bounding box of the region of the destination * to which a particular Rectangle of the specified source * will be mapped. * * @param sourceRect the Rectangle in source coordinates. * @param sourceIndex the index of the source image. * * @return a Rectangle indicating the destination * bounding box, or null if the bounding box * is unknown. * * @throws IllegalArgumentException if sourceIndex is * negative or greater than the index of the last source. * @throws IllegalArgumentException if sourceRect is * null. */ protected Rectangle forwardMapRect(Rectangle sourceRect, int sourceIndex) { if ( sourceRect == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sourceIndex != 0) { throw new IllegalArgumentException(JaiI18N.getString("Generic1")); } // Get the source dimensions int x0 = sourceRect.x - blockX +1; int y0 = sourceRect.y - blockY +1; x0 = x0 < 0? 0: x0; y0 = y0 < 0? 0: y0; int dx0 = (int)(x0 * scaleX); int dy0 = (int)(y0 * scaleY); while (xValues[dx0] > x0 && dx0 > 0){ dx0--; } while(yValues[dy0] > y0 && dy0 > 0){ dy0--; } int x1 = sourceRect.x + sourceRect.width - 1; int y1 = sourceRect.y + sourceRect.height- 1; int dx1 = (int)Math.round(x1 * scaleX); int dy1 = (int)Math.round(y1 * scaleY); dx1 = dx1 >= dWidth ? dWidth -1 : dx1; dy1 = dy1 >= dHeight? dHeight-1 : dy1; while (xValues[dx1] < x1 && dx1 < dWidth -1){ dx1++; } while (yValues[dy1] < y1 && dy1 < dHeight-1){ dy1++; } dx0 += this.minX; dy0 += this.minY; dx1 += this.minX; dy1 += this.minY; // Return the writable destination area return new Rectangle(dx0, dy0, dx1-dx0+1, dy1-dy0+1); } /** * Returns the minimum bounding box of the region of the specified * source to which a particular Rectangle of the * destination will be mapped. * * @param destRect the Rectangle in destination coordinates. * @param sourceIndex the index of the source image. * * @return a Rectangle indicating the source bounding box, * or null if the bounding box is unknown. * * @throws IllegalArgumentException if sourceIndex is * negative or greater than the index of the last source. * @throws IllegalArgumentException if destRect is * null. */ protected Rectangle backwardMapRect(Rectangle destRect, int sourceIndex) { if ( destRect == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sourceIndex != 0) { throw new IllegalArgumentException(JaiI18N.getString("Generic1")); } // Get the destination rectangle coordinates and dimensions // Note: indices starting from 0, thus minX/Y should be considered int sx0 = xValues[destRect.x - this.minX]; int sy0 = yValues[destRect.y - this.minY]; int sx1 = xValues[destRect.x - this.minX + destRect.width -1]; int sy1 = yValues[destRect.y - this.minY + destRect.height-1]; return new Rectangle(sx0, sy0, sx1 - sx0 + blockX, sy1 - sy0 + blockY); } /** * Performs a subsamplebinarytogray operation on a specified rectangle. * The sources are cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; switch (source.getSampleModel().getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_INT: byteLoop(source, dest, destRect); break; default: throw new RuntimeException(JaiI18N.getString("SubsampleBinaryToGrayOpImage0")); } } private void byteLoop(Raster source, WritableRaster dest, Rectangle destRect){ PixelAccessor pa = new PixelAccessor(source.getSampleModel(), null); PackedImageData pid = pa.getPackedPixels(source, source.getBounds(), false, false); byte[] sourceData = pid.data; int sourceDBOffset = pid.offset; int dx = destRect.x; int dy = destRect.y; int dwi = destRect.width; int dhi = destRect.height; int sourceTransX = pid.rect.x; // source.getSampleModelTranslateX(); int sourceTransY = pid.rect.y; // source.getSampleModelTranslateY(); PixelInterleavedSampleModel destSM = (PixelInterleavedSampleModel)dest.getSampleModel(); DataBufferByte destDB = (DataBufferByte)dest.getDataBuffer(); int destTransX = dest.getSampleModelTranslateX(); int destTransY = dest.getSampleModelTranslateY(); int destScanlineStride = destSM.getScanlineStride(); byte[] destData = destDB.getData(); int destDBOffset = destDB.getOffset(); int[] sbytenum = new int[dwi]; int[] sstartbit= new int[dwi]; int[] sAreaBitsOn = new int[dwi]; for (int i = 0; i < dwi; i++) { int x = xValues[dx+i-this.minX]; int sbitnum = pid.bitOffset + (x - sourceTransX); sbytenum[i] = sbitnum >> 3; sstartbit[i] = sbitnum % 8; } for(int j = 0; j < dhi; j++) { for(int i=0; i < dwi; i++){ sAreaBitsOn[i] = 0; } for(int y = yValues[dy+j-this.minY]; y < yValues[dy+j-this.minY]+blockY; y++){ int sourceYOffset = (y - sourceTransY)*pid.lineStride + sourceDBOffset; int delement=0, selement, sendbiti, sendbytenumi; for(int i=0; i < dwi; i++){ delement = 0; sendbiti = sstartbit[i] + blockX - 1; sendbytenumi = sbytenum[i]+(sendbiti>>3); sendbiti %= 8; selement = 0x00ff & (int)sourceData[sourceYOffset + sbytenum[i]]; if(sbytenum[i]==sendbytenumi){ selement <<= 24 + sstartbit[i]; selement >>>= 31 - sendbiti + sstartbit[i]; delement += lut[selement]; }else{ selement <<= 24 + sstartbit[i]; selement >>>= 24; delement += lut[selement]; for(int b=sbytenum[i]+1; b< sendbytenumi; b++){ selement = 0x00ff & (int)sourceData[sourceYOffset + b]; delement += lut[selement]; } selement = 0x00ff & (int)sourceData[sourceYOffset + sendbytenumi]; selement >>>= 7-sendbiti; delement += lut[selement]; } sAreaBitsOn[i] += delement; } } int destYOffset = (j + dy - destTransY)*destScanlineStride + destDBOffset; destYOffset += dx - destTransX; // update dest values for row j in raster for(int i = 0; i < dwi; i++){ destData[destYOffset + i] = lutGray[sAreaBitsOn[i]]; } } } private void computeDestInfo(int srcWidth, int srcHeight){ // Inverse scale factors invScaleX = 1.0F / scaleX; invScaleY = 1.0F / scaleY; blockX = (int)Math.ceil(invScaleX); blockY = (int)Math.ceil(invScaleY); // calculate dst width and height float f_dw = scaleX * srcWidth; float f_dh = scaleY * srcHeight; floatTol = .1F * Math.min(scaleX/(f_dw+1.0F),scaleY/(f_dh+1.0F)); dWidth = (int)(f_dw); dHeight= (int)(f_dh); // let it be int in the almost int case // espacially in the true int case with float calculation errors if (Math.abs(Math.round(f_dw)-f_dw) < floatTol){ dWidth = Math.round(f_dw); } if (Math.abs(Math.round(f_dh)-f_dh) < floatTol){ dHeight= Math.round(f_dh); } if (Math.abs(Math.round(invScaleX) - invScaleX) < floatTol){ invScaleX = Math.round(invScaleX); blockX = (int)invScaleX; } if (Math.abs(Math.round(invScaleY) - invScaleY) < floatTol){ invScaleY = Math.round(invScaleY); blockY = (int)invScaleY; } } // buildLookupTables() // initializes variabes bitSet and lut // to be called mainly in the constructor private final void buildLookupTables(){ // lut lut[0] = 0; lut[1] = 1; lut[2] = 1; lut[3] = 2; lut[4] = 1; lut[5] = 2; lut[6] = 2; lut[7] = 3; lut[8] = 1; lut[9] = 2; lut[10]= 2; lut[11]= 3; lut[12]= 2; lut[13]= 3; lut[14]= 3; lut[15]= 4; for(int i= 16; i < 256; i++){ lut[i] = lut[i&(0x0f)] + lut[(i>>4)&(0x0f)]; } // lutGray if (lutGray != null) return; lutGray = new byte[blockX * blockY +1]; for (int i=0; i < lutGray.length; i++){ int tmp = (int)Math.round(255.0F*i/(lutGray.length-1.0F)); lutGray[i] = tmp>255? (byte)0xff : (byte)tmp; } // switch black-white if needed if (isMinWhite(this.getSourceImage(0).getColorModel())) for(int i=0; i < lutGray.length; i++) lutGray[i]= (byte)(255-(0xff &lutGray[i])); } // this function can be called // only after dWidth and dHeight has been set in the constructor // XY values should be computed and stored for repeatable behavior // taking care of non-zero minX, minY private void computeXYValues(int srcWidth, int srcHeight, int srcMinX, int srcMinY){ if (xValues==null || yValues == null){ xValues = new int[dWidth]; yValues = new int[dHeight]; } float tmp; for(int i= 0; i < dWidth; i++){ tmp = invScaleX * i; xValues[i] = (int)Math.round(tmp); } if (xValues[dWidth-1]+blockX > srcWidth){ xValues[dWidth-1]--; } for(int i=0; i < dHeight; i++){ tmp = invScaleY * i; yValues[i] = Math.round(tmp); } if (yValues[dHeight-1] + blockY > srcHeight){ yValues[dHeight-1]--; } // if case the source MinX/Y are not zeros if (srcMinX != 0) for (int i = 0; i < dWidth; i++) xValues[i] += srcMinX; if (srcMinY != 0) for (int i = 0; i < dHeight; i++) yValues[i] += srcMinY; } // check to see whether an indexed colormodel is inverted // returns false if cm not IndexColorModel // red[0] = 0 returns false // red[0] = 255 returns true static boolean isMinWhite(ColorModel cm){ if (cm == null || !(cm instanceof IndexColorModel)) return false; byte[] red = new byte[256]; ((IndexColorModel)cm).getReds(red); return (red[0]==(byte)255?true:false); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/AWTImageOpImage.java0000644000175000017500000002110610203035544026733 0ustar mathieumathieu/* * $RCSfile: AWTImageOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:10 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Canvas; import java.awt.Image; import java.awt.MediaTracker; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.DataBufferInt; import java.awt.image.ImageObserver; import java.awt.image.PixelGrabber; import java.awt.image.Raster; import java.awt.image.SinglePixelPackedSampleModel; import java.awt.image.WritableRaster; import java.util.Map; import javax.media.jai.ImageLayout; import javax.media.jai.PlanarImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFactory; import javax.media.jai.SourcelessOpImage; import javax.media.jai.RasterFormatTag; /** * An OpImage implementing the "AWTImage" operation as * described in javax.media.jai.operator.AWTImageDescriptor. * It takes a regular java.awt.Image and converts it into a * javax.media.jai.PlanarImage. * *

The layout of the PlanarImage may be specified using the * ImageLayout parameter at construction. The image * bounds (minX, minY, width, height), SampleModel, and * ColorModel, if supplied, are ignored. The tile grid * offsets will be ignored if neither of the tile dimensions are * supplied or equal the respective image dimensions. * *

The image origin is forced to (0, 0) and the width and height * to the width and height, respectively, of the AWT image parameter. * If a tile dimension is not set it defaults to the corresponding * image dimension. If neither tile dimension is set or both equal the * corresponding image dimensions, the tile grid offsets default to the * image origin. * *

The SampleModel is forced to a * SinglePixelPackedSampleModel if the tile dimensions equal * the image dimensions, otherwise it is forced to a * PixelInterleavedSampleModel. In either case the * ColorModel is set using * PlanarImage.createColorModel(). * * @see javax.media.jai.operator.AWTImageDescriptor * @see AWTImageRIF * */ final class AWTImageOpImage extends SourcelessOpImage { /* The entire image's pixel values. */ private int[] pixels; /* RasterFormatTag for dest sampleModels */ private RasterFormatTag rasterFormatTag = null; private static final ImageLayout layoutHelper (ImageLayout layout, Image image) { /* Determine image width and height using MediaTracker. */ MediaTracker tracker = new MediaTracker(new Canvas()); tracker.addImage(image, 0); try { tracker.waitForID(0); } catch (InterruptedException e) { e.printStackTrace(); throw new RuntimeException(JaiI18N.getString("AWTImageOpImage0")); } if (tracker.isErrorID(0)) { // not standard file format throw new RuntimeException(JaiI18N.getString("AWTImageOpImage1")); } tracker.removeImage(image); // Create layout if none supplied. if(layout == null) layout = new ImageLayout(); // Override minX, minY, width, height layout.setMinX(0); layout.setMinY(0); layout.setWidth(image.getWidth(null)); layout.setHeight(image.getHeight(null)); // Override tileWidth, tileHeight if not supplied in layout if (!layout.isValid(ImageLayout.TILE_WIDTH_MASK)) { layout.setTileWidth(layout.getWidth(null)); } if (!layout.isValid(ImageLayout.TILE_HEIGHT_MASK)) { layout.setTileHeight(layout.getHeight(null)); } // Override sampleModel // TODO: what if bands != 3? if(layout.getTileWidth(null) == layout.getWidth(null) && layout.getTileHeight(null) == layout.getHeight(null)) { // Override tile grid offsets so we have a single tile. layout.setTileGridXOffset(layout.getMinX(null)); layout.setTileGridYOffset(layout.getMinY(null)); int[] bitMasks = new int[] {0x00ff0000, 0x0000ff00, 0x000000ff}; layout.setSampleModel( new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT, layout.getWidth(null), layout.getHeight(null), bitMasks)); } else { layout.setSampleModel(RasterFactory.createPixelInterleavedSampleModel( DataBuffer.TYPE_BYTE, layout.getTileWidth(null), layout.getTileHeight(null), 3)); } layout.setColorModel(PlanarImage.createColorModel(layout.getSampleModel(null))); return layout; } /** * Constructs an AWTImageOpImage. * * @param layout Image layout. * @param image The AWT image. */ public AWTImageOpImage(Map config, ImageLayout layout, Image image) { // We don't know the width, height, and sample model yet super(layout = layoutHelper(layout, image), config, layout.getSampleModel(null), layout.getMinX(null), layout.getMinY(null), layout.getWidth(null), layout.getHeight(null)); // Set the format tag if and only if we will use the RasterAccessor. if(getTileWidth() != getWidth() || getTileHeight() != getHeight()) { rasterFormatTag = new RasterFormatTag(getSampleModel(), RasterAccessor.TAG_BYTE_UNCOPIED); } // Grab the entire image this.pixels = new int[width * height]; PixelGrabber grabber = new PixelGrabber(image, 0, 0, width, height, pixels, 0, width); try { if (!grabber.grabPixels()) { if ((grabber.getStatus() & ImageObserver.ABORT) != 0) { throw new RuntimeException(JaiI18N.getString("AWTImageOpImage2")); } else { throw new RuntimeException(grabber.getStatus() + JaiI18N.getString("AWTImageOpImage3")); } } } catch (InterruptedException e) { e.printStackTrace(); throw new RuntimeException(JaiI18N.getString("AWTImageOpImage4")); } } public Raster computeTile(int tileX, int tileY) { if(getTileWidth() == getWidth() && getTileHeight() == getHeight()) { DataBuffer dataBuffer = new DataBufferInt(pixels, pixels.length); return Raster.createWritableRaster(getSampleModel(), dataBuffer, new Point(tileXToX(tileX), tileYToY(tileY))); } return super.computeTile(tileX, tileY); } protected void computeRect(PlanarImage[] sources, WritableRaster dest, Rectangle destRect) { RasterAccessor dst = new RasterAccessor(dest, destRect, rasterFormatTag,null); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int lineStride = dst.getScanlineStride(); int pixelStride = dst.getPixelStride(); int lineOffset0 = dst.getBandOffset(0); int lineOffset1 = dst.getBandOffset(1); int lineOffset2 = dst.getBandOffset(2); byte[] data = dst.getByteDataArray(0); int offset = (destRect.y - minY) * width + (destRect.x - minX); for (int h = 0; h < dheight; h++) { int pixelOffset0 = lineOffset0; int pixelOffset1 = lineOffset1; int pixelOffset2 = lineOffset2; lineOffset0 += lineStride; lineOffset1 += lineStride; lineOffset2 += lineStride; int i = offset; offset += width; for (int w = 0; w < dwidth; w++) { data[pixelOffset0] = (byte)((pixels[i] >> 16 ) & 0xFF); data[pixelOffset1] = (byte)((pixels[i] >> 8 ) & 0xFF); data[pixelOffset2] = (byte)(pixels[i] & 0xFF); pixelOffset0 += pixelStride; pixelOffset1 += pixelStride; pixelOffset2 += pixelStride; i++; } } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/FilteredSubsampleOpImage.java0000644000175000017500000017403010203035544030754 0ustar mathieumathieu/* * $RCSfile: FilteredSubsampleOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:26 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.geom.Point2D; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.DataBuffer; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.ImageLayout; import java.util.Map; import javax.media.jai.GeometricOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationNearest; import javax.media.jai.InterpolationBilinear; import javax.media.jai.InterpolationBicubic; import javax.media.jai.InterpolationBicubic2; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import com.sun.media.jai.util.ImageUtil; /** *

A class extending GeometricOpImage to * subsample and antialias filter images. Image scaling operations * require rectilinear backwards mapping and padding by the resampling * and filter dimensions. * *

When applying scale factors of scaleX, scaleY to a source image * with width of src_width and height of src_height, the resulting image * is defined to have the following bounds: * * * dst minX = round(src minX / scaleX)
* dst minY = round(src minY / scaleY)
* dst width = round(src width / scaleX)
* dst height = round(src height / scaleY)
*
* *

The applied filter is quadrant symmetric (typically antialias + resample). The * filter is product-separable, quadrant symmetric, and is defined by half of its * span. For example, if the input filter, qsFilter, was of size 3, it would have * width and height 5 and have the symmetric form: * qs[2] qs[1] qs[0] qs[1] qs[2] * Because we have chosen to keep the filters in compact form we need * to keep track of parity. * *

A fully expanded 5 by 5 kernel has format (25 entries defined by * only 3 entries): * * *

qs[2]*qs[2] qs[2]*qs[1] qs[2]*qs[0] qs[2]*qs[1] qs[2]*qs[2]
* * qs[1]*qs[2] qs[1]*qs[1] qs[1]*qs[0] qs[1]*qs[1] qs[1]*qs[2]
* * qs[0]*qs[2] qs[0]*qs[1] qs[0]*qs[0] qs[0]*qs[1] qs[0]*qs[2]
* * qs[1]*qs[2] qs[1]*qs[1] qs[1]*qs[0] qs[1]*qs[1] qs[1]*qs[2]
* * qs[2]*qs[2] qs[2]*qs[1] qs[2]*qs[0] qs[2]*qs[1] qs[2]*qs[2] *

* *

Horizontal and vertical kernels representing convolved resample and qsFilter * kernels are computed from the input filter, the resample type, and because the * downsample factors affect resample weights, the downsample scale factors. If the * scale factors are odd, then the resample kernel is unity. Parity is used to * signify whether the symmetric kernel has a double center (even parity) or a * single center value (odd parity). * *

This operator is similar to the image scale operator. Important * differences are described here. The coordinate transformation differences * between the FilteredDownsampleOpImage and the ScaleOpImage operators can be * understood by comparing their mapping equations directly. * *

For the scale operator, the destination (D) to source (S) mapping * equations are given by * * *

xS = (xD - xTrans)/xScale
* yS = (yD - yTrans)/yScale *
* *

The scale and translation terms are floating point values in D-frame * pixel units. For scale this means that one S pixel maps to xScale * by yScale D-frame pixels. The translation vector, (xTrans, yTrans), * is in D-frame pixel units. * *

The filtered downsample operator mapping equations are given by * * *

xS = xD*scaleX + (int)(hKernel.length/2)
* yS = yD*scaleY + (int)(vKernel.length/2) *
* *

The mapping equations have the intended property that the convolution * kernel overlays the upper left source pixels for the upper left destination * pixel. * *

The downsample terms are restricted to positive integral values. * Geometrically, one D-frame pixel maps to scaleX by scaleY S-frame * pixels. The combination of downsampling and filtering has performance * benefits over sequential operator usage in part due to the symmetry * constraints imposed by only allowing integer parameters for scaling and * only allowing separable symmetric filters. With odd scale factors, D-frame * pixels map directly onto S-frame pixel centers. With even scale factors, * D-frame pixels map squarely between S-frame pixel centers. Below are * examples of even, odd, and combination cases. * *

s = S-frame pixel centers
* d = D-frame pixel centers mapped to S-frame *

* *
 s   s   s   s   s   s           s   s   s   s   s   s  
*
   d       d       d  
*
 s   s   s   s   s   s           s   d   s   s   d   s  
*
  
*
 s   s   s   s   s   s           s   s   s   s   s   s  
*
   d       d       d  
*
 s   s   s   s   s   s           s   s   s   s   s   s  
*
  
*
 s   s   s   s   s   s           s   d   s   s   d   s  
*
   d       d       d  
*
 s   s   s   s   s   s           s   s   s   s   s   s  
*
  
*
 Even scaleX/Y factors            Odd scaleX/Y factors  
*
   
*
 s   s   s   s   s   s           s   s   s   s   s   s  
*
     d           d    
*
 s   s   s   s   s   s           s d s   s d s   s d s  
*
   
*
 s   s   s   s   s   s           s   s   s   s   s   s  
*
     d           d    
*
 s   s   s   s   s   s           s   s   s   s   s   s  
*
   
*
 s   s   s   s   s   s           s d s   s d s   s d s  
*
     d           d    
*
 s   s   s   s   s   s           s   s   s   s   s   s  
*
   
*
  Odd/even scaleX/Y factors      Even/odd scaleX/Y factors  

*
* *

The convolution kernel is restricted to have quadrant symmetry (qs). This * type of symmetry is also product separable. The qsFilter is specified by * a floating array. If qsFilter[0], qsFilter[1], ... , qsFilter[qsFilter.len() - 1] * is the filter input, then the entire separable kernel is given by
* qsFilter[qsFilter.len() - 1], ... , qsFilter[0], ... , qsFilter[qsFilter.len() - 1]
* *

The restriction of integer parameter constraints allows full product * separablity and symmetry when applying the combined resample and filter * convolution operations. * *

If Bilinear or Bicubic interpolation is specified, the source needs * to be extended such that it has the extra pixels needed to compute all * the destination pixels. This extension is performed via the * BorderExtender class. The type of border extension can be * specified as a RenderingHint to the JAI.create * method. * *

If no BorderExtender is specified, the source will * not be extended. The scaled image size is still calculated * according to the formula specified above. However since there is not * enough source to compute all the destination pixels, only that * subset of the destination image's pixels which can be computed, * will be written in the destination. The rest of the destination * will be set to zeros. * *

The current implementation of this operator does not support * MultiPixelPackedSampleModel source data. The Rendered Image * Factory for this operator, FilteredSubsampleRIF, will throw an * IllegalArgumentException for this type of input. * * @see GeometricOpImage */ public class FilteredSubsampleOpImage extends GeometricOpImage { /**

The horizontal downsample factor. */ protected int scaleX; /**

The vertical downsample factor. */ protected int scaleY; /**

Horizontal filter parity. Rules: 0 => even, 1 => odd. See hKernel */ protected int hParity; /**

Vertical filter parity. Rules: 0 => even, 1 => odd. See vKernel */ protected int vParity; /**

Compact form of combined resample and antialias filters * used by computeRect method. hKernel is the horizontal kernel. * *

The symmetric filter is applied even or odd depending on filter * parity. * *

Expanded even kernel example (hParity = 0):
* * hKernel[2] hKernel[1] hKernel[0] hKernel[0] hKernel[1] hKernel[2] * * *

Expanded odd kernel example (hParity = 1):
* * hKernel[2] hKernel[1] hKernel[0] hKernel[1] hKernel[2] * */ protected float [] hKernel; /**

Compact form of combined resample and antialias filters * used by computeRect method. vKernel is the vertical kernel. * *

The symmetric filter is applied even or odd depending on filter * parity. * *

Expanded even kernel example (vParity = 0):
* * vKernel[2] vKernel[1] vKernel[0] vKernel[0] vKernel[1] vKernel[2] * * *

Expanded odd kernel example (vParity = 1):
* * vKernel[2] vKernel[1] vKernel[0] vKernel[1] vKernel[2] * */ protected float [] vKernel; /**

convolveFullKernels -- convolve two kernels and return the * result in a floating array. * * @param a floating kernel array. * @param b floating kernel array. * @return floating kernel array representing a*b (full convolution) */ private static float [] convolveFullKernels(float [] a, float [] b) { int lenA = a.length; int lenB = b.length; float [] c = new float [lenA + lenB - 1]; for (int k = 0 ; k < c.length ; k++) for (int j = Math.max(0, k-lenB+1) ; j<=Math.min(k, lenA-1) ; j++) c[k] += a[j] * b[k - j]; return c; } // convolveFullKernels /**

convolveSymmetricKernels uses a symmetric representation * (partial kernels) of input and output kernels. For example, with * aParity 1 (odd) and bParity 0 (even) the passed kernels a and b would * have form: * * a: a[lenA-1] ... a[1] a[0] a[1] ... a[lenA-1] * b: b[lenB-1] ... b[1] b[0] b[0] b[1] ... b[lenB-1] * * * (i.e., don't send symmetric parts but assumes parity controls filter * lengths). * *

It is possible to do this convolution without resorting to full * kernels but this is messy. @see convolveFullKernels for details. * *

Further notes: * 1. The return kernel, c, has parity * 1 + aParity + bParity mod 2 * 2. The reason for setting up the kernels this way is to enforce symmetry * constraints. (Design choice.) * * @param aParity int that is 0 or 1. * @param bParity int that is 0 or 1. * @param a floating partial kernel array. * @param b floating partial kernel array. * @return symmetric portion of floating array representing a*b (convolution). */ private static float [] convolveSymmetricKernels(int aParity, int bParity, float [] a, float [] b) { int lenA = a.length; int lenB = b.length; int lenTmpA = 2*lenA - aParity; int lenTmpB = 2*lenB - bParity; int lenTmpC = lenTmpA + lenTmpB - 1; float [] tmpA = new float [lenTmpA]; float [] tmpB = new float [lenTmpB]; float [] tmpC; float [] c = new float [(lenTmpC + 1)/2]; // Construct "full" a for (int k=0 ; k combineFilters based on resampleType and * input partial qsFilter (see above for details on qsFilter format). * Input qsFilter is restricted to have odd parity * (qsFilter[0] is at the center of the kernel). * * @param scaleFactor positive int representing the downsample factor. * @param resampleType int representing the interpolation type. * @param qsFilter floating partial kernel array (antialias filter). * @return floating partial kernel representing combined resample and qsFilter. */ private static float [] combineFilters(int scaleFactor, int resampleType, float [] qsFilter) { // Odd scale factors imply no resample filter is required // return pointer to the qsFilter if ((scaleFactor%2) == 1) return (float [])qsFilter.clone(); int qsParity = 1; int resampParity = 0; // Unless nearest neighbor case is selected (ignored) switch (resampleType) { case Interpolation.INTERP_NEAREST: // Return a copy of the qsFilter return (float [])qsFilter.clone(); case Interpolation.INTERP_BILINEAR: // 2 by 2 resample filter float [] bilinearKernel = { 1.0F/2.0F }; return convolveSymmetricKernels( qsParity, resampParity, qsFilter, bilinearKernel); case Interpolation.INTERP_BICUBIC: // 4 by 4 resample filter float [] bicubicKernel = { 9.0F/16.0F, -1.0F/16.0F }; return convolveSymmetricKernels( qsParity, resampParity, qsFilter, bicubicKernel); case Interpolation.INTERP_BICUBIC_2: // alternate 4 by 4 resample filter float [] bicubic2Kernel = { 5.0F/8.0F, -1.0F/8.0F }; return convolveSymmetricKernels( qsParity, resampParity, qsFilter, bicubic2Kernel); default: throw new IllegalArgumentException( JaiI18N.getString("FilteredSubsample0")); } } // combineFilters /**

filterParity -- Returns combined filter/resample parity. * This is odd only when we have an odd scale factor or we have nearest neighbor * filtering (no resample kernel needed). scaleFactor was validated * by the constructor. Possible return values are 0 or 1. * * @param scaleFactor positive int representing the downsample factor. * @param resampleType int representing the interpolation type. * @return int representing combined filter parity (0 or 1). */ private static int filterParity(int scaleFactor, int resampleType) { // Test scale factor for oddness or nearest neighbor resampling if ((scaleFactor%2 == 1) || (resampleType == Interpolation.INTERP_NEAREST)) return 1; // for all other cases we will be convolving an odd filter with an even // filter, thus producing an even filter return 0; } // filterParity /**

layoutHelper validates input and returns an * ImageLayout object. * * @param source a RenderedImage object. * @param interp an Interpolation object. * @param scaleX an int downsample factor. * @param scaleY an int downsample factor. * @param filterSize an int representing the size of the combined * filter and resample kernel. * @param il an ImageLayout object. * @return validated ImageLayout object. */ private static final ImageLayout layoutHelper(RenderedImage source, Interpolation interp, int scaleX, int scaleY, int filterSize, ImageLayout il) { if (scaleX < 1 || scaleY < 1 ) { throw new IllegalArgumentException( JaiI18N.getString("FilteredSubsample1")); } if (filterSize < 1) { throw new IllegalArgumentException( JaiI18N.getString("FilteredSubsample2")); } // Set the bounds to the scaled source bounds. Rectangle bounds = forwardMapRect(source.getMinX(), source.getMinY(), source.getWidth(), source.getHeight(), scaleX, scaleY); // If the user has supplied a layout, use it ImageLayout layout = (il == null) ? new ImageLayout(bounds.x, bounds.y, bounds.width, bounds.height) : (ImageLayout)il.clone(); // Override dimensions if user passed a hint if (il != null) { layout.setWidth(bounds.width); layout.setHeight(bounds.height); layout.setMinX(bounds.x); layout.setMinY(bounds.y); } return layout; } // setLayout /**

FilteredSubsampleOpImage constructs an OpImage representing * filtered integral subsampling. The scale factors represent the ratio of * source to destination dimensions. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param config a Map object possibly holding tile cache information * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param interp a Interpolation object to use for resampling. * @param scaleX downsample factor along x axis. * @param scaleY downsample factor along y axis. * @param qsFilter symmetric filter coefficients (partial kernel). * @throws IllegalArgumentException if the interp type is not one of: * INTERP_NEAREST, INTERP_BILINEAR, INTERP_BICUBIC, or INTERP_BICUBIC_2 */ public FilteredSubsampleOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, int scaleX, int scaleY, float [] qsFilter, Interpolation interp) { // Propagate to GeometricOpImage constructor super(vectorize(source), layoutHelper(source, interp, scaleX, scaleY, qsFilter.length, layout), config, // Map object true, // cobbleSources, extender, // extender interp, // Interpolation object null); int resampleType; // Determine the interpolation type, if not supported throw exception if (interp instanceof InterpolationNearest) { resampleType = Interpolation.INTERP_NEAREST; } else if (interp instanceof InterpolationBilinear) { resampleType = Interpolation.INTERP_BILINEAR; } else if (interp instanceof InterpolationBicubic) { resampleType = Interpolation.INTERP_BICUBIC; } else if (interp instanceof InterpolationBicubic2) { resampleType = Interpolation.INTERP_BICUBIC_2; } else { throw new IllegalArgumentException( JaiI18N.getString("FilteredSubsample3")); } // Construct combined anti-alias and resample kernels. this.hParity = filterParity(scaleX,resampleType); this.vParity = filterParity(scaleY,resampleType); this.hKernel = combineFilters(scaleX,resampleType,qsFilter); this.vKernel = combineFilters(scaleY,resampleType,qsFilter); this.scaleX = scaleX; this.scaleY = scaleY; } // FilteredSubsampleOpImage /** * Computes the source point corresponding to the supplied point. * * @param destPt the position in destination image coordinates * to map to source image coordinates. * * @return a Point2D of the same class as * destPt. * * @throws IllegalArgumentException if destPt is * null. * * @since JAI 1.1.2 */ public Point2D mapDestPoint(Point2D destPt) { if (destPt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } Point2D pt = (Point2D)destPt.clone(); pt.setLocation(destPt.getX()*scaleX, destPt.getY()*scaleY); return pt; } /** * Computes the destination point corresponding to the supplied point. * * @param sourcePt the position in source image coordinates * to map to destination image coordinates. * * @return a Point2D of the same class as * sourcePt. * * @throws IllegalArgumentException if sourcePt is * null. * * @since JAI 1.1.2 */ public Point2D mapSourcePoint(Point2D sourcePt) { if (sourcePt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } Point2D pt = (Point2D)sourcePt.clone(); pt.setLocation(sourcePt.getX()/scaleX, sourcePt.getY()/scaleY); return pt; } /** *

Returns a conservative estimate of the destination region that * can potentially be affected by the pixels of a rectangle of a * given source. * * @param sourceRect The Rectangle in source coordinates. * @param sourceIndex The index of the source image. * * @return a Rectangle indicating the potentially * affected destination region, or null if * the region is unknown. * * @throws IllegalArgumentException if sourceIndex is * negative or greater than the index of the last source. * @throws NullPointerException if sourceRect is * null. */ public Rectangle mapSourceRect(Rectangle sourceRect, int sourceIndex) { if (sourceIndex != 0) { // this image only has one source throw new IllegalArgumentException(JaiI18N.getString("FilteredSubsample4")); } int xOffset = sourceRect.x + hKernel.length - hParity - scaleX/2; int yOffset = sourceRect.y + vKernel.length - vParity - scaleY/2; int rectWidth = sourceRect.width - 2*hKernel.length + hParity + 1; int rectHeight = sourceRect.height - 2*vKernel.length + vParity + 1; return forwardMapRect(xOffset, yOffset, rectWidth, rectHeight, scaleX, scaleY); } // mapSourceRect /**

Forward map a source Rectangle into destination space. * * @param x source frame coordinate. * @param y source frame coordinate. * @param w source frame width. * @param h source frame height. * @param scaleX downsample factor. * @param scaleY downsample factor. * @return a Rectangle indicating the destination region. */ private static final Rectangle forwardMapRect(int x, int y, int w, int h, int scaleX, int scaleY) { float sx = 1.0F/scaleX; float sy = 1.0F/scaleY; x = Math.round(x*sx); y = Math.round(y*sy); return new Rectangle(x, y, Math.round((x + w)*sx) - x, Math.round((y + h)*sy) - y); } // forwardMapRect /**

Forward map a source Rectangle into destination space. * Required by abstract GeometricOpImage * * @param srcRect a source Rectangle. * @param srcIndex int source index (0 for this operator) * @return a Rectangle indicating the destination region. */ protected final Rectangle forwardMapRect(Rectangle srcRect, int srcIndex) { int x = srcRect.x; int y = srcRect.y; int w = srcRect.width; int h = srcRect.height; float sx = 1.0F/scaleX; float sy = 1.0F/scaleY; x = Math.round(x*sx); y = Math.round(y*sy); return new Rectangle(x, y, Math.round((x + w)*sx) - x, Math.round((y + h)*sy) - y); } // forwardMapRect /**

Backward map a destination Rectangle into source space. * * @param destRect a destination Rectangle. * @param srcIndex int source index (0 for this operator) * @return a Rectangle indicating the source region. */ protected final Rectangle backwardMapRect(Rectangle destRect, int srcIncex) { int x = destRect.x; int y = destRect.y; int w = destRect.width; int h = destRect.height; return new Rectangle(x*scaleX, y*scaleY, (x + w)*scaleX - x, (y + h)*scaleY - y); } // backwardMapRect /** *

Returns a conservative estimate of the region of a specified * source that is required in order to compute the pixels of a * given destination rectangle. * * @param destRect The Rectangle in destination coordinates. * @param sourceIndex The index of the source image. * * @return a Rectangle indicating the required source region. * * @throws IllegalArgumentException if sourceIndex is * negative or greater than the index of the last source. * @throws NullPointerException if destRect is * null. */ public Rectangle mapDestRect(Rectangle destRect, int sourceIndex) { if (sourceIndex != 0) { // this image only has one source throw new IllegalArgumentException( JaiI18N.getString("FilteredSubsample4")); } int xOffset = destRect.x*scaleX - hKernel.length + hParity + scaleX/2; int yOffset = destRect.y*scaleY - vKernel.length + vParity + scaleY/2; int rectWidth = destRect.width*scaleX + 2*hKernel.length - hParity - 1; int rectHeight = destRect.height*scaleY + 2*vKernel.length - vParity - 1; return new Rectangle(xOffset, yOffset, rectWidth, rectHeight); } // mapDestRect /** *

Performs a combined subsample/filter operation on a specified rectangle. * The sources are cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ public void computeRect(Raster [] sources, WritableRaster dest, Rectangle destRect) { // Get RasterAccessor tags (initialized in OpImage superclass). RasterFormatTag[] formatTags = getFormatTags(); // Get destination accessor. RasterAccessor dst = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); // Get source accessor. RasterAccessor src = new RasterAccessor(sources[0], mapDestRect(destRect, 0), formatTags[0], getSourceImage(0).getColorModel()); switch (dst.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(src, dst); break; case DataBuffer.TYPE_USHORT: computeRectUShort(src, dst); break; case DataBuffer.TYPE_SHORT: computeRectShort(src, dst); break; case DataBuffer.TYPE_INT: computeRectInt(src, dst); break; case DataBuffer.TYPE_FLOAT: computeRectFloat(src, dst); break; case DataBuffer.TYPE_DOUBLE: computeRectDouble(src, dst); break; default: throw new IllegalArgumentException( JaiI18N.getString("FilteredSubsample5")); } // If the RasterAccessor set up a temporary write buffer for the // operator, tell it to copy that data to the destination Raster. if (dst.isDataCopy()) { dst.clampDataArrays(); dst.copyDataToRaster(); } } // computeRect /** computeRectByte filter subsamples byte pixel data. * * @param src RasterAccessor for source image. * @param dst RasterAccessor for output image. */ protected void computeRectByte(RasterAccessor src, RasterAccessor dst) { // Get dimensions. int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); // Get destination data array references and strides. byte dstDataArrays[][] = dst.getByteDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); // Get source data array references and strides. byte srcDataArrays[][] = src.getByteDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); // Compute reused numbers int kernelNx = 2*hKernel.length - hParity; int kernelNy = 2*vKernel.length - vParity; int stepDown = (kernelNy - 1)*srcScanlineStride; int stepRight = (kernelNx - 1)*srcPixelStride; int upLeft, upRight, dnLeft, dnRight; float kk; float vCtr = vKernel[0]; float hCtr = hKernel[0]; int kInd, xLeft, xRight, xUp, xDown; for (int band = 0; band < dnumBands ; band++) { byte dstData[] = dstDataArrays[band]; byte srcData[] = srcDataArrays[band]; int srcScanlineOffset = srcBandOffsets[band]; int dstScanlineOffset = dstBandOffsets[band]; // Step over source raster coordinates for (int ySrc = 0 ; ySrc < scaleY*dheight ; ySrc += scaleY) { int dInd = dstScanlineOffset; for (int xSrc = 0 ; xSrc < scaleX*dwidth ; xSrc += scaleX) { int upLeft0 = xSrc*srcPixelStride + ySrc*srcScanlineStride + srcScanlineOffset; int upRight0 = upLeft0 + stepRight; int dnLeft0 = upLeft0 + stepDown; int dnRight0 = upRight0 + stepDown; // Exploit 4-fold symmetry float sum = 0; // Make the rectangle squeeze in the vertical direction for (int iy = vKernel.length - 1 ; iy > vParity - 1 ; iy--) { upLeft = upLeft0; upRight = upRight0; dnLeft = dnLeft0; dnRight = dnRight0; // Make the rectangle squeeze in the horizontal direction for (int ix = hKernel.length - 1 ; ix > hParity - 1 ; ix--) { kk = hKernel[ix]*vKernel[iy]; sum += kk*((int)(srcData[upLeft]&0xff) + (int)(srcData[upRight]&0xff) + (int)(srcData[dnLeft]&0xff) + (int)(srcData[dnRight]&0xff)); upLeft += srcPixelStride; upRight -= srcPixelStride; dnLeft += srcPixelStride; dnRight -= srcPixelStride; } // ix upLeft0 += srcScanlineStride; // down a row upRight0 += srcScanlineStride; dnLeft0 -= srcScanlineStride; // up a row dnRight0 -= srcScanlineStride; } // iy // Compute the remaining 2-Fold symmetry portions (across and down as needed) // Loop down the center (hParity is odd) if (hParity == 1) { xUp = (xSrc + hKernel.length - 1)*srcPixelStride + ySrc*srcScanlineStride + srcScanlineOffset; xDown = xUp + stepDown; kInd = vKernel.length - 1; while (xUp < xDown) { kk = hCtr*vKernel[kInd--]; sum += kk*((int)(srcData[xUp]&0xff) + (int)(srcData[xDown]&0xff)); xUp += srcScanlineStride; xDown -= srcScanlineStride; } } // hParity // Loop across the center (vParity is odd), pick up the center if hParity was odd if (vParity == 1) { xLeft = xSrc*srcPixelStride + (ySrc + vKernel.length - 1)*srcScanlineStride + srcScanlineOffset; xRight = xLeft + stepRight; kInd = hKernel.length - 1; while (xLeft < xRight) { kk = vCtr*hKernel[kInd--]; sum += kk*((int)(srcData[xLeft]&0xff) + (int)(srcData[xRight]&0xff)); xLeft += srcPixelStride; xRight -= srcPixelStride; } // while xLeft // Grab the center pixel if hParity was odd also if (hParity == 1) sum += vCtr*hCtr*(int)(srcData[xLeft]&0xff); } // if vParity // Convert the sum to an output pixel if (sum < 0.0) sum = 0; if (sum > 255.0) sum = 255; dstData[dInd] = (byte)(sum + 0.5); dInd += dstPixelStride; } // for xSrc dstScanlineOffset += dstScanlineStride; } // for ySrc } // for band return; } // computeRectByte /** computeRectUShort filter subsamples unsigned short pixel data. * * @param src RasterAccessor for source image. * @param dst RasterAccessor for output image. */ protected void computeRectUShort(RasterAccessor src, RasterAccessor dst) { // Get dimensions. int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); // Get destination data array references and strides. short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); // Get source data array references and strides. short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); // Compute reused numbers int kernelNx = 2*hKernel.length - hParity; int kernelNy = 2*vKernel.length - vParity; int stepDown = (kernelNy - 1)*srcScanlineStride; int stepRight = (kernelNx - 1)*srcPixelStride; int upLeft, upRight, dnLeft, dnRight; float kk; float vCtr = vKernel[0]; float hCtr = hKernel[0]; int kInd, xLeft, xRight, xUp, xDown; for (int band = 0; band < dnumBands ; band++) { short dstData[] = dstDataArrays[band]; short srcData[] = srcDataArrays[band]; int srcScanlineOffset = srcBandOffsets[band]; int dstScanlineOffset = dstBandOffsets[band]; // Step over source raster coordinates for (int ySrc = 0 ; ySrc < scaleY*dheight ; ySrc += scaleY) { int dInd = dstScanlineOffset; for (int xSrc = 0 ; xSrc < scaleX*dwidth ; xSrc += scaleX) { int upLeft0 = xSrc*srcPixelStride + ySrc*srcScanlineStride + srcScanlineOffset; int upRight0 = upLeft0 + stepRight; int dnLeft0 = upLeft0 + stepDown; int dnRight0 = upRight0 + stepDown; // Exploit 4-fold symmetry float sum = 0; // Make the rectangle squeeze in the vertical direction for (int iy = vKernel.length - 1 ; iy > vParity - 1 ; iy--) { upLeft = upLeft0; upRight = upRight0; dnLeft = dnLeft0; dnRight = dnRight0; // Make the rectangle squeeze in the horizontal direction for (int ix = hKernel.length - 1 ; ix > hParity - 1 ; ix--) { kk = hKernel[ix]*vKernel[iy]; sum += kk*((int)(srcData[upLeft] &0xffff) + (int)(srcData[upRight]&0xffff) + (int)(srcData[dnLeft] &0xffff) + (int)(srcData[dnRight]&0xffff)); upLeft += srcPixelStride; upRight -= srcPixelStride; dnLeft += srcPixelStride; dnRight -= srcPixelStride; } // ix upLeft0 += srcScanlineStride; // down a row upRight0 += srcScanlineStride; dnLeft0 -= srcScanlineStride; // up a row dnRight0 -= srcScanlineStride; } // iy // Compute the remaining 2-Fold symmetry portions // (across and down as needed) // Loop down the center (hParity is odd) if (hParity == 1) { xUp = (xSrc + hKernel.length - 1)*srcPixelStride + ySrc*srcScanlineStride + srcScanlineOffset; xDown = xUp + stepDown; kInd = vKernel.length - 1; while (xUp < xDown) { kk = hCtr*vKernel[kInd--]; sum += kk*((int)(srcData[xUp] &0xffff) + (int)(srcData[xDown]&0xffff)); xUp += srcScanlineStride; xDown -= srcScanlineStride; } } // hParity // Loop across the center (vParity is odd), pick up the center if hParity was odd if (vParity == 1) { xLeft = xSrc*srcPixelStride + (ySrc + vKernel.length - 1)*srcScanlineStride + srcScanlineOffset; xRight = xLeft + stepRight; kInd = hKernel.length - 1; while (xLeft < xRight) { kk = vCtr*hKernel[kInd--]; sum += kk*((int)(srcData[xLeft] &0xffff) + (int)(srcData[xRight]&0xffff)); xLeft += srcPixelStride; xRight -= srcPixelStride; } // while xLeft // Grab the center pixel if hParity was odd also if (hParity == 1) sum += vCtr*hCtr*(int)(srcData[xLeft]&0xffff); } // if vParity int val = (int)(sum + 0.5); dstData[dInd] = (short)(val > 0xffff ? 0xffff : (val < 0 ? 0 : val)); dInd += dstPixelStride; } // for xSrc dstScanlineOffset += dstScanlineStride; } // for ySrc } // for band return; } // computeRectUShort /** computeRectShort filter subsamples short pixel data. * * @param src RasterAccessor for source image. * @param dst RasterAccessor for output image. */ protected void computeRectShort(RasterAccessor src, RasterAccessor dst) { // Get dimensions. int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); // Get destination data array references and strides. short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); // Get source data array references and strides. short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); // Compute reused numbers int kernelNx = 2*hKernel.length - hParity; int kernelNy = 2*vKernel.length - vParity; int stepDown = (kernelNy - 1)*srcScanlineStride; int stepRight = (kernelNx - 1)*srcPixelStride; int upLeft, upRight, dnLeft, dnRight; float kk; float vCtr = vKernel[0]; float hCtr = hKernel[0]; int kInd, xLeft, xRight, xUp, xDown; for (int band = 0; band < dnumBands ; band++) { short dstData[] = dstDataArrays[band]; short srcData[] = srcDataArrays[band]; int srcScanlineOffset = srcBandOffsets[band]; int dstScanlineOffset = dstBandOffsets[band]; // Step over source raster coordinates for (int ySrc = 0 ; ySrc < scaleY*dheight ; ySrc += scaleY) { int dInd = dstScanlineOffset; for (int xSrc = 0 ; xSrc < scaleX*dwidth ; xSrc += scaleX) { int upLeft0 = xSrc*srcPixelStride + ySrc*srcScanlineStride + srcScanlineOffset; int upRight0 = upLeft0 + stepRight; int dnLeft0 = upLeft0 + stepDown; int dnRight0 = upRight0 + stepDown; // Exploit 4-fold symmetry float sum = 0; // Make the rectangle squeeze in the vertical direction for (int iy = vKernel.length - 1 ; iy > vParity - 1 ; iy--) { upLeft = upLeft0; upRight = upRight0; dnLeft = dnLeft0; dnRight = dnRight0; // Make the rectangle squeeze in the horizontal direction for (int ix = hKernel.length - 1 ; ix > hParity - 1 ; ix--) { kk = hKernel[ix]*vKernel[iy]; sum += kk*((int)(srcData[upLeft]) + (int)(srcData[upRight]) + (int)(srcData[dnLeft]) + (int)(srcData[dnRight])); upLeft += srcPixelStride; upRight -= srcPixelStride; dnLeft += srcPixelStride; dnRight -= srcPixelStride; } // ix upLeft0 += srcScanlineStride; // down a row upRight0 += srcScanlineStride; dnLeft0 -= srcScanlineStride; // up a row dnRight0 -= srcScanlineStride; } // iy // Compute the remaining 2-Fold symmetry portions // (across and down as needed) // Loop down the center (hParity is odd) if (hParity == 1) { xUp = (xSrc + hKernel.length - 1)*srcPixelStride + ySrc*srcScanlineStride + srcScanlineOffset; xDown = xUp + stepDown; kInd = vKernel.length - 1; while (xUp < xDown) { kk = hCtr*vKernel[kInd--]; sum += kk*((int)(srcData[xUp]) + (int)(srcData[xDown])); xUp += srcScanlineStride; xDown -= srcScanlineStride; } } // hParity // Loop across the center (vParity is odd), pick up the center if hParity was odd if (vParity == 1) { xLeft = xSrc*srcPixelStride + (ySrc + vKernel.length - 1)*srcScanlineStride + srcScanlineOffset; xRight = xLeft + stepRight; kInd = hKernel.length - 1; while (xLeft < xRight) { kk = vCtr*hKernel[kInd--]; sum += kk*((int)(srcData[xLeft]) + (int)(srcData[xRight])); xLeft += srcPixelStride; xRight -= srcPixelStride; } // while xLeft // Grab the center pixel if hParity was odd also if (hParity == 1) sum += vCtr*hCtr*(int)(srcData[xLeft]); } // if vParity dstData[dInd] = ImageUtil.clampShort((int)(sum + 0.5)); dInd += dstPixelStride; } // for xSrc dstScanlineOffset += dstScanlineStride; } // for ySrc } // for band return; } // computeRectShort /** computeRectInt filter subsamples int pixel data. * * @param src RasterAccessor for source image. * @param dst RasterAccessor for output image. */ protected void computeRectInt(RasterAccessor src, RasterAccessor dst) { // Get dimensions. int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); // Get destination data array references and strides. int dstDataArrays[][] = dst.getIntDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); // Get source data array references and strides. int srcDataArrays[][] = src.getIntDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); // Compute reused numbers int kernelNx = 2*hKernel.length - hParity; int kernelNy = 2*vKernel.length - vParity; int stepDown = (kernelNy - 1)*srcScanlineStride; int stepRight = (kernelNx - 1)*srcPixelStride; int upLeft, upRight, dnLeft, dnRight; double kk; double vCtr = (double)vKernel[0]; double hCtr = (double)hKernel[0]; int kInd, xLeft, xRight, xUp, xDown; for (int band = 0; band < dnumBands ; band++) { int dstData[] = dstDataArrays[band]; int srcData[] = srcDataArrays[band]; int srcScanlineOffset = srcBandOffsets[band]; int dstScanlineOffset = dstBandOffsets[band]; // Step over source raster coordinates for (int ySrc = 0 ; ySrc < scaleY*dheight ; ySrc += scaleY) { int dInd = dstScanlineOffset; for (int xSrc = 0 ; xSrc < scaleX*dwidth ; xSrc += scaleX) { int upLeft0 = xSrc*srcPixelStride + ySrc*srcScanlineStride + srcScanlineOffset; int upRight0 = upLeft0 + stepRight; int dnLeft0 = upLeft0 + stepDown; int dnRight0 = upRight0 + stepDown; // Exploit 4-fold symmetry double sum = 0; // Make the rectangle squeeze in the vertical direction for (int iy = vKernel.length - 1 ; iy > vParity - 1 ; iy--) { upLeft = upLeft0; upRight = upRight0; dnLeft = dnLeft0; dnRight = dnRight0; // Make the rectangle squeeze in the horizontal direction for (int ix = hKernel.length - 1 ; ix > hParity - 1 ; ix--) { kk = hKernel[ix]*vKernel[iy]; sum += kk*((long)srcData[upLeft] + (long)srcData[upRight] + (long)srcData[dnLeft] + (long)srcData[dnRight]); upLeft += srcPixelStride; upRight -= srcPixelStride; dnLeft += srcPixelStride; dnRight -= srcPixelStride; } // ix upLeft0 += srcScanlineStride; // down a row upRight0 += srcScanlineStride; dnLeft0 -= srcScanlineStride; // up a row dnRight0 -= srcScanlineStride; } // iy // Compute the remaining 2-Fold symmetry portions // (across and down as needed) // Loop down the center (hParity is odd) if (hParity == 1) { xUp = (xSrc + hKernel.length - 1)*srcPixelStride + ySrc*srcScanlineStride + srcScanlineOffset; xDown = xUp + stepDown; kInd = vKernel.length - 1; while (xUp < xDown) { kk = hCtr*vKernel[kInd--]; sum += kk*((long)srcData[xUp] + (long)srcData[xDown]); xUp += srcScanlineStride; xDown -= srcScanlineStride; } } // hParity // Loop across the center (vParity is odd), pick up the center if hParity was odd if (vParity == 1) { xLeft = xSrc*srcPixelStride + (ySrc + vKernel.length - 1)*srcScanlineStride + srcScanlineOffset; xRight = xLeft + stepRight; kInd = hKernel.length - 1; while (xLeft < xRight) { kk = vCtr*hKernel[kInd--]; sum += kk*((long)(srcData[xLeft]) + (long)(srcData[xRight])); xLeft += srcPixelStride; xRight -= srcPixelStride; } // while xLeft // Grab the center pixel if hParity was odd also if (hParity == 1) sum += vCtr*hCtr*((long)srcData[xLeft]); } // if vParity dstData[dInd] = ImageUtil.clampInt((int)(sum + 0.5)); dInd += dstPixelStride; } // for xSrc dstScanlineOffset += dstScanlineStride; } // for ySrc } // for band return; } // computeRectInt /** computeRectFloat filter subsamples float pixel data. * * @param src RasterAccessor for source image. * @param dst RasterAccessor for output image. */ protected void computeRectFloat(RasterAccessor src, RasterAccessor dst) { // Get dimensions. int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); // Get destination data array references and strides. float dstDataArrays[][] = dst.getFloatDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); // Get source data array references and strides. float srcDataArrays[][] = src.getFloatDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); // Compute reused numbers int kernelNx = 2*hKernel.length - hParity; int kernelNy = 2*vKernel.length - vParity; int stepDown = (kernelNy - 1)*srcScanlineStride; int stepRight = (kernelNx - 1)*srcPixelStride; int upLeft, upRight, dnLeft, dnRight; double kk; double vCtr = (double)vKernel[0]; double hCtr = (double)hKernel[0]; int kInd, xLeft, xRight, xUp, xDown; for (int band = 0; band < dnumBands ; band++) { float dstData[] = dstDataArrays[band]; float srcData[] = srcDataArrays[band]; int srcScanlineOffset = srcBandOffsets[band]; int dstScanlineOffset = dstBandOffsets[band]; // Step over source raster coordinates for (int ySrc = 0 ; ySrc < scaleY*dheight ; ySrc += scaleY) { int dInd = dstScanlineOffset; for (int xSrc = 0 ; xSrc < scaleX*dwidth ; xSrc += scaleX) { int upLeft0 = xSrc*srcPixelStride + ySrc*srcScanlineStride + srcScanlineOffset; int upRight0 = upLeft0 + stepRight; int dnLeft0 = upLeft0 + stepDown; int dnRight0 = upRight0 + stepDown; // Exploit 4-fold symmetry double sum = 0; // Make the rectangle squeeze in the vertical direction for (int iy = vKernel.length - 1 ; iy > vParity - 1 ; iy--) { upLeft = upLeft0; upRight = upRight0; dnLeft = dnLeft0; dnRight = dnRight0; // Make the rectangle squeeze in the horizontal direction for (int ix = hKernel.length - 1 ; ix > hParity - 1 ; ix--) { kk = hKernel[ix]*vKernel[iy]; sum += kk*((double)srcData[upLeft] + (double)srcData[upRight] + (double)srcData[dnLeft] + (double)srcData[dnRight]); upLeft += srcPixelStride; upRight -= srcPixelStride; dnLeft += srcPixelStride; dnRight -= srcPixelStride; } // ix upLeft0 += srcScanlineStride; // down a row upRight0 += srcScanlineStride; dnLeft0 -= srcScanlineStride; // up a row dnRight0 -= srcScanlineStride; } // iy // Compute the remaining 2-Fold symmetry portions // (across and down as needed) // Loop down the center (hParity is odd) if (hParity == 1) { xUp = (xSrc + hKernel.length - 1)*srcPixelStride + ySrc*srcScanlineStride + srcScanlineOffset; xDown = xUp + stepDown; kInd = vKernel.length - 1; while (xUp < xDown) { kk = hCtr*vKernel[kInd--]; sum += kk*((double)srcData[xUp] + (double)srcData[xDown]); xUp += srcScanlineStride; xDown -= srcScanlineStride; } } // hParity // Loop across the center (vParity is odd), pick up the center if hParity was odd if (vParity == 1) { xLeft = xSrc*srcPixelStride + (ySrc + vKernel.length - 1)*srcScanlineStride + srcScanlineOffset; xRight = xLeft + stepRight; kInd = hKernel.length - 1; while (xLeft < xRight) { kk = vCtr*hKernel[kInd--]; sum += kk*((double)(srcData[xLeft]) + (double)(srcData[xRight])); xLeft += srcPixelStride; xRight -= srcPixelStride; } // while xLeft // Grab the center pixel if hParity was odd also if (hParity == 1) sum += vCtr*hCtr*((double)srcData[xLeft]); } // if vParity dstData[dInd] = ImageUtil.clampFloat(sum); dInd += dstPixelStride; } // for xSrc dstScanlineOffset += dstScanlineStride; } // for ySrc } // for band return; } // computeRectFloat /** computeRectDouble filter subsamples double pixel data. * * @param src RasterAccessor for source image. * @param dst RasterAccessor for output image. */ protected void computeRectDouble(RasterAccessor src, RasterAccessor dst) { // Get dimensions. int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); // Get destination data array references and strides. double dstDataArrays[][] = dst.getDoubleDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); // Get source data array references and strides. double srcDataArrays[][] = src.getDoubleDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); // Compute reused numbers int kernelNx = 2*hKernel.length - hParity; int kernelNy = 2*vKernel.length - vParity; int stepDown = (kernelNy - 1)*srcScanlineStride; int stepRight = (kernelNx - 1)*srcPixelStride; int upLeft, upRight, dnLeft, dnRight; double kk; double vCtr = (double)vKernel[0]; double hCtr = (double)hKernel[0]; int kInd, xLeft, xRight, xUp, xDown; for (int band = 0; band < dnumBands ; band++) { double dstData[] = dstDataArrays[band]; double srcData[] = srcDataArrays[band]; int srcScanlineOffset = srcBandOffsets[band]; int dstScanlineOffset = dstBandOffsets[band]; // Step over source raster coordinates for (int ySrc = 0 ; ySrc < scaleY*dheight ; ySrc += scaleY) { int dInd = dstScanlineOffset; for (int xSrc = 0 ; xSrc < scaleX*dwidth ; xSrc += scaleX) { int upLeft0 = xSrc*srcPixelStride + ySrc*srcScanlineStride + srcScanlineOffset; int upRight0 = upLeft0 + stepRight; int dnLeft0 = upLeft0 + stepDown; int dnRight0 = upRight0 + stepDown; // Exploit 4-fold symmetry double sum = 0; // Make the rectangle squeeze in the vertical direction for (int iy = vKernel.length - 1 ; iy > vParity - 1 ; iy--) { upLeft = upLeft0; upRight = upRight0; dnLeft = dnLeft0; dnRight = dnRight0; // Make the rectangle squeeze in the horizontal direction for (int ix = hKernel.length - 1 ; ix > hParity - 1 ; ix--) { kk = hKernel[ix]*vKernel[iy]; sum += kk*(srcData[upLeft] + srcData[upRight] + srcData[dnLeft] + srcData[dnRight]); upLeft += srcPixelStride; upRight -= srcPixelStride; dnLeft += srcPixelStride; dnRight -= srcPixelStride; } // ix upLeft0 += srcScanlineStride; // down a row upRight0 += srcScanlineStride; dnLeft0 -= srcScanlineStride; // up a row dnRight0 -= srcScanlineStride; } // iy // Compute the remaining 2-Fold symmetry portions // (across and down as needed) // Loop down the center (hParity is odd) if (hParity == 1) { xUp = (xSrc + hKernel.length - 1)*srcPixelStride + ySrc*srcScanlineStride + srcScanlineOffset; xDown = xUp + stepDown; kInd = vKernel.length - 1; while (xUp < xDown) { kk = hCtr*vKernel[kInd--]; sum += kk*(srcData[xUp] + srcData[xDown]); xUp += srcScanlineStride; xDown -= srcScanlineStride; } } // hParity // Loop across the center (vParity is odd), pick up the center if hParity was odd if (vParity == 1) { xLeft = xSrc*srcPixelStride + (ySrc + vKernel.length - 1)*srcScanlineStride + srcScanlineOffset; xRight = xLeft + stepRight; kInd = hKernel.length - 1; while (xLeft < xRight) { kk = vCtr*hKernel[kInd--]; sum += kk*(srcData[xLeft] + srcData[xRight]); xLeft += srcPixelStride; xRight -= srcPixelStride; } // while xLeft // Grab the center pixel if hParity was odd also if (hParity == 1) sum += vCtr*hCtr*srcData[xLeft]; } // if vParity dstData[dInd] = sum; dInd += dstPixelStride; } // for xSrc dstScanlineOffset += dstScanlineStride; } // for ySrc } // for band return; } // computeRectDouble } // class FilteredSubsampleOpImage jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MedianFilterRIF.java0000644000175000017500000000614710203035544027007 0ustar mathieumathieu/* * $RCSfile: MedianFilterRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:34 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; import java.util.Map; import javax.media.jai.operator.MedianFilterDescriptor; import javax.media.jai.operator.MedianFilterShape; /** * Creates a MedianFilterOpImage subclass for the given input * mask type * @see MedianFilterOpImage */ public class MedianFilterRIF implements RenderedImageFactory { /** Constructor. */ public MedianFilterRIF() {} /** * Create a new instance of MedianFilterOpImage in the rendered layer. * This method satisfies the implementation of RIF. * * @param paramBlock The source image and the convolution kernel. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); // Get BorderExtender from renderHints if any. BorderExtender extender = RIFUtil.getBorderExtenderHint(renderHints); MedianFilterShape maskType = (MedianFilterShape)paramBlock.getObjectParameter(0); int maskSize = paramBlock.getIntParameter(1); RenderedImage ri = paramBlock.getRenderedSource(0); if(maskType.equals(MedianFilterDescriptor.MEDIAN_MASK_SQUARE)) { return new MedianFilterSquareOpImage(ri, extender, renderHints, layout, maskSize); } else if(maskType.equals(MedianFilterDescriptor.MEDIAN_MASK_PLUS)) { return new MedianFilterPlusOpImage(ri, extender, renderHints, layout, maskSize); } else if(maskType.equals(MedianFilterDescriptor.MEDIAN_MASK_X)) { return new MedianFilterXOpImage(ri, extender, renderHints, layout, maskSize); } else if(maskType.equals(MedianFilterDescriptor.MEDIAN_MASK_SQUARE_SEPARABLE)) { return new MedianFilterSeparableOpImage(ri, extender, renderHints, layout, maskSize); } return null; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/DivideCRIF.java0000644000175000017500000000323410203035544025745 0ustar mathieumathieu/* * $RCSfile: DivideCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:23 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D.Float; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import java.awt.image.renderable.RenderContext; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "Divide" operation in the rendered * and renderable image layers. * * @since EA2 * @see javax.media.jai.operator.DivideDescriptor * @see DivideOpImage * */ public class DivideCRIF extends CRIFImpl { /** Constructor. */ public DivideCRIF() { super("divide"); } /** * Creates a new instance of DivideOpImage in the rendered * layer. This method satisfies the implementation of RIF. * * @param paramBlock The two source images to be divided. * @param renderHints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new DivideOpImage(paramBlock.getRenderedSource(0), paramBlock.getRenderedSource(1), renderHints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/PiecewiseOpImage.java0000644000175000017500000004520110203035544027254 0ustar mathieumathieu/* * $RCSfile: PiecewiseOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:40 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import javax.media.jai.ColormapOpImage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.Histogram; import javax.media.jai.ImageLayout; import javax.media.jai.LookupTableJAI; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; import com.sun.media.jai.util.ImageUtil; /** * An OpImage implementing the "Piecewise" operation. * *

The "Piecewise" operation maps the pixel values of an image using * a piecewise linear function represented by a set of breakpoints for each * band. The abscissa of each breakpoint is the source image gray level for * the band in question and the ordinate is the destination image gray level * to which it is mapped. * * @see javax.media.jai.operator.PiecewiseDescriptor * @see PiecewiseCRIF * * * @since EA4 */ final class PiecewiseOpImage extends ColormapOpImage { /** The abscissas of the breakpoints. */ private float[][] abscissas; /** The slope at each abscissa. */ private float[][] slopes; /** The intercept at each abscissa. */ private float[][] intercepts; /** The minimum values of the output per band. */ private float[] minOrdinates; /** The maximum values of the output per band. */ private float[] maxOrdinates; /** Flag indicating byte data. */ private boolean isByteData = false; /** A lookup table for use in the case of byte data. */ private LookupTableJAI lut; /** * Find the ordinate value for a given abscissa value. * * @param x The abscissa array. * @param minValue The minimum source gray level in the breakpoint set. * @param maxValue The maximum source gray level in the breakpoint set. * @param a The array of piecewise slopes. * @param b The array of piecewise ordinate intercepts. * @param value The source gray level. * @return The destination gray level. */ private static float binarySearch(float[] x, float minValue, float maxValue, float[] a, float[] b, float value) { int highIndex = x.length - 1; if(value <= x[0]) { return minValue; } else if(value >= x[highIndex]) { return maxValue; } int lowIndex = 0; int deltaIndex = highIndex - lowIndex; while(deltaIndex > 1) { int meanIndex = lowIndex + deltaIndex/2; if(value >= x[meanIndex]) { lowIndex = meanIndex; } else { highIndex = meanIndex; } deltaIndex = highIndex - lowIndex; } return a[lowIndex]*value + b[lowIndex]; } /** * Constructor. * * @param source The source image. * @param layout The destination image layout. * @param breakpoints The piecewise mapping stored by reference. The * arrays breakpoints[b][0] and breakpoints[b][1] represent the abscissas * and ordinates of the breakpoints, respectively, for band b. The * number of sets of breakpoints must be one or equal to the number of * image bands. */ public PiecewiseOpImage(RenderedImage source, Map config, ImageLayout layout, float[][][] breakpoints) { super(source, layout, config, true); // Ensure that the number of sets of breakpoints is either unity // or equal to the number of bands. int numBands = sampleModel.getNumBands(); // Initalize the instance variables. initFields(numBands, breakpoints); // Set the byte data flag. isByteData = sampleModel.getTransferType() == DataBuffer.TYPE_BYTE; // Perform byte-specific initialization. if(isByteData) { // Initialize the lookup table. createLUT(); // Clear the other instance variables for the garbage collector. unsetFields(); } // Set flag to permit in-place operation. permitInPlaceOperation(); // Initialize the colormap if necessary. initializeColormapOperation(); } /** * Transform the colormap according to the rescaling parameters. */ protected void transformColormap(byte[][] colormap) { byte byteTable[][] = lut.getByteData(); for(int b = 0; b < 3; b++) { byte[] map = colormap[b]; byte[] luTable = byteTable[b >= byteTable.length ? 0 : b]; int mapSize = map.length; for(int i = 0; i < mapSize; i++) { map[i] = luTable[(map[i] & 0xFF)]; } } } /** * Initialize various instance variables from the array of breakpoints. * The principal derived values are the slope and ordinate-intercept of * each piecewise segment. * * @param numBands The number of bands in the image. * @param The breakpoints as breakpoints[numBands][0..1][numPoints]. */ private void initFields(int numBands, float[][][] breakpoints) { abscissas = new float[numBands][]; slopes = new float[numBands][]; intercepts = new float[numBands][]; minOrdinates = new float[numBands]; maxOrdinates = new float[numBands]; for(int band = 0; band < numBands; band++) { abscissas[band] = breakpoints.length == 1 ? breakpoints[0][0] : breakpoints[band][0]; int maxIndex = abscissas[band].length - 1; minOrdinates[band] = breakpoints.length == 1 ? breakpoints[0][1][0] : breakpoints[band][1][0]; maxOrdinates[band] = breakpoints.length == 1 ? breakpoints[0][1][maxIndex] : breakpoints[band][1][maxIndex]; slopes[band] = new float[maxIndex]; intercepts[band] = new float[maxIndex]; float[] x = abscissas[band]; float[] y = breakpoints.length == 1 ? breakpoints[0][1] : breakpoints[band][1]; float[] a = slopes[band]; float[] b = intercepts[band]; for(int i1 = 0; i1 < maxIndex; i1++) { int i2 = i1 + 1; a[i1] = (y[i2]-y[i1])/(x[i2] - x[i1]); b[i1] = y[i1] - x[i1]*a[i1]; } } } /** * Clear all instance fields which are ununsed references so the GC * may clear them. */ private void unsetFields() { abscissas = null; slopes = null; intercepts = null; minOrdinates = null; maxOrdinates = null; } /** * Create a lookup table to be used in the case of byte data. */ private void createLUT() { // Allocate memory for the data array references. int numBands = abscissas.length; byte[][] data = new byte[numBands][]; // Generate the data for each band. for(int band = 0; band < numBands; band++) { // Allocate memory for this band. data[band] = new byte[256]; // Cache the references to avoid extra indexing. byte[] table = data[band]; float[] x = abscissas[band]; float[] a = slopes[band]; float[] b = intercepts[band]; float yL = minOrdinates[band]; float yH = maxOrdinates[band]; // Initialize the lookup table data. for(int value = 0; value < 256; value++) { table[value] = ImageUtil.clampRoundByte(binarySearch(x, yL, yH, a, b, value)); } } // Construct the lookup table. lut = new LookupTableJAI(data); } /** * Piecewises to the pixel values within a specified rectangle. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); if(isByteData) { computeRectByte(sources, dest, destRect); } else { RasterAccessor dst = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); RasterAccessor src = new RasterAccessor(sources[0], destRect, formatTags[0], getSource(0).getColorModel()); switch (dst.getDataType()) { case DataBuffer.TYPE_USHORT: computeRectUShort(src, dst); break; case DataBuffer.TYPE_SHORT: computeRectShort(src, dst); break; case DataBuffer.TYPE_INT: computeRectInt(src, dst); break; case DataBuffer.TYPE_FLOAT: computeRectFloat(src, dst); break; case DataBuffer.TYPE_DOUBLE: computeRectDouble(src, dst); break; } dst.copyDataToRaster(); } } private void computeRectByte(Raster[] sources, WritableRaster dest, Rectangle destRect) { lut.lookup(sources[0], dest, destRect); } private void computeRectUShort(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); short[][] dstData = dst.getShortDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); short[][] srcData = src.getShortDataArrays(); for (int b = 0; b < dstBands; b++) { short[] d = dstData[b]; short[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; // Cache the references to avoid extra indexing. float[] x = abscissas[b]; float[] gain = slopes[b]; float[] bias = intercepts[b]; float yL = minOrdinates[b]; float yH = maxOrdinates[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = ImageUtil.clampRoundUShort(binarySearch(x, yL, yH, gain, bias, s[srcPixelOffset] & 0xFFFF)); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectShort(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); short[][] dstData = dst.getShortDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); short[][] srcData = src.getShortDataArrays(); for (int b = 0; b < dstBands; b++) { short[] d = dstData[b]; short[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; // Cache the references to avoid extra indexing. float[] x = abscissas[b]; float[] gain = slopes[b]; float[] bias = intercepts[b]; float yL = minOrdinates[b]; float yH = maxOrdinates[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = ImageUtil.clampRoundShort(binarySearch(x, yL, yH, gain, bias, s[srcPixelOffset])); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectInt(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); int[][] dstData = dst.getIntDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); int[][] srcData = src.getIntDataArrays(); for (int b = 0; b < dstBands; b++) { int[] d = dstData[b]; int[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; // Cache the references to avoid extra indexing. float[] x = abscissas[b]; float[] gain = slopes[b]; float[] bias = intercepts[b]; float yL = minOrdinates[b]; float yH = maxOrdinates[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = ImageUtil.clampRoundInt(binarySearch(x, yL, yH, gain, bias, s[srcPixelOffset])); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectFloat(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); float[][] dstData = dst.getFloatDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); float[][] srcData = src.getFloatDataArrays(); for (int b = 0; b < dstBands; b++) { float[] d = dstData[b]; float[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; // Cache the references to avoid extra indexing. float[] x = abscissas[b]; float[] gain = slopes[b]; float[] bias = intercepts[b]; float yL = minOrdinates[b]; float yH = maxOrdinates[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = binarySearch(x, yL, yH, gain, bias, s[srcPixelOffset]); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectDouble(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); double[][] dstData = dst.getDoubleDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); double[][] srcData = src.getDoubleDataArrays(); for (int b = 0; b < dstBands; b++) { double[] d = dstData[b]; double[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; // Cache the references to avoid extra indexing. float[] x = abscissas[b]; float[] gain = slopes[b]; float[] bias = intercepts[b]; float yL = minOrdinates[b]; float yH = maxOrdinates[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = binarySearch(x, yL, yH, gain, bias, (float)s[srcPixelOffset]); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/OrConstCRIF.java0000644000175000017500000000273410203035544026134 0ustar mathieumathieu/* * $RCSfile: OrConstCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:38 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "OrConst" operation in the rendered * and renderable image layers. * * @see javax.media.jai.operator.OrConstDescriptor * @see OrConstOpImage * * * @since EA2 */ public class OrConstCRIF extends CRIFImpl { /** Constructor. */ public OrConstCRIF() { super("orconst"); } /** * Creates a new instance of OrConstOpImage in the * rendered layer. * * @param args The source image and the constants. * @param hints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new OrConstOpImage(args.getRenderedSource(0), renderHints, layout, (int[])args.getObjectParameter(0)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MultiplyConstOpImage.java0000644000175000017500000003304110203035544030164 0ustar mathieumathieu/* * $RCSfile: MultiplyConstOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:37 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import javax.media.jai.ColormapOpImage; import com.sun.media.jai.util.ImageUtil; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; /** * An OpImage implementing the "MultiplyConst" operation. * *

This OpImage multiplies a set of constants, one for * each band of the source image, to the pixels of a rendered image. * The destination pixel values are calculated as: *

 *     for (int h = 0; h < dstHeight; h++) {
 *         for (int w = 0; w < dstWidth; w++) {
 *             for (int b = 0; b < dstNumBands; b++) {
 *                 if (constants.length < dstNumBands) {
 *                     dst[h][w][b] = srcs[h][w][b] * constants[0];
 *                 } else {
 *                     dst[h][w][b] = srcs[h][w][b] * constants[b];
 *                 }
 *             }
 *         }
 *     }
 * 
* * @see javax.media.jai.operator.MultiplyConstDescriptor * @see MultiplyConstCRIF * * * @since EA2 */ final class MultiplyConstOpImage extends ColormapOpImage { /** The constants to be multiplied, one for each band. */ protected double[] constants; /** * Constructor. * * @param source The source image. * @param layout The destination image layout. * @param constants The constants to be multiplied, stored as reference. */ public MultiplyConstOpImage(RenderedImage source, Map config, ImageLayout layout, double[] constants) { super(source, layout, config, true); int numBands = getSampleModel().getNumBands(); if (constants.length < numBands) { this.constants = new double[numBands]; for (int i = 0; i < numBands; i++) { this.constants[i] = constants[0]; } } else { this.constants = (double[])constants.clone(); } // Set flag to permit in-place operation. permitInPlaceOperation(); // Initialize the colormap if necessary. initializeColormapOperation(); } /** * Transform the colormap according to the rescaling parameters. */ protected void transformColormap(byte[][] colormap) { for(int b = 0; b < 3; b++) { byte[] map = colormap[b]; int mapSize = map.length; double c = b < constants.length ? constants[b] : constants[0]; for(int i = 0; i < mapSize; i++) { map[i] = ImageUtil.clampRoundByte((map[i] & 0xFF) * c); } } } /** * Multiplies a constant to the pixel values within a specified rectangle. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); Rectangle srcRect = mapDestRect(destRect, 0); RasterAccessor dst = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); RasterAccessor src = new RasterAccessor(sources[0], srcRect, formatTags[0], getSource(0).getColorModel()); switch (dst.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(src, dst); break; case DataBuffer.TYPE_USHORT: computeRectUShort(src, dst); break; case DataBuffer.TYPE_SHORT: computeRectShort(src, dst); break; case DataBuffer.TYPE_INT: computeRectInt(src, dst); break; case DataBuffer.TYPE_FLOAT: computeRectFloat(src, dst); break; case DataBuffer.TYPE_DOUBLE: computeRectDouble(src, dst); break; } if (dst.needsClamping()) { /* Further clamp down to underlying raster data type. */ dst.clampDataArrays(); } dst.copyDataToRaster(); } private void computeRectByte(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); byte[][] dstData = dst.getByteDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); byte[][] srcData = src.getByteDataArrays(); for (int b = 0; b < dstBands; b++) { float c = (float)constants[b]; byte[] d = dstData[b]; byte[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = ImageUtil.clampRoundByte( (s[srcPixelOffset] & 0xFF) * c); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectUShort(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); short[][] dstData = dst.getShortDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); short[][] srcData = src.getShortDataArrays(); for (int b = 0; b < dstBands; b++) { float c = (float)constants[b]; short[] d = dstData[b]; short[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = ImageUtil.clampRoundUShort( (s[srcPixelOffset] & 0xFFFF) * c); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectShort(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); short[][] dstData = dst.getShortDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); short[][] srcData = src.getShortDataArrays(); for (int b = 0; b < dstBands; b++) { float c = (float)constants[b]; short[] d = dstData[b]; short[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = ImageUtil.clampRoundShort(s[srcPixelOffset] * c); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectInt(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); int[][] dstData = dst.getIntDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); int[][] srcData = src.getIntDataArrays(); for (int b = 0; b < dstBands; b++) { double c = constants[b]; int[] d = dstData[b]; int[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = ImageUtil.clampRoundInt(s[srcPixelOffset] * c); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectFloat(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); float[][] dstData = dst.getFloatDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); float[][] srcData = src.getFloatDataArrays(); for (int b = 0; b < dstBands; b++) { double c = constants[b]; float[] d = dstData[b]; float[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = ImageUtil.clampFloat(s[srcPixelOffset] * c); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectDouble(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); double[][] dstData = dst.getDoubleDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); double[][] srcData = src.getDoubleDataArrays(); for (int b = 0; b < dstBands; b++) { double c = constants[b]; double[] d = dstData[b]; double[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = s[srcPixelOffset] * c; dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ScaleCRIF.java0000644000175000017500000002200010203035544025560 0ustar mathieumathieu/* * $RCSfile: ScaleCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:42 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.DataBuffer; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.renderable.RenderedImageFactory; import java.awt.image.renderable.RenderContext; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationNearest; import javax.media.jai.InterpolationBilinear; import javax.media.jai.InterpolationBicubic; import javax.media.jai.InterpolationBicubic2; import javax.media.jai.InterpolationTable; import javax.media.jai.TileCache; import javax.media.jai.CRIFImpl; import java.util.Map; /** * @see ScaleOpImage */ public class ScaleCRIF extends CRIFImpl { private static final float TOLERANCE = 0.01F; /** Constructor. */ public ScaleCRIF() { super("scale"); } /** * Creates a new instance of ScaleOpImage in the rendered layer. * This method satisfies the implementation of RIF. * * @param paramBlock The source image, the X and Y scale factor, * and the interpolation method for resampling. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); // Get TileCache from renderHints if any. TileCache cache = RIFUtil.getTileCacheHint(renderHints); // Get BorderExtender from renderHints if any. BorderExtender extender = RIFUtil.getBorderExtenderHint(renderHints); RenderedImage source = paramBlock.getRenderedSource(0); float xScale = paramBlock.getFloatParameter(0); float yScale = paramBlock.getFloatParameter(1); float xTrans = paramBlock.getFloatParameter(2); float yTrans = paramBlock.getFloatParameter(3); Interpolation interp = (Interpolation)paramBlock.getObjectParameter(4); // Check and see if we are scaling by 1.0 in both x and y and no // translations. If so call the copy operation. if (xScale == 1.0F && yScale == 1.0F && xTrans == 0.0F && yTrans == 0.0F) { return new CopyOpImage(source, renderHints, layout); } // Check to see whether the operation specified is a pure // integer translation. If so call translate // If the hints contain an ImageLayout hint, then we can't use // TranslateIntOpImage since that can't deal with the ImageLayout hint if (xScale == 1.0F && yScale == 1.0F && (Math.abs(xTrans - (int)xTrans) < TOLERANCE) && (Math.abs(yTrans - (int)yTrans) < TOLERANCE) && layout == null) { // It's an integer translate. return new TranslateIntOpImage(source, renderHints, (int)xTrans, (int)yTrans); } if (interp instanceof InterpolationNearest) { // // Special case -- if the image is represented using // a MultiPixelPackedSampleModel and a byte, ushort, // or int DataBuffer we can access the pixel data directly. // Note that there is a potential loophole that has not been // resolved by Java2D as to whether the underlying DataBuffer // must be of one of the standard types. Here we make the // assumption that it will be -- we can't check without // forcing an actual tile to be computed. // SampleModel sm = source.getSampleModel(); if ((sm instanceof MultiPixelPackedSampleModel) && (sm.getSampleSize(0) == 1) && (sm.getDataType() == DataBuffer.TYPE_BYTE || sm.getDataType() == DataBuffer.TYPE_USHORT || sm.getDataType() == DataBuffer.TYPE_INT)) { return new ScaleNearestBinaryOpImage(source, extender, renderHints, layout, xScale, yScale, xTrans, yTrans, interp); } else { return new ScaleNearestOpImage(source, extender, renderHints, layout, xScale, yScale, xTrans, yTrans, interp); } } else if (interp instanceof InterpolationBilinear) { SampleModel sm = source.getSampleModel(); if ((sm instanceof MultiPixelPackedSampleModel) && (sm.getSampleSize(0) == 1) && (sm.getDataType() == DataBuffer.TYPE_BYTE || sm.getDataType() == DataBuffer.TYPE_USHORT || sm.getDataType() == DataBuffer.TYPE_INT)) { return new ScaleBilinearBinaryOpImage(source, extender, renderHints, layout, xScale, yScale, xTrans, yTrans, interp); } else { return new ScaleBilinearOpImage(source, extender, renderHints, layout, xScale, yScale, xTrans, yTrans, interp); } } else if ((interp instanceof InterpolationBicubic) || (interp instanceof InterpolationBicubic2)) { return new ScaleBicubicOpImage(source, extender, renderHints, layout, xScale, yScale, xTrans, yTrans, interp); } else { return new ScaleGeneralOpImage(source, extender, renderHints, layout, xScale, yScale, xTrans, yTrans, interp); } } /** * Creates a new instance of AffineOpImage * in the renderable layer. This method satisfies the * implementation of CRIF. */ public RenderedImage create(RenderContext renderContext, ParameterBlock paramBlock) { return paramBlock.getRenderedSource(0); } /** * Maps the output RenderContext into the RenderContext for the ith * source. * This method satisfies the implementation of CRIF. * * @param i The index of the source image. * @param renderContext The renderContext being applied to the operation. * @param paramBlock The ParameterBlock containing the sources * and the translation factors. * @param image The RenderableImageOp from which this method * was called. */ public RenderContext mapRenderContext(int i, RenderContext renderContext, ParameterBlock paramBlock, RenderableImage image) { float scale_x = paramBlock.getFloatParameter(0); float scale_y = paramBlock.getFloatParameter(1); float trans_x = paramBlock.getFloatParameter(2); float trans_y = paramBlock.getFloatParameter(3); AffineTransform scale = new AffineTransform(scale_x, 0.0, 0.0, scale_y, trans_x, trans_y); RenderContext RC = (RenderContext)renderContext.clone(); AffineTransform usr2dev = RC.getTransform(); usr2dev.concatenate(scale); RC.setTransform(usr2dev); return RC; } /** * Gets the bounding box for the output of ScaleOpImage. * This method satisfies the implementation of CRIF. */ public Rectangle2D getBounds2D(ParameterBlock paramBlock) { RenderableImage source = paramBlock.getRenderableSource(0); float scale_x = paramBlock.getFloatParameter(0); float scale_y = paramBlock.getFloatParameter(1); float trans_x = paramBlock.getFloatParameter(2); float trans_y = paramBlock.getFloatParameter(3); Interpolation interp = (Interpolation)paramBlock.getObjectParameter(4); // Get the source dimensions float x0 = (float)source.getMinX(); float y0 = (float)source.getMinY() ; float w = (float)source.getWidth(); float h = (float)source.getHeight(); // Forward map the source using x0, y0, w and h float d_x0 = x0 * scale_x + trans_x; float d_y0 = y0 * scale_y + trans_y; float d_w = w * scale_x; float d_h = h * scale_y; return new Rectangle2D.Float(d_x0, d_y0, d_w, d_h); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/NotOpImage.java0000644000175000017500000002353110203035544026101 0ustar mathieumathieu/* * $RCSfile: NotOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:37 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import javax.media.jai.ColormapOpImage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage implementing the "Not" operation as * described in javax.media.jai.operator.NotDescriptor. * *

This OpImage performs a logical "not" operation on * the the pixel values of the source image on a per-band basis. * *

The value of the pixel (x, y) in the destination image is defined as: *

 * for (b = 0; b < numBands; b++) {
 *     dst[y][x][b] = ~(src[y][x][b]);
 * }
 * 
* * The data type byte is treated as unsigned, with maximum * value as 255 and minimum value as 0. * * @since EA2 * @see javax.media.jai.operator.NotDescriptor * @see NotCRIF * */ final class NotOpImage extends ColormapOpImage { /** * Constructs an NotOpImage. * * @param source The source image. * @param layout The destination image layout. */ public NotOpImage(RenderedImage source, Map config, ImageLayout layout) { super(source, layout, config, true); // Set flag to permit in-place operation. permitInPlaceOperation(); // Initialize the colormap if necessary. initializeColormapOperation(); } /** * Transform the colormap according to the rescaling parameters. */ protected void transformColormap(byte[][] colormap) { for(int b = 0; b < 3; b++) { byte[] map = colormap[b]; int mapSize = map.length; for(int i = 0; i < mapSize; i++) { map[i] = (byte)(~map[i]); } } } /** * Nots the pixel values of the source image within a specified * rectangle. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); /* For ColormapOpImage, srcRect = destRect. */ RasterAccessor src = new RasterAccessor(sources[0], destRect, formatTags[0], getSource(0).getColorModel()); RasterAccessor dst = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); if(dst.isBinary()) { byte[] srcBits = src.getBinaryDataArray(); byte[] dstBits = dst.getBinaryDataArray(); int length = dstBits.length; for(int i = 0; i < length; i++) { dstBits[i] = (byte)(~(srcBits[i])); } dst.copyBinaryDataToRaster(); return; } int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); int dstNumBands = dst.getNumBands(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); switch (dst.getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(dstNumBands, dstWidth, dstHeight, srcLineStride, srcPixelStride, srcBandOffsets, src.getByteDataArrays(), dstLineStride, dstPixelStride, dstBandOffsets, dst.getByteDataArrays()); break; case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: shortLoop(dstNumBands, dstWidth, dstHeight, srcLineStride, srcPixelStride, srcBandOffsets, src.getShortDataArrays(), dstLineStride, dstPixelStride, dstBandOffsets, dst.getShortDataArrays()); break; case DataBuffer.TYPE_INT: intLoop(dstNumBands, dstWidth, dstHeight, srcLineStride, srcPixelStride, srcBandOffsets, src.getIntDataArrays(), dstLineStride, dstPixelStride, dstBandOffsets, dst.getIntDataArrays()); break; } dst.copyDataToRaster(); } private void byteLoop(int dstNumBands, int dstWidth, int dstHeight, int srcLineStride, int srcPixelStride, int[] srcBandOffsets, byte[][] srcData, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, byte[][] dstData) { for (int b = 0; b < dstNumBands; b++) { byte[] s = srcData[b]; byte[] d = dstData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = (byte)(~(s[srcPixelOffset])); srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } private void shortLoop(int dstNumBands, int dstWidth, int dstHeight, int srcLineStride, int srcPixelStride, int[] srcBandOffsets, short[][] srcData, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, short[][] dstData) { for (int b = 0; b < dstNumBands; b++) { short[] s = srcData[b]; short[] d = dstData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = (short)(~(s[srcPixelOffset])); srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } private void intLoop(int dstNumBands, int dstWidth, int dstHeight, int srcLineStride, int srcPixelStride, int[] srcBandOffsets, int[][] srcData, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, int[][] dstData) { for (int b = 0; b < dstNumBands; b++) { int[] s = srcData[b]; int[] d = dstData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = ~(s[srcPixelOffset]); srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } // public static void main(String args[]) { // System.out.println("NotOpImage Test"); // ImageLayout layout; // OpImage src, dst; // Rectangle rect = new Rectangle(0, 0, 5, 5); // System.out.println("1. PixelInterleaved byte 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 800, 800, 0, 0, 200, 200, DataBuffer.TYPE_BYTE, 3, false); // src = OpImageTester.createRandomOpImage(layout); // dst = new NotOpImage(src, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("2. Banded byte 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 800, 800, 0, 0, 200, 200, DataBuffer.TYPE_BYTE, 3, true); // src = OpImageTester.createRandomOpImage(layout); // dst = new NotOpImage(src, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("3. PixelInterleaved int 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_INT, 3, false); // src = OpImageTester.createRandomOpImage(layout); // dst = new NotOpImage(src, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("4. Banded int 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_INT, 3, true); // src = OpImageTester.createRandomOpImage(layout); // dst = new NotOpImage(src, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/DCTCRIF.java0000644000175000017500000000244310203035544025154 0ustar mathieumathieu/* * $RCSfile: DCTCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:21 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "DCT" operation in the rendered * image layer. * * @since Beta * @see javax.media.jai.operator.DCTDescriptor * */ public class DCTCRIF extends CRIFImpl { /** Constructor. */ public DCTCRIF() { super("dct"); } /** * Creates a new instance of a DCT operator. * * @param paramBlock The scaling type. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); RenderedImage source = paramBlock.getRenderedSource(0); return new DCTOpImage(source, renderHints, layout, new FCT(true, 2)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/BandCombineCRIF.java0000644000175000017500000000304410203035544026701 0ustar mathieumathieu/* * $RCSfile: BandCombineCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:14 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "BandCombine" operation in the * rendered and renderable image layers. * * @see javax.media.jai.operator.BandCombineDescriptor * @see BandCombineOpImage * * * @since EA3 */ public class BandCombineCRIF extends CRIFImpl { /** Constructor. */ public BandCombineCRIF() { super("bandcombine"); } /** * Creates a new instance of BandCombineOpImage * in the rendered layer. * * @param args The source image and the constants. * @param hints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new BandCombineOpImage(args.getRenderedSource(0), renderHints, layout, (double[][])args.getObjectParameter(0)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MaxFilterRIF.java0000644000175000017500000000604510203035544026334 0ustar mathieumathieu/* * $RCSfile: MaxFilterRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:32 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; import java.util.Map; import javax.media.jai.operator.MaxFilterDescriptor; import javax.media.jai.operator.MaxFilterShape; /** * Creates a MaxFilterOpImage subclass for the given input * mask type * @see MaxFilterOpImage */ public class MaxFilterRIF implements RenderedImageFactory { /** Constructor. */ public MaxFilterRIF() {} /** * Create a new instance of MaxFilterOpImage in the rendered layer. * This method satisfies the implementation of RIF. * * @param paramBlock The source image and the convolution kernel. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); // Get BorderExtender from renderHints if any. BorderExtender extender = RIFUtil.getBorderExtenderHint(renderHints); MaxFilterShape maskType = (MaxFilterShape)paramBlock.getObjectParameter(0); int maskSize = paramBlock.getIntParameter(1); RenderedImage ri = paramBlock.getRenderedSource(0); if(maskType.equals(MaxFilterDescriptor.MAX_MASK_SQUARE)) { return new MaxFilterSquareOpImage(ri, extender, renderHints, layout, maskSize); } else if(maskType.equals(MaxFilterDescriptor.MAX_MASK_PLUS)) { return new MaxFilterPlusOpImage(ri, extender, renderHints, layout, maskSize); } else if(maskType.equals(MaxFilterDescriptor.MAX_MASK_X)) { return new MaxFilterXOpImage(ri, extender, renderHints, layout, maskSize); } else if(maskType.equals(MaxFilterDescriptor.MAX_MASK_SQUARE_SEPARABLE)) { return new MaxFilterSeparableOpImage(ri, extender, renderHints, layout, maskSize); } return null; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/OrConstOpImage.java0000644000175000017500000002123310203035544026725 0ustar mathieumathieu/* * $RCSfile: OrConstOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:38 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import javax.media.jai.ColormapOpImage; import com.sun.media.jai.util.ImageUtil; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; /** * An OpImage implementing the "OrConst" operation. * *

This OpImage logically "ors" the pixels of a rendered * image with a set of constants, one for each band of the source image. * The destination pixel values are calculated as: *

 *     for (int h = 0; h < dstHeight; h++) {
 *         for (int w = 0; w < dstWidth; w++) {
 *             for (int b = 0; b < dstNumBands; b++) {
 *                 if (constants.length < dstNumBands) {
 *                     dst[h][w][b] = srcs[h][w][b] | constants[0];
 *                 } else {
 *                     dst[h][w][b] = srcs[h][w][b] | constants[b];
 *                 }
 *             }
 *         }
 *     }
 * 
* * @see javax.media.jai.operator.OrConstDescriptor * @see OrConstCRIF * * * @since EA2 */ final class OrConstOpImage extends ColormapOpImage { /** The constants to be ored, one for each band. */ protected int[] constants; /** * Constructor. * * @param source The source image. * @param layout The destination image layout. * @param constants The constants to be ored, stored as reference. */ public OrConstOpImage(RenderedImage source, Map config, ImageLayout layout, int[] constants) { super(source, layout, config, true); int numBands = getSampleModel().getNumBands(); if (constants.length < numBands) { this.constants = new int[numBands]; for (int i = 0; i < numBands; i++) { this.constants[i] = constants[0]; } } else { this.constants = (int[])constants.clone(); } // Set flag to permit in-place operation. permitInPlaceOperation(); // Initialize the colormap if necessary. initializeColormapOperation(); } /** * Transform the colormap according to the rescaling parameters. */ protected void transformColormap(byte[][] colormap) { for(int b = 0; b < 3; b++) { byte[] map = colormap[b]; int mapSize = map.length; int c = b < constants.length ? constants[b] : constants[0]; for(int i = 0; i < mapSize; i++) { map[i] = ImageUtil.clampRoundByte((map[i] & 0xFF) | c); } } } /** * Logically "ors" a constant with the pixel values within a specified * rectangle. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); Rectangle srcRect = mapDestRect(destRect, 0); RasterAccessor dst = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); RasterAccessor src = new RasterAccessor(sources[0], srcRect, formatTags[0], getSource(0).getColorModel()); switch (dst.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(src, dst); break; case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: computeRectShort(src, dst); break; case DataBuffer.TYPE_INT: computeRectInt(src, dst); break; } /* Do not clamp dst data. */ dst.copyDataToRaster(); } private void computeRectByte(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); byte[][] dstData = dst.getByteDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); byte[][] srcData = src.getByteDataArrays(); for (int b = 0; b < dstBands; b++) { int c = constants[b]; byte[] d = dstData[b]; byte[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = (byte)(s[srcPixelOffset] | c); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectShort(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); short[][] dstData = dst.getShortDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); short[][] srcData = src.getShortDataArrays(); for (int b = 0; b < dstBands; b++) { int c = constants[b]; short[] d = dstData[b]; short[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = (short)(s[srcPixelOffset] | c); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectInt(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); int[][] dstData = dst.getIntDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); int[][] srcData = src.getIntDataArrays(); for (int b = 0; b < dstBands; b++) { int c = constants[b]; int[] d = dstData[b]; int[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = s[srcPixelOffset] | c; dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MaxFilterSeparableOpImage.java0000644000175000017500000005347110203035544031061 0ustar mathieumathieu/* * $RCSfile: MaxFilterSeparableOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:32 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import java.util.Map; import javax.media.jai.operator.MaxFilterDescriptor; import com.sun.media.jai.opimage.MaxFilterOpImage; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class to perform max filtering on a source image. * */ final class MaxFilterSeparableOpImage extends MaxFilterOpImage { /** * Creates a MaxFilterSeperableOpImage with the given source and * maskSize. The image dimensions are derived from the source * image. The tile grid layout, SampleModel, and ColorModel may * optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param maskSize the mask size. */ public MaxFilterSeparableOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, int maskSize) { super(source, extender, config, layout, MaxFilterDescriptor.MAX_MASK_SQUARE, maskSize); } protected void byteLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); byte dstDataArrays[][] = dst.getByteDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); byte srcDataArrays[][] = src.getByteDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int maxValues[] = new int[filterSize]; int val, maxval; int wp = filterSize; int tmpBuffer[] = new int[filterSize*dwidth]; int tmpBufferSize = filterSize*dwidth; for (int k = 0; k < dnumBands; k++) { byte dstData[] = dstDataArrays[k]; byte srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; int revolver = 0; for (int j = 0; j < filterSize-1; j++) { int srcPixelOffset = srcScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; maxval = Integer.MIN_VALUE; for (int v = 0; v < wp; v++) { val = srcData[imageOffset] & 0xff; imageOffset += srcPixelStride; maxval = (val > maxval) ? val : maxval; } tmpBuffer[revolver+i] = maxval; srcPixelOffset += srcPixelStride; } revolver += dwidth; srcScanlineOffset += srcScanlineStride; } // srcScanlineStride already bumped by // filterSize-1*scanlineStride for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; maxval = Integer.MIN_VALUE; for (int v = 0; v < wp; v++) { val = srcData[imageOffset] & 0xff; imageOffset += srcPixelStride; maxval = (val > maxval) ? val : maxval; } tmpBuffer[revolver + i] = maxval; maxval = Integer.MIN_VALUE; for (int b = i; b < tmpBufferSize; b += dwidth) { val = tmpBuffer[b]; maxval = (val > maxval) ? val : maxval; } dstData[dstPixelOffset] = (byte)maxval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } revolver += dwidth; if (revolver == tmpBufferSize) { revolver = 0; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void shortLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int maxValues[] = new int[filterSize]; int val, maxval; int wp = filterSize; int tmpBuffer[] = new int[filterSize*dwidth]; int tmpBufferSize = filterSize*dwidth; for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; int revolver = 0; for (int j = 0; j < filterSize-1; j++) { int srcPixelOffset = srcScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; maxval = Integer.MIN_VALUE; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += srcPixelStride; maxval = (val > maxval) ? val : maxval; } tmpBuffer[revolver+i] = maxval; srcPixelOffset += srcPixelStride; } revolver += dwidth; srcScanlineOffset += srcScanlineStride; } // srcScanlineStride already bumped by // filterSize-1*scanlineStride for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; maxval = Integer.MIN_VALUE; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += srcPixelStride; maxval = (val > maxval) ? val : maxval; } tmpBuffer[revolver + i] = maxval; maxval = Integer.MIN_VALUE; for (int b = i; b < tmpBufferSize; b += dwidth) { val = tmpBuffer[b]; maxval = (val > maxval) ? val : maxval; } dstData[dstPixelOffset] = (short)maxval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } revolver += dwidth; if (revolver == tmpBufferSize) { revolver = 0; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void ushortLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int maxValues[] = new int[filterSize]; int val, maxval; int wp = filterSize; int tmpBuffer[] = new int[filterSize*dwidth]; int tmpBufferSize = filterSize*dwidth; for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; int revolver = 0; for (int j = 0; j < filterSize-1; j++) { int srcPixelOffset = srcScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; maxval = Integer.MIN_VALUE; for (int v = 0; v < wp; v++) { val = srcData[imageOffset] & 0xfff; imageOffset += srcPixelStride; maxval = (val > maxval) ? val : maxval; } tmpBuffer[revolver+i] = maxval; srcPixelOffset += srcPixelStride; } revolver += dwidth; srcScanlineOffset += srcScanlineStride; } // srcScanlineStride already bumped by // filterSize-1*scanlineStride for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; maxval = Integer.MIN_VALUE; for (int v = 0; v < wp; v++) { val = srcData[imageOffset] & 0xffff; imageOffset += srcPixelStride; maxval = (val > maxval) ? val : maxval; } tmpBuffer[revolver + i] = maxval; maxval = Integer.MIN_VALUE; for (int b = i; b < tmpBufferSize; b += dwidth) { val = tmpBuffer[b]; maxval = (val > maxval) ? val : maxval; } dstData[dstPixelOffset] = (short)maxval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } revolver += dwidth; if (revolver == tmpBufferSize) { revolver = 0; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void intLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); int dstDataArrays[][] = dst.getIntDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcDataArrays[][] = src.getIntDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int maxValues[] = new int[filterSize]; int val, maxval; int wp = filterSize; int tmpBuffer[] = new int[filterSize*dwidth]; int tmpBufferSize = filterSize*dwidth; for (int k = 0; k < dnumBands; k++) { int dstData[] = dstDataArrays[k]; int srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; int revolver = 0; for (int j = 0; j < filterSize-1; j++) { int srcPixelOffset = srcScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; maxval = Integer.MIN_VALUE; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += srcPixelStride; maxval = (val > maxval) ? val : maxval; } tmpBuffer[revolver+i] = maxval; srcPixelOffset += srcPixelStride; } revolver += dwidth; srcScanlineOffset += srcScanlineStride; } // srcScanlineStride already bumped by // filterSize-1*scanlineStride for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; maxval = Integer.MIN_VALUE; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += srcPixelStride; maxval = (val > maxval) ? val : maxval; } tmpBuffer[revolver + i] = maxval; maxval = Integer.MIN_VALUE; for (int b = i; b < tmpBufferSize; b += dwidth) { val = tmpBuffer[b]; maxval = (val > maxval) ? val : maxval; } dstData[dstPixelOffset] = maxval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } revolver += dwidth; if (revolver == tmpBufferSize) { revolver = 0; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void floatLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); float dstDataArrays[][] = dst.getFloatDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); float srcDataArrays[][] = src.getFloatDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); float maxValues[] = new float[filterSize]; float val, maxval; int wp = filterSize; float tmpBuffer[] = new float[filterSize*dwidth]; int tmpBufferSize = filterSize*dwidth; for (int k = 0; k < dnumBands; k++) { float dstData[] = dstDataArrays[k]; float srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; int revolver = 0; for (int j = 0; j < filterSize-1; j++) { int srcPixelOffset = srcScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; maxval = -Float.MAX_VALUE; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += srcPixelStride; maxval = (val > maxval) ? val : maxval; } tmpBuffer[revolver+i] = maxval; srcPixelOffset += srcPixelStride; } revolver += dwidth; srcScanlineOffset += srcScanlineStride; } // srcScanlineStride already bumped by // filterSize-1*scanlineStride for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; maxval = -Float.MAX_VALUE; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += srcPixelStride; maxval = (val > maxval) ? val : maxval; } tmpBuffer[revolver + i] = maxval; maxval = -Float.MAX_VALUE; for (int b = i; b < tmpBufferSize; b += dwidth) { val = tmpBuffer[b]; maxval = (val > maxval) ? val : maxval; } dstData[dstPixelOffset] = maxval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } revolver += dwidth; if (revolver == tmpBufferSize) { revolver = 0; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void doubleLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); double dstDataArrays[][] = dst.getDoubleDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); double srcDataArrays[][] = src.getDoubleDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); double maxValues[] = new double[filterSize]; double val, maxval; int wp = filterSize; double tmpBuffer[] = new double[filterSize*dwidth]; int tmpBufferSize = filterSize*dwidth; for (int k = 0; k < dnumBands; k++) { double dstData[] = dstDataArrays[k]; double srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; int revolver = 0; for (int j = 0; j < filterSize-1; j++) { int srcPixelOffset = srcScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; maxval = -Double.MAX_VALUE; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += srcPixelStride; maxval = (val > maxval) ? val : maxval; } tmpBuffer[revolver+i] = maxval; srcPixelOffset += srcPixelStride; } revolver += dwidth; srcScanlineOffset += srcScanlineStride; } // srcScanlineStride already bumped by // filterSize-1*scanlineStride for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; maxval = -Double.MAX_VALUE; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += srcPixelStride; maxval = (val > maxval) ? val : maxval; } tmpBuffer[revolver + i] = maxval; maxval = -Double.MAX_VALUE; for (int b = i; b < tmpBufferSize; b += dwidth) { val = tmpBuffer[b]; maxval = (val > maxval) ? val : maxval; } dstData[dstPixelOffset] = maxval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } revolver += dwidth; if (revolver == tmpBufferSize) { revolver = 0; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } // public static OpImage createTestImage(OpImageTester oit) { // return // new MaxFilterSeparableOpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource()), // 3); // } // public static void main(String args[]) { // String classname = // "com.sun.media.jai.opimage.MaxFilterSeparableOpImage"; // OpImageTester.performDiagnostics(classname,args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/AndConstOpImage.java0000644000175000017500000002116410203035544027052 0ustar mathieumathieu/* * $RCSfile: AndConstOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:14 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import javax.media.jai.ColormapOpImage; import com.sun.media.jai.util.ImageUtil; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; /** * An OpImage implementing the "AndConst" operation. * *

This OpImage logically "ands" the pixels of a rendered * image with a set of constants, one for each band of the source image. * The destination pixel values are calculated as: *

 *     for (int h = 0; h < dstHeight; h++) {
 *         for (int w = 0; w < dstWidth; w++) {
 *             for (int b = 0; b < dstNumBands; b++) {
 *                 if (constants.length < dstNumBands) {
 *                     dst[h][w][b] = srcs[h][w][b] & constants[0];
 *                 } else {
 *                     dst[h][w][b] = srcs[h][w][b] & constants[b];
 *                 }
 *             }
 *         }
 *     }
 * 
* * @see javax.media.jai.operator.AndConstDescriptor * @see AndConstCRIF * * * @since EA2 */ final class AndConstOpImage extends ColormapOpImage { /** The constants to be anded, one for each band. */ protected int[] constants; /** * Constructor. * * @param source The source image. * @param layout The destination image layout. * @param constants The constants to be anded, stored as reference. */ public AndConstOpImage(RenderedImage source, Map config, ImageLayout layout, int[] constants) { super(source, layout, config, true); int numBands = getSampleModel().getNumBands(); if (constants.length < numBands) { this.constants = new int[numBands]; for (int i = 0; i < numBands; i++) { this.constants[i] = constants[0]; } } else { this.constants = (int[])constants.clone(); } // Set flag to permit in-place operation. permitInPlaceOperation(); // Initialize the colormap if necessary. initializeColormapOperation(); } /** * Transform the colormap according to the rescaling parameters. */ protected void transformColormap(byte[][] colormap) { for(int b = 0; b < 3; b++) { byte[] map = colormap[b]; int mapSize = map.length; int c = b < constants.length ? constants[b] : constants[0]; for(int i = 0; i < mapSize; i++) { map[i] = ImageUtil.clampRoundByte((map[i] & 0xFF) & c); } } } /** * Logically "ands" a constant to the pixel values within a specified * rectangle. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); Rectangle srcRect = mapDestRect(destRect, 0); RasterAccessor dst = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); RasterAccessor src = new RasterAccessor(sources[0], srcRect, formatTags[0], getSourceImage(0).getColorModel()); switch (dst.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(src, dst); break; case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: computeRectShort(src, dst); break; case DataBuffer.TYPE_INT: computeRectInt(src, dst); break; } /* Do not clamp dst data. */ dst.copyDataToRaster(); } private void computeRectByte(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); byte[][] dstData = dst.getByteDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); byte[][] srcData = src.getByteDataArrays(); for (int b = 0; b < dstBands; b++) { int c = constants[b]; byte[] d = dstData[b]; byte[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = (byte)(s[srcPixelOffset] & c); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectShort(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); short[][] dstData = dst.getShortDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); short[][] srcData = src.getShortDataArrays(); for (int b = 0; b < dstBands; b++) { int c = constants[b]; short[] d = dstData[b]; short[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = (short)(s[srcPixelOffset] & c); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectInt(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); int[][] dstData = dst.getIntDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); int[][] srcData = src.getIntDataArrays(); for (int b = 0; b < dstBands; b++) { int c = constants[b]; int[] d = dstData[b]; int[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = s[srcPixelOffset] & c; dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/PNMRIF.java0000644000175000017500000000265710203035544025100 0ustar mathieumathieu/* * $RCSfile: PNMRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:39 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.io.InputStream; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.NullOpImage; import javax.media.jai.OpImage; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.ImageDecoder; import com.sun.media.jai.codec.SeekableStream; /** * A RIF supporting the "PNM" operation in the * rendered image layer. * * @since EA2 * @see javax.media.jai.operator.PNMDescriptor */ public class PNMRIF implements RenderedImageFactory { /** Constructor. */ public PNMRIF() {} /** * Creates a RenderedImage representing the contents * of a PNM-encoded image. * * @param paramBlock A ParameterBlock containing the PNM * SeekableStream to read. * @param renderHints An instance of RenderingHints, * or null. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { return CodecRIFUtil.create("pnm", paramBlock, renderHints); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/RescaleCRIF.java0000644000175000017500000000311710203035544026117 0ustar mathieumathieu/* * $RCSfile: RescaleCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:41 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "Rescale" operation in the rendered * and renderable image layers. * * @see javax.media.jai.operator.RescaleDescriptor * @see RescaleOpImage * * * @since EA3 */ public class RescaleCRIF extends CRIFImpl { /** Constructor. */ public RescaleCRIF() { super("rescale"); } /** * Creates a new instance of RescaleOpImage in the * rendered layer. * * @param args The source image and the constants (slope, y-intercept). * @param hints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new RescaleOpImage(args.getRenderedSource(0), renderHints, layout, (double[])args.getObjectParameter(0), (double[])args.getObjectParameter(1)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MagnitudeCRIF.java0000644000175000017500000000272010203035544026455 0ustar mathieumathieu/* * $RCSfile: MagnitudeCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:31 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.MagnitudePhaseOpImage; /** * A CRIF supporting the "Magnitude" operation in the rendered * image layer. * * @since Beta * @see javax.media.jai.operator.MagnitudeDescriptor * */ public class MagnitudeCRIF extends CRIFImpl { /** Constructor. */ public MagnitudeCRIF() { super("magnitude"); } /** * Creates a new instance of a Magnitude operator. * * @param paramBlock The scaling type. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); RenderedImage source = paramBlock.getRenderedSource(0); return new MagnitudePhaseOpImage(source, renderHints, layout, MagnitudePhaseOpImage.MAGNITUDE); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/TransposeBinaryOpImage.java0000644000175000017500000005516710203035544030476 0ustar mathieumathieu/* * $RCSfile: TransposeBinaryOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:46 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferInt; import java.awt.image.DataBufferUShort; import java.awt.image.IndexColorModel; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PlanarImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.RasterFactory; import java.util.Map; import javax.media.jai.JAI; import javax.media.jai.WarpAffine; import javax.media.jai.WarpOpImage; /** * An OpImage class to perform a transpose (flip) of an image with a * single 1-bit channel, represented using a * MultiPixelPackedSampleModel and byte, short, or int DataBuffer. * * @since 1.0.1 * @see TransposeOpImage */ final class TransposeBinaryOpImage extends TransposeOpImage { // Force the SampleModel and ColorModel to be the same as for // the source image. private static ImageLayout layoutHelper(ImageLayout layout, SampleModel sm, ColorModel cm) { ImageLayout newLayout; if (layout != null) { newLayout = (ImageLayout)layout.clone(); } else { newLayout = new ImageLayout(); } newLayout.setSampleModel(sm); newLayout.setColorModel(cm); return newLayout; } // Since this operation deals with packed binary data, we do not need // to expand the IndexColorModel private static Map configHelper(Map configuration) { Map config; if (configuration == null) { config = new RenderingHints(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.FALSE); } else { config = configuration; if (!(config.containsKey(JAI.KEY_REPLACE_INDEX_COLOR_MODEL))) { RenderingHints hints = (RenderingHints)configuration; config = (RenderingHints)hints.clone(); config.put(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.FALSE); } } return config; } /** * Constructs an TransposeBinaryOpImage from a RenderedImage source, * and Transpose type. The image dimensions are determined by * forward-mapping the source bounds. * The tile grid layout, SampleModel, and ColorModel are specified * by the image source, possibly overridden by values from the * ImageLayout parameter. * * @param source a RenderedImage. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param type the desired Tranpose type. */ public TransposeBinaryOpImage(RenderedImage source, Map config, ImageLayout layout, int type) { super(source, configHelper(config), layoutHelper(layout, source.getSampleModel(), source.getColorModel()), type); } protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; MultiPixelPackedSampleModel mppsm = (MultiPixelPackedSampleModel)source.getSampleModel(); int srcScanlineStride = mppsm.getScanlineStride(); int incr1 = 0, incr2 = 0, s_x = 0, s_y = 0; int bits = 8; int dataType = source.getSampleModel().getDataType(); if (dataType == DataBuffer.TYPE_USHORT) { bits = 16; } else if (dataType == DataBuffer.TYPE_INT) { bits = 32; } PlanarImage src = getSource(0); int sMinX = src.getMinX(); int sMinY = src.getMinY(); int sWidth = src.getWidth(); int sHeight = src.getHeight(); int sMaxX = sMinX + sWidth - 1; int sMaxY = sMinY + sHeight - 1; // Backwards map starting point of destination rectangle int[] pt = new int[2]; pt[0] = destRect.x; pt[1] = destRect.y; mapPoint(pt, sMinX, sMinY, sMaxX, sMaxY, type, false); s_x = pt[0]; s_y = pt[1]; // Determine source stride along dest row (incr1) and column (incr2) switch (type) { case 0: // FLIP_VERTICAL incr1 = 1; incr2 = -bits*srcScanlineStride; break; case 1: // FLIP_HORIZONTAL incr1 = -1; incr2 = bits*srcScanlineStride; break; case 2: // FLIP_DIAGONAL; incr1 = bits*srcScanlineStride; incr2 = 1; break; case 3: // FLIP_ANTIDIAGONAL incr1 = -bits*srcScanlineStride; incr2 = -1; break; case 4: // ROTATE_90 incr1 = -bits*srcScanlineStride; incr2 = 1; break; case 5: // ROTATE_180 incr1 = -1; incr2 = -bits*srcScanlineStride; break; case 6: // ROTATE_270 incr1 = bits*srcScanlineStride; incr2 = -1; break; } switch (source.getSampleModel().getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(source, dest, destRect, incr1, incr2, s_x, s_y); break; case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_USHORT: shortLoop(source, dest, destRect, incr1, incr2, s_x, s_y); break; case DataBuffer.TYPE_INT: intLoop(source, dest, destRect, incr1, incr2, s_x, s_y); break; } } private void byteLoop(Raster source, WritableRaster dest, Rectangle destRect, int incr1, int incr2, int s_x, int s_y) { MultiPixelPackedSampleModel sourceSM = (MultiPixelPackedSampleModel)source.getSampleModel(); DataBufferByte sourceDB = (DataBufferByte)source.getDataBuffer(); int sourceTransX = source.getSampleModelTranslateX(); int sourceTransY = source.getSampleModelTranslateY(); int sourceDataBitOffset = sourceSM.getDataBitOffset(); int sourceScanlineStride = sourceSM.getScanlineStride(); MultiPixelPackedSampleModel destSM = (MultiPixelPackedSampleModel)dest.getSampleModel(); DataBufferByte destDB = (DataBufferByte)dest.getDataBuffer(); int destMinX = dest.getMinX(); int destMinY = dest.getMinY(); int destTransX = dest.getSampleModelTranslateX(); int destTransY = dest.getSampleModelTranslateY(); int destDataBitOffset = destSM.getDataBitOffset(); int destScanlineStride = destSM.getScanlineStride(); byte[] sourceData = sourceDB.getData(); int sourceDBOffset = sourceDB.getOffset(); byte[] destData = destDB.getData(); int destDBOffset = destDB.getOffset(); int dx = destRect.x; int dy = destRect.y; int dwidth = destRect.width; int dheight = destRect.height; int sourceOffset = 8*(s_y - sourceTransY)*sourceScanlineStride + 8*sourceDBOffset + (s_x - sourceTransX) + sourceDataBitOffset; int destOffset = 8*(dy - destTransY)*destScanlineStride + 8*destDBOffset + (dx - destTransX) + destDataBitOffset; for (int j = 0; j < dheight; j++) { int sOffset = sourceOffset; int dOffset = destOffset; int selement, val, dindex, delement; int i = 0; while ((i < dwidth) && ((dOffset & 7) != 0)) { selement = sourceData[sOffset >> 3]; val = (selement >> (7 - (sOffset & 7))) & 0x1; dindex = dOffset >> 3; int dshift = 7 - (dOffset & 7); delement = destData[dindex]; delement |= val << dshift; destData[dindex] = (byte)delement; sOffset += incr1; ++dOffset; ++i; } dindex = dOffset >> 3; if ((incr1 & 7) == 0) { // // We are stepping along Y in the source so the shift // position for each source pixel is fixed for the entire // destination scanline. // int shift = 7 - (sOffset & 7); int offset = sOffset >> 3; int incr = incr1 >> 3; while (i < dwidth - 7) { selement = sourceData[offset]; val = (selement >> shift) & 0x1; delement = val << 7; offset += incr; selement = sourceData[offset]; val = (selement >> shift) & 0x1; delement |= val << 6; offset += incr; selement = sourceData[offset]; val = (selement >> shift) & 0x1; delement |= val << 5; offset += incr; selement = sourceData[offset]; val = (selement >> shift) & 0x1; delement |= val << 4; offset += incr; selement = sourceData[offset]; val = (selement >> shift) & 0x1; delement |= val << 3; offset += incr; selement = sourceData[offset]; val = (selement >> shift) & 0x1; delement |= val << 2; offset += incr; selement = sourceData[offset]; val = (selement >> shift) & 0x1; delement |= val << 1; offset += incr; selement = sourceData[offset]; val = (selement >> shift) & 0x1; delement |= val; offset += incr; destData[dindex] = (byte)delement; sOffset += 8*incr1; dOffset += 8; i += 8; ++dindex; } } else { // // If we are here, incr1 must be 1 or -1. // There are further optimization opportunites here. // while (i < dwidth - 7) { selement = sourceData[sOffset >> 3]; val = (selement >> (7 - (sOffset & 7))) & 0x1; delement = val << 7; sOffset += incr1; selement = sourceData[sOffset >> 3]; val = (selement >> (7 - (sOffset & 7))) & 0x1; delement |= val << 6; sOffset += incr1; selement = sourceData[sOffset >> 3]; val = (selement >> (7 - (sOffset & 7))) & 0x1; delement |= val << 5; sOffset += incr1; selement = sourceData[sOffset >> 3]; val = (selement >> (7 - (sOffset & 7))) & 0x1; delement |= val << 4; sOffset += incr1; selement = sourceData[sOffset >> 3]; val = (selement >> (7 - (sOffset & 7))) & 0x1; delement |= val << 3; sOffset += incr1; selement = sourceData[sOffset >> 3]; val = (selement >> (7 - (sOffset & 7))) & 0x1; delement |= val << 2; sOffset += incr1; selement = sourceData[sOffset >> 3]; val = (selement >> (7 - (sOffset & 7))) & 0x1; delement |= val << 1; sOffset += incr1; selement = sourceData[sOffset >> 3]; val = (selement >> (7 - (sOffset & 7))) & 0x1; delement |= val; sOffset += incr1; destData[dindex] = (byte)delement; dOffset += 8; i += 8; ++dindex; } } while (i < dwidth) { selement = sourceData[sOffset >> 3]; val = (selement >> (7 - (sOffset & 7))) & 0x1; dindex = dOffset >> 3; int dshift = 7 - (dOffset & 7); delement = destData[dindex]; delement |= val << dshift; destData[dindex] = (byte)delement; sOffset += incr1; ++dOffset; ++i; } sourceOffset += incr2; destOffset += 8*destScanlineStride; } } private void shortLoop(Raster source, Raster dest, Rectangle destRect, int incr1, int incr2, int s_x, int s_y) { MultiPixelPackedSampleModel sourceSM = (MultiPixelPackedSampleModel)source.getSampleModel(); DataBufferUShort sourceDB = (DataBufferUShort)source.getDataBuffer(); int sourceTransX = source.getSampleModelTranslateX(); int sourceTransY = source.getSampleModelTranslateY(); int sourceDataBitOffset = sourceSM.getDataBitOffset(); int sourceScanlineStride = sourceSM.getScanlineStride(); MultiPixelPackedSampleModel destSM = (MultiPixelPackedSampleModel)dest.getSampleModel(); DataBufferUShort destDB = (DataBufferUShort)dest.getDataBuffer(); int destMinX = dest.getMinX(); int destMinY = dest.getMinY(); int destTransX = dest.getSampleModelTranslateX(); int destTransY = dest.getSampleModelTranslateY(); int destDataBitOffset = destSM.getDataBitOffset(); int destScanlineStride = destSM.getScanlineStride(); short[] sourceData = sourceDB.getData(); int sourceDBOffset = sourceDB.getOffset(); short[] destData = destDB.getData(); int destDBOffset = destDB.getOffset(); int dx = destRect.x; int dy = destRect.y; int dwidth = destRect.width; int dheight = destRect.height; int sourceOffset = 16*(s_y - sourceTransY)*sourceScanlineStride + 16*sourceDBOffset + (s_x - sourceTransX) + sourceDataBitOffset; int destOffset = 16*(dy - destTransY)*destScanlineStride + 16*destDBOffset + (dx - destTransX) + destDataBitOffset; for (int j = 0; j < dheight; j++) { int sOffset = sourceOffset; int dOffset = destOffset; int selement, val, dindex, delement; int i = 0; while ((i < dwidth) && ((dOffset & 15) != 0)) { selement = sourceData[sOffset >> 4]; val = (selement >> (15 - (sOffset & 15))) & 0x1; dindex = dOffset >> 4; int dshift = 15 - (dOffset & 15); delement = destData[dindex]; delement |= val << dshift; destData[dindex] = (short)delement; sOffset += incr1; ++dOffset; ++i; } dindex = dOffset >> 4; if ((incr1 & 15) == 0) { int shift = 15 - (sOffset & 5); int offset = sOffset >> 4; int incr = incr1 >> 4; while (i < dwidth - 15) { delement = 0; for (int b = 15; b >= 0; b--) { selement = sourceData[offset]; val = (selement >> shift) & 0x1; delement |= val << b; offset += incr; } destData[dindex] = (short)delement; sOffset += 16*incr1; dOffset += 16; i += 16; ++dindex; } } else { while (i < dwidth - 15) { delement = 0; for (int b = 15; b >= 0; b--) { selement = sourceData[sOffset >> 4]; val = (selement >> (15 - (sOffset & 15))) & 0x1; delement |= val << b; sOffset += incr1; } destData[dindex] = (short)delement; dOffset += 15; i += 16; ++dindex; } } while (i < dwidth) { selement = sourceData[sOffset >> 4]; val = (selement >> (15 - (sOffset & 15))) & 0x1; dindex = dOffset >> 4; int dshift = 15 - (dOffset & 15); delement = destData[dindex]; delement |= val << dshift; destData[dindex] = (short)delement; sOffset += incr1; ++dOffset; ++i; } sourceOffset += incr2; destOffset += 16*destScanlineStride; } } private void intLoop(Raster source, Raster dest, Rectangle destRect, int incr1, int incr2, int s_x, int s_y) { MultiPixelPackedSampleModel sourceSM = (MultiPixelPackedSampleModel)source.getSampleModel(); DataBufferInt sourceDB = (DataBufferInt)source.getDataBuffer(); int sourceTransX = source.getSampleModelTranslateX(); int sourceTransY = source.getSampleModelTranslateY(); int sourceDataBitOffset = sourceSM.getDataBitOffset(); int sourceScanlineStride = sourceSM.getScanlineStride(); MultiPixelPackedSampleModel destSM = (MultiPixelPackedSampleModel)dest.getSampleModel(); DataBufferInt destDB = (DataBufferInt)dest.getDataBuffer(); int destMinX = dest.getMinX(); int destMinY = dest.getMinY(); int destTransX = dest.getSampleModelTranslateX(); int destTransY = dest.getSampleModelTranslateY(); int destDataBitOffset = destSM.getDataBitOffset(); int destScanlineStride = destSM.getScanlineStride(); int[] sourceData = sourceDB.getData(); int sourceDBOffset = sourceDB.getOffset(); int[] destData = destDB.getData(); int destDBOffset = destDB.getOffset(); int dx = destRect.x; int dy = destRect.y; int dwidth = destRect.width; int dheight = destRect.height; int sourceOffset = 32*(s_y - sourceTransY)*sourceScanlineStride + 32*sourceDBOffset + (s_x - sourceTransX) + sourceDataBitOffset; int destOffset = 32*(dy - destTransY)*destScanlineStride + 32*destDBOffset + (dx - destTransX) + destDataBitOffset; for (int j = 0; j < dheight; j++) { int sOffset = sourceOffset; int dOffset = destOffset; int selement, val, dindex, delement; int i = 0; while ((i < dwidth) && ((dOffset & 31) != 0)) { selement = sourceData[sOffset >> 5]; val = (selement >> (31 - (sOffset & 31))) & 0x1; dindex = dOffset >> 5; int dshift = 31 - (dOffset & 31); delement = destData[dindex]; delement |= val << dshift; destData[dindex] = (int)delement; sOffset += incr1; ++dOffset; ++i; } dindex = dOffset >> 5; if ((incr1 & 31) == 0) { int shift = 31 - (sOffset & 5); int offset = sOffset >> 5; int incr = incr1 >> 5; while (i < dwidth - 31) { delement = 0; for (int b = 31; b >= 0; b--) { selement = sourceData[offset]; val = (selement >> shift) & 0x1; delement |= val << b; offset += incr; } destData[dindex] = (int)delement; sOffset += 32*incr1; dOffset += 32; i += 32; ++dindex; } } else { while (i < dwidth - 31) { delement = 0; for (int b = 31; b >= 0; b--) { selement = sourceData[sOffset >> 5]; val = (selement >> (31 - (sOffset & 31))) & 0x1; delement |= val << b; sOffset += incr1; } destData[dindex] = (int)delement; dOffset += 31; i += 32; ++dindex; } } while (i < dwidth) { selement = sourceData[sOffset >> 5]; val = (selement >> (31 - (sOffset & 31))) & 0x1; dindex = dOffset >> 5; int dshift = 31 - (dOffset & 31); delement = destData[dindex]; delement |= val << dshift; destData[dindex] = (int)delement; sOffset += incr1; ++dOffset; ++i; } sourceOffset += incr2; destOffset += 32*destScanlineStride; } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ScaleBilinearBinaryOpImage.java0000644000175000017500000004767010203035544031215 0ustar mathieumathieu/* * $RCSfile: ScaleBilinearBinaryOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:42 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferInt; import java.awt.image.DataBufferUShort; import java.awt.image.IndexColorModel; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationNearest; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PlanarImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.ScaleOpImage; import javax.media.jai.TileCache; import javax.media.jai.BorderExtender; import com.sun.media.jai.util.Rational; import java.util.Map; /** * An OpImage subclass that performs bilinear scaling * for binary images with a MultiPixelPackedSampleModel * and byte, short, or int DataBuffers. * */ final public class ScaleBilinearBinaryOpImage extends ScaleOpImage { /* The number of SubsampleBits */ private int subsampleBits; /* Subsampling related variables */ int one, shift2, round2; long invScaleXInt, invScaleXFrac; long invScaleYInt, invScaleYFrac; /** * Constructs a ScaleBilinearBinaryOpImage from a RenderedImage source, * * @param source a RenderedImage. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param xScale scale factor along x axis. * @param yScale scale factor along y axis. * @param xTrans translation factor along x axis. * @param yTrans translation factor along y axis. * @param interp an Interpolation object to use for resampling. */ public ScaleBilinearBinaryOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, float xScale, float yScale, float xTrans, float yTrans, Interpolation interp) { super(source, layout, config, true, extender, interp, xScale, yScale, xTrans, yTrans); subsampleBits = interp.getSubsampleBitsH (); // Numnber of subsampling positions one = 1 << subsampleBits; //Subsampling related variables shift2 = 2 * subsampleBits; round2 = 1 << (shift2 - 1); // Propagate source's ColorModel if (layout != null) { colorModel = layout.getColorModel(source); } else { colorModel = source.getColorModel(); } sampleModel = source.getSampleModel().createCompatibleSampleModel(tileWidth, tileHeight); if (invScaleXRational.num > invScaleXRational.denom) { invScaleXInt = invScaleXRational.num / invScaleXRational.denom; invScaleXFrac = invScaleXRational.num % invScaleXRational.denom; } else { invScaleXInt = 0; invScaleXFrac = invScaleXRational.num; } if (invScaleYRational.num > invScaleYRational.denom) { invScaleYInt = invScaleYRational.num / invScaleYRational.denom; invScaleYFrac = invScaleYRational.num % invScaleYRational.denom; } else { invScaleYInt = 0; invScaleYFrac = invScaleYRational.num; } } /** * Performs a scale operation on a specified rectangle. The sources are * cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect (Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; // Get the source rectangle Rectangle srcRect = source.getBounds(); int srcRectX = srcRect.x; int srcRectY = srcRect.y; // Destination rectangle dimensions. int dx = destRect.x; int dy = destRect.y; int dwidth = destRect.width; int dheight = destRect.height; // Precalculate the x positions and store them in an array. int[] xvalues = new int[dwidth]; int[] yvalues = new int[dheight]; int[] xfracvalues = new int[dwidth]; int[] yfracvalues = new int[dheight]; long sxNum = dx, sxDenom = 1; long syNum = dy, syDenom = 1; // Subtract the X translation factor sx -= transX sxNum = sxNum * transXRationalDenom - transXRationalNum * sxDenom; sxDenom *= transXRationalDenom; syNum = syNum*transYRationalDenom - transYRationalNum*syDenom; syDenom *= transYRationalDenom; // Add 0.5 sxNum = 2*sxNum + sxDenom; sxDenom *= 2; syNum = 2*syNum + syDenom; syDenom *= 2; // Multply by invScaleX & Y sxNum *= invScaleXRationalNum; sxDenom *= invScaleXRationalDenom; syNum *= invScaleYRationalNum; syDenom *= invScaleYRationalDenom; // Subtract 0.5 // jxz sxNum = 2*sxNum - sxDenom; sxDenom *= 2; syNum = 2*syNum - syDenom; syDenom *= 2; // Separate the x source coordinate into integer and fractional part int srcXInt = Rational.floor(sxNum , sxDenom); long srcXFrac = sxNum % sxDenom; if (srcXInt < 0) { srcXFrac = sxDenom + srcXFrac; } int srcYInt = Rational.floor(syNum, syDenom); long srcYFrac = syNum % syDenom; if (srcYInt < 0) { srcYFrac = syDenom + srcYFrac; } // Normalize - Get a common denominator for the fracs of // src and invScaleX long commonXDenom = sxDenom*invScaleXRationalDenom; srcXFrac *= invScaleXRationalDenom; long newInvScaleXFrac = invScaleXFrac*sxDenom; long commonYDenom = syDenom * invScaleYRationalDenom; srcYFrac *= invScaleYRationalDenom; long newInvScaleYFrac = invScaleYFrac * syDenom; for (int i = 0; i < dwidth; i++) { // Calculate the position // xfracvalues is the fractional part of x position in terms // of the nuber of subpixel points xvalues[i] = srcXInt; // added by jxz; for the case frac is less then 1/2, // the previous location is used // e.g. 24.25 is between the two half points 23.5 and 24.5 // thus 23rd and 24th are the pixel rows // XXX watch for side effects associated with sfracvalues //if(2 * srcXFrac < commonXDenom && xvalues[i] > 0){ //--xvalues[i]; //} xfracvalues[i] = (int) ( ( (float) srcXFrac / (float) commonXDenom) * one); // Move onto the next source pixel. // Add the integral part of invScaleX to the integral part // of srcX srcXInt += invScaleXInt; // Add the fractional part of invScaleX to the fractional part // of srcX srcXFrac += newInvScaleXFrac; // If the fractional part is now greater than equal to the // denominator, divide so as to reduce the numerator to be less // than the denominator and add the overflow to the integral part. if (srcXFrac >= commonXDenom) { srcXInt += 1; srcXFrac -= commonXDenom; } } // Precalculate the y positions and store them in an array. for (int i = 0; i < dheight; i++) { // Calculate the position yvalues[i] = srcYInt; yfracvalues[i] = (int) ( ( ( float) srcYFrac / (float) commonYDenom) * one ); // added by jxz; for the case frac is less then 1/2, // the previous location is used // e.g. 24.25 is between the two half points 23.5 and 24.5 // thus 23rd and 24th are the pixel rows // XXX watch for side effects associated with yfracvalues // if(2 * srcYFrac < commonYDenom && yvalues[i] > 0){ // --yvalues[i]; // } // Move onto the next source pixel. // Add the integral part of invScaleY to the integral part // of srcY srcYInt += invScaleYInt; // Add the fractional part of invScaleY to the fractional part // of srcY srcYFrac += newInvScaleYFrac; // If the fractional part is now greater than equal to the // denominator, divide so as to reduce the numerator to be less // than the denominator and add the overflow to the integral part. if (srcYFrac >= commonYDenom) { srcYInt += 1; srcYFrac -= commonYDenom; } } switch (source.getSampleModel().getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(source, dest, dx, dy, dwidth, dheight, xvalues, yvalues, xfracvalues, yfracvalues); break; case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_USHORT: shortLoop(source, dest, dx, dy, dwidth, dheight, xvalues, yvalues, xfracvalues, yfracvalues); break; case DataBuffer.TYPE_INT: intLoop(source, dest, dx, dy, dwidth, dheight, xvalues, yvalues, xfracvalues, yfracvalues); break; default: throw new RuntimeException(JaiI18N.getString("OrderedDitherOpImage0")); } } private void byteLoop(Raster source, WritableRaster dest, int dx, int dy, int dwidth, int dheight, int[] xvalues, int[] yvalues, int[] xfracvalues, int[] yfracvalues) { MultiPixelPackedSampleModel sourceSM = (MultiPixelPackedSampleModel)source.getSampleModel(); DataBufferByte sourceDB = (DataBufferByte)source.getDataBuffer(); int sourceTransX = source.getSampleModelTranslateX(); int sourceTransY = source.getSampleModelTranslateY(); int sourceDataBitOffset = sourceSM.getDataBitOffset(); int sourceScanlineStride = sourceSM.getScanlineStride(); MultiPixelPackedSampleModel destSM = (MultiPixelPackedSampleModel)dest.getSampleModel(); DataBufferByte destDB = (DataBufferByte)dest.getDataBuffer(); int destMinX = dest.getMinX(); int destMinY = dest.getMinY(); int destTransX = dest.getSampleModelTranslateX(); int destTransY = dest.getSampleModelTranslateY(); int destDataBitOffset = destSM.getDataBitOffset(); int destScanlineStride = destSM.getScanlineStride(); byte[] sourceData = sourceDB.getData(); int sourceDBOffset = sourceDB.getOffset(); byte[] destData = destDB.getData(); int destDBOffset = destDB.getOffset(); int[] sbytenum = new int[dwidth]; int[] sshift = new int[dwidth]; // Since the source data is MultiPixel packed // precalculate the byte no and the and the shift // after masking required to extract a single pixel // sample from a byte for (int i = 0; i < dwidth; i++) { int x = xvalues[i]; int sbitnum = sourceDataBitOffset + (x - sourceTransX); sbytenum[i] = sbitnum >> 3; sshift[i] = 7 - (sbitnum & 7); } int sourceYOffset; int s00, s01, s10, s11, s0, s1, s; int x =0, y = 0; int yfrac, xfrac; int xNextBitNo; int xNextByteNo; int xNextShiftNo; int destYOffset = (dy - destTransY) * destScanlineStride + destDBOffset; int dbitnum = destDataBitOffset + (dx - destTransX); int destByteNum; int destBitShift; int i = 0, j = 0; // Loop through height of image for ( j = 0; j < dheight; j++) { y = yvalues[j]; yfrac = yfracvalues[j]; sourceYOffset = (y - sourceTransY) * sourceScanlineStride + sourceDBOffset; dbitnum = destDataBitOffset + ( dx - destTransX ); // loop through one scan line for ( i = 0; i < dwidth; i ++ ) { xfrac = xfracvalues[i]; x = xvalues[i]; xNextBitNo = sourceDataBitOffset + (x + 1 - sourceTransX); xNextByteNo = xNextBitNo >> 3; xNextShiftNo = 7 - ( xNextBitNo & 7); /* Four surrounding pixels are needed for Bilinear interpolation. * If the dest pixel to be calculated is at (dx, dy) then the * actual source pixel (sx, sy) required is (dx/scaleX, dy/scaleY). * The four pixels that surround it are at the positions: * s00 = src(sxlow, sylow) * s01 = src(sxhigh, sylow) * s10 = src(sxlow, syhigh) * s11 = src(sxhigh, syhigh) * where sxlow = Math.floor(sx), sxhigh = Math.ceil(sx) * and sylow = Math.floor(sy), syhigh = Math.ceil(sy) * * The value of the destination pixel can now be calculated as: * s0 = (s01 - s00)*xfrac + s00; * s1 = (s11 - s10)*xfrac + s10; * dst(x,y) = (s1 - s0)*yfrac + s0; */ //Obtain sample values for 4 adjacent pixels in the source s00 = (sourceData[sourceYOffset + sbytenum[i]] >> sshift[i]) & 0x01; s01 = (sourceData[sourceYOffset + xNextByteNo] >> xNextShiftNo ) & 0x01; s10 = (sourceData[sourceYOffset + sourceScanlineStride + sbytenum[i]] >> sshift[i]) & 0x01; s11 = (sourceData[sourceYOffset + sourceScanlineStride + xNextByteNo] >> xNextShiftNo ) & 0x01; // perform the bilinear interpolation s0 = ( s01 - s00 ) * xfrac + (s00 << subsampleBits); s1 = ( s11 - s10 ) * xfrac + (s10 << subsampleBits); // The bilinear intrerpolated value s = ( ( s1 - s0 ) * yfrac + ( (s0 << subsampleBits) + round2) ) >> shift2; destByteNum = dbitnum >> 3; destBitShift = 7 - (dbitnum & 7); if ( s == 1 ) { //the destBit must be set destData[destYOffset + destByteNum] |= ( 0x01 << destBitShift ); } else { //the destBit must be cleared destData[destYOffset + destByteNum] &= ( 0xff - ( 0x01 << destBitShift ) ); } dbitnum ++; } destYOffset += destScanlineStride; } } private void shortLoop(Raster source, WritableRaster dest, int dx, int dy, int dwidth, int dheight, int[] xvalues, int[] yvalues, int[] xfracvalues, int[] yfracvalues) { MultiPixelPackedSampleModel sourceSM = (MultiPixelPackedSampleModel)source.getSampleModel(); int sourceTransX = source.getSampleModelTranslateX(); int sourceTransY = source.getSampleModelTranslateY(); int sourceDataBitOffset = sourceSM.getDataBitOffset(); int sourceScanlineStride = sourceSM.getScanlineStride(); MultiPixelPackedSampleModel destSM = (MultiPixelPackedSampleModel) dest.getSampleModel(); int destMinX = dest.getMinX(); int destMinY = dest.getMinY(); int destTransX = dest.getSampleModelTranslateX(); int destTransY = dest.getSampleModelTranslateY(); int destDataBitOffset = destSM.getDataBitOffset(); int destScanlineStride = destSM.getScanlineStride(); DataBufferUShort sourceDB = (DataBufferUShort) source.getDataBuffer(); short[] sourceData = sourceDB.getData(); int sourceDBOffset = sourceDB.getOffset(); DataBufferUShort destDB = (DataBufferUShort) dest.getDataBuffer(); short[] destData = destDB.getData(); int destDBOffset = destDB.getOffset(); int[] sshortnum = new int[dwidth]; int[] sshift = new int[dwidth]; for (int i = 0; i < dwidth; i++) { int x = xvalues[i]; int sbitnum = sourceDataBitOffset + (x - sourceTransX); sshortnum[i] = sbitnum >> 4; sshift[i] = 15 - (sbitnum & 15); } int sourceYOffset; int s00, s01, s10, s11, s0, s1, s; int x, y; int yfrac, xfrac; int xNextBitNo; int xNextShortNo; int xNextShiftNo; int destYOffset = (dy - destTransY) * destScanlineStride + destDBOffset; int dbitnum = destDataBitOffset + (dx - destTransX); int destShortNum; int destBitShift; for (int j = 0; j < dheight; j++) { y = yvalues[j]; yfrac = yfracvalues[j]; sourceYOffset = (y - sourceTransY)*sourceScanlineStride + sourceDBOffset; dbitnum = destDataBitOffset + (dx - destTransX); for ( int i = 0; i < dwidth; i++) { xfrac = xfracvalues[i]; x = xvalues[i]; xNextBitNo = sourceDataBitOffset + (x + 1 - sourceTransX); xNextShortNo = xNextBitNo >> 4; xNextShiftNo = 15 - (xNextBitNo & 15); s00 = (sourceData[sourceYOffset + sshortnum[i]] >> sshift[i]) & 0x01; s01 = (sourceData[sourceYOffset + xNextShortNo] >> xNextShiftNo ) & 0x01; s10 = (sourceData[sourceYOffset + sourceScanlineStride + sshortnum[i]] >> sshift[i]) & 0x01; s11 = (sourceData[sourceYOffset + sourceScanlineStride + xNextShortNo] >> xNextShiftNo ) & 0x01; s0 = ( s01 - s00 ) * xfrac + (s00 << subsampleBits); s1 = ( s11 - s10 ) * xfrac + (s10 << subsampleBits); s = ( ( s1 - s0 ) * yfrac + (s0 << subsampleBits) + round2 ) >> shift2; destShortNum = dbitnum >> 4; destBitShift = 15 - (dbitnum & 15); if ( s == 1) { destData [destYOffset + destShortNum] |= ( 0x01 << destBitShift ); } else { destData [destYOffset + destShortNum] &= ( 0xffff - ( 0x01 << destBitShift ) ); } dbitnum ++; } destYOffset += destScanlineStride; } } private void intLoop(Raster source, WritableRaster dest, int dx, int dy, int dwidth, int dheight, int[] xvalues, int[] yvalues, int[] xfracvalues, int[] yfracvalues) { MultiPixelPackedSampleModel sourceSM = (MultiPixelPackedSampleModel)source.getSampleModel(); DataBufferInt sourceDB = (DataBufferInt)source.getDataBuffer(); int sourceTransX = source.getSampleModelTranslateX(); int sourceTransY = source.getSampleModelTranslateY(); int sourceDataBitOffset = sourceSM.getDataBitOffset(); int sourceScanlineStride = sourceSM.getScanlineStride(); MultiPixelPackedSampleModel destSM = (MultiPixelPackedSampleModel)dest.getSampleModel(); DataBufferInt destDB = (DataBufferInt)dest.getDataBuffer(); int destMinX = dest.getMinX(); int destMinY = dest.getMinY(); int destTransX = dest.getSampleModelTranslateX(); int destTransY = dest.getSampleModelTranslateY(); int destDataBitOffset = destSM.getDataBitOffset(); int destScanlineStride = destSM.getScanlineStride(); int[] sourceData = sourceDB.getData(); int sourceDBOffset = sourceDB.getOffset(); int[] destData = destDB.getData(); int destDBOffset = destDB.getOffset(); int[] sintnum = new int[dwidth]; int[] sshift = new int[dwidth]; for (int i = 0; i < dwidth; i++) { int x = xvalues[i]; int sbitnum = sourceDataBitOffset + (x - sourceTransX); sintnum[i] = sbitnum >> 5; sshift[i] = 31 - (sbitnum & 31); } int sourceYOffset; int s00, s01, s10, s11, s0, s1, s; int x, y; int yfrac, xfrac; int xNextBitNo; int xNextIntNo; int xNextShiftNo; int destYOffset = (dy - destTransY) * destScanlineStride + destDBOffset; int dbitnum = destDataBitOffset + (dx - destTransX); int destIntNum; int destBitShift; for (int j = 0; j < dheight; j++) { y = yvalues[j]; yfrac = yfracvalues[j]; sourceYOffset = (y - sourceTransY)*sourceScanlineStride + sourceDBOffset; dbitnum = destDataBitOffset + ( dx - destTransX ); for ( int i = 0; i < dwidth; i ++) { xfrac = xfracvalues[i]; x = xvalues[i]; xNextBitNo = sourceDataBitOffset + (x + 1 - sourceTransX); xNextIntNo = xNextBitNo >> 5; xNextShiftNo = 31 - ( xNextBitNo & 31 ); s00 = (sourceData[sourceYOffset + sintnum[i]] >> sshift[i]) & 0x01; s01 = (sourceData[sourceYOffset + xNextIntNo] >> xNextShiftNo ) & 0x01; s10 = (sourceData[sourceYOffset + sourceScanlineStride + sintnum[i]] >> sshift[i]) & 0x01; s11 = (sourceData[sourceYOffset + sourceScanlineStride + xNextIntNo] >> xNextShiftNo ) & 0x01; s0 = ( s01 - s00 ) * xfrac + (s00 << subsampleBits); s1 = ( s11 - s10 ) * xfrac + (s10 << subsampleBits); s = ( ( s1 - s0 ) * yfrac + (s0 << subsampleBits) + round2 ) >> shift2; destIntNum = dbitnum >> 5; destBitShift = 31 - ( dbitnum & 31 ); if ( s == 1 ) { //Is above the threshold, the destBit must be set destData [destYOffset + destIntNum] |= ( 0x01 << destBitShift ); } else { destData [destYOffset + destIntNum] &= ( 0xff - ( 0x01 << destBitShift ) ); } dbitnum ++; } destYOffset += destScanlineStride; } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/BinarizeCRIF.java0000644000175000017500000000266310203035544026311 0ustar mathieumathieu/* * $RCSfile: BinarizeCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:15 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; /** * A CRIF supporting the "Binarize" operation in the rendered * and renderable image layers. * * @see javax.media.jai.operator.BinarizeDescriptor * @see javax.media.jai.operator.BinarizeDescriptor * @see BinarizeOpImage * */ public class BinarizeCRIF extends CRIFImpl { /** Constructor. */ public BinarizeCRIF() { super("binarize"); } /** * Creates a new instance of BinarizeOpImage in the * rendered layer. * * @param args The source image and the input parameters. * @param hints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new BinarizeOpImage(args.getRenderedSource(0), renderHints, layout, args.getDoubleParameter(0)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/SubtractFromConstOpImage.java0000644000175000017500000003546210203035544030771 0ustar mathieumathieu/* * $RCSfile: SubtractFromConstOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:45 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import javax.media.jai.ColormapOpImage; import com.sun.media.jai.util.ImageUtil; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.lang.ref.SoftReference; import javax.media.jai.ImageLayout; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; /** * An OpImage implementing the "SubtractFromConst" operation. * *

This OpImage subtracts the pixels of a rendered * image from a set of constants, one for each band of the source image. * The destination pixel values are calculated as: *

 *     for (int h = 0; h < dstHeight; h++) {
 *         for (int w = 0; w < dstWidth; w++) {
 *             for (int b = 0; b < dstNumBands; b++) {
 *                 if (constants.length < dstNumBands) {
 *                     dst[h][w][b] = constants[0] - srcs[h][w][b];
 *                 } else {
 *                     dst[h][w][b] = constants[b] - srcs[h][w][b];
 *                 }
 *             }
 *         }
 *     }
 * 
* * @see javax.media.jai.operator.SubtractFromConstDescriptor * @see SubtractFromConstCRIF * * * @since EA2 */ final class SubtractFromConstOpImage extends ColormapOpImage { /** The constants to be subtracted from, one for each band. */ protected double constants[]; private byte[][] byteTable = null; private synchronized void initByteTable() { if (byteTable != null) return; int nbands = constants.length; byteTable = new byte[nbands][256]; // Initialize table which implements SubtractFromConst and clamp for(int band=0; band 255) { t[i] = (byte)255; } else { t[i] = (byte)diff; } } } } /** * Constructor. * * @param source The source image. * @param layout The destination image layout. * @param constants The constants to be subtracted from. */ public SubtractFromConstOpImage(RenderedImage source, Map config, ImageLayout layout, double[] constants) { super(source, layout, config, true); int numBands = getSampleModel().getNumBands(); if (constants.length < numBands) { this.constants = new double[numBands]; for (int i = 0; i < numBands; i++) { this.constants[i] = constants[0]; } } else { this.constants = (double[])constants.clone(); } // Set flag to permit in-place operation. permitInPlaceOperation(); // Initialize the colormap if necessary. initializeColormapOperation(); } /** * Transform the colormap according to the rescaling parameters. */ protected void transformColormap(byte[][] colormap) { initByteTable(); for(int b = 0; b < 3; b++) { byte[] map = colormap[b]; byte[] luTable = byteTable[b >= byteTable.length ? 0 : b]; int mapSize = map.length; for(int i = 0; i < mapSize; i++) { map[i] = luTable[(map[i] & 0xFF)]; } } } /** * Subtracts the pixel values within a specified rectangle from a constant. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); Rectangle srcRect = mapDestRect(destRect, 0); RasterAccessor dst = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); RasterAccessor src = new RasterAccessor(sources[0], srcRect, formatTags[0], getSource(0).getColorModel()); switch (dst.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(src, dst); break; case DataBuffer.TYPE_USHORT: computeRectUShort(src, dst); break; case DataBuffer.TYPE_SHORT: computeRectShort(src, dst); break; case DataBuffer.TYPE_INT: computeRectInt(src, dst); break; case DataBuffer.TYPE_FLOAT: computeRectFloat(src, dst); break; case DataBuffer.TYPE_DOUBLE: computeRectDouble(src, dst); break; } if (dst.needsClamping()) { /* Further clamp down to underlying raster data type. */ dst.clampDataArrays(); } dst.copyDataToRaster(); } private void computeRectByte(RasterAccessor src, RasterAccessor dst) { initByteTable(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); byte[][] dstData = dst.getByteDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); byte[][] srcData = src.getByteDataArrays(); for (int b = 0; b < dstBands; b++) { int c = ImageUtil.clampRoundInt(constants[b]); byte[] d = dstData[b]; byte[] s = srcData[b]; byte[] clamp = byteTable[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; int dstEnd = dstPixelOffset + dstWidth*dstPixelStride; while (dstPixelOffset < dstEnd) { d[dstPixelOffset] = clamp[s[srcPixelOffset] & 0xFF]; dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectUShort(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); short[][] dstData = dst.getShortDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); short[][] srcData = src.getShortDataArrays(); for (int b = 0; b < dstBands; b++) { int c = ImageUtil.clampRoundInt(constants[b]); short[] d = dstData[b]; short[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; int dstEnd = dstPixelOffset + dstWidth*dstPixelStride; while (dstPixelOffset < dstEnd) { d[dstPixelOffset] = ImageUtil.clampUShort( c - (s[srcPixelOffset] & 0xFFFF)); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectShort(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); short[][] dstData = dst.getShortDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); short[][] srcData = src.getShortDataArrays(); for (int b = 0; b < dstBands; b++) { int c = ImageUtil.clampRoundInt(constants[b]); short[] d = dstData[b]; short[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; int dstEnd = dstPixelOffset + dstWidth*dstPixelStride; while (dstPixelOffset < dstEnd) { d[dstPixelOffset] = ImageUtil.clampShort(c - s[srcPixelOffset]); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectInt(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); int[][] dstData = dst.getIntDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); int[][] srcData = src.getIntDataArrays(); for (int b = 0; b < dstBands; b++) { long c = ImageUtil.clampRoundInt(constants[b]); int[] d = dstData[b]; int[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; int dstEnd = dstPixelOffset + dstWidth*dstPixelStride; while (dstPixelOffset < dstEnd) { d[dstPixelOffset] = ImageUtil.clampInt(c - s[srcPixelOffset]); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectFloat(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); float[][] dstData = dst.getFloatDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); float[][] srcData = src.getFloatDataArrays(); for (int b = 0; b < dstBands; b++) { double c = constants[b]; float[] d = dstData[b]; float[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; int dstEnd = dstPixelOffset + dstWidth*dstPixelStride; while (dstPixelOffset < dstEnd) { d[dstPixelOffset] = ImageUtil.clampFloat(c - s[srcPixelOffset]); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectDouble(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); double[][] dstData = dst.getDoubleDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); double[][] srcData = src.getDoubleDataArrays(); for (int b = 0; b < dstBands; b++) { double c = constants[b]; double[] d = dstData[b]; double[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; int dstEnd = dstPixelOffset + dstWidth*dstPixelStride; while (dstPixelOffset < dstEnd) { d[dstPixelOffset] = c - s[srcPixelOffset]; dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/SubtractCRIF.java0000644000175000017500000000334110203035544026327 0ustar mathieumathieu/* * $RCSfile: SubtractCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:44 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D.Float; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import java.awt.image.renderable.RenderContext; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "Subtract" operation in the rendered * and renderable image layers. * * @see javax.media.jai.operator.SubtractDescriptor * @see SubtractOpImage * */ public class SubtractCRIF extends CRIFImpl { /** Constructor. */ public SubtractCRIF() { super("subtract"); } /** * Creates a new instance of SubtractOpImage in the rendered * layer. This method satisfies the implementation of RIF. * * @param paramBlock The two source images to be subtracted. * @param renderHints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new SubtractOpImage(paramBlock.getRenderedSource(0), paramBlock.getRenderedSource(1), renderHints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/CompositeNoDestAlphaOpImage.java0000644000175000017500000013250010203035544031363 0ustar mathieumathieu/* * $RCSfile: CompositeNoDestAlphaOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:18 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.PointOpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.RasterFactory; import java.util.Map; /** * An OpImage implementing the "Composite" operation as * described in javax.media.jai.operator.CompositeDescriptor. * This implementation handles the case where the destination image does * not include its result alpha channel. * *

For two source images source1 and source2, * this OpImage places the foreground source1 * in front of the background source2. This is what commonly * known as the "over" composite. The destination color values are * calculated using the following formula: *

 * dest = source1 * alpha1 + source2 * alpha2 * (1 - alpha1)
 * 
* where source1 and source2 are the color values * of the two source images, without their alpha multiplied to them, and * alpha1 and alpha2 are the two sources's alpha * values in fraction. * * @see javax.media.jai.operator.CompositeDescriptor * @see CompositeCRIF * */ final class CompositeNoDestAlphaOpImage extends PointOpImage { /** The alpha image for the first source. */ private RenderedImage alpha1; /** The alpha image for the second source. */ private RenderedImage alpha2; /** Indicates whether alpha has been premultiplied. */ private boolean premultiplied; /** The RasterAccessor format tags. */ private RasterFormatTag[] tags; /** * Constructor. * * @param source1 The foreground source image. * @param source2 The background source image. * @param layout The destination image layout. * @param alpha1 The alpha image for the first source. * @param alpha2 The alpha image for the second source. If * null, the second source is assumed to be opaque. * @param premultiplied Indicates whether both sources and destination * have their alpha premultiplied. */ public CompositeNoDestAlphaOpImage(RenderedImage source1, RenderedImage source2, Map config, ImageLayout layout, RenderedImage alpha1, RenderedImage alpha2, boolean premultiplied) { super(source1, source2, layout, config, true); this.alpha1 = alpha1; this.alpha2 = alpha2; this.premultiplied = premultiplied; tags = getFormatTags(); } /** * Composites two images within a specified rectangle. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { /* For PointOpImage, srcRect = destRect. */ RasterAccessor s1 = new RasterAccessor( sources[0], destRect, tags[0], getSourceImage(0).getColorModel()); RasterAccessor s2 = new RasterAccessor( sources[1], destRect, tags[1], getSourceImage(1).getColorModel()); RasterAccessor a1 = new RasterAccessor( alpha1.getData(destRect), destRect, tags[2], alpha1.getColorModel()); RasterAccessor a2 = null, d; if (alpha2 == null) { d = new RasterAccessor(dest, destRect, tags[3], getColorModel()); } else { a2 = new RasterAccessor(alpha2.getData(destRect), destRect, tags[3], alpha2.getColorModel()); d = new RasterAccessor(dest, destRect, tags[4], getColorModel()); } switch (d.getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(s1, s2, a1, a2, d); break; case DataBuffer.TYPE_USHORT: ushortLoop(s1, s2, a1, a2, d); break; case DataBuffer.TYPE_SHORT: shortLoop(s1, s2, a1, a2, d); break; case DataBuffer.TYPE_INT: intLoop(s1, s2, a1, a2, d); break; case DataBuffer.TYPE_FLOAT: floatLoop(s1, s2, a1, a2, d); break; case DataBuffer.TYPE_DOUBLE: doubleLoop(s1, s2, a1, a2, d); break; } if (d.isDataCopy()) { d.clampDataArrays(); d.copyDataToRaster(); } } /* * Formulas for integral data types: * * if (premultiplied) { * dest = source1 + source2 * (1 - alpha1/maxValue) * } else { * if (alpha2 == null) { * dest = source1 * alpha1/maxValue + * source2 * (1 - alpha1/maxValue) * } else { * dest = (source1 * alpha1 + * source2 * alpha2 * (1 - alpha1/maxValue)) / * (alpha1 + alpha2 * (1 - alpha1/maxValue)) * } * } */ private void byteLoop(RasterAccessor s1, RasterAccessor s2, RasterAccessor a1, RasterAccessor a2, RasterAccessor d) { /* First source color channels. */ int s1LineStride = s1.getScanlineStride(); int s1PixelStride = s1.getPixelStride(); int[] s1BandOffsets = s1.getBandOffsets(); byte[][] s1Data = s1.getByteDataArrays(); /* Second source color channels. */ int s2LineStride = s2.getScanlineStride(); int s2PixelStride = s2.getPixelStride(); int[] s2BandOffsets = s2.getBandOffsets(); byte[][] s2Data = s2.getByteDataArrays(); /* First source alpha channel. */ int a1LineStride = a1.getScanlineStride(); int a1PixelStride = a1.getPixelStride(); int a1BandOffset = a1.getBandOffset(0); byte[] a1Data = a1.getByteDataArray(0); /* Second source alpha channel (if any). */ int a2LineStride = 0; int a2PixelStride = 0; int a2BandOffset = 0; byte[] a2Data = null; if (alpha2 != null) { a2LineStride = a2.getScanlineStride(); a2PixelStride = a2.getPixelStride(); a2BandOffset = a2.getBandOffset(0); a2Data = a2.getByteDataArray(0); } /* Destination color channels. */ int dLineStride = d.getScanlineStride(); int dPixelStride = d.getPixelStride(); int[] dBandOffsets = d.getBandOffsets(); byte[][] dData = d.getByteDataArrays(); int dwidth = d.getWidth(); int dheight = d.getHeight(); int dbands = d.getNumBands(); float invMax = 1.0F / 0xFF; int s1LineOffset = 0, s2LineOffset = 0, a1LineOffset = 0, a2LineOffset = 0, dLineOffset = 0, s1PixelOffset, s2PixelOffset, a1PixelOffset, a2PixelOffset, dPixelOffset; if (premultiplied) { /* dest = source1 + source2 * (1 - alpha1/max) */ for (int h = 0; h < dheight; h++) { s1PixelOffset = s1LineOffset; s2PixelOffset = s2LineOffset; a1PixelOffset = a1LineOffset + a1BandOffset; dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; a1LineOffset += a1LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { float t = 1.0F - (a1Data[a1PixelOffset] & 0xFF) * invMax; /* Destination color channels. */ for (int b = 0; b < dbands; b++) { dData[b][dPixelOffset+dBandOffsets[b]] = (byte) ((s1Data[b][s1PixelOffset+s1BandOffsets[b]] & 0xFF) + (s2Data[b][s2PixelOffset+s2BandOffsets[b]] & 0xFF) * t); } s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; a1PixelOffset += a1PixelStride; dPixelOffset += dPixelStride; } } } else { if (alpha2 == null) { /* dest = source1 * alpha1/max + source2 * (1 - alpha1/max) */ for (int h = 0; h < dheight; h++) { s1PixelOffset = s1LineOffset; s2PixelOffset = s2LineOffset; a1PixelOffset = a1LineOffset + a1BandOffset; dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; a1LineOffset += a1LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { float t1 = (a1Data[a1PixelOffset] & 0xFF) * invMax; float t2 = 1.0F - t1; /* Destination color channels. */ for (int b = 0; b < dbands; b++) { dData[b][dPixelOffset+dBandOffsets[b]] = (byte) ((s1Data[b][s1PixelOffset+s1BandOffsets[b]] & 0xFF) * t1 + (s2Data[b][s2PixelOffset+s2BandOffsets[b]] & 0xFF) * t2); } s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; a1PixelOffset += a1PixelStride; dPixelOffset += dPixelStride; } } } else { /* * dest = (source1 * alpha1 + * source2 * alpha2 * (1 - alpha1/maxValue)) / * (alpha1 + alpha2 * (1 - alpha1/maxValue)) */ for (int h = 0; h < dheight; h++) { s1PixelOffset = s1LineOffset; s2PixelOffset = s2LineOffset; a1PixelOffset = a1LineOffset + a1BandOffset; a2PixelOffset = a2LineOffset + a2BandOffset; dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; a1LineOffset += a1LineStride; a2LineOffset += a2LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { int t1 = a1Data[a1PixelOffset] & 0xFF; float t2 = (a2Data[a2PixelOffset] & 0xFF) * (1.0F - t1 * invMax); float t3 = t1 + t2; float t4, t5; if (t3 == 0.0F) { t4 = 0.0F; t5 = 0.0F; } else { t4 = t1 / t3; t5 = t2 / t3; } /* Destination color channels. */ for (int b = 0; b < dbands; b++) { dData[b][dPixelOffset+dBandOffsets[b]] = (byte) ((s1Data[b][s1PixelOffset+s1BandOffsets[b]] & 0xFF) * t4 + (s2Data[b][s2PixelOffset+s2BandOffsets[b]] & 0xFF) * t5); } s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; a1PixelOffset += a1PixelStride; a2PixelOffset += a2PixelStride; dPixelOffset += dPixelStride; } } } } } private void ushortLoop(RasterAccessor s1, RasterAccessor s2, RasterAccessor a1, RasterAccessor a2, RasterAccessor d) { /* First source color channels. */ int s1LineStride = s1.getScanlineStride(); int s1PixelStride = s1.getPixelStride(); int[] s1BandOffsets = s1.getBandOffsets(); short[][] s1Data = s1.getShortDataArrays(); /* Second source color channels. */ int s2LineStride = s2.getScanlineStride(); int s2PixelStride = s2.getPixelStride(); int[] s2BandOffsets = s2.getBandOffsets(); short[][] s2Data = s2.getShortDataArrays(); /* First source alpha channel. */ int a1LineStride = a1.getScanlineStride(); int a1PixelStride = a1.getPixelStride(); int a1BandOffset = a1.getBandOffset(0); short[] a1Data = a1.getShortDataArray(0); /* Second source alpha channel (if any). */ int a2LineStride = 0; int a2PixelStride = 0; int a2BandOffset = 0; short[] a2Data = null; if (alpha2 != null) { a2LineStride = a2.getScanlineStride(); a2PixelStride = a2.getPixelStride(); a2BandOffset = a2.getBandOffset(0); a2Data = a2.getShortDataArray(0); } /* Destination color channels. */ int dLineStride = d.getScanlineStride(); int dPixelStride = d.getPixelStride(); int[] dBandOffsets = d.getBandOffsets(); short[][] dData = d.getShortDataArrays(); int dwidth = d.getWidth(); int dheight = d.getHeight(); int dbands = d.getNumBands(); float invMax = 1.0F / 0xFFFF; int s1LineOffset = 0, s2LineOffset = 0, a1LineOffset = 0, a2LineOffset = 0, dLineOffset = 0, s1PixelOffset, s2PixelOffset, a1PixelOffset, a2PixelOffset, dPixelOffset; if (premultiplied) { /* dest = source1 + source2 * (1 - alpha1/max) */ for (int h = 0; h < dheight; h++) { s1PixelOffset = s1LineOffset; s2PixelOffset = s2LineOffset; a1PixelOffset = a1LineOffset + a1BandOffset; dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; a1LineOffset += a1LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { float t = 1.0F - (a1Data[a1PixelOffset] & 0xFFFF) * invMax; /* Destination color channels. */ for (int b = 0; b < dbands; b++) { dData[b][dPixelOffset+dBandOffsets[b]] = (short) ((s1Data[b][s1PixelOffset+s1BandOffsets[b]] & 0xFFFF) + (s2Data[b][s2PixelOffset+s2BandOffsets[b]] & 0xFFFF) * t); } s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; a1PixelOffset += a1PixelStride; dPixelOffset += dPixelStride; } } } else { if (alpha2 == null) { /* dest = source1 * alpha1/max + source2 * (1 - alpha1/max) */ for (int h = 0; h < dheight; h++) { s1PixelOffset = s1LineOffset; s2PixelOffset = s2LineOffset; a1PixelOffset = a1LineOffset + a1BandOffset; dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; a1LineOffset += a1LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { float t1 = (a1Data[a1PixelOffset] & 0xFFFF) * invMax; float t2 = 1.0F - t1; /* Destination color channels. */ for (int b = 0; b < dbands; b++) { dData[b][dPixelOffset+dBandOffsets[b]] = (short) ((s1Data[b][s1PixelOffset+s1BandOffsets[b]] & 0xFFFF) * t1 + (s2Data[b][s2PixelOffset+s2BandOffsets[b]] & 0xFFFF) * t2); } s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; a1PixelOffset += a1PixelStride; dPixelOffset += dPixelStride; } } } else { /* * dest = (source1 * alpha1 + * source2 * alpha2 * (1 - alpha1/maxValue)) / * (alpha1 + alpha2 * (1 - alpha1/maxValue)) */ for (int h = 0; h < dheight; h++) { s1PixelOffset = s1LineOffset; s2PixelOffset = s2LineOffset; a1PixelOffset = a1LineOffset + a1BandOffset; a2PixelOffset = a2LineOffset + a2BandOffset; dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; a1LineOffset += a1LineStride; a2LineOffset += a2LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { int t1 = a1Data[a1PixelOffset] & 0xFFFF; float t2 = (a2Data[a2PixelOffset] & 0xFFFF) * (1.0F - t1 * invMax); float t3 = t1 + t2; float t4, t5; if (t3 == 0.0F) { t4 = 0.0F; t5 = 0.0F; } else { t4 = t1 / t3; t5 = t2 / t3; } /* Destination color channels. */ for (int b = 0; b < dbands; b++) { dData[b][dPixelOffset+dBandOffsets[b]] = (short) ((s1Data[b][s1PixelOffset+s1BandOffsets[b]] & 0xFFFF) * t4 + (s2Data[b][s2PixelOffset+s2BandOffsets[b]] & 0xFFFF) * t5); } s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; a1PixelOffset += a1PixelStride; a2PixelOffset += a2PixelStride; dPixelOffset += dPixelStride; } } } } } private void shortLoop(RasterAccessor s1, RasterAccessor s2, RasterAccessor a1, RasterAccessor a2, RasterAccessor d) { /* First source color channels. */ int s1LineStride = s1.getScanlineStride(); int s1PixelStride = s1.getPixelStride(); int[] s1BandOffsets = s1.getBandOffsets(); short[][] s1Data = s1.getShortDataArrays(); /* Second source color channels. */ int s2LineStride = s2.getScanlineStride(); int s2PixelStride = s2.getPixelStride(); int[] s2BandOffsets = s2.getBandOffsets(); short[][] s2Data = s2.getShortDataArrays(); /* First source alpha channel. */ int a1LineStride = a1.getScanlineStride(); int a1PixelStride = a1.getPixelStride(); int a1BandOffset = a1.getBandOffset(0); short[] a1Data = a1.getShortDataArray(0); /* Second source alpha channel (if any). */ int a2LineStride = 0; int a2PixelStride = 0; int a2BandOffset = 0; short[] a2Data = null; if (alpha2 != null) { a2LineStride = a2.getScanlineStride(); a2PixelStride = a2.getPixelStride(); a2BandOffset = a2.getBandOffset(0); a2Data = a2.getShortDataArray(0); } /* Destination color channels. */ int dLineStride = d.getScanlineStride(); int dPixelStride = d.getPixelStride(); int[] dBandOffsets = d.getBandOffsets(); short[][] dData = d.getShortDataArrays(); int dwidth = d.getWidth(); int dheight = d.getHeight(); int dbands = d.getNumBands(); float invMax = 1.0F / Short.MAX_VALUE; int s1LineOffset = 0, s2LineOffset = 0, a1LineOffset = 0, a2LineOffset = 0, dLineOffset = 0, s1PixelOffset, s2PixelOffset, a1PixelOffset, a2PixelOffset, dPixelOffset; if (premultiplied) { /* dest = source1 + source2 * (1 - alpha1/max) */ for (int h = 0; h < dheight; h++) { s1PixelOffset = s1LineOffset; s2PixelOffset = s2LineOffset; a1PixelOffset = a1LineOffset + a1BandOffset; dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; a1LineOffset += a1LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { float t = 1.0F - a1Data[a1PixelOffset] * invMax; /* Destination color channels. */ for (int b = 0; b < dbands; b++) { dData[b][dPixelOffset+dBandOffsets[b]] = (short) (s1Data[b][s1PixelOffset+s1BandOffsets[b]] + s2Data[b][s2PixelOffset+s2BandOffsets[b]] * t); } s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; a1PixelOffset += a1PixelStride; dPixelOffset += dPixelStride; } } } else { if (alpha2 == null) { /* dest = source1 * alpha1/max + source2 * (1 - alpha1/max) */ for (int h = 0; h < dheight; h++) { s1PixelOffset = s1LineOffset; s2PixelOffset = s2LineOffset; a1PixelOffset = a1LineOffset + a1BandOffset; dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; a1LineOffset += a1LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { float t1 = a1Data[a1PixelOffset] * invMax; float t2 = 1.0F - t1; /* Destination color channels. */ for (int b = 0; b < dbands; b++) { dData[b][dPixelOffset+dBandOffsets[b]] = (short) (s1Data[b][s1PixelOffset+s1BandOffsets[b]] * t1 + s2Data[b][s2PixelOffset+s2BandOffsets[b]] * t2); } s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; a1PixelOffset += a1PixelStride; dPixelOffset += dPixelStride; } } } else { /* * dest = (source1 * alpha1 + * source2 * alpha2 * (1 - alpha1/maxValue)) / * (alpha1 + alpha2 * (1 - alpha1/maxValue)) */ for (int h = 0; h < dheight; h++) { s1PixelOffset = s1LineOffset; s2PixelOffset = s2LineOffset; a1PixelOffset = a1LineOffset + a1BandOffset; a2PixelOffset = a2LineOffset + a2BandOffset; dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; a1LineOffset += a1LineStride; a2LineOffset += a2LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { int t1 = a1Data[a1PixelOffset]; float t2 = a2Data[a2PixelOffset] * (1.0F - t1 * invMax); float t3 = t1 + t2; float t4, t5; if (t3 == 0.0F) { t4 = 0.0F; t5 = 0.0F; } else { t4 = t1 / t3; t5 = t2 / t3; } /* Destination color channels. */ for (int b = 0; b < dbands; b++) { dData[b][dPixelOffset+dBandOffsets[b]] = (short) (s1Data[b][s1PixelOffset+s1BandOffsets[b]] * t4 + s2Data[b][s2PixelOffset+s2BandOffsets[b]] * t5); } s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; a1PixelOffset += a1PixelStride; a2PixelOffset += a2PixelStride; dPixelOffset += dPixelStride; } } } } } private void intLoop(RasterAccessor s1, RasterAccessor s2, RasterAccessor a1, RasterAccessor a2, RasterAccessor d) { /* First source color channels. */ int s1LineStride = s1.getScanlineStride(); int s1PixelStride = s1.getPixelStride(); int[] s1BandOffsets = s1.getBandOffsets(); int[][] s1Data = s1.getIntDataArrays(); /* Second source color channels. */ int s2LineStride = s2.getScanlineStride(); int s2PixelStride = s2.getPixelStride(); int[] s2BandOffsets = s2.getBandOffsets(); int[][] s2Data = s2.getIntDataArrays(); /* First source alpha channel. */ int a1LineStride = a1.getScanlineStride(); int a1PixelStride = a1.getPixelStride(); int a1BandOffset = a1.getBandOffset(0); int[] a1Data = a1.getIntDataArray(0); /* Second source alpha channel (if any). */ int a2LineStride = 0; int a2PixelStride = 0; int a2BandOffset = 0; int[] a2Data = null; if (alpha2 != null) { a2LineStride = a2.getScanlineStride(); a2PixelStride = a2.getPixelStride(); a2BandOffset = a2.getBandOffset(0); a2Data = a2.getIntDataArray(0); } /* Destination color channels. */ int dLineStride = d.getScanlineStride(); int dPixelStride = d.getPixelStride(); int[] dBandOffsets = d.getBandOffsets(); int[][] dData = d.getIntDataArrays(); int dwidth = d.getWidth(); int dheight = d.getHeight(); int dbands = d.getNumBands(); float invMax = 1.0F / Integer.MAX_VALUE; int s1LineOffset = 0, s2LineOffset = 0, a1LineOffset = 0, a2LineOffset = 0, dLineOffset = 0, s1PixelOffset, s2PixelOffset, a1PixelOffset, a2PixelOffset, dPixelOffset; if (premultiplied) { /* dest = source1 + source2 * (1 - alpha1/max) */ for (int h = 0; h < dheight; h++) { s1PixelOffset = s1LineOffset; s2PixelOffset = s2LineOffset; a1PixelOffset = a1LineOffset + a1BandOffset; dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; a1LineOffset += a1LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { float t = 1.0F - a1Data[a1PixelOffset] * invMax; /* Destination color channels. */ for (int b = 0; b < dbands; b++) { dData[b][dPixelOffset+dBandOffsets[b]] = (int) (s1Data[b][s1PixelOffset+s1BandOffsets[b]] + s2Data[b][s2PixelOffset+s2BandOffsets[b]] * t); } s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; a1PixelOffset += a1PixelStride; dPixelOffset += dPixelStride; } } } else { if (alpha2 == null) { /* dest = source1 * alpha1/max + source2 * (1 - alpha1/max) */ for (int h = 0; h < dheight; h++) { s1PixelOffset = s1LineOffset; s2PixelOffset = s2LineOffset; a1PixelOffset = a1LineOffset + a1BandOffset; dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; a1LineOffset += a1LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { float t1 = a1Data[a1PixelOffset] * invMax; float t2 = 1.0F - t1; /* Destination color channels. */ for (int b = 0; b < dbands; b++) { dData[b][dPixelOffset+dBandOffsets[b]] = (int) (s1Data[b][s1PixelOffset+s1BandOffsets[b]] * t1 + s2Data[b][s2PixelOffset+s2BandOffsets[b]] * t2); } s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; a1PixelOffset += a1PixelStride; dPixelOffset += dPixelStride; } } } else { /* * dest = (source1 * alpha1 + * source2 * alpha2 * (1 - alpha1/maxValue)) / * (alpha1 + alpha2 * (1 - alpha1/maxValue)) */ for (int h = 0; h < dheight; h++) { s1PixelOffset = s1LineOffset; s2PixelOffset = s2LineOffset; a1PixelOffset = a1LineOffset + a1BandOffset; a2PixelOffset = a2LineOffset + a2BandOffset; dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; a1LineOffset += a1LineStride; a2LineOffset += a2LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { int t1 = a1Data[a1PixelOffset]; float t2 = a2Data[a2PixelOffset] * (1.0F - t1 * invMax); float t3 = t1 + t2; float t4, t5; if (t3 == 0.0F) { t4 = 0.0F; t5 = 0.0F; } else { t4 = t1 / t3; t5 = t2 / t3; } /* Destination color channels. */ for (int b = 0; b < dbands; b++) { dData[b][dPixelOffset+dBandOffsets[b]] = (int) (s1Data[b][s1PixelOffset+s1BandOffsets[b]] * t4 + s2Data[b][s2PixelOffset+s2BandOffsets[b]] * t5); } s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; a1PixelOffset += a1PixelStride; a2PixelOffset += a2PixelStride; dPixelOffset += dPixelStride; } } } } } private void floatLoop(RasterAccessor s1, RasterAccessor s2, RasterAccessor a1, RasterAccessor a2, RasterAccessor d) { /* First source color channels. */ int s1LineStride = s1.getScanlineStride(); int s1PixelStride = s1.getPixelStride(); int[] s1BandOffsets = s1.getBandOffsets(); float[][] s1Data = s1.getFloatDataArrays(); /* Second source color channels. */ int s2LineStride = s2.getScanlineStride(); int s2PixelStride = s2.getPixelStride(); int[] s2BandOffsets = s2.getBandOffsets(); float[][] s2Data = s2.getFloatDataArrays(); /* First source alpha channel. */ int a1LineStride = a1.getScanlineStride(); int a1PixelStride = a1.getPixelStride(); int a1BandOffset = a1.getBandOffset(0); float[] a1Data = a1.getFloatDataArray(0); /* Second source alpha channel (if any). */ int a2LineStride = 0; int a2PixelStride = 0; int a2BandOffset = 0; float[] a2Data = null; if (alpha2 != null) { a2LineStride = a2.getScanlineStride(); a2PixelStride = a2.getPixelStride(); a2BandOffset = a2.getBandOffset(0); a2Data = a2.getFloatDataArray(0); } /* Destination color channels. */ int dLineStride = d.getScanlineStride(); int dPixelStride = d.getPixelStride(); int[] dBandOffsets = d.getBandOffsets(); float[][] dData = d.getFloatDataArrays(); int dwidth = d.getWidth(); int dheight = d.getHeight(); int dbands = d.getNumBands(); int s1LineOffset = 0, s2LineOffset = 0, a1LineOffset = 0, a2LineOffset = 0, dLineOffset = 0, s1PixelOffset, s2PixelOffset, a1PixelOffset, a2PixelOffset, dPixelOffset; if (premultiplied) { /* dest = source1 + source2 * (1 - alpha1) */ for (int h = 0; h < dheight; h++) { s1PixelOffset = s1LineOffset; s2PixelOffset = s2LineOffset; a1PixelOffset = a1LineOffset + a1BandOffset; dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; a1LineOffset += a1LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { float t = 1.0F - a1Data[a1PixelOffset]; /* Destination color channels. */ for (int b = 0; b < dbands; b++) { dData[b][dPixelOffset+dBandOffsets[b]] = s1Data[b][s1PixelOffset+s1BandOffsets[b]] + s2Data[b][s2PixelOffset+s2BandOffsets[b]] * t; } s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; a1PixelOffset += a1PixelStride; dPixelOffset += dPixelStride; } } } else { if (alpha2 == null) { /* dest = source1 * alpha1 + source2 * (1 - alpha1) */ for (int h = 0; h < dheight; h++) { s1PixelOffset = s1LineOffset; s2PixelOffset = s2LineOffset; a1PixelOffset = a1LineOffset + a1BandOffset; dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; a1LineOffset += a1LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { float t1 = a1Data[a1PixelOffset]; float t2 = 1.0F - t1; /* Destination color channels. */ for (int b = 0; b < dbands; b++) { dData[b][dPixelOffset+dBandOffsets[b]] = s1Data[b][s1PixelOffset+s1BandOffsets[b]] * t1 + s2Data[b][s2PixelOffset+s2BandOffsets[b]] * t2; } s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; a1PixelOffset += a1PixelStride; dPixelOffset += dPixelStride; } } } else { /* * dest = (source1 * alpha1 + source2 * alpha2 * (1 - alpha1)) / * (alpha1 + alpha2 * (1 - alpha1)) */ for (int h = 0; h < dheight; h++) { s1PixelOffset = s1LineOffset; s2PixelOffset = s2LineOffset; a1PixelOffset = a1LineOffset + a1BandOffset; a2PixelOffset = a2LineOffset + a2BandOffset; dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; a1LineOffset += a1LineStride; a2LineOffset += a2LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { float t1 = a1Data[a1PixelOffset]; float t2 = a2Data[a2PixelOffset] * (1.0F - t1); float t3 = t1 + t2; float t4, t5; if (t3 == 0.0F) { t4 = 0.0F; t5 = 0.0F; } else { t4 = t1 / t3; t5 = t2 / t3; } /* Destination color channels. */ for (int b = 0; b < dbands; b++) { dData[b][dPixelOffset+dBandOffsets[b]] = s1Data[b][s1PixelOffset+s1BandOffsets[b]] * t4 + s2Data[b][s2PixelOffset+s2BandOffsets[b]] * t5; } s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; a1PixelOffset += a1PixelStride; a2PixelOffset += a2PixelStride; dPixelOffset += dPixelStride; } } } } } private void doubleLoop(RasterAccessor s1, RasterAccessor s2, RasterAccessor a1, RasterAccessor a2, RasterAccessor d) { /* First source color channels. */ int s1LineStride = s1.getScanlineStride(); int s1PixelStride = s1.getPixelStride(); int[] s1BandOffsets = s1.getBandOffsets(); double[][] s1Data = s1.getDoubleDataArrays(); /* Second source color channels. */ int s2LineStride = s2.getScanlineStride(); int s2PixelStride = s2.getPixelStride(); int[] s2BandOffsets = s2.getBandOffsets(); double[][] s2Data = s2.getDoubleDataArrays(); /* First source alpha channel. */ int a1LineStride = a1.getScanlineStride(); int a1PixelStride = a1.getPixelStride(); int a1BandOffset = a1.getBandOffset(0); double[] a1Data = a1.getDoubleDataArray(0); /* Second source alpha channel (if any). */ int a2LineStride = 0; int a2PixelStride = 0; int a2BandOffset = 0; double[] a2Data = null; if (alpha2 != null) { a2LineStride = a2.getScanlineStride(); a2PixelStride = a2.getPixelStride(); a2BandOffset = a2.getBandOffset(0); a2Data = a2.getDoubleDataArray(0); } /* Destination color channels. */ int dLineStride = d.getScanlineStride(); int dPixelStride = d.getPixelStride(); int[] dBandOffsets = d.getBandOffsets(); double[][] dData = d.getDoubleDataArrays(); int dwidth = d.getWidth(); int dheight = d.getHeight(); int dbands = d.getNumBands(); int s1LineOffset = 0, s2LineOffset = 0, a1LineOffset = 0, a2LineOffset = 0, dLineOffset = 0, s1PixelOffset, s2PixelOffset, a1PixelOffset, a2PixelOffset, dPixelOffset; if (premultiplied) { /* dest = source1 + source2 * (1 - alpha1) */ for (int h = 0; h < dheight; h++) { s1PixelOffset = s1LineOffset; s2PixelOffset = s2LineOffset; a1PixelOffset = a1LineOffset + a1BandOffset; dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; a1LineOffset += a1LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { double t = 1.0 - a1Data[a1PixelOffset]; /* Destination color channels. */ for (int b = 0; b < dbands; b++) { dData[b][dPixelOffset+dBandOffsets[b]] = s1Data[b][s1PixelOffset+s1BandOffsets[b]] + s2Data[b][s2PixelOffset+s2BandOffsets[b]] * t; } s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; a1PixelOffset += a1PixelStride; dPixelOffset += dPixelStride; } } } else { if (alpha2 == null) { /* dest = source1 * alpha1 + source2 * (1 - alpha1) */ for (int h = 0; h < dheight; h++) { s1PixelOffset = s1LineOffset; s2PixelOffset = s2LineOffset; a1PixelOffset = a1LineOffset + a1BandOffset; dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; a1LineOffset += a1LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { double t1 = a1Data[a1PixelOffset]; double t2 = 1.0 - t1; /* Destination color channels. */ for (int b = 0; b < dbands; b++) { dData[b][dPixelOffset+dBandOffsets[b]] = s1Data[b][s1PixelOffset+s1BandOffsets[b]] * t1 + s2Data[b][s2PixelOffset+s2BandOffsets[b]] * t2; } s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; a1PixelOffset += a1PixelStride; dPixelOffset += dPixelStride; } } } else { /* * dest = (source1 * alpha1 + source2 * alpha2 * (1 - alpha1)) / * (alpha1 + alpha2 * (1 - alpha1)) */ for (int h = 0; h < dheight; h++) { s1PixelOffset = s1LineOffset; s2PixelOffset = s2LineOffset; a1PixelOffset = a1LineOffset + a1BandOffset; a2PixelOffset = a2LineOffset + a2BandOffset; dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; a1LineOffset += a1LineStride; a2LineOffset += a2LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { double t1 = a1Data[a1PixelOffset]; double t2 = a2Data[a2PixelOffset] * (1.0 - t1); double t3 = t1 + t2; double t4, t5; if (t3 == 0.0) { t4 = 0.0; t5 = 0.0; } else { t4 = t1 / t3; t5 = t2 / t3; } /* Destination color channels. */ for (int b = 0; b < dbands; b++) { dData[b][dPixelOffset+dBandOffsets[b]] = s1Data[b][s1PixelOffset+s1BandOffsets[b]] * t4 + s2Data[b][s2PixelOffset+s2BandOffsets[b]] * t5; } s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; a1PixelOffset += a1PixelStride; a2PixelOffset += a2PixelStride; dPixelOffset += dPixelStride; } } } } } /** Returns the format tags to be used with RasterAccessor. */ protected synchronized RasterFormatTag[] getFormatTags() { RenderedImage[] ri; if (alpha2 == null) { ri = new RenderedImage[3]; } else { ri = new RenderedImage[4]; ri[3] = alpha2; } ri[0] = getSourceImage(0); ri[1] = getSourceImage(1); ri[2] = alpha1; return RasterAccessor.findCompatibleTags(ri, this); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/StreamRIF.java0000644000175000017500000001515410444643225025705 0ustar mathieumathieu/* * $RCSfile: StreamRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2006-06-17 00:02:28 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.ImageDecodeParam; import com.sun.media.jai.codec.ImageDecoder; import com.sun.media.jai.codec.SeekableStream; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderedImageFactory; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import java.io.IOException; import java.io.InputStream; import java.util.Enumeration; import java.util.Map; import javax.media.jai.ImageLayout; import javax.media.jai.JAI; import javax.media.jai.NullOpImage; import javax.media.jai.OpImage; import javax.media.jai.OperationRegistry; import javax.media.jai.PlanarImage; import javax.media.jai.TileCache; import javax.media.jai.registry.RIFRegistry; import javax.media.jai.util.ImagingException; import javax.media.jai.util.ImagingListener; import com.sun.media.jai.util.DisposableNullOpImage; import com.sun.media.jai.util.ImageUtil; /** * @see javax.media.jai.operator.StreamDescriptor * * @since EA2 * */ public class StreamRIF implements RenderedImageFactory { /** Constructor. */ public StreamRIF() {} /** * Creates an image from a SeekableStream. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { ImagingListener listener = ImageUtil.getImagingListener(renderHints); SeekableStream src = (SeekableStream)paramBlock.getObjectParameter(0); try { src.seek(0L); } catch (IOException e) { listener.errorOccurred(JaiI18N.getString("StreamRIF0"), e, this, false); // e.printStackTrace(); return null; } ImageDecodeParam param = null; if (paramBlock.getNumParameters() > 1) { param = (ImageDecodeParam)paramBlock.getObjectParameter(1); } String[] names = ImageCodec.getDecoderNames(src); OperationRegistry registry = JAI.getDefaultInstance().getOperationRegistry(); int bound = OpImage.OP_IO_BOUND; ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); if (renderHints != null) { RenderingHints.Key key; key = JAI.KEY_OPERATION_REGISTRY; if (renderHints.containsKey(key)) { registry = (OperationRegistry)renderHints.get(key); } key = JAI.KEY_OPERATION_BOUND; if (renderHints.containsKey(key)) { bound = ((Integer)renderHints.get(key)).intValue(); } } // Try to create a JAI operation with the given name for (int i = 0; i < names.length; i++) { RenderedImageFactory rif = null; try { rif = RIFRegistry.get(registry, names[i]); } catch(IllegalArgumentException iae) { // ignore IAE. } if(rif != null) { RenderedImage im = RIFRegistry.create(registry, names[i], paramBlock, renderHints); if (im != null) { return im; } } } // Set flag indicating that a recovery may be attempted if // an OutOfMemoryError occurs during the decodeAsRenderedImage() // call - which is only possible if the stream can seek backwards. boolean canAttemptRecovery = src.canSeekBackwards(); // Save the stream position prior to decodeAsRenderedImage(). long streamPosition = Long.MIN_VALUE; if(canAttemptRecovery) { try { streamPosition = src.getFilePointer(); } catch(IOException ioe) { listener.errorOccurred(JaiI18N.getString("StreamRIF1"), ioe, this, false); // Unset the recovery attempt flag but otherwise // ignore the exception. canAttemptRecovery = false; } } // Try to create an ImageDecoder directly for (int i = 0; i < names.length; i++) { ImageDecoder dec = ImageCodec.createImageDecoder(names[i], src, param); RenderedImage im = null; try { im = dec.decodeAsRenderedImage(); } catch(OutOfMemoryError memoryError) { // Ran out of memory - may be due to the decoder being // obliged to read the entire image when it creates the // RenderedImage it returns. if(canAttemptRecovery) { // First flush the cache if one is defined. TileCache cache = RIFUtil.getTileCacheHint(renderHints); if(cache != null) { cache.flush(); } // Force garbage collection. System.gc(); //slow try { // Reposition the stream before the previous decoding. src.seek(streamPosition); // Retry image decoding. im = dec.decodeAsRenderedImage(); } catch (IOException ioe) { listener.errorOccurred(JaiI18N.getString("StreamRIF2"), ioe, this, false); im = null; } } else { String message = JaiI18N.getString("CodecRIFUtil0"); listener.errorOccurred(message, new ImagingException(message, memoryError), this, false); // Re-throw the error. // throw memoryError; } } catch (IOException e) { listener.errorOccurred(JaiI18N.getString("StreamRIF2"), e, this, false); im = null; } // If decoding succeeded, wrap the result in an OpImage. if (im != null) { return new DisposableNullOpImage(im, layout, renderHints, bound); } } return null; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/SeparableConvolveOpImage.java0000644000175000017500000006350010203035544030753 0ustar mathieumathieu/* * $RCSfile: SeparableConvolveOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:43 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.KernelJAI; import javax.media.jai.OpImage; import java.util.Map; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class to perform separable convolve on a source image. * * */ final class SeparableConvolveOpImage extends AreaOpImage { static int byteLoopCounter =0; protected KernelJAI kernel; protected int kw, kh, kx, ky; protected float hValues[]; protected float vValues[]; protected float hTables[][]; /** * Creates a SeparableConvoveOpImage on the source * with the given pre-rotated kernel. The image dimensions are * derived the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout * object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param kernel a pre-rotated convolution kernel */ public SeparableConvolveOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, KernelJAI kernel) { super(source, layout, config, true, extender, kernel.getLeftPadding(), kernel.getRightPadding(), kernel.getTopPadding(), kernel.getBottomPadding()); this.kernel = kernel; kw = kernel.getWidth(); kh = kernel.getHeight(); kx = kernel.getXOrigin(); ky = kernel.getYOrigin(); hValues = kernel.getHorizontalKernelData(); vValues = kernel.getVerticalKernelData(); if (sampleModel.getDataType() == DataBuffer.TYPE_BYTE) { hTables = new float[hValues.length][256]; for (int i = 0; i < hValues.length; i++) { float k = hValues[i]; for (int j = 0; j < 256; j++) { byte b = (byte)j; float f = (float)j; hTables[i][b+128] = k*f; } } } } /** * Performs convolution on a specified rectangle. The sources are * cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); Raster source = sources[0]; Rectangle srcRect = mapDestRect(destRect, 0); RasterAccessor srcAccessor = new RasterAccessor(source, srcRect, formatTags[0], getSource(0).getColorModel()); RasterAccessor dstAccessor = new RasterAccessor(dest, destRect, formatTags[1], this.getColorModel()); switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(srcAccessor, dstAccessor); break; case DataBuffer.TYPE_INT: intLoop(srcAccessor, dstAccessor); break; case DataBuffer.TYPE_SHORT: shortLoop(srcAccessor, dstAccessor); break; case DataBuffer.TYPE_USHORT: ushortLoop(srcAccessor, dstAccessor); break; case DataBuffer.TYPE_FLOAT: floatLoop(srcAccessor, dstAccessor); break; case DataBuffer.TYPE_DOUBLE: doubleLoop(srcAccessor, dstAccessor); break; default: } // If the RasterAccessor object set up a temporary buffer for the // op to write to, tell the RasterAccessor to write that data // to the raster no that we're done with it. if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } protected void byteLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); byte dstDataArrays[][] = dst.getByteDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); byte srcDataArrays[][] = src.getByteDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); float tmpBuffer[] = new float[kh*dwidth]; int tmpBufferSize = kh*dwidth; for (int k = 0; k < dnumBands; k++) { byte dstData[] = dstDataArrays[k]; byte srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; int revolver = 0; int kvRevolver = 0; // to match kernel vValues for (int j = 0; j < kh-1; j++) { int srcPixelOffset = srcScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; float f = 0.0f; for (int v = 0; v < kw; v++) { f += hTables[v][srcData[imageOffset]+128]; imageOffset += srcPixelStride; } tmpBuffer[revolver+i] = f; srcPixelOffset += srcPixelStride; } revolver += dwidth; srcScanlineOffset += srcScanlineStride; } // srcScanlineStride already bumped by // kh-1*scanlineStride for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; float f = 0.0f; for (int v = 0; v < kw; v++) { f += hTables[v][srcData[imageOffset]+128]; imageOffset += srcPixelStride; } tmpBuffer[revolver + i] = f; f = 0.5f; // int a = 0; // The vertical kernel must revolve as well int b = kvRevolver + i; for (int a=0; a < kh; a++){ f += tmpBuffer[b] * vValues[a]; b += dwidth; if (b >= tmpBufferSize) b -= tmpBufferSize; } int val = (int)f; if (val < 0) { val = 0; } else if (val > 255) { val = 255; } dstData[dstPixelOffset] = (byte)val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } revolver += dwidth; if (revolver == tmpBufferSize) { revolver = 0; } kvRevolver += dwidth; if (kvRevolver == tmpBufferSize) { kvRevolver = 0; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void shortLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); float tmpBuffer[] = new float[kh*dwidth]; int tmpBufferSize = kh*dwidth; for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; int revolver = 0; int kvRevolver = 0; // to match kernel vValues for (int j = 0; j < kh-1; j++) { int srcPixelOffset = srcScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; float f = 0.0f; for (int v = 0; v < kw; v++) { f += (srcData[imageOffset]) * hValues[v]; imageOffset += srcPixelStride; } tmpBuffer[revolver+i] = f; srcPixelOffset += srcPixelStride; } revolver += dwidth; srcScanlineOffset += srcScanlineStride; } // srcScanlineStride already bumped by // kh-1*scanlineStride for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; float f = 0.0f; for (int v = 0; v < kw; v++) { f += (srcData[imageOffset]) * hValues[v]; imageOffset += srcPixelStride; } tmpBuffer[revolver + i] = f; f = 0.5f; int b = kvRevolver + i; for (int a=0; a < kh; a++){ f += tmpBuffer[b] * vValues[a]; b += dwidth; if (b >= tmpBufferSize) b -= tmpBufferSize; } int val = (int)f; if (val < Short.MIN_VALUE) { val = Short.MIN_VALUE; } else if (val > Short.MAX_VALUE) { val = Short.MAX_VALUE; } dstData[dstPixelOffset] = (short)val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } revolver += dwidth; if (revolver == tmpBufferSize) { revolver = 0; } kvRevolver += dwidth; if (kvRevolver == tmpBufferSize) { kvRevolver = 0; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void ushortLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); float tmpBuffer[] = new float[kh*dwidth]; int tmpBufferSize = kh*dwidth; for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; int revolver = 0; int kvRevolver = 0; // to match kernel vValues for (int j = 0; j < kh-1; j++) { int srcPixelOffset = srcScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; float f = 0.0f; for (int v = 0; v < kw; v++) { f += (srcData[imageOffset] & 0xffff) * hValues[v]; imageOffset += srcPixelStride; } tmpBuffer[revolver+i] = f; srcPixelOffset += srcPixelStride; } revolver += dwidth; srcScanlineOffset += srcScanlineStride; } // srcScanlineStride already bumped by // kh-1*scanlineStride for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; float f = 0.0f; for (int v = 0; v < kw; v++) { f += (srcData[imageOffset] & 0xffff) * hValues[v]; imageOffset += srcPixelStride; } tmpBuffer[revolver + i] = f; f = 0.5f; int b = kvRevolver + i; for (int a=0; a < kh; a++){ f += tmpBuffer[b] * vValues[a]; b += dwidth; if (b >= tmpBufferSize) b -= tmpBufferSize; } int val = (int)f; if (val < 0) { val = 0; } else if (val > 0xffff) { val = 0xffff; } dstData[dstPixelOffset] = (short)val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } revolver += dwidth; if (revolver == tmpBufferSize) { revolver = 0; } kvRevolver += dwidth; if (kvRevolver == tmpBufferSize) { kvRevolver = 0; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void intLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); int dstDataArrays[][] = dst.getIntDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcDataArrays[][] = src.getIntDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); float tmpBuffer[] = new float[kh*dwidth]; int tmpBufferSize = kh*dwidth; for (int k = 0; k < dnumBands; k++) { int dstData[] = dstDataArrays[k]; int srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; int revolver = 0; int kvRevolver = 0; // to match kernel vValues for (int j = 0; j < kh-1; j++) { int srcPixelOffset = srcScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; float f = 0.0f; for (int v = 0; v < kw; v++) { f += (srcData[imageOffset]) * hValues[v]; imageOffset += srcPixelStride; } tmpBuffer[revolver+i] = f; srcPixelOffset += srcPixelStride; } revolver += dwidth; srcScanlineOffset += srcScanlineStride; } // srcScanlineStride already bumped by // kh-1*scanlineStride for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; float f = 0.0f; for (int v = 0; v < kw; v++) { f += (srcData[imageOffset]) * hValues[v]; imageOffset += srcPixelStride; } tmpBuffer[revolver + i] = f; f = 0.5f; int b = kvRevolver + i; for (int a=0; a < kh; a++){ f += tmpBuffer[b] * vValues[a]; b += dwidth; if (b >= tmpBufferSize) b -= tmpBufferSize; } int val = (int)f; dstData[dstPixelOffset] = val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } revolver += dwidth; if (revolver == tmpBufferSize) { revolver = 0; } kvRevolver += dwidth; if (kvRevolver == tmpBufferSize) { kvRevolver = 0; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void floatLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); float dstDataArrays[][] = dst.getFloatDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); float srcDataArrays[][] = src.getFloatDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); float tmpBuffer[] = new float[kh*dwidth]; int tmpBufferSize = kh*dwidth; for (int k = 0; k < dnumBands; k++) { float dstData[] = dstDataArrays[k]; float srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; int revolver = 0; int kvRevolver = 0; // to match kernel vValues for (int j = 0; j < kh-1; j++) { int srcPixelOffset = srcScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; float f = 0.0f; for (int v = 0; v < kw; v++) { f += (srcData[imageOffset]) * hValues[v]; imageOffset += srcPixelStride; } tmpBuffer[revolver+i] = f; srcPixelOffset += srcPixelStride; } revolver += dwidth; srcScanlineOffset += srcScanlineStride; } // srcScanlineStride already bumped by // kh-1*scanlineStride for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; float f = 0.0f; for (int v = 0; v < kw; v++) { f += (srcData[imageOffset]) * hValues[v]; imageOffset += srcPixelStride; } tmpBuffer[revolver + i] = f; f = 0.0f; int b = kvRevolver + i; for (int a=0; a < kh; a++){ f += tmpBuffer[b] * vValues[a]; b += dwidth; if (b >= tmpBufferSize) b -= tmpBufferSize; } dstData[dstPixelOffset] = f; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } revolver += dwidth; if (revolver == tmpBufferSize) { revolver = 0; } kvRevolver += dwidth; if (kvRevolver == tmpBufferSize) { kvRevolver = 0; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void doubleLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); double dstDataArrays[][] = dst.getDoubleDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); double srcDataArrays[][] = src.getDoubleDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); double tmpBuffer[] = new double[kh*dwidth]; int tmpBufferSize = kh*dwidth; for (int k = 0; k < dnumBands; k++) { double dstData[] = dstDataArrays[k]; double srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; int revolver = 0; int kvRevolver = 0; // to match kernel vValues for (int j = 0; j < kh-1; j++) { int srcPixelOffset = srcScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; double f = 0.0; for (int v = 0; v < kw; v++) { f += (srcData[imageOffset]) * hValues[v]; imageOffset += srcPixelStride; } tmpBuffer[revolver+i] = f; srcPixelOffset += srcPixelStride; } revolver += dwidth; srcScanlineOffset += srcScanlineStride; } // srcScanlineStride already bumped by // kh-1*scanlineStride for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; double f = 0.0; for (int v = 0; v < kw; v++) { f += (srcData[imageOffset]) * hValues[v]; imageOffset += srcPixelStride; } tmpBuffer[revolver + i] = f; f = 0.0; int b = kvRevolver + i; for (int a=0; a < kh; a++){ f += tmpBuffer[b] * vValues[a]; b += dwidth; if (b >= tmpBufferSize) b -= tmpBufferSize; } dstData[dstPixelOffset] = f; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } revolver += dwidth; if (revolver == tmpBufferSize) { revolver = 0; } kvRevolver += dwidth; if (kvRevolver == tmpBufferSize) { kvRevolver = 0; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } // public static OpImage createTestImage(OpImageTester oit) { // float data[] = {0.05f,0.10f,0.05f, // 0.10f,0.20f,0.10f, // 0.05f,0.10f,0.05f}; // KernelJAI kJAI = new KernelJAI(3,3,1,1,data); // return new SeparableConvolveOpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource()), // kJAI); // } // public static void main(String args[]) { // String classname = "com.sun.media.jai.opimage.SeparableConvolveOpImage"; // OpImageTester.performDiagnostics(classname,args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ExpOpImage.java0000644000175000017500000003611210203035544026074 0ustar mathieumathieu/* * $RCSfile: ExpOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:25 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import javax.media.jai.ColormapOpImage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; import com.sun.media.jai.util.ImageUtil; /** * An OpImage implementing the "Exp" operation as * described in javax.media.jai.operator.ExpDescriptor. * * The result is rounded to the closest integer for intergral data types. *

This OpImage takes the natural exponential of the pixel * values of an image. The operation is done on a per-pixel, per-band * basis. * * @see javax.media.jai.operator.ExpDescriptor * @see ExpCRIF * * @since EA2 * */ final class ExpOpImage extends ColormapOpImage { /** A lookup table for byte data type. */ private byte[] byteTable = null; /** * The largest unsigned short to get a non-overflowed exponential result. * i.e. cloeset to 65536. * exp(11) = 59874.14171, exp(12) = 162754.7914 */ private static int USHORT_UP_BOUND = 11; /** * The largest short to get a non-overflowed exponential result. * i.e. closest to 32767. * exp(10) = 22026.46579, exp(11) = 59874.14171 */ private static int SHORT_UP_BOUND = 10; /** * The largest int to get a non-overflown exponential result. * i.e. cloeset to 2**31-1 = 2147483647. * exp(21) = 1318815734, exp(22) = 3584912846. */ private static int INT_UP_BOUND = 21; /** * The smallest integer to get a non-zero exponential result is * 0. i.e. exp(0) = 1; exp(-1) = 0.367879441, which will be stored as 0. * all other negative values will result in 0. */ private static int LOW_BOUND = 0; /** * Constructor. * *

The layout of the source is used as the fall-back for * the layout of the destination. Any layout parameters not * specified in the layout argument are set to * the same value as that of the source. * * @param source The source image. * @param layout The destination image layout. */ public ExpOpImage(RenderedImage source, Map config, ImageLayout layout) { super(source, layout, config, true); /* Set flag to permit in-place operation. */ permitInPlaceOperation(); // Initialize the colormap if necessary. initializeColormapOperation(); } /** * Transform the colormap according to the rescaling parameters. */ protected void transformColormap(byte[][] colormap) { initByteTable(); for(int b = 0; b < 3; b++) { byte[] map = colormap[b]; int mapSize = map.length; for(int i = 0; i < mapSize; i++) { map[i] = byteTable[(map[i] & 0xFF)]; } } } /** * Map the pixels inside a specified rectangle whose value is within a * range to a constant on a per-band basis. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { /* Retrieve format tags. */ RasterFormatTag[] formatTags = getFormatTags(); /* No need to mapSourceRect for PointOps. */ RasterAccessor s = new RasterAccessor(sources[0], destRect, formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor d = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); switch (d.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(s, d); break; case DataBuffer.TYPE_USHORT: computeRectUShort(s, d); break; case DataBuffer.TYPE_SHORT: computeRectShort(s, d); break; case DataBuffer.TYPE_INT: computeRectInt(s, d); break; case DataBuffer.TYPE_FLOAT: computeRectFloat(s, d); break; case DataBuffer.TYPE_DOUBLE: computeRectDouble(s, d); break; } if (d.needsClamping()) { d.clampDataArrays(); } d.copyDataToRaster(); } private void computeRectByte(RasterAccessor src, RasterAccessor dst) { initByteTable(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); byte[][] srcData = src.getByteDataArrays(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); byte[][] dstData = dst.getByteDataArrays(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); for (int b = 0; b < dstBands; b++) { byte[] s = srcData[b]; byte[] d = dstData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = byteTable[s[srcPixelOffset] & ImageUtil.BYTE_MASK]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } private void computeRectUShort(RasterAccessor src, RasterAccessor dst) { int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); short[][] srcData = src.getShortDataArrays(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); short[][] dstData = dst.getShortDataArrays(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); short max = (short)ImageUtil.USHORT_MASK; for (int b = 0; b < dstBands; b++) { short[] s = srcData[b]; short[] d = dstData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { double p = s[srcPixelOffset] & ImageUtil.USHORT_MASK; if (p == 0) { d[dstPixelOffset] = 1; } else if (p > USHORT_UP_BOUND) { d[dstPixelOffset] = max; } else { d[dstPixelOffset] = (short)(Math.exp(p) + 0.5); } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } private void computeRectShort(RasterAccessor src, RasterAccessor dst) { int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); short[][] srcData = src.getShortDataArrays(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); short[][] dstData = dst.getShortDataArrays(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); for (int b = 0; b < dstBands; b++) { short[] s = srcData[b]; short[] d = dstData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { double p = s[srcPixelOffset]; if (p < LOW_BOUND) { d[dstPixelOffset] = 0; } else if (p == 0) { d[dstPixelOffset] = 1; } else if (p > SHORT_UP_BOUND) { d[dstPixelOffset] = Short.MAX_VALUE; } else { d[dstPixelOffset] = (short)(Math.exp(p) + 0.5); } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } private void computeRectInt(RasterAccessor src, RasterAccessor dst) { int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); int[][] srcData = src.getIntDataArrays(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); int[][] dstData = dst.getIntDataArrays(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); for (int b = 0; b < dstBands; b++) { int[] s = srcData[b]; int[] d = dstData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { double p = s[srcPixelOffset]; if (p < LOW_BOUND) { d[dstPixelOffset] = 0; } else if (p == 0) { d[dstPixelOffset] = 1; } else if (p > INT_UP_BOUND) { d[dstPixelOffset] = Integer.MAX_VALUE; } else { d[dstPixelOffset] = (int)(Math.exp(p) + 0.5); } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } private void computeRectFloat(RasterAccessor src, RasterAccessor dst) { int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); float[][] srcData = src.getFloatDataArrays(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); float[][] dstData = dst.getFloatDataArrays(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); for (int b = 0; b < dstBands; b++) { float[] s = srcData[b]; float[] d = dstData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = (float)Math.exp(s[srcPixelOffset]); srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } private void computeRectDouble(RasterAccessor src, RasterAccessor dst) { int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); double[][] srcData = src.getDoubleDataArrays(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); double[][] dstData = dst.getDoubleDataArrays(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); for (int b = 0; b < dstBands; b++) { double[] s = srcData[b]; double[] d = dstData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = Math.exp(s[srcPixelOffset]); srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } private synchronized void initByteTable() { if (byteTable != null) return; byteTable = new byte[0x100]; /* * exp(5) = 148.4131591... * exp(6) = 403.4287935... * Calculate up to 5 and set the rest to the maximum value. */ byteTable[0] = 1; for (int i = 1; i < 6; i++) { byteTable[i] = (byte)(Math.exp(i) + 0.5); } for (int i = 6; i < 0x100; i++) { byteTable[i] = (byte)ImageUtil.BYTE_MASK; } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/PhaseCRIF.java0000644000175000017500000000266010203035544025603 0ustar mathieumathieu/* * $RCSfile: PhaseCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:40 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.MagnitudePhaseOpImage; /** * A CRIF supporting the "Phase" operation in the rendered * image layer. * * @since Beta * @see javax.media.jai.operator.PhaseDescriptor * */ public class PhaseCRIF extends CRIFImpl { /** Constructor. */ public PhaseCRIF() { super("phase"); } /** * Creates a new instance of a Phase operator. * * @param paramBlock The scaling type. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); RenderedImage source = paramBlock.getRenderedSource(0); return new MagnitudePhaseOpImage(source, renderHints, layout, MagnitudePhaseOpImage.PHASE); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/BandMergeCRIF.java0000644000175000017500000000345010203035544026365 0ustar mathieumathieu/* * $RCSfile: BandMergeCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:15 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.util.Vector; import java.awt.RenderingHints; import java.awt.geom.Rectangle2D; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderContext; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "BandMerge" operation in the * rendered and renderable image layers. * * @see javax.media.jai.operator.BandMergeDescriptor * @see BandMergeOpImage * */ public class BandMergeCRIF extends CRIFImpl { /** Constructor. */ public BandMergeCRIF() { super("bandmerge"); } /** * Creates a new instance of BandMergeOpImage in the * rendered layer. This method satisifies the implementation of RIF. * * @param paramBlock The two or more source images to be "BandMerged" * together, and their corresponding float array vector. * @param renderHints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); // get vector of RenderedImage sources and parameters Vector sources = paramBlock.getSources(); //Vector params = paramBlock.getParameters(); return new BandMergeOpImage(sources, renderHints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/XorConstOpImage.java0000644000175000017500000002134710203035544027123 0ustar mathieumathieu/* * $RCSfile: XorConstOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:48 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import javax.media.jai.ColormapOpImage; import com.sun.media.jai.util.ImageUtil; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; /** * An OpImage implementing the "XorConst" operation. * *

This OpImage logically "xors" the pixels of a rendered * image with a set of constants, one for each band of the source image. * The destination pixel values are calculated as: *

 *     for (int h = 0; h < dstHeight; h++) {
 *         for (int w = 0; w < dstWidth; w++) {
 *             for (int b = 0; b < dstNumBands; b++) {
 *                 if (constants.length < dstNumBands) {
 *                     dst[h][w][b] = srcs[h][w][b] ^ constants[0];
 *                 } else {
 *                     dst[h][w][b] = srcs[h][w][b] ^ constants[b];
 *                 }
 *             }
 *         }
 *     }
 * 
* * @see javax.media.jai.operator.XorConstDescriptor * @see XorConstCRIF * * * @since EA2 */ final class XorConstOpImage extends ColormapOpImage { /** The constants to be xored, one for each band. */ protected int[] constants; /** * Constructor. * * @param source The source image. * @param layout The destination image layout. * @param constants The constants to be xored, stored as reference. */ public XorConstOpImage(RenderedImage source, Map config, ImageLayout layout, int[] constants) { super(source, layout, config, true); int numBands = getSampleModel().getNumBands(); if (constants.length < numBands) { this.constants = new int[numBands]; for (int i = 0; i < numBands; i++) { this.constants[i] = constants[0]; } } else { this.constants = (int[])constants.clone(); } // Set flag to permit in-place operation. permitInPlaceOperation(); // Initialize the colormap if necessary. initializeColormapOperation(); } /** * Transform the colormap according to the rescaling parameters. */ protected void transformColormap(byte[][] colormap) { for(int b = 0; b < 3; b++) { byte[] map = colormap[b]; int mapSize = map.length; int c = b < constants.length ? constants[b] : constants[0]; for(int i = 0; i < mapSize; i++) { map[i] = ImageUtil.clampRoundByte((map[i] & 0xFF) ^ c); } } } /** * Logically "xors" a constant with the pixel values within a specified * rectangle. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); Rectangle srcRect = mapDestRect(destRect, 0); RasterAccessor dst = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); RasterAccessor src = new RasterAccessor(sources[0], srcRect, formatTags[0], getSource(0).getColorModel()); switch (dst.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(src, dst); break; case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: computeRectShort(src, dst); break; case DataBuffer.TYPE_INT: computeRectInt(src, dst); break; } /* Do not clamp dst data. */ dst.copyDataToRaster(); } private void computeRectByte(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); byte[][] dstData = dst.getByteDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); byte[][] srcData = src.getByteDataArrays(); for (int b = 0; b < dstBands; b++) { int c = constants[b]; byte[] d = dstData[b]; byte[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = (byte)(s[srcPixelOffset] ^ c); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectShort(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); short[][] dstData = dst.getShortDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); short[][] srcData = src.getShortDataArrays(); for (int b = 0; b < dstBands; b++) { int c = constants[b]; short[] d = dstData[b]; short[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = (short)(s[srcPixelOffset] ^ c); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectInt(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); int[][] dstData = dst.getIntDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); int[][] srcData = src.getIntDataArrays(); for (int b = 0; b < dstBands; b++) { int c = constants[b]; int[] d = dstData[b]; int[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = s[srcPixelOffset] ^ c; dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } public static void main (String args[]) { System.out.println("XorConstOpImage Test"); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/PatternRIF.java0000644000175000017500000000374210203035544026057 0ustar mathieumathieu/* * $RCSfile: PatternRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:40 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderedImageFactory; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.ImageLayout; import java.util.Map; /** * A RIF supporting the "Pattern" operation in the * rendered image layer. * * @see javax.media.jai.operator.PatternDescriptor * @see PatternOpImage * */ public class PatternRIF implements RenderedImageFactory { /** Constructor. */ public PatternRIF() {} /** * Creates a new instance of PatternOpImage in the rendered layer. * This method satisfies the implementation of RIF. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); int minX = 0; int minY = 0; if (layout != null) { if (layout.isValid(ImageLayout.MIN_X_MASK)) { minX = layout.getMinX(null); } if (layout.isValid(ImageLayout.MIN_Y_MASK)) { minY = layout.getMinY(null); } } RenderedImage source = (RenderedImage)paramBlock.getSource(0); Raster pattern = source.getData(); ColorModel colorModel = source.getColorModel(); // Get image width and height from the parameter block int width = paramBlock.getIntParameter(0); int height = paramBlock.getIntParameter(1); return new PatternOpImage(pattern, colorModel, minX, minY, width, height); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/CropCRIF.java0000644000175000017500000001032410203035544025442 0ustar mathieumathieu/* * $RCSfile: CropCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:21 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import java.awt.image.renderable.RenderContext; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import javax.media.jai.JAI; import javax.media.jai.PlanarImage; import java.util.Map; /** * A CRIF supporting the "Crop" operation in the rendered * and renderable image layers. * * @see javax.media.jai.operator.CropDescriptor * @see CropOpImage * * * @since EA4 */ public class CropCRIF extends CRIFImpl { /** Constructor. */ public CropCRIF() { super("crop"); } /** * Creates a new instance of CropOpImage in the * rendered layer. * * @param args The source image and bounding rectangle * @param hints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints renderHints) { // Get the source image. RenderedImage src = (RenderedImage)args.getRenderedSource(0); // Get the parameters. float originX = args.getFloatParameter(0); float originY = args.getFloatParameter(1); float width = args.getFloatParameter(2); float height = args.getFloatParameter(3); // Return the OpImage. return new CropOpImage(src, originX, originY, width, height); } /** * Creates a RenderedImage from the renderable layer. * * @param renderContext The rendering information associated with * this rendering. * @param paramBlock The parameters used to create the image. * @return A RenderedImage. */ public RenderedImage create(RenderContext renderContext, ParameterBlock paramBlock) { // Get the destination bounds in rendering-independent coordinates. Rectangle2D dstRect2D = getBounds2D(paramBlock); // Map the destination bounds to rendered coordinates. This method // will cause extra data to be present if there is any rotation or // shear. AffineTransform tf = renderContext.getTransform(); Rectangle2D rect = tf.createTransformedShape(dstRect2D).getBounds2D(); // Make sure that the rendered rectangle is non-empty. if(rect.getWidth() < 1.0 || rect.getHeight() < 1.0) { double w = Math.max(rect.getWidth(), 1.0); double h = Math.max(rect.getHeight(), 1.0); rect.setRect(rect.getMinX(), rect.getMinY(), w, h); } // Initialize the rendered ParameterBlock. ParameterBlock pb = new ParameterBlock(); pb.addSource(paramBlock.getRenderedSource(0)); pb.set((float)rect.getMinX(), 0); pb.set((float)rect.getMinY(), 1); pb.set((float)rect.getWidth(), 2); pb.set((float)rect.getHeight(), 3); // Crop the rendered source. return JAI.create("crop", pb, renderContext.getRenderingHints()); } /** * Returns the bounding box for the output of the operation. * * @param paramBlock A ParameterBlock containing the * renderable sources and parameters of the operation. * @return A Rectangle2D specifying the bounding box. */ public Rectangle2D getBounds2D(ParameterBlock paramBlock) { // Return a rectangle representing the desired bounds. return new Rectangle2D.Float(paramBlock.getFloatParameter(0), paramBlock.getFloatParameter(1), paramBlock.getFloatParameter(2), paramBlock.getFloatParameter(3)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/SubsampleBinaryToGray2x2OpImage.java0000644000175000017500000004274310203035544032131 0ustar mathieumathieu/* * $RCSfile: SubsampleBinaryToGray2x2OpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:44 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.geom.Point2D; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.Point; import java.util.Hashtable; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferInt; import java.awt.image.DataBufferUShort; import java.awt.image.IndexColorModel; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.PixelInterleavedSampleModel; import java.awt.image.SinglePixelPackedSampleModel; import java.awt.image.SampleModel; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.ImageLayout; import java.util.Map; import javax.media.jai.GeometricOpImage; import javax.media.jai.PackedImageData; import javax.media.jai.PixelAccessor; /** * A class extending GeometricOpImage to * subsample binary images to gray scale images. * This class provides an acceleration for a special * case of SubsampleBinaryToGrayOpImage, when the * scaling factors in x and y directions are 1/2. * * * dst minX = floor(src minX /2 ) * dst minY = floor(src minY /2 ) * dst width = floor(src width / 2) * dst height = floor(src height / 2) * * * @see ScaleOpImage * @see SubsampleBinaryToGrayOpImage * */ class SubsampleBinaryToGray2x2OpImage extends GeometricOpImage { /** block pixel size; to shrink to one pixel */ private int blockX; private int blockY; /** destination image width */ private int dWidth; /** destination image height*/ private int dHeight; /** the 1st pixel location for destination pixels, i.e., * the source pixel matrix * [yValues[j] yValues[j]+blockY] by [xValues[i] xValues[i]+blockX] * will be condensed to form pixel ith pixel in row j */ // a look up table; lut[i] counts 1s in binary expression of i // lut4_45 counts 1s in bit 4,5 in i&0x0f, the last 8 bits of i // lut4_67 counts 1s in bit 6,7 in i&0x0f, the last 8 bits of i private int[] lut4_45; private int[] lut4_67; // convert from number of bits on count to gray value, with // scaling, i.e. if invScaleX,Y=3,3, then the possible bit // counts are 0..9, hence the lookup tables are [0..9] * 255/9. // there are 4 kinds of scaling, depending on area size // when invScaleX,Y are non integers, // [floor(invScaleY), ceil(invScaleY)] x [floor(invScaleX), ceil(invScaleX)] private byte[] lutGray; /** * Constructs a SubsampleBinaryToGray2x2OpImage from a RenderedImage * source, an optional BorderExtender, x and y scale * and translation factors, and an Interpolation * object. The image dimensions are determined by forward-mapping * the source bounds, and are passed to the superclass constructor * by means of the layout parameter. Other fields of * the layout are passed through unchanged. If * layout is null, a new * ImageLayout will be constructor to hold the bounds * information. * * Note that the scale factors are represented internally as Rational * numbers in order to workaround inexact device specific representation * of floating point numbers. For instance the floating point number 1.2 * is internally represented as 1.200001, which can throw the * calculations off during a forward/backward map. * *

The Rational approximation is valid upto the sixth decimal place. * * @param layout an ImageLayout optionally containing * the tile grid layout, SampleModel, and * ColorModel, or null. * @param source a RenderedImage. * from this OpImage, or null. If * null, no caching will be performed. * @param cobbleSources a boolean indicating whether * computeRect expects contiguous sources. * @param extender a BorderExtender, or null. * @param interp an Interpolation object to use for * resampling. * @param scaleX scale factor along x axis. * @param scaleY scale factor along y axis. * * @throws IllegalArgumentException if combining the * source bounds with the layout parameter results in negative * output width or height. */ public SubsampleBinaryToGray2x2OpImage(RenderedImage source, ImageLayout layout, Map config){ super(vectorize(source), SubsampleBinaryToGrayOpImage.layoutHelper(source, 1.0F/2, 1.0F/2, layout, config), config, true, // cobbleSources, null, // extender null, // interpolation null); blockX = 2; blockY = 2; int srcWidth = source.getWidth(); int srcHeight= source.getHeight(); dWidth = srcWidth / blockX; dHeight = srcHeight/ blockY; if (extender == null) { computableBounds = new Rectangle(0, 0, dWidth, dHeight); } else { // If extender is present we can write the entire destination. computableBounds = getBounds(); } // these can be delayed, such as placed in computeRect() buildLookupTables(); } /** * Computes the source point corresponding to the supplied point. * * @param destPt the position in destination image coordinates * to map to source image coordinates. * * @return a Point2D of the same class as * destPt. * * @throws IllegalArgumentException if destPt is * null. * * @since JAI 1.1.2 */ public Point2D mapDestPoint(Point2D destPt) { if (destPt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } Point2D pt = (Point2D)destPt.clone(); pt.setLocation(destPt.getX()*2.0, destPt.getY()*2.0); return pt; } /** * Computes the destination point corresponding to the supplied point. * * @param sourcePt the position in source image coordinates * to map to destination image coordinates. * * @return a Point2D of the same class as * sourcePt. * * @throws IllegalArgumentException if sourcePt is * null. * * @since JAI 1.1.2 */ public Point2D mapSourcePoint(Point2D sourcePt) { if (sourcePt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } Point2D pt = (Point2D)sourcePt.clone(); pt.setLocation(sourcePt.getX()/2.0, sourcePt.getY()/2.0); return pt; } /** * Returns the minimum bounding box of the region of the destination * to which a particular Rectangle of the specified source * will be mapped. * * @param sourceRect the Rectangle in source coordinates. * @param sourceIndex the index of the source image. * * @return a Rectangle indicating the destination * bounding box, or null if the bounding box * is unknown. * * @throws IllegalArgumentException if sourceIndex is * negative or greater than the index of the last source. * @throws IllegalArgumentException if sourceRect is * null. */ protected Rectangle forwardMapRect(Rectangle sourceRect, int sourceIndex) { if ( sourceRect == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sourceIndex != 0) { throw new IllegalArgumentException(JaiI18N.getString("Generic1")); } // Get the source dimensions int dx0 = sourceRect.x / blockX; int dy0 = sourceRect.y / blockY; int dx1 = (sourceRect.x + sourceRect.width -1) / blockX; int dy1 = (sourceRect.y + sourceRect.height-1) / blockY; return new Rectangle(dx0, dy0, dx1-dx0+1, dy1-dy0+1); } /** * Returns the minimum bounding box of the region of the specified * source to which a particular Rectangle of the * destination will be mapped. * * @param destRect the Rectangle in destination coordinates. * @param sourceIndex the index of the source image. * * @return a Rectangle indicating the source bounding box, * or null if the bounding box is unknown. * * @throws IllegalArgumentException if sourceIndex is * negative or greater than the index of the last source. * @throws IllegalArgumentException if destRect is * null. */ protected Rectangle backwardMapRect(Rectangle destRect, int sourceIndex) { if ( destRect == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sourceIndex != 0) { throw new IllegalArgumentException(JaiI18N.getString("Generic1")); } // Get the destination rectangle coordinates and dimensions int sx0 = destRect.x * blockX; int sy0 = destRect.y * blockY; int sx1 = (destRect.x + destRect.width -1) * blockX; int sy1 = (destRect.y + destRect.height-1) * blockY; return new Rectangle(sx0, sy0, sx1 - sx0 + blockX, sy1 - sy0 + blockY); } /** * Performs a subsamplebinarytogray operation on a specified rectangle. * The sources are cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; switch (source.getSampleModel().getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_INT: byteLoop2x2(source, dest, destRect); break; default: throw new RuntimeException(JaiI18N.getString("SubsampleBinaryToGrayOpImage0")); } } private void byteLoop2x2(Raster source, WritableRaster dest, Rectangle destRect) { PixelAccessor pa = new PixelAccessor(source.getSampleModel(), null); PackedImageData pid = pa.getPackedPixels(source, source.getBounds(), false, false); byte[] sourceData = pid.data; int sourceDBOffset = pid.offset; int dx = destRect.x; int dy = destRect.y; int dwi = destRect.width; int dhi = destRect.height; int sourceTransX = pid.rect.x; // source.getSampleModelTranslateX(); int sourceTransY = pid.rect.y; // source.getSampleModelTranslateY(); int sourceDataBitOffset = pid.bitOffset; int sourceScanlineStride = pid.lineStride; PixelInterleavedSampleModel destSM = (PixelInterleavedSampleModel)dest.getSampleModel(); DataBufferByte destDB = (DataBufferByte)dest.getDataBuffer(); int destTransX = dest.getSampleModelTranslateX(); int destTransY = dest.getSampleModelTranslateY(); int destScanlineStride = destSM.getScanlineStride(); byte[] destData = destDB.getData(); int destDBOffset = destDB.getOffset(); int[] sAreaBitsOn = new int[4]; if ((sourceDataBitOffset & 0x01) == 0){ for(int j = 0; j < dhi; j++) { int y = (dy + j) << 1; // y = (dy+j) * blockY; int sourceYOffset = (y - sourceTransY)*sourceScanlineStride + sourceDBOffset; int sourceYOffset2= sourceYOffset + sourceScanlineStride; int destYOffset = (j + dy - destTransY)*destScanlineStride + destDBOffset; destYOffset += dx - destTransX; int selement, sbitnumi, sstartbiti, sbytenumi; // sbitnumi - the 1st bit position from the minX of the raster // sstartbiti - the 1st bit position in the byte data //sbitnumi = blockX * dx - sourceTransX + sourceDataBitOffset; sbitnumi = (dx<<1) - sourceTransX + sourceDataBitOffset; for(int i=0; i < dwi; ){ sbytenumi = sbitnumi >> 3; sstartbiti = sbitnumi % 8; selement = 0x00ff & (int)sourceData[sourceYOffset + sbytenumi]; sAreaBitsOn[2] = lut4_45[selement & 0x000f]; sAreaBitsOn[3] = lut4_67[selement & 0x000f]; selement >>= 4; sAreaBitsOn[0] = lut4_45[selement]; sAreaBitsOn[1] = lut4_67[selement]; // next line selement = 0x00ff & (int)sourceData[sourceYOffset2 + sbytenumi]; sAreaBitsOn[2] += lut4_45[selement & 0x000f]; sAreaBitsOn[3] += lut4_67[selement & 0x000f]; selement >>= 4; sAreaBitsOn[0] += lut4_45[selement]; sAreaBitsOn[1] += lut4_67[selement]; // set dest elements // count in 2s // sstartbiti = 0 means the 0th of sAreaBitsOn is added to // current dest position, i.e. destYOffset + i; // sstartbiti = 2 means the 1th of sAreaBitsOn is added to // current dest position, i.e. destYOffset + i; // sstartbiti now means different sstartbiti >>= 1; // sstartbiti = sstartbiti / 2; while (sstartbiti < 4 && i < dwi){ destData[destYOffset + i] = lutGray[sAreaBitsOn[sstartbiti]]; sstartbiti++; i++; sbitnumi += blockX; } } } }else{ // need to shift one bit a lot of the time for(int j = 0; j < dhi; j++) { int y = (dy + j)<< 1; // y = (dy+j) * blockY; int sourceYOffset = (y - sourceTransY)*sourceScanlineStride + sourceDBOffset; int sourceYOffset2= sourceYOffset + sourceScanlineStride; int destYOffset = (j + dy - destTransY)*destScanlineStride + destDBOffset; destYOffset += dx - destTransX; int selement, sbitnumi, sstartbiti, sbytenumi; // sbitnumi - the 1st bit position from the minX of the raster // sstartbiti - the 1st bit position in the byte data // sbitnumi = blockX * dx - sourceTransX + sourceDataBitOffset; sbitnumi = (dx<<1) - sourceTransX + sourceDataBitOffset; for(int i=0; i < dwi; ){ sbytenumi = sbitnumi >> 3; sstartbiti = sbitnumi % 8; // shift one bit, so that we can use almost the same code // as even bitOffset cases as above selement = 0x00ff & (sourceData[sourceYOffset + sbytenumi]<< 1); sAreaBitsOn[2] = lut4_45[selement & 0x000f]; sAreaBitsOn[3] = lut4_67[selement & 0x000f]; selement >>= 4; sAreaBitsOn[0] = lut4_45[selement]; sAreaBitsOn[1] = lut4_67[selement]; // next line // shift one bit selement = 0x00ff & (sourceData[sourceYOffset2 + sbytenumi]<<1); sAreaBitsOn[2] += lut4_45[selement & 0x000f]; sAreaBitsOn[3] += lut4_67[selement & 0x000f]; selement >>= 4; sAreaBitsOn[0] += lut4_45[selement]; sAreaBitsOn[1] += lut4_67[selement]; // taking care the extra bit that is in the next byte (<0 means 1 for the 1st bit) // when there is one more byte to go on this line // as long as there is more data in the buffer // adding to the last one is ok; will not be used if out side of raster bounds sbytenumi += 1; // move to next byte if(sbytenumi < sourceData.length - sourceYOffset2){ sAreaBitsOn[3] += sourceData[sourceYOffset + sbytenumi] < 0? 1: 0; sAreaBitsOn[3] += sourceData[sourceYOffset2 + sbytenumi] < 0? 1: 0; } // set dest elements // count in 2s, this corresponds to i th dest // sstartbiti now means different sstartbiti >>= 1; // sstartbiti = sstartbiti / 2; while ( sstartbiti < 4 && i < dwi){ destData[destYOffset + i] = lutGray[sAreaBitsOn[sstartbiti]]; sstartbiti++; i++; sbitnumi += blockX; } } } } } // shortLoop and intLoop are not needed, due to PixelAccessor or RasterAccessor's // returns byte packing for binary data // private void shortLoop(Raster source, WritableRaster dest, Rectangle destRect) {;} // private void intLoop(Raster source, WritableRaster dest, Rectangle destRect) {;} // buildLookupTables() // initializes variabes bitSet and lut // to be called mainly in the constructor private final void buildLookupTables(){ lut4_45 = new int[16]; lut4_67 = new int[16]; // 6-7th bits on lut4_67[0] = 0; lut4_67[1] = 1; lut4_67[2] = 1; lut4_67[3] = 2; for(int i= 4; i < 16; i++) lut4_67[i] = lut4_67[i&0x03]; // 4 and 5 th bits for(int i= 0; i < 16; i++) lut4_45[i] = lut4_67[i>>2]; // lutGray if (lutGray != null) return; lutGray = new byte[blockX * blockY +1]; for (int i=0; i < lutGray.length; i++){ int tmp = (int)Math.round(255.0F*i/(lutGray.length-1.0F)); lutGray[i] = tmp>255? (byte)0xff : (byte)tmp; } // switch black-white if needed if (SubsampleBinaryToGrayOpImage.isMinWhite( this.getSourceImage(0).getColorModel())){ for(int i=0; i < lutGray.length; i++) lutGray[i]= (byte)(255-(0xff &lutGray[i])); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/AffineBilinearOpImage.java0000644000175000017500000012121010203035544030170 0ustar mathieumathieu/* * $RCSfile: AffineBilinearOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:13 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationBilinear; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage subclass that performs bilinear Affine mapping */ final class AffineBilinearOpImage extends AffineOpImage { /** * Constructs an AffineBilinearOpImage from a RenderedImage source, * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param interp an Interpolation object to use for resampling * @param transform the desired AffineTransform. */ public AffineBilinearOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, AffineTransform transform, Interpolation interp, double[] backgroundValues) { super(source, extender, config, layout, transform, interp, backgroundValues); } /** * Performs an affine transform on a specified rectangle. The sources are * cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster [] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); Raster source = sources[0]; Rectangle srcRect = source.getBounds(); int srcRectX = srcRect.x; int srcRectY = srcRect.y; // // Get data for the source rectangle & the destination rectangle // In the first version source Rectangle is the whole source // image always. // // See if we can cache the source to avoid multiple rasteraccesors // RasterAccessor srcAccessor = new RasterAccessor(source, srcRect, formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor dstAccessor = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(srcAccessor, destRect, srcRectX, srcRectY, dstAccessor); break; case DataBuffer.TYPE_INT: intLoop(srcAccessor, destRect, srcRectX, srcRectY, dstAccessor); break; case DataBuffer.TYPE_SHORT: shortLoop(srcAccessor, destRect, srcRectX, srcRectY, dstAccessor); break; case DataBuffer.TYPE_USHORT: ushortLoop(srcAccessor, destRect, srcRectX, srcRectY, dstAccessor); break; case DataBuffer.TYPE_FLOAT: floatLoop(srcAccessor, destRect, srcRectX, srcRectY, dstAccessor); break; case DataBuffer.TYPE_DOUBLE: doubleLoop(srcAccessor, destRect, srcRectX, srcRectY, dstAccessor); break; } // If the RasterAccessor object set up a temporary buffer for the // op to write to, tell the RasterAccessor to write that data // to the raster, that we're done with it. if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } private void byteLoop(RasterAccessor src, Rectangle destRect, int srcRectX, int srcRectY, RasterAccessor dst) { float src_rect_x1 = src.getX(); float src_rect_y1 = src.getY(); float src_rect_x2 = src_rect_x1 + src.getWidth(); float src_rect_y2 = src_rect_y1 + src.getHeight(); float s_x, s_y; float fracx, fracy; int pxlow, pylow, pxhigh, pyhigh; int s, s00, s01, s10, s11; float s0, s1; float tmp; int dstPixelOffset; int dstOffset = 0; Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); byte dstDataArrays[][] = dst.getByteDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); byte srcDataArrays[][] = src.getByteDataArrays(); int bandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; byte[] backgroundByte = new byte[dst_num_bands]; for (int i = 0; i < dst_num_bands; i++) backgroundByte[i] = (byte)backgroundValues[i]; for (int y = dst_min_y; y < dst_max_y; y++) { dstPixelOffset = dstOffset; // Backward map the first point in the line // The energy is at the (pt_x + 0.5, pt_y + 0.5) dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates s_x = (float)src_pt.getX(); s_y = (float)src_pt.getY(); // As per definition of bilinear interpolation s_x -= 0.5; s_y -= 0.5; // Floor to get the integral coordinate int s_ix = (int) Math.floor(s_x); int s_iy = (int) Math.floor(s_y); fracx = s_x - (float)s_ix; fracy = s_y - (float)s_iy; // Translate to/from SampleModel space & Raster space pylow = (s_iy - srcRectY) * srcScanlineStride; pxlow = (s_ix - srcRectX) * srcPixelStride; pyhigh = pylow + srcScanlineStride; pxhigh = pxlow + srcPixelStride; int tmp00 = pxlow + pylow; int tmp01 = pxhigh + pylow; int tmp10 = pxlow + pyhigh; int tmp11 = pxhigh + pyhigh; for (int x = dst_min_x; x < dst_max_x; x++) { // // Check against the source rectangle // if ((s_ix >= src_rect_x1) && (s_ix < (src_rect_x2 - 1)) && (s_iy >= src_rect_y1) && (s_iy < (src_rect_y2 - 1))) { for (int k2=0; k2 < dst_num_bands; k2++) { // // Get the 4 neighbourhood pixels // byte tmp_row[]; int tmp_col; // Get to the right row tmp_row = srcDataArrays[k2]; // Position at the bandOffset tmp_col = bandOffsets[k2]; s00 = tmp_row[tmp00 + tmp_col] & 0xff; s01 = tmp_row[tmp01 + tmp_col] & 0xff; s10 = tmp_row[tmp10 + tmp_col] & 0xff; s11 = tmp_row[tmp11 + tmp_col] & 0xff; // Weighted Average of these 4 pixels s0 = (float) s00 + ((float) (s01 - s00) * fracx); s1 = (float) s10 + ((float) (s11 - s10) * fracx); tmp = s0 + ((s1 - s0) * fracy); // Round if (tmp < 0.5F) { s = 0; } else if (tmp > 254.5F) { s = 255; } else { s = (int) (tmp + 0.5F); } // Write the result dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = (byte) (s & 0xff); } } else if (setBackground) { for (int k=0; k < dst_num_bands; k++) dstDataArrays[k][dstPixelOffset+dstBandOffsets[k]] = backgroundByte[k]; } // walk if (fracx < fracdx1) { s_ix += incx; fracx += fracdx; } else { s_ix += incx1; fracx -= fracdx1; } if (fracy < fracdy1) { s_iy += incy; fracy += fracdy; } else { s_iy += incy1; fracy -= fracdy1; } // Translate to/from SampleModel space & Raster space pylow = (s_iy - srcRectY) * srcScanlineStride; pxlow = (s_ix - srcRectX) * srcPixelStride; pyhigh = pylow + srcScanlineStride; pxhigh = pxlow + srcPixelStride; tmp00 = pxlow + pylow; tmp01 = pxhigh + pylow; tmp10 = pxlow + pyhigh; tmp11 = pxhigh + pyhigh; // Go to next pixel dstPixelOffset += dstPixelStride; } // Go to the next line in the destination rectangle dstOffset += dstScanlineStride; } } private void intLoop(RasterAccessor src, Rectangle destRect, int srcRectX, int srcRectY, RasterAccessor dst) { float src_rect_x1 = src.getX(); float src_rect_y1 = src.getY(); float src_rect_x2 = src_rect_x1 + src.getWidth(); float src_rect_y2 = src_rect_y1 + src.getHeight(); float s_x, s_y; float fracx, fracy; int pxlow, pylow, pxhigh, pyhigh; int s, s00, s01, s10, s11; float s0, s1; float tmp; int dstPixelOffset; int dstOffset = 0; Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); int dstDataArrays[][] = dst.getIntDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcDataArrays[][] = src.getIntDataArrays(); int bandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; int[] backgroundInt = new int[dst_num_bands]; for (int i = 0; i < dst_num_bands; i++) backgroundInt[i] = (int)backgroundValues[i]; for (int y = dst_min_y; y < dst_max_y; y++) { dstPixelOffset = dstOffset; // Backward map the first point in the line dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates s_x = (float)src_pt.getX(); s_y = (float)src_pt.getY(); // As per definition of bilinear interpolation s_x -= 0.5; s_y -= 0.5; // Floor to get the integral coordinate int s_ix = (int) Math.floor(s_x); int s_iy = (int) Math.floor(s_y); fracx = s_x - (float)s_ix; fracy = s_y - (float)s_iy; // Translate to/from SampleModel space & Raster space pylow = (s_iy - srcRectY) * srcScanlineStride; pxlow = (s_ix - srcRectX) * srcPixelStride; pyhigh = pylow + srcScanlineStride; pxhigh = pxlow + srcPixelStride; int tmp00 = pxlow + pylow; int tmp01 = pxhigh + pylow; int tmp10 = pxlow + pyhigh; int tmp11 = pxhigh + pyhigh; for (int x = dst_min_x; x < dst_max_x; x++) { // // Check against the source rectangle // if ((s_ix >= src_rect_x1) && (s_ix < (src_rect_x2 - 1)) && (s_iy >= src_rect_y1) && (s_iy < (src_rect_y2 - 1))) { for (int k2=0; k2 < dst_num_bands; k2++) { // // Get the 4 neighbourhood pixels // int tmp_row[]; int tmp_col; // Get to the right row tmp_row = srcDataArrays[k2]; // Position at the bandOffset tmp_col = bandOffsets[k2]; s00 = tmp_row[tmp00 + tmp_col]; s01 = tmp_row[tmp01 + tmp_col]; s10 = tmp_row[tmp10 + tmp_col]; s11 = tmp_row[tmp11 + tmp_col]; // Weighted Average of these 4 pixels s0 = (float) s00 + ((float) (s01 - s00) * fracx); s1 = (float) s10 + ((float) (s11 - s10) * fracx); tmp = s0 + ((s1 - s0) * fracy); // Round if (tmp < (float) Integer.MIN_VALUE) { s = Integer.MIN_VALUE; } else if (tmp > (float) Integer.MAX_VALUE) { s = Integer.MAX_VALUE; } else if (tmp > 0) { s = (int) (tmp + 0.5F); } else { s = (int) (tmp - 0.5F); } // Write the result dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = s; } } else if (setBackground) { for (int k=0; k < dst_num_bands; k++) dstDataArrays[k][dstPixelOffset+dstBandOffsets[k]] = backgroundInt[k]; } // walk if (fracx < fracdx1) { s_ix += incx; fracx += fracdx; } else { s_ix += incx1; fracx -= fracdx1; } if (fracy < fracdy1) { s_iy += incy; fracy += fracdy; } else { s_iy += incy1; fracy -= fracdy1; } // Translate to/from SampleModel space & Raster space pylow = (s_iy - srcRectY) * srcScanlineStride; pxlow = (s_ix - srcRectX) * srcPixelStride; pyhigh = pylow + srcScanlineStride; pxhigh = pxlow + srcPixelStride; tmp00 = pxlow + pylow; tmp01 = pxhigh + pylow; tmp10 = pxlow + pyhigh; tmp11 = pxhigh + pyhigh; dstPixelOffset += dstPixelStride; } dstOffset += dstScanlineStride; } } private void shortLoop(RasterAccessor src, Rectangle destRect, int srcRectX, int srcRectY, RasterAccessor dst) { float src_rect_x1 = src.getX(); float src_rect_y1 = src.getY(); float src_rect_x2 = src_rect_x1 + src.getWidth(); float src_rect_y2 = src_rect_y1 + src.getHeight(); float s_x, s_y; float fracx, fracy; int pxlow, pylow, pxhigh, pyhigh; int s, s00, s01, s10, s11; float s0, s1; float tmp; int dstPixelOffset; int dstOffset = 0; Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int bandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; short[] backgroundShort = new short[dst_num_bands]; for (int i = 0; i < dst_num_bands; i++) backgroundShort[i] = (short)backgroundValues[i]; for (int y = dst_min_y; y < dst_max_y; y++) { dstPixelOffset = dstOffset; // Backward map the first point in the line dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates s_x = (float)src_pt.getX(); s_y = (float)src_pt.getY(); // As per definition of bilinear interpolation s_x -= 0.5; s_y -= 0.5; // Floor to get the integral coordinate int s_ix = (int) Math.floor(s_x); int s_iy = (int) Math.floor(s_y); fracx = s_x - (float)s_ix; fracy = s_y - (float)s_iy; // Translate to/from SampleModel space & Raster space pylow = (s_iy - srcRectY) * srcScanlineStride; pxlow = (s_ix - srcRectX) * srcPixelStride; pyhigh = pylow + srcScanlineStride; pxhigh = pxlow + srcPixelStride; int tmp00 = pxlow + pylow; int tmp01 = pxhigh + pylow; int tmp10 = pxlow + pyhigh; int tmp11 = pxhigh + pyhigh; for (int x = dst_min_x; x < dst_max_x; x++) { // // Check against the source rectangle // if ((s_ix >= src_rect_x1) && (s_ix < (src_rect_x2 - 1)) && (s_iy >= src_rect_y1) && (s_iy < (src_rect_y2 - 1))) { for (int k2=0; k2 < dst_num_bands; k2++) { // // Get the 4 neighbourhood pixels // short tmp_row[]; int tmp_col; // Get to the right row tmp_row = srcDataArrays[k2]; // Position at the bandOffset tmp_col = bandOffsets[k2]; s00 = tmp_row[tmp00 + tmp_col]; s01 = tmp_row[tmp01 + tmp_col]; s10 = tmp_row[tmp10 + tmp_col]; s11 = tmp_row[tmp11 + tmp_col]; // Weighted Average of these 4 pixels s0 = (float) s00 + ((float) (s01 - s00) * fracx); s1 = (float) s10 + ((float) (s11 - s10) * fracx); tmp = s0 + ((s1 - s0) * fracy); // Round if (tmp < ((float) Short.MIN_VALUE)) { s = Short.MIN_VALUE; } else if (tmp > ((float) Short.MAX_VALUE)) { s = Short.MAX_VALUE; } else if (tmp > 0 ) { s = (int) (tmp + 0.5F); } else { s = (int) (tmp - 0.5F); } // Write the result dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = (short)(s); } } else if (setBackground) { for (int k=0; k < dst_num_bands; k++) dstDataArrays[k][dstPixelOffset+dstBandOffsets[k]] = backgroundShort[k]; } // walk if (fracx < fracdx1) { s_ix += incx; fracx += fracdx; } else { s_ix += incx1; fracx -= fracdx1; } if (fracy < fracdy1) { s_iy += incy; fracy += fracdy; } else { s_iy += incy1; fracy -= fracdy1; } // Translate to/from SampleModel space & Raster space pylow = (s_iy - srcRectY) * srcScanlineStride; pxlow = (s_ix - srcRectX) * srcPixelStride; pyhigh = pylow + srcScanlineStride; pxhigh = pxlow + srcPixelStride; tmp00 = pxlow + pylow; tmp01 = pxhigh + pylow; tmp10 = pxlow + pyhigh; tmp11 = pxhigh + pyhigh; dstPixelOffset += dstPixelStride; } dstOffset += dstScanlineStride; } } private void ushortLoop(RasterAccessor src, Rectangle destRect, int srcRectX, int srcRectY, RasterAccessor dst) { float src_rect_x1 = src.getX(); float src_rect_y1 = src.getY(); float src_rect_x2 = src_rect_x1 + src.getWidth(); float src_rect_y2 = src_rect_y1 + src.getHeight(); float s_x, s_y; float fracx, fracy; int pxlow, pylow, pxhigh, pyhigh; int s, s00, s01, s10, s11; float s0, s1; float tmp; int dstPixelOffset; int dstOffset = 0; Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int bandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; short[] backgroundUShort = new short[dst_num_bands]; for (int i = 0; i < dst_num_bands; i++) backgroundUShort[i] = (short)backgroundValues[i]; for (int y = dst_min_y; y < dst_max_y; y++) { dstPixelOffset = dstOffset; // Backward map the first point in the line dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates s_x = (float)src_pt.getX(); s_y = (float)src_pt.getY(); // As per definition of bilinear interpolation s_x -= 0.5; s_y -= 0.5; // Floor to get the integral coordinate int s_ix = (int) Math.floor(s_x); int s_iy = (int) Math.floor(s_y); fracx = s_x - (float)s_ix; fracy = s_y - (float)s_iy; // Translate to/from SampleModel space & Raster space pylow = (s_iy - srcRectY) * srcScanlineStride; pxlow = (s_ix - srcRectX) * srcPixelStride; pyhigh = pylow + srcScanlineStride; pxhigh = pxlow + srcPixelStride; int tmp00 = pxlow + pylow; int tmp01 = pxhigh + pylow; int tmp10 = pxlow + pyhigh; int tmp11 = pxhigh + pyhigh; for (int x = dst_min_x; x < dst_max_x; x++) { // // Check against the source rectangle // if ((s_ix >= src_rect_x1) && (s_ix < (src_rect_x2 - 1)) && (s_iy >= src_rect_y1) && (s_iy < (src_rect_y2 - 1))) { for (int k2=0; k2 < dst_num_bands; k2++) { // // Get the 4 neighbourhood pixels // short tmp_row[]; int tmp_col; // Get to the right row tmp_row = srcDataArrays[k2]; // Position at the bandOffset tmp_col = bandOffsets[k2]; s00 = tmp_row[tmp00 + tmp_col] & 0xffff; s01 = tmp_row[tmp01 + tmp_col] & 0xffff; s10 = tmp_row[tmp10 + tmp_col] & 0xffff; s11 = tmp_row[tmp11 + tmp_col] & 0xffff; // Weighted Average of these 4 pixels s0 = (float) s00 + ((float) (s01 - s00) * fracx); s1 = (float) s10 + ((float) (s11 - s10) * fracx); tmp = s0 + ((s1 - s0) * fracy); // Round if (tmp < 0.0) { s = 0; } else if (tmp > (float)(USHORT_MAX)) { s = (int) (USHORT_MAX); } else { s = (int) (tmp + 0.5F); } // Write the result dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = (short)(s & 0xFFFF); } } else if (setBackground) { for (int k=0; k < dst_num_bands; k++) dstDataArrays[k][dstPixelOffset+dstBandOffsets[k]] = backgroundUShort[k]; } // walk if (fracx < fracdx1) { s_ix += incx; fracx += fracdx; } else { s_ix += incx1; fracx -= fracdx1; } if (fracy < fracdy1) { s_iy += incy; fracy += fracdy; } else { s_iy += incy1; fracy -= fracdy1; } // Translate to/from SampleModel space & Raster space pylow = (s_iy - srcRectY) * srcScanlineStride; pxlow = (s_ix - srcRectX) * srcPixelStride; pyhigh = pylow + srcScanlineStride; pxhigh = pxlow + srcPixelStride; tmp00 = pxlow + pylow; tmp01 = pxhigh + pylow; tmp10 = pxlow + pyhigh; tmp11 = pxhigh + pyhigh; dstPixelOffset += dstPixelStride; } dstOffset += dstScanlineStride; } } private void floatLoop(RasterAccessor src, Rectangle destRect, int srcRectX, int srcRectY, RasterAccessor dst) { float src_rect_x1 = src.getX(); float src_rect_y1 = src.getY(); float src_rect_x2 = src_rect_x1 + src.getWidth(); float src_rect_y2 = src_rect_y1 + src.getHeight(); float s_x, s_y; float fracx, fracy; int pxlow, pylow, pxhigh, pyhigh; float s, s00, s01, s10, s11; float s0, s1; int dstPixelOffset; int dstOffset = 0; Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); float dstDataArrays[][] = dst.getFloatDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); float srcDataArrays[][] = src.getFloatDataArrays(); int bandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; float[] backgroundFloat = new float[dst_num_bands]; for (int i = 0; i < dst_num_bands; i++) backgroundFloat[i] = (float)backgroundValues[i]; for (int y = dst_min_y; y < dst_max_y; y++) { dstPixelOffset = dstOffset; // Backward map the first point in the line dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates s_x = (float)src_pt.getX(); s_y = (float)src_pt.getY(); // As per definition of bilinear interpolation s_x -= 0.5; s_y -= 0.5; // Floor to get the integral coordinate int s_ix = (int) Math.floor(s_x); int s_iy = (int) Math.floor(s_y); fracx = s_x - (float)s_ix; fracy = s_y - (float)s_iy; // Translate to/from SampleModel space & Raster space pylow = (s_iy - srcRectY) * srcScanlineStride; pxlow = (s_ix - srcRectX) * srcPixelStride; pyhigh = pylow + srcScanlineStride; pxhigh = pxlow + srcPixelStride; int tmp00 = pxlow + pylow; int tmp01 = pxhigh + pylow; int tmp10 = pxlow + pyhigh; int tmp11 = pxhigh + pyhigh; for (int x = dst_min_x; x < dst_max_x; x++) { // // Check against the source rectangle // if ((s_ix >= src_rect_x1) && (s_ix < (src_rect_x2 - 1)) && (s_iy >= src_rect_y1) && (s_iy < (src_rect_y2 - 1))) { for (int k2=0; k2 < dst_num_bands; k2++) { // // Get the 4 neighbourhood pixels // float tmp_row[]; int tmp_col; // Get to the right row tmp_row = srcDataArrays[k2]; // Position at the bandOffset tmp_col = bandOffsets[k2]; s00 = tmp_row[tmp00 + tmp_col]; s01 = tmp_row[tmp01 + tmp_col]; s10 = tmp_row[tmp10 + tmp_col]; s11 = tmp_row[tmp11 + tmp_col]; // Weighted Average of these 4 pixels s0 = s00 + ((s01 - s00) * fracx); s1 = s10 + ((s11 - s10) * fracx); s = s0 + ((s1 - s0) * fracy); // Write the result dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = s; } } else if (setBackground) { for (int k=0; k < dst_num_bands; k++) dstDataArrays[k][dstPixelOffset+dstBandOffsets[k]] = backgroundFloat[k]; } // walk if (fracx < fracdx1) { s_ix += incx; fracx += fracdx; } else { s_ix += incx1; fracx -= fracdx1; } if (fracy < fracdy1) { s_iy += incy; fracy += fracdy; } else { s_iy += incy1; fracy -= fracdy1; } // Translate to/from SampleModel space & Raster space pylow = (s_iy - srcRectY) * srcScanlineStride; pxlow = (s_ix - srcRectX) * srcPixelStride; pyhigh = pylow + srcScanlineStride; pxhigh = pxlow + srcPixelStride; tmp00 = pxlow + pylow; tmp01 = pxhigh + pylow; tmp10 = pxlow + pyhigh; tmp11 = pxhigh + pyhigh; dstPixelOffset += dstPixelStride; } dstOffset += dstScanlineStride; } } private void doubleLoop(RasterAccessor src, Rectangle destRect, int srcRectX, int srcRectY, RasterAccessor dst) { float src_rect_x1 = src.getX(); float src_rect_y1 = src.getY(); float src_rect_x2 = src_rect_x1 + src.getWidth(); float src_rect_y2 = src_rect_y1 + src.getHeight(); float s_x, s_y; double fracx, fracy; int pxlow, pylow, pxhigh, pyhigh; double s, s00, s01, s10, s11; double s0, s1; int dstPixelOffset; int dstOffset = 0; Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); double dstDataArrays[][] = dst.getDoubleDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); double srcDataArrays[][] = src.getDoubleDataArrays(); int bandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; for (int y = dst_min_y; y < dst_max_y; y++) { dstPixelOffset = dstOffset; // Backward map the first point in the line dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates s_x = (float)src_pt.getX(); s_y = (float)src_pt.getY(); // As per definition of bilinear interpolation s_x -= 0.5; s_y -= 0.5; // Floor to get the integral coordinate int s_ix = (int) Math.floor(s_x); int s_iy = (int) Math.floor(s_y); fracx = s_x - (float)s_ix; fracy = s_y - (float)s_iy; // Translate to/from SampleModel space & Raster space pylow = (s_iy - srcRectY) * srcScanlineStride; pxlow = (s_ix - srcRectX) * srcPixelStride; pyhigh = pylow + srcScanlineStride; pxhigh = pxlow + srcPixelStride; int tmp00 = pxlow + pylow; int tmp01 = pxhigh + pylow; int tmp10 = pxlow + pyhigh; int tmp11 = pxhigh + pyhigh; for (int x = dst_min_x; x < dst_max_x; x++) { // // Check against the source rectangle // if ((s_ix >= src_rect_x1) && (s_ix < (src_rect_x2 - 1)) && (s_iy >= src_rect_y1) && (s_iy < (src_rect_y2 - 1))) { for (int k2=0; k2 < dst_num_bands; k2++) { // // Get the 4 neighbourhood pixels // double tmp_row[]; int tmp_col; // Get to the right row tmp_row = srcDataArrays[k2]; // Position at the bandOffset tmp_col = bandOffsets[k2]; s00 = tmp_row[tmp00 + tmp_col]; s01 = tmp_row[tmp01 + tmp_col]; s10 = tmp_row[tmp10 + tmp_col]; s11 = tmp_row[tmp11 + tmp_col]; // Weighted Average of these 4 pixels s0 = s00 + ((s01 - s00) * fracx); s1 = s10 + ((s11 - s10) * fracx); s = s0 + ((s1 - s0) * fracy); // Write the result dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = s; } } else if (setBackground) { for (int k=0; k < dst_num_bands; k++) dstDataArrays[k][dstPixelOffset+dstBandOffsets[k]] = backgroundValues[k]; } // walk if (fracx < fracdx1) { s_ix += incx; fracx += fracdx; } else { s_ix += incx1; fracx -= fracdx1; } if (fracy < fracdy1) { s_iy += incy; fracy += fracdy; } else { s_iy += incy1; fracy -= fracdy1; } // Translate to/from SampleModel space & Raster space pylow = (s_iy - srcRectY) * srcScanlineStride; pxlow = (s_ix - srcRectX) * srcPixelStride; pyhigh = pylow + srcScanlineStride; pxhigh = pxlow + srcPixelStride; tmp00 = pxlow + pylow; tmp01 = pxhigh + pylow; tmp10 = pxlow + pyhigh; tmp11 = pxhigh + pyhigh; dstPixelOffset += dstPixelStride; } dstOffset += dstScanlineStride; } } // public static OpImage createTestImage(OpImageTester oit) { // Interpolation interp = new InterpolationBilinear(); // AffineTransform tr = new AffineTransform(0.707107, // -0.707106, // 0.707106, // 0.707107, // 0.0, // 0.0); // return new AffineBilinearOpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource()), // tr, // interp); // } // // Calls a method on OpImage that uses introspection, to make this // // class, discover it's createTestImage() call, call it and then // // benchmark the performance of the created OpImage chain. // public static void main(String args[]) { // String classname = "com.sun.media.jai.opimage.AffineBilinearOpImage"; // OpImageTester.performDiagnostics(classname, args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MinFilterSeparableOpImage.java0000644000175000017500000005346310203035544031060 0ustar mathieumathieu/* * $RCSfile: MinFilterSeparableOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:35 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import java.util.Map; import javax.media.jai.operator.MinFilterDescriptor; import com.sun.media.jai.opimage.MinFilterOpImage; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class to perform min filtering on a source image. * */ final class MinFilterSeparableOpImage extends MinFilterOpImage { /** * Creates a MinFilterSeperableOpImage with the given source and * maskSize. The image dimensions are derived from the source * image. The tile grid layout, SampleModel, and ColorModel may * optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param maskSize the mask size. */ public MinFilterSeparableOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, int maskSize) { super(source, extender, config, layout, MinFilterDescriptor.MIN_MASK_SQUARE, maskSize); } protected void byteLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); byte dstDataArrays[][] = dst.getByteDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); byte srcDataArrays[][] = src.getByteDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int minValues[] = new int[filterSize]; int val, minval; int wp = filterSize; int tmpBuffer[] = new int[filterSize*dwidth]; int tmpBufferSize = filterSize*dwidth; for (int k = 0; k < dnumBands; k++) { byte dstData[] = dstDataArrays[k]; byte srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; int revolver = 0; for (int j = 0; j < filterSize-1; j++) { int srcPixelOffset = srcScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; minval = Integer.MAX_VALUE; for (int v = 0; v < wp; v++) { val = srcData[imageOffset] & 0xff; imageOffset += srcPixelStride; minval = (val < minval) ? val : minval; } tmpBuffer[revolver+i] = minval; srcPixelOffset += srcPixelStride; } revolver += dwidth; srcScanlineOffset += srcScanlineStride; } // srcScanlineStride already bumped by // filterSize-1*scanlineStride for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; minval = Integer.MAX_VALUE; for (int v = 0; v < wp; v++) { val = srcData[imageOffset] & 0xff; imageOffset += srcPixelStride; minval = (val < minval) ? val : minval; } tmpBuffer[revolver + i] = minval; minval = Integer.MAX_VALUE; for (int b = i; b < tmpBufferSize; b += dwidth) { val = tmpBuffer[b]; minval = (val < minval) ? val : minval; } dstData[dstPixelOffset] = (byte)minval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } revolver += dwidth; if (revolver == tmpBufferSize) { revolver = 0; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void shortLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int minValues[] = new int[filterSize]; int val, minval; int wp = filterSize; int tmpBuffer[] = new int[filterSize*dwidth]; int tmpBufferSize = filterSize*dwidth; for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; int revolver = 0; for (int j = 0; j < filterSize-1; j++) { int srcPixelOffset = srcScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; minval = Integer.MAX_VALUE; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += srcPixelStride; minval = (val < minval) ? val : minval; } tmpBuffer[revolver+i] = minval; srcPixelOffset += srcPixelStride; } revolver += dwidth; srcScanlineOffset += srcScanlineStride; } // srcScanlineStride already bumped by // filterSize-1*scanlineStride for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; minval = Integer.MAX_VALUE; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += srcPixelStride; minval = (val < minval) ? val : minval; } tmpBuffer[revolver + i] = minval; minval = Integer.MAX_VALUE; for (int b = i; b < tmpBufferSize; b += dwidth) { val = tmpBuffer[b]; minval = (val < minval) ? val : minval; } dstData[dstPixelOffset] = (short)minval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } revolver += dwidth; if (revolver == tmpBufferSize) { revolver = 0; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void ushortLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int minValues[] = new int[filterSize]; int val, minval; int wp = filterSize; int tmpBuffer[] = new int[filterSize*dwidth]; int tmpBufferSize = filterSize*dwidth; for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; int revolver = 0; for (int j = 0; j < filterSize-1; j++) { int srcPixelOffset = srcScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; minval = Integer.MAX_VALUE; for (int v = 0; v < wp; v++) { val = srcData[imageOffset] & 0xfff; imageOffset += srcPixelStride; minval = (val < minval) ? val : minval; } tmpBuffer[revolver+i] = minval; srcPixelOffset += srcPixelStride; } revolver += dwidth; srcScanlineOffset += srcScanlineStride; } // srcScanlineStride already bumped by // filterSize-1*scanlineStride for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; minval = Integer.MAX_VALUE; for (int v = 0; v < wp; v++) { val = srcData[imageOffset] & 0xffff; imageOffset += srcPixelStride; minval = (val < minval) ? val : minval; } tmpBuffer[revolver + i] = minval; minval = Integer.MAX_VALUE; for (int b = i; b < tmpBufferSize; b += dwidth) { val = tmpBuffer[b]; minval = (val < minval) ? val : minval; } dstData[dstPixelOffset] = (short)minval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } revolver += dwidth; if (revolver == tmpBufferSize) { revolver = 0; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void intLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); int dstDataArrays[][] = dst.getIntDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcDataArrays[][] = src.getIntDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int minValues[] = new int[filterSize]; int val, minval; int wp = filterSize; int tmpBuffer[] = new int[filterSize*dwidth]; int tmpBufferSize = filterSize*dwidth; for (int k = 0; k < dnumBands; k++) { int dstData[] = dstDataArrays[k]; int srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; int revolver = 0; for (int j = 0; j < filterSize-1; j++) { int srcPixelOffset = srcScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; minval = Integer.MAX_VALUE; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += srcPixelStride; minval = (val < minval) ? val : minval; } tmpBuffer[revolver+i] = minval; srcPixelOffset += srcPixelStride; } revolver += dwidth; srcScanlineOffset += srcScanlineStride; } // srcScanlineStride already bumped by // filterSize-1*scanlineStride for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; minval = Integer.MAX_VALUE; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += srcPixelStride; minval = (val < minval) ? val : minval; } tmpBuffer[revolver + i] = minval; minval = Integer.MAX_VALUE; for (int b = i; b < tmpBufferSize; b += dwidth) { val = tmpBuffer[b]; minval = (val < minval) ? val : minval; } dstData[dstPixelOffset] = minval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } revolver += dwidth; if (revolver == tmpBufferSize) { revolver = 0; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void floatLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); float dstDataArrays[][] = dst.getFloatDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); float srcDataArrays[][] = src.getFloatDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); float minValues[] = new float[filterSize]; float val, minval; int wp = filterSize; float tmpBuffer[] = new float[filterSize*dwidth]; int tmpBufferSize = filterSize*dwidth; for (int k = 0; k < dnumBands; k++) { float dstData[] = dstDataArrays[k]; float srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; int revolver = 0; for (int j = 0; j < filterSize-1; j++) { int srcPixelOffset = srcScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; minval = Float.MAX_VALUE; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += srcPixelStride; minval = (val < minval) ? val : minval; } tmpBuffer[revolver+i] = minval; srcPixelOffset += srcPixelStride; } revolver += dwidth; srcScanlineOffset += srcScanlineStride; } // srcScanlineStride already bumped by // filterSize-1*scanlineStride for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; minval = Float.MAX_VALUE; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += srcPixelStride; minval = (val < minval) ? val : minval; } tmpBuffer[revolver + i] = minval; minval = Float.MAX_VALUE; for (int b = i; b < tmpBufferSize; b += dwidth) { val = tmpBuffer[b]; minval = (val < minval) ? val : minval; } dstData[dstPixelOffset] = minval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } revolver += dwidth; if (revolver == tmpBufferSize) { revolver = 0; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void doubleLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); double dstDataArrays[][] = dst.getDoubleDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); double srcDataArrays[][] = src.getDoubleDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); double minValues[] = new double[filterSize]; double val, minval; int wp = filterSize; double tmpBuffer[] = new double[filterSize*dwidth]; int tmpBufferSize = filterSize*dwidth; for (int k = 0; k < dnumBands; k++) { double dstData[] = dstDataArrays[k]; double srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; int revolver = 0; for (int j = 0; j < filterSize-1; j++) { int srcPixelOffset = srcScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; minval = Double.MAX_VALUE; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += srcPixelStride; minval = (val < minval) ? val : minval; } tmpBuffer[revolver+i] = minval; srcPixelOffset += srcPixelStride; } revolver += dwidth; srcScanlineOffset += srcScanlineStride; } // srcScanlineStride already bumped by // filterSize-1*scanlineStride for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; minval = Double.MAX_VALUE; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += srcPixelStride; minval = (val < minval) ? val : minval; } tmpBuffer[revolver + i] = minval; minval = Double.MAX_VALUE; for (int b = i; b < tmpBufferSize; b += dwidth) { val = tmpBuffer[b]; minval = (val < minval) ? val : minval; } dstData[dstPixelOffset] = minval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } revolver += dwidth; if (revolver == tmpBufferSize) { revolver = 0; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } // public static OpImage createTestImage(OpImageTester oit) { // return // new MinFilterSeparableOpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource()), // 3); // } // public static void main(String args[]) { // String classname = // "com.sun.media.jai.opimage.MinFilterSeparableOpImage"; // OpImageTester.performDiagnostics(classname,args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/RotateCRIF.java0000644000175000017500000004207410340447404026010 0ustar mathieumathieu/* * $RCSfile: RotateCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-11-21 22:49:40 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.geom.NoninvertibleTransformException; import java.awt.image.DataBuffer; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.renderable.RenderableImage; import java.awt.image.renderable.RenderableImageOp; import java.awt.image.renderable.RenderContext; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.CRIFImpl; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationBicubic; import javax.media.jai.InterpolationBicubic2; import javax.media.jai.InterpolationBilinear; import javax.media.jai.InterpolationNearest; import javax.media.jai.InterpolationTable; import javax.media.jai.JAI; import javax.media.jai.OpImage; import javax.media.jai.PlanarImage; import javax.media.jai.RenderedOp; import com.sun.media.jai.opimage.PointMapperOpImage; import java.util.Map; import java.awt.geom.Rectangle2D; import java.awt.geom.Point2D; /** * @since EA4 * @see AffineOpimage */ public class RotateCRIF extends CRIFImpl { /** Constructor. */ public RotateCRIF() { super("rotate"); } /** * Creates an rotate operation. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); // Get BorderExtender from renderHints if any. BorderExtender extender = RIFUtil.getBorderExtenderHint(renderHints); RenderedImage source = paramBlock.getRenderedSource(0); float x_center = paramBlock.getFloatParameter(0); float y_center = paramBlock.getFloatParameter(1); float angle = paramBlock.getFloatParameter(2); Object arg1 = paramBlock.getObjectParameter(3); Interpolation interp = (Interpolation)arg1; double[] backgroundValues = (double[]) paramBlock.getObjectParameter(4); SampleModel sm = source.getSampleModel(); boolean isBinary = (sm instanceof MultiPixelPackedSampleModel) && (sm.getSampleSize(0) == 1) && (sm.getDataType() == DataBuffer.TYPE_BYTE || sm.getDataType() == DataBuffer.TYPE_USHORT || sm.getDataType() == DataBuffer.TYPE_INT); // // Convert angle to degrees (within some precision) given PI's // transcendental nature. All this, to check if we can call // simpler methods like Copy or Transpose for certain angles // viz., 0, 90, 180, 270, 360, 450, ..... // double tmp_angle = (180.0/Math.PI)*angle; double rnd_angle = Math.round(tmp_angle); // // Represent the angle as an AffineTransform // AffineTransform transform = AffineTransform.getRotateInstance(angle, x_center, y_center); // Check if angle is (nearly) integral if (Math.abs(rnd_angle - tmp_angle) < 0.0001) { int dangle = (int)rnd_angle % 360; // Shift dangle into the range [0..359]. if (dangle < 0) { dangle += 360; } // // Do a copy if angle is 0 degrees or // multiple of 360 degrees // if (dangle == 0) { return new CopyOpImage(source, renderHints, layout); } int ix_center = (int)Math.round(x_center); int iy_center = (int)Math.round(y_center); // Do a transpose if angle is mutiple of 270, 180, 90 degrees // and the translation is (nearly) integral. if (((dangle % 90) == 0) && (Math.abs(x_center - ix_center) < 0.0001) && (Math.abs(y_center - iy_center) < 0.0001)) { int transType = -1; int rotMinX = 0; int rotMinY = 0; int sourceMinX = source.getMinX(); int sourceMinY = source.getMinY(); int sourceMaxX = sourceMinX + source.getWidth(); int sourceMaxY = sourceMinY + source.getHeight(); if (dangle == 90) { transType = 4; rotMinX = ix_center - (sourceMaxY - iy_center); rotMinY = iy_center - (ix_center - sourceMinX); } else if (dangle == 180) { transType = 5; rotMinX = 2*ix_center - sourceMaxX; rotMinY = 2*iy_center - sourceMaxY; } else { // dangle == 270 transType = 6; rotMinX = ix_center - (iy_center - sourceMinY); rotMinY = iy_center - (sourceMaxX - ix_center); } RenderedImage trans; if (isBinary) { trans = new TransposeBinaryOpImage(source, renderHints, layout, transType); } else { trans = new TransposeOpImage(source, renderHints, layout, transType); } // Determine current image origin int imMinX = trans.getMinX(); int imMinY = trans.getMinY(); // TranslateIntOpImage can't deal with ImageLayout hint if (layout == null) { // Translate image and return it OpImage intermediateImage = new TranslateIntOpImage(trans, renderHints, rotMinX - imMinX, rotMinY - imMinY); try { return new PointMapperOpImage(intermediateImage, renderHints, transform); } catch(NoninvertibleTransformException nite) { return intermediateImage; } } else { ParameterBlock pbScale = new ParameterBlock(); pbScale.addSource(trans); pbScale.add(0F); pbScale.add(0F); pbScale.add(rotMinX - imMinX); pbScale.add(rotMinY - imMinY); pbScale.add(interp); PlanarImage intermediateImage = JAI.create("scale", pbScale, renderHints).getRendering(); try { return new PointMapperOpImage(intermediateImage, renderHints, transform); } catch(NoninvertibleTransformException nite) { return intermediateImage; } } } } // // At this point we know that we cannot call other operations. // Have to do Affine. // // // Do the Affine operation // if (interp instanceof InterpolationNearest) { if (isBinary) { return new AffineNearestBinaryOpImage(source, extender, renderHints, layout, transform, interp, backgroundValues); } else { return new AffineNearestOpImage(source, extender, renderHints, layout, transform, interp, backgroundValues); } } else if (interp instanceof InterpolationBilinear) { return new AffineBilinearOpImage(source, extender, renderHints, layout, transform, interp, backgroundValues); } else if (interp instanceof InterpolationBicubic) { return new AffineBicubicOpImage(source, extender, renderHints, layout, transform, interp, backgroundValues); } else if (interp instanceof InterpolationBicubic2) { return new AffineBicubic2OpImage(source, extender, renderHints, layout, transform, interp, backgroundValues); } else { return new AffineGeneralOpImage(source, extender, renderHints, layout, transform, interp, backgroundValues); } } /** * Creates a new instance of AffineOpImage * in the renderable layer. This method satisfies the * implementation of CRIF. */ public RenderedImage create(RenderContext renderContext, ParameterBlock paramBlock) { return paramBlock.getRenderedSource(0); } /** * Maps the output RenderContext into the RenderContext for the ith * source. * This method satisfies the implementation of CRIF. * * @param i The index of the source image. * @param renderContext The renderContext being applied to the operation. * @param paramBlock The ParameterBlock containing the sources * and the translation factors. * @param image The RenderableImageOp from which this method * was called. */ public RenderContext mapRenderContext(int i, RenderContext renderContext, ParameterBlock paramBlock, RenderableImage image) { float x_center = paramBlock.getFloatParameter(0); float y_center = paramBlock.getFloatParameter(1); float angle = paramBlock.getFloatParameter(2); AffineTransform rotate = AffineTransform.getRotateInstance(angle, x_center, y_center); RenderContext RC = (RenderContext)renderContext.clone(); AffineTransform usr2dev = RC.getTransform(); usr2dev.concatenate(rotate); RC.setTransform(usr2dev); return RC; } /** * Gets the bounding box for the output of TranslateOpImage. * This method satisfies the implementation of CRIF. */ public Rectangle2D getBounds2D(ParameterBlock paramBlock) { RenderableImage source = paramBlock.getRenderableSource(0); float x_center = paramBlock.getFloatParameter(0); float y_center = paramBlock.getFloatParameter(1); float angle = paramBlock.getFloatParameter(2); Interpolation interp = (Interpolation)paramBlock.getObjectParameter(3); // // Convert angle to degrees (within some precision) given PI's // transcendantal nature. All this, to check if we can call // simpler methods like Copy or Transpose for certain angles // viz., 0, 90, 180, 270, 360, 450, ..... // int dangle = 0; double tmp_angle = 180.0F * angle / Math.PI; double rnd_angle = Math.round(tmp_angle); if (Math.abs(rnd_angle - tmp_angle) < 0.0001) { dangle = (int) rnd_angle; } else { dangle = (int) tmp_angle; } // // It's a copy if angle is 0 degrees or multiple of 360 degrees // if (dangle % 360 == 0) { return new Rectangle2D.Float(source.getMinX(), source.getMinY(), source.getWidth(), source.getHeight()); } // // It's a transpose if angle is mutiple of 270, 180, 90 degrees // float x0 = (float)source.getMinX(); float y0 = (float)source.getMinY(); float s_width = (float)source.getWidth(); float s_height = (float)source.getHeight(); float x1 = x0 + s_width - 1; float y1 = y0 + s_height - 1; float tx0 = 0; float ty0 = 0; float tx1 = 0; float ty1 = 0; if (dangle % 270 == 0) { if (dangle < 0) { // -270 degrees tx0 = s_height - y1 - 1; ty0 = x0; tx1 = s_height - y0 - 1; ty1 = x1; return new Rectangle2D.Float(tx0, ty0, tx1 - tx0 + 1, ty1 - ty0 + 1); } else { // 270 degrees tx0 = y0; ty0 = s_width - x1 - 1; tx1 = y1; ty1 = s_width - x0 - 1; return new Rectangle2D.Float(tx0, ty0, tx1 - tx0 + 1, ty1 - ty0 + 1); } } if (dangle % 180 == 0) { tx0 = s_width - x1 - 1; ty0 = s_height - y1 - 1; tx1 = s_width - x0 - 1; ty1 = s_height - y0 - 1; // 180 degrees return new Rectangle2D.Float(tx0, ty0, tx1 - tx0 + 1, ty1 - ty0 + 1); } if (dangle % 90 == 0) { if (dangle < 0) { // -90 degrees tx0 = y0; ty0 = s_width - x1 - 1; tx1 = y1; ty1 = s_width - x0 - 1; return new Rectangle2D.Float(tx0, ty0, tx1 - tx0 + 1, ty1 - ty0 + 1); } else { // 90 degrees tx0 = s_height - y1 - 1; ty0 = x0; tx1 = s_height - y0 - 1; ty1 = x1; return new Rectangle2D.Float(tx0, ty0, tx1 - tx0 + 1, ty1 - ty0 + 1); } } // // It's a Affine // AffineTransform rotate = AffineTransform.getRotateInstance(angle, x_center, y_center); // // Get sx0,sy0 coordinates and width & height of the source // float sx0 = (float) source.getMinX(); float sy0 = (float) source.getMinY(); float sw = (float) source.getWidth(); float sh = (float) source.getHeight(); // // The 4 points (clockwise order) are // (sx0, sy0), (sx0+sw, sy0) // (sx0, sy0+sh), (sx0+sw, sy0+sh) // Point2D[] pts = new Point2D[4]; pts[0] = new Point2D.Float(sx0, sy0); pts[1] = new Point2D.Float((sx0+sw), sy0); pts[2] = new Point2D.Float((sx0+sw), (sy0+sh)); pts[3] = new Point2D.Float(sx0, (sy0+sh)); // Forward map rotate.transform(pts, 0, pts, 0, 4); float dx0 = Float.MAX_VALUE; float dy0 = Float.MAX_VALUE; float dx1 = -Float.MAX_VALUE; float dy1 = -Float.MAX_VALUE; for (int i = 0; i < 4; i++) { float px = (float)pts[i].getX(); float py = (float)pts[i].getY(); dx0 = Math.min(dx0, px); dy0 = Math.min(dy0, py); dx1 = Math.max(dx1, px); dy1 = Math.max(dy1, py); } // // Get the width & height of the resulting bounding box. // This is set on the layout // float lw = dx1 - dx0; float lh = dy1 - dy0; return new Rectangle2D.Float(dx0, dy0, lw, lh); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/LogCRIF.java0000644000175000017500000000316410203035544025264 0ustar mathieumathieu/* * $RCSfile: LogCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:30 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D.Float; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import java.awt.image.renderable.RenderContext; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * This image factory supports image operator LogOpImage * in the rendered and renderable image layers. * * @since EA2 * @see javax.media.jai.operator.LogDescriptor * @see LogOpImage * */ public class LogCRIF extends CRIFImpl { /** Constructor. */ public LogCRIF() { super("log"); } /** * Creates a new instance of LogOpImage in the * rendered layer. This method satisfies the implementation of RIF. * * @param paramBlock The source image and the constants. * @param renderHints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock pb, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new LogOpImage(pb.getRenderedSource(0), renderHints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/DivideComplexCRIF.java0000644000175000017500000000355310203035544027301 0ustar mathieumathieu/* * $RCSfile: DivideComplexCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:23 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.geom.Rectangle2D; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderContext; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "DivideComplex" operation in the * rendered and renderable image layers. * * @since EA4 * @see javax.media.jai.operator.DivideComplexDescriptor * @see ComplexArithmeticOpImage * */ public class DivideComplexCRIF extends CRIFImpl { /** Constructor. */ public DivideComplexCRIF() { super("dividecomplex"); } /** * Creates a new instance of ComplexArithmeticOpImage in the * rendered layer. This method satisifies the implementation of RIF. * * @param paramBlock The two source images to be multiplied. * @param renderHints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new ComplexArithmeticOpImage(paramBlock.getRenderedSource(0), paramBlock.getRenderedSource(1), renderHints, layout, true); // true implies divide } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/AddConstToCollectionCIF.java0000644000175000017500000001112610203035544030434 0ustar mathieumathieu/* * $RCSfile: AddConstToCollectionCIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:12 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.renderable.ParameterBlock; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import javax.media.jai.CollectionImage; import javax.media.jai.CollectionImageFactory; import javax.media.jai.CollectionOp; import javax.media.jai.ImageLayout; import javax.media.jai.PlanarImage; import javax.media.jai.RenderedOp; /** * A CIF supporting the "AddConstToCollection" operation. * * @see javax.media.jai.operator.AddConstToCollectionDescriptor * @see AddConstToCollectionOpImage * * * @since EA4 */ public class AddConstToCollectionCIF implements CollectionImageFactory { /** Constructor. */ public AddConstToCollectionCIF() {} /** * Creates a new instance of AddConstToCollectionOpImage. * * @param args Input source collection and constants * @param hints Optionally contains destination image layout. */ public CollectionImage create(ParameterBlock args, RenderingHints hints) { return new AddConstToCollectionOpImage( (Collection)args.getSource(0), hints, (double[])args.getObjectParameter(0)); } /** * Updates an instance of AddConstToCollectionOpImage. */ public CollectionImage update(ParameterBlock oldParamBlock, RenderingHints oldHints, ParameterBlock newParamBlock, RenderingHints newHints, CollectionImage oldRendering, CollectionOp op) { CollectionImage updatedCollection = null; if(oldParamBlock.getObjectParameter(0).equals(newParamBlock.getObjectParameter(0)) && (oldHints == null ? newHints == null : oldHints.equals(newHints))) { // Retrieve the old and new sources and the parameters. Collection oldSource = (Collection)oldParamBlock.getSource(0); Collection newSource = (Collection)newParamBlock.getSource(0); double[] constants = (double[])oldParamBlock.getObjectParameter(0); // Construct a Collection of common sources. Collection commonSources = new ArrayList(); Iterator it = oldSource.iterator(); while(it.hasNext()) { Object oldElement = it.next(); if(newSource.contains(oldElement)) { commonSources.add(oldElement); } } if(commonSources.size() != 0) { // Construct a Collection of the RenderedOp nodes that // will be retained in the new CollectionImage. ArrayList commonNodes = new ArrayList(commonSources.size()); it = oldRendering.iterator(); while(it.hasNext()) { RenderedOp node = (RenderedOp)it.next(); PlanarImage source = (PlanarImage)node.getSourceImage(0); if(commonSources.contains(source)) { commonNodes.add(node); } } // Create a new CollectionImage. updatedCollection = new AddConstToCollectionOpImage(newSource, newHints, constants); // Remove from the new CollectionImage all nodes that // are common with the old CollectionImage. ArrayList newNodes = new ArrayList(oldRendering.size() - commonSources.size()); it = updatedCollection.iterator(); while(it.hasNext()) { RenderedOp node = (RenderedOp)it.next(); PlanarImage source = (PlanarImage)node.getSourceImage(0); if(commonSources.contains(source)) { it.remove(); } } // Add all the common nodes to the new CollectionImage. it = commonNodes.iterator(); while(it.hasNext()) { updatedCollection.add(it.next()); } } } return updatedCollection; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/DilateOpImage.java0000644000175000017500000005211010203035544026536 0ustar mathieumathieu/* * $RCSfile: DilateOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:23 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; // import com.sun.media.jai.test.OpImageTester; /** * * An OpImage class to perform dilation on a source image. * * Dilation for grey scale images can be charaterized by "slide, add and max", * while for binary images by "slide and set". As always, the kernel * is expected to come with a key position. * *

Grey scale dilation is a spatial operation that computes * each output sample by adding elements of a kernel to the samples * surrounding a particular source sample and taking the maximum. * A mathematical expression is: * *

For a kernel K with a key position (xKey,yKey), the dilation * of image I at (x,y) is given by: *

 *     max{ I(x-i, y-j) + K(xKey+i, yKey+j): some (i,j) restriction }
 *  
 *      where the (i,j) restriction means:
 *      all possible (i,j) so that both I(x-i,y-j) and K(xKey+i, yKey+j)
 *      are defined, that is, these indecies are in bounds.
 *
 * 
*

Intuitively in 2D, the kernel is like * an unbrella and the key point is the handle. When the handle moves * all over the image surface, the upper outbounds of all the umbrella * positions is the dilation. Thus if you want the image to dilate in * the upper right direction, the following kernel would do with * the bold face key position. * *

* * * * *
0050
0500
000
* *

Note also that zero kernel have effects on the dilation! * That is because of the "max" in the add and max process. Thus * a 3 x 1 zero kernel with the key persion at the bottom of the kernel * dilates the image upwards. * *

* After the kernel is rotated 180 degrees, Pseudo code for dilation operation * is as follows. Of course, you should provide the kernel in its * (unrotated) original form. Assuming the kernel K is of size M rows x N cols * and the key position is (xKey, yKey). * * // dilation * for every dst pixel location (x,y){ * dst[x][y] = -infinity; * for (i = -xKey; i < M - xKey; i++){ * for (j = -yKey; j < N - yKey; j++){ * if((x+i, y+j) are in bounds of src && * (xKey+i, yKey+j) are in bounds of K){ * tmp = src[x + i][y + j]+ K[xKey + i][yKey + j]; * dst[x][y] = max{tmp, dst[x][y]}; * } * } * } * } * * *

Dilation, unlike convolution and most neighborhood operations, * actually can grow the image region. But to conform with other * image neighborhood operations, the border pixels are set to 0. * For a 3 x 3 kernel with the key point at the center, there will * be a pixel wide 0 stripe around the border. * *

The kernel cannot be bigger in any dimension than the image data. * *

Binary Image Dilation * requires the kernel K to be binary. * Intuitively, starting from dst image being a duplicate of src, * binary dilation slides the kernel K to place the key position * at every non-zero point (x,y) in src image and set dst positions * under ones of K to 1. * *

After the kernel is rotated 180 degrees, the pseudo code for * dilation operation is as follows. (Of course, you should provide * the kernel in its original unrotated form.) * *

 * 
 * // dilating
 * for every dst pixel location (x,y){
 *    dst[x][y] = src[x][y];
 *    for (i = -xKey; i < M - xKey; i++){
 *       for (j = -yKey; j < N - yKey; j++){
 *         if(src[x+i,y+i]==1 && Key(xKey+i, yKey+j)==1){
 *            dst[x][y] = 1; break;
 *          }
 *       }
 *    }
 * }
 * 
*

Reference: An Introduction to Nonlinear Image Processing, * by Edward R. Bougherty and Jaakko Astola, * Spie Optical Engineering Press, 1994. * * * @see KernelJAI */ final class DilateOpImage extends AreaOpImage { /** * The kernel with which to do the dilate operation. */ protected KernelJAI kernel; /** Kernel variables. */ private int kw, kh, kx, ky; private float[] kdata; /** * Creates a DilateOpImage given a ParameterBlock containing the image * source and pre-rotated dilation kernel. The image dimensions are * derived * from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout * object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param kernel the pre-rotated dilation KernelJAI. */ public DilateOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, KernelJAI kernel) { super(source, layout, config, true, extender, kernel.getLeftPadding(), kernel.getRightPadding(), kernel.getTopPadding(), kernel.getBottomPadding()); this.kernel = kernel; kw = kernel.getWidth(); kh = kernel.getHeight(); kx = kernel.getXOrigin(); ky = kernel.getYOrigin(); kdata = kernel.getKernelData(); } /** * Performs dilation on a specified rectangle. The sources are * cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); Raster source = sources[0]; Rectangle srcRect = mapDestRect(destRect, 0); RasterAccessor srcAccessor = new RasterAccessor(source, srcRect, formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor dstAccessor = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(srcAccessor, dstAccessor); break; case DataBuffer.TYPE_INT: intLoop(srcAccessor, dstAccessor); break; case DataBuffer.TYPE_SHORT: shortLoop(srcAccessor, dstAccessor); break; case DataBuffer.TYPE_USHORT: ushortLoop(srcAccessor, dstAccessor); break; case DataBuffer.TYPE_FLOAT: floatLoop(srcAccessor, dstAccessor); break; case DataBuffer.TYPE_DOUBLE: doubleLoop(srcAccessor, dstAccessor); break; default: } // If the RasterAccessor object set up a temporary buffer for the // op to write to, tell the RasterAccessor to write that data // to the raster no that we're done with it. if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } private void byteLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); byte dstDataArrays[][] = dst.getByteDataArrays(); byte srcDataArrays[][] = src.getByteDataArrays(); for (int k = 0; k < dnumBands; k++) { byte dstData[] = dstDataArrays[k]; byte srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int kernelVerticalOffset = 0; int imageVerticalOffset = srcPixelOffset; float f = Float.NEGATIVE_INFINITY; for (int u = 0; u < kh; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < kw; v++) { float tmpIK = ((int)srcData[imageOffset]&0xff) + kdata[kernelVerticalOffset + v]; if(tmpIK > f){ f = tmpIK; } imageOffset += srcPixelStride; } kernelVerticalOffset += kw; imageVerticalOffset += srcScanlineStride; } int val = (int)f; if (val < 0) { val = 0; } else if (val > 255) { val = 255; } dstData[dstPixelOffset] = (byte)val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } private void shortLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); short dstDataArrays[][] = dst.getShortDataArrays(); short srcDataArrays[][] = src.getShortDataArrays(); for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int kernelVerticalOffset = 0; int imageVerticalOffset = srcPixelOffset; float f = Float.NEGATIVE_INFINITY; for (int u = 0; u < kh; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < kw; v++) { float tmpIK = srcData[imageOffset] + kdata[kernelVerticalOffset + v]; if(tmpIK > f){ f = tmpIK; } imageOffset += srcPixelStride; } kernelVerticalOffset += kw; imageVerticalOffset += srcScanlineStride; } int val = (int)f; if (val < Short.MIN_VALUE) { val = Short.MIN_VALUE; } else if (val > Short.MAX_VALUE) { val = Short.MAX_VALUE; } dstData[dstPixelOffset] = (short)val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } private void ushortLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); short dstDataArrays[][] = dst.getShortDataArrays(); short srcDataArrays[][] = src.getShortDataArrays(); for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int kernelVerticalOffset = 0; int imageVerticalOffset = srcPixelOffset; float f = Float.NEGATIVE_INFINITY; for (int u = 0; u < kh; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < kw; v++) { float tmpIK = (srcData[imageOffset] & 0xffff) + kdata[kernelVerticalOffset + v]; if(tmpIK > f){ f = tmpIK; } imageOffset += srcPixelStride; } kernelVerticalOffset += kw; imageVerticalOffset += srcScanlineStride; } int val = (int)f; if (val < 0) { val = 0; } else if (val > 0xffff) { val = 0xffff; } dstData[dstPixelOffset] = (short)val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } private void intLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dstDataArrays[][] = dst.getIntDataArrays(); int srcDataArrays[][] = src.getIntDataArrays(); for (int k = 0; k < dnumBands; k++) { int dstData[] = dstDataArrays[k]; int srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int kernelVerticalOffset = 0; int imageVerticalOffset = srcPixelOffset; float f = Float.NEGATIVE_INFINITY; for (int u = 0; u < kh; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < kw; v++) { float tmpIK = (int)srcData[imageOffset] + kdata[kernelVerticalOffset + v]; if(tmpIK > f){ f = tmpIK; } imageOffset += srcPixelStride; } kernelVerticalOffset += kw; imageVerticalOffset += srcScanlineStride; } dstData[dstPixelOffset] = (int)f; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } private void floatLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); float dstDataArrays[][] = dst.getFloatDataArrays(); float srcDataArrays[][] = src.getFloatDataArrays(); for (int k = 0; k < dnumBands; k++) { float dstData[] = dstDataArrays[k]; float srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int kernelVerticalOffset = 0; int imageVerticalOffset = srcPixelOffset; float f = Float.NEGATIVE_INFINITY; for (int u = 0; u < kh; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < kw; v++) { float tmpIK = srcData[imageOffset] + kdata[kernelVerticalOffset + v]; if(tmpIK > f){ f = tmpIK; } imageOffset += srcPixelStride; } kernelVerticalOffset += kw; imageVerticalOffset += srcScanlineStride; } dstData[dstPixelOffset] = f; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } private void doubleLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); double dstDataArrays[][] = dst.getDoubleDataArrays(); double srcDataArrays[][] = src.getDoubleDataArrays(); for (int k = 0; k < dnumBands; k++) { double dstData[] = dstDataArrays[k]; double srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int kernelVerticalOffset = 0; int imageVerticalOffset = srcPixelOffset; double f = Double.NEGATIVE_INFINITY; for (int u = 0; u < kh; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < kw; v++) { double tmpIK = srcData[imageOffset] + kdata[kernelVerticalOffset + v]; if(tmpIK > f){ f = tmpIK; } imageOffset += srcPixelStride; } kernelVerticalOffset += kw; imageVerticalOffset += srcScanlineStride; } dstData[dstPixelOffset] = f; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/AffineOpImage.java0000644000175000017500000004733310203035544026537 0ustar mathieumathieu/* * $RCSfile: AffineOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:14 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Point; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.DataBuffer; import javax.media.jai.BorderExtender; import javax.media.jai.GeometricOpImage; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationNearest; import javax.media.jai.InterpolationBilinear; import javax.media.jai.InterpolationBicubic; import javax.media.jai.InterpolationBicubic2; import javax.media.jai.PlanarImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFactory; import javax.media.jai.util.ImagingException; import javax.media.jai.util.ImagingListener; import javax.media.jai.util.Range; import java.util.Map; import com.sun.media.jai.util.ImageUtil; /** * An OpImage class to perform (possibly filtered) affine mapping between * a source and destination image. * * The geometric relationship between source and destination pixels * is defined as the following (x and y denote * the source pixel coordinates; x' and y' * denote the destination pixel coordinates; m denotes the * 3x2 transform matrix): *

    * * x' = m[0][0] * x + m[0][1] * y + m[0][2] *
    * y' = m[1][0] * x + m[1][1] * y + m[1][2] *
    *
* */ class AffineOpImage extends GeometricOpImage { /** * Unsigned short Max Value */ protected static final int USHORT_MAX = Short.MAX_VALUE - Short.MIN_VALUE; /** * The forward AffineTransform describing the image transformation. */ protected AffineTransform f_transform; /** * The inverse AffineTransform describing the image transformation. */ protected AffineTransform i_transform; /** The Interpolation object. */ protected Interpolation interp; /** Store source & padded rectangle info */ private Rectangle srcimg, padimg; /** The BorderExtender */ protected BorderExtender extender; /** The true writable area */ private Rectangle theDest; /** Cache the ImagingListener. */ private ImagingListener listener; /** * Scanline walking : variables & constants */ /** The fixed-point denominator of the fractional offsets. */ protected static final int geom_frac_max = 0x100000; double m00, m10, flr_m00, flr_m10; double fracdx, fracdx1, fracdy, fracdy1; int incx, incx1, incy, incy1; int ifracdx, ifracdx1, ifracdy, ifracdy1; /** * Padding values for interpolation */ public int lpad, rpad, tpad, bpad; /** * Computes floor(num/denom) using integer arithmetic. * denom must not be equal to 0. */ protected static int floorRatio(long num, long denom) { if (denom < 0) { denom = -denom; num = -num; } if (num >= 0) { return (int)(num/denom); } else { return (int)((num - denom + 1)/denom); } } /** * Computes ceil(num/denom) using integer arithmetic. * denom must not be equal to 0. */ protected static int ceilRatio(long num, long denom) { if (denom < 0) { denom = -denom; num = -num; } if (num >= 0) { return (int)((num + denom - 1)/denom); } else { return (int)(num/denom); } } private static ImageLayout layoutHelper(ImageLayout layout, RenderedImage source, AffineTransform forward_tr) { ImageLayout newLayout; if (layout != null) { newLayout = (ImageLayout)layout.clone(); } else { newLayout = new ImageLayout(); } // // Get sx0,sy0 coordinates and width & height of the source // float sx0 = (float) source.getMinX(); float sy0 = (float) source.getMinY(); float sw = (float) source.getWidth(); float sh = (float) source.getHeight(); // // The 4 points (clockwise order) are // (sx0, sy0), (sx0+sw, sy0) // (sx0, sy0+sh), (sx0+sw, sy0+sh) // Point2D[] pts = new Point2D[4]; pts[0] = new Point2D.Float(sx0, sy0); pts[1] = new Point2D.Float((sx0+sw), sy0); pts[2] = new Point2D.Float((sx0+sw), (sy0+sh)); pts[3] = new Point2D.Float(sx0, (sy0+sh)); // Forward map forward_tr.transform(pts, 0, pts, 0, 4); float dx0 = Float.MAX_VALUE; float dy0 = Float.MAX_VALUE; float dx1 = -Float.MAX_VALUE; float dy1 = -Float.MAX_VALUE; for (int i = 0; i < 4; i++) { float px = (float)pts[i].getX(); float py = (float)pts[i].getY(); dx0 = Math.min(dx0, px); dy0 = Math.min(dy0, py); dx1 = Math.max(dx1, px); dy1 = Math.max(dy1, py); } // // Get the width & height of the resulting bounding box. // This is set on the layout // int lw = (int)(dx1 - dx0); int lh = (int)(dy1 - dy0); // // Set the starting integral coordinate // with the following criterion. // If it's greater than 0.5, set it to the next integral value (ceil) // else set it to the integral value (floor). // int lx0, ly0; int i_dx0 = (int)Math.floor(dx0); if (Math.abs(dx0 - i_dx0) <= 0.5) { lx0 = i_dx0; } else { lx0 = (int) Math.ceil(dx0); } int i_dy0 = (int)Math.floor(dy0); if (Math.abs(dy0 - i_dy0) <= 0.5) { ly0 = i_dy0; } else { ly0 = (int) Math.ceil(dy0); } // // Create the layout // newLayout.setMinX(lx0); newLayout.setMinY(ly0); newLayout.setWidth(lw); newLayout.setHeight(lh); return newLayout; } /** * Constructs an AffineOpImage from a RenderedImage source, * AffineTransform, and Interpolation object. The image * dimensions are determined by forward-mapping the source bounds. * The tile grid layout, SampleModel, and ColorModel are specified * by the image source, possibly overridden by values from the * ImageLayout parameter. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param transform the desired AffineTransform. * @param interp an Interpolation object. */ public AffineOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, AffineTransform transform, Interpolation interp, double[] backgroundValues) { super(vectorize(source), layoutHelper(layout, source, transform), config, true, extender, interp, backgroundValues); listener = ImageUtil.getImagingListener((java.awt.RenderingHints)config); // store the interp and extender objects this.interp = interp; // the extender this.extender = extender; // Store the padding values lpad = interp.getLeftPadding(); rpad = interp.getRightPadding(); tpad = interp.getTopPadding(); bpad = interp.getBottomPadding(); // // Store source bounds rectangle // and the padded rectangle (for extension cases) // srcimg = new Rectangle(getSourceImage(0).getMinX(), getSourceImage(0).getMinY(), getSourceImage(0).getWidth(), getSourceImage(0).getHeight()); padimg = new Rectangle(srcimg.x - lpad, srcimg.y - tpad, srcimg.width + lpad + rpad, srcimg.height + tpad + bpad); if (extender == null) { // // Source has to be shrunk as per interpolation // as a result the destination produced could // be different from the layout // // // Get sx0,sy0 coordinates and width & height of the source // float sx0 = (float) srcimg.x; float sy0 = (float) srcimg.y; float sw = (float) srcimg.width; float sh = (float) srcimg.height; // // get padding amounts as per interpolation // float f_lpad = (float)lpad; float f_rpad = (float)rpad; float f_tpad = (float)tpad; float f_bpad = (float)bpad; // // As per pixel defined to be at (0.5, 0.5) // if (!(interp instanceof InterpolationNearest)) { f_lpad += 0.5; f_tpad += 0.5; f_rpad += 0.5; f_bpad += 0.5; } // // Shrink the source by padding amount prior to forward map // This is the maxmimum available source than can be mapped // sx0 += f_lpad; sy0 += f_tpad; sw -= (f_lpad + f_rpad); sh -= (f_tpad + f_bpad); // // The 4 points are (x0, y0), (x0+w, y0) // (x0+w, y0+h), (x0, y0+h) // Point2D[] pts = new Point2D[4]; pts[0] = new Point2D.Float(sx0, sy0); pts[1] = new Point2D.Float((sx0 + sw), sy0); pts[2] = new Point2D.Float((sx0 + sw), (sy0 + sh)); pts[3] = new Point2D.Float(sx0, (sy0 + sh)); // Forward map transform.transform(pts, 0, pts, 0, 4); float dx0 = Float.MAX_VALUE; float dy0 = Float.MAX_VALUE; float dx1 = -Float.MAX_VALUE; float dy1 = -Float.MAX_VALUE; for (int i = 0; i < 4; i++) { float px = (float)pts[i].getX(); float py = (float)pts[i].getY(); dx0 = Math.min(dx0, px); dy0 = Math.min(dy0, py); dx1 = Math.max(dx1, px); dy1 = Math.max(dy1, py); } // // The layout is the wholly contained integer area of the // corresponding floating point bounding box. // We cannot round the corners of the floating rect because it // would increase the size of the rect, so we need to ceil the // upper corner and floor the lower corner. // int lx0 = (int)Math.ceil(dx0); int ly0 = (int)Math.ceil(dy0); int lx1 = (int)Math.floor(dx1); int ly1 = (int)Math.floor(dy1); theDest = new Rectangle(lx0, ly0, lx1 - lx0, ly1 - ly0); } else { theDest = getBounds(); } // Store the inverse and forward transforms. try { this.i_transform = transform.createInverse(); } catch (Exception e) { String message = JaiI18N.getString("AffineOpImage0"); listener.errorOccurred(message, new ImagingException(message, e), this, false); // throw new RuntimeException(JaiI18N.getString("AffineOpImage0")); } this.f_transform = (AffineTransform)transform.clone(); // // Store the incremental values used in scanline walking. // m00 = i_transform.getScaleX(); // get m00 flr_m00 = Math.floor(m00); fracdx = m00 - flr_m00; fracdx1 = 1.0F - fracdx; incx = (int) flr_m00; // Movement incx1 = incx + 1; // along x ifracdx = (int) Math.round(fracdx * geom_frac_max); ifracdx1 = geom_frac_max - ifracdx; m10 = i_transform.getShearY(); // get m10 flr_m10 = Math.floor(m10); fracdy = m10 - flr_m10; fracdy1 = 1.0F - fracdy; incy = (int) flr_m10; // Movement incy1 = incy + 1; // along y ifracdy = (int) Math.round(fracdy * geom_frac_max); ifracdy1 = geom_frac_max - ifracdy; } /** * Computes the source point corresponding to the supplied point. * * @param destPt the position in destination image coordinates * to map to source image coordinates. * * @return a Point2D of the same class as * destPt. * * @throws IllegalArgumentException if destPt is * null. * * @since JAI 1.1.2 */ public Point2D mapDestPoint(Point2D destPt) { if (destPt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } Point2D dpt = (Point2D)destPt.clone(); dpt.setLocation(dpt.getX() + 0.5, dpt.getY() + 0.5); Point2D spt = i_transform.transform(dpt, null); spt.setLocation(spt.getX() - 0.5, spt.getY() - 0.5); return spt; } /** * Computes the destination point corresponding to the supplied point. * * @param sourcePt the position in source image coordinates * to map to destination image coordinates. * * @return a Point2D of the same class as * sourcePt. * * @throws IllegalArgumentException if destPt is * null. * * @since JAI 1.1.2 */ public Point2D mapSourcePoint(Point2D sourcePt) { if (sourcePt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } Point2D spt = (Point2D)sourcePt.clone(); spt.setLocation(spt.getX() + 0.5, spt.getY() + 0.5); Point2D dpt = f_transform.transform(spt, null); dpt.setLocation(dpt.getX() - 0.5, dpt.getY() - 0.5); return dpt; } /** * Forward map the source Rectangle. */ protected Rectangle forwardMapRect(Rectangle sourceRect, int sourceIndex) { return f_transform.createTransformedShape(sourceRect).getBounds(); } /** * Backward map the destination Rectangle. */ protected Rectangle backwardMapRect(Rectangle destRect, int sourceIndex) { // // Backward map the destination to get the corresponding // source Rectangle // float dx0 = (float) destRect.x; float dy0 = (float) destRect.y; float dw = (float) (destRect.width); float dh = (float) (destRect.height); Point2D[] pts = new Point2D[4]; pts[0] = new Point2D.Float(dx0, dy0); pts[1] = new Point2D.Float((dx0 + dw), dy0); pts[2] = new Point2D.Float((dx0 + dw), (dy0 + dh)); pts[3] = new Point2D.Float(dx0, (dy0 + dh)); i_transform.transform(pts, 0, pts, 0, 4); float f_sx0 = Float.MAX_VALUE; float f_sy0 = Float.MAX_VALUE; float f_sx1 = -Float.MAX_VALUE; float f_sy1 = -Float.MAX_VALUE; for (int i = 0; i < 4; i++) { float px = (float)pts[i].getX(); float py = (float)pts[i].getY(); f_sx0 = Math.min(f_sx0, px); f_sy0 = Math.min(f_sy0, py); f_sx1 = Math.max(f_sx1, px); f_sy1 = Math.max(f_sy1, py); } int s_x0 = 0, s_y0 = 0, s_x1 = 0, s_y1 = 0; // Find the bounding box of the source rectangle if (interp instanceof InterpolationNearest) { s_x0 = (int) Math.floor(f_sx0); s_y0 = (int) Math.floor(f_sy0); // Fix for bug 4485920 was to add " + 0.05" to the following // two lines. It should be noted that the fix was made based // on empirical evidence and tested thoroughly, but it is not // known whether this is the root cause. s_x1 = (int) Math.ceil(f_sx1 + 0.5); s_y1 = (int) Math.ceil(f_sy1 + 0.5); } else { s_x0 = (int) Math.floor(f_sx0 - 0.5); s_y0 = (int) Math.floor(f_sy0 - 0.5); s_x1 = (int) Math.ceil(f_sx1); s_y1 = (int) Math.ceil(f_sy1); } // // Return the new rectangle // return new Rectangle(s_x0, s_y0, s_x1 - s_x0, s_y1 - s_y0); } /** * Backward map a destination coordinate (using inverse_transform) * to get the corresponding source coordinate. * We need not worry about interpolation here. * * @param destPt the destination point to backward map * @return source point result of the backward map */ public void mapDestPoint(Point2D destPoint, Point2D srcPoint) { i_transform.transform(destPoint, srcPoint); } public Raster computeTile(int tileX, int tileY) { // // Create a new WritableRaster to represent this tile. // Point org = new Point(tileXToX(tileX), tileYToY(tileY)); WritableRaster dest = createWritableRaster(sampleModel, org); // // Clip output rectangle to image bounds. // Rectangle rect = new Rectangle(org.x, org.y, tileWidth, tileHeight); // // Clip destination tile against the writable destination // area. This is either the layout or a smaller area if // no extension is specified. // Rectangle destRect = rect.intersection(theDest); Rectangle destRect1 = rect.intersection(getBounds()); if ((destRect.width <= 0) || (destRect.height <= 0)) { // No area to write if (setBackground) ImageUtil.fillBackground(dest, destRect1, backgroundValues); return dest; } // // determine the source rectangle needed to compute the destRect // Rectangle srcRect = mapDestRect(destRect, 0); if (extender == null) { srcRect = srcRect.intersection(srcimg); } else { srcRect = srcRect.intersection(padimg); } if (!(srcRect.width > 0 && srcRect.height > 0)) { if (setBackground) ImageUtil.fillBackground(dest, destRect1, backgroundValues); return dest; } if (!destRect1.equals(destRect)) { // beware that estRect1 contains destRect ImageUtil.fillBordersWithBackgroundValues(destRect1, destRect, dest, backgroundValues); } Raster[] sources = new Raster[1]; // Get the source data if (extender == null) { sources[0] = getSourceImage(0).getData(srcRect); } else { sources[0] = getSourceImage(0).getExtendedData(srcRect, extender); } // Compute destination tile computeRect(sources, dest, destRect); // Recycle the source tile if(getSourceImage(0).overlapsMultipleTiles(srcRect)) { recycleTile(sources[0]); } return dest; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/AffineNearestOpImage.java0000644000175000017500000014703710352100073030056 0ustar mathieumathieu/* * $RCSfile: AffineNearestOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-12-20 22:05:47 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.IndexColorModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationNearest; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.util.Range; import java.util.Map; import javax.media.jai.BorderExtender; /** * An OpImage subclass that performs nearest-neighbour Affine mapping */ class AffineNearestOpImage extends AffineOpImage { /** * Constructs an AffineNearestOpImage from a RenderedImage source, * * @param source a RenderedImage. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param interp an Interpolation object to use for resampling * @param transform the desired AffineTransform. */ public AffineNearestOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, AffineTransform transform, Interpolation interp, double[] backgroundValues) { super(source, extender, config, layout, transform, interp, backgroundValues); // If the source has an IndexColorModel, override the default setting // in OpImage. The dest shall have exactly the same SampleModel and // ColorModel as the source. // Note, in this case, the source should have an integral data type. ColorModel srcColorModel = source.getColorModel(); if (srcColorModel instanceof IndexColorModel) { sampleModel = source.getSampleModel().createCompatibleSampleModel( tileWidth, tileHeight); colorModel = srcColorModel; } } // Scanline clipping stuff /** * Sets clipMinX, clipMaxX based on s_ix, s_iy, ifracx, ifracy, * dst_min_x, and dst_min_y. Padding factors are added and * subtracted from the source bounds as given by * src_rect_{x,y}{1,2}. For example, for nearest-neighbor interpo * the padding factors should be set to (0, 0, 0, 0); for * bilinear, (0, 1, 0, 1); and for bicubic, (1, 2, 1, 2). * *

The returned Range object will be for the Integer class and * will contain extrema equivalent to clipMinX and clipMaxX. */ protected Range performScanlineClipping(float src_rect_x1, float src_rect_y1, float src_rect_x2, float src_rect_y2, int s_ix, int s_iy, int ifracx, int ifracy, int dst_min_x, int dst_max_x, int lpad, int rpad, int tpad, int bpad) { int clipMinX = dst_min_x; int clipMaxX = dst_max_x; long xdenom = incx*geom_frac_max + ifracdx; if (xdenom != 0) { long clipx1 = (long)src_rect_x1 + lpad; long clipx2 = (long)src_rect_x2 - rpad; long x1 = ((clipx1 - s_ix)*geom_frac_max - ifracx) + dst_min_x*xdenom; long x2 = ((clipx2 - s_ix)*geom_frac_max - ifracx) + dst_min_x*xdenom; // Moving backwards, switch roles of left and right edges if (xdenom < 0) { long tmp = x1; x1 = x2; x2 = tmp; } int dx1 = ceilRatio(x1, xdenom); clipMinX = Math.max(clipMinX, dx1); int dx2 = floorRatio(x2, xdenom) + 1; clipMaxX = Math.min(clipMaxX, dx2); } else { // xdenom == 0, all points have same x coordinate as the first if (s_ix < src_rect_x1 || s_ix >= src_rect_x2) { clipMinX = clipMaxX = dst_min_x; return new Range(Integer.class, new Integer(clipMinX), new Integer(clipMaxX)); } } long ydenom = incy*geom_frac_max + ifracdy; if (ydenom != 0) { long clipy1 = (long)src_rect_y1 + tpad; long clipy2 = (long)src_rect_y2 - bpad; long y1 = ((clipy1 - s_iy)*geom_frac_max - ifracy) + dst_min_x*ydenom; long y2 = ((clipy2 - s_iy)*geom_frac_max - ifracy) + dst_min_x*ydenom; // Moving backwards, switch roles of top and bottom edges if (ydenom < 0) { long tmp = y1; y1 = y2; y2 = tmp; } int dx1 = ceilRatio(y1, ydenom); clipMinX = Math.max(clipMinX, dx1); int dx2 = floorRatio(y2, ydenom) + 1; clipMaxX = Math.min(clipMaxX, dx2); } else { // ydenom == 0, all points have same y coordinate as the first if (s_iy < src_rect_y1 || s_iy >= src_rect_y2) { clipMinX = clipMaxX = dst_min_x; } } if (clipMinX > dst_max_x) clipMinX = dst_max_x; if (clipMaxX < dst_min_x) clipMaxX = dst_min_x; return new Range(Integer.class, new Integer(clipMinX), new Integer(clipMaxX)); } /** * Sets s_ix, s_iy, ifracx, ifracy to their values at x == clipMinX * from their initial values at x == dst_min_x. * *

The return Point array will contain the updated values of s_ix * and s_iy in the first element and those of ifracx and ifracy in the * second element. */ protected Point[] advanceToStartOfScanline(int dst_min_x, int clipMinX, int s_ix, int s_iy, int ifracx, int ifracy) { // Skip output up to clipMinX long skip = clipMinX - dst_min_x; long dx = ((long)ifracx + skip*ifracdx)/geom_frac_max; long dy = ((long)ifracy + skip*ifracdy)/geom_frac_max; s_ix += skip*incx + (int)dx; s_iy += skip*incy + (int)dy; long lfracx = ifracx + skip*ifracdx; if (lfracx >= 0) { ifracx = (int)(lfracx % geom_frac_max); } else { ifracx = (int)(-(-lfracx % geom_frac_max)); } long lfracy = ifracy + skip*ifracdy; if (lfracy >= 0) { ifracy = (int)(lfracy % geom_frac_max); } else { ifracy = (int)(-(-lfracy % geom_frac_max)); } return new Point[] {new Point(s_ix, s_iy), new Point(ifracx, ifracy)}; } // computeRect() and data type-specific loop methods. /** * Performs an affine transform on a specified rectangle. The sources are * cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster [] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); Raster source = sources[0]; Rectangle srcRect = source.getBounds(); int srcRectX = srcRect.x; int srcRectY = srcRect.y; // // Get data for the source rectangle & the destination rectangle // RasterAccessor srcAccessor = new RasterAccessor(source, srcRect, formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor dstAccessor = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: int dstNumBands = dstAccessor.getNumBands(); if (dstNumBands == 1) { byteLoop_1band(srcAccessor, destRect, srcRectX, srcRectY, dstAccessor); } else if (dstNumBands == 3) { byteLoop_3band(srcAccessor, destRect, srcRectX, srcRectY, dstAccessor); } else { byteLoop(srcAccessor, destRect, srcRectX, srcRectY, dstAccessor); } break; case DataBuffer.TYPE_INT: intLoop(srcAccessor, destRect, srcRectX, srcRectY, dstAccessor); break; case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_USHORT: shortLoop(srcAccessor, destRect, srcRectX, srcRectY, dstAccessor); break; case DataBuffer.TYPE_FLOAT: floatLoop(srcAccessor, destRect, srcRectX, srcRectY, dstAccessor); break; case DataBuffer.TYPE_DOUBLE: doubleLoop(srcAccessor, destRect, srcRectX, srcRectY, dstAccessor); break; } // // If the RasterAccessor object set up a temporary buffer for the // op to write to, tell the RasterAccessor to write that data // to the raster, that we're done with it. // if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } private void byteLoop(RasterAccessor src, Rectangle destRect, int srcRectX, int srcRectY, RasterAccessor dst) { float src_rect_x1 = src.getX(); float src_rect_y1 = src.getY(); float src_rect_x2 = src_rect_x1 + src.getWidth(); float src_rect_y2 = src_rect_y1 + src.getHeight(); float s_x, s_y; int src_x, src_y, src_pos; double fracx, fracy; int dstPixelOffset; int dstOffset = 0; Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); byte dstDataArrays[][] = dst.getByteDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); byte srcDataArrays[][] = src.getByteDataArrays(); int bandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; int incxStride = incx*srcPixelStride; int incx1Stride = incx1*srcPixelStride; int incyStride = incy*srcScanlineStride; int incy1Stride = incy1*srcScanlineStride; byte[] backgroundByte = new byte[dst_num_bands]; for (int i = 0; i < dst_num_bands; i++) backgroundByte[i] = (byte)backgroundValues[i]; for (int y = dst_min_y; y < dst_max_y; y++) { dstPixelOffset = dstOffset; // Backward map the first point in the line // The energy is at the (pt_x + 0.5, pt_y + 0.5) dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates s_x = (float)src_pt.getX(); s_y = (float)src_pt.getY(); // Floor to get the integral coordinate int s_ix = (int) Math.floor(s_x); int s_iy = (int) Math.floor(s_y); fracx = s_x - (double)s_ix; fracy = s_y - (double)s_iy; int ifracx = (int) Math.floor(fracx * geom_frac_max); int ifracy = (int) Math.floor(fracy * geom_frac_max); // Compute clipMinX, clipMinY Range clipRange = performScanlineClipping(src_rect_x1, src_rect_y1, // Last point in the source is // x2 = x1 + width - 1 // y2 = y1 + height - 1 src_rect_x2 - 1, src_rect_y2 - 1, s_ix, s_iy, ifracx, ifracy, dst_min_x, dst_max_x, 0, 0, 0, 0); int clipMinX = ((Integer)clipRange.getMinValue()).intValue(); int clipMaxX = ((Integer)clipRange.getMaxValue()).intValue(); // Advance s_ix, s_iy, ifracx, ifracy Point[] startPts = advanceToStartOfScanline(dst_min_x, clipMinX, s_ix, s_iy, ifracx, ifracy); s_ix = startPts[0].x; s_iy = startPts[0].y; ifracx = startPts[1].x; ifracy = startPts[1].y; // Translate to/from SampleModel space & Raster space src_pos = (s_iy - srcRectY)*srcScanlineStride + (s_ix - srcRectX)*srcPixelStride; if (setBackground) { for (int x = dst_min_x; x < clipMinX; x++) { for (int k2=0; k2 < dst_num_bands; k2++) dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = backgroundByte[k2]; dstPixelOffset += dstPixelStride; } } else // Advance to first pixel dstPixelOffset += (clipMinX - dst_min_x)*dstPixelStride; for (int x = clipMinX; x < clipMaxX; x++) { for (int k2=0; k2 < dst_num_bands; k2++) { dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = srcDataArrays[k2][src_pos + bandOffsets[k2]]; } // walk if (ifracx < ifracdx1) { /* // DEBUG s_ix += incx; */ src_pos += incxStride; ifracx += ifracdx; } else { /* // DEBUG s_ix += incx1; */ src_pos += incx1Stride; ifracx -= ifracdx1; } if (ifracy < ifracdy1) { /* // DEBUG s_iy += incy; */ src_pos += incyStride; ifracy += ifracdy; } else { /* // DEBUG s_iy += incy1; */ src_pos += incy1Stride; ifracy -= ifracdy1; } // Go to next pixel dstPixelOffset += dstPixelStride; } if (setBackground && clipMinX <= clipMaxX) { for (int x = clipMaxX; x < dst_max_x ; x++) { for (int k2=0; k2 < dst_num_bands; k2++) dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = backgroundByte[k2]; dstPixelOffset += dstPixelStride; } } // Go to the next line in the destination rectangle dstOffset += dstScanlineStride; } } private void byteLoop_1band(RasterAccessor src, Rectangle destRect, int srcRectX, int srcRectY, RasterAccessor dst) { float src_rect_x1 = src.getX(); float src_rect_y1 = src.getY(); float src_rect_x2 = src_rect_x1 + src.getWidth(); float src_rect_y2 = src_rect_y1 + src.getHeight(); float s_x, s_y; int src_x, src_y, src_pos; double fracx, fracy; int dstPixelOffset; int dstOffset = 0; Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); byte dstDataArrays[][] = dst.getByteDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); byte[] dstDataArray0 = dstDataArrays[0]; int dstBandOffset0 = dstBandOffsets[0]; byte srcDataArrays[][] = src.getByteDataArrays(); int bandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); byte[] srcDataArray0 = srcDataArrays[0]; int bandOffsets0 = bandOffsets[0]; int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; int incxStride = incx*srcPixelStride; int incx1Stride = incx1*srcPixelStride; int incyStride = incy*srcScanlineStride; int incy1Stride = incy1*srcScanlineStride; byte backgroundByte = (byte)backgroundValues[0]; for (int y = dst_min_y; y < dst_max_y; y++) { dstPixelOffset = dstOffset; // Backward map the first point in the line // The energy is at the (pt_x + 0.5, pt_y + 0.5) dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates s_x = (float)src_pt.getX(); s_y = (float)src_pt.getY(); // Floor to get the integral coordinate int s_ix = (int) Math.floor(s_x); int s_iy = (int) Math.floor(s_y); fracx = s_x - (double)s_ix; fracy = s_y - (double)s_iy; int ifracx = (int) Math.floor(fracx * geom_frac_max); int ifracy = (int) Math.floor(fracy * geom_frac_max); // Compute clipMinX, clipMinY Range clipRange = performScanlineClipping(src_rect_x1, src_rect_y1, // Last point in the source is // x2 = x1 + width - 1 // y2 = y1 + height - 1 src_rect_x2 - 1, src_rect_y2 - 1, s_ix, s_iy, ifracx, ifracy, dst_min_x, dst_max_x, 0, 0, 0, 0); int clipMinX = ((Integer)clipRange.getMinValue()).intValue(); int clipMaxX = ((Integer)clipRange.getMaxValue()).intValue(); // Advance s_ix, s_iy, ifracx, ifracy Point[] startPts = advanceToStartOfScanline(dst_min_x, clipMinX, s_ix, s_iy, ifracx, ifracy); s_ix = startPts[0].x; s_iy = startPts[0].y; ifracx = startPts[1].x; ifracy = startPts[1].y; // Translate to/from SampleModel space & Raster space src_pos = (s_iy - srcRectY)*srcScanlineStride + (s_ix - srcRectX)*srcPixelStride; if (setBackground) { for (int x = dst_min_x; x < clipMinX; x++) { dstDataArray0[dstPixelOffset + dstBandOffset0] = backgroundByte; dstPixelOffset += dstPixelStride; } } else // Advance to first pixel dstPixelOffset += (clipMinX - dst_min_x)*dstPixelStride; for (int x = clipMinX; x < clipMaxX; x++) { dstDataArray0[dstPixelOffset + dstBandOffset0] = srcDataArray0[src_pos + bandOffsets0]; // walk if (ifracx < ifracdx1) { /* // DEBUG s_ix += incx; */ src_pos += incxStride; ifracx += ifracdx; } else { /* // DEBUG s_ix += incx1; */ src_pos += incx1Stride; ifracx -= ifracdx1; } if (ifracy < ifracdy1) { /* // DEBUG s_iy += incy; */ src_pos += incyStride; ifracy += ifracdy; } else { /* // DEBUG s_iy += incy1; */ src_pos += incy1Stride; ifracy -= ifracdy1; } // Go to next pixel dstPixelOffset += dstPixelStride; } if (setBackground && clipMinX <= clipMaxX) { for (int x = clipMaxX; x < dst_max_x; x++) { dstDataArray0[dstPixelOffset + dstBandOffset0] = backgroundByte; dstPixelOffset += dstPixelStride; } } // Go to the next line in the destination rectangle dstOffset += dstScanlineStride; } } private void byteLoop_3band(RasterAccessor src, Rectangle destRect, int srcRectX, int srcRectY, RasterAccessor dst) { float src_rect_x1 = src.getX(); float src_rect_y1 = src.getY(); float src_rect_x2 = src_rect_x1 + src.getWidth(); float src_rect_y2 = src_rect_y1 + src.getHeight(); float s_x, s_y; int src_x, src_y, src_pos; double fracx, fracy; int dstPixelOffset; int dstOffset = 0; Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); byte dstDataArrays[][] = dst.getByteDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); byte[] dstDataArray0 = dstDataArrays[0]; byte[] dstDataArray1 = dstDataArrays[1]; byte[] dstDataArray2 = dstDataArrays[2]; int dstBandOffset0 = dstBandOffsets[0]; int dstBandOffset1 = dstBandOffsets[1]; int dstBandOffset2 = dstBandOffsets[2]; byte srcDataArrays[][] = src.getByteDataArrays(); int bandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); byte[] srcDataArray0 = srcDataArrays[0]; byte[] srcDataArray1 = srcDataArrays[1]; byte[] srcDataArray2 = srcDataArrays[2]; int bandOffsets0 = bandOffsets[0]; int bandOffsets1 = bandOffsets[1]; int bandOffsets2 = bandOffsets[2]; int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; int incxStride = incx*srcPixelStride; int incx1Stride = incx1*srcPixelStride; int incyStride = incy*srcScanlineStride; int incy1Stride = incy1*srcScanlineStride; byte background0 = (byte)backgroundValues[0]; byte background1 = (byte)backgroundValues[1]; byte background2 = (byte)backgroundValues[2]; for (int y = dst_min_y; y < dst_max_y; y++) { dstPixelOffset = dstOffset; // Backward map the first point in the line // The energy is at the (pt_x + 0.5, pt_y + 0.5) dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates s_x = (float)src_pt.getX(); s_y = (float)src_pt.getY(); // Floor to get the integral coordinate int s_ix = (int) Math.floor(s_x); int s_iy = (int) Math.floor(s_y); fracx = s_x - (double)s_ix; fracy = s_y - (double)s_iy; int ifracx = (int) Math.floor(fracx * geom_frac_max); int ifracy = (int) Math.floor(fracy * geom_frac_max); // Compute clipMinX, clipMinY Range clipRange = performScanlineClipping(src_rect_x1, src_rect_y1, // Last point in the source is // x2 = x1 + width - 1 // y2 = y1 + height - 1 src_rect_x2 - 1, src_rect_y2 - 1, s_ix, s_iy, ifracx, ifracy, dst_min_x, dst_max_x, 0, 0, 0, 0); int clipMinX = ((Integer)clipRange.getMinValue()).intValue(); int clipMaxX = ((Integer)clipRange.getMaxValue()).intValue(); // Advance s_ix, s_iy, ifracx, ifracy Point[] startPts = advanceToStartOfScanline(dst_min_x, clipMinX, s_ix, s_iy, ifracx, ifracy); s_ix = startPts[0].x; s_iy = startPts[0].y; ifracx = startPts[1].x; ifracy = startPts[1].y; // Translate to/from SampleModel space & Raster space src_pos = (s_iy - srcRectY)*srcScanlineStride + (s_ix - srcRectX)*srcPixelStride; if (setBackground) { for (int x = dst_min_x; x < clipMinX; x++) { dstDataArray0[dstPixelOffset + dstBandOffset0] = background0; dstDataArray1[dstPixelOffset + dstBandOffset1] = background1; dstDataArray2[dstPixelOffset + dstBandOffset2] = background2; dstPixelOffset += dstPixelStride; } } else // Advance to first pixel dstPixelOffset += (clipMinX - dst_min_x)*dstPixelStride; for (int x = clipMinX; x < clipMaxX; x++) { dstDataArray0[dstPixelOffset + dstBandOffset0] = srcDataArray0[src_pos + bandOffsets0]; dstDataArray1[dstPixelOffset + dstBandOffset1] = srcDataArray1[src_pos + bandOffsets1]; dstDataArray2[dstPixelOffset + dstBandOffset2] = srcDataArray2[src_pos + bandOffsets2]; // walk if (ifracx < ifracdx1) { /* // DEBUG s_ix += incx; */ src_pos += incxStride; ifracx += ifracdx; } else { /* // DEBUG s_ix += incx1; */ src_pos += incx1Stride; ifracx -= ifracdx1; } if (ifracy < ifracdy1) { /* // DEBUG s_iy += incy; */ src_pos += incyStride; ifracy += ifracdy; } else { /* // DEBUG s_iy += incy1; */ src_pos += incy1Stride; ifracy -= ifracdy1; } // Go to next pixel dstPixelOffset += dstPixelStride; } if (setBackground && clipMinX <= clipMaxX) { for (int x = clipMaxX; x < dst_max_x; x++) { dstDataArray0[dstPixelOffset + dstBandOffset0] = background0; dstDataArray1[dstPixelOffset + dstBandOffset1] = background1; dstDataArray2[dstPixelOffset + dstBandOffset2] = background2; dstPixelOffset += dstPixelStride; } } // Go to the next line in the destination rectangle dstOffset += dstScanlineStride; } } private void intLoop(RasterAccessor src, Rectangle destRect, int srcRectX, int srcRectY, RasterAccessor dst) { float src_rect_x1 = src.getX(); float src_rect_y1 = src.getY(); float src_rect_x2 = src_rect_x1 + src.getWidth(); float src_rect_y2 = src_rect_y1 + src.getHeight(); float s_x, s_y; int src_x, src_y, src_pos; double fracx, fracy; int dstPixelOffset; int dstOffset = 0; Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dstDataArrays[][] = dst.getIntDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcDataArrays[][] = src.getIntDataArrays(); int bandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; int incxStride = incx*srcPixelStride; int incx1Stride = incx1*srcPixelStride; int incyStride = incy*srcScanlineStride; int incy1Stride = incy1*srcScanlineStride; int[] backgroundInt = new int[dst_num_bands]; for (int i = 0; i < dst_num_bands; i++) backgroundInt[i] = (int)backgroundValues[i]; for (int y = dst_min_y; y < dst_max_y; y++) { dstPixelOffset = dstOffset; // Backward map the first point in the line // The energy is at the (pt_x + 0.5, pt_y + 0.5) dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates s_x = (float)src_pt.getX(); s_y = (float)src_pt.getY(); // Floor value to get the integral coordinate int s_ix = (int) Math.floor(s_x); int s_iy = (int) Math.floor(s_y); fracx = s_x - (double)s_ix; fracy = s_y - (double)s_iy; int ifracx = (int) Math.floor(fracx * geom_frac_max); int ifracy = (int) Math.floor(fracy * geom_frac_max); // Compute clipMinX, clipMinY Range clipRange = performScanlineClipping(src_rect_x1, src_rect_y1, // Last point in the source is // x2 = x1 + width - 1 // y2 = y1 + height - 1 src_rect_x2 - 1, src_rect_y2 - 1, s_ix, s_iy, ifracx, ifracy, dst_min_x, dst_max_x, 0, 0, 0, 0); int clipMinX = ((Integer)clipRange.getMinValue()).intValue(); int clipMaxX = ((Integer)clipRange.getMaxValue()).intValue(); // Advance s_ix, s_iy, ifracx, ifracy Point[] startPts = advanceToStartOfScanline(dst_min_x, clipMinX, s_ix, s_iy, ifracx, ifracy); s_ix = startPts[0].x; s_iy = startPts[0].y; ifracx = startPts[1].x; ifracy = startPts[1].y; // Translate to/from SampleModel space & Raster space src_pos = (s_iy - srcRectY)*srcScanlineStride + (s_ix - srcRectX)*srcPixelStride; if (setBackground) { for (int x = dst_min_x; x < clipMinX; x++) { for (int k2=0; k2 < dst_num_bands; k2++) dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = backgroundInt[k2]; dstPixelOffset += dstPixelStride; } } else // Advance to first pixel dstPixelOffset += (clipMinX - dst_min_x)*dstPixelStride; for (int x = clipMinX; x < clipMaxX; x++) { for (int k2=0; k2 < dst_num_bands; k2++) { dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = srcDataArrays[k2][src_pos + bandOffsets[k2]]; } // walk if (ifracx < ifracdx1) { /* // DEBUG s_ix += incx; */ src_pos += incxStride; ifracx += ifracdx; } else { /* // DEBUG s_ix += incx1; */ src_pos += incx1Stride; ifracx -= ifracdx1; } if (ifracy < ifracdy1) { /* // DEBUG s_iy += incy; */ src_pos += incyStride; ifracy += ifracdy; } else { /* // DEBUG s_iy += incy1; */ src_pos += incy1Stride; ifracy -= ifracdy1; } dstPixelOffset += dstPixelStride; } if (setBackground && clipMinX <= clipMaxX) { for (int x = clipMaxX; x < dst_max_x ; x++) { for (int k2=0; k2 < dst_num_bands; k2++) dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = backgroundInt[k2]; dstPixelOffset += dstPixelStride; } } dstOffset += dstScanlineStride; } } private void shortLoop(RasterAccessor src, Rectangle destRect, int srcRectX, int srcRectY, RasterAccessor dst) { float src_rect_x1 = src.getX(); float src_rect_y1 = src.getY(); float src_rect_x2 = src_rect_x1 + src.getWidth(); float src_rect_y2 = src_rect_y1 + src.getHeight(); float s_x, s_y; int src_x, src_y, src_pos; double fracx, fracy; int dstPixelOffset; int dstOffset = 0; Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int bandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; int incxStride = incx*srcPixelStride; int incx1Stride = incx1*srcPixelStride; int incyStride = incy*srcScanlineStride; int incy1Stride = incy1*srcScanlineStride; short[] backgroundShort = new short[dst_num_bands]; for (int i = 0; i < dst_num_bands; i++) backgroundShort[i] = (short)backgroundValues[i]; for (int y = dst_min_y; y < dst_max_y; y++) { dstPixelOffset = dstOffset; // Backward map the first point in the line // The energy is at the (pt_x + 0.5, pt_y + 0.5) dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates s_x = (float)src_pt.getX(); s_y = (float)src_pt.getY(); // Floor value to get the integral coordinate int s_ix = (int) Math.floor(s_x); int s_iy = (int) Math.floor(s_y); fracx = s_x - (double)s_ix; fracy = s_y - (double)s_iy; int ifracx = (int) Math.floor(fracx * geom_frac_max); int ifracy = (int) Math.floor(fracy * geom_frac_max); // Compute clipMinX, clipMinY Range clipRange = performScanlineClipping(src_rect_x1, src_rect_y1, // Last point in the source is // x2 = x1 + width - 1 // y2 = y1 + height - 1 src_rect_x2 - 1, src_rect_y2 - 1, s_ix, s_iy, ifracx, ifracy, dst_min_x, dst_max_x, 0, 0, 0, 0); int clipMinX = ((Integer)clipRange.getMinValue()).intValue(); int clipMaxX = ((Integer)clipRange.getMaxValue()).intValue(); // Advance s_ix, s_iy, ifracx, ifracy Point[] startPts = advanceToStartOfScanline(dst_min_x, clipMinX, s_ix, s_iy, ifracx, ifracy); s_ix = startPts[0].x; s_iy = startPts[0].y; ifracx = startPts[1].x; ifracy = startPts[1].y; // Translate to/from SampleModel space & Raster space src_pos = (s_iy - srcRectY)*srcScanlineStride + (s_ix - srcRectX)*srcPixelStride; if (setBackground) { for (int x = dst_min_x; x < clipMinX; x++) { for (int k2=0; k2 < dst_num_bands; k2++) dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = backgroundShort[k2]; dstPixelOffset += dstPixelStride; } } else // Advance to first pixel dstPixelOffset += (clipMinX - dst_min_x)*dstPixelStride; for (int x = clipMinX; x < clipMaxX; x++) { for (int k2=0; k2 < dst_num_bands; k2++) { dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = srcDataArrays[k2][src_pos + bandOffsets[k2]]; } // walk if (ifracx < ifracdx1) { /* // DEBUG s_ix += incx; */ src_pos += incxStride; ifracx += ifracdx; } else { /* // DEBUG s_ix += incx1; */ src_pos += incx1Stride; ifracx -= ifracdx1; } if (ifracy < ifracdy1) { /* // DEBUG s_iy += incy; */ src_pos += incyStride; ifracy += ifracdy; } else { /* // DEBUG s_iy += incy1; */ src_pos += incy1Stride; ifracy -= ifracdy1; } dstPixelOffset += dstPixelStride; } if (setBackground && clipMinX <= clipMaxX) { for (int x = clipMaxX; x < dst_max_x ; x++) { for (int k2=0; k2 < dst_num_bands; k2++) dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = backgroundShort[k2]; dstPixelOffset += dstPixelStride; } } dstOffset += dstScanlineStride; } } private void floatLoop(RasterAccessor src, Rectangle destRect, int srcRectX, int srcRectY, RasterAccessor dst) { float src_rect_x1 = src.getX(); float src_rect_y1 = src.getY(); float src_rect_x2 = src_rect_x1 + src.getWidth(); float src_rect_y2 = src_rect_y1 + src.getHeight(); float s_x, s_y; int src_x, src_y, src_pos; double fracx, fracy; int dstPixelOffset; int dstOffset = 0; Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); float dstDataArrays[][] = dst.getFloatDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); float srcDataArrays[][] = src.getFloatDataArrays(); int bandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; int incxStride = incx*srcPixelStride; int incx1Stride = incx1*srcPixelStride; int incyStride = incy*srcScanlineStride; int incy1Stride = incy1*srcScanlineStride; float[] backgroundFloat = new float[dst_num_bands]; for (int i = 0; i < dst_num_bands; i++) backgroundFloat[i] = (float)backgroundValues[i]; for (int y = dst_min_y; y < dst_max_y; y++) { dstPixelOffset = dstOffset; // Backward map the first point in the line // The energy is at the (pt_x + 0.5, pt_y + 0.5) dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates s_x = (float)src_pt.getX(); s_y = (float)src_pt.getY(); // Floor value to get the integral coordinate int s_ix = (int) Math.floor(s_x); int s_iy = (int) Math.floor(s_y); fracx = s_x - (double)s_ix; fracy = s_y - (double)s_iy; int ifracx = (int) Math.floor(fracx * geom_frac_max); int ifracy = (int) Math.floor(fracy * geom_frac_max); // Compute clipMinX, clipMinY Range clipRange = performScanlineClipping(src_rect_x1, src_rect_y1, // Last point in the source is // x2 = x1 + width - 1 // y2 = y1 + height - 1 src_rect_x2 - 1, src_rect_y2 - 1, s_ix, s_iy, ifracx, ifracy, dst_min_x, dst_max_x, 0, 0, 0, 0); int clipMinX = ((Integer)clipRange.getMinValue()).intValue(); int clipMaxX = ((Integer)clipRange.getMaxValue()).intValue(); // Advance s_ix, s_iy, ifracx, ifracy Point[] startPts = advanceToStartOfScanline(dst_min_x, clipMinX, s_ix, s_iy, ifracx, ifracy); s_ix = startPts[0].x; s_iy = startPts[0].y; ifracx = startPts[1].x; ifracy = startPts[1].y; // Translate to/from SampleModel space & Raster space src_pos = (s_iy - srcRectY)*srcScanlineStride + (s_ix - srcRectX)*srcPixelStride; if (setBackground) { for (int x = dst_min_x; x < clipMinX; x++) { for (int k2=0; k2 < dst_num_bands; k2++) dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = backgroundFloat[k2]; dstPixelOffset += dstPixelStride; } } else // Advance to first pixel dstPixelOffset += (clipMinX - dst_min_x)*dstPixelStride; for (int x = clipMinX; x < clipMaxX; x++) { for (int k2=0; k2 < dst_num_bands; k2++) { dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = srcDataArrays[k2][src_pos + bandOffsets[k2]]; } // walk if (ifracx < ifracdx1) { /* // DEBUG s_ix += incx; */ src_pos += incxStride; ifracx += ifracdx; } else { /* // DEBUG s_ix += incx1; */ src_pos += incx1Stride; ifracx -= ifracdx1; } if (ifracy < ifracdy1) { /* // DEBUG s_iy += incy; */ src_pos += incyStride; ifracy += ifracdy; } else { /* // DEBUG s_iy += incy1; */ src_pos += incy1Stride; ifracy -= ifracdy1; } dstPixelOffset += dstPixelStride; } if (setBackground && clipMinX <= clipMaxX) { for (int x = clipMaxX; x < dst_max_x ; x++) { for (int k2=0; k2 < dst_num_bands; k2++) dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = backgroundFloat[k2]; dstPixelOffset += dstPixelStride; } } dstOffset += dstScanlineStride; } } private void doubleLoop(RasterAccessor src, Rectangle destRect, int srcRectX, int srcRectY, RasterAccessor dst) { float src_rect_x1 = src.getX(); float src_rect_y1 = src.getY(); float src_rect_x2 = src_rect_x1 + src.getWidth(); float src_rect_y2 = src_rect_y1 + src.getHeight(); float s_x, s_y; int src_x, src_y, src_pos; double fracx, fracy; int dstPixelOffset; int dstOffset = 0; Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); double dstDataArrays[][] = dst.getDoubleDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); double srcDataArrays[][] = src.getDoubleDataArrays(); int bandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; int incxStride = incx*srcPixelStride; int incx1Stride = incx1*srcPixelStride; int incyStride = incy*srcScanlineStride; int incy1Stride = incy1*srcScanlineStride; for (int y = dst_min_y; y < dst_max_y; y++) { dstPixelOffset = dstOffset; // Backward map the first point in the line // The energy is at the (pt_x + 0.5, pt_y + 0.5) dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates s_x = (float)src_pt.getX(); s_y = (float)src_pt.getY(); // Floor value to get the integral coordinate int s_ix = (int) Math.floor(s_x); int s_iy = (int) Math.floor(s_y); fracx = s_x - (double)s_ix; fracy = s_y - (double)s_iy; int ifracx = (int) Math.floor(fracx * geom_frac_max); int ifracy = (int) Math.floor(fracy * geom_frac_max); // Compute clipMinX, clipMinY Range clipRange = performScanlineClipping(src_rect_x1, src_rect_y1, src_rect_x2 - 1, src_rect_y2 - 1, s_ix, s_iy, ifracx, ifracy, dst_min_x, dst_max_x, 0, 0, 0, 0); int clipMinX = ((Integer)clipRange.getMinValue()).intValue(); int clipMaxX = ((Integer)clipRange.getMaxValue()).intValue(); // Advance s_ix, s_iy, ifracx, ifracy Point[] startPts = advanceToStartOfScanline(dst_min_x, clipMinX, s_ix, s_iy, ifracx, ifracy); s_ix = startPts[0].x; s_iy = startPts[0].y; ifracx = startPts[1].x; ifracy = startPts[1].y; // Translate to/from SampleModel space & Raster space src_pos = (s_iy - srcRectY)*srcScanlineStride + (s_ix - srcRectX)*srcPixelStride; if (setBackground) { for (int x = dst_min_x; x < clipMinX; x++) { for (int k2=0; k2 < dst_num_bands; k2++) dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = backgroundValues[k2]; dstPixelOffset += dstPixelStride; } } else // Advance to first pixel dstPixelOffset += (clipMinX - dst_min_x)*dstPixelStride; for (int x = clipMinX; x < clipMaxX; x++) { for (int k2=0; k2 < dst_num_bands; k2++) { dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = srcDataArrays[k2][src_pos + bandOffsets[k2]]; } // walk if (ifracx < ifracdx1) { /* // DEBUG s_ix += incx; */ src_pos += incxStride; ifracx += ifracdx; } else { /* // DEBUG s_ix += incx1; */ src_pos += incx1Stride; ifracx -= ifracdx1; } if (ifracy < ifracdy1) { /* // DEBUG s_iy += incy; */ src_pos += incyStride; ifracy += ifracdy; } else { /* // DEBUG s_iy += incy1; */ src_pos += incy1Stride; ifracy -= ifracdy1; } dstPixelOffset += dstPixelStride; } if (setBackground && clipMinX <= clipMaxX) { for (int x = clipMaxX; x < dst_max_x ; x++) { for (int k2=0; k2 < dst_num_bands; k2++) dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = backgroundValues[k2]; dstPixelOffset += dstPixelStride; } } dstOffset += dstScanlineStride; } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/RenderableCRIF.java0000644000175000017500000001556510203035544026616 0ustar mathieumathieu/* * $RCSfile: RenderableCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:41 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.geom.Rectangle2D; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderContext; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import java.lang.ref.SoftReference; import java.util.Hashtable; import java.util.Vector; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageMIPMap; import javax.media.jai.MultiResolutionRenderableImage; import javax.media.jai.RenderedOp; /** * A CRIF supporting the "Renderable" operation in the * renderable image layers. * * @see javax.media.jai.operator.RenderableDescriptor * * * @since 1.0 */ public class RenderableCRIF extends CRIFImpl { /** Cache of SoftReferences to the MultiResolutionRenderableImages. */ private Hashtable mresTable = null; /** Derives a hash key for this ParameterBlock. */ private static final Object getKey(ParameterBlock paramBlock) { // Initialize the key string. String key = new String(); // Add the hash code of the source to the key. key += String.valueOf(paramBlock.getRenderedSource(0).hashCode()); // Add the RenderedOp parameter to the key. key += getKey((RenderedOp)paramBlock.getObjectParameter(0)); // Add the numerical parameters to the key. key += String.valueOf(paramBlock.getIntParameter(1)); key += String.valueOf(paramBlock.getFloatParameter(2)); key += String.valueOf(paramBlock.getFloatParameter(3)); key += String.valueOf(paramBlock.getFloatParameter(4)); return key; } /** Derives a hash key string for this RenderedOp. */ private static final String getKey(RenderedOp op) { // Initialize the key string to the RenderedOp hash code. String key = new String(String.valueOf(op.hashCode())); // Get the ParameterBlock ParameterBlock pb = op.getParameterBlock(); // Add the sources. int numSources = pb.getNumSources(); for(int s = 0; s < numSources; s++) { RenderedImage src = pb.getRenderedSource(s); // If the source is a node recurse up the chain. if(src instanceof RenderedOp) { key += getKey((RenderedOp)src); } else { key += String.valueOf(src.hashCode()); } } // Add the parameters. int numParameters = pb.getNumParameters(); for(int p = 0; p < numParameters; p++) { // Use toString() instead of hashCode() here because the // majority of parameters are numerical. key += pb.getObjectParameter(p).toString(); } return key; } /** Constructor. */ public RenderableCRIF() {} /** * Creates a RenderableImage pyramid from the source and parameters. * If the down sampler operation chain does not decrease both the * width and height at a given level an IllegalArgumentException will * be thrown. * * @param paramBlock The ParameterBlock containing a single RenderedImage * source and parameters sufficient to create an image pyramid. */ private RenderableImage createRenderable(ParameterBlock paramBlock) { // Create the Hashtable "just in time". if(mresTable == null) { mresTable = new Hashtable(); } // Check for a SoftReference hashed on a ParameterBlock-derived key. Object key = getKey(paramBlock); SoftReference ref = (SoftReference)mresTable.get(key); // Retrieve the image from the SoftReference if possible. RenderableImage mres = null; if(ref != null && (mres = (RenderableImage)ref.get()) == null) { // null referent: remove the ParameterBlock key from the Hashtable. mresTable.remove(key); } // Derive the image if necessary. if(mres == null) { // Retrieve the source and parameters. RenderedImage source = paramBlock.getRenderedSource(0); RenderedOp downSampler = (RenderedOp)paramBlock.getObjectParameter(0); int maxLowResDim = paramBlock.getIntParameter(1); float minX = paramBlock.getFloatParameter(2); float minY = paramBlock.getFloatParameter(3); float height = paramBlock.getFloatParameter(4); // Create an image pyramid. ImageMIPMap pyramid = new ImageMIPMap(source, downSampler); // Create a Vector of RenderedImages from the pyramid. Vector sourceVector = new Vector(); RenderedImage currentImage = pyramid.getCurrentImage(); sourceVector.add(currentImage); while(currentImage.getWidth() > maxLowResDim || currentImage.getHeight() > maxLowResDim) { RenderedImage nextImage = pyramid.getDownImage(); if(nextImage.getWidth() >= currentImage.getWidth() || nextImage.getHeight() >= currentImage.getHeight()) { throw new IllegalArgumentException(JaiI18N.getString("RenderableCRIF0")); } sourceVector.add(nextImage); currentImage = nextImage; } // Create a RenderableImage mres = new MultiResolutionRenderableImage(sourceVector, minX, minY, height); // Store a SoftReference to the RenderableImage in the Hashtable. mresTable.put(key, new SoftReference(mres)); } return mres; } /** * Returns the source RenderedImage. * This method satisfies the implementation of RIF. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { return paramBlock.getRenderedSource(0); } /** * Creates a new instance of AffineOpImage * in the renderable layer. This method satisfies the * implementation of CRIF. */ public RenderedImage create(RenderContext renderContext, ParameterBlock paramBlock) { RenderableImage mres = createRenderable(paramBlock); return mres.createRendering(renderContext); } /** * Gets the output bounding box in rendering-independent space. * This method satisfies the implementation of CRIF. */ public Rectangle2D getBounds2D(ParameterBlock paramBlock) { RenderableImage mres = createRenderable(paramBlock); return new Rectangle2D.Float(mres.getMinX(), mres.getMinY(), mres.getWidth(), mres.getHeight()); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ErodeRIF.java0000644000175000017500000000436210203035544025477 0ustar mathieumathieu/* * $RCSfile: ErodeRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:24 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; import java.util.Map; /** * @see ErodeOpImage */ public class ErodeRIF implements RenderedImageFactory { /** Constructor. */ public ErodeRIF() {} /** * Create a new instance of ErodeOpImage in the rendered layer. * This method satisfies the implementation of RIF. * * @param paramBlock The source image and the erosion kernel. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); // Get BorderExtender from renderHints if any. BorderExtender extender = RIFUtil.getBorderExtenderHint(renderHints); KernelJAI unRotatedKernel = (KernelJAI)paramBlock.getObjectParameter(0); KernelJAI kJAI = unRotatedKernel.getRotatedKernel(); RenderedImage source = paramBlock.getRenderedSource(0); SampleModel sm = source.getSampleModel(); // check dataType and binary int dataType = sm.getDataType(); boolean isBinary = (sm instanceof MultiPixelPackedSampleModel) && (sm.getSampleSize(0) == 1) && (dataType == DataBuffer.TYPE_BYTE || dataType == DataBuffer.TYPE_USHORT || dataType == DataBuffer.TYPE_INT); // possible speed up later: 3x3 with table lookup if (isBinary){ return new ErodeBinaryOpImage(source, extender, renderHints, layout, kJAI); }else{ return new ErodeOpImage(source, extender, renderHints, layout, kJAI); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MinOpImage.java0000644000175000017500000004265010203035544026067 0ustar mathieumathieu/* * $RCSfile: MinOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:36 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PointOpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; import java.lang.ref.SoftReference; /** * An OpImage implementing the "Min" operation as * described in javax.media.jai.operator.MinDescriptor. * *

This OpImage chooses the minimum pixel values of the * two source images on a per-band basis. In case the two source images * have different number of bands, the number of bands for the destination * image is the smaller band number of the two source images. That is * dstNumBands = Math.min(src1NumBands, src2NumBands). * In case the two source images have different data types, the data type * for the destination image is the higher data type of the two source * images. * *

The value of the pixel (x, y) in the destination image is defined as: *

 * for (b = 0; b < numBands; b++) {
 *     dst[y][x][b] = Math.min(src1[y][x][b], src2[y][x][b]);
 * }
 * 
* * @see javax.media.jai.operator.MinDescriptor * @see MinRIF * */ final class MinOpImage extends PointOpImage { private static long negativeZeroFloatBits = Float.floatToIntBits(-0.0f); private static long negativeZeroDoubleBits = Double.doubleToLongBits(-0.0d); private static byte[] byteTable = null; private static SoftReference softRef = null; private synchronized void allocByteTable() { if ((softRef == null) || (softRef.get() == null)) { // // First time or reference has been cleared. // Create the table and make a soft reference to it. // byteTable = new byte[256*256]; softRef = new SoftReference(byteTable); // Initialize table which implements Min int idx = 0; for (int i1 = 0; i1 < 256; i1++) { int base = i1 << 8; for(int i2=0; i2MinOpImage. * * @param source1 The first source image. * @param source2 The second source image. * @param layout The destination image layout. */ public MinOpImage(RenderedImage source1, RenderedImage source2, Map config, ImageLayout layout) { super(source1, source2, layout, config, true); if (sampleModel.getTransferType() == DataBuffer.TYPE_BYTE) { allocByteTable(); } // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * Return the minimum pixel value of the source images for a * specified rectangle. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); /* For PointOpImage, srcRect = destRect. */ RasterAccessor s1 = new RasterAccessor(sources[0], destRect, formatTags[0], getSource(0).getColorModel()); RasterAccessor s2 = new RasterAccessor(sources[1], destRect, formatTags[1], getSource(1).getColorModel()); RasterAccessor d = new RasterAccessor(dest, destRect, formatTags[2], getColorModel()); switch (d.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(s1, s2, d); break; case DataBuffer.TYPE_USHORT: computeRectUShort(s1, s2, d); break; case DataBuffer.TYPE_SHORT: computeRectShort(s1, s2, d); break; case DataBuffer.TYPE_INT: computeRectInt(s1, s2, d); break; case DataBuffer.TYPE_FLOAT: computeRectFloat(s1, s2, d); break; case DataBuffer.TYPE_DOUBLE: computeRectDouble(s1, s2, d); break; } if (d.isDataCopy()) { d.clampDataArrays(); d.copyDataToRaster(); } } private void computeRectByte(RasterAccessor src1, RasterAccessor src2, RasterAccessor dst) { int s1LineStride = src1.getScanlineStride(); int s1PixelStride = src1.getPixelStride(); int[] s1BandOffsets = src1.getBandOffsets(); byte[][] s1Data = src1.getByteDataArrays(); int s2LineStride = src2.getScanlineStride(); int s2PixelStride = src2.getPixelStride(); int[] s2BandOffsets = src2.getBandOffsets(); byte[][] s2Data = src2.getByteDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int bands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); byte[][] dData = dst.getByteDataArrays(); for (int b = 0; b < bands; b++) { byte[] s1 = s1Data[b]; byte[] s2 = s2Data[b]; byte[] d = dData[b]; int s1LineOffset = s1BandOffsets[b]; int s2LineOffset = s2BandOffsets[b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int s1PixelOffset = s1LineOffset; int s2PixelOffset = s2LineOffset; int dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; dLineOffset += dLineStride; int dstEnd = dPixelOffset + dwidth*dPixelStride; while (dPixelOffset < dstEnd) { int i1 = s1[s1PixelOffset]&0xFF; int i2 = s2[s2PixelOffset]&0xFF; d[dPixelOffset] = byteTable[(i1<<8) + i2]; s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; dPixelOffset += dPixelStride; } } } } private void computeRectUShort(RasterAccessor src1, RasterAccessor src2, RasterAccessor dst) { int s1LineStride = src1.getScanlineStride(); int s1PixelStride = src1.getPixelStride(); int[] s1BandOffsets = src1.getBandOffsets(); short[][] s1Data = src1.getShortDataArrays(); int s2LineStride = src2.getScanlineStride(); int s2PixelStride = src2.getPixelStride(); int[] s2BandOffsets = src2.getBandOffsets(); short[][] s2Data = src2.getShortDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int bands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); short[][] dData = dst.getShortDataArrays(); for (int b = 0; b < bands; b++) { short[] s1 = s1Data[b]; short[] s2 = s2Data[b]; short[] d = dData[b]; int s1LineOffset = s1BandOffsets[b]; int s2LineOffset = s2BandOffsets[b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int s1PixelOffset = s1LineOffset; int s2PixelOffset = s2LineOffset; int dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { d[dPixelOffset] = minUShort(s1[s1PixelOffset], s2[s2PixelOffset]); s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; dPixelOffset += dPixelStride; } } } } private void computeRectShort(RasterAccessor src1, RasterAccessor src2, RasterAccessor dst) { int s1LineStride = src1.getScanlineStride(); int s1PixelStride = src1.getPixelStride(); int[] s1BandOffsets = src1.getBandOffsets(); short[][] s1Data = src1.getShortDataArrays(); int s2LineStride = src2.getScanlineStride(); int s2PixelStride = src2.getPixelStride(); int[] s2BandOffsets = src2.getBandOffsets(); short[][] s2Data = src2.getShortDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int bands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); short[][] dData = dst.getShortDataArrays(); for (int b = 0; b < bands; b++) { short[] s1 = s1Data[b]; short[] s2 = s2Data[b]; short[] d = dData[b]; int s1LineOffset = s1BandOffsets[b]; int s2LineOffset = s2BandOffsets[b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int s1PixelOffset = s1LineOffset; int s2PixelOffset = s2LineOffset; int dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { d[dPixelOffset] = minShort(s1[s1PixelOffset], s2[s2PixelOffset]); s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; dPixelOffset += dPixelStride; } } } } private void computeRectInt(RasterAccessor src1, RasterAccessor src2, RasterAccessor dst) { int s1LineStride = src1.getScanlineStride(); int s1PixelStride = src1.getPixelStride(); int[] s1BandOffsets = src1.getBandOffsets(); int[][] s1Data = src1.getIntDataArrays(); int s2LineStride = src2.getScanlineStride(); int s2PixelStride = src2.getPixelStride(); int[] s2BandOffsets = src2.getBandOffsets(); int[][] s2Data = src2.getIntDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int bands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); int[][] dData = dst.getIntDataArrays(); for (int b = 0; b < bands; b++) { int[] s1 = s1Data[b]; int[] s2 = s2Data[b]; int[] d = dData[b]; int s1LineOffset = s1BandOffsets[b]; int s2LineOffset = s2BandOffsets[b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int s1PixelOffset = s1LineOffset; int s2PixelOffset = s2LineOffset; int dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { d[dPixelOffset] = minInt(s1[s1PixelOffset], s2[s2PixelOffset]); s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; dPixelOffset += dPixelStride; } } } } private void computeRectFloat(RasterAccessor src1, RasterAccessor src2, RasterAccessor dst) { int s1LineStride = src1.getScanlineStride(); int s1PixelStride = src1.getPixelStride(); int[] s1BandOffsets = src1.getBandOffsets(); float[][] s1Data = src1.getFloatDataArrays(); int s2LineStride = src2.getScanlineStride(); int s2PixelStride = src2.getPixelStride(); int[] s2BandOffsets = src2.getBandOffsets(); float[][] s2Data = src2.getFloatDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int bands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); float[][] dData = dst.getFloatDataArrays(); for (int b = 0; b < bands; b++) { float[] s1 = s1Data[b]; float[] s2 = s2Data[b]; float[] d = dData[b]; int s1LineOffset = s1BandOffsets[b]; int s2LineOffset = s2BandOffsets[b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int s1PixelOffset = s1LineOffset; int s2PixelOffset = s2LineOffset; int dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { d[dPixelOffset] = minFloat(s1[s1PixelOffset], s2[s2PixelOffset]); s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; dPixelOffset += dPixelStride; } } } } private void computeRectDouble(RasterAccessor src1, RasterAccessor src2, RasterAccessor dst) { int s1LineStride = src1.getScanlineStride(); int s1PixelStride = src1.getPixelStride(); int[] s1BandOffsets = src1.getBandOffsets(); double[][] s1Data = src1.getDoubleDataArrays(); int s2LineStride = src2.getScanlineStride(); int s2PixelStride = src2.getPixelStride(); int[] s2BandOffsets = src2.getBandOffsets(); double[][] s2Data = src2.getDoubleDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int bands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); double[][] dData = dst.getDoubleDataArrays(); for (int b = 0; b < bands; b++) { double[] s1 = s1Data[b]; double[] s2 = s2Data[b]; double[] d = dData[b]; int s1LineOffset = s1BandOffsets[b]; int s2LineOffset = s2BandOffsets[b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int s1PixelOffset = s1LineOffset; int s2PixelOffset = s2LineOffset; int dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { d[dPixelOffset] = minDouble(s1[s1PixelOffset], s2[s2PixelOffset]); s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; dPixelOffset += dPixelStride; } } } } private final short minUShort(short a, short b) { return (a&0xFFFF) < (b&0xFFFF) ? a : b; } private final short minShort(short a, short b) { return (a <= b) ? a : b; } private final int minInt(int a, int b) { return (a <= b) ? a : b; } public static float minFloat(float a, float b) { if (a != a) return a; // a is NaN if ((a == 0.0f) && (b == 0.0f) && (Float.floatToIntBits(b) == negativeZeroFloatBits)) { return b; } return (a <= b) ? a : b; } public static double minDouble(double a, double b) { if (a != a) return a; // a is NaN if ((a == 0.0d) && (b == 0.0d) && (Double.doubleToLongBits(b) == negativeZeroDoubleBits)) { return b; } return (a <= b) ? a : b; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/AndConstCRIF.java0000644000175000017500000000274710203035544026262 0ustar mathieumathieu/* * $RCSfile: AndConstCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:14 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "AndConst" operation in the rendered * and renderable image layers. * * @see javax.media.jai.operator.AndConstDescriptor * @see AndConstOpImage * * * @since EA2 */ public class AndConstCRIF extends CRIFImpl { /** Constructor. */ public AndConstCRIF() { super("andconst"); } /** * Creates a new instance of AndConstOpImage * in the rendered layer. * * @param args The source image and the constants. * @param hints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new AndConstOpImage(args.getRenderedSource(0), renderHints, layout, (int[])args.getObjectParameter(0)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/BMPRIF.java0000644000175000017500000000260610203035544025056 0ustar mathieumathieu/* * $RCSfile: BMPRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:14 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.io.InputStream; import java.io.IOException; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.NullOpImage; import javax.media.jai.OpImage; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.ImageDecoder; import com.sun.media.jai.codec.SeekableStream; /** * A RIF supporting the "BMP" operation in the * rendered image layer. * * @see javax.media.jai.operator.BMPDescriptor * */ public class BMPRIF implements RenderedImageFactory { /** Constructor. */ public BMPRIF() {} /** * Creates a RenderedImage representing the contents * of a BMP-encoded image. * * @param paramBlock A ParameterBlock containing the BMP * SeekableStream to read. * @param renderHints Ignored. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { return CodecRIFUtil.create("bmp", paramBlock, renderHints); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MinFilterRIF.java0000644000175000017500000000604510203035544026332 0ustar mathieumathieu/* * $RCSfile: MinFilterRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:35 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; import java.util.Map; import javax.media.jai.operator.MinFilterDescriptor; import javax.media.jai.operator.MinFilterShape; /** * Creates a MinFilterOpImage subclass for the given input * mask type * @see MinFilterOpImage */ public class MinFilterRIF implements RenderedImageFactory { /** Constructor. */ public MinFilterRIF() {} /** * Create a new instance of MinFilterOpImage in the rendered layer. * This method satisfies the implementation of RIF. * * @param paramBlock The source image and the convolution kernel. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); // Get BorderExtender from renderHints if any. BorderExtender extender = RIFUtil.getBorderExtenderHint(renderHints); MinFilterShape maskType = (MinFilterShape)paramBlock.getObjectParameter(0); int maskSize = paramBlock.getIntParameter(1); RenderedImage ri = paramBlock.getRenderedSource(0); if(maskType.equals(MinFilterDescriptor.MIN_MASK_SQUARE)) { return new MinFilterSquareOpImage(ri, extender, renderHints, layout, maskSize); } else if(maskType.equals(MinFilterDescriptor.MIN_MASK_PLUS)) { return new MinFilterPlusOpImage(ri, extender, renderHints, layout, maskSize); } else if(maskType.equals(MinFilterDescriptor.MIN_MASK_X)) { return new MinFilterXOpImage(ri, extender, renderHints, layout, maskSize); } else if(maskType.equals(MinFilterDescriptor.MIN_MASK_SQUARE_SEPARABLE)) { return new MinFilterSeparableOpImage(ri, extender, renderHints, layout, maskSize); } return null; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/TransposeOpImage.java0000644000175000017500000007674310347635673027356 0ustar mathieumathieu/* * $RCSfile: TransposeOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-12-13 21:23:06 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.IndexColorModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.GeometricOpImage; import javax.media.jai.ImageLayout; import javax.media.jai.IntegerSequence; import javax.media.jai.OpImage; import javax.media.jai.PlanarImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.RasterFactory; import java.util.Map; /** * An OpImage class to perform transposition and 90 degree rotation * of an image. * * @since EA2 * */ public class TransposeOpImage extends GeometricOpImage { /** The Transpose type */ protected int type; /** * Store source width & height */ protected int src_width, src_height; protected Rectangle sourceBounds; // Set the bounds and tile grid of the output image. private static ImageLayout layoutHelper(ImageLayout layout, RenderedImage source, int type) { ImageLayout newLayout; if (layout != null) { newLayout = (ImageLayout)layout.clone(); } else { newLayout = new ImageLayout(); } // Set the size of the destination to exactly cover the // forward-mapped source image bounds Rectangle sourceBounds = new Rectangle(source.getMinX(), source.getMinY(), source.getWidth(), source.getHeight()); Rectangle rect = mapRect(sourceBounds, sourceBounds, type, true); newLayout.setMinX(rect.x); newLayout.setMinY(rect.y); newLayout.setWidth(rect.width); newLayout.setHeight(rect.height); // Make each destination tile correspond to a source tile Rectangle tileRect = new Rectangle(source.getTileGridXOffset(), source.getTileGridYOffset(), source.getTileWidth(), source.getTileHeight()); rect = mapRect(tileRect, sourceBounds, type, true); // Respect any pre-existing tile grid settings if (newLayout.isValid(ImageLayout.TILE_GRID_X_OFFSET_MASK)) { newLayout.setTileGridXOffset(rect.x); } if (newLayout.isValid(ImageLayout.TILE_GRID_Y_OFFSET_MASK)) { newLayout.setTileGridYOffset(rect.y); } if (newLayout.isValid(ImageLayout.TILE_WIDTH_MASK)) { newLayout.setTileWidth(Math.abs(rect.width)); } if (newLayout.isValid(ImageLayout.TILE_HEIGHT_MASK)) { newLayout.setTileHeight(Math.abs(rect.height)); } return newLayout; } /** * Constructs an TransposeOpImage from a RenderedImage source, * and Transpose type. The image dimensions are determined by * forward-mapping the source bounds. * The tile grid layout, SampleModel, and ColorModel are specified * by the image source, possibly overridden by values from the * ImageLayout parameter. * * @param source a RenderedImage. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param type the desired Tranpose type. */ public TransposeOpImage(RenderedImage source, Map config, ImageLayout layout, int type) { super(vectorize(source), layoutHelper(layout, source, type), config, true, null, // BorderExtender null, null); // Interpolation (superclass defaults to nearest neighbor) // If the source has an IndexColorModel, override the default setting // in OpImage. The dest shall have exactly the same SampleModel and // ColorModel as the source. // Note, in this case, the source should have an integral data type. ColorModel srcColorModel = source.getColorModel(); if (srcColorModel instanceof IndexColorModel) { sampleModel = source.getSampleModel().createCompatibleSampleModel( tileWidth, tileHeight); colorModel = srcColorModel; } // store the Transpose type this.type = type; // Store the source width & height this.src_width = source.getWidth(); this.src_height = source.getHeight(); this.sourceBounds = new Rectangle(source.getMinX(), source.getMinY(), source.getWidth(), source.getHeight()); } /** * Forward map the source Rectangle. */ protected Rectangle forwardMapRect(Rectangle sourceRect, int sourceIndex) { return mapRect(sourceRect, sourceBounds, type, true); } /** * Backward map the destination Rectangle. */ protected Rectangle backwardMapRect(Rectangle destRect, int sourceIndex) { return mapRect(destRect, sourceBounds, type, false); } /** * Map a point according to the transposition type. * If mapForwards is true, * the point is considered to lie in the source image and * is mapping into the destination space. Otherwise, * the point lies in the destination and is mapped * into the source space. * *

In either case, the bounds of the source image * must be supplied. The bounds are given by the indices * of the upper left and lower right pixels, i.e., * maxX = minX + width - 1 and similarly for maxY. */ protected static void mapPoint(int[] pt, int minX, int minY, int maxX, int maxY, int type, boolean mapForwards) { int sx = pt[0]; int sy = pt[1]; int dx = -1; int dy = -1; switch (type) { case 0: // FLIP_VERTICAL dx = sx; dy = minY + maxY - sy; break; case 1: // FLIP_HORIZONTAL dx = minX + maxX - sx; dy = sy; break; case 2: // FLIP_DIAGONAL dx = minX - minY + sy; dy = minY - minX + sx; break; case 3: // FLIP_ANTIDIAGONAL if (mapForwards) { dx = minX + maxY - sy; dy = minY + maxX - sx; } else { dx = minY + maxX - sy; dy = minX + maxY - sx; } break; case 4: // ROTATE_90 if (mapForwards) { dx = minX + maxY - sy; dy = minY - minX + sx; } else { dx = minX - minY + sy; dy = minX + maxY - sx; } break; case 5: // ROTATE_180 dx = minX + maxX - sx; dy = minY + maxY - sy; break; case 6: // ROTATE_270 if (mapForwards) { dx = minX - minY + sy; dy = maxX + minY - sx; } else { dx = maxX + minY - sy; dy = minY - minX + sx; } break; } pt[0] = dx; pt[1] = dy; } private static Rectangle mapRect(Rectangle rect, Rectangle sourceBounds, int type, boolean mapForwards) { int sMinX = sourceBounds.x; int sMinY = sourceBounds.y; int sMaxX = sMinX + sourceBounds.width - 1; int sMaxY = sMinY + sourceBounds.height - 1; int dMinX, dMinY, dMaxX, dMaxY; int[] pt = new int[2]; pt[0] = rect.x; pt[1] = rect.y; mapPoint(pt, sMinX, sMinY, sMaxX, sMaxY, type, mapForwards); dMinX = dMaxX = pt[0]; dMinY = dMaxY = pt[1]; pt[0] = rect.x + rect.width - 1; pt[1] = rect.y; mapPoint(pt, sMinX, sMinY, sMaxX, sMaxY, type, mapForwards); dMinX = Math.min(dMinX, pt[0]); dMinY = Math.min(dMinY, pt[1]); dMaxX = Math.max(dMaxX, pt[0]); dMaxY = Math.max(dMaxY, pt[1]); pt[0] = rect.x; pt[1] = rect.y + rect.height - 1; mapPoint(pt, sMinX, sMinY, sMaxX, sMaxY, type, mapForwards); dMinX = Math.min(dMinX, pt[0]); dMinY = Math.min(dMinY, pt[1]); dMaxX = Math.max(dMaxX, pt[0]); dMaxY = Math.max(dMaxY, pt[1]); pt[0] = rect.x + rect.width - 1; pt[1] = rect.y + rect.height - 1; mapPoint(pt, sMinX, sMinY, sMaxX, sMaxY, type, mapForwards); dMinX = Math.min(dMinX, pt[0]); dMinY = Math.min(dMinY, pt[1]); dMaxX = Math.max(dMaxX, pt[0]); dMaxY = Math.max(dMaxY, pt[1]); return new Rectangle(dMinX, dMinY, dMaxX - dMinX + 1, dMaxY - dMinY + 1); } public Raster computeTile(int tileX, int tileY) { // Create a new WritableRaster. Point org = new Point(tileXToX(tileX), tileYToY(tileY)); WritableRaster dest = createWritableRaster(sampleModel, org); // Output bounds are initially equal to the tile bounds. int destMinX = dest.getMinX(); int destMinY = dest.getMinY(); int destMaxX = destMinX + dest.getWidth(); int destMaxY = destMinY + dest.getHeight(); // Clip output bounds to the dest image bounds. Rectangle bounds = getBounds(); if (destMinX < bounds.x) { destMinX = bounds.x; } int boundsMaxX = bounds.x + bounds.width; if (destMaxX > boundsMaxX) { destMaxX = boundsMaxX; } if (destMinY < bounds.y) { destMinY = bounds.y; } int boundsMaxY = bounds.y + bounds.height; if (destMaxY > boundsMaxY) { destMaxY = boundsMaxY; } if (destMinX >= destMaxX || destMinY >= destMaxY) { return dest; // nothing to write } // Initialize the (possibly clipped) destination Rectangle. Rectangle destRect = new Rectangle(destMinX, destMinY, destMaxX - destMinX, destMaxY - destMinY); // Initialize X and Y split sequences with the dest bounds IntegerSequence xSplits = new IntegerSequence(destMinX, destMaxX); xSplits.insert(destMinX); xSplits.insert(destMaxX); IntegerSequence ySplits = new IntegerSequence(destMinY, destMaxY); ySplits.insert(destMinY); ySplits.insert(destMaxY); // Overlay the forward-mapped source tile grid PlanarImage src = getSource(0); int sMinX = src.getMinX(); int sMinY = src.getMinY(); int sWidth = src.getWidth(); int sHeight = src.getHeight(); int sMaxX = sMinX + sWidth - 1; int sMaxY = sMinY + sHeight - 1; int sTileWidth = src.getTileWidth(); int sTileHeight = src.getTileHeight(); int sTileGridXOffset = src.getTileGridXOffset(); int sTileGridYOffset = src.getTileGridYOffset(); int xStart = 0; int xGap = 0; int yStart = 0; int yGap = 0; // Insert splits from source image. // // We can think of the splits as forming an infinite sequence // xStart + kx*xGap, yStart + ky*yGap, where kx and ky range // over all integers, negative and positive. // Forward map the source tile grid origin Note that in cases // where an axis is "flipped" an adjustment must be made. For // example, consider flipping an image horizontally. // If the image has a tile X origin of 0, a tile width // of 50, and a total width of 100, then forward mapping the // points (0, 0) and (50, 0) yields the points (99, 0) and // (49, 0). In the original image, the tile split lines lay // to the left of the pixel; in the flipped image, they lie // to the right of the forward mapped pixels. Thus 1 must // be added to the forward mapped pixel position to get the // correct split location. int[] pt = new int[2]; pt[0] = sTileGridXOffset; pt[1] = sTileGridYOffset; mapPoint(pt, sMinX, sMinY, sMaxX, sMaxY, type, true); xStart = pt[0]; yStart = pt[1]; // Forward map the input tile size switch (type) { case 0: // FLIP_VERTICAL ++yStart; xGap = sTileWidth; yGap = sTileHeight; break; case 1: // FLIP_HORIZONTAL ++xStart; xGap = sTileWidth; yGap = sTileHeight; break; case 2: // FLIP_DIAGONAL xGap = sTileHeight; yGap = sTileWidth; break; case 3: // FLIP_ANTIDIAGONAL ++xStart; ++yStart; xGap = sTileHeight; yGap = sTileWidth; break; case 4: // ROTATE_90 ++xStart; xGap = sTileHeight; yGap = sTileWidth; break; case 5: // ROTATE_180 ++xStart; ++yStart; xGap = sTileWidth; yGap = sTileHeight; break; case 6: // ROTATE_270 ++yStart; xGap = sTileHeight; yGap = sTileWidth; break; } // Now we identify the source splits that intersect // the destination rectangle and merge them in. int kx = (int)Math.floor((double)(destMinX - xStart)/xGap); int xSplit = xStart + kx*xGap; while (xSplit < destMaxX) { xSplits.insert(xSplit); xSplit += xGap; } int ky = (int)Math.floor((double)(destMinY - yStart)/yGap); int ySplit = yStart + ky*yGap; while (ySplit < destMaxY) { ySplits.insert(ySplit); ySplit += yGap; } // Allocate memory for source Rasters. Raster[] sources = new Raster[1]; // // Divide destRect into sub rectangles based on the source // splits, and compute each sub rectangle separately. // int x1, x2, y1, y2, w, h; Rectangle subRect = new Rectangle(); ySplits.startEnumeration(); for (y1 = ySplits.nextElement(); ySplits.hasMoreElements(); y1 = y2) { y2 = ySplits.nextElement(); h = y2 - y1; xSplits.startEnumeration(); for (x1 = xSplits.nextElement(); xSplits.hasMoreElements(); x1 = x2) { x2 = xSplits.nextElement(); w = x2 - x1; // Get sources // Backwards map the starting destination point pt[0] = x1; pt[1] = y1; mapPoint(pt, sMinX, sMinY, sMaxX, sMaxY, type, false); // Determine the source tile involved int tx = src.XToTileX(pt[0]); int ty = src.YToTileY(pt[1]); sources[0] = src.getTile(tx, ty); subRect.x = x1; subRect.y = y1; subRect.width = w; subRect.height = h; computeRect(sources, dest, subRect); } } return dest; } protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); Raster src = sources[0]; // // Get the minX, minY, width & height of sources raster // PlanarImage source = getSource(0); int sMinX = source.getMinX(); int sMinY = source.getMinY(); int sWidth = source.getWidth(); int sHeight = source.getHeight(); int sMaxX = sMinX + sWidth - 1; int sMaxY = sMinY + sHeight - 1; int translateX = src.getSampleModelTranslateX(); int translateY = src.getSampleModelTranslateY(); // // Get data for the source rectangle & the destination rectangle Rectangle srcRect = src.getBounds(); RasterAccessor srcAccessor = new RasterAccessor(src, srcRect, formatTags[0], getSource(0).getColorModel()); RasterAccessor dstAccessor = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); int incr1 = 0, incr2 = 0, s_x = 0, s_y = 0; int srcPixelStride = srcAccessor.getPixelStride(); int srcScanlineStride = srcAccessor.getScanlineStride(); // Backwards map starting point of destination rectangle int[] pt = new int[2]; pt[0] = destRect.x; pt[1] = destRect.y; mapPoint(pt, sMinX, sMinY, sMaxX, sMaxY, type, false); s_x = pt[0]; s_y = pt[1]; // Determine source stride along dest row (incr1) and column (incr2) switch (type) { case 0: // FLIP_VERTICAL incr1 = srcPixelStride; incr2 = -srcScanlineStride; break; case 1: // FLIP_HORIZONTAL incr1 = -srcPixelStride; incr2 = srcScanlineStride; break; case 2: // FLIP_DIAGONAL; incr1 = srcScanlineStride; incr2 = srcPixelStride; break; case 3: // FLIP_ANTIDIAGONAL incr1 = -srcScanlineStride; incr2 = -srcPixelStride; break; case 4: // ROTATE_90 incr1 = -srcScanlineStride; incr2 = srcPixelStride; break; case 5: // ROTATE_180 incr1 = -srcPixelStride; incr2 = -srcScanlineStride; break; case 6: // ROTATE_270 incr1 = srcScanlineStride; incr2 = -srcPixelStride; break; } switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(srcAccessor, destRect, translateX, translateY, dstAccessor, incr1, incr2, s_x, s_y); break; case DataBuffer.TYPE_INT: intLoop(srcAccessor, destRect, translateX, translateY, dstAccessor, incr1, incr2, s_x, s_y); break; case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_USHORT: shortLoop(srcAccessor, destRect, translateX, translateY, dstAccessor, incr1, incr2, s_x, s_y); break; case DataBuffer.TYPE_FLOAT: floatLoop(srcAccessor, destRect, translateX, translateY, dstAccessor, incr1, incr2, s_x, s_y); break; case DataBuffer.TYPE_DOUBLE: doubleLoop(srcAccessor, destRect, translateX, translateY, dstAccessor, incr1, incr2, s_x, s_y); break; } // // If the RasterAccessor object set up a temporary buffer for the // op to write to, tell the RasterAccessor to write that data // to the raster, that we're done with it. // if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } private void byteLoop(RasterAccessor src, Rectangle destRect, int srcTranslateX, int srcTranslateY, RasterAccessor dst, int incr1, int incr2, int s_x, int s_y) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); byte dstDataArrays[][] = dst.getByteDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); byte srcDataArrays[][] = src.getByteDataArrays(); int bandOffsets[] = src.getOffsetsForBands(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; // Translate to/from SampleModel space & Raster space int posy = (s_y - srcTranslateY) * srcScanlineStride; int posx = (s_x - srcTranslateX) * srcPixelStride; int srcScanlineOffset = posx + posy; int dstScanlineOffset = 0; // loop around for (int y = dst_min_y; y < dst_max_y; y++) { for (int k2=0; k2 < dst_num_bands; k2++) { byte[] srcDataArray = srcDataArrays[k2]; byte[] dstDataArray = dstDataArrays[k2]; int dstPixelOffset = dstScanlineOffset + dstBandOffsets[k2]; int srcPixelOffset = srcScanlineOffset + bandOffsets[k2]; for (int x = dst_min_x; x < dst_max_x; x++) { dstDataArray[dstPixelOffset] = srcDataArray[srcPixelOffset]; srcPixelOffset += incr1; // Go to next pixel dstPixelOffset += dstPixelStride; } } srcScanlineOffset += incr2; // Got the next line in the destination rectangle dstScanlineOffset += dstScanlineStride; } } private void intLoop(RasterAccessor src, Rectangle destRect, int srcTranslateX, int srcTranslateY, RasterAccessor dst, int incr1, int incr2, int s_x, int s_y) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); int dstDataArrays[][] = dst.getIntDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcDataArrays[][] = src.getIntDataArrays(); int bandOffsets[] = src.getOffsetsForBands(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; // Translate to/from SampleModel space & Raster space int posy = (s_y - srcTranslateY) * srcScanlineStride; int posx = (s_x - srcTranslateX) * srcPixelStride; int srcScanlineOffset = posx + posy; int dstScanlineOffset = 0; // loop around for (int y = dst_min_y; y < dst_max_y; y++) { for (int k2=0; k2 < dst_num_bands; k2++) { int[] srcDataArray = srcDataArrays[k2]; int[] dstDataArray = dstDataArrays[k2]; int dstPixelOffset = dstScanlineOffset + dstBandOffsets[k2]; int srcPixelOffset = srcScanlineOffset + bandOffsets[k2]; for (int x = dst_min_x; x < dst_max_x; x++) { dstDataArray[dstPixelOffset] = srcDataArray[srcPixelOffset]; srcPixelOffset += incr1; // Go to next pixel dstPixelOffset += dstPixelStride; } } srcScanlineOffset += incr2; // Got the next line in the destination rectangle dstScanlineOffset += dstScanlineStride; } } private void shortLoop(RasterAccessor src, Rectangle destRect, int srcTranslateX, int srcTranslateY, RasterAccessor dst, int incr1, int incr2, int s_x, int s_y) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int bandOffsets[] = src.getOffsetsForBands(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; // Translate to/from SampleModel space & Raster space int posy = (s_y - srcTranslateY) * srcScanlineStride; int posx = (s_x - srcTranslateX) * srcPixelStride; int srcScanlineOffset = posx + posy; int dstScanlineOffset = 0; // loop around for (int y = dst_min_y; y < dst_max_y; y++) { for (int k2=0; k2 < dst_num_bands; k2++) { short[] srcDataArray = srcDataArrays[k2]; short[] dstDataArray = dstDataArrays[k2]; int dstPixelOffset = dstScanlineOffset + dstBandOffsets[k2]; int srcPixelOffset = srcScanlineOffset + bandOffsets[k2]; for (int x = dst_min_x; x < dst_max_x; x++) { dstDataArray[dstPixelOffset] = srcDataArray[srcPixelOffset]; srcPixelOffset += incr1; // Go to next pixel dstPixelOffset += dstPixelStride; } } srcScanlineOffset += incr2; // Got the next line in the destination rectangle dstScanlineOffset += dstScanlineStride; } } private void floatLoop(RasterAccessor src, Rectangle destRect, int srcTranslateX, int srcTranslateY, RasterAccessor dst, int incr1, int incr2, int s_x, int s_y) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); float dstDataArrays[][] = dst.getFloatDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); float srcDataArrays[][] = src.getFloatDataArrays(); int bandOffsets[] = src.getOffsetsForBands(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; // Translate to/from SampleModel space & Raster space int posy = (s_y - srcTranslateY) * srcScanlineStride; int posx = (s_x - srcTranslateX) * srcPixelStride; int srcScanlineOffset = posx + posy; int dstScanlineOffset = 0; // loop around for (int y = dst_min_y; y < dst_max_y; y++) { for (int k2=0; k2 < dst_num_bands; k2++) { float[] srcDataArray = srcDataArrays[k2]; float[] dstDataArray = dstDataArrays[k2]; int dstPixelOffset = dstScanlineOffset + dstBandOffsets[k2]; int srcPixelOffset = srcScanlineOffset + bandOffsets[k2]; for (int x = dst_min_x; x < dst_max_x; x++) { dstDataArray[dstPixelOffset] = srcDataArray[srcPixelOffset]; srcPixelOffset += incr1; // Go to next pixel dstPixelOffset += dstPixelStride; } } srcScanlineOffset += incr2; // Got the next line in the destination rectangle dstScanlineOffset += dstScanlineStride; } } private void doubleLoop(RasterAccessor src, Rectangle destRect, int srcTranslateX, int srcTranslateY, RasterAccessor dst, int incr1, int incr2, int s_x, int s_y) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); double dstDataArrays[][] = dst.getDoubleDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); double srcDataArrays[][] = src.getDoubleDataArrays(); int bandOffsets[] = src.getOffsetsForBands(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; // Translate to/from SampleModel space & Raster space int posy = (s_y - srcTranslateY) * srcScanlineStride; int posx = (s_x - srcTranslateX) * srcPixelStride; int srcScanlineOffset = posx + posy; int dstScanlineOffset = 0; // loop around for (int y = dst_min_y; y < dst_max_y; y++) { for (int k2=0; k2 < dst_num_bands; k2++) { double[] srcDataArray = srcDataArrays[k2]; double[] dstDataArray = dstDataArrays[k2]; int dstPixelOffset = dstScanlineOffset + dstBandOffsets[k2]; int srcPixelOffset = srcScanlineOffset + bandOffsets[k2]; for (int x = dst_min_x; x < dst_max_x; x++) { dstDataArray[dstPixelOffset] = srcDataArray[srcPixelOffset]; srcPixelOffset += incr1; // Go to next pixel dstPixelOffset += dstPixelStride; } } srcScanlineOffset += incr2; // Got the next line in the destination rectangle dstScanlineOffset += dstScanlineStride; } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/EncodeRIF.java0000644000175000017500000000466310203035544025642 0ustar mathieumathieu/* * $RCSfile: EncodeRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:24 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import java.io.OutputStream; import java.io.IOException; import javax.media.jai.RenderedOp; import javax.media.jai.util.ImagingListener; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.ImageEncodeParam; import com.sun.media.jai.codec.ImageEncoder; import com.sun.media.jai.util.ImageUtil; /** * @see javax.media.jai.operator.FileDescriptor * * @since EA4 * */ public class EncodeRIF implements RenderedImageFactory { /** Constructor. */ public EncodeRIF() {} /** * Stores an image to a stream. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { ImagingListener listener = ImageUtil.getImagingListener(renderHints); // Retrieve the OutputStream. OutputStream stream = (OutputStream)paramBlock.getObjectParameter(0); // Retrieve the format. String format = (String)paramBlock.getObjectParameter(1); // Retrieve the ImageEncodeParam (which may be null). ImageEncodeParam param = null; if(paramBlock.getNumParameters() > 2) { param = (ImageEncodeParam)paramBlock.getObjectParameter(2); } // Create an ImageEncoder. ImageEncoder encoder = ImageCodec.createImageEncoder(format, stream, param); // Check the ImageEncoder. if(encoder == null) { throw new RuntimeException(JaiI18N.getString("EncodeRIF0")); } // Store the data. RenderedImage im = (RenderedImage)paramBlock.getSource(0); try { encoder.encode(im); stream.flush(); // Fix 4665208: EncodeRIF closed the stream after flush // User may put more into the stream //stream.close(); } catch (IOException e) { String message = JaiI18N.getString("EncodeRIF1") + " " + format; listener.errorOccurred(message, e, this, false); // e.printStackTrace(); return null; } return im; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/AffineBicubicOpImage.java0000644000175000017500000017520510203035544030020 0ustar mathieumathieu/* * $RCSfile: AffineBicubicOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:12 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.geom.Point2D; import java.awt.geom.AffineTransform; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationBicubic; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage subclass that performs bicubic Affine mapping */ final class AffineBicubicOpImage extends AffineOpImage { /** * Constructs an AffineBicubicOpImage from a RenderedImage source, * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param interp an Interpolation object to use for resampling * @param transform the desired AffineTransform. */ public AffineBicubicOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, AffineTransform transform, Interpolation interp, double[] backgroundValues) { super(source, extender, config, layout, transform, interp, backgroundValues); } /** * Performs an affine transform on a specified rectangle. The sources are * cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster [] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); Raster source = sources[0]; Rectangle srcRect = source.getBounds(); int srcRectX = srcRect.x; int srcRectY = srcRect.y; // // Get data for the source rectangle & the destination rectangle // In the first version source Rectangle is the whole source // image always. // // See if we can cache the source to avoid multiple rasteraccesors // RasterAccessor srcAccessor = new RasterAccessor(source, srcRect, formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor dstAccessor = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(srcAccessor, destRect, srcRectX, srcRectY, dstAccessor); break; case DataBuffer.TYPE_INT: intLoop(srcAccessor, destRect, srcRectX, srcRectY, dstAccessor); break; case DataBuffer.TYPE_SHORT: shortLoop(srcAccessor, destRect, srcRectX, srcRectY, dstAccessor); break; case DataBuffer.TYPE_USHORT: ushortLoop(srcAccessor, destRect, srcRectX, srcRectY, dstAccessor); break; case DataBuffer.TYPE_FLOAT: floatLoop(srcAccessor, destRect, srcRectX, srcRectY, dstAccessor); break; case DataBuffer.TYPE_DOUBLE: doubleLoop(srcAccessor, destRect, srcRectX, srcRectY, dstAccessor); break; } // If the RasterAccessor object set up a temporary buffer for the // op to write to, tell the RasterAccessor to write that data // to the raster, that we're done with it. if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } private void byteLoop(RasterAccessor src, Rectangle destRect, int srcRectX, int srcRectY, RasterAccessor dst) { float src_rect_x1 = src.getX(); float src_rect_y1 = src.getY(); float src_rect_x2 = src_rect_x1 + src.getWidth(); float src_rect_y2 = src_rect_y1 + src.getHeight(); float s_x, s_y; float fracx, fracy; float float_fracx, float_fracy; float frac_xx, frac_yy; int s_ix, s_iy; int p_x, p_y; int p__, p0_, p1_, p2_; int p_0, p00, p01, p02; int p_1, p10, p11, p12; int p_2, p20, p21, p22; int s__, s0_, s1_, s2_; int s_0, s00, s01, s02; int s_1, s10, s11, s12; int s_2, s20, s21, s22; float s0, s1, s_, s2; float q_, q0, q1, q2; float s, q; int result; int dstPixelOffset; int dstOffset = 0; Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); byte dstDataArrays[][] = dst.getByteDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); byte srcDataArrays[][] = src.getByteDataArrays(); int bandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; byte[] backgroundByte = new byte[dst_num_bands]; for (int i = 0; i < dst_num_bands; i++) backgroundByte[i] = (byte)backgroundValues[i]; for (int y = dst_min_y; y < dst_max_y ; y++) { dstPixelOffset = dstOffset; // Backward map the first point in the line // The energy is at the (pt_x + 0.5, pt_y + 0.5) dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates s_x = (float) src_pt.getX(); s_y = (float) src_pt.getY(); // As per definition of bicubic interpolation s_x -= 0.5; s_y -= 0.5; // Floor to get the integral coordinate s_ix = (int) Math.floor(s_x); s_iy = (int) Math.floor(s_y); fracx = s_x - (float) s_ix; fracy = s_y - (float) s_iy; // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; // // Get the 16 neighbouring positions of the // coordinate in question (p00). // // p__ p0_ p1_ p2_ // p_0 p00 p10 p20 // p_1 p01 p11 p21 // p_2 p02 p12 p22 // p__ = p_x + p_y - srcScanlineStride - srcPixelStride; p0_ = p__ + srcPixelStride; p1_ = p0_ + srcPixelStride; p2_ = p1_ + srcPixelStride; p_0 = p__ + srcScanlineStride; p00 = p_0 + srcPixelStride; p10 = p00 + srcPixelStride; p20 = p10 + srcPixelStride; p_1 = p_0 + srcScanlineStride; p01 = p_1 + srcPixelStride; p11 = p01 + srcPixelStride; p21 = p11 + srcPixelStride; p_2 = p_1 + srcScanlineStride; p02 = p_2 + srcPixelStride; p12 = p02 + srcPixelStride; p22 = p12 + srcPixelStride; for (int x = dst_min_x; x < dst_max_x; x++) { // // Check against the source rectangle // if ((s_ix >= src_rect_x1 + 1) && (s_ix < (src_rect_x2 - 2)) && (s_iy >= (src_rect_y1 + 1)) && (s_iy < (src_rect_y2 - 2))) { for (int k2=0; k2 < dst_num_bands; k2++) { // // Get the pixels // byte tmp_row[]; int tmp_col; tmp_row = srcDataArrays[k2]; tmp_col = bandOffsets[k2]; s__ = tmp_row[p__ + tmp_col] & 0xff; s0_ = tmp_row[p0_ + tmp_col] & 0xff; s1_ = tmp_row[p1_ + tmp_col] & 0xff; s2_ = tmp_row[p2_ + tmp_col] & 0xff; s_0 = tmp_row[p_0 + tmp_col] & 0xff; s00 = tmp_row[p00 + tmp_col] & 0xff; s10 = tmp_row[p10 + tmp_col] & 0xff; s20 = tmp_row[p20 + tmp_col] & 0xff; s_1 = tmp_row[p_1 + tmp_col] & 0xff; s01 = tmp_row[p01 + tmp_col] & 0xff; s11 = tmp_row[p11 + tmp_col] & 0xff; s21 = tmp_row[p21 + tmp_col] & 0xff; s_2 = tmp_row[p_2 + tmp_col] & 0xff; s02 = tmp_row[p02 + tmp_col] & 0xff; s12 = tmp_row[p12 + tmp_col] & 0xff; s22 = tmp_row[p22 + tmp_col] & 0xff; // Get the new frac values float_fracx = fracx; float_fracy = fracy; frac_xx = float_fracx * (1.0F - float_fracx); frac_yy = float_fracx * (1.0F - float_fracy); s0 = s00 + ((s10 - s00) * float_fracx); s1 = s01 + ((s11 - s01) * float_fracx); s_ = s0_ + ((s1_ - s0_) * float_fracx); s2 = s02 + ((s12 - s02) * float_fracx); q_ = (s1_ + s__) + (((s2_ + s0_) - (s1_ + s__)) * float_fracx); q0 = (s10 + s_0) + (((s20 + s00) - (s10 + s_0)) * float_fracx); q1 = (s11 + s_1) + ((s21 + s01) - (s11 + s_1)) * float_fracx; q2 = (s12 + s_2) + (((s22 + s02) - (s12 + s_2)) * float_fracx); q_ = s_ - q_ / 2.0F; q0 = s0 - q0 / 2.0F; q1 = s1 - q1 / 2.0F; q2 = s2 - q2 / 2.0F; s_ += (q_ * frac_xx); s0 += (q0 * frac_xx); s1 += (q1 * frac_xx); s2 += (q2 * frac_xx); s = s0 + ((s1 - s0) * float_fracy); q = (s1 + s_) + (((s2 + s0) - (s1 + s_)) * float_fracy); q = s - q / 2.0F; s += (q * frac_yy); // Round if (s < 0.5F) { result = 0; } else if (s > 254.5F) { result = 255; } else { result = (int) (s + 0.5F); } // write the result dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = (byte) (result & 0xff); } } else if (setBackground) { for (int k=0; k < dst_num_bands; k++) dstDataArrays[k][dstPixelOffset+dstBandOffsets[k]] = backgroundByte[k]; } // walk if (fracx < fracdx1) { s_ix += incx; fracx += fracdx; } else { s_ix += incx1; fracx -= fracdx1; } if (fracy < fracdy1) { s_iy += incy; fracy += fracdy; } else { s_iy += incy1; fracy -= fracdy1; } // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; // // Get the 16 neighbouring positions of the // coordinate in question (p00). // // p__ p0_ p1_ p2_ // p_0 p00 p10 p20 // p_1 p01 p11 p21 // p_2 p02 p12 p22 // p__ = p_x + p_y - srcScanlineStride - srcPixelStride; p0_ = p__ + srcPixelStride; p1_ = p0_ + srcPixelStride; p2_ = p1_ + srcPixelStride; p_0 = p__ + srcScanlineStride; p00 = p_0 + srcPixelStride; p10 = p00 + srcPixelStride; p20 = p10 + srcPixelStride; p_1 = p_0 + srcScanlineStride; p01 = p_1 + srcPixelStride; p11 = p01 + srcPixelStride; p21 = p11 + srcPixelStride; p_2 = p_1 + srcScanlineStride; p02 = p_2 + srcPixelStride; p12 = p02 + srcPixelStride; p22 = p12 + srcPixelStride; dstPixelOffset += dstPixelStride; } dstOffset += dstScanlineStride; } } private void intLoop(RasterAccessor src, Rectangle destRect, int srcRectX, int srcRectY, RasterAccessor dst) { float src_rect_x1 = src.getX(); float src_rect_y1 = src.getY(); float src_rect_x2 = src_rect_x1 + src.getWidth(); float src_rect_y2 = src_rect_y1 + src.getHeight(); float s_x, s_y; float fracx, fracy; float float_fracx, float_fracy; float frac_xx, frac_yy; int s_ix, s_iy; int p_x, p_y; int p__, p0_, p1_, p2_; int p_0, p00, p01, p02; int p_1, p10, p11, p12; int p_2, p20, p21, p22; int s__, s0_, s1_, s2_; int s_0, s00, s01, s02; int s_1, s10, s11, s12; int s_2, s20, s21, s22; float s0, s1, s_, s2; float q_, q0, q1, q2; float s, q; int result; int dstPixelOffset; int dstOffset = 0; Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); int dstDataArrays[][] = dst.getIntDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcDataArrays[][] = src.getIntDataArrays(); int bandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; int[] backgroundInt = new int[dst_num_bands]; for (int i = 0; i < dst_num_bands; i++) backgroundInt[i] = (int)backgroundValues[i]; for (int y = dst_min_y; y < dst_max_y ; y++) { dstPixelOffset = dstOffset; // Backward map the first point in the line // The energy is at the (pt_x + 0.5, pt_y + 0.5) dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates s_x = (float) src_pt.getX(); s_y = (float) src_pt.getY(); // As per definition of bicubic interpolation s_x -= 0.5; s_y -= 0.5; // Floor to get the integral coordinate s_ix = (int) Math.floor(s_x); s_iy = (int) Math.floor(s_y); fracx = s_x - (float) s_ix; fracy = s_y - (float) s_iy; // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; // // Get the 16 neighbouring positions of the // coordinate in question (p00). // // p__ p0_ p1_ p2_ // p_0 p00 p10 p20 // p_1 p01 p11 p21 // p_2 p02 p12 p22 // p__ = p_x + p_y - srcScanlineStride - srcPixelStride; p0_ = p__ + srcPixelStride; p1_ = p0_ + srcPixelStride; p2_ = p1_ + srcPixelStride; p_0 = p__ + srcScanlineStride; p00 = p_0 + srcPixelStride; p10 = p00 + srcPixelStride; p20 = p10 + srcPixelStride; p_1 = p_0 + srcScanlineStride; p01 = p_1 + srcPixelStride; p11 = p01 + srcPixelStride; p21 = p11 + srcPixelStride; p_2 = p_1 + srcScanlineStride; p02 = p_2 + srcPixelStride; p12 = p02 + srcPixelStride; p22 = p12 + srcPixelStride; for (int x = dst_min_x; x < dst_max_x; x++) { // // Check against the source rectangle // if ((s_ix >= (src_rect_x1 + 1)) && (s_ix < (src_rect_x2 - 2)) && (s_iy >= (src_rect_y1 + 1)) && (s_iy < (src_rect_y2 - 2))) { for (int k2=0; k2 < dst_num_bands; k2++) { // // Get the pixels // int tmp_row[]; int tmp_col; tmp_row = srcDataArrays[k2]; tmp_col = bandOffsets[k2]; s__ = tmp_row[p__ + tmp_col]; s0_ = tmp_row[p0_ + tmp_col]; s1_ = tmp_row[p1_ + tmp_col]; s2_ = tmp_row[p2_ + tmp_col]; s_0 = tmp_row[p_0 + tmp_col]; s00 = tmp_row[p00 + tmp_col]; s10 = tmp_row[p10 + tmp_col]; s20 = tmp_row[p20 + tmp_col]; s_1 = tmp_row[p_1 + tmp_col]; s01 = tmp_row[p01 + tmp_col]; s11 = tmp_row[p11 + tmp_col]; s21 = tmp_row[p21 + tmp_col]; s_2 = tmp_row[p_2 + tmp_col]; s02 = tmp_row[p02 + tmp_col]; s12 = tmp_row[p12 + tmp_col]; s22 = tmp_row[p22 + tmp_col]; // Get the new frac values float_fracx = fracx; float_fracy = fracy; frac_xx = float_fracx * (1.0F - float_fracx); frac_yy = float_fracx * (1.0F - float_fracy); s0 = s00 + ((s10 - s00) * float_fracx); s1 = s01 + ((s11 - s01) * float_fracx); s_ = s0_ + ((s1_ - s0_) * float_fracx); s2 = s02 + ((s12 - s02) * float_fracx); q_ = (s1_ + s__) + (((s2_ + s0_) - (s1_ + s__)) * float_fracx); q0 = (s10 + s_0) + (((s20 + s00) - (s10 + s_0)) * float_fracx); q1 = (s11 + s_1) + ((s21 + s01) - (s11 + s_1)) * float_fracx; q2 = (s12 + s_2) + (((s22 + s02) - (s12 + s_2)) * float_fracx); q_ = s_ - q_ / 2.0F; q0 = s0 - q0 / 2.0F; q1 = s1 - q1 / 2.0F; q2 = s2 - q2 / 2.0F; s_ += (q_ * frac_xx); s0 += (q0 * frac_xx); s1 += (q1 * frac_xx); s2 += (q2 * frac_xx); s = s0 + ((s1 - s0) * float_fracy); q = (s1 + s_) + (((s2 + s0) - (s1 + s_)) * float_fracy); q = s - q / 2.0F; s += (q * frac_yy); // Round the result if (s < (float)(Integer.MIN_VALUE)) { result = Integer.MIN_VALUE; } else if (s > (float)(Integer.MAX_VALUE)) { result = Integer.MAX_VALUE; } else if (s > 0.0) { result = (int) (s + 0.5F); } else { result = (int) (s - 0.5F); } // write the result dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = result; } } else if (setBackground) { for (int k=0; k < dst_num_bands; k++) dstDataArrays[k][dstPixelOffset+dstBandOffsets[k]] = backgroundInt[k]; } // walk if (fracx < fracdx1) { s_ix += incx; fracx += fracdx; } else { s_ix += incx1; fracx -= fracdx1; } if (fracy < fracdy1) { s_iy += incy; fracy += fracdy; } else { s_iy += incy1; fracy -= fracdy1; } // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; // // Get the 16 neighbouring positions of the // coordinate in question (p00). // // p__ p0_ p1_ p2_ // p_0 p00 p10 p20 // p_1 p01 p11 p21 // p_2 p02 p12 p22 // p__ = p_x + p_y - srcScanlineStride - srcPixelStride; p0_ = p__ + srcPixelStride; p1_ = p0_ + srcPixelStride; p2_ = p1_ + srcPixelStride; p_0 = p__ + srcScanlineStride; p00 = p_0 + srcPixelStride; p10 = p00 + srcPixelStride; p20 = p10 + srcPixelStride; p_1 = p_0 + srcScanlineStride; p01 = p_1 + srcPixelStride; p11 = p01 + srcPixelStride; p21 = p11 + srcPixelStride; p_2 = p_1 + srcScanlineStride; p02 = p_2 + srcPixelStride; p12 = p02 + srcPixelStride; p22 = p12 + srcPixelStride; dstPixelOffset += dstPixelStride; } dstOffset += dstScanlineStride; } } private void shortLoop(RasterAccessor src, Rectangle destRect, int srcRectX, int srcRectY, RasterAccessor dst) { float src_rect_x1 = src.getX(); float src_rect_y1 = src.getY(); float src_rect_x2 = src_rect_x1 + src.getWidth(); float src_rect_y2 = src_rect_y1 + src.getHeight(); float s_x, s_y; float fracx, fracy; float float_fracx, float_fracy; float frac_xx, frac_yy; int s_ix, s_iy; int p_x, p_y; int p__, p0_, p1_, p2_; int p_0, p00, p01, p02; int p_1, p10, p11, p12; int p_2, p20, p21, p22; short s__, s0_, s1_, s2_; short s_0, s00, s01, s02; short s_1, s10, s11, s12; short s_2, s20, s21, s22; float s0, s1, s_, s2; float q_, q0, q1, q2; float s, q; short result; int dstPixelOffset; int dstOffset = 0; Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int bandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; short[] backgroundShort = new short[dst_num_bands]; for (int i = 0; i < dst_num_bands; i++) backgroundShort[i] = (short)backgroundValues[i]; for (int y = dst_min_y; y < dst_max_y ; y++) { dstPixelOffset = dstOffset; // Backward map the first point in the line // The energy is at the (pt_x + 0.5, pt_y + 0.5) dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates s_x = (float) src_pt.getX(); s_y = (float) src_pt.getY(); // As per definition of bicubic interpolation s_x -= 0.5; s_y -= 0.5; // Floor to get the integral coordinate s_ix = (int) Math.floor(s_x); s_iy = (int) Math.floor(s_y); fracx = s_x - (float) s_ix; fracy = s_y - (float) s_iy; // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; // // Get the 16 neighbouring positions of the // coordinate in question (p00). // // p__ p0_ p1_ p2_ // p_0 p00 p10 p20 // p_1 p01 p11 p21 // p_2 p02 p12 p22 // p__ = p_x + p_y - srcScanlineStride - srcPixelStride; p0_ = p__ + srcPixelStride; p1_ = p0_ + srcPixelStride; p2_ = p1_ + srcPixelStride; p_0 = p__ + srcScanlineStride; p00 = p_0 + srcPixelStride; p10 = p00 + srcPixelStride; p20 = p10 + srcPixelStride; p_1 = p_0 + srcScanlineStride; p01 = p_1 + srcPixelStride; p11 = p01 + srcPixelStride; p21 = p11 + srcPixelStride; p_2 = p_1 + srcScanlineStride; p02 = p_2 + srcPixelStride; p12 = p02 + srcPixelStride; p22 = p12 + srcPixelStride; for (int x = dst_min_x; x < dst_max_x; x++) { // // Check against the source rectangle // if ((s_ix >= (src_rect_x1 + 1)) && (s_ix < (src_rect_x2 - 2)) && (s_iy >= (src_rect_y1 + 1)) && (s_iy < (src_rect_y2 - 2))) { for (int k2=0; k2 < dst_num_bands; k2++) { // // Get the pixels // short tmp_row[]; int tmp_col; tmp_row = srcDataArrays[k2]; tmp_col = bandOffsets[k2]; s__ = tmp_row[p__ + tmp_col]; s0_ = tmp_row[p0_ + tmp_col]; s1_ = tmp_row[p1_ + tmp_col]; s2_ = tmp_row[p2_ + tmp_col]; s_0 = tmp_row[p_0 + tmp_col]; s00 = tmp_row[p00 + tmp_col]; s10 = tmp_row[p10 + tmp_col]; s20 = tmp_row[p20 + tmp_col]; s_1 = tmp_row[p_1 + tmp_col]; s01 = tmp_row[p01 + tmp_col]; s11 = tmp_row[p11 + tmp_col]; s21 = tmp_row[p21 + tmp_col]; s_2 = tmp_row[p_2 + tmp_col]; s02 = tmp_row[p02 + tmp_col]; s12 = tmp_row[p12 + tmp_col]; s22 = tmp_row[p22 + tmp_col]; // Get the new frac values float_fracx = fracx; float_fracy = fracy; frac_xx = float_fracx * (1.0F - float_fracx); frac_yy = float_fracx * (1.0F - float_fracy); s0 = s00 + ((s10 - s00) * float_fracx); s1 = s01 + ((s11 - s01) * float_fracx); s_ = s0_ + ((s1_ - s0_) * float_fracx); s2 = s02 + ((s12 - s02) * float_fracx); q_ = (s1_ + s__) + (((s2_ + s0_) - (s1_ + s__)) * float_fracx); q0 = (s10 + s_0) + (((s20 + s00) - (s10 + s_0)) * float_fracx); q1 = (s11 + s_1) + ((s21 + s01) - (s11 + s_1)) * float_fracx; q2 = (s12 + s_2) + (((s22 + s02) - (s12 + s_2)) * float_fracx); q_ = s_ - q_ / 2.0F; q0 = s0 - q0 / 2.0F; q1 = s1 - q1 / 2.0F; q2 = s2 - q2 / 2.0F; s_ += (q_ * frac_xx); s0 += (q0 * frac_xx); s1 += (q1 * frac_xx); s2 += (q2 * frac_xx); s = s0 + ((s1 - s0) * float_fracy); q = (s1 + s_) + (((s2 + s0) - (s1 + s_)) * float_fracy); q = s - q / 2.0F; s += (q * frac_yy); // Round the result if (s < (float)Short.MIN_VALUE) { result = Short.MIN_VALUE; } else if (s > (float)Short.MAX_VALUE) { result = Short.MAX_VALUE; } else if (s > 0.0) { result = (short) (s + 0.5F); } else { result = (short) (s - 0.5F); } // write the result dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = result; } } else if (setBackground) { for (int k=0; k < dst_num_bands; k++) dstDataArrays[k][dstPixelOffset+dstBandOffsets[k]] = backgroundShort[k]; } // walk if (fracx < fracdx1) { s_ix += incx; fracx += fracdx; } else { s_ix += incx1; fracx -= fracdx1; } if (fracy < fracdy1) { s_iy += incy; fracy += fracdy; } else { s_iy += incy1; fracy -= fracdy1; } // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; // // Get the 16 neighbouring positions of the // coordinate in question (p00). // // p__ p0_ p1_ p2_ // p_0 p00 p10 p20 // p_1 p01 p11 p21 // p_2 p02 p12 p22 // p__ = p_x + p_y - srcScanlineStride - srcPixelStride; p0_ = p__ + srcPixelStride; p1_ = p0_ + srcPixelStride; p2_ = p1_ + srcPixelStride; p_0 = p__ + srcScanlineStride; p00 = p_0 + srcPixelStride; p10 = p00 + srcPixelStride; p20 = p10 + srcPixelStride; p_1 = p_0 + srcScanlineStride; p01 = p_1 + srcPixelStride; p11 = p01 + srcPixelStride; p21 = p11 + srcPixelStride; p_2 = p_1 + srcScanlineStride; p02 = p_2 + srcPixelStride; p12 = p02 + srcPixelStride; p22 = p12 + srcPixelStride; dstPixelOffset += dstPixelStride; } dstOffset += dstScanlineStride; } } private void ushortLoop(RasterAccessor src, Rectangle destRect, int srcRectX, int srcRectY, RasterAccessor dst) { float src_rect_x1 = src.getX(); float src_rect_y1 = src.getY(); float src_rect_x2 = src_rect_x1 + src.getWidth(); float src_rect_y2 = src_rect_y1 + src.getHeight(); float s_x, s_y; float fracx, fracy; float float_fracx, float_fracy; float frac_xx, frac_yy; int s_ix, s_iy; int p_x, p_y; int p__, p0_, p1_, p2_; int p_0, p00, p01, p02; int p_1, p10, p11, p12; int p_2, p20, p21, p22; int s__, s0_, s1_, s2_; int s_0, s00, s01, s02; int s_1, s10, s11, s12; int s_2, s20, s21, s22; float s0, s1, s_, s2; float q_, q0, q1, q2; float s, q; int result; int dstPixelOffset; int dstOffset = 0; Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int bandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; short[] backgroundUShort = new short[dst_num_bands]; for (int i = 0; i < dst_num_bands; i++) backgroundUShort[i] = (short)backgroundValues[i]; for (int y = dst_min_y; y < dst_max_y ; y++) { dstPixelOffset = dstOffset; // Backward map the first point in the line // The energy is at the (pt_x + 0.5, pt_y + 0.5) dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates s_x = (float) src_pt.getX(); s_y = (float) src_pt.getY(); // As per definition of bicubic interpolation s_x -= 0.5; s_y -= 0.5; // Floor to get the integral coordinate s_ix = (int) Math.floor(s_x); s_iy = (int) Math.floor(s_y); fracx = s_x - (float) s_ix; fracy = s_y - (float) s_iy; // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; // // Get the 16 neighbouring positions of the // coordinate in question (p00). // // p__ p0_ p1_ p2_ // p_0 p00 p10 p20 // p_1 p01 p11 p21 // p_2 p02 p12 p22 // p__ = p_x + p_y - srcScanlineStride - srcPixelStride; p0_ = p__ + srcPixelStride; p1_ = p0_ + srcPixelStride; p2_ = p1_ + srcPixelStride; p_0 = p__ + srcScanlineStride; p00 = p_0 + srcPixelStride; p10 = p00 + srcPixelStride; p20 = p10 + srcPixelStride; p_1 = p_0 + srcScanlineStride; p01 = p_1 + srcPixelStride; p11 = p01 + srcPixelStride; p21 = p11 + srcPixelStride; p_2 = p_1 + srcScanlineStride; p02 = p_2 + srcPixelStride; p12 = p02 + srcPixelStride; p22 = p12 + srcPixelStride; for (int x = dst_min_x; x < dst_max_x; x++) { // // Check against the source rectangle // if ((s_ix >= (src_rect_x1 + 1)) && (s_ix < (src_rect_x2 - 2)) && (s_iy >= (src_rect_y1 + 1)) && (s_iy < (src_rect_y2 - 2))) { for (int k2=0; k2 < dst_num_bands; k2++) { // // Get the pixels // short tmp_row[]; int tmp_col; tmp_row = srcDataArrays[k2]; tmp_col = bandOffsets[k2]; s__ = tmp_row[p__ + tmp_col] & 0xffff; s0_ = tmp_row[p0_ + tmp_col] & 0xffff; s1_ = tmp_row[p1_ + tmp_col] & 0xffff; s2_ = tmp_row[p2_ + tmp_col] & 0xffff; s_0 = tmp_row[p_0 + tmp_col] & 0xffff; s00 = tmp_row[p00 + tmp_col] & 0xffff; s10 = tmp_row[p10 + tmp_col] & 0xffff; s20 = tmp_row[p20 + tmp_col] & 0xffff; s_1 = tmp_row[p_1 + tmp_col] & 0xffff; s01 = tmp_row[p01 + tmp_col] & 0xffff; s11 = tmp_row[p11 + tmp_col] & 0xffff; s21 = tmp_row[p21 + tmp_col] & 0xffff; s_2 = tmp_row[p_2 + tmp_col] & 0xffff; s02 = tmp_row[p02 + tmp_col] & 0xffff; s12 = tmp_row[p12 + tmp_col] & 0xffff; s22 = tmp_row[p22 + tmp_col] & 0xffff; // Get the new frac values float_fracx = fracx; float_fracy = fracy; frac_xx = float_fracx * (1.0F - float_fracx); frac_yy = float_fracx * (1.0F - float_fracy); s0 = s00 + ((s10 - s00) * float_fracx); s1 = s01 + ((s11 - s01) * float_fracx); s_ = s0_ + ((s1_ - s0_) * float_fracx); s2 = s02 + ((s12 - s02) * float_fracx); q_ = (s1_ + s__) + (((s2_ + s0_) - (s1_ + s__)) * float_fracx); q0 = (s10 + s_0) + (((s20 + s00) - (s10 + s_0)) * float_fracx); q1 = (s11 + s_1) + ((s21 + s01) - (s11 + s_1)) * float_fracx; q2 = (s12 + s_2) + (((s22 + s02) - (s12 + s_2)) * float_fracx); q_ = s_ - q_ / 2.0F; q0 = s0 - q0 / 2.0F; q1 = s1 - q1 / 2.0F; q2 = s2 - q2 / 2.0F; s_ += (q_ * frac_xx); s0 += (q0 * frac_xx); s1 += (q1 * frac_xx); s2 += (q2 * frac_xx); s = s0 + ((s1 - s0) * float_fracy); q = (s1 + s_) + (((s2 + s0) - (s1 + s_)) * float_fracy); q = s - q / 2.0F; s += (q * frac_yy); // Round if (s < 0.0) { result = 0; } else if (s > (float) USHORT_MAX) { result = USHORT_MAX; } else { result = (int) (s + 0.5F); } // write the result dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = (short)(result & 0xFFFF); } } else if (setBackground) { for (int k=0; k < dst_num_bands; k++) dstDataArrays[k][dstPixelOffset+dstBandOffsets[k]] = backgroundUShort[k]; } // walk if (fracx < fracdx1) { s_ix += incx; fracx += fracdx; } else { s_ix += incx1; fracx -= fracdx1; } if (fracy < fracdy1) { s_iy += incy; fracy += fracdy; } else { s_iy += incy1; fracy -= fracdy1; } // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; // // Get the 16 neighbouring positions of the // coordinate in question (p00). // // p__ p0_ p1_ p2_ // p_0 p00 p10 p20 // p_1 p01 p11 p21 // p_2 p02 p12 p22 // p__ = p_x + p_y - srcScanlineStride - srcPixelStride; p0_ = p__ + srcPixelStride; p1_ = p0_ + srcPixelStride; p2_ = p1_ + srcPixelStride; p_0 = p__ + srcScanlineStride; p00 = p_0 + srcPixelStride; p10 = p00 + srcPixelStride; p20 = p10 + srcPixelStride; p_1 = p_0 + srcScanlineStride; p01 = p_1 + srcPixelStride; p11 = p01 + srcPixelStride; p21 = p11 + srcPixelStride; p_2 = p_1 + srcScanlineStride; p02 = p_2 + srcPixelStride; p12 = p02 + srcPixelStride; p22 = p12 + srcPixelStride; dstPixelOffset += dstPixelStride; } dstOffset += dstScanlineStride; } } private void floatLoop(RasterAccessor src, Rectangle destRect, int srcRectX, int srcRectY, RasterAccessor dst) { float src_rect_x1 = src.getX(); float src_rect_y1 = src.getY(); float src_rect_x2 = src_rect_x1 + src.getWidth(); float src_rect_y2 = src_rect_y1 + src.getHeight(); float s_x, s_y; float fracx, fracy; float float_fracx, float_fracy; float frac_xx, frac_yy; int s_ix, s_iy; int p_x, p_y; int p__, p0_, p1_, p2_; int p_0, p00, p01, p02; int p_1, p10, p11, p12; int p_2, p20, p21, p22; float s__, s0_, s1_, s2_; float s_0, s00, s01, s02; float s_1, s10, s11, s12; float s_2, s20, s21, s22; float s0, s1, s_, s2; float q_, q0, q1, q2; float s, q; int dstPixelOffset; int dstOffset = 0; Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); float dstDataArrays[][] = dst.getFloatDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); float srcDataArrays[][] = src.getFloatDataArrays(); int bandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; float[] backgroundFloat = new float[dst_num_bands]; for (int i = 0; i < dst_num_bands; i++) backgroundFloat[i] = (float)backgroundValues[i]; for (int y = dst_min_y; y < dst_max_y ; y++) { dstPixelOffset = dstOffset; // Backward map the first point in the line // The energy is at the (pt_x + 0.5, pt_y + 0.5) dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates s_x = (float) src_pt.getX(); s_y = (float) src_pt.getY(); // As per definition of bicubic interpolation s_x -= 0.5; s_y -= 0.5; // Floor to get the integral coordinate s_ix = (int) Math.floor(s_x); s_iy = (int) Math.floor(s_y); fracx = s_x - (float) s_ix; fracy = s_y - (float) s_iy; // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; // // Get the 16 neighbouring positions of the // coordinate in question (p00). // // p__ p0_ p1_ p2_ // p_0 p00 p10 p20 // p_1 p01 p11 p21 // p_2 p02 p12 p22 // p__ = p_x + p_y - srcScanlineStride - srcPixelStride; p0_ = p__ + srcPixelStride; p1_ = p0_ + srcPixelStride; p2_ = p1_ + srcPixelStride; p_0 = p__ + srcScanlineStride; p00 = p_0 + srcPixelStride; p10 = p00 + srcPixelStride; p20 = p10 + srcPixelStride; p_1 = p_0 + srcScanlineStride; p01 = p_1 + srcPixelStride; p11 = p01 + srcPixelStride; p21 = p11 + srcPixelStride; p_2 = p_1 + srcScanlineStride; p02 = p_2 + srcPixelStride; p12 = p02 + srcPixelStride; p22 = p12 + srcPixelStride; for (int x = dst_min_x; x < dst_max_x; x++) { // // Check against the source rectangle // if ((s_ix >= (src_rect_x1 + 1)) && (s_ix < (src_rect_x2 - 2)) && (s_iy >= (src_rect_y1 + 1)) && (s_iy < (src_rect_y2 - 2))) { for (int k2=0; k2 < dst_num_bands; k2++) { // // Get the pixels // float tmp_row[]; int tmp_col; tmp_row = srcDataArrays[k2]; tmp_col = bandOffsets[k2]; s__ = tmp_row[p__ + tmp_col]; s0_ = tmp_row[p0_ + tmp_col]; s1_ = tmp_row[p1_ + tmp_col]; s2_ = tmp_row[p2_ + tmp_col]; s_0 = tmp_row[p_0 + tmp_col]; s00 = tmp_row[p00 + tmp_col]; s10 = tmp_row[p10 + tmp_col]; s20 = tmp_row[p20 + tmp_col]; s_1 = tmp_row[p_1 + tmp_col]; s01 = tmp_row[p01 + tmp_col]; s11 = tmp_row[p11 + tmp_col]; s21 = tmp_row[p21 + tmp_col]; s_2 = tmp_row[p_2 + tmp_col]; s02 = tmp_row[p02 + tmp_col]; s12 = tmp_row[p12 + tmp_col]; s22 = tmp_row[p22 + tmp_col]; // Get the new frac values float_fracx = fracx; float_fracy = fracy; frac_xx = float_fracx * (1.0F - float_fracx); frac_yy = float_fracx * (1.0F - float_fracy); s0 = s00 + ((s10 - s00) * float_fracx); s1 = s01 + ((s11 - s01) * float_fracx); s_ = s0_ + ((s1_ - s0_) * float_fracx); s2 = s02 + ((s12 - s02) * float_fracx); q_ = (s1_ + s__) + (((s2_ + s0_) - (s1_ + s__)) * float_fracx); q0 = (s10 + s_0) + (((s20 + s00) - (s10 + s_0)) * float_fracx); q1 = (s11 + s_1) + ((s21 + s01) - (s11 + s_1)) * float_fracx; q2 = (s12 + s_2) + (((s22 + s02) - (s12 + s_2)) * float_fracx); q_ = s_ - q_ / 2.0F; q0 = s0 - q0 / 2.0F; q1 = s1 - q1 / 2.0F; q2 = s2 - q2 / 2.0F; s_ += (q_ * frac_xx); s0 += (q0 * frac_xx); s1 += (q1 * frac_xx); s2 += (q2 * frac_xx); s = s0 + ((s1 - s0) * float_fracy); q = (s1 + s_) + (((s2 + s0) - (s1 + s_)) * float_fracy); q = s - q / 2.0F; s += (q * frac_yy); // write the result dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = s; } } else if (setBackground) { for (int k=0; k < dst_num_bands; k++) dstDataArrays[k][dstPixelOffset+dstBandOffsets[k]] = backgroundFloat[k]; } // walk if (fracx < fracdx1) { s_ix += incx; fracx += fracdx; } else { s_ix += incx1; fracx -= fracdx1; } if (fracy < fracdy1) { s_iy += incy; fracy += fracdy; } else { s_iy += incy1; fracy -= fracdy1; } // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; // // Get the 16 neighbouring positions of the // coordinate in question (p00). // // p__ p0_ p1_ p2_ // p_0 p00 p10 p20 // p_1 p01 p11 p21 // p_2 p02 p12 p22 // p__ = p_x + p_y - srcScanlineStride - srcPixelStride; p0_ = p__ + srcPixelStride; p1_ = p0_ + srcPixelStride; p2_ = p1_ + srcPixelStride; p_0 = p__ + srcScanlineStride; p00 = p_0 + srcPixelStride; p10 = p00 + srcPixelStride; p20 = p10 + srcPixelStride; p_1 = p_0 + srcScanlineStride; p01 = p_1 + srcPixelStride; p11 = p01 + srcPixelStride; p21 = p11 + srcPixelStride; p_2 = p_1 + srcScanlineStride; p02 = p_2 + srcPixelStride; p12 = p02 + srcPixelStride; p22 = p12 + srcPixelStride; dstPixelOffset += dstPixelStride; } dstOffset += dstScanlineStride; } } private void doubleLoop(RasterAccessor src, Rectangle destRect, int srcRectX, int srcRectY, RasterAccessor dst) { float src_rect_x1 = src.getX(); float src_rect_y1 = src.getY(); float src_rect_x2 = src_rect_x1 + src.getWidth(); float src_rect_y2 = src_rect_y1 + src.getHeight(); double s_x, s_y; double fracx, fracy; double float_fracx, float_fracy; double frac_xx, frac_yy; int s_ix, s_iy; int p_x, p_y; int p__, p0_, p1_, p2_; int p_0, p00, p01, p02; int p_1, p10, p11, p12; int p_2, p20, p21, p22; double s__, s0_, s1_, s2_; double s_0, s00, s01, s02; double s_1, s10, s11, s12; double s_2, s20, s21, s22; double s0, s1, s_, s2; double q_, q0, q1, q2; double s, q; int dstPixelOffset; int dstOffset = 0; Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); double dstDataArrays[][] = dst.getDoubleDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); double srcDataArrays[][] = src.getDoubleDataArrays(); int bandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; for (int y = dst_min_y; y < dst_max_y ; y++) { dstPixelOffset = dstOffset; // Backward map the first point in the line // The energy is at the (pt_x + 0.5, pt_y + 0.5) dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates s_x = (double) src_pt.getX(); s_y = (double) src_pt.getY(); // As per definition of bicubic interpolation s_x -= 0.5; s_y -= 0.5; // Floor to get the integral coordinate s_ix = (int) Math.floor(s_x); s_iy = (int) Math.floor(s_y); fracx = s_x - (double) s_ix; fracy = s_y - (double) s_iy; // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; // // Get the 16 neighbouring positions of the // coordinate in question (p00). // // p__ p0_ p1_ p2_ // p_0 p00 p10 p20 // p_1 p01 p11 p21 // p_2 p02 p12 p22 // p__ = p_x + p_y - srcScanlineStride - srcPixelStride; p0_ = p__ + srcPixelStride; p1_ = p0_ + srcPixelStride; p2_ = p1_ + srcPixelStride; p_0 = p__ + srcScanlineStride; p00 = p_0 + srcPixelStride; p10 = p00 + srcPixelStride; p20 = p10 + srcPixelStride; p_1 = p_0 + srcScanlineStride; p01 = p_1 + srcPixelStride; p11 = p01 + srcPixelStride; p21 = p11 + srcPixelStride; p_2 = p_1 + srcScanlineStride; p02 = p_2 + srcPixelStride; p12 = p02 + srcPixelStride; p22 = p12 + srcPixelStride; for (int x = dst_min_x; x < dst_max_x; x++) { // // Check against the source rectangle // if ((s_ix >= (src_rect_x1 + 1)) && (s_ix < (src_rect_x2 - 2)) && (s_iy >= (src_rect_y1 + 1)) && (s_iy < (src_rect_y2 - 2))) { for (int k2=0; k2 < dst_num_bands; k2++) { // // Get the pixels // double tmp_row[]; int tmp_col; tmp_row = srcDataArrays[k2]; tmp_col = bandOffsets[k2]; s__ = tmp_row[p__ + tmp_col]; s0_ = tmp_row[p0_ + tmp_col]; s1_ = tmp_row[p1_ + tmp_col]; s2_ = tmp_row[p2_ + tmp_col]; s_0 = tmp_row[p_0 + tmp_col]; s00 = tmp_row[p00 + tmp_col]; s10 = tmp_row[p10 + tmp_col]; s20 = tmp_row[p20 + tmp_col]; s_1 = tmp_row[p_1 + tmp_col]; s01 = tmp_row[p01 + tmp_col]; s11 = tmp_row[p11 + tmp_col]; s21 = tmp_row[p21 + tmp_col]; s_2 = tmp_row[p_2 + tmp_col]; s02 = tmp_row[p02 + tmp_col]; s12 = tmp_row[p12 + tmp_col]; s22 = tmp_row[p22 + tmp_col]; // Get the new frac values float_fracx = fracx; float_fracy = fracy; frac_xx = float_fracx * (1.0F - float_fracx); frac_yy = float_fracx * (1.0F - float_fracy); s0 = s00 + ((s10 - s00) * float_fracx); s1 = s01 + ((s11 - s01) * float_fracx); s_ = s0_ + ((s1_ - s0_) * float_fracx); s2 = s02 + ((s12 - s02) * float_fracx); q_ = (s1_ + s__) + (((s2_ + s0_) - (s1_ + s__)) * float_fracx); q0 = (s10 + s_0) + (((s20 + s00) - (s10 + s_0)) * float_fracx); q1 = (s11 + s_1) + ((s21 + s01) - (s11 + s_1)) * float_fracx; q2 = (s12 + s_2) + (((s22 + s02) - (s12 + s_2)) * float_fracx); q_ = s_ - q_ / 2.0F; q0 = s0 - q0 / 2.0F; q1 = s1 - q1 / 2.0F; q2 = s2 - q2 / 2.0F; s_ += (q_ * frac_xx); s0 += (q0 * frac_xx); s1 += (q1 * frac_xx); s2 += (q2 * frac_xx); s = s0 + ((s1 - s0) * float_fracy); q = (s1 + s_) + (((s2 + s0) - (s1 + s_)) * float_fracy); q = s - q / 2.0F; s += (q * frac_yy); // write the result dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = s; } } else if (setBackground) { for (int k=0; k < dst_num_bands; k++) dstDataArrays[k][dstPixelOffset+dstBandOffsets[k]] = backgroundValues[k]; } // walk if (fracx < fracdx1) { s_ix += incx; fracx += fracdx; } else { s_ix += incx1; fracx -= fracdx1; } if (fracy < fracdy1) { s_iy += incy; fracy += fracdy; } else { s_iy += incy1; fracy -= fracdy1; } // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; // // Get the 16 neighbouring positions of the // coordinate in question (p00). // // p__ p0_ p1_ p2_ // p_0 p00 p10 p20 // p_1 p01 p11 p21 // p_2 p02 p12 p22 // p__ = p_x + p_y - srcScanlineStride - srcPixelStride; p0_ = p__ + srcPixelStride; p1_ = p0_ + srcPixelStride; p2_ = p1_ + srcPixelStride; p_0 = p__ + srcScanlineStride; p00 = p_0 + srcPixelStride; p10 = p00 + srcPixelStride; p20 = p10 + srcPixelStride; p_1 = p_0 + srcScanlineStride; p01 = p_1 + srcPixelStride; p11 = p01 + srcPixelStride; p21 = p11 + srcPixelStride; p_2 = p_1 + srcScanlineStride; p02 = p_2 + srcPixelStride; p12 = p02 + srcPixelStride; p22 = p12 + srcPixelStride; dstPixelOffset += dstPixelStride; } dstOffset += dstScanlineStride; } } // public static OpImage createTestImage(OpImageTester oit) { // Interpolation interp = new InterpolationBicubic(8); // AffineTransform tr = new AffineTransform(0.707107, // -0.707106, // 0.707106, // 0.707107, // 0.0, // 0.0); // return new AffineBicubicOpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource()), // tr, // interp); // } // // Calls a method on OpImage that uses introspection, to make this // // class, discover it's createTestImage() call, call it and then // // benchmark the performance of the created OpImage chain. // public static void main(String args[]) { // String classname = "com.sun.media.jai.opimage.AffineBicubicOpImage"; // OpImageTester.performDiagnostics(classname, args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MaxFilterXOpImage.java0000644000175000017500000004237110203035544027367 0ustar mathieumathieu/* * $RCSfile: MaxFilterXOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:32 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import java.util.Map; import javax.media.jai.operator.MaxFilterDescriptor; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class to perform max filtering on a source image. * */ final class MaxFilterXOpImage extends MaxFilterOpImage { /** * Creates a MaxFilterXOpImage with the given source and * maskSize. The image dimensions are derived from the source * image. The tile grid layout, SampleModel, and ColorModel may * optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. */ public MaxFilterXOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, int maskSize) { super(source, extender, config, layout, MaxFilterDescriptor.MAX_MASK_PLUS, maskSize); } protected void byteLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); byte dstDataArrays[][] = dst.getByteDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); byte srcDataArrays[][] = src.getByteDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int scanPlusPixelStride = srcScanlineStride+srcPixelStride; int scanMinusPixelStride = srcScanlineStride-srcPixelStride; int topRightOffset = srcPixelStride*(filterSize-1); int maxval, val; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { byte dstData[] = dstDataArrays[k]; byte srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { maxval = Integer.MIN_VALUE; // figure out where the top left of the X starts int imageOffset = srcPixelOffset; for (int u = 0; u < wp; u++) { val = (int)(srcData[imageOffset]&0xff); imageOffset += scanPlusPixelStride; maxval = (val > maxval) ? val : maxval; } // figure out where the top right of the X starts imageOffset = srcPixelOffset + topRightOffset; for (int v = 0; v < wp; v++) { val = (int)(srcData[imageOffset]&0xff); imageOffset += scanMinusPixelStride; maxval = (val > maxval) ? val : maxval; } dstData[dstPixelOffset] = (byte)maxval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void shortLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int scanPlusPixelStride = srcScanlineStride+srcPixelStride; int scanMinusPixelStride = srcScanlineStride-srcPixelStride; int topRightOffset = srcPixelStride*(filterSize-1); int maxval, val; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { maxval = Integer.MIN_VALUE; // figure out where the top left of the X starts int imageOffset = srcPixelOffset; for (int u = 0; u < wp; u++) { val = (int)(srcData[imageOffset]); imageOffset += scanPlusPixelStride; maxval = (val > maxval) ? val : maxval; } // figure out where the top right of the X starts imageOffset = srcPixelOffset + topRightOffset; for (int v = 0; v < wp; v++) { val = (int)(srcData[imageOffset]); imageOffset += scanMinusPixelStride; maxval = (val > maxval) ? val : maxval; } dstData[dstPixelOffset] = (short)maxval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void ushortLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int scanPlusPixelStride = srcScanlineStride+srcPixelStride; int scanMinusPixelStride = srcScanlineStride-srcPixelStride; int topRightOffset = srcPixelStride*(filterSize-1); int maxval, val; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { maxval = Integer.MIN_VALUE; // figure out where the top left of the X starts int imageOffset = srcPixelOffset; for (int u = 0; u < wp; u++) { val = (int)(srcData[imageOffset]&0xffff); imageOffset += scanPlusPixelStride; maxval = (val > maxval) ? val : maxval; } // figure out where the top right of the X starts imageOffset = srcPixelOffset + topRightOffset; for (int v = 0; v < wp; v++) { val = (int)(srcData[imageOffset]&0xffff); imageOffset += scanMinusPixelStride; maxval = (val > maxval) ? val : maxval; } dstData[dstPixelOffset] = (short)maxval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void intLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); int dstDataArrays[][] = dst.getIntDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcDataArrays[][] = src.getIntDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int scanPlusPixelStride = srcScanlineStride+srcPixelStride; int scanMinusPixelStride = srcScanlineStride-srcPixelStride; int topRightOffset = srcPixelStride*(filterSize-1); int maxval, val; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { int dstData[] = dstDataArrays[k]; int srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { maxval = Integer.MIN_VALUE; // figure out where the top left of the X starts int imageOffset = srcPixelOffset; for (int u = 0; u < wp; u++) { val = srcData[imageOffset]; imageOffset += scanPlusPixelStride; maxval = (val > maxval) ? val : maxval; } // figure out where the top right of the X starts imageOffset = srcPixelOffset + topRightOffset; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += scanMinusPixelStride; maxval = (val > maxval) ? val : maxval; } dstData[dstPixelOffset] = maxval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void floatLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); float dstDataArrays[][] = dst.getFloatDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); float srcDataArrays[][] = src.getFloatDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int scanPlusPixelStride = srcScanlineStride+srcPixelStride; int scanMinusPixelStride = srcScanlineStride-srcPixelStride; int topRightOffset = srcPixelStride*(filterSize-1); float maxval, val; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { float dstData[] = dstDataArrays[k]; float srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { maxval = -Float.MAX_VALUE; // figure out where the top left of the X starts int imageOffset = srcPixelOffset; for (int u = 0; u < wp; u++) { val = srcData[imageOffset]; imageOffset += scanPlusPixelStride; maxval = (val > maxval) ? val : maxval; } // figure out where the top right of the X starts imageOffset = srcPixelOffset + topRightOffset; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += scanMinusPixelStride; maxval = (val > maxval) ? val : maxval; } dstData[dstPixelOffset] = maxval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void doubleLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); double dstDataArrays[][] = dst.getDoubleDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); double srcDataArrays[][] = src.getDoubleDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int scanPlusPixelStride = srcScanlineStride+srcPixelStride; int scanMinusPixelStride = srcScanlineStride-srcPixelStride; int topRightOffset = srcPixelStride*(filterSize-1); double maxval, val; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { double dstData[] = dstDataArrays[k]; double srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { maxval = -Double.MAX_VALUE; // figure out where the top left of the X starts int imageOffset = srcPixelOffset; for (int u = 0; u < wp; u++) { val = srcData[imageOffset]; imageOffset += scanPlusPixelStride; maxval = (val > maxval) ? val : maxval; } // figure out where the top right of the X starts imageOffset = srcPixelOffset + topRightOffset; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += scanMinusPixelStride; maxval = (val > maxval) ? val : maxval; } dstData[dstPixelOffset] = maxval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } // public static OpImage createTestImage(OpImageTester oit) { // return new MaxFilterXOpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource()), // 3); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/PolarToComplexOpImage.java0000644000175000017500000006230610203035544030254 0ustar mathieumathieu/* * $RCSfile: PolarToComplexOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:41 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.PixelInterleavedSampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.PointOpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.RasterFactory; import java.util.Map; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.util.JDKWorkarounds; /// XXX Testing /// import javax.media.jai.TiledImage; /// import javax.media.jai.JAI; /** * An OpImage implementing the "PolarToComplex" operation * as described in * javax.media.jai.operator.PolarToComplexDescriptor. * *

The number of bands in the destination image is clamped to twice the * minimum number of bands across all source images. The two source images * are expected to be magnitude (first source) and phase (second source). * If the phase image is integral, its values are assumed to lie in the * range [0, MAX_VALUE] where MAX_VALUE is a functino of the data type. * These values will be scaled to the range [-Math.PI, Math.PI] before being * used. * * @since EA4 * * @see javax.media.jai.PointOpImage * @see javax.media.jai.operator.MagnitudeDescriptor * @see javax.media.jai.operator.PhaseDescriptor * @see javax.media.jai.operator.PolarToComplexDescriptor * */ final class PolarToComplexOpImage extends PointOpImage { /** The gain to be applied to the phase. */ private double phaseGain = 1.0; /** The bias to be applied to the phase. */ private double phaseBias = 0.0; /** * Constructs a PolarToComplexOpImage object. * *

The tile grid layout, SampleModel, and ColorModel may optionally * be specified by an ImageLayout object. * * @param magnitude A RenderedImage representing magnitude. * @param phase A RenderedImage representing phase. * @param layout An ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. */ public PolarToComplexOpImage(RenderedImage magnitude, RenderedImage phase, Map config, ImageLayout layout) { super(magnitude, phase, layout, config, true); // Force the number of bands to be twice the minimum source band count. int numBands = 2*Math.min(magnitude.getSampleModel().getNumBands(), phase.getSampleModel().getNumBands()); if(sampleModel.getNumBands() != numBands) { // Create a new SampleModel for the destination. sampleModel = RasterFactory.createComponentSampleModel(sampleModel, sampleModel.getTransferType(), sampleModel.getWidth(), sampleModel.getHeight(), numBands); if(colorModel != null && !JDKWorkarounds.areCompatibleDataModels(sampleModel, colorModel)) { colorModel = ImageUtil.getCompatibleColorModel(sampleModel, config); } } // Set phase gain and bias as a function of the phase image data type. switch(phase.getSampleModel().getTransferType()) { case DataBuffer.TYPE_BYTE: phaseGain = (2.0*Math.PI)/255.0; phaseBias = -Math.PI; break; case DataBuffer.TYPE_SHORT: phaseGain = (2.0*Math.PI)/Short.MAX_VALUE; phaseBias = -Math.PI; break; case DataBuffer.TYPE_USHORT: phaseGain = (2.0*Math.PI)/(Short.MAX_VALUE - Short.MIN_VALUE); phaseBias = -Math.PI; break; case DataBuffer.TYPE_INT: phaseGain = (2.0*Math.PI)/Integer.MAX_VALUE; phaseBias = -Math.PI; break; default: // A floating point type: do nothing - use class defaults. } // TODO: Set "complex" property. } /* * Calculate a complex rectangle given the magnitude and phase. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); // Construct RasterAccessors. RasterAccessor magAccessor = new RasterAccessor(sources[0], destRect, formatTags[0], getSource(0).getColorModel()); RasterAccessor phsAccessor = new RasterAccessor(sources[1], destRect, formatTags[1], getSource(1).getColorModel()); RasterAccessor dstAccessor = new RasterAccessor(dest, destRect, formatTags[2], getColorModel()); // Branch to the method appropriate to the accessor data type. switch(dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(magAccessor, phsAccessor, dstAccessor, destRect.height, destRect.width); break; case DataBuffer.TYPE_SHORT: computeRectShort(magAccessor, phsAccessor, dstAccessor, destRect.height, destRect.width); break; case DataBuffer.TYPE_USHORT: computeRectUShort(magAccessor, phsAccessor, dstAccessor, destRect.height, destRect.width); break; case DataBuffer.TYPE_INT: computeRectInt(magAccessor, phsAccessor, dstAccessor, destRect.height, destRect.width); break; case DataBuffer.TYPE_FLOAT: computeRectFloat(magAccessor, phsAccessor, dstAccessor, destRect.height, destRect.width); break; case DataBuffer.TYPE_DOUBLE: computeRectDouble(magAccessor, phsAccessor, dstAccessor, destRect.height, destRect.width); break; default: // NB: This statement should be unreachable. throw new RuntimeException(JaiI18N.getString("PolarToComplexOpImage0")); } if (dstAccessor.needsClamping()) { dstAccessor.clampDataArrays(); } // Make sure that the output data is copied to the destination. dstAccessor.copyDataToRaster(); } private void computeRectDouble(RasterAccessor magAccessor, RasterAccessor phsAccessor, RasterAccessor dstAccessor, int numRows, int numCols) { // Set pixel and line strides. int dstPixelStride = dstAccessor.getPixelStride(); int dstScanlineStride = dstAccessor.getScanlineStride(); int magPixelStride = magAccessor.getPixelStride(); int magScanlineStride = magAccessor.getScanlineStride(); int phsPixelStride = phsAccessor.getPixelStride(); int phsScanlineStride = phsAccessor.getScanlineStride(); // Loop over the destination components. int numComponents = sampleModel.getNumBands()/2; for(int component = 0; component < numComponents; component++) { // Set source band indices. int dstBandReal = 2*component; int dstBandImag = dstBandReal + 1; // Get the source and destination arrays for this band. double[] dstReal = dstAccessor.getDoubleDataArray(dstBandReal); double[] dstImag = dstAccessor.getDoubleDataArray(dstBandImag); double[] magData = magAccessor.getDoubleDataArray(component); double[] phsData = phsAccessor.getDoubleDataArray(component); // Initialize the data offsets for this band. int dstOffsetReal = dstAccessor.getBandOffset(dstBandReal); int dstOffsetImag = dstAccessor.getBandOffset(dstBandImag); int magOffset = magAccessor.getBandOffset(component); int phsOffset = phsAccessor.getBandOffset(component); // Initialize the line offsets for looping. int dstLineReal = dstOffsetReal; int dstLineImag = dstOffsetImag; int magLine = magOffset; int phsLine = phsOffset; for(int row = 0; row < numRows; row++) { // Initialize pixel offsets for this row. int dstPixelReal = dstLineReal; int dstPixelImag = dstLineImag; int magPixel = magLine; int phsPixel = phsLine; for(int col = 0; col < numCols; col++) { double mag = magData[magPixel]; double phs = phsData[phsPixel]*phaseGain + phaseBias; dstReal[dstPixelReal] = mag*Math.cos(phs); dstImag[dstPixelImag] = mag*Math.sin(phs); dstPixelReal += dstPixelStride; dstPixelImag += dstPixelStride; magPixel += magPixelStride; phsPixel += phsPixelStride; } // Increment the line offsets. dstLineReal += dstScanlineStride; dstLineImag += dstScanlineStride; magLine += magScanlineStride; phsLine += phsScanlineStride; } } } private void computeRectFloat(RasterAccessor magAccessor, RasterAccessor phsAccessor, RasterAccessor dstAccessor, int numRows, int numCols) { // Set pixel and line strides. int dstPixelStride = dstAccessor.getPixelStride(); int dstScanlineStride = dstAccessor.getScanlineStride(); int magPixelStride = magAccessor.getPixelStride(); int magScanlineStride = magAccessor.getScanlineStride(); int phsPixelStride = phsAccessor.getPixelStride(); int phsScanlineStride = phsAccessor.getScanlineStride(); // Loop over the destination components. int numComponents = sampleModel.getNumBands()/2; for(int component = 0; component < numComponents; component++) { // Set source band indices. int dstBandReal = 2*component; int dstBandImag = dstBandReal + 1; // Get the source and destination arrays for this band. float[] dstReal = dstAccessor.getFloatDataArray(dstBandReal); float[] dstImag = dstAccessor.getFloatDataArray(dstBandImag); float[] magData = magAccessor.getFloatDataArray(component); float[] phsData = phsAccessor.getFloatDataArray(component); // Initialize the data offsets for this band. int dstOffsetReal = dstAccessor.getBandOffset(dstBandReal); int dstOffsetImag = dstAccessor.getBandOffset(dstBandImag); int magOffset = magAccessor.getBandOffset(component); int phsOffset = phsAccessor.getBandOffset(component); // Initialize the line offsets for looping. int dstLineReal = dstOffsetReal; int dstLineImag = dstOffsetImag; int magLine = magOffset; int phsLine = phsOffset; for(int row = 0; row < numRows; row++) { // Initialize pixel offsets for this row. int dstPixelReal = dstLineReal; int dstPixelImag = dstLineImag; int magPixel = magLine; int phsPixel = phsLine; for(int col = 0; col < numCols; col++) { double mag = magData[magPixel]; double phs = phsData[phsPixel]*phaseGain + phaseBias; dstReal[dstPixelReal] = ImageUtil.clampFloat(mag*Math.cos(phs)); dstImag[dstPixelImag] = ImageUtil.clampFloat(mag*Math.sin(phs)); dstPixelReal += dstPixelStride; dstPixelImag += dstPixelStride; magPixel += magPixelStride; phsPixel += phsPixelStride; } // Increment the line offsets. dstLineReal += dstScanlineStride; dstLineImag += dstScanlineStride; magLine += magScanlineStride; phsLine += phsScanlineStride; } } } private void computeRectInt(RasterAccessor magAccessor, RasterAccessor phsAccessor, RasterAccessor dstAccessor, int numRows, int numCols) { // Set pixel and line strides. int dstPixelStride = dstAccessor.getPixelStride(); int dstScanlineStride = dstAccessor.getScanlineStride(); int magPixelStride = magAccessor.getPixelStride(); int magScanlineStride = magAccessor.getScanlineStride(); int phsPixelStride = phsAccessor.getPixelStride(); int phsScanlineStride = phsAccessor.getScanlineStride(); // Loop over the destination components. int numComponents = sampleModel.getNumBands()/2; for(int component = 0; component < numComponents; component++) { // Set source band indices. int dstBandReal = 2*component; int dstBandImag = dstBandReal + 1; // Get the source and destination arrays for this band. int[] dstReal = dstAccessor.getIntDataArray(dstBandReal); int[] dstImag = dstAccessor.getIntDataArray(dstBandImag); int[] magData = magAccessor.getIntDataArray(component); int[] phsData = phsAccessor.getIntDataArray(component); // Initialize the data offsets for this band. int dstOffsetReal = dstAccessor.getBandOffset(dstBandReal); int dstOffsetImag = dstAccessor.getBandOffset(dstBandImag); int magOffset = magAccessor.getBandOffset(component); int phsOffset = phsAccessor.getBandOffset(component); // Initialize the line offsets for looping. int dstLineReal = dstOffsetReal; int dstLineImag = dstOffsetImag; int magLine = magOffset; int phsLine = phsOffset; for(int row = 0; row < numRows; row++) { // Initialize pixel offsets for this row. int dstPixelReal = dstLineReal; int dstPixelImag = dstLineImag; int magPixel = magLine; int phsPixel = phsLine; for(int col = 0; col < numCols; col++) { double mag = magData[magPixel]; double phs = phsData[phsPixel]*phaseGain + phaseBias; dstReal[dstPixelReal] = ImageUtil.clampRoundInt(mag*Math.cos(phs)); dstImag[dstPixelImag] = ImageUtil.clampRoundInt(mag*Math.sin(phs)); dstPixelReal += dstPixelStride; dstPixelImag += dstPixelStride; magPixel += magPixelStride; phsPixel += phsPixelStride; } // Increment the line offsets. dstLineReal += dstScanlineStride; dstLineImag += dstScanlineStride; magLine += magScanlineStride; phsLine += phsScanlineStride; } } } private void computeRectUShort(RasterAccessor magAccessor, RasterAccessor phsAccessor, RasterAccessor dstAccessor, int numRows, int numCols) { // Set pixel and line strides. int dstPixelStride = dstAccessor.getPixelStride(); int dstScanlineStride = dstAccessor.getScanlineStride(); int magPixelStride = magAccessor.getPixelStride(); int magScanlineStride = magAccessor.getScanlineStride(); int phsPixelStride = phsAccessor.getPixelStride(); int phsScanlineStride = phsAccessor.getScanlineStride(); // Loop over the destination components. int numComponents = sampleModel.getNumBands()/2; for(int component = 0; component < numComponents; component++) { // Set source band indices. int dstBandReal = 2*component; int dstBandImag = dstBandReal + 1; // Get the source and destination arrays for this band. short[] dstReal = dstAccessor.getShortDataArray(dstBandReal); short[] dstImag = dstAccessor.getShortDataArray(dstBandImag); short[] magData = magAccessor.getShortDataArray(component); short[] phsData = phsAccessor.getShortDataArray(component); // Initialize the data offsets for this band. int dstOffsetReal = dstAccessor.getBandOffset(dstBandReal); int dstOffsetImag = dstAccessor.getBandOffset(dstBandImag); int magOffset = magAccessor.getBandOffset(component); int phsOffset = phsAccessor.getBandOffset(component); // Initialize the line offsets for looping. int dstLineReal = dstOffsetReal; int dstLineImag = dstOffsetImag; int magLine = magOffset; int phsLine = phsOffset; for(int row = 0; row < numRows; row++) { // Initialize pixel offsets for this row. int dstPixelReal = dstLineReal; int dstPixelImag = dstLineImag; int magPixel = magLine; int phsPixel = phsLine; for(int col = 0; col < numCols; col++) { double mag = magData[magPixel]&0xffff; double phs = (phsData[phsPixel]&0xffff)*phaseGain + phaseBias; dstReal[dstPixelReal] = ImageUtil.clampRoundUShort(mag*Math.cos(phs)); dstImag[dstPixelImag] = ImageUtil.clampRoundUShort(mag*Math.sin(phs)); dstPixelReal += dstPixelStride; dstPixelImag += dstPixelStride; magPixel += magPixelStride; phsPixel += phsPixelStride; } // Increment the line offsets. dstLineReal += dstScanlineStride; dstLineImag += dstScanlineStride; magLine += magScanlineStride; phsLine += phsScanlineStride; } } } private void computeRectShort(RasterAccessor magAccessor, RasterAccessor phsAccessor, RasterAccessor dstAccessor, int numRows, int numCols) { // Set pixel and line strides. int dstPixelStride = dstAccessor.getPixelStride(); int dstScanlineStride = dstAccessor.getScanlineStride(); int magPixelStride = magAccessor.getPixelStride(); int magScanlineStride = magAccessor.getScanlineStride(); int phsPixelStride = phsAccessor.getPixelStride(); int phsScanlineStride = phsAccessor.getScanlineStride(); // Loop over the destination components. int numComponents = sampleModel.getNumBands()/2; for(int component = 0; component < numComponents; component++) { // Set source band indices. int dstBandReal = 2*component; int dstBandImag = dstBandReal + 1; // Get the source and destination arrays for this band. short[] dstReal = dstAccessor.getShortDataArray(dstBandReal); short[] dstImag = dstAccessor.getShortDataArray(dstBandImag); short[] magData = magAccessor.getShortDataArray(component); short[] phsData = phsAccessor.getShortDataArray(component); // Initialize the data offsets for this band. int dstOffsetReal = dstAccessor.getBandOffset(dstBandReal); int dstOffsetImag = dstAccessor.getBandOffset(dstBandImag); int magOffset = magAccessor.getBandOffset(component); int phsOffset = phsAccessor.getBandOffset(component); // Initialize the line offsets for looping. int dstLineReal = dstOffsetReal; int dstLineImag = dstOffsetImag; int magLine = magOffset; int phsLine = phsOffset; for(int row = 0; row < numRows; row++) { // Initialize pixel offsets for this row. int dstPixelReal = dstLineReal; int dstPixelImag = dstLineImag; int magPixel = magLine; int phsPixel = phsLine; for(int col = 0; col < numCols; col++) { double mag = magData[magPixel]; double phs = phsData[phsPixel]*phaseGain + phaseBias; dstReal[dstPixelReal] = ImageUtil.clampRoundShort(mag*Math.cos(phs)); dstImag[dstPixelImag] = ImageUtil.clampRoundShort(mag*Math.sin(phs)); dstPixelReal += dstPixelStride; dstPixelImag += dstPixelStride; magPixel += magPixelStride; phsPixel += phsPixelStride; } // Increment the line offsets. dstLineReal += dstScanlineStride; dstLineImag += dstScanlineStride; magLine += magScanlineStride; phsLine += phsScanlineStride; } } } private void computeRectByte(RasterAccessor magAccessor, RasterAccessor phsAccessor, RasterAccessor dstAccessor, int numRows, int numCols) { // Set pixel and line strides. int dstPixelStride = dstAccessor.getPixelStride(); int dstScanlineStride = dstAccessor.getScanlineStride(); int magPixelStride = magAccessor.getPixelStride(); int magScanlineStride = magAccessor.getScanlineStride(); int phsPixelStride = phsAccessor.getPixelStride(); int phsScanlineStride = phsAccessor.getScanlineStride(); // Loop over the destination components. int numComponents = sampleModel.getNumBands()/2; for(int component = 0; component < numComponents; component++) { // Set source band indices. int dstBandReal = 2*component; int dstBandImag = dstBandReal + 1; // Get the source and destination arrays for this band. byte[] dstReal = dstAccessor.getByteDataArray(dstBandReal); byte[] dstImag = dstAccessor.getByteDataArray(dstBandImag); byte[] magData = magAccessor.getByteDataArray(component); byte[] phsData = phsAccessor.getByteDataArray(component); // Initialize the data offsets for this band. int dstOffsetReal = dstAccessor.getBandOffset(dstBandReal); int dstOffsetImag = dstAccessor.getBandOffset(dstBandImag); int magOffset = magAccessor.getBandOffset(component); int phsOffset = phsAccessor.getBandOffset(component); // Initialize the line offsets for looping. int dstLineReal = dstOffsetReal; int dstLineImag = dstOffsetImag; int magLine = magOffset; int phsLine = phsOffset; for(int row = 0; row < numRows; row++) { // Initialize pixel offsets for this row. int dstPixelReal = dstLineReal; int dstPixelImag = dstLineImag; int magPixel = magLine; int phsPixel = phsLine; for(int col = 0; col < numCols; col++) { double mag = magData[magPixel]&0xff; double phs = (phsData[phsPixel]&0xff)*phaseGain + phaseBias; dstReal[dstPixelReal] = ImageUtil.clampRoundByte(mag*Math.cos(phs)); dstImag[dstPixelImag] = ImageUtil.clampRoundByte(mag*Math.sin(phs)); dstPixelReal += dstPixelStride; dstPixelImag += dstPixelStride; magPixel += magPixelStride; phsPixel += phsPixelStride; } // Increment the line offsets. dstLineReal += dstScanlineStride; dstLineImag += dstScanlineStride; magLine += magScanlineStride; phsLine += phsScanlineStride; } } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ConjugateOpImage.java0000644000175000017500000004054110203035544027260 0ustar mathieumathieu/* * $RCSfile: ConjugateOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:19 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.ImageLayout; import javax.media.jai.PointOpImage; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.RasterFactory; import java.util.Map; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.util.JDKWorkarounds; /// Testing /// import javax.media.jai.JAI; /// import javax.media.jai.TiledImage; /** * An OpImage implementing the "conjugate" operation as * described in javax.media.jai.operator.ConjugateDescriptor. * *

Note that this operation requires a signed destination image so that * the SampleModel is overriden to force byte and unsigned short types to * short and integer, respectively. * * @since EA4 */ final class ConjugateOpImage extends PointOpImage { /** * Force the destination image to have a signed data type. */ private static ImageLayout layoutHelper(ImageLayout layout, RenderedImage source) { // Create or clone the layout. ImageLayout il = layout == null ? new ImageLayout() : (ImageLayout)layout.clone(); // Get the reference SampleModel. SampleModel sm = il.getSampleModel(source); // Get the data type. int dataType = sm.getTransferType(); // Determine whether the destination requires a different data type // and set it if so. boolean createNewSampleModel = false; if(dataType == DataBuffer.TYPE_BYTE) { dataType = DataBuffer.TYPE_SHORT; createNewSampleModel = true; } else if(dataType == DataBuffer.TYPE_USHORT) { dataType = DataBuffer.TYPE_INT; createNewSampleModel = true; } // Create a new SampleModel for the destination if necessary. if(createNewSampleModel) { sm = RasterFactory.createComponentSampleModel(sm, dataType, sm.getWidth(), sm.getHeight(), sm.getNumBands()); il.setSampleModel(sm); // Check ColorModel. ColorModel cm = il.getColorModel(null); if(cm != null && !JDKWorkarounds.areCompatibleDataModels(sm, cm)) { // Clear the mask bit if incompatible. il.unsetValid(ImageLayout.COLOR_MODEL_MASK); } } return il; } /** * Constructs a ConjugateOpImage. The image dimensions are copied * from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout object. * The destination data type must be the smallest appropriate signed * data type. * * @param source a RenderedImage. * @param layout an ImageLayout optionally containing the tile * grid layout, SampleModel, and ColorModel, or null. */ public ConjugateOpImage(RenderedImage source, Map config, ImageLayout layout) { super(source, layoutHelper(layout, source), config, true); // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * Calculates the complex conjugate of the source image. * The sources are cobbled. * * @param sources an array of sources, guarantee to provide all * necessary source data for computing the rectangle. * @param dest a tile that contains the rectangle to be computed. * @param destRect the rectangle within this OpImage to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); Raster source = sources[0]; Rectangle srcRect = mapDestRect(destRect, 0); RasterAccessor srcAccessor = new RasterAccessor(source,srcRect, formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor dstAccessor = new RasterAccessor(dest,destRect, formatTags[1], getColorModel()); switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_SHORT: shortLoop(srcAccessor,dstAccessor); break; case DataBuffer.TYPE_INT: intLoop(srcAccessor,dstAccessor); break; case DataBuffer.TYPE_FLOAT: floatLoop(srcAccessor,dstAccessor); break; case DataBuffer.TYPE_DOUBLE: doubleLoop(srcAccessor,dstAccessor); break; case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: default: throw new RuntimeException(JaiI18N.getString("ConjugateOpImage0")); } // If the RasterAccessor object set up a temporary buffer for the // op to write to, tell the RasterAccessor to write that data // to the raster no that we're done with it. if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } private void shortLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); for (int k = 0; k < dnumBands; k++) { boolean isRealPart = k % 2 == 0; short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; if(isRealPart) { // real part: copy value for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { dstData[dstPixelOffset] = srcData[srcPixelOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } else { // imaginary part: negate value for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { dstData[dstPixelOffset] = ImageUtil.clampShort(-srcData[srcPixelOffset]); srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } } // identical to byteLoops, except datatypes have changed. clumsy, // but there's no other way in Java private void intLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); int dstDataArrays[][] = dst.getIntDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcDataArrays[][] = src.getIntDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); for (int k = 0; k < dnumBands; k++) { boolean isRealPart = k % 2 == 0; int dstData[] = dstDataArrays[k]; int srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; if(isRealPart) { // real part: copy value for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { dstData[dstPixelOffset] = srcData[srcPixelOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } else { // imaginary part: negate value for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { dstData[dstPixelOffset] = -srcData[srcPixelOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } } private void floatLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); float dstDataArrays[][] = dst.getFloatDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); float srcDataArrays[][] = src.getFloatDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); for (int k = 0; k < dnumBands; k++) { boolean isRealPart = k % 2 == 0; float dstData[] = dstDataArrays[k]; float srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; if(isRealPart) { // real part: copy value for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { dstData[dstPixelOffset] = srcData[srcPixelOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } else { // imaginary part: negate value for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { dstData[dstPixelOffset] = -srcData[srcPixelOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } } private void doubleLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); double dstDataArrays[][] = dst.getDoubleDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); double srcDataArrays[][] = src.getDoubleDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); for (int k = 0; k < dnumBands; k++) { boolean isRealPart = k % 2 == 0; double dstData[] = dstDataArrays[k]; double srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; if(isRealPart) { // real part: copy value for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { dstData[dstPixelOffset] = srcData[srcPixelOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } else { // imaginary part: negate value for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { dstData[dstPixelOffset] = -srcData[srcPixelOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } } // public static void main(String[] args) { // int[] dataTypes = // new int[] {DataBuffer.TYPE_BYTE, DataBuffer.TYPE_SHORT, // DataBuffer.TYPE_USHORT, DataBuffer.TYPE_INT, // DataBuffer.TYPE_FLOAT, DataBuffer.TYPE_DOUBLE}; // for(int dt = 0; dt < dataTypes.length; dt++) { // WritableRaster wr = // RasterFactory.createBandedRaster(dataTypes[dt], // 5, 5, 4, null); // int k = 0; // for(int y = 0; y < 5; y++) { // for(int x = 0; x < 5; x++) { // for(int z = 0; z < 4; z++) { // wr.setSample(x, y, z, k++); // } // } // } // SampleModel sm = // RasterFactory.createBandedSampleModel(dataTypes[dt], // 2, 2, 4, null, null); // TiledImage ti = new TiledImage(0, 0, 5, 5, 0, 0, sm, null); // ti.setData(wr); // RenderedImage ri = JAI.create("conjugate", ti); // Raster r = ri.getData(); // for(int y = 0; y < 5; y++) { // for(int x = 0; x < 5; x++) { // for(int z = 0; z < 4; z++) { // System.out.print(r.getSampleDouble(x, y, z)+" "); // } // System.out.print(" : "); // } // System.out.println(""); // } // System.out.println(""); // } // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ErrorDiffusionRIF.java0000644000175000017500000000326510203035544027402 0ustar mathieumathieu/* * $RCSfile: ErrorDiffusionRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:25 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import java.util.Map; import javax.media.jai.KernelJAI; import javax.media.jai.LookupTableJAI; /** * A RIF supporting the "ErrorDiffusion" operation in the rendered * image layer. * * @since EA2 * @see javax.media.jai.operator.ErrorDiffusionDescriptor */ public class ErrorDiffusionRIF implements RenderedImageFactory { /** Constructor. */ public ErrorDiffusionRIF() {} /** * Creates a new instance of an error diffusion operator according to the * color map and error filter kernel. * * @param paramBlock The color map and error filter kernel objects. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); RenderedImage source = paramBlock.getRenderedSource(0); LookupTableJAI lookupTable = (LookupTableJAI)paramBlock.getObjectParameter(0); KernelJAI kernel = (KernelJAI)paramBlock.getObjectParameter(1); return new ErrorDiffusionOpImage(source, renderHints, layout, lookupTable, kernel); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MatchCDFCRIF.java0000644000175000017500000001205010203035544026106 0ustar mathieumathieu/* * $RCSfile: MatchCDFCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:31 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.CRIFImpl; import javax.media.jai.Histogram; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "MatchCDF" operation in the rendered * and renderable image layers. * * @see javax.media.jai.operator.MatchCDFDescriptor * @see javax.media.jai.operator.PiecewiseDescriptor * @see PiecewiseOpImage * * * @since EA4 */ public class MatchCDFCRIF extends CRIFImpl { /* * Creates a piecewise mapping from an image with cumulative distribution * function of pixel values CDFin to another given by CDFout. */ private static void createHistogramMap(float[] CDFin, float[] CDFout, double lowValue, double binWidth, int numBins, float[] abscissa, float[] ordinate) { // Initialize the first abscissa and the output CDF index. double x = lowValue; int j = 0; int jMax = numBins - 1; // Generate one breakpoint for each bin. for(int i = 0; i < numBins; i++) { // Find the first output bin such that the output CDF is // greater than or equal to the input CDF at the current bin. float w = CDFin[i]; while(CDFout[j] < w && j < jMax) j++; // Set the breakpoint values. abscissa[i] = (float)x; ordinate[i] = (float)(j*binWidth); // Increment the abscissa. x += binWidth; } } /** * Create a set of breakpoints which will map the input histogram to * an output histogram with the specified cumulative distribution function. * * @param histIn The input histogram. * @param CDFOut The output CDF. * @return A piecewise transform which will modify the input histogram * to match the output CDF. */ private static float[][][] createSpecificationMap(Histogram histIn, float[][] CDFOut) { // Allocate the overall breakpoint array. int numBands = histIn.getNumBands(); float[][][] bp = new float[numBands][][]; // Calculate a different set of breakpoints for each band. float[] CDFin = null; for(int band = 0; band < numBands; band++) { // Allocate memory for the breakpoints for this band. int numBins = histIn.getNumBins(band); bp[band] = new float[2][]; bp[band][0] = new float[numBins]; bp[band][1] = new float[numBins]; // Calculate the total count over all bins of this band. int[] binsIn = histIn.getBins(band); long binTotalIn = binsIn[0]; for(int i = 1; i < numBins; i++) { binTotalIn += binsIn[i]; } // Allocate memory for the CDF for this band only if needed. if(CDFin == null || CDFin.length < numBins) { CDFin = new float[numBins]; } // Calculate the Cumulative Distribution Function (CDF) for the // input histogram for this band. CDFin[0] = (float)binsIn[0]/binTotalIn; for(int i = 1; i < numBins; i++) { CDFin[i] = CDFin[i-1] + (float)binsIn[i]/binTotalIn; } // Calculate the mapping function. double binWidth = (histIn.getHighValue(band) - histIn.getLowValue(band))/numBins; createHistogramMap(CDFin, CDFOut.length > 1 ? CDFOut[band] : CDFOut[0], histIn.getLowValue(band), binWidth, numBins, bp[band][0], bp[band][1]); } return bp; } /** Constructor. */ public MatchCDFCRIF() { super("matchcdf"); } /** * Creates a new instance of PiecewiseOpImage in the * rendered layer. * * @param args The source image and the breakpoints. * @param hints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); // Derive breakpoints from the histogram and specified CDF. RenderedImage src = args.getRenderedSource(0); Histogram hist = (Histogram)src.getProperty("histogram"); float[][] CDF = (float[][])args.getObjectParameter(0); float[][][] bp = createSpecificationMap(hist, CDF); return new PiecewiseOpImage(src, renderHints, layout, bp); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/WarpGeneralOpImage.java0000644000175000017500000007102610203035544027552 0ustar mathieumathieu/* * $RCSfile: WarpGeneralOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:47 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.IndexColorModel; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.PlanarImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; import javax.media.jai.Warp; import javax.media.jai.WarpOpImage; import javax.media.jai.iterator.RandomIter; import javax.media.jai.iterator.RandomIterFactory; import com.sun.media.jai.util.ImageUtil; /** * An OpImage implementing the general "Warp" operation as * described in javax.media.jai.operator.WarpDescriptor. * It supports all interpolation cases. * * @since EA2 * @see javax.media.jai.Warp * @see javax.media.jai.WarpOpImage * @see javax.media.jai.operator.WarpDescriptor * @see WarpRIF * */ final class WarpGeneralOpImage extends WarpOpImage { /** Color table representing source's IndexColorModel. */ private byte[][] ctable = null; /** * Constructs a WarpGeneralOpImage. * * @param source The source image. * @param extender A BorderExtender, or null. * @param layout The destination image layout. * @param warp An object defining the warp algorithm. * @param interp An object describing the interpolation method. */ public WarpGeneralOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, Warp warp, Interpolation interp, double[] backgroundValues) { super(source, layout, config, false, extender, interp, warp, backgroundValues); /* * If the source has IndexColorModel, get the RGB color table. * Note, in this case, the source should have an integral data type. * And dest always has data type byte. */ ColorModel srcColorModel = source.getColorModel(); if (srcColorModel instanceof IndexColorModel) { IndexColorModel icm = (IndexColorModel)srcColorModel; ctable = new byte[3][icm.getMapSize()]; icm.getReds(ctable[0]); icm.getGreens(ctable[1]); icm.getBlues(ctable[2]); } } /** Warps a rectangle. */ protected void computeRect(PlanarImage[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); RasterAccessor d = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); switch (d.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(sources[0], d); break; case DataBuffer.TYPE_USHORT: computeRectUShort(sources[0], d); break; case DataBuffer.TYPE_SHORT: computeRectShort(sources[0], d); break; case DataBuffer.TYPE_INT: computeRectInt(sources[0], d); break; case DataBuffer.TYPE_FLOAT: computeRectFloat(sources[0], d); break; case DataBuffer.TYPE_DOUBLE: computeRectDouble(sources[0], d); break; } if (d.isDataCopy()) { d.clampDataArrays(); d.copyDataToRaster(); } } private void computeRectByte(PlanarImage src, RasterAccessor dst) { int lpad, rpad, tpad, bpad; if(interp != null) { lpad = interp.getLeftPadding(); rpad = interp.getRightPadding(); tpad = interp.getTopPadding(); bpad = interp.getBottomPadding(); } else { lpad = rpad = tpad = bpad = 0; } int minX, maxX, minY, maxY; RandomIter iter; if(extender != null) { minX = src.getMinX(); maxX = src.getMaxX(); minY = src.getMinY(); maxY = src.getMaxY(); Rectangle bounds = new Rectangle(src.getMinX() - lpad, src.getMinY() - tpad, src.getWidth() + lpad + rpad, src.getHeight() + tpad + bpad); iter = RandomIterFactory.create(src.getExtendedData(bounds, extender), bounds); } else { minX = src.getMinX() + lpad; maxX = src.getMaxX() - rpad; minY = src.getMinY() + tpad; maxY = src.getMaxY() - bpad; iter = RandomIterFactory.create(src, src.getBounds()); } int kwidth = interp.getWidth(); int kheight = interp.getHeight(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int lineStride = dst.getScanlineStride(); int pixelStride = dst.getPixelStride(); int[] bandOffsets = dst.getBandOffsets(); byte[][] data = dst.getByteDataArrays(); int precH = 1 << interp.getSubsampleBitsH(); int precV = 1 << interp.getSubsampleBitsV(); float[] warpData = new float[2 * dstWidth]; int[][] samples = new int[kheight][kwidth]; int lineOffset = 0; byte[] backgroundByte = new byte[dstBands]; for (int i = 0; i < dstBands; i++) backgroundByte[i] = (byte)backgroundValues[i]; if (ctable == null) { // source does not have IndexColorModel for (int h = 0; h < dstHeight; h++) { int pixelOffset = lineOffset; lineOffset += lineStride; warp.warpRect(dst.getX(), dst.getY()+h, dstWidth, 1, warpData); int count = 0; for (int w = 0; w < dstWidth; w++) { float sx = warpData[count++]; float sy = warpData[count++]; int xint = floor(sx); int yint = floor(sy); int xfrac = (int)((sx - xint) * precH); int yfrac = (int)((sy - yint) * precV); if (xint < minX || xint >= maxX || yint < minY || yint >= maxY) { /* Fill with a background color. */ if (setBackground) { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = backgroundByte[b]; } } } else { xint -= lpad; yint -= tpad; for (int b = 0; b < dstBands; b++) { for (int j = 0; j < kheight; j++) { for (int i = 0; i < kwidth; i++) { samples[j][i] = iter.getSample( xint+i, yint+j, b) & 0xFF; } } data[b][pixelOffset+bandOffsets[b]] = ImageUtil.clampByte( interp.interpolate(samples, xfrac, yfrac)); } } pixelOffset += pixelStride; } } } else { // source has IndexColorModel for (int h = 0; h < dstHeight; h++) { int pixelOffset = lineOffset; lineOffset += lineStride; warp.warpRect(dst.getX(), dst.getY()+h, dstWidth, 1, warpData); int count = 0; for (int w = 0; w < dstWidth; w++) { float sx = warpData[count++]; float sy = warpData[count++]; int xint = floor(sx); int yint = floor(sy); int xfrac = (int)((sx - xint) * precH); int yfrac = (int)((sy - yint) * precV); if (xint < minX || xint >= maxX || yint < minY || yint >= maxY) { /* Fill with a background color. */ if (setBackground) { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = backgroundByte[b]; } } } else { xint -= lpad; yint -= tpad; for (int b = 0; b < dstBands; b++) { byte[] t = ctable[b]; for (int j = 0; j < kheight; j++) { for (int i = 0; i < kwidth; i++) { samples[j][i] = t[iter.getSample( xint+i, yint+j, 0) & 0xFF] & 0xFF; } } data[b][pixelOffset+bandOffsets[b]] = ImageUtil.clampByte( interp.interpolate(samples, xfrac, yfrac)); } } pixelOffset += pixelStride; } } } } private void computeRectUShort(PlanarImage src, RasterAccessor dst) { int lpad, rpad, tpad, bpad; if(interp != null) { lpad = interp.getLeftPadding(); rpad = interp.getRightPadding(); tpad = interp.getTopPadding(); bpad = interp.getBottomPadding(); } else { lpad = rpad = tpad = bpad = 0; } int minX, maxX, minY, maxY; RandomIter iter; if(extender != null) { minX = src.getMinX(); maxX = src.getMaxX(); minY = src.getMinY(); maxY = src.getMaxY(); Rectangle bounds = new Rectangle(src.getMinX() - lpad, src.getMinY() - tpad, src.getWidth() + lpad + rpad, src.getHeight() + tpad + bpad); iter = RandomIterFactory.create(src.getExtendedData(bounds, extender), bounds); } else { minX = src.getMinX() + lpad; maxX = src.getMaxX() - rpad; minY = src.getMinY() + tpad; maxY = src.getMaxY() - bpad; iter = RandomIterFactory.create(src, src.getBounds()); } int kwidth = interp.getWidth(); int kheight = interp.getHeight(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int lineStride = dst.getScanlineStride(); int pixelStride = dst.getPixelStride(); int[] bandOffsets = dst.getBandOffsets(); short[][] data = dst.getShortDataArrays(); int precH = 1 << interp.getSubsampleBitsH(); int precV = 1 << interp.getSubsampleBitsV(); float[] warpData = new float[2 * dstWidth]; int[][] samples = new int[kheight][kwidth]; int lineOffset = 0; short[] backgroundUShort = new short[dstBands]; for (int i = 0; i < dstBands; i++) backgroundUShort[i] = (short)backgroundValues[i]; for (int h = 0; h < dstHeight; h++) { int pixelOffset = lineOffset; lineOffset += lineStride; warp.warpRect(dst.getX(), dst.getY()+h, dstWidth, 1, warpData); int count = 0; for (int w = 0; w < dstWidth; w++) { float sx = warpData[count++]; float sy = warpData[count++]; int xint = floor(sx); int yint = floor(sy); int xfrac = (int)((sx - xint) * precH); int yfrac = (int)((sy - yint) * precV); if (xint < minX || xint >= maxX || yint < minY || yint >= maxY) { /* Fill with a background color. */ if (setBackground) { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = backgroundUShort[b]; } } } else { xint -= lpad; yint -= tpad; for (int b = 0; b < dstBands; b++) { for (int j = 0; j < kheight; j++) { for (int i = 0; i < kwidth; i++) { samples[j][i] = iter.getSample( xint+i, yint+j, b) & 0xFFFF; } } data[b][pixelOffset+bandOffsets[b]] = ImageUtil.clampUShort( interp.interpolate(samples, xfrac, yfrac)); } } pixelOffset += pixelStride; } } } private void computeRectShort(PlanarImage src, RasterAccessor dst) { int lpad, rpad, tpad, bpad; if(interp != null) { lpad = interp.getLeftPadding(); rpad = interp.getRightPadding(); tpad = interp.getTopPadding(); bpad = interp.getBottomPadding(); } else { lpad = rpad = tpad = bpad = 0; } int minX, maxX, minY, maxY; RandomIter iter; if(extender != null) { minX = src.getMinX(); maxX = src.getMaxX(); minY = src.getMinY(); maxY = src.getMaxY(); Rectangle bounds = new Rectangle(src.getMinX() - lpad, src.getMinY() - tpad, src.getWidth() + lpad + rpad, src.getHeight() + tpad + bpad); iter = RandomIterFactory.create(src.getExtendedData(bounds, extender), bounds); } else { minX = src.getMinX() + lpad; maxX = src.getMaxX() - rpad; minY = src.getMinY() + tpad; maxY = src.getMaxY() - bpad; iter = RandomIterFactory.create(src, src.getBounds()); } int kwidth = interp.getWidth(); int kheight = interp.getHeight(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int lineStride = dst.getScanlineStride(); int pixelStride = dst.getPixelStride(); int[] bandOffsets = dst.getBandOffsets(); short[][] data = dst.getShortDataArrays(); int precH = 1 << interp.getSubsampleBitsH(); int precV = 1 << interp.getSubsampleBitsV(); float[] warpData = new float[2 * dstWidth]; int[][] samples = new int[kheight][kwidth]; int lineOffset = 0; short[] backgroundShort = new short[dstBands]; for (int i = 0; i < dstBands; i++) backgroundShort[i] = (short)backgroundValues[i]; for (int h = 0; h < dstHeight; h++) { int pixelOffset = lineOffset; lineOffset += lineStride; warp.warpRect(dst.getX(), dst.getY()+h, dstWidth, 1, warpData); int count = 0; for (int w = 0; w < dstWidth; w++) { float sx = warpData[count++]; float sy = warpData[count++]; int xint = floor(sx); int yint = floor(sy); int xfrac = (int)((sx - xint) * precH); int yfrac = (int)((sy - yint) * precV); if (xint < minX || xint >= maxX || yint < minY || yint >= maxY) { /* Fill with a background color. */ if (setBackground) { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = backgroundShort[b]; } } } else { xint -= lpad; yint -= tpad; for (int b = 0; b < dstBands; b++) { for (int j = 0; j < kheight; j++) { for (int i = 0; i < kwidth; i++) { samples[j][i] = iter.getSample( xint+i, yint+j, b); } } data[b][pixelOffset+bandOffsets[b]] = ImageUtil.clampShort( interp.interpolate(samples, xfrac, yfrac)); } } pixelOffset += pixelStride; } } } private void computeRectInt(PlanarImage src, RasterAccessor dst) { int lpad, rpad, tpad, bpad; if(interp != null) { lpad = interp.getLeftPadding(); rpad = interp.getRightPadding(); tpad = interp.getTopPadding(); bpad = interp.getBottomPadding(); } else { lpad = rpad = tpad = bpad = 0; } int minX, maxX, minY, maxY; RandomIter iter; if(extender != null) { minX = src.getMinX(); maxX = src.getMaxX(); minY = src.getMinY(); maxY = src.getMaxY(); Rectangle bounds = new Rectangle(src.getMinX() - lpad, src.getMinY() - tpad, src.getWidth() + lpad + rpad, src.getHeight() + tpad + bpad); iter = RandomIterFactory.create(src.getExtendedData(bounds, extender), bounds); } else { minX = src.getMinX() + lpad; maxX = src.getMaxX() - rpad; minY = src.getMinY() + tpad; maxY = src.getMaxY() - bpad; iter = RandomIterFactory.create(src, src.getBounds()); } int kwidth = interp.getWidth(); int kheight = interp.getHeight(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int lineStride = dst.getScanlineStride(); int pixelStride = dst.getPixelStride(); int[] bandOffsets = dst.getBandOffsets(); int[][] data = dst.getIntDataArrays(); int precH = 1 << interp.getSubsampleBitsH(); int precV = 1 << interp.getSubsampleBitsV(); float[] warpData = new float[2 * dstWidth]; int[][] samples = new int[kheight][kwidth]; int lineOffset = 0; int[] backgroundInt = new int[dstBands]; for (int i = 0; i < dstBands; i++) backgroundInt[i] = (int)backgroundValues[i]; for (int h = 0; h < dstHeight; h++) { int pixelOffset = lineOffset; lineOffset += lineStride; warp.warpRect(dst.getX(), dst.getY()+h, dstWidth, 1, warpData); int count = 0; for (int w = 0; w < dstWidth; w++) { float sx = warpData[count++]; float sy = warpData[count++]; int xint = floor(sx); int yint = floor(sy); int xfrac = (int)((sx - xint) * precH); int yfrac = (int)((sy - yint) * precV); if (xint < minX || xint >= maxX || yint < minY || yint >= maxY) { /* Fill with a background color. */ if (setBackground) { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = backgroundInt[b]; } } } else { xint -= lpad; yint -= tpad; for (int b = 0; b < dstBands; b++) { for (int j = 0; j < kheight; j++) { for (int i = 0; i < kwidth; i++) { samples[j][i] = iter.getSample( xint+i, yint+j, b); } } data[b][pixelOffset+bandOffsets[b]] = interp.interpolate(samples, xfrac, yfrac); } } pixelOffset += pixelStride; } } } private void computeRectFloat(PlanarImage src, RasterAccessor dst) { int lpad, rpad, tpad, bpad; if(interp != null) { lpad = interp.getLeftPadding(); rpad = interp.getRightPadding(); tpad = interp.getTopPadding(); bpad = interp.getBottomPadding(); } else { lpad = rpad = tpad = bpad = 0; } int minX, maxX, minY, maxY; RandomIter iter; if(extender != null) { minX = src.getMinX(); maxX = src.getMaxX(); minY = src.getMinY(); maxY = src.getMaxY(); Rectangle bounds = new Rectangle(src.getMinX() - lpad, src.getMinY() - tpad, src.getWidth() + lpad + rpad, src.getHeight() + tpad + bpad); iter = RandomIterFactory.create(src.getExtendedData(bounds, extender), bounds); } else { minX = src.getMinX() + lpad; maxX = src.getMaxX() - rpad; minY = src.getMinY() + tpad; maxY = src.getMaxY() - bpad; iter = RandomIterFactory.create(src, src.getBounds()); } int kwidth = interp.getWidth(); int kheight = interp.getHeight(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int lineStride = dst.getScanlineStride(); int pixelStride = dst.getPixelStride(); int[] bandOffsets = dst.getBandOffsets(); float[][] data = dst.getFloatDataArrays(); float[] warpData = new float[2 * dstWidth]; float[][] samples = new float[kheight][kwidth]; int lineOffset = 0; float[] backgroundFloat = new float[dstBands]; for (int i = 0; i < dstBands; i++) backgroundFloat[i] = (float)backgroundValues[i]; for (int h = 0; h < dstHeight; h++) { int pixelOffset = lineOffset; lineOffset += lineStride; warp.warpRect(dst.getX(), dst.getY()+h, dstWidth, 1, warpData); int count = 0; for (int w = 0; w < dstWidth; w++) { float sx = warpData[count++]; float sy = warpData[count++]; int xint = floor(sx); int yint = floor(sy); float xfrac = sx - xint; float yfrac = sy - yint; if (xint < minX || xint >= maxX || yint < minY || yint >= maxY) { /* Fill with a background color. */ if (setBackground) { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = backgroundFloat[b]; } } } else { xint -= lpad; yint -= tpad; for (int b = 0; b < dstBands; b++) { for (int j = 0; j < kheight; j++) { for (int i = 0; i < kwidth; i++) { samples[j][i] = iter.getSampleFloat( xint+i, yint+j, b); } } data[b][pixelOffset+bandOffsets[b]] = interp.interpolate(samples, xfrac, yfrac); } } pixelOffset += pixelStride; } } } private void computeRectDouble(PlanarImage src, RasterAccessor dst) { int lpad, rpad, tpad, bpad; if(interp != null) { lpad = interp.getLeftPadding(); rpad = interp.getRightPadding(); tpad = interp.getTopPadding(); bpad = interp.getBottomPadding(); } else { lpad = rpad = tpad = bpad = 0; } int minX, maxX, minY, maxY; RandomIter iter; if(extender != null) { minX = src.getMinX(); maxX = src.getMaxX(); minY = src.getMinY(); maxY = src.getMaxY(); Rectangle bounds = new Rectangle(src.getMinX() - lpad, src.getMinY() - tpad, src.getWidth() + lpad + rpad, src.getHeight() + tpad + bpad); iter = RandomIterFactory.create(src.getExtendedData(bounds, extender), bounds); } else { minX = src.getMinX() + lpad; maxX = src.getMaxX() - rpad; minY = src.getMinY() + tpad; maxY = src.getMaxY() - bpad; iter = RandomIterFactory.create(src, src.getBounds()); } int kwidth = interp.getWidth(); int kheight = interp.getHeight(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int lineStride = dst.getScanlineStride(); int pixelStride = dst.getPixelStride(); int[] bandOffsets = dst.getBandOffsets(); double[][] data = dst.getDoubleDataArrays(); float[] warpData = new float[2 * dstWidth]; double[][] samples = new double[kheight][kwidth]; int lineOffset = 0; for (int h = 0; h < dstHeight; h++) { int pixelOffset = lineOffset; lineOffset += lineStride; warp.warpRect(dst.getX(), dst.getY()+h, dstWidth, 1, warpData); int count = 0; for (int w = 0; w < dstWidth; w++) { float sx = warpData[count++]; float sy = warpData[count++]; int xint = floor(sx); int yint = floor(sy); float xfrac = sx - xint; float yfrac = sy - yint; if (xint < minX || xint >= maxX || yint < minY || yint >= maxY) { /* Fill with a background color. */ if (setBackground) { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = backgroundValues[b]; } } } else { xint -= lpad; yint -= tpad; for (int b = 0; b < dstBands; b++) { for (int j = 0; j < kheight; j++) { for (int i = 0; i < kwidth; i++) { samples[j][i] = iter.getSampleDouble( xint+i, yint+j, b); } } data[b][pixelOffset+bandOffsets[b]] = interp.interpolate(samples, xfrac, yfrac); } } pixelOffset += pixelStride; } } } /** Returns the "floor" value of a float. */ private static final int floor(float f) { return f >= 0 ? (int)f : (int)f - 1; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MultiplyComplexCRIF.java0000644000175000017500000000357310203035544027716 0ustar mathieumathieu/* * $RCSfile: MultiplyComplexCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:36 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.geom.Rectangle2D; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderContext; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "MultiplyComplex" operation in the * rendered and renderable image layers. * * @since EA4 * @see javax.media.jai.operator.MultiplyComplexDescriptor * @see ComplexArithmeticOpImage * */ public class MultiplyComplexCRIF extends CRIFImpl { /** Constructor. */ public MultiplyComplexCRIF() { super("multiplycomplex"); } /** * Creates a new instance of ComplexArithmeticOpImage in the * rendered layer. This method satisifies the implementation of RIF. * * @param paramBlock The two source images to be multiplied. * @param renderHints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new ComplexArithmeticOpImage(paramBlock.getRenderedSource(0), paramBlock.getRenderedSource(1), renderHints, layout, false); // false implies multiply } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/DivideByConstCRIF.java0000644000175000017500000000341010203035544027243 0ustar mathieumathieu/* * $RCSfile: DivideByConstCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:23 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "DivideByConst" operation in the rendered * and renderable image layers. * * @see javax.media.jai.operator.DivideByConstDescriptor * @see MultiplyConstOpImage * * * @since EA2 */ public class DivideByConstCRIF extends CRIFImpl { /** Constructor. */ public DivideByConstCRIF() { super("dividebyconst"); } /** * Creates a new instance of DivideByConstOpImage in the * rendered layer. * * @param args The source image and the constants. * @param hints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); /* Invert the constants. */ double[] constants = (double[])args.getObjectParameter(0); int length = constants.length; double[] invConstants = new double[length]; for (int i = 0; i < length; i++) { invConstants[i] = 1.0 / constants[i]; } return new MultiplyConstOpImage(args.getRenderedSource(0), renderHints, layout, invConstants); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/WarpNearestOpImage.java0000644000175000017500000004233710203035544027601 0ustar mathieumathieu/* * $RCSfile: WarpNearestOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:47 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.IndexColorModel; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.PlanarImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.RasterFormatTag; import java.util.Map; import javax.media.jai.Warp; import javax.media.jai.WarpOpImage; import javax.media.jai.iterator.RandomIter; import javax.media.jai.iterator.RandomIterFactory; /** * An OpImage implementing the general "Warp" operation as * described in javax.media.jai.operator.WarpDescriptor. * It supports the nearest-neighbor interpolation. * *

The layout for the destination image may be specified via the * ImageLayout parameter. However, only those settings * suitable for this operation will be used. The unsuitable settings * will be replaced by default suitable values. * * @since EA2 * @see javax.media.jai.Warp * @see javax.media.jai.WarpOpImage * @see javax.media.jai.operator.WarpDescriptor * @see WarpRIF * */ final class WarpNearestOpImage extends WarpOpImage { /** * Constructs a WarpNearestOpImage. * * @param source The source image. * @param layout The destination image layout. * @param warp An object defining the warp algorithm. * @param interp An object describing the interpolation method. */ public WarpNearestOpImage(RenderedImage source, Map config, ImageLayout layout, Warp warp, Interpolation interp, double[] backgroundValues) { super(source, layout, config, false, null, // extender interp, warp, backgroundValues); /* * If the source has IndexColorModel, override the default setting * in OpImage. The dest shall have exactly the same SampleModel and * ColorModel as the source. * Note, in this case, the source should have an integral data type. */ ColorModel srcColorModel = source.getColorModel(); if (srcColorModel instanceof IndexColorModel) { sampleModel = source.getSampleModel().createCompatibleSampleModel( tileWidth, tileHeight); colorModel = srcColorModel; } } /** Warps a rectangle. */ protected void computeRect(PlanarImage[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); RasterAccessor d = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); switch (d.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(sources[0], d); break; case DataBuffer.TYPE_USHORT: computeRectUShort(sources[0], d); break; case DataBuffer.TYPE_SHORT: computeRectShort(sources[0], d); break; case DataBuffer.TYPE_INT: computeRectInt(sources[0], d); break; case DataBuffer.TYPE_FLOAT: computeRectFloat(sources[0], d); break; case DataBuffer.TYPE_DOUBLE: computeRectDouble(sources[0], d); break; } if (d.isDataCopy()) { d.clampDataArrays(); d.copyDataToRaster(); } } private void computeRectByte(PlanarImage src, RasterAccessor dst) { RandomIter iter = RandomIterFactory.create(src, src.getBounds()); int minX = src.getMinX(); int maxX = src.getMaxX(); int minY = src.getMinY(); int maxY = src.getMaxY(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int lineStride = dst.getScanlineStride(); int pixelStride = dst.getPixelStride(); int[] bandOffsets = dst.getBandOffsets(); byte[][] data = dst.getByteDataArrays(); float[] warpData = new float[2 * dstWidth]; int lineOffset = 0; byte[] backgroundByte = new byte[dstBands]; for (int i = 0; i < dstBands; i++) backgroundByte[i] = (byte)backgroundValues[i]; for (int h = 0; h < dstHeight; h++) { int pixelOffset = lineOffset; lineOffset += lineStride; warp.warpRect(dst.getX(), dst.getY()+h, dstWidth, 1, warpData); int count = 0; for (int w = 0; w < dstWidth; w++) { /* * The warp object subtract 0.5 from backward mapped * source coordinate. Need to do a round to get the * nearest neighbor. This is different from the standard * nearest implementation. */ int sx = round(warpData[count++]); int sy = round(warpData[count++]); if (sx < minX || sx >= maxX || sy < minY || sy >= maxY) { /* Fill with a background color. */ if (setBackground) { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = backgroundByte[b]; } } } else { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = (byte)(iter.getSample(sx, sy, b) & 0xFF); } } pixelOffset += pixelStride; } } } private void computeRectUShort(PlanarImage src, RasterAccessor dst) { RandomIter iter = RandomIterFactory.create(src, src.getBounds()); int minX = src.getMinX(); int maxX = src.getMaxX(); int minY = src.getMinY(); int maxY = src.getMaxY(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int lineStride = dst.getScanlineStride(); int pixelStride = dst.getPixelStride(); int[] bandOffsets = dst.getBandOffsets(); short[][] data = dst.getShortDataArrays(); float[] warpData = new float[2 * dstWidth]; int lineOffset = 0; short[] backgroundUShort = new short[dstBands]; for (int i = 0; i < dstBands; i++) backgroundUShort[i] = (short)backgroundValues[i]; for (int h = 0; h < dstHeight; h++) { int pixelOffset = lineOffset; lineOffset += lineStride; warp.warpRect(dst.getX(), dst.getY()+h, dstWidth, 1, warpData); int count = 0; for (int w = 0; w < dstWidth; w++) { /* * The warp object subtract 0.5 from backward mapped * source coordinate. Need to do a round to get the * nearest neighbor. This is different from the standard * nearest implementation. */ int sx = round(warpData[count++]); int sy = round(warpData[count++]); if (sx < minX || sx >= maxX || sy < minY || sy >= maxY) { /* Fill with a background color. */ if (setBackground) { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = backgroundUShort[b]; } } } else { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = (short)(iter.getSample(sx, sy, b) & 0xFFFF); } } pixelOffset += pixelStride; } } } private void computeRectShort(PlanarImage src, RasterAccessor dst) { RandomIter iter = RandomIterFactory.create(src, src.getBounds()); int minX = src.getMinX(); int maxX = src.getMaxX(); int minY = src.getMinY(); int maxY = src.getMaxY(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int lineStride = dst.getScanlineStride(); int pixelStride = dst.getPixelStride(); int[] bandOffsets = dst.getBandOffsets(); short[][] data = dst.getShortDataArrays(); float[] warpData = new float[2 * dstWidth]; int lineOffset = 0; short[] backgroundShort = new short[dstBands]; for (int i = 0; i < dstBands; i++) backgroundShort[i] = (short)backgroundValues[i]; for (int h = 0; h < dstHeight; h++) { int pixelOffset = lineOffset; lineOffset += lineStride; warp.warpRect(dst.getX(), dst.getY()+h, dstWidth, 1, warpData); int count = 0; for (int w = 0; w < dstWidth; w++) { /* * The warp object subtract 0.5 from backward mapped * source coordinate. Need to do a round to get the * nearest neighbor. This is different from the standard * nearest implementation. */ int sx = round(warpData[count++]); int sy = round(warpData[count++]); if (sx < minX || sx >= maxX || sy < minY || sy >= maxY) { /* Fill with a background color. */ if (setBackground) { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = backgroundShort[b]; } } } else { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = (short)iter.getSample(sx, sy, b); } } pixelOffset += pixelStride; } } } private void computeRectInt(PlanarImage src, RasterAccessor dst) { RandomIter iter = RandomIterFactory.create(src, src.getBounds()); int minX = src.getMinX(); int maxX = src.getMaxX(); int minY = src.getMinY(); int maxY = src.getMaxY(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int lineStride = dst.getScanlineStride(); int pixelStride = dst.getPixelStride(); int[] bandOffsets = dst.getBandOffsets(); int[][] data = dst.getIntDataArrays(); float[] warpData = new float[2 * dstWidth]; int lineOffset = 0; int[] backgroundInt = new int[dstBands]; for (int i = 0; i < dstBands; i++) backgroundInt[i] = (int)backgroundValues[i]; for (int h = 0; h < dstHeight; h++) { int pixelOffset = lineOffset; lineOffset += lineStride; warp.warpRect(dst.getX(), dst.getY()+h, dstWidth, 1, warpData); int count = 0; for (int w = 0; w < dstWidth; w++) { /* * The warp object subtract 0.5 from backward mapped * source coordinate. Need to do a round to get the * nearest neighbor. This is different from the standard * nearest implementation. */ int sx = round(warpData[count++]); int sy = round(warpData[count++]); if (sx < minX || sx >= maxX || sy < minY || sy >= maxY) { /* Fill with a background color. */ if (setBackground) { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = backgroundInt[b]; } } } else { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = iter.getSample(sx, sy, b); } } pixelOffset += pixelStride; } } } private void computeRectFloat(PlanarImage src, RasterAccessor dst) { RandomIter iter = RandomIterFactory.create(src, src.getBounds()); int minX = src.getMinX(); int maxX = src.getMaxX(); int minY = src.getMinY(); int maxY = src.getMaxY(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int lineStride = dst.getScanlineStride(); int pixelStride = dst.getPixelStride(); int[] bandOffsets = dst.getBandOffsets(); float[][] data = dst.getFloatDataArrays(); float[] warpData = new float[2 * dstWidth]; int lineOffset = 0; float[] backgroundFloat = new float[dstBands]; for (int i = 0; i < dstBands; i++) backgroundFloat[i] = (float)backgroundValues[i]; for (int h = 0; h < dstHeight; h++) { int pixelOffset = lineOffset; lineOffset += lineStride; warp.warpRect(dst.getX(), dst.getY()+h, dstWidth, 1, warpData); int count = 0; for (int w = 0; w < dstWidth; w++) { /* * The warp object subtract 0.5 from backward mapped * source coordinate. Need to do a round to get the * nearest neighbor. This is different from the standard * nearest implementation. */ int sx = round(warpData[count++]); int sy = round(warpData[count++]); if (sx < minX || sx >= maxX || sy < minY || sy >= maxY) { /* Fill with a background color. */ if (setBackground) { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = backgroundFloat[b]; } } } else { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = iter.getSampleFloat(sx, sy, b); } } pixelOffset += pixelStride; } } } private void computeRectDouble(PlanarImage src, RasterAccessor dst) { RandomIter iter = RandomIterFactory.create(src, src.getBounds()); int minX = src.getMinX(); int maxX = src.getMaxX(); int minY = src.getMinY(); int maxY = src.getMaxY(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int lineStride = dst.getScanlineStride(); int pixelStride = dst.getPixelStride(); int[] bandOffsets = dst.getBandOffsets(); double[][] data = dst.getDoubleDataArrays(); float[] warpData = new float[2 * dstWidth]; int lineOffset = 0; for (int h = 0; h < dstHeight; h++) { int pixelOffset = lineOffset; lineOffset += lineStride; warp.warpRect(dst.getX(), dst.getY()+h, dstWidth, 1, warpData); int count = 0; for (int w = 0; w < dstWidth; w++) { /* * The warp object subtract 0.5 from backward mapped * source coordinate. Need to do a round to get the * nearest neighbor. This is different from the standard * nearest implementation. */ int sx = round(warpData[count++]); int sy = round(warpData[count++]); if (sx < minX || sx >= maxX || sy < minY || sy >= maxY) { /* Fill with a background color. */ if (setBackground) { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = backgroundValues[b]; } } } else { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = iter.getSampleDouble(sx, sy, b); } } pixelOffset += pixelStride; } } } /** Returns the "round" value of a float. */ private static final int round(float f) { return f >= 0 ? (int)(f + 0.5F) : (int)(f - 0.5F); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ExtremaOpImage.java0000644000175000017500000010764310203035544026755 0ustar mathieumathieu/* * $RCSfile: ExtremaOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:25 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Image; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.util.ArrayList; import java.util.Hashtable; import java.util.LinkedList; import java.util.ListIterator; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PixelAccessor; import javax.media.jai.PlanarImage; import javax.media.jai.ROI; import javax.media.jai.StatisticsOpImage; import javax.media.jai.UnpackedImageData; import com.sun.media.jai.util.ImageUtil; /** * An OpImage implementing the "Extrema" operation as * described in javax.media.jai.operator.ExtremaDescriptor. * * @see javax.media.jai.operator.ExtremaDescriptor * @see ExtremaCRIF */ public class ExtremaOpImage extends StatisticsOpImage { protected double[][] extrema; protected ArrayList[] minLocations; protected ArrayList[] maxLocations; protected int[] minCounts; protected int[] maxCounts; protected boolean saveLocations; protected int maxRuns; protected int numMinLocations = 0; protected int numMaxLocations = 0; private boolean isInitialized = false; private PixelAccessor srcPA; private int srcSampleType; private final boolean tileIntersectsROI(int tileX, int tileY) { if (roi == null) { // ROI is entire tile return true; } else { return roi.intersects(tileXToX(tileX), tileYToY(tileY), tileWidth, tileHeight); } } /** * Constructs an ExtremaOpImage. * * @param source The source image. */ public ExtremaOpImage(RenderedImage source, ROI roi, int xStart, int yStart, int xPeriod, int yPeriod, boolean saveLocations, int maxRuns) { super(source, roi, xStart, yStart, xPeriod, yPeriod); extrema = null; this.saveLocations = saveLocations; this.maxRuns = maxRuns; } /** Returns one of the available statistics as a property. */ public Object getProperty(String name) { int numBands = sampleModel.getNumBands(); if (extrema == null) { // Statistics have not been accumulated: call superclass // method to do so. return super.getProperty(name); } else if (name.equalsIgnoreCase("extrema")) { double[][] stats = new double[2][numBands]; for (int i = 0; i < numBands; i++) { stats[0][i] = extrema[0][i]; stats[1][i] = extrema[1][i]; } return stats; } else if (name.equalsIgnoreCase("minimum")) { double[] stats = new double[numBands]; for (int i = 0; i < numBands; i++) { stats[i] = extrema[0][i]; } return stats; } else if (name.equalsIgnoreCase("maximum")) { double[] stats = new double[numBands]; for (int i = 0; i < numBands; i++) { stats[i] = extrema[1][i]; } return stats; } else if (saveLocations && name.equalsIgnoreCase("minLocations")) { return minLocations; } else if (saveLocations && name.equalsIgnoreCase("maxLocations")) { return maxLocations; } return java.awt.Image.UndefinedProperty; } protected String[] getStatisticsNames() { return new String[] {"extrema", "maximum", "minimum", "maxLocations", "minLocations"}; } protected Object createStatistics(String name) { int numBands = sampleModel.getNumBands(); Object stats = null; if (name.equalsIgnoreCase("extrema")) { stats = new double[2][numBands]; } else if (name.equalsIgnoreCase("minimum") || name.equalsIgnoreCase("maximum")) { stats = new double[numBands]; } else if (saveLocations && (name.equalsIgnoreCase("minLocations") || name.equalsIgnoreCase("maxLocations"))){ stats = new ArrayList[numBands]; } else { stats = java.awt.Image.UndefinedProperty; } return stats; } private final int startPosition(int pos, int start, int period) { int t = (pos - start) % period; return t == 0 ? pos : pos + (period - t); } protected void accumulateStatistics(String name, Raster source, Object stats) { if(!isInitialized) { srcPA = new PixelAccessor(getSourceImage(0)); srcSampleType = srcPA.sampleType == PixelAccessor.TYPE_BIT ? DataBuffer.TYPE_BYTE : srcPA.sampleType; isInitialized = true; } Rectangle srcBounds = getSourceImage(0).getBounds().intersection( source.getBounds()); LinkedList rectList; if (roi == null) { // ROI is the whole Raster rectList = new LinkedList(); rectList.addLast(srcBounds); } else { rectList = roi.getAsRectangleList(srcBounds.x, srcBounds.y, srcBounds.width, srcBounds.height); if (rectList == null) { return; // ROI does not intersect with Raster boundary. } } ListIterator iterator = rectList.listIterator(0); while (iterator.hasNext()) { Rectangle rect = srcBounds.intersection((Rectangle)iterator.next()); int tx = rect.x; int ty = rect.y; // Find the actual ROI based on start and period. rect.x = startPosition(tx, xStart, xPeriod); rect.y = startPosition(ty, yStart, yPeriod); rect.width = tx + rect.width - rect.x; rect.height = ty + rect.height - rect.y; if (rect.isEmpty()) { continue; // no pixel to count in this rectangle } initializeState(source); UnpackedImageData uid = srcPA.getPixels(source, rect, srcSampleType, false); switch (uid.type) { case DataBuffer.TYPE_BYTE: accumulateStatisticsByte(uid); break; case DataBuffer.TYPE_USHORT: accumulateStatisticsUShort(uid); break; case DataBuffer.TYPE_SHORT: accumulateStatisticsShort(uid); break; case DataBuffer.TYPE_INT: accumulateStatisticsInt(uid); break; case DataBuffer.TYPE_FLOAT: accumulateStatisticsFloat(uid); break; case DataBuffer.TYPE_DOUBLE: accumulateStatisticsDouble(uid); break; } } if (name.equalsIgnoreCase("extrema")) { double[][] ext = (double[][])stats; for (int i = 0; i < srcPA.numBands; i++) { ext[0][i] = extrema[0][i]; ext[1][i] = extrema[1][i]; } } else if (name.equalsIgnoreCase("minimum")) { double[] min = (double[])stats; for (int i = 0; i < srcPA.numBands; i++) { min[i] = extrema[0][i]; } } else if (name.equalsIgnoreCase("maximum")) { double[] max = (double[])stats; for (int i = 0; i < srcPA.numBands; i++) { max[i] = extrema[1][i]; } } else if (name.equalsIgnoreCase("minLocations")) { ArrayList[] minLoc = (ArrayList[])stats; for (int i = 0; i < srcPA.numBands; i++) { minLoc[i] = minLocations[i]; } } else if (name.equalsIgnoreCase("maxLocations")) { ArrayList[] maxLoc = (ArrayList[])stats; for (int i = 0; i < srcPA.numBands; i++) maxLoc[i] = maxLocations[i]; } } private void accumulateStatisticsByte(UnpackedImageData uid) { Rectangle rect = uid.rect; byte[][] data = uid.getByteData(); int lineStride = uid.lineStride; int pixelStride = uid.pixelStride; int lineInc = lineStride * yPeriod; int pixelInc = pixelStride * xPeriod; if (!saveLocations) { for (int b = 0; b < srcPA.numBands; b++) { int min = (int)extrema[0][b]; // minimum int max = (int)extrema[1][b]; // maximum byte[] d = data[b]; int lastLine = uid.bandOffsets[b] + rect.height * lineStride; for (int lo = uid.bandOffsets[b]; lo < lastLine; lo += lineInc) { int lastPixel = lo + rect.width * pixelStride; for (int po = lo; po < lastPixel; po += pixelInc) { int p = d[po] & 0xff; if (p < min) { min = p; } else if (p > max) { max = p; } } } extrema[0][b] = min; extrema[1][b] = max; } } else { for (int b = 0; b < srcPA.numBands; b++) { int min = (int)extrema[0][b]; // minimum int max = (int)extrema[1][b]; // maximum ArrayList minList = minLocations[b]; ArrayList maxList = maxLocations[b]; int minCount = minCounts[b]; int maxCount = maxCounts[b]; byte[] d = data[b]; int lastLine = uid.bandOffsets[b] + rect.height * lineStride; for (int lo = uid.bandOffsets[b], y = rect.y; lo < lastLine; lo += lineInc, y += yPeriod) { int lastPixel = lo + rect.width * pixelStride; int minStart = 0; int maxStart = 0; int minLength = 0; int maxLength = 0; for (int po = lo, x = rect.x; po < lastPixel; po += pixelInc, x += xPeriod) { int p = d[po] & 0xff; if (p < min) { min = p; minStart = x; minLength = 1; minList.clear(); minCount = 0; } else if (p > max) { max = p; maxStart = x; maxLength = 1; maxList.clear(); maxCount = 0; } else { if (p == min) { if (minLength == 0) minStart = x; minLength++; } else if (minLength > 0 && minCount < maxRuns) { minList.add(new int[]{minStart, y, minLength}); minCount++; minLength = 0; } if (p == max) { if (maxLength == 0) maxStart = x; maxLength++; } else if (maxLength > 0 && maxCount < maxRuns) { maxList.add(new int[]{maxStart, y, maxLength}); maxCount++; maxLength = 0; } } } if (maxLength > 0 && maxCount < maxRuns) { maxList.add(new int[]{maxStart, y, maxLength}); maxCount++; } if (minLength > 0 && minCount < maxRuns) { minList.add(new int[]{minStart, y, minLength}); minCount++; } } extrema[0][b] = min; extrema[1][b] = max; minCounts[b] = minCount; maxCounts[b] = maxCount; } } } private void accumulateStatisticsUShort(UnpackedImageData uid) { Rectangle rect = uid.rect; short[][] data = uid.getShortData(); int lineStride = uid.lineStride; int pixelStride = uid.pixelStride; int lineInc = lineStride * yPeriod; int pixelInc = pixelStride * xPeriod; if (!saveLocations) { for (int b = 0; b < srcPA.numBands; b++) { int min = (int)extrema[0][b]; // minimum int max = (int)extrema[1][b]; // maximum short[] d = data[b]; int lastLine = uid.bandOffsets[b] + rect.height * lineStride; for (int lo = uid.bandOffsets[b]; lo < lastLine; lo += lineInc) { int lastPixel = lo + rect.width * pixelStride; for (int po = lo; po < lastPixel; po += pixelInc) { int p = d[po] & 0xffff; if (p < min) { min = p; } else if (p > max) { max = p; } } } extrema[0][b] = min; extrema[1][b] = max; } } else { for (int b = 0; b < srcPA.numBands; b++) { int min = (int)extrema[0][b]; // minimum int max = (int)extrema[1][b]; // maximum ArrayList minList = minLocations[b]; ArrayList maxList = maxLocations[b]; int minCount = minCounts[b]; int maxCount = maxCounts[b]; short[] d = data[b]; int lastLine = uid.bandOffsets[b] + rect.height * lineStride; for (int lo = uid.bandOffsets[b], y = rect.y; lo < lastLine; lo += lineInc, y += yPeriod) { int lastPixel = lo + rect.width * pixelStride; int minStart = 0; int maxStart = 0; int minLength = 0; int maxLength = 0; for (int po = lo, x = rect.x; po < lastPixel; po += pixelInc, x += xPeriod) { int p = d[po] & 0xffff; if (p < min) { min = p; minStart = x; minLength = 1; minList.clear(); minCount = 0; } else if (p > max) { max = p; maxStart = x; maxLength = 1; maxList.clear(); maxCount = 0; } else { if (p == min) { if (minLength == 0) minStart = x; minLength++; } else if (minLength > 0 && minCount < maxRuns) { minList.add(new int[]{minStart, y, minLength}); minCount++; minLength = 0; } if (p == max) { if (maxLength == 0) maxStart = x; maxLength++; } else if (maxLength > 0 && maxCount < maxRuns) { maxList.add(new int[]{maxStart, y, maxLength}); maxCount++; maxLength = 0; } } } if (maxLength > 0 && maxCount < maxRuns) { maxList.add(new int[]{maxStart, y, maxLength}); maxCount++; } if (minLength > 0 && minCount < maxRuns) { minList.add(new int[]{minStart, y, minLength}); minCount++; } } extrema[0][b] = min; extrema[1][b] = max; minCounts[b] = minCount; maxCounts[b] = maxCount; } } } private void accumulateStatisticsShort(UnpackedImageData uid) { Rectangle rect = uid.rect; short[][] data = uid.getShortData(); int lineStride = uid.lineStride; int pixelStride = uid.pixelStride; int lineInc = lineStride * yPeriod; int pixelInc = pixelStride * xPeriod; if (!saveLocations) { for (int b = 0; b < srcPA.numBands; b++) { int min = (int)extrema[0][b]; // minimum int max = (int)extrema[1][b]; // maximum short[] d = data[b]; int lastLine = uid.bandOffsets[b] + rect.height * lineStride; for (int lo = uid.bandOffsets[b]; lo < lastLine; lo += lineInc) { int lastPixel = lo + rect.width * pixelStride; for (int po = lo; po < lastPixel; po += pixelInc) { int p = d[po]; if (p < min) { min = p; } else if (p > max) { max = p; } } } extrema[0][b] = min; extrema[1][b] = max; } } else { for (int b = 0; b < srcPA.numBands; b++) { int min = (int)extrema[0][b]; // minimum int max = (int)extrema[1][b]; // maximum ArrayList minList = minLocations[b]; ArrayList maxList = maxLocations[b]; int minCount = minCounts[b]; int maxCount = maxCounts[b]; short[] d = data[b]; int lastLine = uid.bandOffsets[b] + rect.height * lineStride; for (int lo = uid.bandOffsets[b], y = rect.y; lo < lastLine; lo += lineInc, y += yPeriod) { int lastPixel = lo + rect.width * pixelStride; int minStart = 0; int maxStart = 0; int minLength = 0; int maxLength = 0; for (int po = lo, x = rect.x; po < lastPixel; po += pixelInc, x += xPeriod) { int p = d[po]; if (p < min) { min = p; minStart = x; minLength = 1; minList.clear(); minCount = 0; } else if (p > max) { max = p; maxStart = x; maxLength = 1; maxList.clear(); maxCount = 0; } else { if (p == min) { if (minLength == 0) minStart = x; minLength++; } else if (minLength > 0 && minCount < maxRuns) { minList.add(new int[]{minStart, y, minLength}); minCount++; minLength = 0; } if (p == max) { if (maxLength == 0) maxStart = x; maxLength++; } else if (maxLength > 0 && maxCount < maxRuns) { maxList.add(new int[]{maxStart, y, maxLength}); maxCount++; maxLength = 0; } } } if (maxLength > 0 && maxCount < maxRuns) { maxList.add(new int[]{maxStart, y, maxLength}); maxCount++; } if (minLength > 0 && minCount < maxRuns) { minList.add(new int[]{minStart, y, minLength}); minCount++; } } extrema[0][b] = min; extrema[1][b] = max; minCounts[b] = minCount; maxCounts[b] = maxCount; } } } private void accumulateStatisticsInt(UnpackedImageData uid) { Rectangle rect = uid.rect; int[][] data = uid.getIntData(); int lineStride = uid.lineStride; int pixelStride = uid.pixelStride; int lineInc = lineStride * yPeriod; int pixelInc = pixelStride * xPeriod; if (!saveLocations) { for (int b = 0; b < srcPA.numBands; b++) { int min = (int)extrema[0][b]; // minimum int max = (int)extrema[1][b]; // maximum int[] d = data[b]; int lastLine = uid.bandOffsets[b] + rect.height * lineStride; for (int lo = uid.bandOffsets[b]; lo < lastLine; lo += lineInc) { int lastPixel = lo + rect.width * pixelStride; for (int po = lo; po < lastPixel; po += pixelInc) { int p = d[po]; if (p < min) { min = p; } else if (p > max) { max = p; } } } extrema[0][b] = min; extrema[1][b] = max; } } else { for (int b = 0; b < srcPA.numBands; b++) { int min = (int)extrema[0][b]; // minimum int max = (int)extrema[1][b]; // maximum ArrayList minList = minLocations[b]; ArrayList maxList = maxLocations[b]; int minCount = minCounts[b]; int maxCount = maxCounts[b]; int[] d = data[b]; int lastLine = uid.bandOffsets[b] + rect.height * lineStride; for (int lo = uid.bandOffsets[b], y = rect.y; lo < lastLine; lo += lineInc, y += yPeriod) { int lastPixel = lo + rect.width * pixelStride; int minStart = 0; int maxStart = 0; int minLength = 0; int maxLength = 0; for (int po = lo, x = rect.x; po < lastPixel; po += pixelInc, x += xPeriod) { int p = d[po]; if (p < min) { min = p; minStart = x; minLength = 1; minList.clear(); minCount = 0; } else if (p > max) { max = p; maxStart = x; maxLength = 1; maxList.clear(); maxCount = 0; } else { if (p == min) { if (minLength == 0) minStart = x; minLength++; } else if (minLength > 0 && minCount < maxRuns) { minList.add(new int[]{minStart, y, minLength}); minCount++; minLength = 0; } if (p == max) { if (maxLength == 0) maxStart = x; maxLength++; } else if (maxLength > 0 && maxCount < maxRuns) { maxList.add(new int[]{maxStart, y, maxLength}); maxCount++; maxLength = 0; } } } if (maxLength > 0 && maxCount < maxRuns) { maxList.add(new int[]{maxStart, y, maxLength}); maxCount++; } if (minLength > 0 && minCount < maxRuns) { minList.add(new int[]{minStart, y, minLength}); minCount++; } } extrema[0][b] = min; extrema[1][b] = max; minCounts[b] = minCount; maxCounts[b] = maxCount; } } } private void accumulateStatisticsFloat(UnpackedImageData uid) { Rectangle rect = uid.rect; float[][] data = uid.getFloatData(); int lineStride = uid.lineStride; int pixelStride = uid.pixelStride; int lineInc = lineStride * yPeriod; int pixelInc = pixelStride * xPeriod; if (!saveLocations) { for (int b = 0; b < srcPA.numBands; b++) { float min = (float)extrema[0][b]; // minimum float max = (float)extrema[1][b]; // maximum float[] d = data[b]; int lastLine = uid.bandOffsets[b] + rect.height * lineStride; for (int lo = uid.bandOffsets[b]; lo < lastLine; lo += lineInc) { int lastPixel = lo + rect.width * pixelStride; for (int po = lo; po < lastPixel; po += pixelInc) { float p = d[po]; if (p < min) { min = p; } else if (p > max) { max = p; } } } extrema[0][b] = min; extrema[1][b] = max; } } else { for (int b = 0; b < srcPA.numBands; b++) { float min = (float)extrema[0][b]; // minimum float max = (float)extrema[1][b]; // maximum ArrayList minList = minLocations[b]; ArrayList maxList = maxLocations[b]; int minCount = minCounts[b]; int maxCount = maxCounts[b]; float[] d = data[b]; int lastLine = uid.bandOffsets[b] + rect.height * lineStride; for (int lo = uid.bandOffsets[b], y = rect.y; lo < lastLine; lo += lineInc, y += yPeriod) { int lastPixel = lo + rect.width * pixelStride; int minStart = 0; int maxStart = 0; int minLength = 0; int maxLength = 0; for (int po = lo, x = rect.x; po < lastPixel; po += pixelInc, x += xPeriod) { float p = d[po]; if (p < min) { min = p; minStart = x; minLength = 1; minList.clear(); minCount = 0; } else if (p > max) { max = p; maxStart = x; maxLength = 1; maxList.clear(); maxCount = 0; } else { if (p == min) { if (minLength == 0) minStart = x; minLength++; } else if (minLength > 0 && minCount < maxRuns) { minList.add(new int[]{minStart, y, minLength}); minCount++; minLength = 0; } if (p == max) { if (maxLength == 0) maxStart = x; maxLength++; } else if (maxLength > 0 && maxCount < maxRuns) { maxList.add(new int[]{maxStart, y, maxLength}); maxCount++; maxLength = 0; } } } if (maxLength > 0 && maxCount < maxRuns) { maxList.add(new int[]{maxStart, y, maxLength}); maxCount++; } if (minLength > 0 && minCount < maxRuns) { minList.add(new int[]{minStart, y, minLength}); minCount++; } } extrema[0][b] = min; extrema[1][b] = max; minCounts[b] = minCount; maxCounts[b] = maxCount; } } } private void accumulateStatisticsDouble(UnpackedImageData uid) { Rectangle rect = uid.rect; double[][] data = uid.getDoubleData(); int lineStride = uid.lineStride; int pixelStride = uid.pixelStride; int lineInc = lineStride * yPeriod; int pixelInc = pixelStride * xPeriod; if (!saveLocations) { for (int b = 0; b < srcPA.numBands; b++) { double min = extrema[0][b]; // minimum double max = extrema[1][b]; // maximum double[] d = data[b]; int lastLine = uid.bandOffsets[b] + rect.height * lineStride; for (int lo = uid.bandOffsets[b]; lo < lastLine; lo += lineInc) { int lastPixel = lo + rect.width * pixelStride; for (int po = lo; po < lastPixel; po += pixelInc) { double p = d[po]; if (p < min) { min = p; } else if (p > max) { max = p; } } } extrema[0][b] = min; extrema[1][b] = max; } } else { for (int b = 0; b < srcPA.numBands; b++) { double min = extrema[0][b]; // minimum double max = extrema[1][b]; // maximum ArrayList minList = minLocations[b]; ArrayList maxList = maxLocations[b]; int minCount = minCounts[b]; int maxCount = maxCounts[b]; double[] d = data[b]; int lastLine = uid.bandOffsets[b] + rect.height * lineStride; for (int lo = uid.bandOffsets[b], y = rect.y; lo < lastLine; lo += lineInc, y += yPeriod) { int lastPixel = lo + rect.width * pixelStride; int minStart = 0; int maxStart = 0; int minLength = 0; int maxLength = 0; for (int po = lo, x = rect.x; po < lastPixel; po += pixelInc, x += xPeriod) { double p = d[po]; if (p < min) { min = p; minStart = x; minLength = 1; minList.clear(); minCount = 0; } else if (p > max) { max = p; maxStart = x; maxLength = 1; maxList.clear(); maxCount = 0; } else { if (p == min) { if (minLength == 0) minStart = x; minLength++; } else if (minLength > 0 && minCount < maxRuns) { minList.add(new int[]{minStart, y, minLength}); minCount++; minLength = 0; } if (p == max) { if (maxLength == 0) maxStart = x; maxLength++; } else if (maxLength > 0 && maxCount < maxRuns) { maxList.add(new int[]{maxStart, y, maxLength}); maxCount++; maxLength = 0; } } } if (maxLength > 0 && maxCount < maxRuns) { maxList.add(new int[]{maxStart, y, maxLength}); maxCount++; } if (minLength > 0 && minCount < maxRuns) { minList.add(new int[]{minStart, y, minLength}); minCount++; } } extrema[0][b] = min; extrema[1][b] = max; minCounts[b] = minCount; maxCounts[b] = maxCount; } } } protected void initializeState(Raster source) { if (extrema == null) { int numBands = sampleModel.getNumBands(); extrema = new double[2][numBands]; Rectangle rect = source.getBounds(); // Initialize extrema with the first pixel value. // Fix 4810617: Extrema intialization problem; When a ROI // parameter is used, the ROI may not include the fix pixel // of the image. So initializing with the first pixel value // of the image is not correct. if (roi != null) { LinkedList rectList = roi.getAsRectangleList(rect.x, rect.y, rect.width, rect.height); if (rectList == null) { return; // ROI does not intersect with Raster boundary. } ListIterator iterator = rectList.listIterator(0); if (iterator.hasNext()) rect = rect.intersection((Rectangle)iterator.next()); } // Find the actual ROI based on start and period. rect.x = startPosition(rect.x, xStart, xPeriod); rect.y = startPosition(rect.y, yStart, yPeriod); source.getPixel(rect.x, rect.y, extrema[0]); for (int i = 0; i < numBands; i++) { extrema[1][i] = extrema[0][i]; } if (saveLocations) { minLocations = new ArrayList[numBands]; maxLocations = new ArrayList[numBands]; minCounts = new int[numBands]; maxCounts = new int[numBands]; for (int i = 0; i < numBands; i++) { minLocations[i] = new ArrayList(); maxLocations[i] = new ArrayList(); minCounts[i] = maxCounts[i] = 0; } } } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ErodeBinaryOpImage.java0000644000175000017500000002671710346113517027562 0ustar mathieumathieu/* * $RCSfile: ErodeBinaryOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-12-08 20:27:59 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferInt; import java.awt.image.DataBufferUShort; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.JAI; import javax.media.jai.KernelJAI; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; import javax.media.jai.PixelAccessor; import javax.media.jai.PackedImageData; /** * * An OpImage class to perform erosion on a source image. * *

This class implements an erosion operation. * *

Grey Scale Erosion * is a spatial operation that computes * each output sample by subtract elements of a kernel to the samples * surrounding a particular source sample with some care. * A mathematical expression is: * *

For a kernel K with a key position (xKey, yKey), the erosion * of image I at (x,y) is given by: *

 *     max{a:  a + K(xKey+i, yKey+j) <= I(x+i,y+j): all (i,j) }
 *
 *      all possible (i,j) means that both I(x+i,y+j) and K(xKey+i, yKey+j)
 *      are in bounds. Otherwise, the value is set to 0.
 *
 * 
*

Intuitively, the kernel is like an unbrella and the key point * is the handle. At every point, you try to push the umbrella up as high * as possible but still underneath the image surface. The final height * of the handle is the value after erosion. Thus if you want the image * to erode from the upper right to bottom left, the following would do. * *

* * * * *
00X
0X0
X00
* *

Note that zero kernel erosion has effects on the image, the * location of the key position and size of kernel all matter. * *

Pseudo code for the erosion operation is as follows. * Assuming the kernel K is of size M rows x N cols * and the key position is (xKey, yKey). * *

 * 
 * // erosion
 * for every dst pixel location (x,y){
 *    tmp = infinity;
 *    for (i = -xKey; i < M - xKey; i++){
 *       for (j = -yKey; j < N - yKey; j++){
 *          if((x+i, y+j) are in bounds of src){
 *             tmp = min{tmp, src[x + i][y + j] - K[xKey + i][yKey + j]};
 *          }
 *       }
 *    }
 *    dst[x][y] = tmp;
 *    if (dst[x][y] == infinity)
 *        dst[x][y] = 0;
 * }
 * 
* *

The kernel cannot be bigger in any dimension than the image data. * *

Binary Image Erosion * requires the kernel to be binary as well. * Intuitively, binary erosion slides the kernel * key position and place it at every non-zero point (x,y) in the src image. * The dst value at this position is set to 1 if all the kernel * are fully supported by the src image, and the src image value is 1 * whenever the kernel has value 1. * Otherwise, the value after erosion at (x,y) is set to 0. * Erosion usually shrinks images, but it can fill holes * with kernels like *

 [1 0 1] 
* and the key position at the center. * *

Pseudo code for the erosion operation is as follows. * *

 * // erosion
 * for every dst pixel location (x,y){
 *    dst[x][y] = 1;
 *    for (i = -xKey; i < M - xKey; i++){
 *       for (j = -yKey; j < N - yKey; j++){
 *         if((x+i,y+j) is out of bounds of src ||
 *             src(x+i, y+j)==0 && Key(xKey+i, yKey+j)==1){
 *            dst[x][y] = 0; break;
 *          }
 *       }
 *    }
 * }
 * 
* *

Reference: An Introduction to Nonlinear Image Processing, * by Edward R. Bougherty and Jaakko Astola, * Spie Optical Engineering Press, 1994. * * * @see KernelJAI */ final class ErodeBinaryOpImage extends AreaOpImage { /** * The kernel with which to do the erode operation. */ protected KernelJAI kernel; /** Kernel variables. */ private int kw, kh, kx, ky; private int[] kdataPack; // Pack kernel into integers; private int kwPack; // for factoring things out private int dwidth, dheight; private int dnumBands; // should be 1; gray images private int bits; // per packed unit, 8, 16 or 32 //private int dstBandOffsets[]; private int dstDBOffset; private int dstScanlineStride; private int dstScanlineStrideBits; private int dstMinX, dstMinY, dstTransX, dstTransY; private int dstDataBitOffset; //private int srcBandOffsets[]; private int srcDBOffset; private int srcScanlineStride; private int srcScanlineStrideBits; private int srcMinX, srcMinY, srcTransX, srcTransY; private int srcDataBitOffset; // Since this operation deals with packed binary data, we do not need // to expand the IndexColorModel private static Map configHelper(Map configuration) { Map config; if (configuration == null) { config = new RenderingHints(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.FALSE); } else { config = configuration; if (!(config.containsKey(JAI.KEY_REPLACE_INDEX_COLOR_MODEL))) { RenderingHints hints = (RenderingHints)configuration; config = (RenderingHints)hints.clone(); config.put(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.FALSE); } } return config; } /** * Creates a ErodeBinaryOpImage given a ParameterBlock containing the image * source and pre-rotated erosion kernel. The image dimensions are * derived * from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout * object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param kernel the pre-rotated erosion KernelJAI. */ public ErodeBinaryOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, KernelJAI kernel) { super(source, layout, configHelper(config), true, extender, kernel.getLeftPadding(), kernel.getRightPadding(), kernel.getTopPadding(), kernel.getBottomPadding()); this.kernel = kernel; kw = kernel.getWidth(); kh = kernel.getHeight(); kx = kernel.getXOrigin(); ky = kernel.getYOrigin(); kwPack = (kw+31)/32; kdataPack = packKernel(kernel); } /** * Performs erosion on a specified rectangle. The sources are * cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; PixelAccessor pa = new PixelAccessor(source.getSampleModel(), null); PackedImageData srcIm = pa.getPackedPixels(source, source.getBounds(), false, false); pa = new PixelAccessor(dest.getSampleModel(), null); PackedImageData dstIm = pa.getPackedPixels(dest, destRect, true, false); // src data under kernel, packed in int. int[] srcUK = new int [kwPack * kh]; // sliding the kernel row by row // general the packed matrix under the row int dheight = destRect.height; int dwidth = destRect.width; int sOffset = srcIm.offset; int dOffset = dstIm.offset; for (int j = 0; j < dheight; j++) { int selement, val, dindex, delement; // reset srcUK for each row beginning // src[sOffset +[-kx:kw-kx, -ky:kh-ky]] placed in srcUK // for (int m = 0; m < srcUK.length; m++){ srcUK[m] = 0; } // initial srcUK // first shift left the packed bits under the sliding kernel by 1 bit // then fill (compute) in the last bit of each row for(int i = 0; i < kw -1; i++){ bitShiftMatrixLeft(srcUK, kh, kwPack); // expand for speedup? int lastCol = kwPack - 1; int bitLoc = srcIm.bitOffset + i; int byteLoc = bitLoc >> 3; bitLoc = 7 - (bitLoc & 7); for(int m=0, sOffsetB = sOffset; m < kh; m++, sOffsetB += srcIm.lineStride){ selement = (int)srcIm.data[sOffsetB + byteLoc]; val = (selement >> bitLoc) & 0x1; srcUK[lastCol] |= val; lastCol += kwPack; } } // same as above // also setting dest for (int i = 0; i < dwidth; i++){ bitShiftMatrixLeft(srcUK, kh, kwPack); // expand for speedup? int lastCol = kwPack - 1; int bitLoc = srcIm.bitOffset + i + kw -1; int byteLoc = bitLoc >> 3; bitLoc = 7 - (bitLoc & 7); for(int m=0, sOffsetB = sOffset; m < kh; m++, sOffsetB += srcIm.lineStride){ selement = (int)srcIm.data[sOffsetB + byteLoc]; val = (selement >> bitLoc) & 0x1; srcUK[lastCol] |= val; lastCol += kwPack; } int dBitLoc = dstIm.bitOffset + i; int dshift = 7 - (dBitLoc & 7); int dByteLoc= (dBitLoc >> 3) + dOffset; delement = (int)dstIm.data[dByteLoc]; delement |= (0x1) << dshift; for (int m = 0; m < srcUK.length; m++){ if ((srcUK[m] & kdataPack[m]) != kdataPack[m]){ delement &= ~((0x1) << dshift); break; } } dstIm.data[dByteLoc] = (byte)delement; } sOffset += srcIm.lineStride; dOffset += dstIm.lineStride; } pa.setPackedPixels(dstIm); } /** pack kernel into integers by row, aligned to the right; * extra bits on the left are filled with 0 bits * @params kernel - the given kernel (already rotated) * @returns an integer array of ints from packed kernel data */ private final int[] packKernel(KernelJAI kernel){ int kw = kernel.getWidth(); int kh = kernel.getHeight(); int kwPack = (31+kw)/32; int kerPacked[] = new int[kwPack * kh]; float[] kdata = kernel.getKernelData(); for (int j=0; j .9F){ // same as == 1.0F kerPacked[lastCol] |= 0x1; } } } return kerPacked; } // to shift an integer matrix one bit left // assuming that the matrix is row oriented // each row is viewed as a long bit array // rows and cols are the dimention after packing private final static void bitShiftMatrixLeft(int[] mat, int rows, int cols){ int m = 0; for (int i=0; i>> 31); m++; } mat[m] <<= 1; m++; } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/DilateBinaryOpImage.java0000644000175000017500000002634210346113517027720 0ustar mathieumathieu/* * $RCSfile: DilateBinaryOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-12-08 20:27:58 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferInt; import java.awt.image.DataBufferUShort; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.JAI; import javax.media.jai.KernelJAI; import javax.media.jai.OpImage; import java.util.Map; import javax.media.jai.PixelAccessor; import javax.media.jai.PackedImageData; /** * * An OpImage class to perform dilation on a source image. * * Dilation for grey scale images can be charaterized by "slide, add and max", * while for binary images by "slide and set". As always, the kernel * is expected to come with a key position. * *

Grey scale dilation is a spatial operation that computes * each output sample by adding elements of a kernel to the samples * surrounding a particular source sample and taking the maximum. * A mathematical expression is: * *

For a kernel K with a key position (xKey,yKey), the dilation * of image I at (x,y) is given by: *

 *     max{ I(x-i, y-j) + K(xKey+i, yKey+j): some (i,j) restriction }
 *
 *      where the (i,j) restriction means:
 *      all possible (i,j) so that both I(x-i,y-j) and K(xKey+i, yKey+j)
 *      are defined, that is, these indecies are in bounds.
 *
 * 
*

Intuitively in 2D, the kernel is like * an unbrella and the key point is the handle. When the handle moves * all over the image surface, the upper outbounds of all the umbrella * positions is the dilation. Thus if you want the image to dilate in * the upper right direction, the following kernel would do with * the bold face key position. * *

* * * * *
0050
0500
000
* *

Note also that zero kernel have effects on the dilation! * That is because of the "max" in the add and max process. Thus * a 3 x 1 zero kernel with the key persion at the bottom of the kernel * dilates the image upwards. * *

* After the kernel is rotated 180 degrees, Pseudo code for dilation operation * is as follows. Of course, you should provide the kernel in its * (unrotated) original form. Assuming the kernel K is of size M rows x N cols * and the key position is (xKey, yKey). * * // dilation * for every dst pixel location (x,y){ * dst[x][y] = -infinity; * for (i = -xKey; i < M - xKey; i++){ * for (j = -yKey; j < N - yKey; j++){ * if((x+i, y+j) are in bounds of src && * (xKey+i, yKey+j) are in bounds of K){ * tmp = src[x + i][y + j]+ K[xKey + i][yKey + j]; * dst[x][y] = max{tmp, dst[x][y]}; * } * } * } * } * * *

Dilation, unlike convolution and most neighborhood operations, * actually can grow the image region. But to conform with other * image neighborhood operations, the border pixels are set to 0. * For a 3 x 3 kernel with the key point at the center, there will * be a pixel wide 0 stripe around the border. * *

The kernel cannot be bigger in any dimension than the image data. * *

Binary Image Dilation * requires the kernel K to be binary. * Intuitively, starting from dst image being a duplicate of src, * binary dilation slides the kernel K to place the key position * at every non-zero point (x,y) in src image and set dst positions * under ones of K to 1. * *

After the kernel is rotated 180 degrees, the pseudo code for * dilation operation is as follows. (Of course, you should provide * the kernel in its original unrotated form.) * *

 *
 * // dilating
 * for every dst pixel location (x,y){
 *    dst[x][y] = src[x][y];
 *    for (i = -xKey; i < M - xKey; i++){
 *       for (j = -yKey; j < N - yKey; j++){
 *         if(src[x+i,y+i]==1 && Key(xKey+i, yKey+j)==1){
 *            dst[x][y] = 1; break;
 *          }
 *       }
 *    }
 * }
 * 
*

Reference: An Introduction to Nonlinear Image Processing, * by Edward R. Bougherty and Jaakko Astola, * Spie Optical Engineering Press, 1994. * * * @see KernelJAI */ final class DilateBinaryOpImage extends AreaOpImage { /** * The kernel with which to do the dilate operation. */ protected KernelJAI kernel; /** Kernel variables. */ private int kw, kh, kx, ky; private int[] kdataPack; // Pack kernel into int; private int kwPack; // num of int needed to pack each row of the kernel // Since this operation deals with packed binary data, we do not need // to expand the IndexColorModel private static Map configHelper(Map configuration) { Map config; if (configuration == null) { config = new RenderingHints(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.FALSE); } else { config = configuration; if (!(config.containsKey(JAI.KEY_REPLACE_INDEX_COLOR_MODEL))) { config.put(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.FALSE); RenderingHints hints = (RenderingHints)configuration; config = (RenderingHints)hints.clone(); } } return config; } /** * Creates a DilateBinaryOpImage given a ParameterBlock containing the * image source and pre-rotated dilation kernel. The image dimensions * are derived from the source image. The tile grid layout, SampleModel, * and ColorModel may optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param kernel the pre-rotated dilation KernelJAI. */ public DilateBinaryOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, KernelJAI kernel) { super(source, layout, configHelper(config), true, extender, kernel.getLeftPadding(), kernel.getRightPadding(), kernel.getTopPadding(), kernel.getBottomPadding()); this.kernel = kernel; kw = kernel.getWidth(); kh = kernel.getHeight(); kx = kernel.getXOrigin(); ky = kernel.getYOrigin(); kwPack = (kw+31)/32; kdataPack = packKernel(kernel); } /** * Performs dilation on a specified rectangle. The sources are * cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; PixelAccessor pa = new PixelAccessor(source.getSampleModel(), null); PackedImageData srcIm = pa.getPackedPixels(source, source.getBounds(), false, false); pa = new PixelAccessor(dest.getSampleModel(), null); PackedImageData dstIm = pa.getPackedPixels(dest, destRect, true, false); // src data under kernel, packed in int. int[] srcUK = new int [kwPack * kh]; // sliding the kernel row by row // general the packed matrix under the row int dheight = destRect.height; int dwidth = destRect.width; int sOffset = srcIm.offset; int dOffset = dstIm.offset; for (int j = 0; j < dheight; j++) { int selement, val, dindex, delement; // reset srcUK for each row beginning // src[sOffset +[-kx:kw-kx, -ky:kh-ky]] placed in srcUK // for (int m = 0; m < srcUK.length; m++){ srcUK[m] = 0; } // initial srcUK // first shift left the packed bits under the sliding kernel by 1 bit // then fill (compute) in the last bit of each row for(int i = 0; i < kw -1; i++){ bitShiftMatrixLeft(srcUK, kh, kwPack); // expand for speedup? int lastCol = kwPack - 1; int bitLoc = srcIm.bitOffset + i; int byteLoc = bitLoc >> 3; bitLoc = 7 - (bitLoc & 7); for(int m=0, sOffsetB = sOffset; m < kh; m++, sOffsetB += srcIm.lineStride){ selement = (int)srcIm.data[sOffsetB + byteLoc]; val = (selement >> bitLoc) & 0x1; srcUK[lastCol] |= val; lastCol += kwPack; } } // same as above // also setting dest for (int i = 0; i < dwidth; i++){ bitShiftMatrixLeft(srcUK, kh, kwPack); // expand for speedup? int lastCol = kwPack - 1; int bitLoc = srcIm.bitOffset + i + kw -1; int byteLoc = bitLoc >> 3; bitLoc = 7 - (bitLoc & 7); for(int m=0, sOffsetB = sOffset; m < kh; m++, sOffsetB += srcIm.lineStride){ selement = (int)srcIm.data[sOffsetB + byteLoc]; val = (selement >> bitLoc) & 0x1; srcUK[lastCol] |= val; lastCol += kwPack; } // set dest bits for (int m = 0; m < srcUK.length; m++){ if ((srcUK[m] & kdataPack[m]) != 0){ int dBitLoc = dstIm.bitOffset + i; int dshift = 7 - (dBitLoc & 7); int dByteLoc= (dBitLoc >> 3) + dOffset; delement = (int)dstIm.data[dByteLoc]; delement |= (0x1) << dshift; dstIm.data[dByteLoc] = (byte)delement; break; } } } sOffset += srcIm.lineStride; dOffset += dstIm.lineStride; } pa.setPackedPixels(dstIm); } /** pack kernel into integers by row, aligned to the right; * extra bits on the left are filled with 0 bits * @params kernel - the given kernel (already rotated) * @returns an integer array of ints from packed kernel data */ private final int[] packKernel(KernelJAI kernel){ int kw = kernel.getWidth(); int kh = kernel.getHeight(); int kwPack = (31+kw)/32; int kerPacked[] = new int[kwPack * kh]; float[] kdata = kernel.getKernelData(); for (int j=0; j .9F){ // same as == 1.0F kerPacked[lastCol] |= 0x1; } } } return kerPacked; } // to shift an integer matrix one bit left // assuming that the matrix is row oriented // each row is viewed as a long bit array // rows and cols are the dimention after packing private final static void bitShiftMatrixLeft(int[] mat, int rows, int cols){ int m = 0; for (int i=0; i>> 31); m++; } mat[m] <<= 1; m++; } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/NotCRIF.java0000644000175000017500000000315510203035544025303 0ustar mathieumathieu/* * $RCSfile: NotCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:37 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.geom.Rectangle2D; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderContext; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "Not" operation in the * rendered and renderable image layers. * * @since EA2 * @see javax.media.jai.operator.NotDescriptor * @see NotOpImage * */ public class NotCRIF extends CRIFImpl { /** Constructor. */ public NotCRIF() { super("not"); } /** * Creates a new instance of NotOpImage in the * rendered layer. This method satisifies the implementation of RIF. * * @param paramBlock The source image to perform the logical "not" * operation on. * @param renderHints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new NotOpImage(paramBlock.getRenderedSource(0), renderHints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/BandSelectOpImage.java0000644000175000017500000001236710203035544027352 0ustar mathieumathieu/* * $RCSfile: BandSelectOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:15 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.ComponentSampleModel; import java.awt.image.DataBuffer; import java.awt.image.PixelInterleavedSampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.SinglePixelPackedSampleModel; import java.awt.image.WritableRaster; import java.util.Map; import javax.media.jai.ImageLayout; import javax.media.jai.PointOpImage; import com.sun.media.jai.util.JDKWorkarounds; /** * An OpImage implementing the "BandSelect" operation. * *

This OpImage copies the specified bands of the source * image to the destination image in the order that is specified. * * @see javax.media.jai.operator.BandSelectDescriptor * @see BandSelectCRIF * * * @since EA2 */ final class BandSelectOpImage extends PointOpImage { // Set if the source has a SinglePixelPackedSampleModel and // bandIndices.length < 3. private boolean areDataCopied; private int[] bandIndices; private static ImageLayout layoutHelper(ImageLayout layout, RenderedImage source, int[] bandIndices) { ImageLayout il = layout == null ? new ImageLayout() : (ImageLayout)layout.clone(); // Create a sub-banded SampleModel. SampleModel sourceSM = source.getSampleModel(); int numBands = bandIndices.length; // The only ColorModel compatible with a SinglePixelPackedSampleModel // in the J2SE is a DirectColorModel which is by definition of // ColorSpace.TYPE_RGB. Therefore if there are fewer than 3 bands // a data copy is obligatory if a ColorModel will be possible. SampleModel sm = null; if(sourceSM instanceof SinglePixelPackedSampleModel && numBands < 3) { sm = new PixelInterleavedSampleModel( DataBuffer.TYPE_BYTE, sourceSM.getWidth(), sourceSM.getHeight(), numBands, sourceSM.getWidth()*numBands, numBands == 1 ? new int[] {0} : new int[] {0, 1}); } else { sm = sourceSM.createSubsetSampleModel(bandIndices); } il.setSampleModel(sm); // Clear the ColorModel mask if needed. ColorModel cm = il.getColorModel(null); if(cm != null && !JDKWorkarounds.areCompatibleDataModels(sm, cm)) { // Clear the mask bit if incompatible. il.unsetValid(ImageLayout.COLOR_MODEL_MASK); } // Force the tile grid to be identical to that of the source. il.setTileGridXOffset(source.getTileGridXOffset()); il.setTileGridYOffset(source.getTileGridYOffset()); il.setTileWidth(source.getTileWidth()); il.setTileHeight(source.getTileHeight()); return il; } /** * Constructor. * * @param source The source image. * @param layout The destination image layout. * @param bandIndices The selected band indices of the source. * The number of bands of the destination is * determined by bandIndices.length. */ public BandSelectOpImage(RenderedImage source, Map config, ImageLayout layout, int[] bandIndices) { super(vectorize(source), layoutHelper(layout, source, bandIndices), config, true); this.areDataCopied = source.getSampleModel() instanceof SinglePixelPackedSampleModel && bandIndices.length < 3; this.bandIndices = (int[])bandIndices.clone(); } public boolean computesUniqueTiles() { return areDataCopied; } public Raster computeTile(int tileX, int tileY) { Raster tile = getSourceImage(0).getTile(tileX, tileY); if(areDataCopied) { // Copy the data as there is no concrete ColorModel for // a SinglePixelPackedSampleModel with numBands < 3. tile = tile.createChild(tile.getMinX(), tile.getMinY(), tile.getWidth(), tile.getHeight(), tile.getMinX(), tile.getMinY(), bandIndices); WritableRaster raster = createTile(tileX, tileY); raster.setRect(tile); return raster; } else { // Simply return a child of the corresponding source tile. return tile.createChild(tile.getMinX(), tile.getMinY(), tile.getWidth(), tile.getHeight(), tile.getMinX(), tile.getMinY(), bandIndices); } } public Raster getTile(int tileX, int tileY) { // Just to return computeTile() result so as to avoid caching. return computeTile(tileX, tileY); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/FCT.java0000644000175000017500000000605710203035544024517 0ustar mathieumathieu/* * $RCSfile: FCT.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:25 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.image.DataBuffer; import javax.media.jai.operator.DFTDescriptor; import com.sun.media.jai.util.MathJAI; /** * The Fast Cosine Transform (FCT) class. * * @since EA3 */ public class FCT { /* * Flag indicating whether the transform is forward (true) * or inverse (false). */ protected boolean isForwardTransform; /** The FFT object by which the FCT is actually calculated. */ private FFT fft = null; /** Default constructor */ public FCT() {} /** * Construct a new FCT object. * * @param length The length of the FCT; must be a positive power of 2. */ public FCT(boolean isForwardTransform, int length) { // Cache the directional flag. this.isForwardTransform = isForwardTransform; // Create the FFT object. fft = new FFT(isForwardTransform, new Integer(DFTDescriptor.SCALING_NONE.getValue()), length); } /** * Initialize the length-dependent fields. * * @param length The length of the FCT; must be a positive power of 2. */ public void setLength(int length) { fft.setLength(length); } /** * Set the internal work data array of the FCT object. * * @param dataType The data type of the source data according to * one of the DataBuffer TYPE_* flags. This should be either * DataBuffer.TYPE_FLOAT or DataBuffer.TYPE_DOUBLE. * @param data Float or double array of data. * @param offset Offset into the data array. * @param stride The data array stride value. * @param count The number of values to copy. */ public void setData(int dataType, Object data, int offset, int stride, int count) { if(isForwardTransform) { fft.setFCTData(dataType, data, offset, stride, count); } else { fft.setIFCTData(dataType, data, offset, stride, count); } } /** * Get data from the internal work data array of the FCT object. * * @param dataType The data type of the source data according to * one of the DataBuffer TYPE_* flags. This should be either * DataBuffer.TYPE_FLOAT or DataBuffer.TYPE_DOUBLE. * @param data Float or double array of data. * @param offset Offset into the data array. * @param stride The data array stride value. */ public void getData(int dataType, Object data, int offset, int stride) { if(isForwardTransform) { fft.getFCTData(dataType, data, offset, stride); } else { fft.getIFCTData(dataType, data, offset, stride); } } /** * Calculate the DCT of a sequence using the FCT algorithm. */ public void transform() { fft.transform(); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MosaicOpImage.java0000644000175000017500000026063210207167542026571 0ustar mathieumathieu/* * $RCSfile: MosaicOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-02-23 21:02:26 $ * $State: Exp $ */package com.sun.media.jai.opimage; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.util.ArrayList; import java.util.Arrays; import java.util.Map; import java.util.Vector; import javax.media.jai.BorderExtender; import javax.media.jai.BorderExtenderConstant; import javax.media.jai.ImageLayout; import javax.media.jai.JAI; import javax.media.jai.OpImage; import javax.media.jai.PlanarImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.ROI; import javax.media.jai.operator.MosaicType; import javax.media.jai.operator.MosaicDescriptor; import com.sun.media.jai.util.ImageUtil; public class MosaicOpImage extends OpImage { private static final int WEIGHT_TYPE_ALPHA = 1; private static final int WEIGHT_TYPE_ROI = 2; private static final int WEIGHT_TYPE_THRESHOLD = 3; protected MosaicType mosaicType; protected PlanarImage[] sourceAlpha; protected ROI[] sourceROI; protected double[][] sourceThreshold; protected double[] backgroundValues; protected int numBands; /** Integral background value. */ protected int[] background; /** Integral thresholds. */ protected int[][] threshold; protected boolean isAlphaBitmask = false; private BorderExtender sourceExtender; private BorderExtender zeroExtender; private PlanarImage[] roiImage; private static final ImageLayout getLayout(Vector sources, ImageLayout layout) { // Contingent variables. RenderedImage source0 = null; SampleModel targetSM = null; ColorModel targetCM = null; // Get source count (might be zero). int numSources = sources.size(); if(numSources > 0) { // Get SampleModel and ColorModel from first source. source0 = (RenderedImage)sources.get(0); targetSM = source0.getSampleModel(); targetCM = source0.getColorModel(); } else if(layout != null && layout.isValid(ImageLayout.WIDTH_MASK | ImageLayout.HEIGHT_MASK | ImageLayout.SAMPLE_MODEL_MASK)) { // Get SampleModel and ColorModel from layout. targetSM = layout.getSampleModel(null); if(targetSM == null) { throw new IllegalArgumentException (JaiI18N.getString("MosaicOpImage7")); } } else { throw new IllegalArgumentException (JaiI18N.getString("MosaicOpImage8")); } // Get data type, band count, and sample depth. int dataType = targetSM.getDataType(); int numBands = targetSM.getNumBands(); int sampleSize = targetSM.getSampleSize(0); // Sample size must equal that of first band. for(int i = 1; i < numBands; i++) { if(targetSM.getSampleSize(i) != sampleSize) { throw new IllegalArgumentException(JaiI18N.getString("MosaicOpImage1")); } } // Return a clone of ImageLayout passed in if no sources. // The input ImageLayout was checked for null above. if(numSources < 1) { return (ImageLayout)layout.clone(); } // Check the other sources against the first. for(int i = 1; i < numSources; i++) { RenderedImage source = (RenderedImage)sources.get(i); SampleModel sourceSM = source.getSampleModel(); // Data type and band count must be equal. if(sourceSM.getDataType() != dataType) { throw new IllegalArgumentException(JaiI18N.getString("MosaicOpImage2")); } else if(sourceSM.getNumBands() != numBands) { throw new IllegalArgumentException(JaiI18N.getString("MosaicOpImage3")); } // Sample size must be equal. for(int j = 0; j < numBands; j++) { if(sourceSM.getSampleSize(j) != sampleSize) { throw new IllegalArgumentException(JaiI18N.getString("MosaicOpImage1")); } } } // Create a new layout or clone the one passed in. ImageLayout mosaicLayout = layout == null ? new ImageLayout() : (ImageLayout)layout.clone(); // Determine the mosaic bounds. Rectangle mosaicBounds = new Rectangle(); if(mosaicLayout.isValid(ImageLayout.MIN_X_MASK | ImageLayout.MIN_Y_MASK | ImageLayout.WIDTH_MASK | ImageLayout.HEIGHT_MASK)) { // Set the mosaic bounds to the value given in the layout. mosaicBounds.setBounds(mosaicLayout.getMinX(null), mosaicLayout.getMinY(null), mosaicLayout.getWidth(null), mosaicLayout.getHeight(null)); } else if(numSources > 0) { // Set the mosaic bounds to the union of source bounds. mosaicBounds.setBounds(source0.getMinX(), source0.getMinY(), source0.getWidth(), source0.getHeight()); for(int i = 1; i < numSources; i++) { RenderedImage source = (RenderedImage)sources.get(i); Rectangle sourceBounds = new Rectangle(source.getMinX(), source.getMinY(), source.getWidth(), source.getHeight()); mosaicBounds = mosaicBounds.union(sourceBounds); } } // Set the mosaic bounds in the layout. mosaicLayout.setMinX(mosaicBounds.x); mosaicLayout.setMinY(mosaicBounds.y); mosaicLayout.setWidth(mosaicBounds.width); mosaicLayout.setHeight(mosaicBounds.height); // Check the SampleModel if defined. if(mosaicLayout.isValid(ImageLayout.SAMPLE_MODEL_MASK)) { // Get the SampleModel. SampleModel destSM = mosaicLayout.getSampleModel(null); // Unset SampleModel if differing band count or data type. boolean unsetSampleModel = destSM.getNumBands() != numBands || destSM.getDataType() != dataType; // Unset SampleModel if differing sample size. for(int i = 0; !unsetSampleModel && i < numBands; i++) { if(destSM.getSampleSize(i) != sampleSize) { unsetSampleModel = true; } } // Unset SampleModel if needed. if(unsetSampleModel) { mosaicLayout.unsetValid(ImageLayout.SAMPLE_MODEL_MASK); } } return mosaicLayout; } public MosaicOpImage(Vector sources, ImageLayout layout, Map config, MosaicType mosaicType, PlanarImage[] sourceAlpha, ROI[] sourceROI, double[][] sourceThreshold, double[] backgroundValues) { super(sources, getLayout(sources, layout), config, true); // Set the band count. this.numBands = sampleModel.getNumBands(); // Set the source count. int numSources = getNumSources(); // Set the mosaic type. this.mosaicType = mosaicType; // Save the alpha array. this.sourceAlpha = null; if(sourceAlpha != null) { // Check alpha images. for(int i = 0; i < sourceAlpha.length; i++) { if(sourceAlpha[i] != null) { SampleModel alphaSM = sourceAlpha[i].getSampleModel(); if(alphaSM.getNumBands() != 1) { throw new IllegalArgumentException(JaiI18N.getString("MosaicOpImage4")); } else if(alphaSM.getDataType() != sampleModel.getDataType()) { throw new IllegalArgumentException(JaiI18N.getString("MosaicOpImage5")); } else if(alphaSM.getSampleSize(0) != sampleModel.getSampleSize(0)) { throw new IllegalArgumentException(JaiI18N.getString("MosaicOpImage6")); } } } this.sourceAlpha = new PlanarImage[numSources]; System.arraycopy(sourceAlpha, 0, this.sourceAlpha, 0, Math.min(sourceAlpha.length, numSources)); } // Save the ROI array. this.sourceROI = null; if(sourceROI != null) { this.sourceROI = new ROI[numSources]; System.arraycopy(sourceROI, 0, this.sourceROI, 0, Math.min(sourceROI.length, numSources)); } // isAlphaBitmask is true if and only if type is blend and an // alpha image is supplied for each source. this.isAlphaBitmask = !(mosaicType == MosaicDescriptor.MOSAIC_TYPE_BLEND && sourceAlpha != null && !(sourceAlpha.length < numSources)); if(!this.isAlphaBitmask) { for(int i = 0; i < numSources; i++) { if(sourceAlpha[i] == null) { this.isAlphaBitmask = true; break; } } } // Copy the threshold values according to the specification. this.sourceThreshold = new double[numSources][numBands]; // Ensure the parameter is non-null and has one value. if(sourceThreshold == null) { sourceThreshold = new double[][] {{1.0}}; } for(int i = 0; i < numSources; i++) { // If there's an array for this source, use it. if(i < sourceThreshold.length && sourceThreshold[i] != null) { if(sourceThreshold[i].length < numBands) { // If the array is less than numBands, fill with element 0. Arrays.fill(this.sourceThreshold[i], sourceThreshold[i][0]); } else { // Copy the whole array. System.arraycopy(sourceThreshold[i], 0, this.sourceThreshold[i], 0, numBands); } } else { // Beyond the array or a null element: use the zeroth array. this.sourceThreshold[i] = this.sourceThreshold[0]; } } // Initialize the integral thresholds. this.threshold = new int[numSources][numBands]; for(int i = 0; i < numSources; i++) { for(int j = 0; j < numBands; j++) { // Truncate as the specified comparison is ">=". this.threshold[i][j] = (int)this.sourceThreshold[i][j]; } } // Copy the background values per the specification. this.backgroundValues = new double[numBands]; if(backgroundValues == null) { backgroundValues = new double[] {0.0}; } if(backgroundValues.length < numBands) { Arrays.fill(this.backgroundValues, backgroundValues[0]); } else { System.arraycopy(backgroundValues, 0, this.backgroundValues, 0, numBands); } // Clamp the floating point values for the integral cases. this.background = new int[this.backgroundValues.length]; int dataType = sampleModel.getDataType(); for(int i = 0; i < this.background.length; i++) { switch (dataType) { case DataBuffer.TYPE_BYTE: this.background[i] = ImageUtil.clampRoundByte(this.backgroundValues[i]); break; case DataBuffer.TYPE_USHORT: this.background[i] = ImageUtil.clampRoundUShort(this.backgroundValues[i]); break; case DataBuffer.TYPE_SHORT: this.background[i] = ImageUtil.clampRoundShort(this.backgroundValues[i]); break; case DataBuffer.TYPE_INT: this.background[i] = ImageUtil.clampRoundInt(this.backgroundValues[i]); break; default: } } // Determine constant value for source border extension. double sourceExtensionConstant; switch (dataType) { case DataBuffer.TYPE_BYTE: sourceExtensionConstant = 0.0; break; case DataBuffer.TYPE_USHORT: sourceExtensionConstant = 0.0; break; case DataBuffer.TYPE_SHORT: sourceExtensionConstant = Short.MIN_VALUE; break; case DataBuffer.TYPE_INT: sourceExtensionConstant = Integer.MIN_VALUE; break; case DataBuffer.TYPE_FLOAT: sourceExtensionConstant = -Float.MAX_VALUE; break; case DataBuffer.TYPE_DOUBLE: default: sourceExtensionConstant = -Double.MAX_VALUE; } // Extend the sources filling with the minimum possible value // on account of the threshold technique. this.sourceExtender = sourceExtensionConstant == 0.0 ? BorderExtender.createInstance(BorderExtender.BORDER_ZERO) : new BorderExtenderConstant(new double[] {sourceExtensionConstant}); // Extends alpha or ROI data with zeros. if(sourceAlpha != null || sourceROI != null) { this.zeroExtender = BorderExtender.createInstance(BorderExtender.BORDER_ZERO); } // Get the ROI images. if(sourceROI != null) { roiImage = new PlanarImage[numSources]; for(int i = 0; i < sourceROI.length; i++) { if(sourceROI[i] != null) { roiImage[i] = sourceROI[i].getAsImage(); } } } } public Rectangle mapDestRect(Rectangle destRect, int sourceIndex) { if(destRect == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if(sourceIndex < 0 || sourceIndex >= getNumSources()) { throw new IllegalArgumentException(JaiI18N.getString("Generic1")); } return destRect.intersection(getSourceImage(sourceIndex).getBounds()); } public Rectangle mapSourceRect(Rectangle sourceRect, int sourceIndex) { if(sourceRect == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if(sourceIndex < 0 || sourceIndex >= getNumSources()) { throw new IllegalArgumentException(JaiI18N.getString("Generic1")); } return sourceRect.intersection(getBounds()); } public Raster computeTile(int tileX, int tileY) { // Create a new Raster. WritableRaster dest = createWritableRaster(sampleModel, new Point(tileXToX(tileX), tileYToY(tileY))); // Determine the active area; tile intersects with image's bounds. Rectangle destRect = getTileRect(tileX, tileY); int numSources = getNumSources(); Raster[] rasterSources = new Raster[numSources]; Raster[] alpha = sourceAlpha != null ? new Raster[numSources] : null; Raster[] roi = sourceROI != null ? new Raster[numSources] : null; // Cobble areas for (int i = 0; i < numSources; i++) { PlanarImage source = getSourceImage(i); Rectangle srcRect = mapDestRect(destRect, i); // If srcRect is empty, set the Raster for this source to // null; otherwise pass srcRect to getData(). If srcRect // is null, getData() will return a Raster containing the // data of the entire source image. rasterSources[i] = srcRect != null && srcRect.isEmpty() ? null : source.getExtendedData(destRect, sourceExtender); if(rasterSources[i] != null) { if(sourceAlpha != null && sourceAlpha[i] != null) { alpha[i] = sourceAlpha[i].getExtendedData(destRect, zeroExtender); } if(sourceROI != null && sourceROI[i] != null) { roi[i] = roiImage[i].getExtendedData(destRect, zeroExtender); } } } computeRect(rasterSources, dest, destRect, alpha, roi); for (int i = 0; i < numSources; i++) { Raster sourceData = rasterSources[i]; if(sourceData != null) { PlanarImage source = getSourceImage(i); // Recycle the source tile if(source.overlapsMultipleTiles(sourceData.getBounds())) { recycleTile(sourceData); } } } return dest; } protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { computeRect(sources, dest, destRect, null, null); } protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect, Raster[] alphaRaster, Raster[] roiRaster) { // Save the source count. int numSources = sources.length; // Put all non-null sources in a list. ArrayList sourceList = new ArrayList(numSources); for(int i = 0; i < numSources; i++) { if(sources[i] != null) { sourceList.add(sources[i]); } } // Clear the background and return if no sources. int numNonNullSources = sourceList.size(); if(numNonNullSources == 0) { ImageUtil.fillBackground(dest, destRect, backgroundValues); return; } // Determine the format tag id. SampleModel[] sourceSM = new SampleModel[numNonNullSources]; for(int i = 0; i < numNonNullSources; i++) { sourceSM[i] = ((Raster)sourceList.get(i)).getSampleModel(); } int formatTagID = RasterAccessor.findCompatibleTag(sourceSM, dest.getSampleModel()); // Create source accessors. RasterAccessor[] s = new RasterAccessor[numSources]; for(int i = 0; i < numSources; i++) { if(sources[i] != null) { RasterFormatTag formatTag = new RasterFormatTag(sources[i].getSampleModel(), formatTagID); s[i] = new RasterAccessor(sources[i], destRect, formatTag, null); } } // Create dest accessor. RasterAccessor d = new RasterAccessor(dest, destRect, new RasterFormatTag(dest.getSampleModel(), formatTagID), null); // Create the alpha accessors. RasterAccessor[] a = new RasterAccessor[numSources]; if(alphaRaster != null) { for(int i = 0; i < numSources; i++) { if(alphaRaster[i] != null) { SampleModel alphaSM = alphaRaster[i].getSampleModel(); int alphaFormatTagID = RasterAccessor.findCompatibleTag(null, alphaSM); RasterFormatTag alphaFormatTag = new RasterFormatTag(alphaSM, alphaFormatTagID); a[i] = new RasterAccessor(alphaRaster[i], destRect, alphaFormatTag, sourceAlpha[i].getColorModel()); } } } // Branch to data type-specific method. switch (d.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(s, d, a, roiRaster); break; case DataBuffer.TYPE_USHORT: computeRectUShort(s, d, a, roiRaster); break; case DataBuffer.TYPE_SHORT: computeRectShort(s, d, a, roiRaster); break; case DataBuffer.TYPE_INT: computeRectInt(s, d, a, roiRaster); break; case DataBuffer.TYPE_FLOAT: computeRectFloat(s, d, a, roiRaster); break; case DataBuffer.TYPE_DOUBLE: computeRectDouble(s, d, a, roiRaster); break; } d.copyDataToRaster(); } private void computeRectByte(RasterAccessor[] src, RasterAccessor dst, RasterAccessor[] alfa, Raster[] roi) { // Save the source count. int numSources = src.length; // Allocate stride, offset, and data arrays for sources. int[] srcLineStride = new int[numSources]; int[] srcPixelStride = new int[numSources]; int[][] srcBandOffsets = new int[numSources][]; byte[][][] srcData = new byte[numSources][][]; // Initialize stride, offset, and data arrays for sources. for(int i = 0; i < numSources; i++) { if(src[i] != null) { srcLineStride[i] = src[i].getScanlineStride(); srcPixelStride[i] = src[i].getPixelStride(); srcBandOffsets[i] = src[i].getBandOffsets(); srcData[i] = src[i].getByteDataArrays(); } } // Initialize destination variables. int dstMinX = dst.getX(); int dstMinY = dst.getY(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstMaxX = dstMinX + dstWidth; // x max exclusive int dstMaxY = dstMinY + dstHeight; // y max exclusive int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); byte[][] dstData = dst.getByteDataArrays(); // Check for alpha. boolean hasAlpha = false; for(int i = 0; i < numSources; i++) { if(alfa[i] != null) { hasAlpha = true; break; } } // Declare alpha channel arrays. int[] alfaLineStride = null; int[] alfaPixelStride = null; int[][] alfaBandOffsets = null; byte[][][] alfaData = null; if(hasAlpha) { // Allocate stride, offset, and data arrays for alpha channels. alfaLineStride = new int[numSources]; alfaPixelStride = new int[numSources]; alfaBandOffsets = new int[numSources][]; alfaData = new byte[numSources][][]; // Initialize stride, offset, and data arrays for alpha channels. for(int i = 0; i < numSources; i++) { if(alfa[i] != null) { alfaLineStride[i] = alfa[i].getScanlineStride(); alfaPixelStride[i] = alfa[i].getPixelStride(); alfaBandOffsets[i] = alfa[i].getBandOffsets(); alfaData[i] = alfa[i].getByteDataArrays(); } } } // Initialize weight type arrays. int[] weightTypes = new int[numSources]; for(int i = 0; i < numSources; i++) { weightTypes[i] = WEIGHT_TYPE_THRESHOLD; if(alfa[i] != null) { weightTypes[i] = WEIGHT_TYPE_ALPHA; } else if(sourceROI != null && sourceROI[i] != null) { weightTypes[i] = WEIGHT_TYPE_ROI; } } // Set up source offset and data variabls. int[] sLineOffsets = new int[numSources]; int[] sPixelOffsets = new int[numSources]; byte[][] sBandData = new byte[numSources][]; // Set up alpha offset and data variabls. int[] aLineOffsets = null; int[] aPixelOffsets = null; byte[][] aBandData = null; if(hasAlpha) { aLineOffsets = new int[numSources]; aPixelOffsets = new int[numSources]; aBandData = new byte[numSources][]; } for(int b = 0; b < dstBands; b++) { // Initialize source and alpha band array and line offsets. for(int s = 0; s < numSources; s++) { if(src[s] != null) { sBandData[s] = srcData[s][b]; sLineOffsets[s] = srcBandOffsets[s][b]; } if(weightTypes[s] == WEIGHT_TYPE_ALPHA) { aBandData[s] = alfaData[s][0]; aLineOffsets[s] = alfaBandOffsets[s][0]; } } // Initialize destination band array and line offsets. byte[] dBandData = dstData[b]; int dLineOffset = dstBandOffsets[b]; if(mosaicType == MosaicDescriptor.MOSAIC_TYPE_OVERLAY) { for(int dstY = dstMinY; dstY < dstMaxY; dstY++) { // Initialize source and alpha pixel offsets and // update line offsets. for(int s = 0; s < numSources; s++) { if(src[s] != null) { sPixelOffsets[s] = sLineOffsets[s]; sLineOffsets[s] += srcLineStride[s]; } if(alfa[s] != null) { aPixelOffsets[s] = aLineOffsets[s]; aLineOffsets[s] += alfaLineStride[s]; } } // Initialize destination pixel offset and update // line offset. int dPixelOffset = dLineOffset; dLineOffset += dstLineStride; for(int dstX = dstMinX; dstX < dstMaxX; dstX++) { // Unset destination update flag. boolean setDestValue = false; // Loop over source until a non-zero weight is // encountered. for(int s = 0; s < numSources; s++) { if(src[s] == null) continue; byte sourceValue = sBandData[s][sPixelOffsets[s]]; sPixelOffsets[s] += srcPixelStride[s]; switch(weightTypes[s]) { case WEIGHT_TYPE_ALPHA: setDestValue = aBandData[s][aPixelOffsets[s]] != 0; aPixelOffsets[s] += alfaPixelStride[s]; break; case WEIGHT_TYPE_ROI: setDestValue = roi[s].getSample(dstX, dstY, 0) > 0; break; default: // WEIGHT_TYPE_THRESHOLD setDestValue = (sourceValue&0xff) >= sourceThreshold[s][b]; } // Set the destination value if a non-zero // weight was found. if(setDestValue) { dBandData[dPixelOffset] = sourceValue; // Increment offset of subsequent sources. for(int k = s + 1; k < numSources; k++) { if(src[k] != null) { sPixelOffsets[k] += srcPixelStride[k]; } if(alfa[k] != null) { aPixelOffsets[k] += alfaPixelStride[k]; } } break; } } // Set the destination value to the background if // no value was set. if(!setDestValue) { dBandData[dPixelOffset] = (byte)background[b]; } dPixelOffset += dstPixelStride; } } } else { // mosaicType == MosaicDescriptor.MOSAIC_TYPE_BLEND for(int dstY = dstMinY; dstY < dstMaxY; dstY++) { // Initialize source and alpha pixel offsets and // update line offsets. for(int s = 0; s < numSources; s++) { if(src[s] != null) { sPixelOffsets[s] = sLineOffsets[s]; sLineOffsets[s] += srcLineStride[s]; } if(weightTypes[s] == WEIGHT_TYPE_ALPHA) { aPixelOffsets[s] = aLineOffsets[s]; aLineOffsets[s] += alfaLineStride[s]; } } // Initialize destination pixel offset and update // line offset. int dPixelOffset = dLineOffset; dLineOffset += dstLineStride; for(int dstX = dstMinX; dstX < dstMaxX; dstX++) { // Clear values for blending ratio. float numerator = 0.0F; float denominator = 0.0F; // Accumulate into numerator and denominator. for(int s = 0; s < numSources; s++) { if(src[s] == null) continue; byte sourceValue = sBandData[s][sPixelOffsets[s]]; sPixelOffsets[s] += srcPixelStride[s]; float weight = 0.0F; switch(weightTypes[s]) { case WEIGHT_TYPE_ALPHA: weight = (aBandData[s][aPixelOffsets[s]]&0xff); if(weight > 0.0F && isAlphaBitmask) { weight = 1.0F; } else { weight /= 255.0F; } aPixelOffsets[s] += alfaPixelStride[s]; break; case WEIGHT_TYPE_ROI: weight = roi[s].getSample(dstX, dstY, 0) > 0 ? 1.0F : 0.0F; break; default: // WEIGHT_TYPE_THRESHOLD weight = (sourceValue&0xff) >= sourceThreshold[s][b] ? 1.0F : 0.0F; } // Update numerator and denominator. numerator += (weight*(sourceValue&0xff)); denominator += weight; } // Clear the background if all weights were zero, // otherwise blend the values. if(denominator == 0.0) { dBandData[dPixelOffset] = (byte)background[b]; } else { dBandData[dPixelOffset] = ImageUtil.clampRoundByte(numerator / denominator); } dPixelOffset += dstPixelStride; } } } } } private void computeRectUShort(RasterAccessor[] src, RasterAccessor dst, RasterAccessor[] alfa, Raster[] roi) { // Save the source count. int numSources = src.length; // Allocate stride, offset, and data arrays for sources. int[] srcLineStride = new int[numSources]; int[] srcPixelStride = new int[numSources]; int[][] srcBandOffsets = new int[numSources][]; short[][][] srcData = new short[numSources][][]; // Initialize stride, offset, and data arrays for sources. for(int i = 0; i < numSources; i++) { if(src[i] != null) { srcLineStride[i] = src[i].getScanlineStride(); srcPixelStride[i] = src[i].getPixelStride(); srcBandOffsets[i] = src[i].getBandOffsets(); srcData[i] = src[i].getShortDataArrays(); } } // Initialize destination variables. int dstMinX = dst.getX(); int dstMinY = dst.getY(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstMaxX = dstMinX + dstWidth; // x max exclusive int dstMaxY = dstMinY + dstHeight; // y max exclusive int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); short[][] dstData = dst.getShortDataArrays(); // Check for alpha. boolean hasAlpha = false; for(int i = 0; i < numSources; i++) { if(alfa[i] != null) { hasAlpha = true; break; } } // Declare alpha channel arrays. int[] alfaLineStride = null; int[] alfaPixelStride = null; int[][] alfaBandOffsets = null; short[][][] alfaData = null; if(hasAlpha) { // Allocate stride, offset, and data arrays for alpha channels. alfaLineStride = new int[numSources]; alfaPixelStride = new int[numSources]; alfaBandOffsets = new int[numSources][]; alfaData = new short[numSources][][]; // Initialize stride, offset, and data arrays for alpha channels. for(int i = 0; i < numSources; i++) { if(alfa[i] != null) { alfaLineStride[i] = alfa[i].getScanlineStride(); alfaPixelStride[i] = alfa[i].getPixelStride(); alfaBandOffsets[i] = alfa[i].getBandOffsets(); alfaData[i] = alfa[i].getShortDataArrays(); } } } // Initialize weight type arrays. int[] weightTypes = new int[numSources]; for(int i = 0; i < numSources; i++) { weightTypes[i] = WEIGHT_TYPE_THRESHOLD; if(alfa[i] != null) { weightTypes[i] = WEIGHT_TYPE_ALPHA; } else if(sourceROI != null && sourceROI[i] != null) { weightTypes[i] = WEIGHT_TYPE_ROI; } } // Set up source offset and data variabls. int[] sLineOffsets = new int[numSources]; int[] sPixelOffsets = new int[numSources]; short[][] sBandData = new short[numSources][]; // Set up alpha offset and data variabls. int[] aLineOffsets = null; int[] aPixelOffsets = null; short[][] aBandData = null; if(hasAlpha) { aLineOffsets = new int[numSources]; aPixelOffsets = new int[numSources]; aBandData = new short[numSources][]; } for(int b = 0; b < dstBands; b++) { // Initialize source and alpha band array and line offsets. for(int s = 0; s < numSources; s++) { if(src[s] != null) { sBandData[s] = srcData[s][b]; sLineOffsets[s] = srcBandOffsets[s][b]; } if(weightTypes[s] == WEIGHT_TYPE_ALPHA) { aBandData[s] = alfaData[s][0]; aLineOffsets[s] = alfaBandOffsets[s][0]; } } // Initialize destination band array and line offsets. short[] dBandData = dstData[b]; int dLineOffset = dstBandOffsets[b]; if(mosaicType == MosaicDescriptor.MOSAIC_TYPE_OVERLAY) { for(int dstY = dstMinY; dstY < dstMaxY; dstY++) { // Initialize source and alpha pixel offsets and // update line offsets. for(int s = 0; s < numSources; s++) { if(src[s] != null) { sPixelOffsets[s] = sLineOffsets[s]; sLineOffsets[s] += srcLineStride[s]; } if(alfa[s] != null) { aPixelOffsets[s] = aLineOffsets[s]; aLineOffsets[s] += alfaLineStride[s]; } } // Initialize destination pixel offset and update // line offset. int dPixelOffset = dLineOffset; dLineOffset += dstLineStride; for(int dstX = dstMinX; dstX < dstMaxX; dstX++) { // Unset destination update flag. boolean setDestValue = false; // Loop over source until a non-zero weight is // encountered. for(int s = 0; s < numSources; s++) { if(src[s] == null) continue; short sourceValue = sBandData[s][sPixelOffsets[s]]; sPixelOffsets[s] += srcPixelStride[s]; switch(weightTypes[s]) { case WEIGHT_TYPE_ALPHA: setDestValue = aBandData[s][aPixelOffsets[s]] != 0; aPixelOffsets[s] += alfaPixelStride[s]; break; case WEIGHT_TYPE_ROI: setDestValue = roi[s].getSample(dstX, dstY, 0) > 0; break; default: // WEIGHT_TYPE_THRESHOLD setDestValue = (sourceValue&0xffff) >= sourceThreshold[s][b]; } // Set the destination value if a non-zero // weight was found. if(setDestValue) { dBandData[dPixelOffset] = sourceValue; // Increment offset of subsequent sources. for(int k = s + 1; k < numSources; k++) { if(src[k] != null) { sPixelOffsets[k] += srcPixelStride[k]; } if(alfa[k] != null) { aPixelOffsets[k] += alfaPixelStride[k]; } } break; } } // Set the destination value to the background if // no value was set. if(!setDestValue) { dBandData[dPixelOffset] = (short)background[b]; } dPixelOffset += dstPixelStride; } } } else { // mosaicType == MosaicDescriptor.MOSAIC_TYPE_BLEND for(int dstY = dstMinY; dstY < dstMaxY; dstY++) { // Initialize source and alpha pixel offsets and // update line offsets. for(int s = 0; s < numSources; s++) { if(src[s] != null) { sPixelOffsets[s] = sLineOffsets[s]; sLineOffsets[s] += srcLineStride[s]; } if(weightTypes[s] == WEIGHT_TYPE_ALPHA) { aPixelOffsets[s] = aLineOffsets[s]; aLineOffsets[s] += alfaLineStride[s]; } } // Initialize destination pixel offset and update // line offset. int dPixelOffset = dLineOffset; dLineOffset += dstLineStride; for(int dstX = dstMinX; dstX < dstMaxX; dstX++) { // Clear values for blending ratio. float numerator = 0.0F; float denominator = 0.0F; // Accumulate into numerator and denominator. for(int s = 0; s < numSources; s++) { if(src[s] == null) continue; short sourceValue = sBandData[s][sPixelOffsets[s]]; sPixelOffsets[s] += srcPixelStride[s]; float weight = 0.0F; switch(weightTypes[s]) { case WEIGHT_TYPE_ALPHA: weight = (aBandData[s][aPixelOffsets[s]]&0xffff); if(weight > 0.0F && isAlphaBitmask) { weight = 1.0F; } else { weight /= 65535.0F; } aPixelOffsets[s] += alfaPixelStride[s]; break; case WEIGHT_TYPE_ROI: weight = roi[s].getSample(dstX, dstY, 0) > 0 ? 1.0F : 0.0F; break; default: // WEIGHT_TYPE_THRESHOLD weight = (sourceValue&0xffff) >= sourceThreshold[s][b] ? 1.0F : 0.0F; } // Update numerator and denominator. numerator += (weight*(sourceValue&0xffff)); denominator += weight; } // Clear the background if all weights were zero, // otherwise blend the values. if(denominator == 0.0) { dBandData[dPixelOffset] = (short)background[b]; } else { dBandData[dPixelOffset] = ImageUtil.clampRoundUShort(numerator / denominator); } dPixelOffset += dstPixelStride; } } } } } private void computeRectShort(RasterAccessor[] src, RasterAccessor dst, RasterAccessor[] alfa, Raster[] roi) { // Save the source count. int numSources = src.length; // Allocate stride, offset, and data arrays for sources. int[] srcLineStride = new int[numSources]; int[] srcPixelStride = new int[numSources]; int[][] srcBandOffsets = new int[numSources][]; short[][][] srcData = new short[numSources][][]; // Initialize stride, offset, and data arrays for sources. for(int i = 0; i < numSources; i++) { if(src[i] != null) { srcLineStride[i] = src[i].getScanlineStride(); srcPixelStride[i] = src[i].getPixelStride(); srcBandOffsets[i] = src[i].getBandOffsets(); srcData[i] = src[i].getShortDataArrays(); } } // Initialize destination variables. int dstMinX = dst.getX(); int dstMinY = dst.getY(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstMaxX = dstMinX + dstWidth; // x max exclusive int dstMaxY = dstMinY + dstHeight; // y max exclusive int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); short[][] dstData = dst.getShortDataArrays(); // Check for alpha. boolean hasAlpha = false; for(int i = 0; i < numSources; i++) { if(alfa[i] != null) { hasAlpha = true; break; } } // Declare alpha channel arrays. int[] alfaLineStride = null; int[] alfaPixelStride = null; int[][] alfaBandOffsets = null; short[][][] alfaData = null; if(hasAlpha) { // Allocate stride, offset, and data arrays for alpha channels. alfaLineStride = new int[numSources]; alfaPixelStride = new int[numSources]; alfaBandOffsets = new int[numSources][]; alfaData = new short[numSources][][]; // Initialize stride, offset, and data arrays for alpha channels. for(int i = 0; i < numSources; i++) { if(alfa[i] != null) { alfaLineStride[i] = alfa[i].getScanlineStride(); alfaPixelStride[i] = alfa[i].getPixelStride(); alfaBandOffsets[i] = alfa[i].getBandOffsets(); alfaData[i] = alfa[i].getShortDataArrays(); } } } // Initialize weight type arrays. int[] weightTypes = new int[numSources]; for(int i = 0; i < numSources; i++) { weightTypes[i] = WEIGHT_TYPE_THRESHOLD; if(alfa[i] != null) { weightTypes[i] = WEIGHT_TYPE_ALPHA; } else if(sourceROI != null && sourceROI[i] != null) { weightTypes[i] = WEIGHT_TYPE_ROI; } } // Set up source offset and data variabls. int[] sLineOffsets = new int[numSources]; int[] sPixelOffsets = new int[numSources]; short[][] sBandData = new short[numSources][]; // Set up alpha offset and data variabls. int[] aLineOffsets = null; int[] aPixelOffsets = null; short[][] aBandData = null; if(hasAlpha) { aLineOffsets = new int[numSources]; aPixelOffsets = new int[numSources]; aBandData = new short[numSources][]; } for(int b = 0; b < dstBands; b++) { // Initialize source and alpha band array and line offsets. for(int s = 0; s < numSources; s++) { if(src[s] != null) { sBandData[s] = srcData[s][b]; sLineOffsets[s] = srcBandOffsets[s][b]; } if(weightTypes[s] == WEIGHT_TYPE_ALPHA) { aBandData[s] = alfaData[s][0]; aLineOffsets[s] = alfaBandOffsets[s][0]; } } // Initialize destination band array and line offsets. short[] dBandData = dstData[b]; int dLineOffset = dstBandOffsets[b]; if(mosaicType == MosaicDescriptor.MOSAIC_TYPE_OVERLAY) { for(int dstY = dstMinY; dstY < dstMaxY; dstY++) { // Initialize source and alpha pixel offsets and // update line offsets. for(int s = 0; s < numSources; s++) { if(src[s] != null) { sPixelOffsets[s] = sLineOffsets[s]; sLineOffsets[s] += srcLineStride[s]; } if(alfa[s] != null) { aPixelOffsets[s] = aLineOffsets[s]; aLineOffsets[s] += alfaLineStride[s]; } } // Initialize destination pixel offset and update // line offset. int dPixelOffset = dLineOffset; dLineOffset += dstLineStride; for(int dstX = dstMinX; dstX < dstMaxX; dstX++) { // Unset destination update flag. boolean setDestValue = false; // Loop over source until a non-zero weight is // encountered. for(int s = 0; s < numSources; s++) { if(src[s] == null) continue; short sourceValue = sBandData[s][sPixelOffsets[s]]; sPixelOffsets[s] += srcPixelStride[s]; switch(weightTypes[s]) { case WEIGHT_TYPE_ALPHA: setDestValue = aBandData[s][aPixelOffsets[s]] != 0; aPixelOffsets[s] += alfaPixelStride[s]; break; case WEIGHT_TYPE_ROI: setDestValue = roi[s].getSample(dstX, dstY, 0) > 0; break; default: // WEIGHT_TYPE_THRESHOLD setDestValue = sourceValue >= sourceThreshold[s][b]; } // Set the destination value if a non-zero // weight was found. if(setDestValue) { dBandData[dPixelOffset] = sourceValue; // Increment offset of subsequent sources. for(int k = s + 1; k < numSources; k++) { if(src[k] != null) { sPixelOffsets[k] += srcPixelStride[k]; } if(alfa[k] != null) { aPixelOffsets[k] += alfaPixelStride[k]; } } break; } } // Set the destination value to the background if // no value was set. if(!setDestValue) { dBandData[dPixelOffset] = (short)background[b]; } dPixelOffset += dstPixelStride; } } } else { // mosaicType == MosaicDescriptor.MOSAIC_TYPE_BLEND for(int dstY = dstMinY; dstY < dstMaxY; dstY++) { // Initialize source and alpha pixel offsets and // update line offsets. for(int s = 0; s < numSources; s++) { if(src[s] != null) { sPixelOffsets[s] = sLineOffsets[s]; sLineOffsets[s] += srcLineStride[s]; } if(weightTypes[s] == WEIGHT_TYPE_ALPHA) { aPixelOffsets[s] = aLineOffsets[s]; aLineOffsets[s] += alfaLineStride[s]; } } // Initialize destination pixel offset and update // line offset. int dPixelOffset = dLineOffset; dLineOffset += dstLineStride; for(int dstX = dstMinX; dstX < dstMaxX; dstX++) { // Clear values for blending ratio. float numerator = 0.0F; float denominator = 0.0F; // Accumulate into numerator and denominator. for(int s = 0; s < numSources; s++) { if(src[s] == null) continue; short sourceValue = sBandData[s][sPixelOffsets[s]]; sPixelOffsets[s] += srcPixelStride[s]; float weight = 0.0F; switch(weightTypes[s]) { case WEIGHT_TYPE_ALPHA: weight = aBandData[s][aPixelOffsets[s]]; if(weight > 0.0F && isAlphaBitmask) { weight = 1.0F; } else { weight /= (float)Short.MAX_VALUE; } aPixelOffsets[s] += alfaPixelStride[s]; break; case WEIGHT_TYPE_ROI: weight = roi[s].getSample(dstX, dstY, 0) > 0 ? 1.0F : 0.0F; break; default: // WEIGHT_TYPE_THRESHOLD weight = sourceValue >= sourceThreshold[s][b] ? 1.0F : 0.0F; } // Update numerator and denominator. numerator += weight*sourceValue; denominator += weight; } // Clear the background if all weights were zero, // otherwise blend the values. if(denominator == 0.0) { dBandData[dPixelOffset] = (short)background[b]; } else { dBandData[dPixelOffset] = ImageUtil.clampRoundShort(numerator / denominator); } dPixelOffset += dstPixelStride; } } } } } private void computeRectInt(RasterAccessor[] src, RasterAccessor dst, RasterAccessor[] alfa, Raster[] roi) { // Save the source count. int numSources = src.length; // Allocate stride, offset, and data arrays for sources. int[] srcLineStride = new int[numSources]; int[] srcPixelStride = new int[numSources]; int[][] srcBandOffsets = new int[numSources][]; int[][][] srcData = new int[numSources][][]; // Initialize stride, offset, and data arrays for sources. for(int i = 0; i < numSources; i++) { if(src[i] != null) { srcLineStride[i] = src[i].getScanlineStride(); srcPixelStride[i] = src[i].getPixelStride(); srcBandOffsets[i] = src[i].getBandOffsets(); srcData[i] = src[i].getIntDataArrays(); } } // Initialize destination variables. int dstMinX = dst.getX(); int dstMinY = dst.getY(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstMaxX = dstMinX + dstWidth; // x max exclusive int dstMaxY = dstMinY + dstHeight; // y max exclusive int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); int[][] dstData = dst.getIntDataArrays(); // Check for alpha. boolean hasAlpha = false; for(int i = 0; i < numSources; i++) { if(alfa[i] != null) { hasAlpha = true; break; } } // Declare alpha channel arrays. int[] alfaLineStride = null; int[] alfaPixelStride = null; int[][] alfaBandOffsets = null; int[][][] alfaData = null; if(hasAlpha) { // Allocate stride, offset, and data arrays for alpha channels. alfaLineStride = new int[numSources]; alfaPixelStride = new int[numSources]; alfaBandOffsets = new int[numSources][]; alfaData = new int[numSources][][]; // Initialize stride, offset, and data arrays for alpha channels. for(int i = 0; i < numSources; i++) { if(alfa[i] != null) { alfaLineStride[i] = alfa[i].getScanlineStride(); alfaPixelStride[i] = alfa[i].getPixelStride(); alfaBandOffsets[i] = alfa[i].getBandOffsets(); alfaData[i] = alfa[i].getIntDataArrays(); } } } // Initialize weight type arrays. int[] weightTypes = new int[numSources]; for(int i = 0; i < numSources; i++) { weightTypes[i] = WEIGHT_TYPE_THRESHOLD; if(alfa[i] != null) { weightTypes[i] = WEIGHT_TYPE_ALPHA; } else if(sourceROI != null && sourceROI[i] != null) { weightTypes[i] = WEIGHT_TYPE_ROI; } } // Set up source offset and data variabls. int[] sLineOffsets = new int[numSources]; int[] sPixelOffsets = new int[numSources]; int[][] sBandData = new int[numSources][]; // Set up alpha offset and data variabls. int[] aLineOffsets = null; int[] aPixelOffsets = null; int[][] aBandData = null; if(hasAlpha) { aLineOffsets = new int[numSources]; aPixelOffsets = new int[numSources]; aBandData = new int[numSources][]; } for(int b = 0; b < dstBands; b++) { // Initialize source and alpha band array and line offsets. for(int s = 0; s < numSources; s++) { if(src[s] != null) { sBandData[s] = srcData[s][b]; sLineOffsets[s] = srcBandOffsets[s][b]; } if(weightTypes[s] == WEIGHT_TYPE_ALPHA) { aBandData[s] = alfaData[s][0]; aLineOffsets[s] = alfaBandOffsets[s][0]; } } // Initialize destination band array and line offsets. int[] dBandData = dstData[b]; int dLineOffset = dstBandOffsets[b]; if(mosaicType == MosaicDescriptor.MOSAIC_TYPE_OVERLAY) { for(int dstY = dstMinY; dstY < dstMaxY; dstY++) { // Initialize source and alpha pixel offsets and // update line offsets. for(int s = 0; s < numSources; s++) { if(src[s] != null) { sPixelOffsets[s] = sLineOffsets[s]; sLineOffsets[s] += srcLineStride[s]; } if(alfa[s] != null) { aPixelOffsets[s] = aLineOffsets[s]; aLineOffsets[s] += alfaLineStride[s]; } } // Initialize destination pixel offset and update // line offset. int dPixelOffset = dLineOffset; dLineOffset += dstLineStride; for(int dstX = dstMinX; dstX < dstMaxX; dstX++) { // Unset destination update flag. boolean setDestValue = false; // Loop over source until a non-zero weight is // encountered. for(int s = 0; s < numSources; s++) { if(src[s] == null) continue; int sourceValue = sBandData[s][sPixelOffsets[s]]; sPixelOffsets[s] += srcPixelStride[s]; switch(weightTypes[s]) { case WEIGHT_TYPE_ALPHA: setDestValue = aBandData[s][aPixelOffsets[s]] != 0; aPixelOffsets[s] += alfaPixelStride[s]; break; case WEIGHT_TYPE_ROI: setDestValue = roi[s].getSample(dstX, dstY, 0) > 0; break; default: // WEIGHT_TYPE_THRESHOLD setDestValue = sourceValue >= sourceThreshold[s][b]; } // Set the destination value if a non-zero // weight was found. if(setDestValue) { dBandData[dPixelOffset] = sourceValue; // Increment offset of subsequent sources. for(int k = s + 1; k < numSources; k++) { if(src[k] != null) { sPixelOffsets[k] += srcPixelStride[k]; } if(alfa[k] != null) { aPixelOffsets[k] += alfaPixelStride[k]; } } break; } } // Set the destination value to the background if // no value was set. if(!setDestValue) { dBandData[dPixelOffset] = (int)background[b]; } dPixelOffset += dstPixelStride; } } } else { // mosaicType == MosaicDescriptor.MOSAIC_TYPE_BLEND for(int dstY = dstMinY; dstY < dstMaxY; dstY++) { // Initialize source and alpha pixel offsets and // update line offsets. for(int s = 0; s < numSources; s++) { if(src[s] != null) { sPixelOffsets[s] = sLineOffsets[s]; sLineOffsets[s] += srcLineStride[s]; } if(weightTypes[s] == WEIGHT_TYPE_ALPHA) { aPixelOffsets[s] = aLineOffsets[s]; aLineOffsets[s] += alfaLineStride[s]; } } // Initialize destination pixel offset and update // line offset. int dPixelOffset = dLineOffset; dLineOffset += dstLineStride; for(int dstX = dstMinX; dstX < dstMaxX; dstX++) { // Clear values for blending ratio. double numerator = 0.0; double denominator = 0.0; // Accumulate into numerator and denominator. for(int s = 0; s < numSources; s++) { if(src[s] == null) continue; int sourceValue = sBandData[s][sPixelOffsets[s]]; sPixelOffsets[s] += srcPixelStride[s]; double weight = 0.0; switch(weightTypes[s]) { case WEIGHT_TYPE_ALPHA: weight = aBandData[s][aPixelOffsets[s]]; if(weight > 0.0F && isAlphaBitmask) { weight = 1.0F; } else { weight /= Integer.MAX_VALUE; } aPixelOffsets[s] += alfaPixelStride[s]; break; case WEIGHT_TYPE_ROI: weight = roi[s].getSample(dstX, dstY, 0) > 0 ? 1.0F : 0.0F; break; default: // WEIGHT_TYPE_THRESHOLD weight = sourceValue >= sourceThreshold[s][b] ? 1.0F : 0.0F; } // Update numerator and denominator. numerator += weight*sourceValue; denominator += weight; } // Clear the background if all weights were zero, // otherwise blend the values. if(denominator == 0.0) { dBandData[dPixelOffset] = (int)background[b]; } else { dBandData[dPixelOffset] = ImageUtil.clampRoundInt(numerator / denominator); } dPixelOffset += dstPixelStride; } } } } } private void computeRectFloat(RasterAccessor[] src, RasterAccessor dst, RasterAccessor[] alfa, Raster[] roi) { // Save the source count. int numSources = src.length; // Allocate stride, offset, and data arrays for sources. int[] srcLineStride = new int[numSources]; int[] srcPixelStride = new int[numSources]; int[][] srcBandOffsets = new int[numSources][]; float[][][] srcData = new float[numSources][][]; // Initialize stride, offset, and data arrays for sources. for(int i = 0; i < numSources; i++) { if(src[i] != null) { srcLineStride[i] = src[i].getScanlineStride(); srcPixelStride[i] = src[i].getPixelStride(); srcBandOffsets[i] = src[i].getBandOffsets(); srcData[i] = src[i].getFloatDataArrays(); } } // Initialize destination variables. int dstMinX = dst.getX(); int dstMinY = dst.getY(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstMaxX = dstMinX + dstWidth; // x max exclusive int dstMaxY = dstMinY + dstHeight; // y max exclusive int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); float[][] dstData = dst.getFloatDataArrays(); // Check for alpha. boolean hasAlpha = false; for(int i = 0; i < numSources; i++) { if(alfa[i] != null) { hasAlpha = true; break; } } // Declare alpha channel arrays. int[] alfaLineStride = null; int[] alfaPixelStride = null; int[][] alfaBandOffsets = null; float[][][] alfaData = null; if(hasAlpha) { // Allocate stride, offset, and data arrays for alpha channels. alfaLineStride = new int[numSources]; alfaPixelStride = new int[numSources]; alfaBandOffsets = new int[numSources][]; alfaData = new float[numSources][][]; // Initialize stride, offset, and data arrays for alpha channels. for(int i = 0; i < numSources; i++) { if(alfa[i] != null) { alfaLineStride[i] = alfa[i].getScanlineStride(); alfaPixelStride[i] = alfa[i].getPixelStride(); alfaBandOffsets[i] = alfa[i].getBandOffsets(); alfaData[i] = alfa[i].getFloatDataArrays(); } } } // Initialize weight type arrays. int[] weightTypes = new int[numSources]; for(int i = 0; i < numSources; i++) { weightTypes[i] = WEIGHT_TYPE_THRESHOLD; if(alfa[i] != null) { weightTypes[i] = WEIGHT_TYPE_ALPHA; } else if(sourceROI != null && sourceROI[i] != null) { weightTypes[i] = WEIGHT_TYPE_ROI; } } // Set up source offset and data variabls. int[] sLineOffsets = new int[numSources]; int[] sPixelOffsets = new int[numSources]; float[][] sBandData = new float[numSources][]; // Set up alpha offset and data variabls. int[] aLineOffsets = null; int[] aPixelOffsets = null; float[][] aBandData = null; if(hasAlpha) { aLineOffsets = new int[numSources]; aPixelOffsets = new int[numSources]; aBandData = new float[numSources][]; } for(int b = 0; b < dstBands; b++) { // Initialize source and alpha band array and line offsets. for(int s = 0; s < numSources; s++) { if(src[s] != null) { sBandData[s] = srcData[s][b]; sLineOffsets[s] = srcBandOffsets[s][b]; } if(weightTypes[s] == WEIGHT_TYPE_ALPHA) { aBandData[s] = alfaData[s][0]; aLineOffsets[s] = alfaBandOffsets[s][0]; } } // Initialize destination band array and line offsets. float[] dBandData = dstData[b]; int dLineOffset = dstBandOffsets[b]; if(mosaicType == MosaicDescriptor.MOSAIC_TYPE_OVERLAY) { for(int dstY = dstMinY; dstY < dstMaxY; dstY++) { // Initialize source and alpha pixel offsets and // update line offsets. for(int s = 0; s < numSources; s++) { if(src[s] != null) { sPixelOffsets[s] = sLineOffsets[s]; sLineOffsets[s] += srcLineStride[s]; } if(alfa[s] != null) { aPixelOffsets[s] = aLineOffsets[s]; aLineOffsets[s] += alfaLineStride[s]; } } // Initialize destination pixel offset and update // line offset. int dPixelOffset = dLineOffset; dLineOffset += dstLineStride; for(int dstX = dstMinX; dstX < dstMaxX; dstX++) { // Unset destination update flag. boolean setDestValue = false; // Loop over source until a non-zero weight is // encountered. for(int s = 0; s < numSources; s++) { if(src[s] == null) continue; float sourceValue = sBandData[s][sPixelOffsets[s]]; sPixelOffsets[s] += srcPixelStride[s]; switch(weightTypes[s]) { case WEIGHT_TYPE_ALPHA: setDestValue = aBandData[s][aPixelOffsets[s]] != 0; aPixelOffsets[s] += alfaPixelStride[s]; break; case WEIGHT_TYPE_ROI: setDestValue = roi[s].getSample(dstX, dstY, 0) > 0; break; default: // WEIGHT_TYPE_THRESHOLD setDestValue = sourceValue >= sourceThreshold[s][b]; } // Set the destination value if a non-zero // weight was found. if(setDestValue) { dBandData[dPixelOffset] = sourceValue; // Increment offset of subsequent sources. for(int k = s + 1; k < numSources; k++) { if(src[k] != null) { sPixelOffsets[k] += srcPixelStride[k]; } if(alfa[k] != null) { aPixelOffsets[k] += alfaPixelStride[k]; } } break; } } // Set the destination value to the background if // no value was set. if(!setDestValue) { dBandData[dPixelOffset] = (float)backgroundValues[b]; } dPixelOffset += dstPixelStride; } } } else { // mosaicType == MosaicDescriptor.MOSAIC_TYPE_BLEND for(int dstY = dstMinY; dstY < dstMaxY; dstY++) { // Initialize source and alpha pixel offsets and // update line offsets. for(int s = 0; s < numSources; s++) { if(src[s] != null) { sPixelOffsets[s] = sLineOffsets[s]; sLineOffsets[s] += srcLineStride[s]; } if(weightTypes[s] == WEIGHT_TYPE_ALPHA) { aPixelOffsets[s] = aLineOffsets[s]; aLineOffsets[s] += alfaLineStride[s]; } } // Initialize destination pixel offset and update // line offset. int dPixelOffset = dLineOffset; dLineOffset += dstLineStride; for(int dstX = dstMinX; dstX < dstMaxX; dstX++) { // Clear values for blending ratio. float numerator = 0.0F; float denominator = 0.0F; // Accumulate into numerator and denominator. for(int s = 0; s < numSources; s++) { if(src[s] == null) continue; float sourceValue = sBandData[s][sPixelOffsets[s]]; sPixelOffsets[s] += srcPixelStride[s]; float weight = 0.0F; switch(weightTypes[s]) { case WEIGHT_TYPE_ALPHA: weight = aBandData[s][aPixelOffsets[s]]; if(weight > 0.0F && isAlphaBitmask) { weight = 1.0F; } aPixelOffsets[s] += alfaPixelStride[s]; break; case WEIGHT_TYPE_ROI: weight = roi[s].getSample(dstX, dstY, 0) > 0 ? 1.0F : 0.0F; break; default: // WEIGHT_TYPE_THRESHOLD weight = sourceValue >= sourceThreshold[s][b] ? 1.0F : 0.0F; } // Update numerator and denominator. numerator += weight*sourceValue; denominator += weight; } // Clear the background if all weights were zero, // otherwise blend the values. if(denominator == 0.0) { dBandData[dPixelOffset] = (float)backgroundValues[b]; } else { dBandData[dPixelOffset] = numerator / denominator; } dPixelOffset += dstPixelStride; } } } } } private void computeRectDouble(RasterAccessor[] src, RasterAccessor dst, RasterAccessor[] alfa, Raster[] roi) { // Save the source count. int numSources = src.length; // Allocate stride, offset, and data arrays for sources. int[] srcLineStride = new int[numSources]; int[] srcPixelStride = new int[numSources]; int[][] srcBandOffsets = new int[numSources][]; double[][][] srcData = new double[numSources][][]; // Initialize stride, offset, and data arrays for sources. for(int i = 0; i < numSources; i++) { if(src[i] != null) { srcLineStride[i] = src[i].getScanlineStride(); srcPixelStride[i] = src[i].getPixelStride(); srcBandOffsets[i] = src[i].getBandOffsets(); srcData[i] = src[i].getDoubleDataArrays(); } } // Initialize destination variables. int dstMinX = dst.getX(); int dstMinY = dst.getY(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstMaxX = dstMinX + dstWidth; // x max exclusive int dstMaxY = dstMinY + dstHeight; // y max exclusive int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); double[][] dstData = dst.getDoubleDataArrays(); // Check for alpha. boolean hasAlpha = false; for(int i = 0; i < numSources; i++) { if(alfa[i] != null) { hasAlpha = true; break; } } // Declare alpha channel arrays. int[] alfaLineStride = null; int[] alfaPixelStride = null; int[][] alfaBandOffsets = null; double[][][] alfaData = null; if(hasAlpha) { // Allocate stride, offset, and data arrays for alpha channels. alfaLineStride = new int[numSources]; alfaPixelStride = new int[numSources]; alfaBandOffsets = new int[numSources][]; alfaData = new double[numSources][][]; // Initialize stride, offset, and data arrays for alpha channels. for(int i = 0; i < numSources; i++) { if(alfa[i] != null) { alfaLineStride[i] = alfa[i].getScanlineStride(); alfaPixelStride[i] = alfa[i].getPixelStride(); alfaBandOffsets[i] = alfa[i].getBandOffsets(); alfaData[i] = alfa[i].getDoubleDataArrays(); } } } // Initialize weight type arrays. int[] weightTypes = new int[numSources]; for(int i = 0; i < numSources; i++) { weightTypes[i] = WEIGHT_TYPE_THRESHOLD; if(alfa[i] != null) { weightTypes[i] = WEIGHT_TYPE_ALPHA; } else if(sourceROI != null && sourceROI[i] != null) { weightTypes[i] = WEIGHT_TYPE_ROI; } } // Set up source offset and data variabls. int[] sLineOffsets = new int[numSources]; int[] sPixelOffsets = new int[numSources]; double[][] sBandData = new double[numSources][]; // Set up alpha offset and data variabls. int[] aLineOffsets = null; int[] aPixelOffsets = null; double[][] aBandData = null; if(hasAlpha) { aLineOffsets = new int[numSources]; aPixelOffsets = new int[numSources]; aBandData = new double[numSources][]; } for(int b = 0; b < dstBands; b++) { // Initialize source and alpha band array and line offsets. for(int s = 0; s < numSources; s++) { if(src[s] != null) { sBandData[s] = srcData[s][b]; sLineOffsets[s] = srcBandOffsets[s][b]; } if(weightTypes[s] == WEIGHT_TYPE_ALPHA) { aBandData[s] = alfaData[s][0]; aLineOffsets[s] = alfaBandOffsets[s][0]; } } // Initialize destination band array and line offsets. double[] dBandData = dstData[b]; int dLineOffset = dstBandOffsets[b]; if(mosaicType == MosaicDescriptor.MOSAIC_TYPE_OVERLAY) { for(int dstY = dstMinY; dstY < dstMaxY; dstY++) { // Initialize source and alpha pixel offsets and // update line offsets. for(int s = 0; s < numSources; s++) { if(src[s] != null) { sPixelOffsets[s] = sLineOffsets[s]; sLineOffsets[s] += srcLineStride[s]; } if(alfa[s] != null) { aPixelOffsets[s] = aLineOffsets[s]; aLineOffsets[s] += alfaLineStride[s]; } } // Initialize destination pixel offset and update // line offset. int dPixelOffset = dLineOffset; dLineOffset += dstLineStride; for(int dstX = dstMinX; dstX < dstMaxX; dstX++) { // Unset destination update flag. boolean setDestValue = false; // Loop over source until a non-zero weight is // encountered. for(int s = 0; s < numSources; s++) { if(src[s] == null) continue; double sourceValue = sBandData[s][sPixelOffsets[s]]; sPixelOffsets[s] += srcPixelStride[s]; switch(weightTypes[s]) { case WEIGHT_TYPE_ALPHA: setDestValue = aBandData[s][aPixelOffsets[s]] != 0; aPixelOffsets[s] += alfaPixelStride[s]; break; case WEIGHT_TYPE_ROI: setDestValue = roi[s].getSample(dstX, dstY, 0) > 0; break; default: // WEIGHT_TYPE_THRESHOLD setDestValue = sourceValue >= sourceThreshold[s][b]; } // Set the destination value if a non-zero // weight was found. if(setDestValue) { dBandData[dPixelOffset] = sourceValue; // Increment offset of subsequent sources. for(int k = s + 1; k < numSources; k++) { if(src[k] != null) { sPixelOffsets[k] += srcPixelStride[k]; } if(alfa[k] != null) { aPixelOffsets[k] += alfaPixelStride[k]; } } break; } } // Set the destination value to the background if // no value was set. if(!setDestValue) { dBandData[dPixelOffset] = backgroundValues[b]; } dPixelOffset += dstPixelStride; } } } else { // mosaicType == MosaicDescriptor.MOSAIC_TYPE_BLEND for(int dstY = dstMinY; dstY < dstMaxY; dstY++) { // Initialize source and alpha pixel offsets and // update line offsets. for(int s = 0; s < numSources; s++) { if(src[s] != null) { sPixelOffsets[s] = sLineOffsets[s]; sLineOffsets[s] += srcLineStride[s]; } if(weightTypes[s] == WEIGHT_TYPE_ALPHA) { aPixelOffsets[s] = aLineOffsets[s]; aLineOffsets[s] += alfaLineStride[s]; } } // Initialize destination pixel offset and update // line offset. int dPixelOffset = dLineOffset; dLineOffset += dstLineStride; for(int dstX = dstMinX; dstX < dstMaxX; dstX++) { // Clear values for blending ratio. double numerator = 0.0F; double denominator = 0.0F; // Accumulate into numerator and denominator. for(int s = 0; s < numSources; s++) { if(src[s] == null) continue; double sourceValue = sBandData[s][sPixelOffsets[s]]; sPixelOffsets[s] += srcPixelStride[s]; double weight = 0.0F; switch(weightTypes[s]) { case WEIGHT_TYPE_ALPHA: weight = aBandData[s][aPixelOffsets[s]]; if(weight > 0.0F && isAlphaBitmask) { weight = 1.0F; } aPixelOffsets[s] += alfaPixelStride[s]; break; case WEIGHT_TYPE_ROI: weight = roi[s].getSample(dstX, dstY, 0) > 0 ? 1.0F : 0.0F; break; default: // WEIGHT_TYPE_THRESHOLD weight = sourceValue >= sourceThreshold[s][b] ? 1.0F : 0.0F; } // Update numerator and denominator. numerator += weight*sourceValue; denominator += weight; } // Clear the background if all weights were zero, // otherwise blend the values. if(denominator == 0.0) { dBandData[dPixelOffset] = backgroundValues[b]; } else { dBandData[dPixelOffset] = numerator / denominator; } dPixelOffset += dstPixelStride; } } } } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ScaleNearestOpImage.java0000644000175000017500000004213310203035544027711 0ustar mathieumathieu/* * $RCSfile: ScaleNearestOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:43 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.IndexColorModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationNearest; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PlanarImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.ScaleOpImage; import java.util.Map; import javax.media.jai.BorderExtender; import com.sun.media.jai.util.Rational; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage subclass that performs nearest-neighbor scaling. * */ final class ScaleNearestOpImage extends ScaleOpImage { long invScaleXInt, invScaleXFrac; long invScaleYInt, invScaleYFrac; /** * Constructs a ScaleNearestOpImage from a RenderedImage source, * * @param source a RenderedImage. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param xScale scale factor along x axis. * @param yScale scale factor along y axis. * @param xTrans translation factor along x axis. * @param yTrans translation factor along y axis. * @param interp an Interpolation object to use for resampling. */ public ScaleNearestOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, float xScale, float yScale, float xTrans, float yTrans, Interpolation interp) { super(source, layout, config, true, extender, interp, xScale, yScale, xTrans, yTrans); // If the source has an IndexColorModel, override the default setting // in OpImage. The dest shall have exactly the same SampleModel and // ColorModel as the source. // Note, in this case, the source should have an integral data type. ColorModel srcColorModel = source.getColorModel(); if (srcColorModel instanceof IndexColorModel) { sampleModel = source.getSampleModel().createCompatibleSampleModel( tileWidth, tileHeight); colorModel = srcColorModel; } if (invScaleXRational.num > invScaleXRational.denom) { invScaleXInt = invScaleXRational.num / invScaleXRational.denom; invScaleXFrac = invScaleXRational.num % invScaleXRational.denom; } else { invScaleXInt = 0; invScaleXFrac = invScaleXRational.num; } if (invScaleYRational.num > invScaleYRational.denom) { invScaleYInt = invScaleYRational.num / invScaleYRational.denom; invScaleYFrac = invScaleYRational.num % invScaleYRational.denom; } else { invScaleYInt = 0; invScaleYFrac = invScaleYRational.num; } } /** * Performs a scale operation on a specified rectangle. The sources are * cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster [] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); Raster source = sources[0]; // Get the source rectangle Rectangle srcRect = source.getBounds(); int srcRectX = srcRect.x; int srcRectY = srcRect.y; RasterAccessor srcAccessor = new RasterAccessor(source, srcRect, formatTags[0], getSource(0).getColorModel()); RasterAccessor dstAccessor = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); int srcScanlineStride = srcAccessor.getScanlineStride(); int srcPixelStride = srcAccessor.getPixelStride(); // Destination rectangle dimensions. int dx = destRect.x; int dy = destRect.y; int dwidth = destRect.width; int dheight = destRect.height; // Precalculate the x positions and store them in an array. int[] xvalues = new int[dwidth]; long sxNum = dx, sxDenom = 1; // Subtract the X translation factor sx -= transX sxNum = sxNum * transXRationalDenom - transXRationalNum * sxDenom; sxDenom *= transXRationalDenom; // Add 0.5 sxNum = 2 * sxNum + sxDenom; sxDenom *= 2; // Multply by invScaleX sxNum *= invScaleXRationalNum; sxDenom *= invScaleXRationalDenom; // Separate the x source coordinate into integer and fractional part // int part is floor(sx), frac part is sx - floor(sx) int srcXInt = Rational.floor(sxNum , sxDenom); long srcXFrac = sxNum % sxDenom; if (srcXInt < 0) { srcXFrac = sxDenom + srcXFrac; } // Normalize - Get a common denominator for the fracs of // src and invScaleX long commonXDenom = sxDenom * invScaleXRationalDenom; srcXFrac *= invScaleXRationalDenom; long newInvScaleXFrac = invScaleXFrac * sxDenom; for (int i = 0; i < dwidth; i++) { // Calculate the position xvalues[i] = (srcXInt - srcRectX) * srcPixelStride; // Move onto the next source pixel. // Add the integral part of invScaleX to the integral part // of srcX srcXInt += invScaleXInt; // Add the fractional part of invScaleX to the fractional part // of srcX srcXFrac += newInvScaleXFrac; // If the fractional part is now greater than equal to the // denominator, divide so as to reduce the numerator to be less // than the denominator and add the overflow to the integral part. if (srcXFrac >= commonXDenom) { srcXInt += 1; srcXFrac -= commonXDenom; } } // Precalculate the y positions and store them in an array. int[] yvalues = new int[dheight]; long syNum = dy, syDenom = 1; // Subtract the X translation factor sy -= transY syNum = syNum * transYRationalDenom - transYRationalNum * syDenom; syDenom *= transYRationalDenom; // Add 0.5 syNum = 2 * syNum + syDenom; syDenom *= 2; // Multply by invScaleX syNum *= invScaleYRationalNum; syDenom *= invScaleYRationalDenom; // Separate the x source coordinate into integer and fractional part int srcYInt = Rational.floor(syNum , syDenom); long srcYFrac = syNum % syDenom; if (srcYInt < 0) { srcYFrac = syDenom + srcYFrac; } // Normalize - Get a common denominator for the fracs of // src and invScaleY long commonYDenom = syDenom * invScaleYRationalDenom; srcYFrac *= invScaleYRationalDenom; long newInvScaleYFrac = invScaleYFrac * syDenom; for (int i = 0; i < dheight; i++) { // Calculate the position yvalues[i] = (srcYInt - srcRectY) * srcScanlineStride; // Move onto the next source pixel. // Add the integral part of invScaleY to the integral part // of srcY srcYInt += invScaleYInt; // Add the fractional part of invScaleY to the fractional part // of srcY srcYFrac += newInvScaleYFrac; // If the fractional part is now greater than equal to the // denominator, divide so as to reduce the numerator to be less // than the denominator and add the overflow to the integral part. if (srcYFrac >= commonYDenom) { srcYInt += 1; srcYFrac -= commonYDenom; } } switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(srcAccessor, destRect, dstAccessor, xvalues, yvalues); break; case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_USHORT: shortLoop(srcAccessor, destRect, dstAccessor, xvalues, yvalues); break; case DataBuffer.TYPE_INT: intLoop(srcAccessor, destRect, dstAccessor, xvalues, yvalues); break; case DataBuffer.TYPE_FLOAT: floatLoop(srcAccessor, destRect, dstAccessor, xvalues, yvalues); break; case DataBuffer.TYPE_DOUBLE: doubleLoop(srcAccessor, destRect, dstAccessor, xvalues, yvalues); break; default: throw new RuntimeException(JaiI18N.getString("OrderedDitherOpImage0")); } // If the RasterAccessor object set up a temporary buffer for the // op to write to, tell the RasterAccessor to write that data // to the raster no that we're done with it. if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } private void byteLoop(RasterAccessor src, Rectangle dstRect, RasterAccessor dst, int xvalues[], int yvalues[]) { int dwidth = dstRect.width; int dheight = dstRect.height; // Get destination related variables. byte dstDataArrays[][] = dst.getByteDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int dnumBands = dst.getNumBands(); // Get source related variables. int bandOffsets[] = src.getBandOffsets(); byte srcDataArrays[][] = src.getByteDataArrays(); int dstPixelOffset; int dstOffset = 0; int posy, posx, pos; int dstScanlineOffset; // For each band for (int k = 0; k < dnumBands; k++) { byte dstData[] = dstDataArrays[k]; byte srcData[] = srcDataArrays[k]; int bandOffset = bandOffsets[k]; dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { dstPixelOffset = dstScanlineOffset; posy = yvalues[j] + bandOffset; for (int i = 0; i < dwidth; i++) { posx = xvalues[i]; pos = posx + posy; dstData[dstPixelOffset] = srcData[pos]; dstPixelOffset += dstPixelStride; } dstScanlineOffset += dstScanlineStride; } } } private void shortLoop(RasterAccessor src, Rectangle dstRect, RasterAccessor dst, int xvalues[], int yvalues[]) { int dwidth = dstRect.width; int dheight = dstRect.height; // Get destination related variables. short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int dnumBands = dst.getNumBands(); // Get source related variables. int bandOffsets[] = src.getBandOffsets(); short srcDataArrays[][] = src.getShortDataArrays(); int dstPixelOffset; int dstOffset = 0; int posy, posx, pos; int dstScanlineOffset; // For each band for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int bandOffset = bandOffsets[k]; dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { dstPixelOffset = dstScanlineOffset; posy = yvalues[j] + bandOffset; for (int i = 0; i < dwidth; i++) { posx = xvalues[i]; pos = posx + posy; dstData[dstPixelOffset] = srcData[pos]; dstPixelOffset += dstPixelStride; } dstScanlineOffset += dstScanlineStride; } } } // identical to byteLoops, except datatypes have changed. clumsy, // but there's no other way in Java private void intLoop(RasterAccessor src, Rectangle dstRect, RasterAccessor dst, int xvalues[], int yvalues[]) { int dwidth = dstRect.width; int dheight = dstRect.height; int dnumBands = dst.getNumBands(); int dstDataArrays[][] = dst.getIntDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int bandOffsets[] = src.getBandOffsets(); int srcDataArrays[][] = src.getIntDataArrays(); int dstPixelOffset; int dstOffset = 0; int posy, posx, pos; int dstScanlineOffset; // For each band for (int k = 0; k < dnumBands; k++) { int dstData[] = dstDataArrays[k]; int srcData[] = srcDataArrays[k]; int bandOffset = bandOffsets[k]; dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { dstPixelOffset = dstScanlineOffset; posy = yvalues[j] + bandOffset; for (int i = 0; i < dwidth; i++) { posx = xvalues[i]; pos = posx + posy; dstData[dstPixelOffset] = srcData[pos]; dstPixelOffset += dstPixelStride; } dstScanlineOffset += dstScanlineStride; } } } // identical to byteLoop, except datatypes have changed. clumsy, // but there's no other way in Java private void floatLoop(RasterAccessor src, Rectangle dstRect, RasterAccessor dst, int xvalues[], int yvalues[]) { int dwidth = dstRect.width; int dheight = dstRect.height; int dnumBands = dst.getNumBands(); float dstDataArrays[][] = dst.getFloatDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); float srcDataArrays[][] = src.getFloatDataArrays(); int bandOffsets[] = src.getBandOffsets(); int dstPixelOffset; int dstOffset = 0; int posy, posx, pos; int dstScanlineOffset; // For each band for (int k = 0; k < dnumBands; k++) { float dstData[] = dstDataArrays[k]; float srcData[] = srcDataArrays[k]; int bandOffset = bandOffsets[k]; dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { dstPixelOffset = dstScanlineOffset; posy = yvalues[j] + bandOffset; for (int i = 0; i < dwidth; i++) { posx = xvalues[i]; pos = posx + posy; dstData[dstPixelOffset] = srcData[pos]; dstPixelOffset += dstPixelStride; } dstScanlineOffset += dstScanlineStride; } } } // identical to byteLoop, except datatypes have changed. clumsy, // but there's no other way in Java private void doubleLoop(RasterAccessor src, Rectangle dstRect, RasterAccessor dst, int xvalues[], int yvalues[]) { int dwidth = dstRect.width; int dheight = dstRect.height; int dnumBands = dst.getNumBands(); double dstDataArrays[][] = dst.getDoubleDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int bandOffsets[] = src.getBandOffsets(); double srcDataArrays[][] = src.getDoubleDataArrays(); int dstPixelOffset; int dstOffset = 0; int posy, posx, pos; int dstScanlineOffset; // For each band for (int k = 0; k < dnumBands; k++) { double dstData[] = dstDataArrays[k]; double srcData[] = srcDataArrays[k]; int bandOffset = bandOffsets[k]; dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { dstPixelOffset = dstScanlineOffset; posy = yvalues[j] + bandOffset; for (int i = 0; i < dwidth; i++) { posx = xvalues[i]; pos = posx + posy; dstData[dstPixelOffset] = srcData[pos]; dstPixelOffset += dstPixelStride; } dstScanlineOffset += dstScanlineStride; } } } // public static OpImage createTestImage(OpImageTester oit) { // Interpolation interp = // Interpolation.getInstance(Interpolation.INTERP_NEAREST); // return new ScaleNearestOpImage(oit.getSource(), null, // new ImageLayout(oit.getSource()), // 2.5F, 2.5F, 0.0F, 0.0F, // interp); // } // public static void main(String args[]) { // String classname = "com.sun.media.jai.opimage.ScaleNearestOpImage"; // OpImageTester.performDiagnostics(classname, args); // System.exit(1); // System.out.println("ScaleOpImage Test"); // ImageLayout layout; // OpImage src, dst; // Rectangle rect = new Rectangle(0, 0, 5, 5); // InterpolationNearest interp = new InterpolationNearest(); // System.out.println("1. PixelInterleaved short 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 200, 200, 0, 0, 64, 64, DataBuffer.TYPE_SHORT, 3, false); // src = OpImageTester.createRandomOpImage(layout); // dst = new ScaleNearestOpImage(src, null, null, // 2.0F, 2.0F, 0.0F, 0.0F, interp); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("2. PixelInterleaved ushort 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_USHORT, 3, false); // src = OpImageTester.createRandomOpImage(layout); // dst = new ScaleNearestOpImage(src, null, null, // 4.0F, 2.0F, 0.0F, 0.0F, interp); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MedianCutOpImage.java0000644000175000017500000004406110240004133027202 0ustar mathieumathieu/* * $RCSfile: MedianCutOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-05-10 01:03:22 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Image; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.util.ArrayList; import java.util.Hashtable; import java.util.LinkedList; import java.util.ListIterator; import java.util.Map; import javax.media.jai.ImageLayout; import javax.media.jai.LookupTableJAI; import javax.media.jai.OpImage; import javax.media.jai.PixelAccessor; import javax.media.jai.PlanarImage; import javax.media.jai.ROI; import javax.media.jai.ROIShape; import javax.media.jai.UnpackedImageData; import com.sun.media.jai.util.ImageUtil; /** * An OpImage implementing the "ColorQuantizer" operation as * described in javax.media.jai.operator.ExtremaDescriptor * based on the median-cut algorithm. * * @see javax.media.jai.operator.ExtremaDescriptor * @see ExtremaCRIF */ public class MedianCutOpImage extends ColorQuantizerOpImage { /** The size of the histogram. */ private int histogramSize; /** The counts of the colors. */ private int[] counts; /** The colors for the color histogram. */ private int[] colors; /** The partition of the RGB color space. Each cube contains one * cluster. */ private Cube[] partition; /** The maximum number of bits to contains 32768 colors. */ private int bits = 8; /** The mask to generate the low bits colors from the original colors. */ private int mask; /** The histgram hash. */ HistogramHash histogram; /** * Constructs an MedianCutOpImage. * * @param source The source image. */ public MedianCutOpImage(RenderedImage source, Map config, ImageLayout layout, int maxColorNum, int upperBound, ROI roi, int xPeriod, int yPeriod) { super(source, config, layout, maxColorNum, roi, xPeriod, yPeriod); colorMap = null; this.histogramSize = upperBound; } protected synchronized void train() { PlanarImage source = getSourceImage(0); if (roi == null) roi = new ROIShape(source.getBounds()); // Cycle throw all source tiles. int minTileX = source.getMinTileX(); int maxTileX = source.getMaxTileX(); int minTileY = source.getMinTileY(); int maxTileY = source.getMaxTileY(); int xStart = source.getMinX(); int yStart = source.getMinY(); histogram = new HistogramHash(histogramSize); while(true) { histogram.init(); int oldbits = bits; mask = (255 << 8 - bits) & 255; mask = mask | (mask << 8) | (mask << 16); for (int y = minTileY; y <= maxTileY; y++) { for (int x = minTileX; x <= maxTileX; x++) { // Determine the required region of this tile. // (Note that getTileRect() instersects tile and // image bounds.) Rectangle tileRect = source.getTileRect(x, y); // Process if and only if within ROI bounds. if (roi.intersects(tileRect)) { // If checking for skipped tiles determine // whether this tile is "hit". if (checkForSkippedTiles && tileRect.x >= xStart && tileRect.y >= yStart) { // Determine the offset within the tile. int offsetX = (xPeriod - ((tileRect.x - xStart) % xPeriod)) % xPeriod; int offsetY = (yPeriod - ((tileRect.y - yStart) % yPeriod)) % yPeriod; // Continue with next tile if offset // is larger than either tile dimension. if (offsetX >= tileRect.width || offsetY >= tileRect.height) { continue; } } // add the histogram. computeHistogram(source.getData(tileRect)); if (histogram.isFull()) break; } } if (histogram.isFull()) break; } if (oldbits == bits) { counts = histogram.getCounts(); colors = histogram.getColors(); break; } } medianCut(maxColorNum); setProperty("LUT", colorMap); setProperty("JAI.LookupTable", colorMap); } private void computeHistogram(Raster source) { if(!isInitialized) { srcPA = new PixelAccessor(getSourceImage(0)); srcSampleType = srcPA.sampleType == PixelAccessor.TYPE_BIT ? DataBuffer.TYPE_BYTE : srcPA.sampleType; isInitialized = true; } Rectangle srcBounds = getSourceImage(0).getBounds().intersection( source.getBounds()); LinkedList rectList; if (roi == null) { // ROI is the whole Raster rectList = new LinkedList(); rectList.addLast(srcBounds); } else { rectList = roi.getAsRectangleList(srcBounds.x, srcBounds.y, srcBounds.width, srcBounds.height); if (rectList == null) { return; // ROI does not intersect with Raster boundary. } } ListIterator iterator = rectList.listIterator(0); int xStart = source.getMinX(); int yStart = source.getMinY(); while (iterator.hasNext()) { Rectangle rect = srcBounds.intersection((Rectangle)iterator.next()); int tx = rect.x; int ty = rect.y; // Find the actual ROI based on start and period. rect.x = startPosition(tx, xStart, xPeriod); rect.y = startPosition(ty, yStart, yPeriod); rect.width = tx + rect.width - rect.x; rect.height = ty + rect.height - rect.y; if (rect.isEmpty()) { continue; // no pixel to count in this rectangle } UnpackedImageData uid = srcPA.getPixels(source, rect, srcSampleType, false); switch (uid.type) { case DataBuffer.TYPE_BYTE: computeHistogramByte(uid); break; } } } private void computeHistogramByte(UnpackedImageData uid) { Rectangle rect = uid.rect; byte[][] data = uid.getByteData(); int lineStride = uid.lineStride; int pixelStride = uid.pixelStride; byte[] rBand = data[0]; byte[] gBand = data[1]; byte[] bBand = data[2]; int lineInc = lineStride * yPeriod; int pixelInc = pixelStride * xPeriod; int lastLine = rect.height * lineStride; for (int lo = 0; lo < lastLine; lo += lineInc) { int lastPixel = lo + rect.width * pixelStride; for (int po = lo; po < lastPixel; po += pixelInc) { int p = ((rBand[po + uid.bandOffsets[0]] & 0xff)<<16) | ((gBand[po + uid.bandOffsets[1]] & 0xff) <<8) | (bBand[po + uid.bandOffsets[2]] & 0xff); if (!histogram.insert(p & mask)) { bits--; return; } } } } /** Applies the Heckbert's median-cut algorithm to partition the color * space into maxcubes cubes. The centroids * of each cube are are used to create a color table. */ public void medianCut(int expectedColorNum) { int k; int num, width; Cube cubeA, cubeB; // Creates the first color cube partition = new Cube[expectedColorNum]; int numCubes = 0; Cube cube = new Cube(); int numColors = 0; for (int i=0; i < histogramSize; i++) { if (counts[i] != 0) { numColors++; cube.count = cube.count + counts[i]; } } cube.lower = 0; cube.upper = numColors-1; cube.level = 0; shrinkCube(cube); partition[numCubes++] = cube; //Partition the cubes until the expected number of cubes are reached, or // cannot further partition while (numCubes < expectedColorNum) { // Search the list of cubes for next cube to split, the lowest level cube int level = 255; int splitableCube = -1; for (k=0; k < numCubes; k++) { if (partition[k].lower != partition[k].upper && partition[k].level < level) { level = partition[k].level; splitableCube = k; } } // no more cubes to split if (splitableCube == -1) break; // Find longest dimension of this cube: 0 - red, 1 - green, 2 - blue cube = partition[splitableCube]; level = cube.level; // Weigted with luminosities int lr = 77 * (cube.rmax - cube.rmin); int lg = 150 * (cube.gmax - cube.gmin); int lb = 29 * (cube.bmax - cube.bmin); int longDimMask = 0; if (lr >= lg && lr >= lb) longDimMask = 0xFF0000; if (lg >= lr && lg >= lb) longDimMask = 0xFF00; if (lb >= lr && lb >= lg) longDimMask = 0xFF; // Sort along "longdim" quickSort(colors, cube.lower, cube.upper, longDimMask); // Find median int count = 0; int median = cube.lower; for (; median <= cube.upper - 1; median++) { if (count >= cube.count/2) break; count = count + counts[median]; } // Now split "cube" at the median and add the two new // cubes to the list of cubes. cubeA = new Cube(); cubeA.lower = cube.lower; cubeA.upper = median-1; cubeA.count = count; cubeA.level = cube.level + 1; shrinkCube(cubeA); partition[splitableCube] = cubeA; // add in old slot cubeB = new Cube(); cubeB.lower = median; cubeB.upper = cube.upper; cubeB.count = cube.count - count; cubeB.level = cube.level + 1; shrinkCube(cubeB); partition[numCubes++] = cubeB; // add in new slot */ } // creates the lookup table and the inverse mapping createLUT(numCubes); } /** Shrinks the provided Cube to a smallest contains * the same colors defined in the histogram. */ private void shrinkCube(Cube cube) { int rmin = 255; int rmax = 0; int gmin = 255; int gmax = 0; int bmin = 255; int bmax = 0; for (int i=cube.lower; i<=cube.upper; i++) { int color = colors[i]; int r = color >> 16; int g = (color >> 8) & 255; int b = color & 255; if (r > rmax) rmax = r; else if (r < rmin) rmin = r; if (g > gmax) gmax = g; else if (g < gmin) gmin = g; if (b > bmax) bmax = b; else if (b < bmin) bmin = b; } cube.rmin = rmin; cube.rmax = rmax; cube.gmin = gmin; cube.gmax = gmax; cube.bmin = bmin; cube.bmax = bmax; } /** Creates the lookup table and computes the inverse mapping. */ private void createLUT(int ncubes) { if (colorMap == null) { colorMap = new LookupTableJAI(new byte[3][ncubes]); } byte[] rLUT = colorMap.getByteData(0); byte[] gLUT = colorMap.getByteData(1); byte[] bLUT = colorMap.getByteData(2); float scale = 255.0f / (mask & 255); for (int k=0; k < ncubes; k++) { Cube cube = partition[k]; float rsum = 0.0f, gsum = 0.0f, bsum = 0.0f; int r, g, b; for (int i=cube.lower; i<=cube.upper; i++) { int color = colors[i]; r = color >> 16; rsum += (float)r*(float)counts[i]; g = (color >> 8) & 255; gsum += (float)g*(float)counts[i]; b = color & 255; bsum += (float)b*(float)counts[i]; } // Update the color map rLUT[k] = (byte)(rsum/(float)cube.count * scale); gLUT[k] = (byte)(gsum/(float)cube.count * scale); bLUT[k] = (byte)(bsum/(float)cube.count * scale); } } void quickSort(int a[], int lo0, int hi0, int longDimMask) { // Based on the QuickSort method by James Gosling from Sun's SortDemo applet int lo = lo0; int hi = hi0; int mid, t; if ( hi0 > lo0) { mid = a[ ( lo0 + hi0 ) / 2 ] & longDimMask; while( lo <= hi ) { while( ( lo < hi0 ) && ( (a[lo] & longDimMask) < mid ) ) ++lo; while( ( hi > lo0 ) && ( (a[hi] & longDimMask) > mid ) ) --hi; if( lo <= hi ) { t = a[lo]; a[lo] = a[hi]; a[hi] = t; t = counts[lo]; counts[lo] = counts[hi]; counts[hi] = t; ++lo; --hi; } } if( lo0 < hi ) quickSort( a, lo0, hi, longDimMask); if( lo < hi0 ) quickSort( a, lo, hi0, longDimMask); } } } class Cube { // structure for a cube in color space int lower; // one corner's index in histogram int upper; // another corner's index in histogram int count; // cube's histogram count int level; // cube's level int rmin, rmax; int gmin, gmax; int bmin, bmax; Cube() { count = 0; } } /** A hash table for the color histogram. This is based on the first * hashtable algorithm I learnt. */ class HistogramHash { int capacity; int[] colors; int[] counts; int size; int hashsize; boolean packed = false; int[] newColors; int[] newCounts; public HistogramHash(int capacity) { this.capacity = capacity; this.hashsize = capacity * 4 / 3; this.colors = new int[hashsize]; this.counts = new int[hashsize]; } void init() { this.size = 0; this.packed = false; for (int i = 0; i < hashsize; i++) { colors[i] = -1; counts[i] = 0; } } boolean insert(int node) { int hashPos = hashCode(node); if (colors[hashPos] == -1) { colors[hashPos] = node; counts[hashPos]++; size++; return size <= capacity; } else if (colors[hashPos] == node) { counts[hashPos]++; return size <= capacity; } else { for (int next = hashPos + 1; next != hashPos; next++) { next %= hashsize; if (colors[next] == -1) { colors[next] = node; counts[next]++; size++; return size <= capacity; } else if (colors[next] == node) { counts[next]++; return size <= capacity; } } } return size <= capacity; } boolean isFull() { return size > capacity; } void put(int node, int value) { int hashPos = hashCode(node); if (colors[hashPos] == -1) { colors[hashPos] = node; counts[hashPos] = value; size++; return; } else if (colors[hashPos] == node) { counts[hashPos] = value; return; } else { for (int next = hashPos + 1; next != hashPos; next++) { next %= hashsize; if (colors[next] == -1) { colors[next] = node; counts[next] = value; size++; return; } else if (colors[next] == node) { counts[next] = value; return; } } } return; } int get(int node) { int hashPos = hashCode(node); if (colors[hashPos] == node) { return counts[hashPos]; } else { for (int next = hashPos + 1; next != hashPos; next++) { next %= hashsize; if (colors[next] == node) { return counts[next]; } } } return -1; } int[] getCounts() { if (!packed) pack(); return newCounts; } int[] getColors() { if (!packed) pack(); return newColors; } void pack() { newColors = new int[capacity]; newCounts = new int[capacity]; for (int i = 0, j = 0; i < hashsize; i++) { if (colors[i] != -1) { newColors[j] = colors[i]; newCounts[j] = counts[i]; j++; } } packed = true; } int hashCode(int value) { return ((value >> 16) * 33023 + ((value >> 8) & 255) * 30013 + (value & 255) * 27011) % hashsize ; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/GIFRIF.java0000644000175000017500000000266310203035544025050 0ustar mathieumathieu/* * $RCSfile: GIFRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:27 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.NullOpImage; import javax.media.jai.OpImage; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.ImageDecoder; import com.sun.media.jai.codec.SeekableStream; /** * A RIF supporting the "GIF" operation in the rendered * layer. * * @since EA2 * @see javax.media.jai.operator.GIFDescriptor * */ public class GIFRIF implements RenderedImageFactory { /** Constructor. */ public GIFRIF() {} /** * Creates a RenderedImage representing the contents * of a GIF-encoded image. Any layout information is ignored. * * @param paramBlock A ParameterBlock containing the GIF * SeekableStream to read. * @param renderHints An instance of RenderingHints, * or null. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { return CodecRIFUtil.create("gif", paramBlock, renderHints); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/SubsampleAverageCRIF.java0000644000175000017500000001021510203035544027764 0ustar mathieumathieu/* * $RCSfile: SubsampleAverageCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:43 $ * $State: Exp $ */package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderContext; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.ImageLayout; import javax.media.jai.CRIFImpl; /** * @see SubsampleAverageOpImage */ public class SubsampleAverageCRIF extends CRIFImpl { /** Constructor. */ public SubsampleAverageCRIF() { super("SubsampleAverage"); } /** * Creates a new instance of SubsampleAverageOpImage in the rendered layer. * This method satisfies the implementation of RIF. * * @param paramBlock The source image, the X and Y scale factor, * and the interpolation method for resampling. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); RenderedImage source = paramBlock.getRenderedSource(0); double scaleX = paramBlock.getDoubleParameter(0); double scaleY = paramBlock.getDoubleParameter(1); // Check and see if we are scaling by 1.0 in both x and y and no // translations. If so return the source directly. if (scaleX == 1.0 && scaleY == 1.0) { return source; } return new SubsampleAverageOpImage(source, layout, renderHints, scaleX, scaleY); } /** * Creates a new instance of SubsampleAverageOpImage * in the renderable layer. This method satisfies the * implementation of CRIF. */ public RenderedImage create(RenderContext renderContext, ParameterBlock paramBlock) { return paramBlock.getRenderedSource(0); } /** * Maps the output RenderContext into the RenderContext for the ith * source. * This method satisfies the implementation of CRIF. * * @param i The index of the source image. * @param renderContext The renderContext being applied to the operation. * @param paramBlock The ParameterBlock containing the sources * and the translation factors. * @param image The RenderableImageOp from which this method * was called. */ public RenderContext mapRenderContext(int i, RenderContext renderContext, ParameterBlock paramBlock, RenderableImage image) { double scaleX = paramBlock.getDoubleParameter(0); double scaleY = paramBlock.getDoubleParameter(1); AffineTransform scale = new AffineTransform(scaleX, 0.0, 0.0, scaleY, 0.0, 0.0); RenderContext RC = (RenderContext)renderContext.clone(); AffineTransform usr2dev = RC.getTransform(); usr2dev.concatenate(scale); RC.setTransform(usr2dev); return RC; } /** * Gets the bounding box for the output of ScaleOpImage. * This method satisfies the implementation of CRIF. */ public Rectangle2D getBounds2D(ParameterBlock paramBlock) { RenderableImage source = paramBlock.getRenderableSource(0); double scaleX = paramBlock.getDoubleParameter(0); double scaleY = paramBlock.getDoubleParameter(1); // Get the source dimensions float x0 = (float)source.getMinX(); float y0 = (float)source.getMinY() ; float w = (float)source.getWidth(); float h = (float)source.getHeight(); // Forward map the source using x0, y0, w and h float d_x0 = (float)(x0 * scaleX); float d_y0 = (float)(y0 * scaleY); float d_w = (float)(w * scaleX); float d_h = (float)(h * scaleY); return new Rectangle2D.Float(d_x0, d_y0, d_w, d_h); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ErrorDiffusionOpImage.java0000644000175000017500000010011510336667010030301 0ustar mathieumathieu/* * $RCSfile: ErrorDiffusionOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-11-16 17:36:08 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.IndexColorModel; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.util.Map; import javax.media.jai.AreaOpImage; import javax.media.jai.ColorCube; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; import javax.media.jai.LookupTableJAI; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.RasterFactory; import javax.media.jai.RasterFormatTag; import javax.media.jai.UntiledOpImage; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.util.JDKWorkarounds; /** * An OpImage implementing the error diffusion operation as * described in javax.media.jai.operator.ErrorDiffusionDescriptor. * *

This OpImage performs dithering of its source image into * a single band image using a specified color map and error filter. For each * pixel in the source image the nearest entry in the color map is found and * the index of this entry is assigned to the OpImage at that * location. The color quantization error is calculated by mapping the index * back through the color map. The error in each band is then "diffused" to * other neighboring pixels in the source image according to the specified * error filter. * * @see javax.media.jai.ColorCube * @see javax.media.jai.KernelJAI * @see javax.media.jai.LookupTableJAI * * @since EA2 * */ final class ErrorDiffusionOpImage extends UntiledOpImage { /** * Smallest float value which when added to unity will yield something * other than unity. */ private static final float FLOAT_EPSILON = 1.192092896E-07F; /** * Variables used in the optimized case of 3-band byte to 1-band byte * with a ColorCube color map and a Floyd-Steinberg kernel. */ private static final int NBANDS = 3; private static final int NGRAYS = 256; private static final int OVERSHOOT = 256; private static final int UNDERSHOOT = 256; private static final int TOTALGRAYS = (NGRAYS + UNDERSHOOT + OVERSHOOT); private static final int ERR_SHIFT = 8; /** * The color map which maps the ErrorDiffusionOpImage to * its source. */ protected LookupTableJAI colorMap; /** * The kernel associated with the selected error filter. */ protected KernelJAI errorKernel; /** * The number of bands in the source image. */ private int numBandsSource; /** * Flag indicating whether this is an optimized case. */ private boolean isOptimizedCase = false; /** * Minimum valid pixel value */ private float minPixelValue; /** * Maximum valid pixel value */ private float maxPixelValue; /** * Determines whether a kernel is the Floyd-Steinberg kernel. * * @param kernel The KernelJAI to examine. * @return Whether the kernel argument is the Floyd-Steinberg kernel. */ private static boolean isFloydSteinbergKernel(KernelJAI kernel) { int ky = kernel.getYOrigin(); return (kernel.getWidth() == 3 && kernel.getXOrigin() == 1 && kernel.getHeight() - ky == 2 && Math.abs(kernel.getElement(2, ky) - 7.0F/16.0F) < FLOAT_EPSILON && Math.abs(kernel.getElement(0, ky+1) - 3.0F/16.0F) < FLOAT_EPSILON && Math.abs(kernel.getElement(1, ky+1) - 5.0F/16.0F) < FLOAT_EPSILON && Math.abs(kernel.getElement(2, ky+1) - 1.0F/16.0F) < FLOAT_EPSILON); } /** * Create the dither table for the 3-band to 1-band byte optimized case. * * @param colorCube The color cube to be used in dithering. * @return The dither table of the optimized algorithm. */ private static int[] initFloydSteinberg24To8(ColorCube colorCube) { // Allocate memory for the dither table. int[] ditherTable = new int[NBANDS*TOTALGRAYS]; float[] thresh = new float[NGRAYS]; // // Get the colorcube parameters // int[] multipliers = colorCube.getMultipliers(); int[] dimsLessOne = colorCube.getDimsLessOne(); int offset = colorCube.getAdjustedOffset(); // // Construct tables for each band // for (int band = 0; band < NBANDS; band++) { int pTab = band*TOTALGRAYS; // // Calculate the binwidth for this band, i.e. the gray level step // from one quantization level to the next. Do this in scaled // integer to maintain precision. // float binWidth = 255.0F / dimsLessOne[band]; // // Pre-calculate the thresholds, so we don't have to do // it in the inner loops. The threshold is always the // midpoint of each bin, since, in error diffusion, the dithering // is done by the error distribution process, not by varying // the dither threshold as in ordered dither. // for (int i = 0; i < dimsLessOne[band]; i++) { thresh[i] = (i + 0.5F) * binWidth; } thresh[dimsLessOne[band]] = 256.0F; // // Populate the range below gray level zero with the same entry // as that for zero. The error distribution can cause undershoots // of as much as 255. // int tableInc = 1 << ERR_SHIFT; int tableValue = (-UNDERSHOOT) << ERR_SHIFT; for (int gray = -UNDERSHOOT; gray < 0; gray++) { ditherTable[pTab++] = tableValue; tableValue += tableInc; } // // Populate the main range of 0...255. // int indexContrib = 0; float frepValue = 0.0F; int repValue; int binNum = 0; float threshold = thresh[0]; int gray = 0; while (gray < 256) { // // Populate all the table values up to the next threshold. // Since the only thing which changes is the error, // and it changes by one scaled gray level, we can // just add the increment at each iteration. // int tableBase = indexContrib; repValue = (int)(frepValue + 0.5F); while ((float)gray < threshold) { ditherTable[pTab++] = ((gray - repValue) << ERR_SHIFT) + tableBase; gray++; } // // Once the gray level crosses a threshold, // move to the next bin threshold. Also update // the color contribution index step and the // representative value, needed to compute the error. // threshold = thresh[++binNum]; indexContrib += multipliers[band]; frepValue += binWidth; } // // Populate the range above gray level 255 with the same entry // as that for 255. As in the under-range case, the error // distribution can cause overshoots as high as 255 over max. // indexContrib -= multipliers[band]; repValue = 255; tableValue = ((256 - repValue) << ERR_SHIFT) | indexContrib; for (gray = 256; gray < (256 + OVERSHOOT); gray++) { ditherTable[pTab++] = tableValue; tableValue += tableInc; } } // End band loop // // Add in the colormap offset value to the index contribution // for the first band. This eliminates the need to add it in // when we do the error diffusion. // int pTab = 0; for (int count = TOTALGRAYS; count != 0; count--) { ditherTable[pTab] += offset; pTab++; } return ditherTable; } /** * Force the destination image to be single-banded. */ private static ImageLayout layoutHelper(ImageLayout layout, RenderedImage source, LookupTableJAI colorMap) { // Create or clone the layout. ImageLayout il = layout == null ? new ImageLayout() : (ImageLayout)layout.clone(); // Force the destination and source origins and dimensions to coincide. il.setMinX(source.getMinX()); il.setMinY(source.getMinY()); il.setWidth(source.getWidth()); il.setHeight(source.getHeight()); // Get the SampleModel. SampleModel sm = il.getSampleModel(source); // Ensure an appropriate SampleModel. if(colorMap.getNumBands() == 1 && colorMap.getNumEntries() == 2 && !ImageUtil.isBinary(il.getSampleModel(source))) { sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, il.getTileWidth(source), il.getTileHeight(source), 1); il.setSampleModel(sm); } // Make sure that this OpImage is single-banded. if (sm.getNumBands() != 1) { sm = RasterFactory.createComponentSampleModel(sm, sm.getTransferType(), sm.getWidth(), sm.getHeight(), 1); il.setSampleModel(sm); // Clear the ColorModel mask if needed. ColorModel cm = il.getColorModel(null); if(cm != null && !JDKWorkarounds.areCompatibleDataModels(sm, cm)) { // Clear the mask bit if incompatible. il.unsetValid(ImageLayout.COLOR_MODEL_MASK); } } // Determine whether a larger bit depth is needed. int numColorMapBands = colorMap.getNumBands(); int maxIndex = 0; for(int i = 0; i < numColorMapBands; i++) { maxIndex = Math.max(colorMap.getOffset(i) + colorMap.getNumEntries() - 1, maxIndex); } // Create a deeper SampleModel if needed. if((maxIndex > 255 && sm.getDataType() == DataBuffer.TYPE_BYTE) || (maxIndex > 65535 && sm.getDataType() != DataBuffer.TYPE_INT)) { int dataType = maxIndex > 65535 ? DataBuffer.TYPE_INT : DataBuffer.TYPE_USHORT; sm = RasterFactory.createComponentSampleModel(sm, dataType, sm.getWidth(), sm.getHeight(), 1); il.setSampleModel(sm); // Clear the ColorModel mask if needed. ColorModel cm = il.getColorModel(null); if(cm != null && !JDKWorkarounds.areCompatibleDataModels(sm, cm)) { // Clear the mask bit if incompatible. il.unsetValid(ImageLayout.COLOR_MODEL_MASK); } } // Set an IndexColorModel on the image if: // a. none is provided in the layout; // b. source and colormap have byte data type; // c. the colormap has 3 bands; // d. destination has byte or ushort data type. if((layout == null || !il.isValid(ImageLayout.COLOR_MODEL_MASK)) && source.getSampleModel().getDataType() == DataBuffer.TYPE_BYTE && (sm.getDataType() == DataBuffer.TYPE_BYTE || sm.getDataType() == DataBuffer.TYPE_USHORT) && colorMap.getDataType() == DataBuffer.TYPE_BYTE && colorMap.getNumBands() == 3) { ColorModel cm = source.getColorModel(); if(cm == null || (cm != null && cm.getColorSpace().isCS_sRGB())) { int size = colorMap.getNumEntries(); byte[][] cmap = new byte[3][maxIndex+1]; for(int i = 0; i < 3; i++) { byte[] band = cmap[i]; byte[] data = colorMap.getByteData(i); int offset = colorMap.getOffset(i); int end = offset + size; for(int j = offset; j < end; j++) { band[j] = data[j - offset]; } } int numBits = sm.getDataType() == DataBuffer.TYPE_BYTE ? 8 : 16; il.setColorModel(new IndexColorModel(numBits, maxIndex + 1, cmap[0], cmap[1], cmap[2])); } } return il; } /** * Constructs an ErrorDiffusionOpImage object. * *

The image dimensions are derived from the source image. The tile * grid layout, SampleModel, and ColorModel may optionally be specified * by an ImageLayout object. The calculation assumes that the entire * color quantization error is distributed to the right and below the * current pixel and the filter kernel values are handled appropriately. * * @param source A RenderedImage. * @param layout An ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param colorMap The color map to use which must have a number of bands * equal to the number of bands in the source image. The offset of this * LookupTableJAI must be the same for all bands. * @param errorKernel The error filter kernel. This must have values * between 0.0 and 1.0. Only the entries to the right of and on the same * row as the key entry, and those entries below of the row of the key * entry are used; all other values are ignored. The values used must sum * to 1.0. Note that if a 1-by-1 error filter kernel is supplied, the value * of the unique kernel element is irrelevant and the output of the * algorithm will simply be the index in the supplied color map of the * nearest matching color to the source pixel at the same position. */ public ErrorDiffusionOpImage(RenderedImage source, Map config, ImageLayout layout, LookupTableJAI colorMap, KernelJAI errorKernel) { super(source, config, layoutHelper(layout, source, colorMap)); // Get the source sample model. SampleModel srcSampleModel = source.getSampleModel(); // Cache the number of bands in the source. numBandsSource = srcSampleModel.getNumBands(); // Set a reference to the LookupTableJAI. this.colorMap = colorMap; // Set a reference to the KernelJAI. this.errorKernel = errorKernel; // Determine whether this is an (read "the") optimized case. isOptimizedCase = (sampleModel.getTransferType() == DataBuffer.TYPE_BYTE && srcSampleModel.getTransferType() == DataBuffer.TYPE_BYTE && numBandsSource == 3 && colorMap instanceof ColorCube && isFloydSteinbergKernel(errorKernel)); // Determine minumum and maximum valid pixel values switch (colorMap.getDataType()) { case DataBuffer.TYPE_BYTE: // Treat byte types as unsigned bytes minPixelValue = 0; maxPixelValue = -Byte.MIN_VALUE + Byte.MAX_VALUE; break; case DataBuffer.TYPE_SHORT: minPixelValue = Short.MIN_VALUE; maxPixelValue = Short.MAX_VALUE; break; case DataBuffer.TYPE_USHORT: minPixelValue = 0; maxPixelValue = -Short.MIN_VALUE + Short.MAX_VALUE; break; case DataBuffer.TYPE_INT: minPixelValue = Integer.MIN_VALUE; maxPixelValue = Integer.MAX_VALUE; break; case DataBuffer.TYPE_FLOAT: minPixelValue = 0; maxPixelValue = Float.MAX_VALUE; break; case DataBuffer.TYPE_DOUBLE: minPixelValue = 0; maxPixelValue = Float.MAX_VALUE; break; default: throw new RuntimeException( JaiI18N.getString("ErrorDiffusionOpImage0")); } } /** * Performs error diffusion on a specified rectangle. The sources are * cobbled. As error diffusion must be calculated on a line-by-line basis * starting at the upper left corner of the image, all image lines through * and including the last line of the tile containing the requested * Rectangle are calculated. * * @param sources The source image Raster. * @param dest A WritableRaster tile containing the area to be computed. * @param destRect The rectangle within dest to be processed. */ protected void computeImage(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; if (isOptimizedCase) { computeImageOptimized(source, dest, destRect); } else { computeImageDefault(source, dest, destRect); } } protected void computeImageDefault(Raster source, WritableRaster dest, Rectangle destRect) { // Set X-coordinate range. int startX = minX; int endX = startX + width - 1; // Set Y-coordinate range. int startY = minY; int endY = startY + height - 1; // Set the number of lines in the calculation buffer. int numLinesBuffer = errorKernel.getHeight() - errorKernel.getYOrigin(); // Allocate memory for the calculation buffer. float[][] bufMem = new float[numLinesBuffer][width*numBandsSource]; // Allocate memory for the buffer index array. int[] bufIdx = new int[numLinesBuffer]; // Initialize the buffer index array and the rolling buffer. for(int idx = 0; idx < numLinesBuffer; idx++) { bufIdx[idx] = idx; source.getPixels(startX, startY + idx, width, 1, bufMem[idx]); } // Set variable to indicate index of last rolling buffer line. int lastLineBuffer = numLinesBuffer - 1; // Initialize some kernel-dependent constants. int kernelWidth = errorKernel.getWidth(); float[] kernelData = errorKernel.getKernelData(); int diffuseRight = kernelWidth - errorKernel.getXOrigin() - 1; int diffuseBelow = errorKernel.getHeight() - errorKernel.getYOrigin() - 1; int kernelOffsetRight = errorKernel.getYOrigin()*kernelWidth + errorKernel.getXOrigin() + 1; int kernelOffsetBelow = (errorKernel.getYOrigin() + 1)*kernelWidth; // Set up some arrays for looping. float[] currentPixel = new float[numBandsSource]; int offset = colorMap.getOffset(); float[] qError = new float[numBandsSource]; // Loop over lines. int[] dstData = new int[width]; for (int y = startY; y <= endY; y++) { int currentIndex = bufIdx[0]; float[] currentLine = bufMem[currentIndex]; // Loop over pixels. int dstOffset = 0; for (int x = startX, z = 0; x <= endX; x++) { // Copy all samples of the current pixel. for (int b = 0; b < numBandsSource; b++) { currentPixel[b] = currentLine[z++]; // Clamp the current sample to the valid range if (currentPixel[b] < minPixelValue || currentPixel[b] > maxPixelValue) { currentPixel[b] = java.lang.Math.max(currentPixel[b], minPixelValue); currentPixel[b] = java.lang.Math.min(currentPixel[b], maxPixelValue); } } // Find the index of the nearest color in the map. int nearestIndex = colorMap.findNearestEntry(currentPixel); // Save the index in the output data buffer. dstData[dstOffset++] = nearestIndex; // Calculate the error between the nearest and actual colors. boolean isQuantizationError = false; for (int b = 0; b < numBandsSource; b++) { qError[b] = currentPixel[b] - colorMap.lookupFloat(b, nearestIndex); if (qError[b] != 0.0F) { isQuantizationError = true; } } // If there was error in at least one band, distribute it. if (isQuantizationError) { // Distribute error to the right of key entry. int rightCount = Math.min(diffuseRight, endX - x); int kernelOffset = kernelOffsetRight; int sampleOffset = z; for (int u = 1; u <= rightCount; u++) { for (int b = 0; b < numBandsSource; b++) { currentLine[sampleOffset++] += qError[b]*kernelData[kernelOffset]; } kernelOffset++; } // Distribute error below key entry. int offsetLeft = Math.min(x - startX, diffuseRight); int count = Math.min(x + diffuseRight, endX) - Math.max(x - diffuseRight, startX) + 1; for (int v = 1; v <= diffuseBelow; v++) { float[] line = bufMem[bufIdx[v]]; kernelOffset = kernelOffsetBelow; sampleOffset = z - (offsetLeft + 1)*numBandsSource; for (int u = 1; u <= count; u++) { for (int b = 0; b < numBandsSource; b++) { line[sampleOffset++] += qError[b]*kernelData[kernelOffset]; } kernelOffset++; } } } } // // Save data for the current destination line. // dest.setSamples(startX, y, destRect.width, 1, 0, dstData); // Rotate the buffer indexes. for (int k = 0; k < lastLineBuffer; k++) { bufIdx[k] = bufIdx[k+1]; } bufIdx[lastLineBuffer] = currentIndex; // If available, load next image line into the last buffer line. if (y + numLinesBuffer < getMaxY()) { source.getPixels(startX, y + numLinesBuffer, width, 1, bufMem[bufIdx[lastLineBuffer]]); } } } protected void computeImageOptimized(Raster source, WritableRaster dest, Rectangle destRect) { // Set X-coordinate range. int startX = minX; int endX = startX + width - 1; // Set Y-coordinate range. int startY = minY; int endY = startY + height - 1; // Initialize the dither table. int[] ditherTable = initFloydSteinberg24To8((ColorCube)colorMap); // Initialize the padded source width. int sourceWidthPadded = source.getWidth() + 2; // Allocate memory for the error buffer. int[] errBuf = new int[sourceWidthPadded*NBANDS]; // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); RasterAccessor srcAccessor = new RasterAccessor(source, new Rectangle(startX, startY, source.getWidth(), source.getHeight()), formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor dstAccessor = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); // Set pixel and line strides. int srcPixelStride = srcAccessor.getPixelStride(); int srcScanlineStride = srcAccessor.getScanlineStride(); int dstPixelStride = dstAccessor.getPixelStride(); int dstScanlineStride = dstAccessor.getScanlineStride(); // Set data arrays. byte[] srcData0 = srcAccessor.getByteDataArray(0); byte[] srcData1 = srcAccessor.getByteDataArray(1); byte[] srcData2 = srcAccessor.getByteDataArray(2); byte[] dstData = dstAccessor.getByteDataArray(0); // Initialize line offset in each band. int srcLine0 = srcAccessor.getBandOffset(0); int srcLine1 = srcAccessor.getBandOffset(1); int srcLine2 = srcAccessor.getBandOffset(2); int dstLine = dstAccessor.getBandOffset(0); // // For each line, calculate and distribute the error into // a 3 line error buffer (one line for each band). // Also accumulate the contributions of the 3 bands // into the same line of the temporary output buffer. // // The error buffer starts out with all zeroes as the // amount of error to propagate forward. // for (int y = startY; y <= endY; y++) { // Initialize pixel offset in each line in each band. int srcPixel0 = srcLine0; int srcPixel1 = srcLine1; int srcPixel2 = srcLine2; int dstPixel = dstLine; // // Determine the error and index contribution for // the each band. Keep the transitory errors // (errA, errC and errD) in local variables // (hopefully registers). The calculated value // of errB gets put into the error buffer, to be used // on the next line. // // This is the logic here. Floyd-Steinberg dithering // distributes errors to four neighboring pixels, // as shown below. X is the pixel being operated on. // // 7/16 of the error goes to pixel A // 3/16 of the error goes to pixel B // 5/16 of the error goes to pixel C // 1/16 of the error goes to pixel D // // X A // B C D // // The error distributed to pixel A is reused immediately // in the calculation of the next pixel on the same line. // The errors distributed to B, C and D will be used on the // following line. As we move from left to right, the // new error distributed to B gets added to the error // at the previous C. Likewise, the new C error gets added // to the previous D error. So only the errors propagating // to position B survive in the saved error buffer. The // only exception is at the line end, where error C must be // saved. The scheme is shown below. // // XA // BCD // BCD // BCD // BCD // // Treat the error buffer as pixel sequential. // This lets us use a single pointer with offsets // for the entries for all three bands. // // // Zero the error holders for all bands // The bands are called Red, Grn and Blu, but are // really just the first, second and third bands. // int errRedA = 0; int errRedC = 0; int errRedD = 0; int errGrnA = 0; int errGrnC = 0; int errGrnD = 0; int errBluA = 0; int errBluC = 0; int errBluD = 0; int pErr = 0; int dstOffset = 0; for (int x = startX; x <= endX; x++) { // // First band (Red) // The color index is initialized here. // Set the table pointer to the "Red" band // int pTab = UNDERSHOOT; int adjVal = ((errRedA + errBuf[pErr+3] + 8) >> 4) + (int)(srcData0[srcPixel0] & 0xff); srcPixel0 += srcPixelStride; int tabval = ditherTable[pTab+adjVal]; int err = tabval >> 8; int err1 = err; int index = (tabval & 0xff); int err2 = err + err; errBuf[pErr] = errRedC + (err += err2); // 3/16 (B) errRedC = errRedD + (err += err2); // 5/16 (C) errRedD = err1; // 1/16 (D) errRedA = (err += err2); // 7/16 (A) // // Second band (Green) // Set the table pointer to the "Green" band // The color index is incremented here. // pTab += TOTALGRAYS; adjVal = ((errGrnA + errBuf[pErr+4] + 8) >> 4) + (int)(srcData1[srcPixel1] & 0xff); srcPixel1 += srcPixelStride; tabval = ditherTable[pTab+adjVal]; err = tabval >> 8; err1 = err; index += (tabval & 0xff); err2 = err + err; errBuf[pErr+1] = errGrnC + (err += err2); errGrnC = errGrnD + (err += err2); errGrnD = err1; errGrnA = (err += err2); pTab += TOTALGRAYS; // // Third band (Blue) // Set the table pointer to the "Blue" band // The color index is incremented here. // adjVal = ((errBluA + errBuf[pErr+5] + 8) >> 4) + (int)(srcData2[srcPixel2] & 0xff); srcPixel2 += srcPixelStride; tabval = ditherTable[pTab+adjVal]; err = tabval >> 8; err1 = err; index += (tabval & 0xff); err2 = err + err; errBuf[pErr+2] = errBluC + (err += err2); errBluC = errBluD + (err += err2); errBluD = err1; errBluA = (err += err2); // Save the result in the output data buffer. dstData[dstPixel] = (byte)(index&0xff); dstPixel += dstPixelStride; pErr += 3; } // End pixel loop // // Save last error in line // int last = 3 * (sourceWidthPadded - 2); errBuf[last] = errRedC; errBuf[last+1] = errGrnC; errBuf[last+2] = errBluC; // Increment offset in each band to next line. srcLine0 += srcScanlineStride; srcLine1 += srcScanlineStride; srcLine2 += srcScanlineStride; dstLine += dstScanlineStride; } // End scanline loop // Make sure that the output data is copied to the destination. dstAccessor.copyDataToRaster(); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/CompositeCRIF.java0000644000175000017500000000750110203035544026504 0ustar mathieumathieu/* * $RCSfile: CompositeCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:18 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import java.awt.image.renderable.RenderContext; import javax.media.jai.CRIFImpl; import javax.media.jai.EnumeratedParameter; import javax.media.jai.ImageLayout; import javax.media.jai.JAI; import java.util.Map; import javax.media.jai.operator.CompositeDescriptor; /** * A CRIF supporting the "Composite" operation in the * rendered and renderable image modes. * * @see javax.media.jai.operator.CompositeDescriptor * @see CompositeOpImage * @see CompositeNoAlphaOpImage * */ public class CompositeCRIF extends CRIFImpl { /** Constructor. */ public CompositeCRIF() { super("composite"); } /** * Creates a new instance of CompositeOpImage * in the rendered layer. This method satisfies the * implementation of RIF. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout from RenderingHints if any. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); RenderedImage source1 = args.getRenderedSource(0); RenderedImage source2 = args.getRenderedSource(1); RenderedImage alpha1 = (RenderedImage)args.getObjectParameter(0); RenderedImage alpha2 = null; if(args.getObjectParameter(1) != null) { alpha2 = (RenderedImage)args.getObjectParameter(1); } boolean premultiplied = ((Boolean)args.getObjectParameter(2)).booleanValue(); EnumeratedParameter destAlpha = (EnumeratedParameter)args.getObjectParameter(3); if (destAlpha.equals(CompositeDescriptor.NO_DESTINATION_ALPHA)) { return new CompositeNoDestAlphaOpImage(source1, source2, hints, layout, alpha1, alpha2, premultiplied); } else { return new CompositeOpImage(source1, source2, hints, layout, alpha1, alpha2, premultiplied, destAlpha.equals(CompositeDescriptor.DESTINATION_ALPHA_FIRST)); } } /** * Creates a RenderedImage from the renderable layer. * * @param renderContext The rendering information associated with * this rendering. * @param paramBlock The parameters used to create the image. * @return A RenderedImage. */ public RenderedImage create(RenderContext renderContext, ParameterBlock paramBlock) { // Get the two renderable alpha images from the parameter block RenderableImage alphaImage1 = (RenderableImage)paramBlock.getObjectParameter(0); RenderableImage alphaImage2 = (RenderableImage)paramBlock.getObjectParameter(1); // Cause the two renderable alpha images to be rendered RenderedImage rAlphaImage1 = alphaImage1.createRendering(renderContext); RenderedImage rAlphaImage2 = alphaImage2.createRendering(renderContext); ParameterBlock newPB = (ParameterBlock)paramBlock.clone(); // Replace the renderable alpha images in the ParameterBlock with // their renderings newPB.set(rAlphaImage1, 0); newPB.set(rAlphaImage2, 1); // Return JAI.create("composite") return JAI.create("composite", newPB, renderContext.getRenderingHints()); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/AndCRIF.java0000644000175000017500000000316610203035544025247 0ustar mathieumathieu/* * $RCSfile: AndCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:14 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderContext; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "And" operation in the * rendered and renderable image layers. * * @since EA2 * @see javax.media.jai.operator.AndDescriptor * @see AndOpImage * */ public class AndCRIF extends CRIFImpl { /** Constructor. */ public AndCRIF() { super("and"); } /** * Creates a new instance of AndOpImage in the * rendered layer. This method satisifies the implementation of RIF. * * @param paramBlock The two source images to be "anded" together. * @param renderHints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new AndOpImage(paramBlock.getRenderedSource(0), paramBlock.getRenderedSource(1), renderHints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/OverlayCRIF.java0000644000175000017500000000271110203035544026161 0ustar mathieumathieu/* * $RCSfile: OverlayCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:39 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "Overlay" operation in the rendered * and renderable image layers. * * @see javax.media.jai.operator.OverlayDescriptor * @see OverlayOpImage * */ public class OverlayCRIF extends CRIFImpl { /** Constructor. */ public OverlayCRIF() { super("overlay"); } /** * Creates a new instance of OverlayOpImage * in the rendered layer. * * @param args The two source images. * @param hints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new OverlayOpImage(args.getRenderedSource(0), args.getRenderedSource(1), renderHints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/IDFTCRIF.java0000644000175000017500000000331710203035544025271 0ustar mathieumathieu/* * $RCSfile: IDFTCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:28 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.CRIFImpl; import javax.media.jai.EnumeratedParameter; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "IDFT" operation in the rendered * image layer. * * @since Beta * @see javax.media.jai.operator.DFTDescriptor * @see javax.media.jai.operator.IDFTDescriptor * */ public class IDFTCRIF extends CRIFImpl { /** Constructor. */ public IDFTCRIF() { super("idft"); } /** * Creates a new instance of an IDFT operator according to the scaling * type. * * @param paramBlock The scaling type. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); RenderedImage source = paramBlock.getRenderedSource(0); EnumeratedParameter scalingType = (EnumeratedParameter)paramBlock.getObjectParameter(0); EnumeratedParameter dataNature = (EnumeratedParameter)paramBlock.getObjectParameter(1); FFT fft = new FFT(false, new Integer(scalingType.getValue()), 2); return new DFTOpImage(source, renderHints, layout, dataNature, fft); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MaxFilterPlusOpImage.java0000644000175000017500000004120310203035544030074 0ustar mathieumathieu/* * $RCSfile: MaxFilterPlusOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:32 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import java.util.Map; import javax.media.jai.operator.MaxFilterDescriptor; import com.sun.media.jai.opimage.MaxFilterOpImage; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class to perform max filtering on a source image. * */ final class MaxFilterPlusOpImage extends MaxFilterOpImage { /** * Creates a MaxFilterPlusOpImage with the given source and * maskSize. The image dimensions are derived from the source * image. The tile grid layout, SampleModel, and ColorModel may * optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param maskSize the mask size. */ public MaxFilterPlusOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, int maskSize) { super(source, extender, config, layout, MaxFilterDescriptor.MAX_MASK_PLUS, maskSize); } protected void byteLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); byte dstDataArrays[][] = dst.getByteDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); byte srcDataArrays[][] = src.getByteDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int maxval, val; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { byte dstData[] = dstDataArrays[k]; byte srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { maxval = Integer.MIN_VALUE; // figure out where the top of the plus starts int imageOffset = srcPixelOffset + srcPixelStride*offset; for (int u = 0; u < wp; u++) { val = (int)(srcData[imageOffset]&0xff); imageOffset += srcScanlineStride; maxval = (val > maxval) ? val : maxval; } // figure out where the left side of plus starts imageOffset = srcPixelOffset + srcScanlineStride*offset; for (int v = 0; v < wp; v++) { val = (int)(srcData[imageOffset]&0xff); imageOffset += srcPixelStride; maxval = (val > maxval) ? val : maxval; } dstData[dstPixelOffset] = (byte)maxval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void shortLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int maxval, val; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { maxval = Integer.MIN_VALUE; // figure out where the top of the plus starts int imageOffset = srcPixelOffset + srcPixelStride*offset; for (int u = 0; u < wp; u++) { val = (int)(srcData[imageOffset]); imageOffset += srcScanlineStride; maxval = (val > maxval) ? val : maxval; } // figure out where the left side of plus starts imageOffset = srcPixelOffset + srcScanlineStride*offset; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += srcPixelStride; maxval = (val > maxval) ? val : maxval; } dstData[dstPixelOffset] = (short)maxval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void ushortLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int maxval, val; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { maxval = Integer.MIN_VALUE; // figure out where the top of the plus starts int imageOffset = srcPixelOffset + srcPixelStride*offset; for (int u = 0; u < wp; u++) { val = (int)(srcData[imageOffset]&0xffff); imageOffset += srcScanlineStride; maxval = (val > maxval) ? val : maxval; } // figure out where the left side of plus starts imageOffset = srcPixelOffset + srcScanlineStride*offset; for (int v = 0; v < wp; v++) { val = (int)(srcData[imageOffset]&0xffff); imageOffset += srcPixelStride; maxval = (val > maxval) ? val : maxval; } dstData[dstPixelOffset] = (short)maxval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void intLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); int dstDataArrays[][] = dst.getIntDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcDataArrays[][] = src.getIntDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int maxval, val; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { int dstData[] = dstDataArrays[k]; int srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { maxval = Integer.MIN_VALUE; // figure out where the top of the plus starts int imageOffset = srcPixelOffset + srcPixelStride*offset; for (int u = 0; u < wp; u++) { val = srcData[imageOffset]; imageOffset += srcScanlineStride; maxval = (val > maxval) ? val : maxval; } // figure out where the left side of plus starts imageOffset = srcPixelOffset + srcScanlineStride*offset; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += srcPixelStride; maxval = (val > maxval) ? val : maxval; } dstData[dstPixelOffset] = maxval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void floatLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); float dstDataArrays[][] = dst.getFloatDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); float srcDataArrays[][] = src.getFloatDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); float maxval, val; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { float dstData[] = dstDataArrays[k]; float srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { maxval = -Float.MAX_VALUE; // figure out where the top of the plus starts int imageOffset = srcPixelOffset + srcPixelStride*offset; for (int u = 0; u < wp; u++) { val = srcData[imageOffset]; imageOffset += srcScanlineStride; maxval = (val > maxval) ? val : maxval; } // figure out where the left side of plus starts imageOffset = srcPixelOffset + srcScanlineStride*offset; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += srcPixelStride; maxval = (val > maxval) ? val : maxval; } dstData[dstPixelOffset] = maxval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void doubleLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); double dstDataArrays[][] = dst.getDoubleDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); double srcDataArrays[][] = src.getDoubleDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); double maxval, val; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { double dstData[] = dstDataArrays[k]; double srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { maxval = -Double.MAX_VALUE; // figure out where the top of the plus starts int imageOffset = srcPixelOffset + srcPixelStride*offset; for (int u = 0; u < wp; u++) { val = srcData[imageOffset]; imageOffset += srcScanlineStride; maxval = (val > maxval) ? val : maxval; } // figure out where the left side of plus starts imageOffset = srcPixelOffset + srcScanlineStride*offset; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += srcPixelStride; maxval = (val > maxval) ? val : maxval; } dstData[dstPixelOffset] = maxval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } // public static OpImage createTestImage(OpImageTester oit) { // return new MaxFilterPlusOpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource()), // 3); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/FPXRIF.java0000644000175000017500000000262010203035544025071 0ustar mathieumathieu/* * $RCSfile: FPXRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:26 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.io.InputStream; import java.io.IOException; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.NullOpImage; import javax.media.jai.OpImage; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.ImageDecoder; import com.sun.media.jai.codec.SeekableStream; /** * A RIF supporting the "FPX" operation in the * rendered image layer. * * @see javax.media.jai.operator.FPXDescriptor */ public class FPXRIF implements RenderedImageFactory { /** Constructor. */ public FPXRIF() {} /** * Creates a RenderedImage representing the contents * of a FlashPIX-encoded image. * * @param paramBlock A ParameterBlock containing the FPX * SeekableStream to read. * @param renderHints Rendering hints. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { return CodecRIFUtil.create("fpx", paramBlock, renderHints); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MeanRIF.java0000644000175000017500000000310210203035544025310 0ustar mathieumathieu/* * $RCSfile: MeanRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:33 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ROI; /** * A RIF supporting the "Mean" operation in the * rendered image layer. * * @since EA2 * @see javax.media.jai.operator.MeanDescriptor * */ public class MeanRIF implements RenderedImageFactory { /** Constructor. */ public MeanRIF() {} /** * Creates a new instance of MeanOpImage * in the rendered layer. Any image layout information in * RenderingHints is ignored. * This method satisfies the implementation of RIF. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { RenderedImage src = paramBlock.getRenderedSource(0); int xStart = src.getMinX(); // default values int yStart = src.getMinY(); int maxWidth = src.getWidth(); int maxHeight = src.getHeight(); return new MeanOpImage(src, (ROI)paramBlock.getObjectParameter(0), xStart, yStart, paramBlock.getIntParameter(1), paramBlock.getIntParameter(2)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/SubsampleBinaryToGrayCRIF.java0000644000175000017500000001412110203035544030764 0ustar mathieumathieu/* * $RCSfile: SubsampleBinaryToGrayCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:44 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.DataBuffer; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.renderable.RenderedImageFactory; import java.awt.image.renderable.RenderContext; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.CRIFImpl; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import java.util.Map; /** * @see SubsampleBinaryToGrayOpImage */ public class SubsampleBinaryToGrayCRIF extends CRIFImpl { /** Constructor. */ public SubsampleBinaryToGrayCRIF() { super("subsamplebinarytogray"); } /** * Creates a new instance of SubsampleBinaryToGrayOpImage in the rendered layer. * This method satisfies the implementation of RIF. * * @param paramBlock The source image, the X and Y scale factor */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); // Get BorderExtender from renderHints if any. BorderExtender extender = RIFUtil.getBorderExtenderHint(renderHints); RenderedImage source = paramBlock.getRenderedSource(0); float xScale = paramBlock.getFloatParameter(0); float yScale = paramBlock.getFloatParameter(1); // Check and see if we are scaling by 1.0 in both x and y and no // translations. If so call the copy operation. if (xScale == 1.0F && yScale == 1.0F){ return new CopyOpImage(source, renderHints, layout); } // The image is assumed to have // a MultiPixelPackedSampleModel and a byte, ushort, // or int DataBuffer we can access the pixel data directly. // Note that there is a potential loophole that has not been // resolved by Java2D as to whether the underlying DataBuffer // must be of one of the standard types. Here we make the // assumption that it will be -- we can't check without // forcing an actual tile to be computed. // SampleModel sm = source.getSampleModel(); if ((sm instanceof MultiPixelPackedSampleModel) && (sm.getSampleSize(0) == 1) && (sm.getDataType() == DataBuffer.TYPE_BYTE || sm.getDataType() == DataBuffer.TYPE_USHORT || sm.getDataType() == DataBuffer.TYPE_INT)) { int srcWidth = source.getWidth(); int srcHeight= source.getHeight(); float floatTol = .1F * Math.min(xScale/(srcWidth*xScale+1.0F), yScale/(srcHeight*yScale+1.0F)); int invScale = (int) Math.round(1.0F/xScale); if(Math.abs((float)invScale - 1.0F/xScale) < floatTol && Math.abs((float)invScale - 1.0F/yScale) < floatTol){ switch (invScale){ case 2: return new SubsampleBinaryToGray2x2OpImage(source, layout, renderHints); case 4: return new SubsampleBinaryToGray4x4OpImage(source, layout, renderHints); default: break; } } return new SubsampleBinaryToGrayOpImage(source, layout, renderHints, xScale, yScale); }else{ throw new IllegalArgumentException(JaiI18N.getString("SubsampleBinaryToGray3")); } } /** * Creates a new instance of AffineOpImage * in the renderable layer. This method satisfies the * implementation of CRIF. */ public RenderedImage create(RenderContext renderContext, ParameterBlock paramBlock) { return paramBlock.getRenderedSource(0); } /** * Maps the output RenderContext into the RenderContext for the ith * source. * This method satisfies the implementation of CRIF. * * @param i The index of the source image. * @param renderContext The renderContext being applied to the operation. * @param paramBlock The ParameterBlock containing the sources * and the translation factors. * @param image The RenderableImageOp from which this method * was called. */ public RenderContext mapRenderContext(int i, RenderContext renderContext, ParameterBlock paramBlock, RenderableImage image) { float scale_x = paramBlock.getFloatParameter(0); float scale_y = paramBlock.getFloatParameter(1); AffineTransform scale = new AffineTransform(scale_x, 0.0, 0.0, scale_y, 0.0, 0.0); RenderContext RC = (RenderContext)renderContext.clone(); AffineTransform usr2dev = RC.getTransform(); usr2dev.concatenate(scale); RC.setTransform(usr2dev); return RC; } /** * Gets the bounding box for the output of SubsampleBinaryToGrayOpImage. * This method satisfies the implementation of CRIF. */ public Rectangle2D getBounds2D(ParameterBlock paramBlock) { RenderableImage source = paramBlock.getRenderableSource(0); float scale_x = paramBlock.getFloatParameter(0); float scale_y = paramBlock.getFloatParameter(1); // Get the source dimensions float x0 = (float)source.getMinX(); float y0 = (float)source.getMinY() ; float w = (float)source.getWidth(); float h = (float)source.getHeight(); // Forward map the source using x0, y0, w and h float d_x0 = x0 * scale_x; float d_y0 = y0 * scale_y; float d_w = w * scale_x; float d_h = h * scale_y; // debugging // System.out.println("*** CRIF getBounds2D() "); return new Rectangle2D.Float(d_x0, d_y0, d_w, d_h); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/AddCollectionCRIF.java0000644000175000017500000000276610203035544027256 0ustar mathieumathieu/* * $RCSfile: AddCollectionCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:11 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.util.Collection; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "AddCollection" operation * in the rendered and renderable image layers. * * @see javax.media.jai.operator.AddCollectionDescriptor * @see AddCollectionOpImage * * * @since EA3 */ public class AddCollectionCRIF extends CRIFImpl { /** Constructor. */ public AddCollectionCRIF() { super("addcollection"); } /** * Creates a new instance of AddCollectionOpImage * in the rendered layer. * * @param args A collection of rendered images to be added. * @param hints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new AddCollectionOpImage((Collection)args.getSource(0), renderHints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MagnitudeSquaredCRIF.java0000644000175000017500000000300610203035544030000 0ustar mathieumathieu/* * $RCSfile: MagnitudeSquaredCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:31 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.MagnitudePhaseOpImage; /** * A CRIF supporting the "MagnitudeSquared" operation in the * rendered image layer. * * @since Beta * @see javax.media.jai.operator.MagnitudeSquaredDescriptor * */ public class MagnitudeSquaredCRIF extends CRIFImpl { /** Constructor. */ public MagnitudeSquaredCRIF() { super("magnitudesquared"); } /** * Creates a new instance of a MagnitudeSquared operator. * * @param paramBlock The scaling type. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); RenderedImage source = paramBlock.getRenderedSource(0); return new MagnitudePhaseOpImage(source, renderHints, layout, MagnitudePhaseOpImage.MAGNITUDE_SQUARED); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/WarpRIF.java0000644000175000017500000000542010203035544025346 0ustar mathieumathieu/* * $RCSfile: WarpRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:47 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationNearest; import javax.media.jai.InterpolationBilinear; import javax.media.jai.InterpolationBicubic; import javax.media.jai.InterpolationBicubic2; import javax.media.jai.InterpolationTable; import java.util.Map; import javax.media.jai.Warp; /** * A RIF supporting the "Warp" operation in the rendered * image layer. * * @since EA2 * @see javax.media.jai.operator.WarpDescriptor * @see GeneralWarpOpImage * */ public class WarpRIF implements RenderedImageFactory { /** Constructor. */ public WarpRIF() {} /** * Creates a new instance of warp operator according to the warp object * and interpolation method. * * @param paramBlock The warp and interpolation objects. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); // Get BorderExtender from renderHints if any. BorderExtender extender = RIFUtil.getBorderExtenderHint(renderHints); RenderedImage source = paramBlock.getRenderedSource(0); Warp warp = (Warp)paramBlock.getObjectParameter(0); Interpolation interp = (Interpolation)paramBlock.getObjectParameter(1); double[] backgroundValues = (double[])paramBlock.getObjectParameter(2); if (interp instanceof InterpolationNearest) { return new WarpNearestOpImage(source, renderHints, layout, warp, interp, backgroundValues); } else if (interp instanceof InterpolationBilinear) { return new WarpBilinearOpImage(source, extender, renderHints, layout, warp, interp, backgroundValues); } else { return new WarpGeneralOpImage(source, extender, renderHints, layout, warp, interp, backgroundValues); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/LogOpImage.java0000644000175000017500000003462610203035544026071 0ustar mathieumathieu/* * $RCSfile: LogOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:30 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import javax.media.jai.ColormapOpImage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; import com.sun.media.jai.util.ImageUtil; /** * An OpImage implementing the "Log" operation as * described in javax.media.jai.operator.LogDescriptor. * *

This OpImage takes the natural logarithm of the pixel * values of an image. The operation is done on a per-pixel, per-band * basis. * *

For all integral data types, the log of 0 is set to 0. For * signed integral data types (short and int), * the log of a negative pixel value is set to -1. * *

For all floating point data types ((float and * double), the log of 0 is set to -Infinity, * and the log of a negative pixel value is set to NaN. * * @see javax.media.jai.operator.LogDescriptor * @see LogCRIF * * @since EA2 * */ final class LogOpImage extends ColormapOpImage { /** A lookup table for byte data type. */ private byte[] byteTable = null; /** * Constructor. * *

The layout of the source is used as the fall-back for * the layout of the destination. Any layout parameters not * specified in the layout argument are set to * the same value as that of the source. * * @param source The source image. * @param layout The destination image layout. */ public LogOpImage(RenderedImage source, Map config, ImageLayout layout) { super(source, layout, config, true); /* Set flag to permit in-place operation. */ permitInPlaceOperation(); // Initialize the colormap if necessary. initializeColormapOperation(); } /** * Transform the colormap according to the rescaling parameters. */ protected void transformColormap(byte[][] colormap) { initByteTable(); for(int b = 0; b < 3; b++) { byte[] map = colormap[b]; int mapSize = map.length; for(int i = 0; i < mapSize; i++) { map[i] = byteTable[(map[i] & 0xFF)]; } } } /** * Finds the natural logarithm of the pixels within a specified * rectangle. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { /* Retrieve format tags. */ RasterFormatTag[] formatTags = getFormatTags(); /* No need to mapSourceRect for PointOps. */ RasterAccessor s = new RasterAccessor(sources[0], destRect, formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor d = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); switch (d.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(s, d); break; case DataBuffer.TYPE_USHORT: computeRectUShort(s, d); break; case DataBuffer.TYPE_SHORT: computeRectShort(s, d); break; case DataBuffer.TYPE_INT: computeRectInt(s, d); break; case DataBuffer.TYPE_FLOAT: computeRectFloat(s, d); break; case DataBuffer.TYPE_DOUBLE: computeRectDouble(s, d); break; } if (d.needsClamping()) { d.clampDataArrays(); } d.copyDataToRaster(); } private void computeRectByte(RasterAccessor src, RasterAccessor dst) { initByteTable(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); byte[][] srcData = src.getByteDataArrays(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); byte[][] dstData = dst.getByteDataArrays(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); for (int b = 0; b < dstBands; b++) { byte[] s = srcData[b]; byte[] d = dstData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = byteTable[s[srcPixelOffset] & ImageUtil.BYTE_MASK]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } private void computeRectUShort(RasterAccessor src, RasterAccessor dst) { int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); short[][] srcData = src.getShortDataArrays(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); short[][] dstData = dst.getShortDataArrays(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); for (int b = 0; b < dstBands; b++) { short[] s = srcData[b]; short[] d = dstData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { /* * For unsigned pixels, there are two choices: * 0, or > 0. The standard function takes care of all. */ d[dstPixelOffset] = (short) (Math.log(s[srcPixelOffset] & ImageUtil.USHORT_MASK) + 0.5); srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } private void computeRectShort(RasterAccessor src, RasterAccessor dst) { int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); short[][] srcData = src.getShortDataArrays(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); short[][] dstData = dst.getShortDataArrays(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); for (int b = 0; b < dstBands; b++) { short[] s = srcData[b]; short[] d = dstData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { /* * For signed pixels, there are three choices: * < 0, 0, > 0. The standard function takes care of all. */ d[dstPixelOffset] = (short) (Math.log(s[srcPixelOffset]) + 0.5); srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } private void computeRectInt(RasterAccessor src, RasterAccessor dst) { int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); int[][] srcData = src.getIntDataArrays(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); int[][] dstData = dst.getIntDataArrays(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); for (int b = 0; b < dstBands; b++) { int[] s = srcData[b]; int[] d = dstData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { /* * For signed pixels, there are three choices: * < 0, 0, > 0. */ double p = s[srcPixelOffset]; if (p > 0) { d[dstPixelOffset] = (int)(Math.log(p) + 0.5); } else if (p == 0) { d[dstPixelOffset] = 0; } else { d[dstPixelOffset] = -1; } srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } private void computeRectFloat(RasterAccessor src, RasterAccessor dst) { int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); float[][] srcData = src.getFloatDataArrays(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); float[][] dstData = dst.getFloatDataArrays(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); for (int b = 0; b < dstBands; b++) { float[] s = srcData[b]; float[] d = dstData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { /* * For signed pixels, there are three choices: * < 0, 0, > 0. The standard function takes care of all. */ d[dstPixelOffset] = (float)Math.log(s[srcPixelOffset]); srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } private void computeRectDouble(RasterAccessor src, RasterAccessor dst) { int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); double[][] srcData = src.getDoubleDataArrays(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); double[][] dstData = dst.getDoubleDataArrays(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); for (int b = 0; b < dstBands; b++) { double[] s = srcData[b]; double[] d = dstData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { /* * For signed pixels, there are three choices: * < 0, 0, > 0. The standard function takes care of all. */ d[dstPixelOffset] = Math.log(s[srcPixelOffset]); srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } private synchronized void initByteTable() { if (byteTable != null) return; byteTable = new byte[0x100]; byteTable[0] = 0; // minimum byte value byteTable[1] = 0; for (int i = 2; i < 0x100; i++) { byteTable[i] = (byte)(Math.log(i) + 0.5); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ConstantOpImage.java0000644000175000017500000001372610203035544027137 0ustar mathieumathieu/* * $RCSfile: ConstantOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:19 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.awt.image.SampleModel; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PlanarImage; import javax.media.jai.RasterFactory; import com.sun.media.jai.util.ImageUtil; /** * An OpImage class to generate an image of constant color. * *

ConstantOpImage defines a constant PlanarImage. It is implemented * as a subclass of PatternOpImage with a constant-colored pattern. * */ final class ConstantOpImage extends PatternOpImage { /** Creates a Raster defining tile (0, 0) of the master pattern. */ private static Raster makePattern(SampleModel sampleModel, Number[] bandValues) { WritableRaster pattern = RasterFactory.createWritableRaster( sampleModel, new Point(0, 0)); int width = sampleModel.getWidth(); int height = sampleModel.getHeight(); int dataType = sampleModel.getTransferType(); int numBands = sampleModel.getNumBands(); switch (dataType) { case DataBuffer.TYPE_BYTE: int[] bvalues = new int[numBands]; for (int i = 0; i < numBands; i++) { bvalues[i] = bandValues[i].intValue() & ImageUtil.BYTE_MASK; } /* Put the first scanline in with setPixels. */ for (int x = 0; x < width; x++) { pattern.setPixel(x, 0, bvalues); } break; case DataBuffer.TYPE_USHORT: // USHORT is less than 127 case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: int[] ivalues = new int[numBands]; for (int i = 0; i < numBands; i++) { ivalues[i] = bandValues[i].intValue(); } /* Put the first scanline in with setPixels. */ for (int x = 0; x < width; x++) { pattern.setPixel(x, 0, ivalues); } break; case DataBuffer.TYPE_FLOAT: float[] fvalues = new float[numBands]; for (int i = 0; i < numBands; i++) { fvalues[i] = bandValues[i].floatValue(); } /* Put the first scanline in with setPixels. */ for (int x = 0; x < width; x++) { pattern.setPixel(x, 0, fvalues); } break; case DataBuffer.TYPE_DOUBLE: double[] dvalues = new double[numBands]; for (int i = 0; i < numBands; i++) { dvalues[i] = bandValues[i].doubleValue(); } /* Put the first scanline in with setPixels. */ for (int x = 0; x < width; x++) { pattern.setPixel(x, 0, dvalues); } break; } /* Copy the first line out. */ Object odata = pattern.getDataElements(0, 0, width, 1, null); /* Use the first line to copy other rows. */ for (int y = 1; y < height; y++) { pattern.setDataElements(0, y, width, 1, odata); } return pattern; } private static SampleModel makeSampleModel(int width, int height, Number[] bandValues) { int numBands = bandValues.length; int dataType; if (bandValues instanceof Byte[]) { dataType = DataBuffer.TYPE_BYTE; } else if (bandValues instanceof Short[]) { /* If all band values are positive, use UShort, else use Short. */ dataType = DataBuffer.TYPE_USHORT; Short[] shortValues = (Short[])bandValues; for (int i = 0; i < numBands; i++) { if (shortValues[i].shortValue() < 0) { dataType = DataBuffer.TYPE_SHORT; break; } } } else if (bandValues instanceof Integer[]) { dataType = DataBuffer.TYPE_INT; } else if (bandValues instanceof Float[]) { dataType = DataBuffer.TYPE_FLOAT; } else if (bandValues instanceof Double[]) { dataType = DataBuffer.TYPE_DOUBLE; } else { dataType = DataBuffer.TYPE_UNDEFINED; } return RasterFactory.createPixelInterleavedSampleModel( dataType, width, height, numBands); } private static Raster patternHelper(int width, int height, Number[] bandValues) { SampleModel sampleModel = makeSampleModel(width, height, bandValues); return makePattern(sampleModel, bandValues); } private static ColorModel colorModelHelper(Number[] bandValues) { SampleModel sampleModel = makeSampleModel(1, 1, bandValues); return PlanarImage.createColorModel(sampleModel); } /** * Constructs a ConstantOpImage from a set of sample values. The * ImageLayout object must contain a complete set of information. * * @param layout an ImageLayout containing image bounds, tile * layout, and SampleModel information. * @param bandValues an array of Numbers representing the values of * each image band. */ public ConstantOpImage(int minX, int minY, int width, int height, int tileWidth, int tileHeight, Number[] bandValues) { super(patternHelper(tileWidth, tileHeight, bandValues), colorModelHelper(bandValues), minX, minY, width, height); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/OctTreeOpImage.java0000644000175000017500000005301410240004133026674 0ustar mathieumathieu/* * $RCSfile: OctTreeOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-05-10 01:03:23 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Image; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.util.ArrayList; import java.util.Hashtable; import java.util.LinkedList; import java.util.ListIterator; import java.util.Map; import javax.media.jai.ImageLayout; import javax.media.jai.LookupTableJAI; import javax.media.jai.OpImage; import javax.media.jai.PixelAccessor; import javax.media.jai.PlanarImage; import javax.media.jai.ROI; import javax.media.jai.ROIShape; import javax.media.jai.UnpackedImageData; import com.sun.media.jai.util.ImageUtil; /** * An OpImage implementing the "ColorQuantizer" operation as * described in javax.media.jai.operator.ExtremaDescriptor * based on the oct-tree algorithm. * * An efficient color quantization algorithm, adapted from the C++ * implementation quantize.c in ImageMagick. The pixels for * an image are placed into an oct tree. The oct tree is reduced in * size, and the pixels from the original image are reassigned to the * nodes in the reduced tree.

* * Here is the copyright notice from ImageMagick: * *

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%  Permission is hereby granted, free of charge, to any person obtaining a    %
%  copy of this software and associated documentation files ("ImageMagick"),  %
%  to deal in ImageMagick without restriction, including without limitation   %
%  the rights to use, copy, modify, merge, publish, distribute, sublicense,   %
%  and/or sell copies of ImageMagick, and to permit persons to whom the       %
%  ImageMagick is furnished to do so, subject to the following conditions:    %
%                                                                             %
%  The above copyright notice and this permission notice shall be included in %
%  all copies or substantial portions of ImageMagick.                         %
%                                                                             %
%  The software is provided "as is", without warranty of any kind, express or %
%  implied, including but not limited to the warranties of merchantability,   %
%  fitness for a particular purpose and noninfringement.  In no event shall   %
%  E. I. du Pont de Nemours and Company be liable for any claim, damages or   %
%  other liability, whether in an action of contract, tort or otherwise,      %
%  arising from, out of or in connection with ImageMagick or the use or other %
%  dealings in ImageMagick.                                                   %
%                                                                             %
%  Except as contained in this notice, the name of the E. I. du Pont de       %
%  Nemours and Company shall not be used in advertising or otherwise to       %
%  promote the sale, use or other dealings in ImageMagick without prior       %
%  written authorization from the E. I. du Pont de Nemours and Company.       %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
* * In this OpImage, two significant bugs in the original code are * fix: (1) The computation of tree depth; (2) The computation of the pixels * on each node. * * @see javax.media.jai.operator.ExtremaDescriptor * @see ExtremaCRIF */ public class OctTreeOpImage extends ColorQuantizerOpImage { /** The size of the histogram. */ private int treeSize; private int maxTreeDepth = 8; // these are precomputed in advance private int squares[]; { squares = new int[(maxColorNum << 1) + 1]; for (int i= -maxColorNum; i <= maxColorNum; i++) { squares[i + maxColorNum] = i * i; } } /** * Constructs an OctTreeOpImage. * * @param source The source image. */ public OctTreeOpImage(RenderedImage source, Map config, ImageLayout layout, int maxColorNum, int upperBound, ROI roi, int xPeriod, int yPeriod) { super(source, config, layout, maxColorNum, roi, xPeriod, yPeriod); colorMap = null; this.treeSize = upperBound; } protected synchronized void train() { Cube cube = new Cube(getSourceImage(0), maxColorNum); cube.constructTree(); cube.reduction(); cube.assignment(); colorMap = new LookupTableJAI(cube.colormap); setProperty("LUT", colorMap); setProperty("JAI.LookupTable", colorMap); } class Cube { PlanarImage source; int max_colors; byte[][] colormap = new byte[3][]; Node root; int depth; // counter for the number of colors in the cube. this gets // recalculated often. int colors; // counter for the number of nodes in the tree int nodes; Cube(PlanarImage source, int max_colors) { this.source = source; this.max_colors = max_colors; int i = max_colors; // tree_depth = log max_colors // 2 for (depth = 0; i != 0; depth++) { i >>>= 1; } if (depth > maxTreeDepth) { depth = maxTreeDepth; } else if (depth < 2) { depth = 2; } root = new Node(this); } void constructTree() { if (roi == null) roi = new ROIShape(source.getBounds()); // Cycle throw all source tiles. int minTileX = source.getMinTileX(); int maxTileX = source.getMaxTileX(); int minTileY = source.getMinTileY(); int maxTileY = source.getMaxTileY(); int xStart = source.getMinX(); int yStart = source.getMinY(); for (int y = minTileY; y <= maxTileY; y++) { for (int x = minTileX; x <= maxTileX; x++) { // Determine the required region of this tile. // (Note that getTileRect() instersects tile and // image bounds.) Rectangle tileRect = source.getTileRect(x, y); // Process if and only if within ROI bounds. if (roi.intersects(tileRect)) { // If checking for skipped tiles determine // whether this tile is "hit". if (checkForSkippedTiles && tileRect.x >= xStart && tileRect.y >= yStart) { // Determine the offset within the tile. int offsetX = (xPeriod - ((tileRect.x - xStart) % xPeriod)) % xPeriod; int offsetY = (yPeriod - ((tileRect.y - yStart) % yPeriod)) % yPeriod; // Continue with next tile if offset // is larger than either tile dimension. if (offsetX >= tileRect.width || offsetY >= tileRect.height) { continue; } } // construct the tree constructTree(source.getData(tileRect)); } } } } private void constructTree(Raster source) { if(!isInitialized) { srcPA = new PixelAccessor(getSourceImage(0)); srcSampleType = srcPA.sampleType == PixelAccessor.TYPE_BIT ? DataBuffer.TYPE_BYTE : srcPA.sampleType; isInitialized = true; } Rectangle srcBounds = getSourceImage(0).getBounds().intersection( source.getBounds()); LinkedList rectList; if (roi == null) { // ROI is the whole Raster rectList = new LinkedList(); rectList.addLast(srcBounds); } else { rectList = roi.getAsRectangleList(srcBounds.x, srcBounds.y, srcBounds.width, srcBounds.height); if (rectList == null) { return; // ROI does not intersect with Raster boundary. } } ListIterator iterator = rectList.listIterator(0); int xStart = source.getMinX(); int yStart = source.getMinY(); while (iterator.hasNext()) { Rectangle rect = srcBounds.intersection((Rectangle)iterator.next()); int tx = rect.x; int ty = rect.y; // Find the actual ROI based on start and period. rect.x = startPosition(tx, xStart, xPeriod); rect.y = startPosition(ty, yStart, yPeriod); rect.width = tx + rect.width - rect.x; rect.height = ty + rect.height - rect.y; if (rect.isEmpty()) { continue; // no pixel to count in this rectangle } UnpackedImageData uid = srcPA.getPixels(source, rect, srcSampleType, false); switch (uid.type) { case DataBuffer.TYPE_BYTE: constructTreeByte(uid); break; } } } /* * Procedure Classification begins by initializing a color * description tree of sufficient depth to represent each * possible input color in a leaf. However, it is impractical * to generate a fully-formed color description tree in the * classification phase for realistic values of cmax. If * colors components in the input image are quantized to k-bit * precision, so that cmax= 2k-1, the tree would need k levels * below the root node to allow representing each possible * input color in a leaf. This becomes prohibitive because the * tree's total number of nodes is 1 + sum(i=1,k,8k). * * A complete tree would require 19,173,961 nodes for k = 8, * cmax = 255. Therefore, to avoid building a fully populated * tree, QUANTIZE: (1) Initializes data structures for nodes * only as they are needed; (2) Chooses a maximum depth for * the tree as a function of the desired number of colors in * the output image (currently log2(colormap size)). * * For each pixel in the input image, classification scans * downward from the root of the color description tree. At * each level of the tree it identifies the single node which * represents a cube in RGB space containing It updates the * following data for each such node: * * number_pixels : Number of pixels whose color is contained * in the RGB cube which this node represents; * * unique : Number of pixels whose color is not represented * in a node at lower depth in the tree; initially, n2 = 0 * for all nodes except leaves of the tree. * * total_red/green/blue : Sums of the red, green, and blue * component values for all pixels not classified at a lower * depth. The combination of these sums and n2 will * ultimately characterize the mean color of a set of pixels * represented by this node. */ private void constructTreeByte(UnpackedImageData uid) { Rectangle rect = uid.rect; byte[][] data = uid.getByteData(); int lineStride = uid.lineStride; int pixelStride = uid.pixelStride; byte[] rBand = data[0]; byte[] gBand = data[1]; byte[] bBand = data[2]; int lineInc = lineStride * yPeriod; int pixelInc = pixelStride * xPeriod; int lastLine = rect.height * lineStride; for (int lo = 0; lo < lastLine; lo += lineInc) { int lastPixel = lo + rect.width * pixelStride; for (int po = lo; po < lastPixel; po += pixelInc) { int red = rBand[po + uid.bandOffsets[0]] & 0xff; int green = gBand[po + uid.bandOffsets[1]] & 0xff; int blue = bBand[po + uid.bandOffsets[2]] & 0xff; // a hard limit on the number of nodes in the tree if (nodes > treeSize) { root.pruneLevel(); --depth; } // walk the tree to depth, increasing the // number_pixels count for each node Node node = root; for (int level = 1; level <= depth; ++level) { int id = ((red > node.mid_red ? 1 : 0) | ((green > node.mid_green ? 1 : 0) << 1) | ((blue > node.mid_blue ? 1 : 0) << 2)); if (node.child[id] == null) { node = new Node(node, id, level); } else node = node.child[id]; node.number_pixels ++; } ++node.unique; node.total_red += red; node.total_green += green; node.total_blue += blue; } } } /* * reduction repeatedly prunes the tree until the number of * nodes with unique > 0 is less than or equal to the maximum * number of colors allowed in the output image. * * When a node to be pruned has offspring, the pruning * procedure invokes itself recursively in order to prune the * tree from the leaves upward. The statistics of the node * being pruned are always added to the corresponding data in * that node's parent. This retains the pruned node's color * characteristics for later averaging. */ void reduction() { int totalSamples = (source.getWidth() + xPeriod -1) / xPeriod * (source.getHeight() + yPeriod -1) / yPeriod; int threshold = Math.max(1, totalSamples/ (max_colors * 8)); while (colors > max_colors) { colors = 0; threshold = root.reduce(threshold, Integer.MAX_VALUE); } } /* * Procedure assignment generates the output image from the * pruned tree. The output image consists of two parts: (1) A * color map, which is an array of color descriptions (RGB * triples) for each color present in the output image; (2) A * pixel array, which represents each pixel as an index into * the color map array. * * First, the assignment phase makes one pass over the pruned * color description tree to establish the image's color map. * For each node with n2 > 0, it divides Sr, Sg, and Sb by n2. * This produces the mean color of all pixels that classify no * lower than this node. Each of these colors becomes an entry * in the color map. * * Finally, the assignment phase reclassifies each pixel in * the pruned tree to identify the deepest node containing the * pixel's color. The pixel's value in the pixel array becomes * the index of this node's mean color in the color map. */ void assignment() { colormap = new byte[3][colors]; colors = 0; root.colormap(); } /** * A single Node in the tree. */ class Node { Cube cube; // parent node Node parent; // child nodes Node child[]; int nchild; // our index within our parent int id; // our level within the tree int level; // our color midpoint int mid_red; int mid_green; int mid_blue; // the pixel count for this node and all children int number_pixels; // the pixel count for this node int unique; // the sum of all pixels contained in this node int total_red; int total_green; int total_blue; // used to build the colormap int color_number; Node(Cube cube) { this.cube = cube; this.parent = this; this.child = new Node[8]; this.id = 0; this.level = 0; this.number_pixels = Integer.MAX_VALUE; this.mid_red = (maxColorNum + 1) >> 1; this.mid_green = (maxColorNum + 1) >> 1; this.mid_blue = (maxColorNum + 1) >> 1; } Node(Node parent, int id, int level) { this.cube = parent.cube; this.parent = parent; this.child = new Node[8]; this.id = id; this.level = level; // add to the cube ++cube.nodes; if (level == cube.depth) { ++cube.colors; } // add to the parent ++parent.nchild; parent.child[id] = this; // figure out our midpoint int bi = (1 << (maxTreeDepth - level)) >> 1; mid_red = parent.mid_red + ((id & 1) > 0 ? bi : -bi); mid_green = parent.mid_green + ((id & 2) > 0 ? bi : -bi); mid_blue = parent.mid_blue + ((id & 4) > 0 ? bi : -bi); } /** * Remove this child node, and make sure our parent * absorbs our pixel statistics. */ void pruneChild() { --parent.nchild; parent.unique += unique; parent.total_red += total_red; parent.total_green += total_green; parent.total_blue += total_blue; parent.child[id] = null; --cube.nodes; cube = null; parent = null; } /** * Prune the lowest layer of the tree. */ void pruneLevel() { if (nchild != 0) { for (int id = 0; id < 8; id++) { if (child[id] != null) { child[id].pruneLevel(); } } } if (level == cube.depth) { pruneChild(); } } /** * Remove any nodes that have fewer than threshold * pixels. Also, as long as we're walking the tree: * * - figure out the color with the fewest pixels * - recalculate the total number of colors in the tree */ int reduce(int threshold, int next_threshold) { if (nchild != 0) { for (int id = 0; id < 8; id++) { if (child[id] != null) { next_threshold = child[id].reduce(threshold, next_threshold); } } } if (number_pixels <= threshold) { pruneChild(); } else { if (unique != 0) { cube.colors++; } if (number_pixels < next_threshold) { next_threshold = number_pixels; } } return next_threshold; } /* * colormap traverses the color cube tree and notes each * colormap entry. A colormap entry is any node in the * color cube tree where the number of unique colors is * not zero. */ void colormap() { if (nchild != 0) { for (int id = 0; id < 8; id++) { if (child[id] != null) { child[id].colormap(); } } } if (unique != 0) { cube.colormap[0][cube.colors] = (byte)((total_red + (unique >> 1)) / unique); cube.colormap[1][cube.colors] = (byte)((total_green + (unique >> 1)) / unique); cube.colormap[2][cube.colors] = (byte)((total_blue + (unique >> 1)) / unique); color_number = cube.colors++; } } } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/OrderedDitherRIF.java0000644000175000017500000000326110203035544027162 0ustar mathieumathieu/* * $RCSfile: OrderedDitherRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:39 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ColorCube; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; import java.util.Map; /** * A RIF supporting the "OrderedDither" operation in the rendered * image layer. * * @since EA3 * @see javax.media.jai.operator.OrderedDitherDescriptor * */ public class OrderedDitherRIF implements RenderedImageFactory { /** Constructor. */ public OrderedDitherRIF() {} /** * Creates a new instance of an ordered dither operator according to the * color map and dither mask kernel array. * * @param paramBlock The color map and dither mask kernel array objects. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); RenderedImage source = paramBlock.getRenderedSource(0); ColorCube colorMap = (ColorCube)paramBlock.getObjectParameter(0); KernelJAI[] ditherMask = (KernelJAI[])paramBlock.getObjectParameter(1); return new OrderedDitherOpImage(source, renderHints, layout, colorMap, ditherMask); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ColorConvertOpImage.java0000644000175000017500000004636310444611715027777 0ustar mathieumathieu/* * $RCSfile: ColorConvertOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.4 $ * $Date: 2006-06-16 20:25:49 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Transparency; import java.awt.color.ColorSpace; import java.awt.image.ColorConvertOp; import java.awt.image.ColorModel; import java.awt.image.ComponentColorModel; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.text.NumberFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.Locale; import java.util.Map; import javax.media.jai.ColorSpaceJAI; import javax.media.jai.IHSColorSpace; import javax.media.jai.ImageLayout; import javax.media.jai.PointOpImage; import javax.media.jai.RasterFactory; import java.lang.ref.SoftReference; /** * An OpImage implementing the "ColorConvert" operation as * described in javax.media.jai.operator.ColorConvertDescriptor. * * @since EA4 * * @see javax.media.jai.PointOpImage * @see javax.media.jai.operator.ColorConvertDescriptor * */ final class ColorConvertOpImage extends PointOpImage { /** Cache a rgb color space */ private static final ColorSpace rgbColorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB); private static SoftReference softRef = null; /** The source image parameters */ private ImageParameters srcParam = null; /** The source image parameters */ private ImageParameters dstParam = null; /** The intermediate image parameters */ private ImageParameters tempParam = null; /** The Java 2D ColorConvertOp instance for converting integer type */ private ColorConvertOp colorConvertOp = null; /** case number */ private int caseNumber; /** * Retrive/cache the ColorConvertOp. Because instantiate a ColorConvertOp * is a time-consuming step, create a hashtable referred to by a * SoftReference to cache the ColorConvertOp for using repeatedly. * * @param src the color space of the source image * dst the color space of the destination image * @return The ColorConvertOp to convert from the source color space to * the destination color space. */ private static synchronized ColorConvertOp getColorConvertOp(ColorSpace src, ColorSpace dst) { HashMap colorConvertOpBuf =null; if (softRef == null || ((colorConvertOpBuf = (HashMap)softRef.get()) == null)) { colorConvertOpBuf = new HashMap(); softRef = new SoftReference(colorConvertOpBuf); } ArrayList hashcode = new ArrayList(2); hashcode.add(0, src); hashcode.add(1, dst); ColorConvertOp op = (ColorConvertOp)colorConvertOpBuf.get(hashcode); if (op == null) { op = new ColorConvertOp(src, dst, null); colorConvertOpBuf.put(hashcode, op); } return op; } /** * Retrieve the minimum value of a data type. * * @param dataType The data type as in DataBuffer.TYPE_*. * @return The minimum value of the specified data type. */ private static float getMinValue(int dataType) { float minValue = 0; switch (dataType) { case DataBuffer.TYPE_BYTE: minValue = 0; break; case DataBuffer.TYPE_SHORT: minValue = Short.MIN_VALUE; break; case DataBuffer.TYPE_USHORT: minValue = 0; break; case DataBuffer.TYPE_INT: minValue = Integer.MIN_VALUE; break; default: minValue = 0; } return minValue; } /** * Retrieve the range of a data type. * * @param dataType The data type as in DataBuffer.TYPE_*. * @return The range of the specified data type. */ private static float getRange(int dataType) { float range = 1; switch (dataType) { case DataBuffer.TYPE_BYTE: range = 255; break; case DataBuffer.TYPE_SHORT: range = Short.MAX_VALUE - (int) Short.MIN_VALUE; break; case DataBuffer.TYPE_USHORT: range = Short.MAX_VALUE - (int)Short.MIN_VALUE; break; case DataBuffer.TYPE_INT: range = Integer.MAX_VALUE - (long) Integer.MIN_VALUE; break; default: range = 1; } return range; } /** * Constructor. * * @param source The source image. * @param config Configurable attributes of the image including * configuration variables indexed by * RenderingHints.Keys and image properties indexed * by Strings or CaselessStringKeys. * This is simply forwarded to the superclass constructor. * @param layout The destination image layout. * @param colorModel The destination color model. */ public ColorConvertOpImage(RenderedImage source, Map config, ImageLayout layout, ColorModel colorModel) { super(source, layout, config, true); this.colorModel = colorModel; // Cache the ColorModels. srcParam = new ImageParameters(source.getColorModel(), source.getSampleModel()); dstParam = new ImageParameters(colorModel, sampleModel); ColorSpace srcColorSpace = srcParam.getColorModel().getColorSpace(); ColorSpace dstColorSpace = dstParam.getColorModel().getColorSpace(); // for each case, define the case number; create tempParam // and/or ColorConvertOp if necessary if (srcColorSpace instanceof ColorSpaceJAI && dstColorSpace instanceof ColorSpaceJAI) { // when both are ColorSpaceJAI, convert via RGB caseNumber = 1; tempParam = createTempParam(); } else if (srcColorSpace instanceof ColorSpaceJAI) { // when source is ColorSpaceJAI, 1. convert via RGB if // the dest isn't RGB; 2. convert to RGB if (dstColorSpace != rgbColorSpace) { caseNumber = 2; tempParam = createTempParam(); colorConvertOp = getColorConvertOp(rgbColorSpace, dstColorSpace); } else caseNumber = 3; } else if (dstColorSpace instanceof ColorSpaceJAI) { // when destination is ColorSpaceJAI, 1. convert via RGB if // source isn't RGB; 2. convert from RGB if (srcColorSpace != rgbColorSpace) { caseNumber = 4; tempParam = createTempParam(); colorConvertOp = getColorConvertOp(srcColorSpace, rgbColorSpace); } else caseNumber = 5; } else { // if all the color space are not ColorSpaceJAI caseNumber = 6; colorConvertOp = getColorConvertOp(srcColorSpace, dstColorSpace); } // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * Computes a tile of the destination image in the destination color space. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { WritableRaster tempRas = null; // Save a reference to the source Raster. Raster source = sources[0]; // Ensure the source Raster has the same bounds as the destination. if(!destRect.equals(source.getBounds())) { source = source.createChild(destRect.x, destRect.y, destRect.width, destRect.height, destRect.x, destRect.y, null); } switch (caseNumber) { // 1. When source and destination color spaces are all ColorSpaceJAI, // convert via RGB color space case 1: tempRas = computeRectColorSpaceJAIToRGB(source, srcParam, null, tempParam); computeRectColorSpaceJAIFromRGB(tempRas, tempParam, dest, dstParam); break; // when only the source color space is ColorSpaceJAI, // 2. if the destination is not RGB, convert to RGB using // ColorSpaceJAI; then convert RGB to the destination // 3. if the destination is RGB, convert using ColorSpaceJAI case 2: tempRas = computeRectColorSpaceJAIToRGB(source, srcParam, null, tempParam); computeRectNonColorSpaceJAI(tempRas, tempParam, dest, dstParam, destRect); break; case 3: computeRectColorSpaceJAIToRGB(source, srcParam, dest, dstParam); break; // 4, 5. When only the destination color space is ColorSpaceJAI, // similar to the case above. case 4: tempRas =createTempWritableRaster(source); computeRectNonColorSpaceJAI(source, srcParam, tempRas, tempParam, destRect); computeRectColorSpaceJAIFromRGB(tempRas, tempParam, dest, dstParam); break; case 5: computeRectColorSpaceJAIFromRGB(source, srcParam, dest, dstParam); break; // 6. If all the color space are not ColorSpaceJAI case 6: computeRectNonColorSpaceJAI(source, srcParam, dest, dstParam, destRect); default : break; } } // when the source color space is ColorSpaceJAI, convert it to RGB. // 1. If the source data type is short/int, shift the data to [0, // MAX-MIN] // 2. Convert to RGB. // 3. Shift back to [MIN, MAX] private WritableRaster computeRectColorSpaceJAIToRGB(Raster src, ImageParameters srcParam, WritableRaster dest, ImageParameters dstParam) { src = convertRasterToUnsigned(src); ColorSpaceJAI colorSpaceJAI = (ColorSpaceJAI) srcParam.getColorModel().getColorSpace(); dest = colorSpaceJAI.toRGB(src, srcParam.getComponentSize(), dest, dstParam.getComponentSize()); dest = convertRasterToSigned(dest); return dest; } // when the source color space is ColorSpaceJAI, convert it from RGB. // 1. If the source data type is short/int, shift the data to [0, // MAX-MIN] // 2. Convert from RGB. // 3. Shift back to [MIN, MAX] private WritableRaster computeRectColorSpaceJAIFromRGB(Raster src, ImageParameters srcParam, WritableRaster dest, ImageParameters dstParam){ src = convertRasterToUnsigned(src); ColorSpaceJAI colorSpaceJAI = (ColorSpaceJAI) dstParam.getColorModel().getColorSpace(); dest = colorSpaceJAI.fromRGB(src, srcParam.getComponentSize(), dest, dstParam.getComponentSize()); dest = convertRasterToSigned(dest); return dest; } // When the source and destination color spaces are not ColorSpaceJAI, // convert using ColorConvertOp of Java 2D for integer type. For the // floating point, use the following method. private void computeRectNonColorSpaceJAI(Raster src, ImageParameters srcParam, WritableRaster dest, ImageParameters dstParam, Rectangle destRect) { if (!srcParam.isFloat() && !dstParam.isFloat()) { // Create a ColorConvertOp if there are only integral data. // Integral type: use the ColorConvertOp. // Ensure that the Rasters are the same size as apparently // required by ColorConvertOp although not so documented. Raster s = src; if (s.getMinX() != destRect.x || s.getMinY() != destRect.y || s.getWidth() != destRect.width || s.getHeight() != destRect.height) { s = s.createChild(destRect.x, destRect.y, destRect.width, destRect.height, destRect.x, destRect.y, null); } WritableRaster d = dest; if (d.getMinX() != destRect.x || d.getMinY() != destRect.y || d.getWidth() != destRect.width || d.getHeight() != destRect.height) { d = d.createWritableChild(destRect.x, destRect.y, destRect.width, destRect.height, destRect.x, destRect.y, null); } // Perform the color conversion on the (possible child) Rasters. synchronized (colorConvertOp.getClass()) { // Lock on the class to prevent crash in non-re-entrant // native code on MP systems (jai-core issue 21). colorConvertOp.filter(s, d); } } else { //For the floating point data types, convert via CIEXYZ color space. //Do it pixel-by-pixel (slow!). ColorSpace srcColorSpace = srcParam.getColorModel().getColorSpace(); ColorSpace dstColorSpace = dstParam.getColorModel().getColorSpace(); boolean srcFloat = srcParam.isFloat(); float srcMinValue = srcParam.getMinValue(); float srcRange = srcParam.getRange(); boolean dstFloat = dstParam.isFloat(); float dstMinValue = dstParam.getMinValue(); float dstRange = dstParam.getRange(); int rectYMax = destRect.y + destRect.height; int rectXMax = destRect.x + destRect.width; int numComponents = srcColorSpace.getNumComponents(); float[] srcPixel = new float[numComponents]; float[] xyzPixel; float[] dstPixel; for (int y = destRect.y; y < rectYMax; y++) { for (int x = destRect.x; x < rectXMax; x++) { srcPixel = src.getPixel(x, y, srcPixel); if (!srcFloat) { // Normalize the source samples. for (int i = 0; i < numComponents; i++) { srcPixel[i] = (srcPixel[i] - srcMinValue)/srcRange; } } // Convert src to dst via CIEXYZ. xyzPixel = srcColorSpace.toCIEXYZ(srcPixel); dstPixel = dstColorSpace.fromCIEXYZ(xyzPixel); if (!dstFloat) { // Scale the destination samples. for (int i = 0; i < numComponents; i++) { dstPixel[i] = (dstPixel[i]*dstRange + dstMinValue); } } dest.setPixel(x, y, dstPixel); } } } } // Back up the destination parameters. Set the destination to the // bridge color space RGB. private ImageParameters createTempParam() { ColorModel cm = null; SampleModel sm = null; if (srcParam.getDataType() > dstParam.getDataType()) { cm = srcParam.getColorModel(); sm = srcParam.getSampleModel(); } else { cm = dstParam.getColorModel(); sm = dstParam.getSampleModel(); } cm = new ComponentColorModel(rgbColorSpace, cm.getComponentSize(), cm.hasAlpha() , cm.isAlphaPremultiplied(), cm.getTransparency(), sm.getDataType()); return new ImageParameters(cm, sm); } // Create an WritableRaster with the same SampleModel and location // as the passed Raster parameter. private WritableRaster createTempWritableRaster(Raster src) { Point origin = new Point(src.getMinX(), src.getMinY()); return RasterFactory.createWritableRaster(src.getSampleModel(), origin); } // Shift the sample value to [0, MAX-MIN] private Raster convertRasterToUnsigned(Raster ras) { int type = ras.getSampleModel().getDataType(); WritableRaster tempRas = null; if ((type == DataBuffer.TYPE_INT || type == DataBuffer.TYPE_SHORT)) { int minX = ras.getMinX(), minY = ras.getMinY(); int w = ras.getWidth() , h = ras.getHeight(); int[] buf = ras.getPixels(minX, minY, w, h, (int[])null); convertBufferToUnsigned(buf, type); tempRas = createTempWritableRaster(ras); tempRas.setPixels(minX, minY, w, h, buf); return tempRas; } return ras; } // Shift the sample value back to [MIN, MAX] private WritableRaster convertRasterToSigned(WritableRaster ras) { int type = ras.getSampleModel().getDataType(); WritableRaster tempRas = null; if ((type == DataBuffer.TYPE_INT || type == DataBuffer.TYPE_SHORT)) { int minX = ras.getMinX(), minY = ras.getMinY(); int w = ras.getWidth() , h = ras.getHeight(); int[] buf = ras.getPixels(minX, minY, w, h, (int[])null); convertBufferToSigned(buf, type); if (ras instanceof WritableRaster) tempRas = (WritableRaster) ras; else tempRas = createTempWritableRaster(ras); tempRas.setPixels(minX, minY, w, h, buf); return tempRas; } return ras; } // Shift the value to [MIN, MAX] private void convertBufferToSigned(int[] buf, int type) { if (buf == null) return; if (type == DataBuffer.TYPE_SHORT) for (int i=0; i < buf.length; i++) { buf[i] += Short.MIN_VALUE; } else if (type == DataBuffer.TYPE_INT) { for (int i=0; i < buf.length; i++) { buf[i] = (int) ((buf[i] & 0xFFFFFFFFl) + Integer.MIN_VALUE); } } } // Shift the value to [0, MAX-MIN] private void convertBufferToUnsigned(int[] buf, int type) { if (buf == null) return; if (type == DataBuffer.TYPE_SHORT) for (int i = 0; i < buf.length; i++) { buf[i] -= Short.MIN_VALUE; } else if (type == DataBuffer.TYPE_INT) { for (int i = 0; i < buf.length; i++) { buf[i] = (int) ((buf[i] & 0xFFFFFFFFl) - Integer.MIN_VALUE); } } } // define a class to cache the parameters private final class ImageParameters { private boolean isFloat; private ColorModel colorModel; private SampleModel sampleModel; private float minValue; private float range; private int[] componentSize; private int dataType; ImageParameters(ColorModel cm, SampleModel sm) { this.colorModel = cm; this.sampleModel = sm; this.dataType = sm.getDataType(); this.isFloat = this.dataType == DataBuffer.TYPE_FLOAT || this.dataType == DataBuffer.TYPE_DOUBLE; this.minValue = ColorConvertOpImage.getMinValue(this.dataType); this.range = ColorConvertOpImage.getRange(this.dataType); this.componentSize = cm.getComponentSize(); } public boolean isFloat() { return isFloat; } public ColorModel getColorModel() { return colorModel; } public SampleModel getSampleModel() { return sampleModel; } public float getMinValue() { return minValue; } public float getRange() { return range; } public int[] getComponentSize() { return componentSize; } public int getDataType() { return dataType; } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/GradientOpImage.java0000644000175000017500000005363310203035544027104 0ustar mathieumathieu/* * $RCSfile: GradientOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:27 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class to perform Gradient operation on a source image. * *

The Kernels cannot be bigger in any dimension than the image data. * * * @see KernelJAI */ final class GradientOpImage extends AreaOpImage { /** * The kernel with which to do the gradient operation. */ protected KernelJAI kernel_h, kernel_v; /** Kernel variables. */ private int kw, kh; /** * Creates a GradientOpImage given the image source and * the pair of orthogonal gradient kernels. The image dimensions are * derived from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param kernel_h the horizontal kernel. * @param kernel_v the vertical kernel */ public GradientOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, KernelJAI kernel_h, KernelJAI kernel_v) { super(source, layout, config, true, extender, kernel_h.getLeftPadding(), kernel_h.getRightPadding(), kernel_h.getTopPadding(), kernel_h.getBottomPadding()); // Local copy of the kernels this.kernel_h = kernel_h; this.kernel_v = kernel_v; // // At this point both kernels should be of same width & height // so it's enough to get the information from one of them // kw = kernel_h.getWidth(); kh = kernel_h.getHeight(); } /** * Performs gradient operation on a specified rectangle. The sources are * cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); Raster source = sources[0]; Rectangle srcRect = mapDestRect(destRect, 0); RasterAccessor srcAccessor = new RasterAccessor(source, srcRect, formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor dstAccessor = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(srcAccessor, dstAccessor); break; case DataBuffer.TYPE_INT: intLoop(srcAccessor, dstAccessor); break; case DataBuffer.TYPE_SHORT: shortLoop(srcAccessor, dstAccessor); break; case DataBuffer.TYPE_USHORT: ushortLoop(srcAccessor, dstAccessor); break; case DataBuffer.TYPE_FLOAT: floatLoop(srcAccessor, dstAccessor); break; case DataBuffer.TYPE_DOUBLE: doubleLoop(srcAccessor, dstAccessor); break; default: } // If the RasterAccessor object set up a temporary buffer for the // op to write to, tell the RasterAccessor to write that data // to the raster no that we're done with it. if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } private void byteLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); float[] kdata_h = kernel_h.getKernelData(); float[] kdata_v = kernel_v.getKernelData(); byte dstDataArrays[][] = dst.getByteDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); byte srcDataArrays[][] = src.getByteDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); for (int k = 0; k < dnumBands; k++) { byte dstData[] = dstDataArrays[k]; byte srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { float f_h = 0.0f; float f_v = 0.0f; int kernelVerticalOffset = 0; int imageVerticalOffset = srcPixelOffset; for (int u = 0; u < kh; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < kw; v++) { f_h += ((int)srcData[imageOffset] & 0xff) * kdata_h[kernelVerticalOffset + v]; f_v += ((int)srcData[imageOffset] & 0xff) * kdata_v[kernelVerticalOffset + v]; imageOffset += srcPixelStride; } kernelVerticalOffset += kw; imageVerticalOffset += srcScanlineStride; } // Do the Gradient float sqr_f_h = f_h * f_h; float sqr_f_v = f_v * f_v; float result = (float)Math.sqrt(sqr_f_h + sqr_f_v); int val = (int)(result + 0.5f); // Round if (val < 0) { val = 0; } else if (val > 255) { val = 255; } dstData[dstPixelOffset] = (byte)val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } private void shortLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); float[] kdata_h = kernel_h.getKernelData(); float[] kdata_v = kernel_v.getKernelData(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { float f_h = 0.0f; float f_v = 0.0f; int kernelVerticalOffset = 0; int imageVerticalOffset = srcPixelOffset; for (int u = 0; u < kh; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < kw; v++) { f_h += (srcData[imageOffset]) * kdata_h[kernelVerticalOffset + v]; f_v += (srcData[imageOffset]) * kdata_v[kernelVerticalOffset + v]; imageOffset += srcPixelStride; } kernelVerticalOffset += kw; imageVerticalOffset += srcScanlineStride; } // Do the Gradient float sqr_f_h = f_h * f_h; float sqr_f_v = f_v * f_v; float result = (float)Math.sqrt(sqr_f_h + sqr_f_v); int val = (int)(result + 0.5f); // Round if (val < Short.MIN_VALUE) { val = Short.MIN_VALUE; } else if (val > Short.MAX_VALUE) { val = Short.MAX_VALUE; } dstData[dstPixelOffset] = (short)val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } private void ushortLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); float[] kdata_h = kernel_h.getKernelData(); float[] kdata_v = kernel_v.getKernelData(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { float f_h = 0.0f; float f_v = 0.0f; int kernelVerticalOffset = 0; int imageVerticalOffset = srcPixelOffset; for (int u = 0; u < kh; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < kw; v++) { f_h += (srcData[imageOffset] & 0xffff) * kdata_h[kernelVerticalOffset + v]; f_v += (srcData[imageOffset] & 0xffff) * kdata_v[kernelVerticalOffset + v]; imageOffset += srcPixelStride; } kernelVerticalOffset += kw; imageVerticalOffset += srcScanlineStride; } // Do the Gradient float sqr_f_h = f_h * f_h; float sqr_f_v = f_v * f_v; float result = (float)Math.sqrt(sqr_f_h + sqr_f_v); int val = (int)(result + 0.5f); // Round if (val < 0) { val = 0; } else if (val > 0xffff) { val = 0xffff; } dstData[dstPixelOffset] = (short)val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } private void intLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); float[] kdata_h = kernel_h.getKernelData(); float[] kdata_v = kernel_v.getKernelData(); int dstDataArrays[][] = dst.getIntDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcDataArrays[][] = src.getIntDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); for (int k = 0; k < dnumBands; k++) { int dstData[] = dstDataArrays[k]; int srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { float f_h = 0.0f; float f_v = 0.0f; int kernelVerticalOffset = 0; int imageVerticalOffset = srcPixelOffset; for (int u = 0; u < kh; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < kw; v++) { f_h += ((int)srcData[imageOffset]) * kdata_h[kernelVerticalOffset + v]; f_v += ((int)srcData[imageOffset]) * kdata_v[kernelVerticalOffset + v]; imageOffset += srcPixelStride; } kernelVerticalOffset += kw; imageVerticalOffset += srcScanlineStride; } // Do the Gradient float sqr_f_h = f_h * f_h; float sqr_f_v = f_v * f_v; float result = (float)Math.sqrt(sqr_f_h + sqr_f_v); dstData[dstPixelOffset] = (int)(result + 0.5f); // Round srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } private void floatLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); float[] kdata_h = kernel_h.getKernelData(); float[] kdata_v = kernel_v.getKernelData(); float dstDataArrays[][] = dst.getFloatDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); float srcDataArrays[][] = src.getFloatDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); for (int k = 0; k < dnumBands; k++) { float dstData[] = dstDataArrays[k]; float srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { float f_h = 0.0f; float f_v = 0.0f; int kernelVerticalOffset = 0; int imageVerticalOffset = srcPixelOffset; for (int u = 0; u < kh; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < kw; v++) { f_h += (srcData[imageOffset]) * kdata_h[kernelVerticalOffset + v]; f_v += (srcData[imageOffset]) * kdata_v[kernelVerticalOffset + v]; imageOffset += srcPixelStride; } kernelVerticalOffset += kw; imageVerticalOffset += srcScanlineStride; } // Do the Gradient float sqr_f_h = f_h * f_h; float sqr_f_v = f_v * f_v; float result = (float)Math.sqrt(sqr_f_h + sqr_f_v); dstData[dstPixelOffset] = result; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } private void doubleLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); float[] kdata_h = kernel_h.getKernelData(); float[] kdata_v = kernel_v.getKernelData(); double dstDataArrays[][] = dst.getDoubleDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); double srcDataArrays[][] = src.getDoubleDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); for (int k = 0; k < dnumBands; k++) { double dstData[] = dstDataArrays[k]; double srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { double f_h = 0.0; double f_v = 0.0; int kernelVerticalOffset = 0; int imageVerticalOffset = srcPixelOffset; for (int u = 0; u < kh; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < kw; v++) { f_h += (srcData[imageOffset]) * kdata_h[kernelVerticalOffset + v]; f_v += (srcData[imageOffset]) * kdata_v[kernelVerticalOffset + v]; imageOffset += srcPixelStride; } kernelVerticalOffset += kw; imageVerticalOffset += srcScanlineStride; } // Do the Gradient double sqr_f_h = f_h * f_h; double sqr_f_v = f_v * f_v; double result = Math.sqrt(sqr_f_h + sqr_f_v); dstData[dstPixelOffset] = result; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } // public static OpImage createTestImage(OpImageTester oit) { // float data_h[] = {-1.0f, -2.0f, -1.0f, // 0.0f, 0.0f, 0.0f, // 1.0f, 2.0f, 1.0f}; // float data_v[] = {-1.0f, 0.0f, 1.0f, // -2.0f, 0.0f, 2.0f, // -1.0f, 0.0f, 1.0f}; // KernelJAI kern_h = new KernelJAI(3,3,data_h); // KernelJAI kern_v = new KernelJAI(3,3,data_v); // return new GradientOpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource()), // kern_h, kern_v); // } // public static void main(String args[]) { // String classname = "com.sun.media.jai.opimage.GradientOpImage"; // OpImageTester.performDiagnostics(classname,args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/AndOpImage.java0000644000175000017500000003111710203035544026042 0ustar mathieumathieu/* * $RCSfile: AndOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:14 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PointOpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage implementing the "And" operation as * described in javax.media.jai.operator.AndDescriptor. * *

This OpImage logically "ands" the pixel values of two source * images on a per-band basis. In case the two source images have different * number of bands, the number of bands for the destination image is the * smaller band number of the two source images. That is * dstNumBands = Math.min(src1NumBands, src2NumBands). * In case the two source images have different data types, the data type * for the destination image is the bigger data type of the two source * images. * *

The value of the pixel (x, y) in the destination image is defined as: *

 * for (b = 0; b < numBands; b++) {
 *     dst[y][x][b] = src1[y][x][b] & src2[y][x][b];
 * }
 * 
* * The data type byte is treated as unsigned, with maximum * value as 255 and minimum value as 0. * * @since EA2 * @see javax.media.jai.operator.AndDescriptor * @see AndCRIF * */ final class AndOpImage extends PointOpImage { /** * Constructs an AndOpImage. * *

The layout parameter may optionally contains the * tile grid layout, sample model, and/or color model. The image * dimension is determined by the intersection of the bounding boxes * of the two source images. * *

The image layout of the first source image, source1, * is used as the fall-back for the image layout of the destination * image. Any layout parameters not specified in the layout * argument are set to the same value as that of source1. * * @param source1 The first source image. * @param source2 The second source image. * @param layout The destination image layout. */ public AndOpImage(RenderedImage source1, RenderedImage source2, Map config, ImageLayout layout) { super(source1, source2, layout, config, true); // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * Multiplies the pixel values of two source images within a specified * rectangle. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); /* For PointOpImage, srcRect = destRect. */ RasterAccessor s1 = new RasterAccessor(sources[0], destRect, formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor s2 = new RasterAccessor(sources[1], destRect, formatTags[1], getSourceImage(1).getColorModel()); RasterAccessor d = new RasterAccessor(dest, destRect, formatTags[2], getColorModel()); if(d.isBinary()) { byte[] src1Bits = s1.getBinaryDataArray(); byte[] src2Bits = s2.getBinaryDataArray(); byte[] dstBits = d.getBinaryDataArray(); int length = dstBits.length; for(int i = 0; i < length; i++) { dstBits[i] = (byte)(src1Bits[i] & src2Bits[i]); } d.copyBinaryDataToRaster(); return; } int src1LineStride = s1.getScanlineStride(); int src1PixelStride = s1.getPixelStride(); int[] src1BandOffsets = s1.getBandOffsets(); int src2LineStride = s2.getScanlineStride(); int src2PixelStride = s2.getPixelStride(); int[] src2BandOffsets = s2.getBandOffsets(); int dstNumBands = d.getNumBands(); int dstWidth = d.getWidth(); int dstHeight = d.getHeight(); int dstLineStride = d.getScanlineStride(); int dstPixelStride = d.getPixelStride(); int[] dstBandOffsets = d.getBandOffsets(); switch (d.getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(dstNumBands, dstWidth, dstHeight, src1LineStride, src1PixelStride, src1BandOffsets, s1.getByteDataArrays(), src2LineStride, src2PixelStride, src2BandOffsets, s2.getByteDataArrays(), dstLineStride, dstPixelStride, dstBandOffsets, d.getByteDataArrays()); break; case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: shortLoop(dstNumBands, dstWidth, dstHeight, src1LineStride, src1PixelStride, src1BandOffsets, s1.getShortDataArrays(), src2LineStride, src2PixelStride, src2BandOffsets, s2.getShortDataArrays(), dstLineStride, dstPixelStride, dstBandOffsets, d.getShortDataArrays()); break; case DataBuffer.TYPE_INT: intLoop(dstNumBands, dstWidth, dstHeight, src1LineStride, src1PixelStride, src1BandOffsets, s1.getIntDataArrays(), src2LineStride, src2PixelStride, src2BandOffsets, s2.getIntDataArrays(), dstLineStride, dstPixelStride, dstBandOffsets, d.getIntDataArrays()); break; } d.copyDataToRaster(); } private void byteLoop(int dstNumBands, int dstWidth, int dstHeight, int src1LineStride, int src1PixelStride, int[] src1BandOffsets, byte[][] src1Data, int src2LineStride, int src2PixelStride, int[] src2BandOffsets, byte[][] src2Data, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, byte[][] dstData) { for (int b = 0; b < dstNumBands; b++) { byte[] s1 = src1Data[b]; byte[] s2 = src2Data[b]; byte[] d = dstData[b]; int src1LineOffset = src1BandOffsets[b]; int src2LineOffset = src2BandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int src1PixelOffset = src1LineOffset; int src2PixelOffset = src2LineOffset; int dstPixelOffset = dstLineOffset; src1LineOffset += src1LineStride; src2LineOffset += src2LineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = (byte)(s1[src1PixelOffset] & s2[src2PixelOffset]); src1PixelOffset += src1PixelStride; src2PixelOffset += src2PixelStride; dstPixelOffset += dstPixelStride; } } } } private void shortLoop(int dstNumBands, int dstWidth, int dstHeight, int src1LineStride, int src1PixelStride, int[] src1BandOffsets, short[][] src1Data, int src2LineStride, int src2PixelStride, int[] src2BandOffsets, short[][] src2Data, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, short[][] dstData) { for (int b = 0; b < dstNumBands; b++) { short[] s1 = src1Data[b]; short[] s2 = src2Data[b]; short[] d = dstData[b]; int src1LineOffset = src1BandOffsets[b]; int src2LineOffset = src2BandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int src1PixelOffset = src1LineOffset; int src2PixelOffset = src2LineOffset; int dstPixelOffset = dstLineOffset; src1LineOffset += src1LineStride; src2LineOffset += src2LineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = (short)(s1[src1PixelOffset] & s2[src2PixelOffset]); src1PixelOffset += src1PixelStride; src2PixelOffset += src2PixelStride; dstPixelOffset += dstPixelStride; } } } } private void intLoop(int dstNumBands, int dstWidth, int dstHeight, int src1LineStride, int src1PixelStride, int[] src1BandOffsets, int[][] src1Data, int src2LineStride, int src2PixelStride, int[] src2BandOffsets, int[][] src2Data, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, int[][] dstData) { for (int b = 0; b < dstNumBands; b++) { int[] s1 = src1Data[b]; int[] s2 = src2Data[b]; int[] d = dstData[b]; int src1LineOffset = src1BandOffsets[b]; int src2LineOffset = src2BandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int src1PixelOffset = src1LineOffset; int src2PixelOffset = src2LineOffset; int dstPixelOffset = dstLineOffset; src1LineOffset += src1LineStride; src2LineOffset += src2LineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = s1[src1PixelOffset] & s2[src2PixelOffset]; src1PixelOffset += src1PixelStride; src2PixelOffset += src2PixelStride; dstPixelOffset += dstPixelStride; } } } } // public static void main(String args[]) { // System.out.println("AndOpImage Test"); // ImageLayout layout; // OpImage src1, src2, dst; // Rectangle rect = new Rectangle(0, 0, 5, 5); // System.out.println("1. PixelInterleaved byte 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 800, 800, 0, 0, 200, 200, DataBuffer.TYPE_BYTE, 3, false); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new AndOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("2. Banded byte 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 800, 800, 0, 0, 200, 200, DataBuffer.TYPE_BYTE, 3, true); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new AndOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("3. PixelInterleaved int 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_INT, 3, false); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new AndOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("4. Banded int 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_INT, 3, true); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new AndOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ClampOpImage.java0000644000175000017500000003632710203035544026404 0ustar mathieumathieu/* * $RCSfile: ClampOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:16 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.PointOpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; import com.sun.media.jai.util.ImageUtil; /** * An OpImage implementing the "Clamp" operation. * *

This OpImage maps all the pixel values of an image * that are less than a lower bound to that lower bound value, and all that * are greater than a upper bound to that upper bound value. All pixel values * fall within these boundaries remain unchanged. The mapping is done on a * per-band basis. * *

Each of the lower bound and upper bound arrays may have only * one value in it. If that is the case, that value is applied to all bands. * The number of elements in each array has to be consistent, i.e. either * all arrays contain one element in them or all they have the same number * of elements that matches the number of bands of the source image. * * @see javax.media.jai.operator.ClampDescriptor * @see ClampCRIF * * * @since EA2 */ final class ClampOpImage extends PointOpImage { /** Lookup table for byte data */ private byte[][] byteTable = null; /** The lower bound, one for each band. */ private final double[] low; /** The upper bound, one for each band. */ private final double[] high; private synchronized void initByteTable() { if (byteTable == null) { /* Initialize byteTable. */ int numBands = getSampleModel().getNumBands(); byteTable = new byte[numBands][0x100]; for (int b = 0; b < numBands; b++) { byte[] t = byteTable[b]; int l = (int)low[b]; int h = (int)high[b]; byte bl = (byte)l; byte bh = (byte)h; for (int i = 0; i < 0x100; i++) { if (i < l) { t[i] = bl; } else if (i > h) { t[i] = bh; } else { t[i] = (byte)i; } } } } } /** * Constructor. * * @param source The source image. * @param layout The destination image layout. * @param low The lower bound of the clamp. * @param high The upper bound of the clamp. */ public ClampOpImage(RenderedImage source, Map config, ImageLayout layout, double[] low, double[] high) { super(source, layout, config, true); int numBands = getSampleModel().getNumBands(); if (low.length < numBands || high.length < numBands) { this.low = new double[numBands]; this.high = new double[numBands]; for (int i = 0; i < numBands; i++) { this.low[i] = low[0]; this.high[i] = high[0]; } } else { this.low = (double[])low.clone(); this.high = (double[])high.clone(); } // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * Map the pixels inside a specified rectangle whose value is within a * range to a constant on a per-band basis. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); Rectangle srcRect = mapDestRect(destRect, 0); RasterAccessor src = new RasterAccessor(sources[0], srcRect, formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor dst = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); switch (dst.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(src, dst); break; case DataBuffer.TYPE_USHORT: computeRectUShort(src, dst); break; case DataBuffer.TYPE_SHORT: computeRectShort(src, dst); break; case DataBuffer.TYPE_INT: computeRectInt(src, dst); break; case DataBuffer.TYPE_FLOAT: computeRectFloat(src, dst); break; case DataBuffer.TYPE_DOUBLE: computeRectDouble(src, dst); break; } dst.copyDataToRaster(); } private void computeRectByte(RasterAccessor src, RasterAccessor dst) { initByteTable(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); byte[][] dstData = dst.getByteDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); byte[][] srcData = src.getByteDataArrays(); for (int b = 0; b < dstBands; b++) { byte[] d = dstData[b]; byte[] s = srcData[b]; byte[] t = byteTable[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = t[s[srcPixelOffset] & ImageUtil.BYTE_MASK]; dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectUShort(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); short[][] dstData = dst.getShortDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); short[][] srcData = src.getShortDataArrays(); for (int b = 0; b < dstBands; b++) { short[] d = dstData[b]; short[] s = srcData[b]; int lo = (int)low[b]; int hi = (int)high[b]; short slo = (short)lo; short shi = (short)hi; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { int p = s[srcPixelOffset] & ImageUtil.USHORT_MASK; if (p < lo) { d[dstPixelOffset] = slo; } else if (p > hi) { d[dstPixelOffset] = shi; } else { d[dstPixelOffset] = (short)p; } dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectShort(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); short[][] dstData = dst.getShortDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); short[][] srcData = src.getShortDataArrays(); for (int b = 0; b < dstBands; b++) { short[] d = dstData[b]; short[] s = srcData[b]; int lo = (int)low[b]; int hi = (int)high[b]; short slo = (short)lo; short shi = (short)hi; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { short p = s[srcPixelOffset]; if (p < lo) { d[dstPixelOffset] = slo; } else if (p > hi) { d[dstPixelOffset] = shi; } else { d[dstPixelOffset] = p; } dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectInt(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); int[][] dstData = dst.getIntDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); int[][] srcData = src.getIntDataArrays(); for (int b = 0; b < dstBands; b++) { int[] d = dstData[b]; int[] s = srcData[b]; double lo = low[b]; double hi = high[b]; int ilo = (int)lo; int ihi = (int)hi; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { int p = s[srcPixelOffset]; if (p < lo) { d[dstPixelOffset] = ilo; } else if (p > hi) { d[dstPixelOffset] = ihi; } else { d[dstPixelOffset] = p; } dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectFloat(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); float[][] dstData = dst.getFloatDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); float[][] srcData = src.getFloatDataArrays(); for (int b = 0; b < dstBands; b++) { float[] d = dstData[b]; float[] s = srcData[b]; double lo = low[b]; double hi = high[b]; float flo = (float)lo; float fhi = (float)hi; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { float p = s[srcPixelOffset]; if (p < lo) { d[dstPixelOffset] = flo; } else if (p > hi) { d[dstPixelOffset] = fhi; } else { d[dstPixelOffset] = p; } dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectDouble(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); double[][] dstData = dst.getDoubleDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); double[][] srcData = src.getDoubleDataArrays(); for (int b = 0; b < dstBands; b++) { double[] d = dstData[b]; double[] s = srcData[b]; double lo = low[b]; double hi = high[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { double p = s[srcPixelOffset]; if (p < lo) { d[dstPixelOffset] = lo; } else if (p > hi) { d[dstPixelOffset] = hi; } else { d[dstPixelOffset] = p; } dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/AddConstCRIF.java0000644000175000017500000000275510203035544026247 0ustar mathieumathieu/* * $RCSfile: AddConstCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:12 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "AddConst" operation in the rendered * and renderable image layers. * * @see javax.media.jai.operator.AddConstDescriptor * @see AddConstOpImage * */ public class AddConstCRIF extends CRIFImpl { /** Constructor. */ public AddConstCRIF() { super("addconst"); } /** * Creates a new instance of AddConstOpImage in the * rendered layer. * * @param args The source image and the constants. * @param hints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new AddConstOpImage(args.getRenderedSource(0), renderHints, layout, (double[])args.getObjectParameter(0)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/BorderOpImage.java0000644000175000017500000002036210203035544026555 0ustar mathieumathieu/* * $RCSfile: BorderOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:16 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.BorderExtender; import javax.media.jai.BorderExtenderConstant; import javax.media.jai.ImageLayout; import javax.media.jai.IntegerSequence; import javax.media.jai.OpImage; import javax.media.jai.PlanarImage; import javax.media.jai.RasterFactory; import javax.media.jai.RasterFormatTag; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; import javax.media.jai.iterator.RandomIter; import javax.media.jai.iterator.RandomIterFactory; import javax.media.jai.operator.BorderDescriptor; import com.sun.media.jai.util.ImageUtil; /** * An OpImage implementing the "border" operation. * *

It adds a border around a source image. The size of the border * is specified by the left, right, top, and bottom padding parameters. * The border may be filled in a variety of ways specified by the * border type parameter, defined in * javax.media.jai.operator.BorderDescriptor: *

    *
  • it may be extended with zeros (BORDER_ZERO_FILL); *
  • it may be extended with a constant set of values (BORDER_CONST_FILL); * it may be created by reflection about the edges of the image * (BORDER_REFLECT); or, *
  • it may be extended by "wrapping" the image plane toroidally, * that is, joining opposite edges of the image. *
* *

When choosing the BORDER_CONST_FILL option, an array * of constants must be supplied. The array must have at least one element, * in which case this same constant is applied to all image bands. Or, * it may have a different constant entry for each cooresponding band. * For all other border types, this constants parameter may * be null. * *

The layout information for this image may be specified via the * layout parameter. However, due to the nature of this * operation, the minX, minY, width, * and height, if specified, will be ignored. They will * be calculated based on the source's dimensions and the padding values. * Likewise, the SampleModel and ColorModel hints * will be ignored. * * @see javax.media.jai.OpImage * @see javax.media.jai.operator.BorderDescriptor * @see BorderRIF * */ final class BorderOpImage extends OpImage { /** * The BorderExtender object used to extend the source data. */ protected BorderExtender extender; /** * Constructor. * * @param source The source image. * @param layout The destination image layout. * @param leftPad The amount of padding to the left of the source. * @param rightPad The amount of padding to the right of the source. * @param topPad The amount of padding to the top of the source. * @param bottomPad The amount of padding to the bottom of the source. * @param type The border type. * @param constants The constants used with border type * BorderDescriptor.BORDER_CONST_FILL, * stored as reference. */ public BorderOpImage(RenderedImage source, Map config, ImageLayout layout, int leftPad, int rightPad, int topPad, int bottomPad, BorderExtender extender) { super(vectorize(source), layoutHelper(layout, source, leftPad, rightPad, topPad, bottomPad), config, true); this.extender = extender; } /** * Sets up the image layout information for this Op. * The minX, minY, width, and height are calculated based on * the source's dimension and padding values. Any of these * values specified in the layout parameter is ignored. * All other variables are taken from the layout parameter or * inherited from the source. */ private static ImageLayout layoutHelper(ImageLayout layout, RenderedImage source, int leftPad, int rightPad, int topPad, int bottomPad) { ImageLayout il = layout == null ? new ImageLayout() : (ImageLayout)layout.clone(); // Set the image bounds according to the padding. il.setMinX(source.getMinX() - leftPad); il.setMinY(source.getMinY() - topPad); il.setWidth(source.getWidth() + leftPad + rightPad); il.setHeight(source.getHeight() + topPad + bottomPad); // Set tile grid offset to minimize the probability that a // tile's bounds does not intersect the source image bounds. if(!il.isValid(ImageLayout.TILE_GRID_X_OFFSET_MASK)) { il.setTileGridXOffset(il.getMinX(null)); } if (!il.isValid(ImageLayout.TILE_GRID_Y_OFFSET_MASK)) { il.setTileGridYOffset(il.getMinY(null)); } // Force inheritance of source image SampleModel and ColorModel. il.setSampleModel(source.getSampleModel()); il.setColorModel(source.getColorModel()); return il; } /** * Returns a conservative estimate of the destination region that * can potentially be affected by the pixels of a rectangle of a * given source. * * @param sourceRect the Rectangle in source coordinates. * @param sourceIndex the index of the source image. * @return a Rectangle indicating the potentially affected * destination region. or null if the region is unknown. * @throws IllegalArgumentException if the source index is * negative or greater than that of the last source. * @throws IllegalArgumentException if sourceRect is null. */ public Rectangle mapSourceRect(Rectangle sourceRect, int sourceIndex) { if ( sourceRect == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sourceIndex < 0 || sourceIndex >= getNumSources()) { throw new IllegalArgumentException(JaiI18N.getString("BorderOpImage0")); } return new Rectangle(sourceRect); } /** * Returns a conservative estimate of the region of a specified * source that is required in order to compute the pixels of a * given destination rectangle. * * @param destRect the Rectangle in destination coordinates. * @param sourceIndex the index of the source image. * @return a Rectangle indicating the required source region. * @throws IllegalArgumentException if the source index is * negative or greater than that of the last source. * @throws IllegalArgumentException if destRect is null. */ public Rectangle mapDestRect(Rectangle destRect, int sourceIndex) { if ( destRect == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sourceIndex < 0 || sourceIndex >= getNumSources()) { throw new IllegalArgumentException(JaiI18N.getString("BorderOpImage2")); } Rectangle srcBounds = getSourceImage(0).getBounds(); return destRect.intersection(srcBounds); } /** Computes the pixel values for the specified tile. */ public Raster computeTile(int tileX, int tileY) { // Create a new Raster. WritableRaster dest = createTile(tileX, tileY); // Extend the data. getSourceImage(0).copyExtendedData(dest, extender); return dest; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MultiplyOpImage.java0000644000175000017500000007121410203035544027161 0ustar mathieumathieu/* * $RCSfile: MultiplyOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:37 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PointOpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.RasterFactory; import java.util.Map; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.util.JDKWorkarounds; /// import com.sun.media.jai.test.OpImageTester; /** * An OpImage implementing the "Multiply" operation as * described in javax.media.jai.operator.MultiplyDescriptor. * *

This OpImage multiplies the pixel values of two source * images on a per-band basis. In case the two source images have different * number of bands, the number of bands for the destination image is the * smaller band number of the two source images. That is * dstNumBands = Math.min(src1NumBands, src2NumBands). * In case the two source images have different data types, the data type * for the destination image is the bigger data type of the two source * images. * *

The value of the pixel (x, y) in the destination image is defined as: *

 * for (b = 0; b < numBands; b++) {
 *     dst[y][x][b] = src1[y][x][b] * src2[y][x][b];
 * }
 * 
* *

If the result of the multiplication overflows/underflows the * maximum/minimum value supported by the destination image, then it * will be clamped to the maximum/minimum value respectively. The * data type byte is treated as unsigned, with maximum * value as 255 and minimum value as 0. * * @since EA2 * @see javax.media.jai.operator.MultiplyDescriptor * @see MultiplyCRIF * */ final class MultiplyOpImage extends PointOpImage { private byte[][] multiplyTableByte; /* Source 1 band increment */ private int s1bd = 1; /* Source 2 band increment */ private int s2bd = 1; /** * Constructs an MultiplyOpImage. * *

The layout parameter may optionally contains the * tile grid layout, sample model, and/or color model. The image * dimension is determined by the intersection of the bounding boxes * of the two source images. * *

The image layout of the first source image, source1, * is used as the fall-back for the image layout of the destination * image. Any layout parameters not specified in the layout * argument are set to the same value as that of source1. * * @param source1 The first source image. * @param source2 The second source image. * @param layout The destination image layout. */ public MultiplyOpImage(RenderedImage source1, RenderedImage source2, Map config, ImageLayout layout) { super(source1, source2, layout, config, true); // Get the source band counts. int numBands1 = source1.getSampleModel().getNumBands(); int numBands2 = source2.getSampleModel().getNumBands(); // Handle the special case of multiplying each band of an N-band // image by a 1-band image if the SampleModel. int numBandsDst; if(layout != null && layout.isValid(ImageLayout.SAMPLE_MODEL_MASK)) { SampleModel sm = layout.getSampleModel(null); numBandsDst = sm.getNumBands(); // One of the sources must be single-banded and the other must // have at most the number of bands in the SampleModel hint. if(numBandsDst > 1 && ((numBands1 == 1 && numBands2 > 1) || (numBands2 == 1 && numBands1 > 1))) { // Clamp the destination band count to the number of // bands in the multi-band source. numBandsDst = Math.min(Math.max(numBands1, numBands2), numBandsDst); // Create a new SampleModel if necessary. if(numBandsDst != sampleModel.getNumBands()) { sampleModel = RasterFactory.createComponentSampleModel( sm, sampleModel.getTransferType(), sampleModel.getWidth(), sampleModel.getHeight(), numBandsDst); if(colorModel != null && !JDKWorkarounds.areCompatibleDataModels(sampleModel, colorModel)) { colorModel = ImageUtil.getCompatibleColorModel(sampleModel, config); } } // Set the source band increments. s1bd = numBands1 == 1 ? 0 : 1; s2bd = numBands2 == 1 ? 0 : 1; } } if (sampleModel.getTransferType() == DataBuffer.TYPE_BYTE) { /* Initialize multiplyTableByte. */ multiplyTableByte = new byte[256][256]; for (int j = 0; j < 256; j++) { byte[] array = multiplyTableByte[j]; for (int i = 0; i < 256; i++) { array[i] = ImageUtil.clampByte(i * j); } } } // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * Multiplies the pixel values of two source images within a specified * rectangle. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); /* For PointOpImage, srcRect = destRect. */ RasterAccessor s1 = new RasterAccessor(sources[0], destRect, formatTags[0], getSource(0).getColorModel()); RasterAccessor s2 = new RasterAccessor(sources[1], destRect, formatTags[1], getSource(1).getColorModel()); RasterAccessor d = new RasterAccessor(dest, destRect, formatTags[2], getColorModel()); if(d.isBinary()) { byte[] src1Bits = s1.getBinaryDataArray(); byte[] src2Bits = s2.getBinaryDataArray(); byte[] dstBits = d.getBinaryDataArray(); int length = dstBits.length; for(int i = 0; i < length; i++) { // "Multiply" is equivalent to "And". dstBits[i] = (byte)(src1Bits[i] & src2Bits[i]); } d.copyBinaryDataToRaster(); return; } int src1LineStride = s1.getScanlineStride(); int src1PixelStride = s1.getPixelStride(); int[] src1BandOffsets = s1.getBandOffsets(); int src2LineStride = s2.getScanlineStride(); int src2PixelStride = s2.getPixelStride(); int[] src2BandOffsets = s2.getBandOffsets(); int dstNumBands = d.getNumBands(); int dstWidth = d.getWidth(); int dstHeight = d.getHeight(); int dstLineStride = d.getScanlineStride(); int dstPixelStride = d.getPixelStride(); int[] dstBandOffsets = d.getBandOffsets(); switch (d.getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(dstNumBands, dstWidth, dstHeight, src1LineStride, src1PixelStride, src1BandOffsets, s1.getByteDataArrays(), src2LineStride, src2PixelStride, src2BandOffsets, s2.getByteDataArrays(), dstLineStride, dstPixelStride, dstBandOffsets, d.getByteDataArrays()); break; case DataBuffer.TYPE_USHORT: ushortLoop(dstNumBands, dstWidth, dstHeight, src1LineStride, src1PixelStride, src1BandOffsets, s1.getShortDataArrays(), src2LineStride, src2PixelStride, src2BandOffsets, s2.getShortDataArrays(), dstLineStride, dstPixelStride, dstBandOffsets, d.getShortDataArrays()); break; case DataBuffer.TYPE_SHORT: shortLoop(dstNumBands, dstWidth, dstHeight, src1LineStride, src1PixelStride, src1BandOffsets, s1.getShortDataArrays(), src2LineStride, src2PixelStride, src2BandOffsets, s2.getShortDataArrays(), dstLineStride, dstPixelStride, dstBandOffsets, d.getShortDataArrays()); break; case DataBuffer.TYPE_INT: intLoop(dstNumBands, dstWidth, dstHeight, src1LineStride, src1PixelStride, src1BandOffsets, s1.getIntDataArrays(), src2LineStride, src2PixelStride, src2BandOffsets, s2.getIntDataArrays(), dstLineStride, dstPixelStride, dstBandOffsets, d.getIntDataArrays()); break; case DataBuffer.TYPE_FLOAT: floatLoop(dstNumBands, dstWidth, dstHeight, src1LineStride, src1PixelStride, src1BandOffsets, s1.getFloatDataArrays(), src2LineStride, src2PixelStride, src2BandOffsets, s2.getFloatDataArrays(), dstLineStride, dstPixelStride, dstBandOffsets, d.getFloatDataArrays()); break; case DataBuffer.TYPE_DOUBLE: doubleLoop(dstNumBands, dstWidth, dstHeight, src1LineStride, src1PixelStride, src1BandOffsets, s1.getDoubleDataArrays(), src2LineStride, src2PixelStride, src2BandOffsets, s2.getDoubleDataArrays(), dstLineStride, dstPixelStride, dstBandOffsets, d.getDoubleDataArrays()); break; } if (d.needsClamping()) { d.clampDataArrays(); } d.copyDataToRaster(); } private void byteLoop(int dstNumBands, int dstWidth, int dstHeight, int src1LineStride, int src1PixelStride, int[] src1BandOffsets, byte[][] src1Data, int src2LineStride, int src2PixelStride, int[] src2BandOffsets, byte[][] src2Data, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, byte[][] dstData) { for (int b = 0, s1b = 0, s2b = 0; b < dstNumBands; b++, s1b += s1bd, s2b += s2bd) { byte[] s1 = src1Data[s1b]; byte[] s2 = src2Data[s2b]; byte[] d = dstData[b]; int src1LineOffset = src1BandOffsets[s1b]; int src2LineOffset = src2BandOffsets[s2b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int src1PixelOffset = src1LineOffset; int src2PixelOffset = src2LineOffset; int dstPixelOffset = dstLineOffset; src1LineOffset += src1LineStride; src2LineOffset += src2LineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = multiplyTableByte[s1[src1PixelOffset]&0xFF][s2[src2PixelOffset]&0xFF]; src1PixelOffset += src1PixelStride; src2PixelOffset += src2PixelStride; dstPixelOffset += dstPixelStride; } } } } private void ushortLoop(int dstNumBands, int dstWidth, int dstHeight, int src1LineStride, int src1PixelStride, int[] src1BandOffsets, short[][] src1Data, int src2LineStride, int src2PixelStride, int[] src2BandOffsets, short[][] src2Data, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, short[][] dstData) { for (int b = 0, s1b = 0, s2b = 0; b < dstNumBands; b++, s1b += s1bd, s2b += s2bd) { short[] s1 = src1Data[s1b]; short[] s2 = src2Data[s2b]; short[] d = dstData[b]; int src1LineOffset = src1BandOffsets[s1b]; int src2LineOffset = src2BandOffsets[s2b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int src1PixelOffset = src1LineOffset; int src2PixelOffset = src2LineOffset; int dstPixelOffset = dstLineOffset; src1LineOffset += src1LineStride; src2LineOffset += src2LineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = ImageUtil.clampUShort((int)(s1[src1PixelOffset]&0xFFFF) * (int)(s2[src2PixelOffset]&0xFFFF)); src1PixelOffset += src1PixelStride; src2PixelOffset += src2PixelStride; dstPixelOffset += dstPixelStride; } } } } private void shortLoop(int dstNumBands, int dstWidth, int dstHeight, int src1LineStride, int src1PixelStride, int[] src1BandOffsets, short[][] src1Data, int src2LineStride, int src2PixelStride, int[] src2BandOffsets, short[][] src2Data, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, short[][] dstData) { for (int b = 0, s1b = 0, s2b = 0; b < dstNumBands; b++, s1b += s1bd, s2b += s2bd) { short[] s1 = src1Data[s1b]; short[] s2 = src2Data[s2b]; short[] d = dstData[b]; int src1LineOffset = src1BandOffsets[s1b]; int src2LineOffset = src2BandOffsets[s2b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int src1PixelOffset = src1LineOffset; int src2PixelOffset = src2LineOffset; int dstPixelOffset = dstLineOffset; src1LineOffset += src1LineStride; src2LineOffset += src2LineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = ImageUtil.clampShort((int)s1[src1PixelOffset] * (int)s2[src2PixelOffset]); src1PixelOffset += src1PixelStride; src2PixelOffset += src2PixelStride; dstPixelOffset += dstPixelStride; } } } } private void intLoop(int dstNumBands, int dstWidth, int dstHeight, int src1LineStride, int src1PixelStride, int[] src1BandOffsets, int[][] src1Data, int src2LineStride, int src2PixelStride, int[] src2BandOffsets, int[][] src2Data, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, int[][] dstData) { /* * The destination data type may be any of the integral data types. * The "clamp" function must clamp to the appropriate range for * that data type. */ switch (sampleModel.getTransferType()) { case DataBuffer.TYPE_BYTE: for (int b = 0, s1b = 0, s2b = 0; b < dstNumBands; b++, s1b += s1bd, s2b += s2bd) { int[] s1 = src1Data[s1b]; int[] s2 = src2Data[s2b]; int[] d = dstData[b]; int src1LineOffset = src1BandOffsets[s1b]; int src2LineOffset = src2BandOffsets[s2b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int src1PixelOffset = src1LineOffset; int src2PixelOffset = src2LineOffset; int dstPixelOffset = dstLineOffset; src1LineOffset += src1LineStride; src2LineOffset += src2LineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = ImageUtil.clampByte(s1[src1PixelOffset] * s2[src2PixelOffset]); src1PixelOffset += src1PixelStride; src2PixelOffset += src2PixelStride; dstPixelOffset += dstPixelStride; } } } break; case DataBuffer.TYPE_USHORT: for (int b = 0, s1b = 0, s2b = 0; b < dstNumBands; b++, s1b += s1bd, s2b += s2bd) { int[] s1 = src1Data[s1b]; int[] s2 = src2Data[s2b]; int[] d = dstData[b]; int src1LineOffset = src1BandOffsets[s1b]; int src2LineOffset = src2BandOffsets[s2b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int src1PixelOffset = src1LineOffset; int src2PixelOffset = src2LineOffset; int dstPixelOffset = dstLineOffset; src1LineOffset += src1LineStride; src2LineOffset += src2LineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = ImageUtil.clampUShort(s1[src1PixelOffset] * s2[src2PixelOffset]); src1PixelOffset += src1PixelStride; src2PixelOffset += src2PixelStride; dstPixelOffset += dstPixelStride; } } } break; case DataBuffer.TYPE_SHORT: for (int b = 0, s1b = 0, s2b = 0; b < dstNumBands; b++, s1b += s1bd, s2b += s2bd) { int[] s1 = src1Data[s1b]; int[] s2 = src2Data[s2b]; int[] d = dstData[b]; int src1LineOffset = src1BandOffsets[s1b]; int src2LineOffset = src2BandOffsets[s2b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int src1PixelOffset = src1LineOffset; int src2PixelOffset = src2LineOffset; int dstPixelOffset = dstLineOffset; src1LineOffset += src1LineStride; src2LineOffset += src2LineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = ImageUtil.clampShort(s1[src1PixelOffset] * s2[src2PixelOffset]); src1PixelOffset += src1PixelStride; src2PixelOffset += src2PixelStride; dstPixelOffset += dstPixelStride; } } } break; case DataBuffer.TYPE_INT: for (int b = 0, s1b = 0, s2b = 0; b < dstNumBands; b++, s1b += s1bd, s2b += s2bd) { int[] s1 = src1Data[s1b]; int[] s2 = src2Data[s2b]; int[] d = dstData[b]; int src1LineOffset = src1BandOffsets[s1b]; int src2LineOffset = src2BandOffsets[s2b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int src1PixelOffset = src1LineOffset; int src2PixelOffset = src2LineOffset; int dstPixelOffset = dstLineOffset; src1LineOffset += src1LineStride; src2LineOffset += src2LineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = ImageUtil.clampInt((long)s1[src1PixelOffset] * (long)s2[src2PixelOffset]); src1PixelOffset += src1PixelStride; src2PixelOffset += src2PixelStride; dstPixelOffset += dstPixelStride; } } } break; } } private void floatLoop(int dstNumBands, int dstWidth, int dstHeight, int src1LineStride, int src1PixelStride, int[] src1BandOffsets, float[][] src1Data, int src2LineStride, int src2PixelStride, int[] src2BandOffsets, float[][] src2Data, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, float[][] dstData) { for (int b = 0, s1b = 0, s2b = 0; b < dstNumBands; b++, s1b += s1bd, s2b += s2bd) { float[] s1 = src1Data[s1b]; float[] s2 = src2Data[s2b]; float[] d = dstData[b]; int src1LineOffset = src1BandOffsets[s1b]; int src2LineOffset = src2BandOffsets[s2b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int src1PixelOffset = src1LineOffset; int src2PixelOffset = src2LineOffset; int dstPixelOffset = dstLineOffset; src1LineOffset += src1LineStride; src2LineOffset += src2LineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = s1[src1PixelOffset] * s2[src2PixelOffset]; src1PixelOffset += src1PixelStride; src2PixelOffset += src2PixelStride; dstPixelOffset += dstPixelStride; } } } } private void doubleLoop(int dstNumBands, int dstWidth, int dstHeight, int src1LineStride, int src1PixelStride, int[] src1BandOffsets, double[][] src1Data, int src2LineStride, int src2PixelStride, int[] src2BandOffsets, double[][] src2Data, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, double[][] dstData) { for (int b = 0, s1b = 0, s2b = 0; b < dstNumBands; b++, s1b += s1bd, s2b += s2bd) { double[] s1 = src1Data[s1b]; double[] s2 = src2Data[s2b]; double[] d = dstData[b]; int src1LineOffset = src1BandOffsets[s1b]; int src2LineOffset = src2BandOffsets[s2b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int src1PixelOffset = src1LineOffset; int src2PixelOffset = src2LineOffset; int dstPixelOffset = dstLineOffset; src1LineOffset += src1LineStride; src2LineOffset += src2LineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = s1[src1PixelOffset] * s2[src2PixelOffset]; src1PixelOffset += src1PixelStride; src2PixelOffset += src2PixelStride; dstPixelOffset += dstPixelStride; } } } } // public static void main(String args[]) { // System.out.println("MultiplyOpImage Test"); // ImageLayout layout; // OpImage src1, src2, dst; // Rectangle rect = new Rectangle(0, 0, 5, 5); // System.out.println("1. PixelInterleaved byte 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 800, 800, 0, 0, 200, 200, DataBuffer.TYPE_BYTE, 3, false); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new MultiplyOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("2. Banded byte 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 800, 800, 0, 0, 200, 200, DataBuffer.TYPE_BYTE, 3, true); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new MultiplyOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("3. PixelInterleaved int 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_INT, 3, false); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new MultiplyOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("4. Banded int 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_INT, 3, true); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new MultiplyOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("5. PixelInterleaved float 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_FLOAT, 3, false); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new MultiplyOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("6. Banded float 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_FLOAT, 3, true); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new MultiplyOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("7. PixelInterleaved double 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_DOUBLE, 3, false); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new MultiplyOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("8. Banded double 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_DOUBLE, 3, true); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new MultiplyOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/PatternOpImage.java0000644000175000017500000000536410203035544026762 0ustar mathieumathieu/* * $RCSfile: PatternOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:39 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.Point; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.SourcelessOpImage; import javax.media.jai.RasterFactory; /** * An OpImage class to generate a repeating pattern of pixels. * *

PatternOpImage defines an image consisting of a repeated * pattern. The pattern is stored internally as a Raster, and * translated versions of the master tile (sharing the same * DataBuffer) are returned by computeTile(). * */ // public since ../test/OpImageTester.java uses it public class PatternOpImage extends SourcelessOpImage { /** The master tile (0, 0) containing the pattern. */ protected Raster pattern; /** Set up image layout. */ private static ImageLayout layoutHelper(Raster pattern, ColorModel colorModel) { return new ImageLayout(pattern.getMinX(), pattern.getMinY(), pattern.getWidth(), pattern.getHeight(), pattern.getSampleModel(), colorModel); } /** * Constructs a PatternOpImage from a Raster. * * @param pattern The Raster pattern to be repeated. * @param colorModel The output image ColorModel. * @param width The output image width. * @param height The output image height. */ public PatternOpImage(Raster pattern, ColorModel colorModel, int minX, int minY, int width, int height) { super(layoutHelper(pattern, colorModel), null, pattern.getSampleModel(), minX, minY, width, height); this.pattern = pattern; } public Raster getTile(int tileX, int tileY) { return computeTile(tileX,tileY); } /** * Returns a suitably translated version of the pattern tile * for reading. * * @param tileX the X index of the tile * @param tileY the Y index of the tile */ public Raster computeTile(int tileX, int tileY) { return pattern.createChild(tileGridXOffset, tileGridYOffset, tileWidth, tileHeight, tileXToX(tileX), tileYToY(tileY), null); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/com.sun.media.jai.opimage.properties0000644000175000017500000001403410203035544032172 0ustar mathieumathieu# # $RCSfile: com.sun.media.jai.opimage.properties,v $ # # Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. # # Use is subject to license terms. # # $Revision: 1.1 $ # $Date: 2005-02-11 04:56:48 $ # $State: Exp $ # Generic0=The input argument(s) may not be null. Generic1=The sourceIndex must be non-negative and less than the number of sources this image has. Generic3=Unkown data type. AWTImageOpImage0=InterruptedException occurred while loading the AWT image using MediaTracker. AWTImageOpImage1=MediaTracker is unable to load the AWT image. AWTImageOpImage2=Grabbing pixels aborted. AWTImageOpImage3= : grabbing pixels failed with this status. AWTImageOpImage4=InterruptedException occurred while attempting to grab pixels. AffineOpImage0=Affine Transform cannot be inverted. AffineRIF0=This interpolation is not yet handled. AffineNotInvertible=The affine transformation is not invertible. AreaOpImage0=The user-supplied image bounds do not intersect the source bounds. BinarizeOpImage0=Source image must have 1 band only. BinarizeOpImage1=Unsupported data type. BorderOpImage0=sourceIndex must be positive and less than the number of sources. CodecRIFUtil0=Cannot recover the memory error CodecRIFUtil1=Cannot decode the image for the type : ColorQuantizerRIF0=Only 3-band byte-type image is supported. ComplexArithmeticOpImage0=Unsupported data type. ConjugateOpImage0=Unsupported data type. Convolve3x3OpImage0=Convolve3x3 only works on 3x3 kernels. Convolve3x3OpImage1=Support for ushort/float/double data not implemented yet. CropOpImage0=sourceIndex must be positive and less than the number of sources. DCTOpImage0=FCT length must be a positive power of 2. DCTOpImage1= : Unexpected data type; should be float or double only. DFTOpImage0=Unsupported number of source bands. DFTOpImage1=Unsupported DFT type real->real (1x1). ErrorDiffusionOpImage0=Unsupported data type. EncodeRIF0=Null ImageEncoder encountered. EncodeRIF1=Cannot encode the provided image into the format : FFT0=FFT length must be a positive power of 2. FFT1=Unknown DFT scaling type. FFT2= : Unexpected data type; should be float or double only. FFT3=Array length must be greater or equal to this. FileLoadRIF0=Cannot find the file : FileLoadRIF1=Cannot create the rendering of the FileLoad operation. FileStoreRIF0=Cannot create the rendering of the FileStore operation. FilteredSubsample0=Invalid resampleType. FilteredSubsample1=Non-positive subsample factor. FilteredSubsample2=Non-positive filter size. FilteredSubsample3=Non-supported interpolation. FilteredSubsample4=Non-zero source index. FilteredSubsample5=Non-supported pixel data type. Generic0=The input argument(s) may not be null. Generic1=Image source index must be non-negative and no greater than a maximum index. HistogramRIF0=Fails to create the rendering of the Histogram operation IIPCRIF0=Incompatible matrix dimensions for multiplication. IIPCRIF1=Matrix and vector are incompatible for band-combine matrix creation. IIPCRIF2=Vector for band-combine matrix must have one column. IIPCRIF3=Destination rectangle of interest is empty. IIPCRIF4=Server-side processing not yet implemented. IIPCRIF5=Source rectangle of interest does not intersect source image. IIPCRIF6=Error inverting an AffineTransform. IIPCRIF7=Cannot read the image from the URL : IIPResolutionOpImage0=IIP Error: IIPResolutionOpImage1=Error reading tile data header. IIPResolutionOpImage2=Error reading tile data. IIPResolutionOpImage3=Error decoding JPEG stream. IIPResolutionOpImage4=Cannot open the URL : IIPResolutionOpImage5=Cannot get the label IIPResolutionOpImage6=Exception occurs when check an error. IIPResolutionOpImage7=Exception occurs when read data. IIPResolutionOpImage8=Exception occurs when flush data. ImageFunctionRIF0=Number of bands in the specified SampleModel is inconsistent with ImageFunction parameter. InvertOpImage0=Float/Double data type handling is not yet implemented. JPEGOpImage0=JPEGOpImage: Unable to process image stream, incorrect format. JPEGOpImage1=Unable to process image stream, IO error. JPEGOpImage2=Decoder returns sample model type other than PixelInterleavedSampleModel, 1 or 3 bands, data type byte. MagnitudePhaseOpImage0=Unsupported data type. MosaicOpImage0=The number of data bands must equal the number of color components for all source images. MosaicOpImage1=All bands of all sources images must have the same sample size. MosaicOpImage2=All source images must have the same data type. MosaicOpImage3=All source images must have the same number of bands. MosaicOpImage4=Alpha images must be single-banded. MosaicOpImage5=Alpha images must have the same data type as the sources. MosaicOpImage6=Alpha images must have the same sample size as the sources. MosaicOpImage7=The ImageLayout must contain a non-null SampleModel. MosaicOpImage8=The ImageLayout must be non-null and contain valid width, height and SampleModel. OrderedDitherOpImage0=Unsupported output data type. OrderedDitherOpImage1=Unsupported data type specified. PeriodicShiftOpImage0=sourceIndex must be positive and less than the number of sources. PolarToComplexOpImage0=Unsupported data type. RGBToIHS0=If the source has no color model, it must be 3-banded. RGBToIHS1=If the source has color model, it is must in RGB color space. RGBToIHS2=The source must be an RGB or ARGB image. RotateRIF0= : Interpolation is not supported for the Rotate operation. RenderableCRIF0=Downsampler must reduce both the width and height at each invocation. StreamRIF0=IOException occurs when seek the stream head. StreamRIF1=IOException occurs when get the stream position. StreamRIF2=IOException occurs when decode the image. SubsampleAverageOpImage0=scaleX <= 0.0 || scaleX > 1.0. SubsampleAverageOpImage1=scaleY <= 0.0 || scaleY > 1.0. SubsampleBinaryToGrayOpImage0=Unsupported output data type. ThresholdOpImage0=One of the parameters is empty. ThresholdOpImage1=Number of bands in the parameters is not consistent. TranslateIntOpImage0=sourceIndex must be positive and less than the number of sources. UnsharpMaskOpImage0=Unsupported data type. URLRIF0=IOException occurs when create the rendering of the URL operation. jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/AffineCRIF.java0000644000175000017500000003751710203035544025744 0ustar mathieumathieu/* * $RCSfile: AffineCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:13 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.image.DataBuffer; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.renderable.RenderableImage; import java.awt.image.renderable.RenderableImageOp; import java.awt.image.renderable.RenderContext; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationBicubic; import javax.media.jai.InterpolationBicubic2; import javax.media.jai.InterpolationBilinear; import javax.media.jai.InterpolationNearest; import javax.media.jai.InterpolationTable; import javax.media.jai.PlanarImage; import javax.media.jai.RenderedOp; import javax.media.jai.TileCache; import javax.media.jai.CRIFImpl; import java.util.Map; import java.awt.geom.Rectangle2D; import java.awt.geom.Point2D; /** * @since EA4 * @see AffineOpimage, ScaleOpImage */ public class AffineCRIF extends CRIFImpl { private static final float TOLERANCE = 0.01F; /** Constructor. */ public AffineCRIF() { super("affine"); } /** * Creates an affine operation as an instance of AffineOpImage. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); // Get TileCache from renderHints if any. TileCache cache = RIFUtil.getTileCacheHint(renderHints); // Get BorderExtender from renderHints if any. BorderExtender extender = RIFUtil.getBorderExtenderHint(renderHints); RenderedImage source = paramBlock.getRenderedSource(0); Object arg0 = paramBlock.getObjectParameter(0); AffineTransform transform = (AffineTransform)arg0; Object arg1 = paramBlock.getObjectParameter(1); Interpolation interp = (Interpolation)arg1; double[] backgroundValues = (double[]) paramBlock.getObjectParameter(2); SampleModel sm = source.getSampleModel(); boolean isBinary = (sm instanceof MultiPixelPackedSampleModel) && (sm.getSampleSize(0) == 1) && (sm.getDataType() == DataBuffer.TYPE_BYTE || sm.getDataType() == DataBuffer.TYPE_USHORT || sm.getDataType() == DataBuffer.TYPE_INT); // Get the affine transform double tr[]; tr = new double[6]; transform.getMatrix(tr); // // Check and see if the affine transform is doing a copy. // If so call the copy operation. // if ((tr[0] == 1.0) && (tr[3] == 1.0) && (tr[2] == 0.0) && (tr[1] == 0.0) && (tr[4] == 0.0) && (tr[5] == 0.0)) { // It's a copy return new CopyOpImage(source, renderHints, layout); } // // Check and see if the affine transform is in fact doing // a Translate operation. That is a scale by 1 and no rotation. // In which case call translate. Note that only integer translate // is applicable. For non-integer translate we'll have to do the // affine. // If the hints contain an ImageLayout hint, we can't use // TranslateIntOpImage since it isn't capable of dealing with that. if ((tr[0] == 1.0) && (tr[3] == 1.0) && (tr[2] == 0.0) && (tr[1] == 0.0) && (Math.abs(tr[4] - (int) tr[4]) < TOLERANCE) && (Math.abs(tr[5] - (int) tr[5]) < TOLERANCE) && layout == null) { // It's a integer translate return new TranslateIntOpImage(source, renderHints, (int) tr[4], (int) tr[5]); } // // Check and see if the affine transform is in fact doing // a Scale operation. In which case call Scale which is more // optimized than Affine. // if ((tr[0] > 0.0) && (tr[2] == 0.0) && (tr[1] == 0.0) && (tr[3] > 0.0)) { // It's a scale if (interp instanceof InterpolationNearest) { if (isBinary) { return new ScaleNearestBinaryOpImage(source, extender, renderHints, layout, (float)tr[0], (float)tr[3], (float)tr[4], (float)tr[5], interp); } else { return new ScaleNearestOpImage(source, extender, renderHints, layout, (float)tr[0], // xScale (float)tr[3], // yScale (float)tr[4], // xTrans (float)tr[5], // yTrans interp); } } else if (interp instanceof InterpolationBilinear) { if (isBinary) { return new ScaleBilinearBinaryOpImage(source, extender, renderHints, layout, (float)tr[0], (float)tr[3], (float)tr[4], (float)tr[5], interp); } else { return new ScaleBilinearOpImage(source, extender, renderHints, layout, (float)tr[0], // xScale (float)tr[3], // yScale (float)tr[4], // xTrans (float)tr[5], // yTrans interp); } } else if ((interp instanceof InterpolationBicubic) || (interp instanceof InterpolationBicubic2)) { return new ScaleBicubicOpImage(source, extender, renderHints, layout, (float)tr[0], // xScale (float)tr[3], // yScale (float)tr[4], // xTrans (float)tr[5], // yTrans interp); } else { return new ScaleGeneralOpImage(source, extender, renderHints, layout, (float)tr[0], // xScale (float)tr[3], // yScale (float)tr[4], // xTrans (float)tr[5], // yTrans interp); } } // Have to do Affine if (interp instanceof InterpolationNearest) { if (isBinary) { return new AffineNearestBinaryOpImage(source, extender, renderHints, layout, transform, interp, backgroundValues); } else { return new AffineNearestOpImage(source, extender, renderHints, layout, transform, interp, backgroundValues); } } else if (interp instanceof InterpolationBilinear) { return new AffineBilinearOpImage(source, extender, renderHints, layout, transform, interp, backgroundValues); } else if (interp instanceof InterpolationBicubic) { return new AffineBicubicOpImage(source, extender, renderHints, layout, transform, interp, backgroundValues); } else if (interp instanceof InterpolationBicubic2) { return new AffineBicubic2OpImage(source, extender, renderHints, layout, transform, interp, backgroundValues); } else { return new AffineGeneralOpImage(source, extender, renderHints, layout, transform, interp, backgroundValues); } } /** * Creates a new instance of AffineOpImage * in the renderable layer. This method satisfies the * implementation of CRIF. */ public RenderedImage create(RenderContext renderContext, ParameterBlock paramBlock) { return paramBlock.getRenderedSource(0); } /** * Maps the output RenderContext into the RenderContext for the ith * source. * This method satisfies the implementation of CRIF. * * @param i The index of the source image. * @param renderContext The renderContext being applied to the operation. * @param paramBlock The ParameterBlock containing the sources * and the translation factors. * @param image The RenderableImageOp from which this method * was called. */ public RenderContext mapRenderContext(int i, RenderContext renderContext, ParameterBlock paramBlock, RenderableImage image) { Object arg0 = paramBlock.getObjectParameter(0); AffineTransform affine = (AffineTransform)arg0; RenderContext RC = (RenderContext)renderContext.clone(); AffineTransform usr2dev = RC.getTransform(); usr2dev.concatenate(affine); RC.setTransform(usr2dev); return RC; } /** * Gets the bounding box for the output of AffineOpImage. * This method satisfies the implementation of CRIF. */ public Rectangle2D getBounds2D(ParameterBlock paramBlock) { RenderableImage source = paramBlock.getRenderableSource(0); Object arg0 = paramBlock.getObjectParameter(0); AffineTransform forward_tr = (AffineTransform)arg0; Object arg1 = paramBlock.getObjectParameter(1); Interpolation interp = (Interpolation)arg1; // Get the affine transform double tr[]; tr = new double[6]; forward_tr.getMatrix(tr); // // Check and see if the affine transform is doing a copy. // if ((tr[0] == 1.0) && (tr[3] == 1.0) && (tr[2] == 0.0) && (tr[1] == 0.0) && (tr[4] == 0.0) && (tr[5] == 0.0)) { return new Rectangle2D.Float(source.getMinX(), source.getMinY(), source.getWidth(), source.getHeight()); } // // Check and see if the affine transform is in fact doing // a Translate operation. // if ((tr[0] == 1.0) && (tr[3] == 1.0) && (tr[2] == 0.0) && (tr[1] == 0.0) && (Math.abs(tr[4] - (int) tr[4]) < TOLERANCE) && (Math.abs(tr[5] - (int) tr[5]) < TOLERANCE)) { return new Rectangle2D.Float(source.getMinX() + (float)tr[4], source.getMinY() + (float)tr[5], source.getWidth(), source.getHeight()); } // // Check and see if the affine transform is in fact doing // a Scale operation. // if ((tr[0] > 0.0) && (tr[2] == 0.0) && (tr[1] == 0.0) && (tr[3] > 0.0)) { // Get the source dimensions float x0 = (float)source.getMinX(); float y0 = (float)source.getMinY() ; float w = (float)source.getWidth(); float h = (float)source.getHeight(); // Forward map the source using x0, y0, w and h float d_x0 = x0 * (float)tr[0] + (float)tr[4]; float d_y0 = y0 * (float)tr[3] + (float)tr[5]; float d_w = w * (float)tr[0]; float d_h = h * (float)tr[3]; return new Rectangle2D.Float(d_x0, d_y0, d_w, d_h); } // It's an Affine // // Get sx0,sy0 coordinates and width & height of the source // float sx0 = (float) source.getMinX(); float sy0 = (float) source.getMinY(); float sw = (float) source.getWidth(); float sh = (float) source.getHeight(); // // The 4 points (clockwise order) are // (sx0, sy0), (sx0+sw, sy0) // (sx0, sy0+sh), (sx0+sw, sy0+sh) // Point2D[] pts = new Point2D[4]; pts[0] = new Point2D.Float(sx0, sy0); pts[1] = new Point2D.Float((sx0+sw), sy0); pts[2] = new Point2D.Float((sx0+sw), (sy0+sh)); pts[3] = new Point2D.Float(sx0, (sy0+sh)); // Forward map forward_tr.transform(pts, 0, pts, 0, 4); float dx0 = Float.MAX_VALUE; float dy0 = Float.MAX_VALUE; float dx1 = -Float.MAX_VALUE; float dy1 = -Float.MAX_VALUE; for (int i = 0; i < 4; i++) { float px = (float)pts[i].getX(); float py = (float)pts[i].getY(); dx0 = Math.min(dx0, px); dy0 = Math.min(dy0, py); dx1 = Math.max(dx1, px); dy1 = Math.max(dy1, py); } // // Get the width & height of the resulting bounding box. // This is set on the layout // float lw = dx1 - dx0; float lh = dy1 - dy0; return new Rectangle2D.Float(dx0, dy0, lw, lh); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/NeuQuantOpImage.java0000644000175000017500000003742610240004133027100 0ustar mathieumathieu/* * $RCSfile: NeuQuantOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-05-10 01:03:23 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Image; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.util.ArrayList; import java.util.Hashtable; import java.util.LinkedList; import java.util.ListIterator; import java.util.Map; import javax.media.jai.ImageLayout; import javax.media.jai.LookupTableJAI; import javax.media.jai.OpImage; import javax.media.jai.PixelAccessor; import javax.media.jai.PlanarImage; import javax.media.jai.iterator.RandomIter; import javax.media.jai.iterator.RandomIterFactory; import javax.media.jai.ROI; import javax.media.jai.ROIShape; import javax.media.jai.UnpackedImageData; import com.sun.media.jai.util.ImageUtil; /** * An OpImage implementing the "ColorQuantizer" operation as * described in javax.media.jai.operator.ExtremaDescriptor * based on the median-cut algorithm. * * This is based on a java-version of Anthony Dekker's implementation of * NeuQuant Neural-Net Quantization Algorithm * * NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994. * See "Kohonen neural networks for optimal colour quantization" * in "Network: Computation in Neural Systems" Vol. 5 (1994) pp 351-367. * for a discussion of the algorithm. * * Any party obtaining a copy of these files from the author, directly or * indirectly, is granted, free of charge, a full and unrestricted irrevocable, * world-wide, paid up, royalty-free, nonexclusive right and license to deal * in this software and documentation files (the "Software"), including without * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons who receive * copies from any such party to do so, with the only requirement being * that this copyright notice remain intact. * * @see javax.media.jai.operator.ExtremaDescriptor * @see ExtremaCRIF */ public class NeuQuantOpImage extends ColorQuantizerOpImage { /** four primes near 500 - assume no image has a length so large * that it is divisible by all four primes */ protected static final int prime1 = 499; protected static final int prime2 = 491; protected static final int prime3 = 487; protected static final int prime4 = 503; /* minimum size for input image */ protected static final int minpicturebytes = (3 * prime4); /** The size of the histogram. */ private int ncycles; /* Program Skeleton ---------------- [select samplefac in range 1..30] [read image from input file] pic = (unsigned char*) malloc(3*width*height); initnet(pic,3*width*height,samplefac); learn(); unbiasnet(); [write output image header, using writecolourmap(f)] inxbuild(); write output image using inxsearch(b,g,r) */ /* Network Definitions ------------------- */ private final int maxnetpos = maxColorNum - 1; private final int netbiasshift = 4; /* bias for colour values */ /* defs for freq and bias */ private final int intbiasshift = 16; /* bias for fractions */ private final int intbias = 1 << intbiasshift; private final int gammashift = 10; /* gamma = 1024 */ private final int gamma = 1 << gammashift; private final int betashift = 10; private final int beta = intbias >> betashift; /* beta = 1/1024 */ private final int betagamma = intbias << (gammashift - betashift); /* defs for decreasing radius factor */ private final int initrad = maxColorNum >> 3; private final int radiusbiasshift = 6; /* at 32.0 biased by 6 bits */ private final int radiusbias = 1 << radiusbiasshift; private final int initradius = initrad * radiusbias; /* and decreases by a */ private final int radiusdec = 30; /* factor of 1/30 each cycle */ /* defs for decreasing alpha factor */ private final int alphabiasshift = 10; /* alpha starts at 1.0 */ private final int initalpha = 1 << alphabiasshift; private int alphadec; /* biased by 10 bits */ /* radbias and alpharadbias used for radpower calculation */ private final int radbiasshift = 8; private final int radbias = 1 << radbiasshift; private final int alpharadbshift = alphabiasshift + radbiasshift; private final int alpharadbias = 1 << alpharadbshift; // typedef int pixel[4]; /* BGRc */ private int[][] network; /* the network itself - [maxColorNum][4] */ private int[] netindex = new int[256]; /* for network lookup - really 256 */ private int[] bias = new int[maxColorNum]; /* bias and freq arrays for learning */ private int[] freq = new int[maxColorNum]; private int[] radpower = new int[initrad]; /* radpower for precomputation */ /** * Constructs an NeuQuantOpImage. * * @param source The source image. */ public NeuQuantOpImage(RenderedImage source, Map config, ImageLayout layout, int maxColorNum, int upperBound, ROI roi, int xPeriod, int yPeriod) { super(source, config, layout, maxColorNum, roi, xPeriod, yPeriod); colorMap = null; this.ncycles = upperBound; } protected synchronized void train() { // intialize the network network = new int[maxColorNum][]; for (int i = 0; i < maxColorNum; i++) { network[i] = new int[4]; int[] p = network[i]; p[0] = p[1] = p[2] = (i << (netbiasshift + 8)) / maxColorNum; freq[i] = intbias / maxColorNum; /* 1/maxColorNum */ bias[i] = 0; } PlanarImage source = getSourceImage(0); Rectangle rect = source.getBounds(); if (roi != null) rect = roi.getBounds(); RandomIter iterator = RandomIterFactory.create(source, rect); int samplefac = xPeriod * yPeriod; int startX = rect.x / xPeriod; int startY = rect.y / yPeriod; int offsetX = rect.x % xPeriod; int offsetY = rect.y % yPeriod; int pixelsPerLine = (rect.width - 1) / xPeriod + 1; int numSamples = pixelsPerLine * ((rect.height - 1) / yPeriod + 1); if (numSamples < minpicturebytes) samplefac = 1; alphadec = 30 + ((samplefac - 1) / 3); int pix = 0; int delta = numSamples / ncycles; int alpha = initalpha; int radius = initradius; int rad = radius >> radiusbiasshift; if (rad <= 1) rad = 0; for (int i = 0; i < rad; i++) radpower[i] = alpha * (((rad * rad - i * i) * radbias) / (rad * rad)); int step; if (numSamples < minpicturebytes) step = 3; else if ((numSamples % prime1) != 0) step = 3 * prime1; else { if ((numSamples % prime2) != 0) step = 3 * prime2; else { if ((numSamples % prime3) != 0) step = 3 * prime3; else step = 3 * prime4; } } int[] pixel = new int[3]; for (int i = 0; i < numSamples;) { int y = (pix / pixelsPerLine + startY) * yPeriod + offsetY; int x = (pix % pixelsPerLine + startX) * xPeriod + offsetX; try { iterator.getPixel(x, y, pixel); } catch (Exception e) { continue; } int b = pixel[2] << netbiasshift; int g = pixel[1] << netbiasshift; int r = pixel[0] << netbiasshift; int j = contest(b , g, r); altersingle(alpha, j, b , g, r); if (rad != 0) alterneigh(rad, j, b , g, r); /* alter neighbours */ pix += step; if (pix >= numSamples) pix -= numSamples; i++; if (i % delta == 0) { alpha -= alpha / alphadec; radius -= radius / radiusdec; rad = radius >> radiusbiasshift; if (rad <= 1) rad = 0; for (j = 0; j < rad; j++) radpower[j] = alpha * (((rad * rad - j * j) * radbias) / (rad * rad)); } } unbiasnet(); inxbuild(); createLUT(); setProperty("LUT", colorMap); setProperty("JAI.LookupTable", colorMap); } private void createLUT() { colorMap = new LookupTableJAI(new byte[3][maxColorNum]); byte[][] map = colorMap.getByteData(); int[] index = new int[maxColorNum]; for (int i = 0; i < maxColorNum; i++) index[network[i][3]] = i; for (int i = 0; i < maxColorNum; i++) { int j = index[i]; map[2][i] = (byte) (network[j][0]); map[1][i] = (byte) (network[j][1]); map[0][i] = (byte) (network[j][2]); } } /** Insertion sort of network and building of netindex[0..255] * (to do after unbias) */ private void inxbuild() { int previouscol = 0; int startpos = 0; for (int i = 0; i < maxColorNum; i++) { int[] p = network[i]; int smallpos = i; int smallval = p[1]; /* index on g */ /* find smallest in i..maxColorNum-1 */ int j; for (j = i + 1; j < maxColorNum; j++) { int[] q = network[j]; if (q[1] < smallval) { /* index on g */ smallpos = j; smallval = q[1]; /* index on g */ } } int[] q = network[smallpos]; /* swap p (i) and q (smallpos) entries */ if (i != smallpos) { j = q[0]; q[0] = p[0]; p[0] = j; j = q[1]; q[1] = p[1]; p[1] = j; j = q[2]; q[2] = p[2]; p[2] = j; j = q[3]; q[3] = p[3]; p[3] = j; } /* smallval entry is now in position i */ if (smallval != previouscol) { netindex[previouscol] = (startpos + i) >> 1; for (j = previouscol + 1; j < smallval; j++) netindex[j] = i; previouscol = smallval; startpos = i; } } netindex[previouscol] = (startpos + maxnetpos) >> 1; for (int j = previouscol + 1; j < 256; j++) netindex[j] = maxnetpos; /* really 256 */ } /** Search for BGR values 0..255 (after net is unbiased) and * return colour index */ protected byte findNearestEntry(int r, int g, int b) { int bestd = 1000; /* biggest possible dist is 256*3 */ int best = -1; int i = netindex[g]; /* index on g */ int j = i - 1; /* start at netindex[g] and work outwards */ while (i < maxColorNum || j >= 0) { if (i < maxColorNum) { int[] p = network[i]; int dist = p[1] - g; /* inx key */ if (dist >= bestd) i = maxColorNum; /* stop iter */ else { i++; if (dist < 0) dist = -dist; int a = p[0] - b; if (a < 0) a = -a; dist += a; if (dist < bestd) { a = p[2] - r; if (a < 0) a = -a; dist += a; if (dist < bestd) { bestd = dist; best = p[3]; } } } } if (j >= 0) { int[] p = network[j]; int dist = g - p[1]; /* inx key - reverse dif */ if (dist >= bestd) j = -1; /* stop iter */ else { j--; if (dist < 0) dist = -dist; int a = p[0] - b; if (a < 0) a = -a; dist += a; if (dist < bestd) { a = p[2] - r; if (a < 0) a = -a; dist += a; if (dist < bestd) { bestd = dist; best = p[3]; } } } } } return (byte)best; } /** Unbias network to give byte values 0..255 and record * position i to prepare for sort. */ private void unbiasnet() { for (int i = 0; i < maxColorNum; i++) { network[i][0] >>= netbiasshift; network[i][1] >>= netbiasshift; network[i][2] >>= netbiasshift; network[i][3] = i; /* record colour no */ } } /** Move adjacent neurons by precomputed * alpha*(1-((i-j)^2/[r]^2)) in radpower[|i-j|] */ private void alterneigh(int rad, int i, int b, int g, int r) { int lo = i - rad; if (lo < -1) lo = -1; int hi = i + rad; if (hi > maxColorNum) hi = maxColorNum; int j = i + 1; int k = i - 1; int m = 1; while ((j < hi) || (k > lo)) { int a = radpower[m++]; if (j < hi) { int[] p = network[j++]; // try { p[0] -= (a * (p[0] - b)) / alpharadbias; p[1] -= (a * (p[1] - g)) / alpharadbias; p[2] -= (a * (p[2] - r)) / alpharadbias; // } catch (Exception e) {} // prevents 1.3 miscompilation } if (k > lo) { int[] p = network[k--]; // try { p[0] -= (a * (p[0] - b)) / alpharadbias; p[1] -= (a * (p[1] - g)) / alpharadbias; p[2] -= (a * (p[2] - r)) / alpharadbias; // } catch (Exception e) {} } } } /** Move neuron i towards biased (b,g,r) by factor alpha. */ private void altersingle(int alpha, int i, int b, int g, int r) { /* alter hit neuron */ int[] n = network[i]; n[0] -= (alpha * (n[0] - b)) / initalpha; n[1] -= (alpha * (n[1] - g)) / initalpha; n[2] -= (alpha * (n[2] - r)) / initalpha; } /** Search for biased BGR values. */ private int contest(int b, int g, int r) { /* finds closest neuron (min dist) and updates freq */ /* finds best neuron (min dist-bias) and returns position */ /* for frequently chosen neurons, freq[i] is high and bias[i] is negative */ /* bias[i] = gamma*((1/maxColorNum)-freq[i]) */ int bestd = ~(((int) 1) << 31); int bestbiasd = bestd; int bestpos = -1; int bestbiaspos = bestpos; for (int i = 0; i < maxColorNum; i++) { int[] n = network[i]; int dist = n[0] - b; if (dist < 0) dist = -dist; int a = n[1] - g; if (a < 0) a = -a; dist += a; a = n[2] - r; if (a < 0) a = -a; dist += a; if (dist < bestd) { bestd = dist; bestpos = i; } int biasdist = dist - ((bias[i]) >> (intbiasshift - netbiasshift)); if (biasdist < bestbiasd) { bestbiasd = biasdist; bestbiaspos = i; } int betafreq = (freq[i] >> betashift); freq[i] -= betafreq; bias[i] += (betafreq << gammashift); } freq[bestpos] += beta; bias[bestpos] -= betagamma; return (bestbiaspos); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/IIPResolutionOpImage.java0000644000175000017500000011740310203035544030050 0ustar mathieumathieu/* * $RCSfile: IIPResolutionOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:28 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Transparency; import java.awt.color.ColorSpace; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.ColorModel; import java.awt.image.ComponentColorModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.io.InputStream; import java.io.ByteArrayInputStream; import java.net.URL; import java.util.Vector; import javax.media.jai.ImageLayout; import javax.media.jai.JAI; import javax.media.jai.OpImage; import javax.media.jai.RasterFactory; import javax.media.jai.util.ImagingException; import javax.media.jai.util.ImagingListener; import java.util.Map; import com.sun.image.codec.jpeg.JPEGCodec; import com.sun.image.codec.jpeg.JPEGDecodeParam; import com.sun.image.codec.jpeg.JPEGImageDecoder; import com.sun.media.jai.util.ImageUtil; /** * An OpImage class to generate an image from an IIP connection. A single * resolution level of the remote IIP image is retrieved. * * @see javax.media.jai.operator.IIPDescriptor * @since 1.0 */ public class IIPResolutionOpImage extends OpImage { // Tile dimension are fixed at 64x64. private static final int TILE_SIZE = 64; // Default dimensions in tiles of block of tiles to retrieve. private static final int TILE_BLOCK_WIDTH = 8; private static final int TILE_BLOCK_HEIGHT = 2; // Significant delimiters in responses. private static final char BLANK = ' '; private static final char COLON = ':'; private static final char SLASH = '/'; private static final char CR = 0x0d; private static final char LF = 0x0a; // Colorspace information private static final int CS_COLORLESS = 0x0; private static final int CS_MONOCHROME = 0x1; private static final int CS_PHOTOYCC = 0x2; private static final int CS_NIFRGB = 0x3; private static final int CS_PLANE_ALPHA = 0x7ffe; // Compression types private static final int TILE_UNCOMPRESSED = 0x0; private static final int TILE_SINGLE_COLOR = 0x1; private static final int TILE_JPEG = 0x2; private static final int TILE_INVALID = 0xffffffff; // cache the ImagingListener private static ImagingListener listener = JAI.getDefaultInstance().getImagingListener(); /* The base URL string of the IIP server and image. */ private String URLString; /* The desired resolution in IIP order: 0 is lowest resolution. */ private int resolution; /* The desired sub-image. */ private int subImage; /* The colorspace type. */ private int colorSpaceType; /* Flag indicating whether the image has an opacity channel. */ private boolean hasAlpha; /* Flag indicating whether the opacity channel is premultiplied. */ private boolean isAlphaPremultilpied; /* The minimum tile in the X direction. */ private int minTileX; /* The minimum tile in the Y direction. */ private int minTileY; /* The number of tiles in the X direction. */ private int numXTiles; /* The JPEGDecodeParam cache */ private JPEGDecodeParam[] decodeParamCache = new JPEGDecodeParam[255]; /* Property initialization flag. */ private boolean arePropertiesInitialized = false; /* Tile block dimensions eventually used. */ private int tileBlockWidth = TILE_BLOCK_WIDTH; private int tileBlockHeight = TILE_BLOCK_HEIGHT; /** cache to extract the ImagingListener. */ private RenderingHints renderHints; /* * Convert YCbCr to NIF RGB using the appropriate algorithm. */ private static final void YCbCrToNIFRGB(Raster raster) { byte[] data = ((DataBufferByte)raster.getDataBuffer()).getData(); int offset = 0; int length = data.length; int MASK1 = 0x000000ff; int MASK2 = 0x0000ff00; if(raster.getSampleModel().getNumBands() == 3) { while(offset < length) { float Y = data[offset] & 0xff; float Cb = data[offset + 1] & 0xff; float Cr = data[offset + 2] & 0xff; int R = (int)(Y + 1.40200F * Cr - 178.255F); int G = (int)(Y - 0.34414F * Cb - 0.71414F * Cr + 135.4307F); int B = (int)(Y + 1.77200F * Cb - 225.43F); int imask = (R >> 5) & 0x18; data[offset++] = (byte)(((R & (MASK1 >> imask)) | (MASK2 >> imask)) & 0xff); imask = (G >> 5) & 0x18; data[offset++] = (byte)(((G & (MASK1 >> imask)) | (MASK2 >> imask)) & 0xff); imask = (B >> 5) & 0x18; data[offset++] = (byte)(((B & (MASK1 >> imask)) | (MASK2 >> imask)) & 0xff); } } else { // numBands == 4 (premultiplied NIFRGB with opacity) while(offset < length) { float Y = data[offset] & 0xff; float Cb = data[offset + 1] & 0xff; float Cr = data[offset + 2] & 0xff; int R = (int)(-Y - 1.40200F * Cr - 433.255F); int G = (int)(-Y + 0.34414F * Cb + 0.71414F * Cr + 119.5693F); int B = (int)(-Y - 1.77200F * Cb - 480.43F); int imask = (R >> 5) & 0x18; data[offset++] = (byte)(((R & (MASK1 >> imask)) | (MASK2 >> imask)) & 0xff); imask = (G >> 5) & 0x18; data[offset++] = (byte)(((G & (MASK1 >> imask)) | (MASK2 >> imask)) & 0xff); imask = (B >> 5) & 0x18; data[offset++] = (byte)(((B & (MASK1 >> imask)) | (MASK2 >> imask)) & 0xff); offset++; // skip the opacity } } } /* * Post one or more IIP commands using the HTTP protocol and return a * stream from which the response(s) may be read. The "commands" parameter * may be null. */ private static InputStream postCommands(String URLSpec, String[] commands) { // Construct the initial command by appending OBJ=IIP,1.0 StringBuffer spec = new StringBuffer(URLSpec+"&OBJ=iip,1.0"); if(commands != null) { // Append the commands to the string. for(int i = 0; i < commands.length; i++) { spec.append("&"+commands[i]); } } // Construct the URL and open a stream to read from it. InputStream stream = null; try { URL url = new URL(spec.toString()); stream = url.openStream(); } catch(Exception e) { String message = JaiI18N.getString("IIPResolution4") + spec.toString(); listener.errorOccurred(message, new ImagingException(message, e), IIPResolutionOpImage.class, false); // throw new RuntimeException(e.getClass()+" "+e.getMessage()); } return stream; } /* * Retrieve the label of the IIP server response. The label is defined * to be the bytes in the stream up to the first SLASH or COLON. The * returned value will be null if an EOS is reached before any characters * which are neither a SLASH or a COLON. If the returned String is non-null * it will be lower case. */ private static String getLabel(InputStream stream) { boolean charsAppended = false; StringBuffer buf = new StringBuffer(16); try { int i; while((i = stream.read()) != -1) { char c = (char)(0x000000ff&i); if(c == SLASH || c == COLON) { break; } buf.append(c); charsAppended = true; } } catch(Exception e) { String message = JaiI18N.getString("IIPResolution5"); listener.errorOccurred(message, new ImagingException(message, e), IIPResolutionOpImage.class, false); // throw new RuntimeException(e.getClass()+" "+e.getMessage()); } return charsAppended ? buf.toString().toLowerCase() : null; } /* * Retrieve the length of the data stream. This length is assumed to * be the an INT derived from the portion of the stream before the * first COLON. */ private static int getLength(InputStream stream) { return Integer.valueOf(getLabel(stream)).intValue(); } /* * Throw or print a RuntimeException in response to an error returned by * the IIP server. If no error was returned do nothing. Also grab and * discard the response to the "OBJ=iip" command which is always present. */ private static InputStream checkError(String label, InputStream stream, boolean throwException) { if(label.equals("error")) { int length = Integer.valueOf(getLabel(stream)).intValue(); byte[] b = new byte[length]; try { stream.read(b); } catch(Exception e) { String message = JaiI18N.getString("IIPResolution6"); listener.errorOccurred(message, new ImagingException(message,e), IIPResolutionOpImage.class, false); // throw new RuntimeException(e.getClass()+" "+e.getMessage()); } String msg = new String(b); if(throwException) { throwIIPException(msg); } else { printIIPException(msg); } } else if(label.startsWith("iip")) { // Ignore this response. String iipObjectResponse = getDataAsString(stream, false); } return stream; } /* * Returns the next segment of the stream until EOS or CRLF as a byte[]. * The length of the data must be available as an INT in the stream before * the COLON. */ private static byte[] getDataAsByteArray(InputStream stream) { int length = getLength(stream); byte[] b = new byte[length]; try { stream.read(b); stream.read(); // CR stream.read(); // LF } catch(Exception e) { String message = JaiI18N.getString("IIPResolution7"); listener.errorOccurred(message, new ImagingException(message,e), IIPResolutionOpImage.class, false); // throw new RuntimeException(e.getClass()+" "+e.getMessage()); } return b; } /* * Returns the next segment of the stream until EOS or CRLF as a String. */ private static String getDataAsString(InputStream stream, boolean hasLength) { String str = null; if(hasLength) { try { int length = getLength(stream); byte[] b = new byte[length]; stream.read(b); stream.read(); // CR stream.read(); // LF str = new String(b); } catch(Exception e) { String message = JaiI18N.getString("IIPResolution7"); listener.errorOccurred(message, new ImagingException(message,e), IIPResolutionOpImage.class, false); // throw new RuntimeException(e.getClass()+" "+e.getMessage()); } } else { StringBuffer buf = new StringBuffer(16); try { int i; while((i = stream.read()) != -1) { char c = (char)(0x000000ff&i); if(c == CR) { // if last byte was CR stream.read(); // LF break; } buf.append(c); } str = buf.toString(); } catch(Exception e) { String message = JaiI18N.getString("IIPResolution7"); listener.errorOccurred(message, new ImagingException(message,e), IIPResolutionOpImage.class, false); // throw new RuntimeException(e.getClass()+" "+e.getMessage()); } } return str; } /* * Flush the next segment of the stream until EOS or CRLF. */ private static void flushData(InputStream stream, boolean hasLength) { if(hasLength) { try { int length = getLength(stream); long numSkipped = stream.skip(length); if(numSkipped == length) { stream.read(); // CR stream.read(); // LF } } catch(Exception e) { String message = JaiI18N.getString("IIPResolution8"); listener.errorOccurred(message, new ImagingException(message,e), IIPResolutionOpImage.class, false); // throw new RuntimeException(e.getClass()+" "+e.getMessage()); } } else { try { int i; while((i = stream.read()) != -1) { if((char)(0x000000ff&i) == CR) { // if last byte was CR stream.read(); // LF break; } } } catch(Exception e) { String message = JaiI18N.getString("IIPResolution8"); listener.errorOccurred(message, new ImagingException(message,e), IIPResolutionOpImage.class, false); // throw new RuntimeException(e.getClass()+" "+e.getMessage()); } } } /* * Convert a string containing BLANK-seprated INTs into an int[]. */ private static int[] stringToIntArray(String s) { // Parse string into a Vector. Vector v = new Vector(); int lastBlank = 0; int nextBlank = s.indexOf(BLANK, 0); do { v.add(Integer.valueOf(s.substring(lastBlank, nextBlank))); lastBlank = nextBlank + 1; nextBlank = s.indexOf(BLANK, lastBlank); } while(nextBlank != -1); v.add(Integer.valueOf(s.substring(lastBlank))); // Convert the Vector to a int[]. int length = v.size(); int[] intArray = new int[length]; for(int i = 0; i < length; i++) { intArray[i] = ((Integer)v.get(i)).intValue(); } return intArray; } /* * Convert a string containing BLANK-seprated FLOATs into a float[]. */ private static float[] stringToFloatArray(String s) { // Parse string into a Vector. Vector v = new Vector(); int lastBlank = 0; int nextBlank = s.indexOf(BLANK, 0); do { v.add(Float.valueOf(s.substring(lastBlank, nextBlank))); lastBlank = nextBlank + 1; nextBlank = s.indexOf(BLANK, lastBlank); } while(nextBlank != -1); v.add(Float.valueOf(s.substring(lastBlank))); // Convert the Vector to a float[]. int length = v.size(); float[] floatArray = new float[length]; for(int i = 0; i < length; i++) { floatArray[i] = ((Float)v.get(i)).floatValue(); } return floatArray; } /* * Format the argument into a generic IIP error message. */ private static String formatIIPErrorMessage(String msg) { return new String(JaiI18N.getString("IIPResolutionOpImage0")+" "+msg); } /* * Throw a RuntimeException with the indicated message. */ private static void throwIIPException(String msg) { throw new RuntimeException(formatIIPErrorMessage(msg)); } /* * Print the supplied message to the standard error stream. */ private static void printIIPException(String msg) { System.err.println(formatIIPErrorMessage(msg)); } /* * Close the supplied stream ignoring any exceptions. */ private static void closeStream(InputStream stream) { try { stream.close(); } catch(Exception e) { // Ignore } } /* * Derive the image layout (tile grid, image bounds, ColorModel, and * SampleModel) by querying the IIP server. */ private static ImageLayout layoutHelper(String URLSpec, int level, int subImage) { // Create an ImageLayout by construction or cloning. ImageLayout il = new ImageLayout(); // Set the tile offsets to (0,0). il.setTileGridXOffset(0); il.setTileGridYOffset(0); // Set the tile dimensions. il.setTileWidth(TILE_SIZE); il.setTileHeight(TILE_SIZE); // Set the image origin to (0,0). il.setMinX(0); il.setMinY(0); // Retrieve the number of resolutions available and the maximum // width and height (the dimensions of resolution numRes - 1). int maxWidth = -1; int maxHeight = -1; int numRes = -1; int resolution = -1; String[] cmd = new String[] {"OBJ=Max-size", "OBJ=Resolution-number"}; InputStream stream = postCommands(URLSpec, cmd); String label = null; while((label = getLabel(stream)) != null) { if(label.equals("max-size")) { String data = getDataAsString(stream, false); int[] wh = stringToIntArray(data); maxWidth = wh[0]; maxHeight = wh[1]; } else if(label.equals("resolution-number")) { String data = getDataAsString(stream, false); numRes = Integer.valueOf(data).intValue(); if(level < 0) { resolution = 0; } else if(level >= numRes) { resolution = numRes - 1; } else { resolution = level; } } else { checkError(label, stream, true); } } closeStream(stream); // Derive the width and height for this resolution level. int w = maxWidth; int h = maxHeight; for(int i = numRes - 1; i > resolution; i--) { w = (w + 1)/2; h = (h + 1)/2; } il.setWidth(w); il.setHeight(h); // Determine image opacity attributes. boolean hasAlpha = false; boolean isAlphaPremultiplied = false; cmd = new String[] {"OBJ=Colorspace,"+resolution+","+subImage}; stream = postCommands(URLSpec, cmd); int colorSpaceIndex = 0; int numBands = 0; while((label = getLabel(stream)) != null) { if(label.startsWith("colorspace")) { int[] ia = stringToIntArray(getDataAsString(stream, false)); numBands = ia[3]; switch(ia[2]) { case CS_MONOCHROME: colorSpaceIndex = ColorSpace.CS_GRAY; break; case CS_PHOTOYCC: colorSpaceIndex = ColorSpace.CS_PYCC; break; case CS_NIFRGB: colorSpaceIndex = ColorSpace.CS_sRGB; break; default: colorSpaceIndex = numBands < 3 ? ColorSpace.CS_GRAY : ColorSpace.CS_sRGB; } for(int j = 1; j <= numBands; j++) { if(ia[3+j] == CS_PLANE_ALPHA) { hasAlpha = true; } } isAlphaPremultiplied = ia[1] == 1; } else { checkError(label, stream, true); } } closeStream(stream); // Set the ColorModel. ColorSpace cs = ColorSpace.getInstance(colorSpaceIndex); int dtSize = DataBuffer.getDataTypeSize(DataBuffer.TYPE_BYTE); int[] bits = new int[numBands]; for(int i = 0; i < numBands; i++) { bits[i] = dtSize; } int transparency = hasAlpha ? Transparency.TRANSLUCENT : Transparency.OPAQUE; ColorModel cm = new ComponentColorModel(cs, bits, hasAlpha, isAlphaPremultiplied, transparency, DataBuffer.TYPE_BYTE); il.setColorModel(cm); // Set the SampleModel. int[] bandOffsets = new int[numBands]; for(int i = 0; i < numBands; i++) { bandOffsets[i] = i; } il.setSampleModel(RasterFactory.createPixelInterleavedSampleModel( DataBuffer.TYPE_BYTE, TILE_SIZE, TILE_SIZE, numBands, numBands*TILE_SIZE, bandOffsets)); return il; } /** * Construct an OpImage given a String representation of a URL, * a resolution, and a sub-image index. * * @param URLSpec The URL of the IIP image including the FIF cimmand * if needed and possibly an SDS command. * @param level The resolution level with 0 as the lowest resolution. * @param subImage The subimage number. * @param layout The layout hint; may be null. */ public IIPResolutionOpImage(Map config, String URLSpec, int level, int subImage) { super((Vector)null, // the image is sourceless layoutHelper(URLSpec, level, subImage), config, false); this.renderHints = (RenderingHints)config; // Cache the constructor parameters. URLString = URLSpec; this.subImage = subImage; // Retrieve required parameters from server. String[] cmd = new String[] {"OBJ=Resolution-number"}; InputStream stream = postCommands(cmd); String label = null; while((label = getLabel(stream)) != null) { if(label.equals("resolution-number")) { String data = getDataAsString(stream, false); int numRes = Integer.valueOf(data).intValue(); if(level < 0) { resolution = 0; } else if(level >= numRes) { resolution = numRes - 1; } else { resolution = level; } } else { checkError(label, stream, true); } } endResponse(stream); // Cache some values which will be used repetitively. ColorSpace cs = colorModel.getColorSpace(); if(cs.isCS_sRGB()) { colorSpaceType = CS_NIFRGB; } else if(cs.equals(ColorSpace.getInstance(ColorSpace.CS_GRAY))) { colorSpaceType = CS_MONOCHROME; } else { colorSpaceType = CS_PHOTOYCC; } hasAlpha = colorModel.hasAlpha(); isAlphaPremultilpied = colorModel.isAlphaPremultiplied(); minTileX = getMinTileX(); minTileY = getMinTileY(); numXTiles = getNumXTiles(); } /* * Post the array of commands to the IIP server. */ private InputStream postCommands(String[] commands) { return postCommands(URLString, commands); } /* * End reading the server response. */ private void endResponse(InputStream stream) { // Add if-then block and handle socket connection. closeStream(stream); } /* * Compute the tile with the specified position in the tile grid. * Either a block of tiles or a single tile is retrieved depending * on where the requested tile false in the tile grid with respect * to the upper left tile in the image. All tiles except that returned * are stored in the TileCache. */ public Raster computeTile(int tileX, int tileY) { Raster raster = null; // If the tile is the upper left tile of a block of tiles // where the blocks are counted from the upper left corner // of the image then retrieve a block of tiles. if((tileX - minTileX) % tileBlockWidth == 0 && (tileY - minTileY) % tileBlockHeight == 0) { int endTileX = tileX + tileBlockWidth - 1; if(endTileX > getMaxTileX()) { endTileX = getMaxTileX(); } int endTileY = tileY + tileBlockHeight - 1; if(endTileY > getMaxTileY()) { endTileY = getMaxTileY(); } raster = getTileBlock(tileX, tileY, endTileX, endTileY); } else if((raster = getTileFromCache(tileX, tileY)) == null) { raster = getTileBlock(tileX, tileY, tileX, tileY); } return raster; } /* * Extract from the IIP response label the coordinates in the tile grid * of the tile returned by the IIP server. * * @param label The IIP response label. * @param xy The tile grid coordinates; may be null. */ private Point getTileXY(String label, Point xy) { // Get the tile number from the IIP response label. int beginIndex = label.indexOf(",", label.indexOf(",")+1)+1; int endIndex = label.lastIndexOf(","); int tile = Integer.valueOf(label.substring(beginIndex, endIndex)).intValue(); // Calculate the tile coordinates. int tileX = (tile + minTileX) % numXTiles; int tileY = (tile + minTileX - tileX)/numXTiles + minTileY; // Create or set the location. if(xy == null) { xy = new Point(tileX, tileY); } else { xy.setLocation(tileX, tileY); } return xy; } /* * Retrieve a block of tiles from the IIP server. All tiles except * the upper left tile are stored in the TileCache wheareas the upper * left tile is returned. */ private Raster getTileBlock(int upperLeftTileX, int upperLeftTileY, int lowerRightTileX, int lowerRightTileY) { int startTile = (upperLeftTileY - minTileY)*numXTiles + upperLeftTileX - minTileX; int endTile = (lowerRightTileY - minTileY)*numXTiles + lowerRightTileX - minTileX; String cmd = null; if(startTile == endTile) { // single tile cmd = new String("til="+resolution+","+startTile+","+subImage); } else { // range of tiles cmd = new String("til="+resolution+","+startTile+"-"+ endTile+","+subImage); } InputStream stream = postCommands(new String[] {cmd}); int compressionType = -1; int compressionSubType = -1; byte[] data = null; String label = null; Raster upperLeftTile = null; Point tileXY = new Point(); while((label = getLabel(stream)) != null) { if(label.startsWith("tile")) { int length = getLength(stream); byte[] header = new byte[8]; try { stream.read(header); } catch(Exception e) { throwIIPException(JaiI18N.getString("IIPResolutionOpImage1")); } length -= 8; compressionType = (int)((header[3]<<24)| (header[2]<<16)| (header[1]<<8)| header[0]); compressionSubType = (int)((header[7]<<24)| (header[6]<<16)| (header[5]<<8)| header[4]); if(length != 0) { data = new byte[length]; try { int numBytesRead = 0; int offset = 0; do { numBytesRead = stream.read(data, offset, length - offset); offset += numBytesRead; } while(offset < length && numBytesRead != -1); if(numBytesRead != -1) { stream.read(); // CR stream.read(); // LF } } catch(Exception e) { throwIIPException(JaiI18N.getString("IIPResolutionOpImage2")); } } getTileXY(label, tileXY); int tileX = (int)tileXY.getX(); int tileY = (int)tileXY.getY(); int tx = tileXToX(tileX); int ty = tileYToY(tileY); Raster raster = null; switch(compressionType) { case TILE_UNCOMPRESSED: raster = getUncompressedTile(tx, ty, data); break; case TILE_SINGLE_COLOR: raster = getSingleColorTile(tx, ty, compressionSubType); break; case TILE_JPEG: raster = getJPEGTile(tx, ty, compressionSubType, data); break; case TILE_INVALID: default: raster = createWritableRaster(sampleModel, new Point(tx, ty)); break; } if(tileX == upperLeftTileX && tileY == upperLeftTileY) { upperLeftTile = raster; } else { //System.out.println("Caching "+label); addTileToCache(tileX, tileY, raster); } } else { checkError(label, stream, true); } } endResponse(stream); return upperLeftTile; } /* * Create a Raster from the data of an uncompressed tile. */ private Raster getUncompressedTile(int tx, int ty, byte[] data) { DataBuffer dataBuffer = new DataBufferByte(data, data.length); return Raster.createRaster(sampleModel, dataBuffer, new Point(tx, ty)); } /* * Create a Raster of a single color. */ private Raster getSingleColorTile(int tx, int ty, int color) { byte R = (byte)(color & 0x000000ff); byte G = (byte)((color >> 8) & 0x000000ff); byte B = (byte)((color >> 16) & 0x000000ff); byte A = (byte)((color >> 24) & 0x000000ff); int numBands = sampleModel.getNumBands(); int length = tileWidth*tileHeight*numBands; byte[] data = new byte[length]; int i = 0; switch(numBands) { case 1: while(i < length) { data[i++] = R; } break; case 2: while(i < length) { data[i++] = R; data[i++] = A; } break; case 3: while(i < length) { data[i++] = R; data[i++] = G; data[i++] = B; } case 4: default: while(i < length) { data[i++] = R; data[i++] = G; data[i++] = B; data[i++] = A; } } DataBuffer dataBuffer = new DataBufferByte(data, data.length); return Raster.createRaster(sampleModel, dataBuffer, new Point(tx, ty)); } /* * Create a Raster from a JPEG-compressed data stream. */ private Raster getJPEGTile(int tx, int ty, int subType, byte[] data) { int tableIndex = (subType >> 24) & 0x000000ff;; boolean colorConversion = (subType & 0x00ff0000) != 0; JPEGDecodeParam decodeParam = null; if(tableIndex != 0) { decodeParam = getJPEGDecodeParam(tableIndex); } ByteArrayInputStream byteStream = new ByteArrayInputStream(data); JPEGImageDecoder decoder = decodeParam == null ? JPEGCodec.createJPEGDecoder(byteStream) : JPEGCodec.createJPEGDecoder(byteStream, decodeParam); Raster raster = null; try { raster = decoder.decodeAsRaster().createTranslatedChild(tx, ty); } catch(Exception e) { ImagingListener listener = ImageUtil.getImagingListener(renderHints); listener.errorOccurred(JaiI18N.getString("IIPResolutionOpImage3"), new ImagingException(e), this, false); /* String msg = JaiI18N.getString("IIPResolutionOpImage3")+" "+ e.getMessage(); throw new RuntimeException(msg); */ } closeStream(byteStream); if(colorSpaceType == CS_NIFRGB && colorConversion) { YCbCrToNIFRGB(raster); } return raster; } /* * Retrieve the JPEGDecodeParam object for the indicated table. If the * object is available in the config, use it; otherwise retrieve it from * the server. An ArrayIndexOutOfBoundsException will be thrown if * the parameter is not in the range [1,256]. */ private synchronized JPEGDecodeParam getJPEGDecodeParam(int tableIndex) { JPEGDecodeParam decodeParam = decodeParamCache[tableIndex-1]; if(decodeParam == null) { String cmd = new String("OBJ=Comp-group,"+ TILE_JPEG+","+tableIndex); InputStream stream = postCommands(new String[] {cmd}); String label = null; while((label = getLabel(stream)) != null) { if(label.startsWith("comp-group")) { byte[] table = getDataAsByteArray(stream); ByteArrayInputStream tableStream = new ByteArrayInputStream(table); JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(tableStream); try { // This call is necessary. decoder.decodeAsRaster(); } catch(Exception e) { // Ignore. } decodeParam = decoder.getJPEGDecodeParam(); } else { checkError(label, stream, true); } } endResponse(stream); if(decodeParam != null) { decodeParamCache[tableIndex-1] = decodeParam; } } return decodeParam; } /* * Obtain valid properties from IIP server and use * setProperty() to store the name/value pairs. */ private synchronized void initializeIIPProperties() { if(!arePropertiesInitialized) { String[] cmd = new String[] {"OBJ=IIP", "OBJ=Basic-info", "OBJ=View-info", "OBJ=Summary-info", "OBJ=Copyright"}; InputStream stream = postCommands(cmd); String label = null; while((label = getLabel(stream)) != null) { String name = label; Object value = null; if(label.equals("error")) { flushData(stream, true); } else if(label.startsWith("colorspace") || label.equals("max-size")) { if(label.startsWith("colorspace")) { name = "colorspace"; } value = stringToIntArray(getDataAsString(stream, false)); } else if(label.equals("resolution-number")) { value = Integer.valueOf(getDataAsString(stream, false)); } else if(label.equals("aspect-ratio") || label.equals("contrast-adjust") || label.equals("filtering-value")) { value = Float.valueOf(getDataAsString(stream, false)); } else if(label.equals("affine-transform")) { float[] a = (float[])stringToFloatArray(getDataAsString(stream, false)); value = new AffineTransform(a[0], a[1], a[3], a[4], a[5], a[7]); } else if(label.equals("color-twist")) { value = stringToFloatArray(getDataAsString(stream, false)); } else if(label.equals("roi")) { name = "roi-iip"; float[] rect = stringToFloatArray(getDataAsString(stream, false)); value = new Rectangle2D.Float(rect[0], rect[1], rect[2], rect[3]); } else if(label.equals("copyright") || label.equals("title") || label.equals("subject") || label.equals("author") || label.equals("keywords") || label.equals("comment") || label.equals("last-author") || label.equals("rev-number") || label.equals("app-name")) { value = getDataAsString(stream, true); } else if(label.equals("iip") || label.equals("iip-server") || label.equals("edit-time") || label.equals("last-printed") || label.equals("create-dtm") || label.equals("last-save-dtm")) { value = getDataAsString(stream, false); } else { // Ignore unknown objects flushData(stream, false); } if(name != null && value != null) { setProperty(name, value); } } endResponse(stream); arePropertiesInitialized = true; } } /* * Forward to superclass after lazy initialization of IIP properties. */ public String[] getPropertyNames() { initializeIIPProperties(); return super.getPropertyNames(); } /* * Forward to superclass after lazy initialization of IIP properties. */ public Object getProperty(String name) { initializeIIPProperties(); return super.getProperty(name); } /** * Throws an IllegalArgumentException since the image has no image * sources. * * @param sourceRect ignored. * @param sourceIndex ignored. * @throws IllegalArgumentException since the image has no image sources. */ public Rectangle mapSourceRect(Rectangle sourceRect, int sourceIndex) { throw new IllegalArgumentException(JaiI18N.getString("AreaOpImage0")); } /** * Throws an IllegalArgumentException since the image has no image * sources. * * @param destRect ignored. * @param sourceIndex ignored. * @throws IllegalArgumentException since the image has no image sources. */ public Rectangle mapDestRect(Rectangle destRect, int sourceIndex) { throw new IllegalArgumentException(JaiI18N.getString("AreaOpImage0")); } /** * Dispose of any allocated resources. */ protected void finalize() throws Throwable { super.finalize(); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MedianFilterSeparableOpImage.java0000644000175000017500000005306710203035544031532 0ustar mathieumathieu/* * $RCSfile: MedianFilterSeparableOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:34 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import java.util.Map; import javax.media.jai.operator.MedianFilterDescriptor; import com.sun.media.jai.opimage.MedianFilterOpImage; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class to perform median filtering on a source image. * * */ final class MedianFilterSeparableOpImage extends MedianFilterOpImage { /** * Creates a MedianFilterSeperableOpImage with the given source and * maskSize. The image dimensions are derived from the source * image. The tile grid layout, SampleModel, and ColorModel may * optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param maskSize the mask size. */ public MedianFilterSeparableOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, int maskSize) { super(source, extender, config, layout, MedianFilterDescriptor.MEDIAN_MASK_SQUARE, maskSize); } protected void byteLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); byte dstDataArrays[][] = dst.getByteDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); byte srcDataArrays[][] = src.getByteDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int medianValues[] = new int[filterSize]; int tmpValues[] = new int[filterSize]; int wp = filterSize; int tmpBuffer[] = new int[filterSize*dwidth]; int tmpBufferSize = filterSize*dwidth; for (int k = 0; k < dnumBands; k++) { byte dstData[] = dstDataArrays[k]; byte srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; int revolver = 0; for (int j = 0; j < filterSize-1; j++) { int srcPixelOffset = srcScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; for (int v = 0; v < wp; v++) { tmpValues[v] = srcData[imageOffset] & 0xff; imageOffset += srcPixelStride; } tmpBuffer[revolver+i] = medianFilter(tmpValues); srcPixelOffset += srcPixelStride; } revolver += dwidth; srcScanlineOffset += srcScanlineStride; } // srcScanlineStride already bumped by // filterSize-1*scanlineStride for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; for (int v = 0; v < wp; v++) { tmpValues[v] = srcData[imageOffset] & 0xff; imageOffset += srcPixelStride; } tmpBuffer[revolver + i] = medianFilter(tmpValues); int a = 0; for (int b = i; b < tmpBufferSize; b += dwidth) { medianValues[a++] = tmpBuffer[b]; } int val = medianFilter(medianValues); dstData[dstPixelOffset] = (byte)val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } revolver += dwidth; if (revolver == tmpBufferSize) { revolver = 0; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void shortLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int medianValues[] = new int[filterSize]; int tmpValues[] = new int[filterSize]; int wp = filterSize; int tmpBuffer[] = new int[filterSize*dwidth]; int tmpBufferSize = filterSize*dwidth; for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; int revolver = 0; for (int j = 0; j < filterSize-1; j++) { int srcPixelOffset = srcScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; for (int v = 0; v < wp; v++) { tmpValues[v] = srcData[imageOffset]; imageOffset += srcPixelStride; } tmpBuffer[revolver+i] = medianFilter(tmpValues); srcPixelOffset += srcPixelStride; } revolver += dwidth; srcScanlineOffset += srcScanlineStride; } // srcScanlineStride already bumped by // filterSize-1*scanlineStride for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; for (int v = 0; v < wp; v++) { tmpValues[v] = srcData[imageOffset]; imageOffset += srcPixelStride; } tmpBuffer[revolver + i] = medianFilter(tmpValues); int a = 0; for (int b = i; b < tmpBufferSize; b += dwidth) { medianValues[a++] = tmpBuffer[b]; } int val = medianFilter(medianValues); dstData[dstPixelOffset] = (short)val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } revolver += dwidth; if (revolver == tmpBufferSize) { revolver = 0; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void ushortLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int medianValues[] = new int[filterSize]; int tmpValues[] = new int[filterSize]; int wp = filterSize; int tmpBuffer[] = new int[filterSize*dwidth]; int tmpBufferSize = filterSize*dwidth; for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; int revolver = 0; for (int j = 0; j < filterSize-1; j++) { int srcPixelOffset = srcScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; for (int v = 0; v < wp; v++) { tmpValues[v] = srcData[imageOffset] & 0xfff; imageOffset += srcPixelStride; } tmpBuffer[revolver+i] = medianFilter(tmpValues); srcPixelOffset += srcPixelStride; } revolver += dwidth; srcScanlineOffset += srcScanlineStride; } // srcScanlineStride already bumped by // filterSize-1*scanlineStride for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; for (int v = 0; v < wp; v++) { tmpValues[v] = srcData[imageOffset] & 0xffff; imageOffset += srcPixelStride; } tmpBuffer[revolver + i] = medianFilter(tmpValues); int a = 0; for (int b = i; b < tmpBufferSize; b += dwidth) { medianValues[a++] = tmpBuffer[b]; } int val = medianFilter(medianValues); dstData[dstPixelOffset] = (short)val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } revolver += dwidth; if (revolver == tmpBufferSize) { revolver = 0; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void intLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); int dstDataArrays[][] = dst.getIntDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcDataArrays[][] = src.getIntDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int medianValues[] = new int[filterSize]; int tmpValues[] = new int[filterSize]; int wp = filterSize; int tmpBuffer[] = new int[filterSize*dwidth]; int tmpBufferSize = filterSize*dwidth; for (int k = 0; k < dnumBands; k++) { int dstData[] = dstDataArrays[k]; int srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; int revolver = 0; for (int j = 0; j < filterSize-1; j++) { int srcPixelOffset = srcScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; for (int v = 0; v < wp; v++) { tmpValues[v] = srcData[imageOffset]; imageOffset += srcPixelStride; } tmpBuffer[revolver+i] = medianFilter(tmpValues); srcPixelOffset += srcPixelStride; } revolver += dwidth; srcScanlineOffset += srcScanlineStride; } // srcScanlineStride already bumped by // filterSize-1*scanlineStride for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; for (int v = 0; v < wp; v++) { tmpValues[v] = srcData[imageOffset]; imageOffset += srcPixelStride; } tmpBuffer[revolver + i] = medianFilter(tmpValues); int a = 0; for (int b = i; b < tmpBufferSize; b += dwidth) { medianValues[a++] = tmpBuffer[b]; } int val = medianFilter(medianValues); dstData[dstPixelOffset] = val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } revolver += dwidth; if (revolver == tmpBufferSize) { revolver = 0; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void floatLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); float dstDataArrays[][] = dst.getFloatDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); float srcDataArrays[][] = src.getFloatDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); float medianValues[] = new float[filterSize]; float tmpValues[] = new float[filterSize]; int wp = filterSize; float tmpBuffer[] = new float[filterSize*dwidth]; int tmpBufferSize = filterSize*dwidth; for (int k = 0; k < dnumBands; k++) { float dstData[] = dstDataArrays[k]; float srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; int revolver = 0; for (int j = 0; j < filterSize-1; j++) { int srcPixelOffset = srcScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; for (int v = 0; v < wp; v++) { tmpValues[v] = srcData[imageOffset]; imageOffset += srcPixelStride; } tmpBuffer[revolver+i] = medianFilterFloat(tmpValues); srcPixelOffset += srcPixelStride; } revolver += dwidth; srcScanlineOffset += srcScanlineStride; } // srcScanlineStride already bumped by // filterSize-1*scanlineStride for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; for (int v = 0; v < wp; v++) { tmpValues[v] = srcData[imageOffset]; imageOffset += srcPixelStride; } tmpBuffer[revolver + i] = medianFilterFloat(tmpValues); int a = 0; for (int b = i; b < tmpBufferSize; b += dwidth) { medianValues[a++] = tmpBuffer[b]; } float val = medianFilterFloat(medianValues); dstData[dstPixelOffset] = val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } revolver += dwidth; if (revolver == tmpBufferSize) { revolver = 0; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void doubleLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); double dstDataArrays[][] = dst.getDoubleDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); double srcDataArrays[][] = src.getDoubleDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); double medianValues[] = new double[filterSize]; double tmpValues[] = new double[filterSize]; int wp = filterSize; double tmpBuffer[] = new double[filterSize*dwidth]; int tmpBufferSize = filterSize*dwidth; for (int k = 0; k < dnumBands; k++) { double dstData[] = dstDataArrays[k]; double srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; int revolver = 0; for (int j = 0; j < filterSize-1; j++) { int srcPixelOffset = srcScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; for (int v = 0; v < wp; v++) { tmpValues[v] = srcData[imageOffset]; imageOffset += srcPixelStride; } tmpBuffer[revolver+i] = medianFilterDouble(tmpValues); srcPixelOffset += srcPixelStride; } revolver += dwidth; srcScanlineOffset += srcScanlineStride; } // srcScanlineStride already bumped by // filterSize-1*scanlineStride for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageOffset = srcPixelOffset; for (int v = 0; v < wp; v++) { tmpValues[v] = srcData[imageOffset]; imageOffset += srcPixelStride; } tmpBuffer[revolver + i] = medianFilterDouble(tmpValues); int a = 0; for (int b = i; b < tmpBufferSize; b += dwidth) { medianValues[a++] = tmpBuffer[b]; } double val = medianFilterDouble(medianValues); dstData[dstPixelOffset] = val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } revolver += dwidth; if (revolver == tmpBufferSize) { revolver = 0; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } // public static OpImage createTestImage(OpImageTester oit) { // return // new MedianFilterSeparableOpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource()), // 3); // } // public static void main(String args[]) { // String classname = // "com.sun.media.jai.opimage.MedianFilterSeparableOpImage"; // OpImageTester.performDiagnostics(classname,args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ConstantCRIF.java0000644000175000017500000001230310203035544026327 0ustar mathieumathieu/* * $RCSfile: ConstantCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:19 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderContext; import java.awt.image.renderable.RenderableImage; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * This image factory supports image operator ConstantOpImage * in the rendered and renderable image layers. * * @see ConstantOpImage */ public class ConstantCRIF extends CRIFImpl { private static final int DEFAULT_TILE_SIZE = 128; /** Constructor. */ public ConstantCRIF() { super("constant"); } /** * Creates a new instance of ConstantOpImage * in the rendered layer. This method satisfies the * implementation of RIF. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); // Get width, height, bandValues from the parameter block int width = Math.round(paramBlock.getFloatParameter(0)); int height = Math.round(paramBlock.getFloatParameter(1)); Number[] bandValues = (Number[])paramBlock.getObjectParameter(2); int minX = 0; int minY = 0; int tileWidth = Math.min(width, DEFAULT_TILE_SIZE); int tileHeight = Math.min(height, DEFAULT_TILE_SIZE); // Attempt to get minX, minY, tileWidth, tileHeight // from the ImageLayout hint if (layout != null) { if (layout.isValid(ImageLayout.MIN_X_MASK)) { minX = layout.getMinX(null); } if (layout.isValid(ImageLayout.MIN_Y_MASK)) { minY = layout.getMinY(null); } if (layout.isValid(ImageLayout.TILE_WIDTH_MASK)) { tileWidth = layout.getTileWidth(null); } if (layout.isValid(ImageLayout.TILE_HEIGHT_MASK)) { tileHeight = layout.getTileHeight(null); } } return new ConstantOpImage(minX, minY, width, height, tileWidth, tileHeight, bandValues); } /** * Creates a new instance of ConstantOpImage * in the renderable layer. This method satisfies the * implementation of CRIF. * * @pram renderContext Rendering information. * @param paramBlock The image layout for the output of * ConstantOpImage * and the constant pixel value. */ public RenderedImage create(RenderContext renderContext, ParameterBlock paramBlock) { float minX = 0; float minY = 0; float width = paramBlock.getFloatParameter(0); float height = paramBlock.getFloatParameter(1); Number[] bandValues = (Number[])paramBlock.getObjectParameter(2); AffineTransform trans = renderContext.getTransform(); float maxX, maxY; float[] ptSrc = new float[8]; float[] ptDst = new float[8]; ptSrc[0] = minX; ptSrc[1] = minY; ptSrc[2] = minX + width; ptSrc[3] = minY; ptSrc[4] = minX + width; ptSrc[5] = minY + height; ptSrc[6] = minX; ptSrc[7] = minY + height; trans.transform(ptSrc, 0, ptDst, 0, 4); minX = Math.min(ptDst[0], ptDst[2]); minX = Math.min(minX, ptDst[4]); minX = Math.min(minX, ptDst[6]); maxX = Math.max(ptDst[0], ptDst[2]); maxX = Math.max(maxX, ptDst[4]); maxX = Math.max(maxX, ptDst[6]); minY = Math.min(ptDst[1], ptDst[3]); minY = Math.min(minY, ptDst[5]); minY = Math.min(minY, ptDst[7]); maxY = Math.max(ptDst[1], ptDst[3]); maxY = Math.max(maxY, ptDst[5]); maxY = Math.max(maxY, ptDst[7]); int iMinX = (int)minX; int iMinY = (int)minY; int iWidth = (int)maxX - iMinX; int iHeight = (int)maxY - iMinY; return new ConstantOpImage(iMinX, iMinY, iWidth, iHeight, Math.min(iWidth, DEFAULT_TILE_SIZE), Math.min(iHeight, DEFAULT_TILE_SIZE), bandValues); } /** * Gets the bounding box for the output of ConstantOpImage. * This method satisfies the implementation of CRIF. * * @param paramBlock Image's width, height, and constant pixel values. */ public Rectangle2D getBounds2D(ParameterBlock paramBlock) { return new Rectangle2D.Float(0, 0, paramBlock.getFloatParameter(0), paramBlock.getFloatParameter(1)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/BinarizeOpImage.java0000644000175000017500000003500610203035544027104 0ustar mathieumathieu/* * $RCSfile: BinarizeOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:15 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.color.ColorSpace; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.ColorModel; import java.awt.image.PackedColorModel; import java.awt.image.WritableRaster; import java.util.Map; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PlanarImage; import javax.media.jai.PointOpImage; import javax.media.jai.PixelAccessor; import javax.media.jai.PackedImageData; import javax.media.jai.UnpackedImageData; import com.sun.media.jai.util.JDKWorkarounds; import com.sun.media.jai.util.ImageUtil; /** * An OpImage implementing the "Binarize" operation as * described in javax.media.jai.operator.BinarizeDescriptor. * *

This OpImage maps all the pixels of an image * whose value falls within a given range to a constant on a per-band basis. * Each of the lower bound, upper bound, and constant arrays may have only * one value in it. If that is the case, that value is used for all bands. * * @see javax.media.jai.operator.BinarizeDescriptor * @see BinarizeCRIF * * @since version 1.1 */ final class BinarizeOpImage extends PointOpImage { /** * Lookup table for ORing bytes of output. */ private static byte[] byteTable = new byte[] { (byte)0x80, (byte)0x40, (byte)0x20, (byte)0x10, (byte)0x08, (byte)0x04, (byte)0x02, (byte)0x01, }; /** * bitsOn[j + (i<<3)] * sets bits on from i to j */ private static int[] bitsOn = null; /** The threshold. */ private double threshold; /** * Constructor. * * @param source The source image. * @param layout The destination image layout. * @param threshold The threshold value for binarization. */ public BinarizeOpImage(RenderedImage source, Map config, ImageLayout layout, double threshold) { super(source, layoutHelper(source, layout, config), config, true); if(source.getSampleModel().getNumBands() != 1) { throw new IllegalArgumentException(JaiI18N.getString("BinarizeOpImage0")); } this.threshold = threshold; } // set the OpImage's SM to be MultiPixelPackedSampleModel private static ImageLayout layoutHelper(RenderedImage source, ImageLayout il, Map config) { ImageLayout layout = (il == null) ? new ImageLayout() : (ImageLayout)il.clone(); SampleModel sm = layout.getSampleModel(source); if(!ImageUtil.isBinary(sm)) { sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, layout.getTileWidth(source), layout.getTileHeight(source), 1); layout.setSampleModel(sm); } ColorModel cm = layout.getColorModel(null); if(cm == null || !JDKWorkarounds.areCompatibleDataModels(sm, cm)) { layout.setColorModel(ImageUtil.getCompatibleColorModel(sm, config)); } return layout; } /** * Map the pixels inside a specified rectangle whose value is within a * rang to a constant on a per-band basis. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { switch (sources[0].getSampleModel().getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(sources[0], dest, destRect); break; case DataBuffer.TYPE_SHORT: shortLoop(sources[0], dest, destRect); break; case DataBuffer.TYPE_USHORT: ushortLoop(sources[0], dest, destRect); break; case DataBuffer.TYPE_INT: intLoop(sources[0], dest, destRect); break; case DataBuffer.TYPE_FLOAT: floatLoop(sources[0], dest, destRect); break; case DataBuffer.TYPE_DOUBLE: doubleLoop(sources[0], dest, destRect); break; default: throw new RuntimeException(JaiI18N.getString("BinarizeOpImage1")); } } private void byteLoop(Raster source, WritableRaster dest, Rectangle destRect){ if(threshold <= 0.0D){ // every bit is 1 setTo1(dest, destRect); return; }else if (threshold > 255.0D){ //every bit is zeros; return; } short thresholdI = (short)Math.ceil(threshold); // computation can be done in integer // even though threshold is of double type // int thresholdI = (int)Math.ceil(this.threshold); // or through a lookup table for byte case Rectangle srcRect = mapDestRect(destRect,0); // should be identical to destRect PixelAccessor pa = new PixelAccessor(dest.getSampleModel(), null); PackedImageData pid = pa.getPackedPixels(dest, destRect, true, false); int offset = pid.offset; PixelAccessor srcPa = new PixelAccessor(source.getSampleModel(), null); UnpackedImageData srcImD = srcPa.getPixels(source, srcRect, DataBuffer.TYPE_BYTE, false); int srcOffset = srcImD.bandOffsets[0]; byte[] srcData = ((byte[][])srcImD.data)[0]; int pixelStride= srcImD.pixelStride; int ind0 = pid.bitOffset; for(int h = 0; h < destRect.height; h++){ int indE = ind0 + destRect.width; for(int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride){ if((srcData[s]&0xFF) >= thresholdI) { pid.data[offset + (b >> 3)] |= byteTable[b%8]; } } offset += pid.lineStride; srcOffset += srcImD.lineStride; } pa.setPackedPixels(pid); } // computation in short private void shortLoop(Raster source, WritableRaster dest, Rectangle destRect){ if(threshold <= Short.MIN_VALUE){ // every bit is 1 setTo1(dest, destRect); return; }else if (threshold > Short.MAX_VALUE){ //every bit is zeros; return; } short thresholdS = (short)( Math.ceil(threshold)); // computation can be done in integer // even though threshold is of double type // int thresholdI = (int)Math.ceil(this.threshold); // or through a lookup table for byte case Rectangle srcRect = mapDestRect(destRect,0); // should be identical to destRect PixelAccessor pa = new PixelAccessor(dest.getSampleModel(), null); PackedImageData pid = pa.getPackedPixels(dest, destRect, true, false); int offset = pid.offset; PixelAccessor srcPa = new PixelAccessor(source.getSampleModel(), null); UnpackedImageData srcImD = srcPa.getPixels(source, srcRect, DataBuffer.TYPE_SHORT, false); int srcOffset = srcImD.bandOffsets[0]; short[] srcData = ((short[][])srcImD.data)[0]; int pixelStride= srcImD.pixelStride; int ind0 = pid.bitOffset; for(int h = 0; h < destRect.height; h++){ int indE = ind0 + destRect.width; for(int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride){ if(srcData[s] >= thresholdS) { pid.data[offset + (b >> 3)] |= byteTable[b%8]; } } offset += pid.lineStride; srcOffset += srcImD.lineStride; } pa.setPackedPixels(pid); } // computation in short private void ushortLoop(Raster source, WritableRaster dest, Rectangle destRect){ if(threshold <= 0.0D){ // every bit is 1 setTo1(dest, destRect); return; }else if (threshold > (double)(0xFFFF)){ //every bit is zeros; return; } int thresholdI = (int)( Math.ceil(threshold)); // computation can be done in integer // even though threshold is of double type // int thresholdI = (int)Math.ceil(this.threshold); // or through a lookup table for byte case Rectangle srcRect = mapDestRect(destRect,0); // should be identical to destRect PixelAccessor pa = new PixelAccessor(dest.getSampleModel(), null); PackedImageData pid = pa.getPackedPixels(dest, destRect, true, false); int offset = pid.offset; PixelAccessor srcPa = new PixelAccessor(source.getSampleModel(), null); UnpackedImageData srcImD = srcPa.getPixels(source, srcRect, DataBuffer.TYPE_USHORT, false); int srcOffset = srcImD.bandOffsets[0]; short[] srcData = ((short[][])srcImD.data)[0]; int pixelStride= srcImD.pixelStride; int ind0 = pid.bitOffset; for(int h = 0; h < destRect.height; h++){ int indE = ind0 + destRect.width; for(int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride){ if((srcData[s]&0xFFFF) >= thresholdI) { pid.data[offset + (b >> 3)] |= byteTable[b%8]; } } offset += pid.lineStride; srcOffset += srcImD.lineStride; } pa.setPackedPixels(pid); } private void intLoop(Raster source, WritableRaster dest, Rectangle destRect){ if(threshold <= Integer.MIN_VALUE){ // every bit is 1 setTo1(dest, destRect); return; }else if (threshold > (double)Integer.MAX_VALUE){ //every bit is zeros; return; } // computation can be done in integer // even though threshold is of double type int thresholdI = (int)Math.ceil(this.threshold); // computation can be done in integer // even though threshold is of double type // int thresholdI = (int)Math.ceil(this.threshold); Rectangle srcRect = mapDestRect(destRect,0); // should be identical to destRect PixelAccessor pa = new PixelAccessor(dest.getSampleModel(), null); PackedImageData pid = pa.getPackedPixels(dest, destRect, true, false); int offset = pid.offset; PixelAccessor srcPa = new PixelAccessor(source.getSampleModel(), null); UnpackedImageData srcImD = srcPa.getPixels(source, srcRect, DataBuffer.TYPE_INT, false); int srcOffset = srcImD.bandOffsets[0]; int[] srcData = ((int[][])srcImD.data)[0]; int pixelStride= srcImD.pixelStride; int ind0 = pid.bitOffset; for(int h = 0; h < destRect.height; h++){ int indE = ind0 + destRect.width; for(int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride){ if(srcData[s] >= threshold) { pid.data[offset + (b >> 3)] |= byteTable[b%8]; } } offset += pid.lineStride; srcOffset += srcImD.lineStride; } pa.setPackedPixels(pid); } // computation in float private void floatLoop(Raster source, WritableRaster dest, Rectangle destRect){ Rectangle srcRect = mapDestRect(destRect,0); // should be identical to destRect PixelAccessor pa = new PixelAccessor(dest.getSampleModel(), null); PackedImageData pid = pa.getPackedPixels(dest, destRect, true, false); int offset = pid.offset; PixelAccessor srcPa = new PixelAccessor(source.getSampleModel(), null); UnpackedImageData srcImD = srcPa.getPixels(source, srcRect, DataBuffer.TYPE_FLOAT, false); int srcOffset = srcImD.bandOffsets[0]; float[] srcData = ((float[][])srcImD.data)[0]; int pixelStride= srcImD.pixelStride; int ind0 = pid.bitOffset; for(int h = 0; h < destRect.height; h++){ int indE = ind0 + destRect.width; for(int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride){ if (srcData[s]>threshold) { pid.data[offset + (b >> 3)] |= byteTable[b%8]; } } offset += pid.lineStride; srcOffset += srcImD.lineStride; } pa.setPackedPixels(pid); } // computation in double private void doubleLoop(Raster source, WritableRaster dest, Rectangle destRect){ Rectangle srcRect = mapDestRect(destRect,0); // should be identical to destRect PixelAccessor pa = new PixelAccessor(dest.getSampleModel(), null); PackedImageData pid = pa.getPackedPixels(dest, destRect, true, false); int offset = pid.offset; PixelAccessor srcPa = new PixelAccessor(source.getSampleModel(), null); UnpackedImageData srcImD = srcPa.getPixels(source, srcRect, DataBuffer.TYPE_DOUBLE, false); int srcOffset = srcImD.bandOffsets[0]; double[] srcData = ((double[][])srcImD.data)[0]; int pixelStride= srcImD.pixelStride; int ind0 = pid.bitOffset; for(int h = 0; h < destRect.height; h++){ int indE = ind0 + destRect.width; for(int b = ind0, s = srcOffset; b < indE; b++, s += pixelStride){ if (srcData[s]>threshold) { pid.data[offset + (b >> 3)] |= byteTable[b%8]; } } offset += pid.lineStride; srcOffset += srcImD.lineStride; } pa.setPackedPixels(pid); } // set all bits in a rectangular region to be 1 // need to be sure that paddings not changing private void setTo1(Raster dest, Rectangle destRect){ initBitsOn(); PixelAccessor pa = new PixelAccessor(dest.getSampleModel(), null); PackedImageData pid = pa.getPackedPixels(dest, destRect, true, false); int offset = pid.offset; for(int h = 0; h < destRect.height; h++){ int ind0 = pid.bitOffset; int indE = ind0 + destRect.width - 1; if (indE < 8){ // the entire row in data[offset] pid.data[offset] = (byte)(pid.data[offset] | bitsOn[indE]); // (0<<3) + indE }else{ //1st byte pid.data[offset] = (byte)(pid.data[offset] | bitsOn[7]); // (0<<3) + 7 //middle bytes for(int b = offset + 1; b <= offset + (indE-7)/8; b++){ pid.data[b] = (byte)(0xff); } //last byte int remBits = indE % 8; if(remBits % 8 != 7){ indE = offset + indE/8; pid.data[indE] = (byte)(pid.data[indE] | bitsOn[remBits]); // (0<<3)+remBits } } offset += pid.lineStride; } pa.setPackedPixels(pid); } // setting bits i to j to 1; // i <= j private static synchronized void initBitsOn() { if(bitsOn != null) return; bitsOn = new int[64]; for(int i = 0; i < 8; i++){ for(int j = i; j< 8; j++){ int bi = (0x00ff) >> i; int bj = (0x00ff) << (7-j); bitsOn[j + (i<<3)] = bi & bj; } } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ColorConvertCRIF.java0000644000175000017500000000304710203035544027162 0ustar mathieumathieu/* * $RCSfile: ColorConvertCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:17 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.ColorModel; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "ColorConvert" operation in the rendered * and renderable image layers. * * @see javax.media.jai.operator.ColorConvertDescriptor * @see ColorConvertOpImage * * @since EA4 * */ public class ColorConvertCRIF extends CRIFImpl { /** Constructor. */ public ColorConvertCRIF() { super("colorconvert"); } /** * Creates a new instance of ColorConvertOpImage in the * rendered layer. * * @param args The source image and the destination ColorModel. * @param hints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new ColorConvertOpImage(args.getRenderedSource(0), renderHints, layout, (ColorModel)args.getObjectParameter(0)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/BandCombineOpImage.java0000644000175000017500000003174310203035544027506 0ustar mathieumathieu/* * $RCSfile: BandCombineOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:15 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.ComponentSampleModel; import java.awt.image.WritableRaster; import java.util.Map; import javax.media.jai.ImageLayout; import javax.media.jai.PlanarImage; import javax.media.jai.PointOpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.RasterFactory; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.util.JDKWorkarounds; /** * An OpImage implementing the "BandCombine" operation. * *

This OpImage performs the arbitrary interband * linear combination of an image using the specified matrix. The * width of the matrix must be one larger that the number of bands * in the source image. The height of the matrix must be equal to * the number of bands in the destination image. Because the matrix * can be of arbitrary size, this function can be used to produce * a destination image with a different number of bands from the * source image. *

The destination image is formed by performing a matrix- * multiply operation between the bands of the source image and * the specified matrix. The extra column of values is a constant * that is added after the matrix-multiply operation takes place. * * @see javax.media.jai.operator.BandCombineDescriptor * @see BandCombineCRIF * * * @since EA3 */ final class BandCombineOpImage extends PointOpImage { private double[][] matrix; /** * Constructor. * * @param source The source image. * @param layout The destination image layout. * @param matrix The matrix of values used to perform the * linear combination. */ public BandCombineOpImage(RenderedImage source, Map config, ImageLayout layout, double[][] matrix) { super(source, layout, config, true); this.matrix = matrix; int numBands = matrix.length; // matrix height is dst numBands if (getSampleModel().getNumBands() != numBands) { sampleModel = RasterFactory.createComponentSampleModel(sampleModel, sampleModel.getDataType(), tileWidth, tileHeight, numBands); if(colorModel != null && !JDKWorkarounds.areCompatibleDataModels(sampleModel, colorModel)) { colorModel = ImageUtil.getCompatibleColorModel(sampleModel, config); } } } /** * Performs linear combination of source image with matrix * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); RasterAccessor s = new RasterAccessor(sources[0], destRect, formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor d = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); switch (d.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(s, d); break; case DataBuffer.TYPE_USHORT: computeRectUShort(s, d); break; case DataBuffer.TYPE_SHORT: computeRectShort(s, d); break; case DataBuffer.TYPE_INT: computeRectInt(s, d); break; case DataBuffer.TYPE_FLOAT: computeRectFloat(s, d); break; case DataBuffer.TYPE_DOUBLE: computeRectDouble(s, d); break; } if (d.isDataCopy()) { d.clampDataArrays(); d.copyDataToRaster(); } } private void computeRectByte(RasterAccessor src, RasterAccessor dst) { int sLineStride = src.getScanlineStride(); int sPixelStride = src.getPixelStride(); int sbands = src.getNumBands(); int[] sBandOffsets = src.getBandOffsets(); byte[][] sData = src.getByteDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dbands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); byte[][] dData = dst.getByteDataArrays(); int sso = 0, dso = 0; for (int h = 0; h < dheight; h++) { int spo = sso; int dpo = dso; for (int w = 0; w < dwidth; w++) { for (int b = 0; b < dbands; b++) { float sum = 0.0F; double[] mat = matrix[b]; for (int k = 0; k < sbands; k++ ) { sum += (float)mat[k] * (float)(sData[k][spo+sBandOffsets[k]] & 0xFF); } dData[b][dpo+dBandOffsets[b]] = ImageUtil.clampRoundByte(sum + (float)mat[sbands]); } spo += sPixelStride; dpo += dPixelStride; } sso += sLineStride; dso += dLineStride; } } private void computeRectUShort(RasterAccessor src, RasterAccessor dst) { int sLineStride = src.getScanlineStride(); int sPixelStride = src.getPixelStride(); int sbands = src.getNumBands(); int[] sBandOffsets = src.getBandOffsets(); short[][] sData = src.getShortDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dbands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); short[][] dData = dst.getShortDataArrays(); int sso = 0, dso = 0; for (int h = 0; h < dheight; h++) { int spo = sso; int dpo = dso; for (int w = 0; w < dwidth; w++) { for (int b = 0; b < dbands; b++) { float sum = 0.0F; double[] mat = matrix[b]; for (int k = 0; k < sbands; k++ ) { sum += (float)mat[k] * (float)(sData[k][spo+sBandOffsets[k]] & 0xFFFF); } dData[b][dpo+dBandOffsets[b]] = ImageUtil.clampRoundUShort(sum + (float)matrix[b][sbands]); } spo += sPixelStride; dpo += dPixelStride; } sso += sLineStride; dso += dLineStride; } } private void computeRectShort(RasterAccessor src, RasterAccessor dst) { int sLineStride = src.getScanlineStride(); int sPixelStride = src.getPixelStride(); int sbands = src.getNumBands(); int[] sBandOffsets = src.getBandOffsets(); short[][] sData = src.getShortDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dbands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); short[][] dData = dst.getShortDataArrays(); int sso = 0, dso = 0; for (int h = 0; h < dheight; h++) { int spo = sso; int dpo = dso; for (int w = 0; w < dwidth; w++) { for (int b = 0; b < dbands; b++) { float sum = 0.0F; double[] mat = matrix[b]; for (int k = 0; k < sbands; k++ ) { sum += (float)mat[k] * (float)(sData[k][spo+sBandOffsets[k]]); } dData[b][dpo+dBandOffsets[b]] = ImageUtil.clampRoundUShort(sum + (float)matrix[b][sbands]); } spo += sPixelStride; dpo += dPixelStride; } sso += sLineStride; dso += dLineStride; } } private void computeRectInt(RasterAccessor src, RasterAccessor dst) { int sLineStride = src.getScanlineStride(); int sPixelStride = src.getPixelStride(); int sbands = src.getNumBands(); int[] sBandOffsets = src.getBandOffsets(); int[][] sData = src.getIntDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dbands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); int[][] dData = dst.getIntDataArrays(); int sso = 0, dso = 0; for (int h = 0; h < dheight; h++) { int spo = sso; int dpo = dso; for (int w = 0; w < dwidth; w++) { for (int b = 0; b < dbands; b++) { float sum = 0.0F; double[] mat = matrix[b]; for (int k = 0; k < sbands; k++ ) { sum += (float)mat[k] * (float)(sData[k][spo+sBandOffsets[k]]); } dData[b][dpo+dBandOffsets[b]] = ImageUtil.clampRoundInt(sum + (float)matrix[b][sbands]); } spo += sPixelStride; dpo += dPixelStride; } sso += sLineStride; dso += dLineStride; } } private void computeRectFloat(RasterAccessor src, RasterAccessor dst) { int sLineStride = src.getScanlineStride(); int sPixelStride = src.getPixelStride(); int sbands = src.getNumBands(); int[] sBandOffsets = src.getBandOffsets(); float[][] sData = src.getFloatDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dbands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); float[][] dData = dst.getFloatDataArrays(); int sso = 0, dso = 0; for (int h = 0; h < dheight; h++) { int spo = sso; int dpo = dso; for (int w = 0; w < dwidth; w++) { for (int b = 0; b < dbands; b++) { float sum = 0.0F; double[] mat = matrix[b]; for (int k = 0; k < sbands; k++ ) { sum += (float)mat[k] * sData[k][spo+sBandOffsets[k]]; } dData[b][dpo+dBandOffsets[b]] = sum + (float)matrix[b][sbands]; } spo += sPixelStride; dpo += dPixelStride; } sso += sLineStride; dso += dLineStride; } } private void computeRectDouble(RasterAccessor src, RasterAccessor dst) { int sLineStride = src.getScanlineStride(); int sPixelStride = src.getPixelStride(); int sbands = src.getNumBands(); int[] sBandOffsets = src.getBandOffsets(); double[][] sData = src.getDoubleDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dbands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); double[][] dData = dst.getDoubleDataArrays(); int sso = 0, dso = 0; for (int h = 0; h < dheight; h++) { int spo = sso; int dpo = dso; for (int w = 0; w < dwidth; w++) { for (int b = 0; b < dbands; b++) { double sum = 0.0D; double[] mat = matrix[b]; for (int k = 0; k < sbands; k++ ) { sum += mat[k] * sData[k][spo+sBandOffsets[k]]; } dData[b][dpo+dBandOffsets[b]] = sum + matrix[b][sbands]; } spo += sPixelStride; dpo += dPixelStride; } sso += sLineStride; dso += dLineStride; } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/SubsampleAverageOpImage.java0000644000175000017500000007074210665127403030605 0ustar mathieumathieu/* * $RCSfile: SubsampleAverageOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.4 $ * $Date: 2007-08-28 23:25:55 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.geom.Point2D; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.util.Map; import javax.media.jai.GeometricOpImage; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.util.InterpAverage; public class SubsampleAverageOpImage extends GeometricOpImage { /* XXX public static void main(String[] args) throws Throwable { javax.media.jai.PlanarImage source = javax.media.jai.JAI.create("fileload", args[0]); double scaleX = args.length > 1 ? Double.valueOf(args[1]).doubleValue() : 0.25; double scaleY = args.length > 2 ? Double.valueOf(args[2]).doubleValue() : scaleX; source.getTiles(); javax.media.jai.PlanarImage dest = new SubsampleAverageOpImage(source, null, null, scaleX, scaleY); long t1 = System.currentTimeMillis(); dest.getTiles(); long t2 = System.currentTimeMillis(); System.out.println("Java time = "+(t2 - t1)); javax.media.jai.PlanarImage destML = new MlibSubsampleAverageOpImage(source, null, null, scaleX, scaleY); long t3 = System.currentTimeMillis(); destML.getTiles(); long t4 = System.currentTimeMillis(); System.out.println("Mlib time = "+(t4 - t3)); RenderedImage diff = javax.media.jai.JAI.create("subtract", javax.media.jai.JAI.create("format", dest, DataBuffer.TYPE_SHORT), javax.media.jai.JAI.create("format", destML, DataBuffer.TYPE_SHORT)); RenderedImage absDiff = javax.media.jai.JAI.create("absolute", diff); double[] maxima = (double[])javax.media.jai.JAI.create("extrema", absDiff).getProperty("maximum"); for(int i = 0; i < maxima.length; i++) { System.out.println(maxima[i]); } System.out.println(source.getClass().getName()+": "+ new ImageLayout(source)); System.out.println(dest.getClass().getName()+": "+ new ImageLayout(dest)); System.out.println(destML.getClass().getName()+": "+ new ImageLayout(destML)); java.awt.Frame frame = new java.awt.Frame("Mlib Sub-average Test"); frame.setLayout(new java.awt.GridLayout(1, 2)); javax.media.jai.widget.ScrollingImagePanel ps = new javax.media.jai.widget.ScrollingImagePanel(dest, 512, 512); javax.media.jai.widget.ScrollingImagePanel pd = new javax.media.jai.widget.ScrollingImagePanel(destML, 512, 512); frame.add(ps); frame.add(pd); frame.pack(); frame.show(); } */ /** The horizontal scale factor. */ protected double scaleX; /** The vertical scale factor. */ protected double scaleY; /** Horizontal size of an averaging block. */ protected int blockX; /** Vertical size of an averaging block. */ protected int blockY; /** Source image minimum x coordinate. */ protected int sourceMinX; /** Source image minimum y coordinate. */ protected int sourceMinY; private static ImageLayout layoutHelper(RenderedImage source, double scaleX, double scaleY, ImageLayout il) { if(scaleX <= 0.0 || scaleX > 1.0) { throw new IllegalArgumentException (JaiI18N.getString("SubsampleAverageOpImage0")); } else if(scaleY <= 0.0 || scaleY > 1.0) { throw new IllegalArgumentException (JaiI18N.getString("SubsampleAverageOpImage1")); } ImageLayout layout = (il == null) ? new ImageLayout() : (ImageLayout)il.clone(); layout.setMinX((int)Math.floor(source.getMinX()*scaleX)); layout.setMinY((int)Math.floor(source.getMinY()*scaleY)); layout.setWidth((int)(source.getWidth()*scaleX)); layout.setHeight((int)(source.getHeight()*scaleY)); return layout; } public SubsampleAverageOpImage(RenderedImage source, ImageLayout layout, Map config, double scaleX, double scaleY) { super(vectorize(source), layoutHelper(source, scaleX, scaleY, layout), config, true, // cobbleSources, null, // BorderExtender new InterpAverage((int)Math.ceil(1.0/scaleX), (int)Math.ceil(1.0/scaleY)), null); this.scaleX = scaleX; this.scaleY = scaleY; this.blockX = (int)Math.ceil(1.0/scaleX); this.blockY = (int)Math.ceil(1.0/scaleY); this.sourceMinX = source.getMinX(); this.sourceMinY = source.getMinY(); } public Point2D mapDestPoint(Point2D destPt) { if(destPt == null) { throw new IllegalArgumentException("destPt == null!"); } Point2D pt = (Point2D)destPt.clone(); pt.setLocation(sourceMinX + (destPt.getX() + 0.5 - minX)/scaleX - 0.5, sourceMinY + (destPt.getY() + 0.5 - minY)/scaleY - 0.5); return pt; } public Point2D mapSourcePoint(Point2D sourcePt) { if(sourcePt == null) { throw new IllegalArgumentException("sourcePt == null!"); } Point2D pt = (Point2D)sourcePt.clone(); pt.setLocation(minX + (sourcePt.getX() + 0.5 - sourceMinX)*scaleX - 0.5, minY + (sourcePt.getY() + 0.5 - sourceMinY)*scaleY - 0.5); return pt; } protected Rectangle backwardMapRect(Rectangle destRect, int sourceIndex) { if(destRect == null) { throw new IllegalArgumentException (JaiI18N.getString("Generic0")); } else if(sourceIndex != 0) { throw new IllegalArgumentException (JaiI18N.getString("Generic1")); } // Map the upper left pixel. Point2D p1 = mapDestPoint(new Point2D.Double(destRect.x, destRect.y)); // Map the lower right pixel. Point2D p2 = mapDestPoint(new Point2D.Double(destRect.x + destRect.width - 1, destRect.y + destRect.height - 1)); // Determine the integral positions. int x1 = (int)Math.floor(p1.getX()); int y1 = (int)Math.floor(p1.getY()); int x2 = (int)Math.ceil(p2.getX()); int y2 = (int)Math.ceil(p2.getY()); // Return rectangle based on integral positions. return new Rectangle(x1, y1, x2 - x1 + 1, y2 - y1 + 1); } protected Rectangle forwardMapRect(Rectangle sourceRect, int sourceIndex) { if(sourceRect == null) { throw new IllegalArgumentException (JaiI18N.getString("Generic0")); } else if(sourceIndex != 0) { throw new IllegalArgumentException (JaiI18N.getString("Generic1")); } // Map the upper left pixel. Point2D p1 = mapSourcePoint(new Point2D.Double(sourceRect.x, sourceRect.y)); // Map the lower right pixel. Point2D p2 = mapSourcePoint(new Point2D.Double(sourceRect.x + sourceRect.width - 1, sourceRect.y + sourceRect.height - 1)); // Determine the integral positions. int x1 = (int)Math.floor(p1.getX()); int y1 = (int)Math.floor(p1.getY()); int x2 = (int)Math.ceil(p2.getX()); int y2 = (int)Math.ceil(p2.getY()); // Return rectangle based on integral positions. return new Rectangle(x1, y1, x2 - x1 + 1, y2 - y1 + 1); } /** * Performs a subsampling operation on a specified rectangle. * The sources are cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster [] sources, WritableRaster dest, Rectangle destRect) { // Get RasterAccessor tags (initialized in OpImage superclass). RasterFormatTag[] formatTags = getFormatTags(); // Get destination accessor. RasterAccessor dst = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); // Backward map destination rectangle to source and clip to the // source image bounds (mapDestRect() does not clip automatically). Rectangle srcRect = mapDestRect(destRect, 0).intersection(sources[0].getBounds()); // Get source accessor. RasterAccessor src = new RasterAccessor(sources[0], srcRect, formatTags[0], getSourceImage(0).getColorModel()); switch(dst.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(src, dst); break; case DataBuffer.TYPE_USHORT: computeRectUShort(src, dst); break; case DataBuffer.TYPE_SHORT: computeRectShort(src, dst); break; case DataBuffer.TYPE_INT: computeRectInt(src, dst); break; case DataBuffer.TYPE_FLOAT: computeRectFloat(src, dst); break; case DataBuffer.TYPE_DOUBLE: computeRectDouble(src, dst); break; default: throw new RuntimeException (JaiI18N.getString("Generic3")); } // If the RasterAccessor set up a temporary write buffer for the // operator, tell it to copy that data to the destination Raster. if (dst.isDataCopy()) { dst.clampDataArrays(); dst.copyDataToRaster(); } } private void computeRectByte(RasterAccessor src, RasterAccessor dst) { // Get dimensions. int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); // Get destination data array references and strides. byte[][] dstDataArrays = dst.getByteDataArrays(); int[] dstBandOffsets = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); // Get source data array references and strides. byte[][] srcDataArrays = src.getByteDataArrays(); int[] srcBandOffsets = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); // Compute scaled source strides. int[] srcPixelStrideScaled = new int[dwidth]; for (int i = 0; i < dwidth; i++) srcPixelStrideScaled[i] = (int)Math.floor(i/scaleX)*srcPixelStride; int[] srcScanlineStrideScaled = new int[dheight]; for (int i = 0; i < dheight; i++) srcScanlineStrideScaled[i] = (int)Math.floor(i/scaleY)*srcScanlineStride; // Cache the product of the block dimensions. float denom = blockX*blockY; for (int k = 0; k < dnumBands; k++) { byte[] dstData = dstDataArrays[k]; byte[] srcData = srcDataArrays[k]; int srcScanlineOffset0 = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; int srcScanlineOffset = srcScanlineOffset0; for (int j = 0; j < dheight; j++) { int srcPixelOffset0 = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; int srcPixelOffset = srcPixelOffset0; for (int i = 0; i < dwidth; i++) { int imageVerticalOffset = srcPixelOffset; // Average the source over the scale-dependent window. int sum = 0; for (int u = 0; u < blockY; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < blockX; v++) { sum += (int)(srcData[imageOffset]&0xff); imageOffset += srcPixelStride; } imageVerticalOffset += srcScanlineStride; } dstData[dstPixelOffset] = ImageUtil.clampRoundByte(sum / denom); srcPixelOffset = srcPixelOffset0 + srcPixelStrideScaled[i]; dstPixelOffset += dstPixelStride; } srcScanlineOffset = srcScanlineOffset0 + srcScanlineStrideScaled[j]; dstScanlineOffset += dstScanlineStride; } } } private void computeRectUShort(RasterAccessor src, RasterAccessor dst) { // Get dimensions. int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); // Get destination data array references and strides. short[][] dstDataArrays = dst.getShortDataArrays(); int[] dstBandOffsets = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); // Get source data array references and strides. short[][] srcDataArrays = src.getShortDataArrays(); int[] srcBandOffsets = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); // Compute scaled source strides. int[] srcPixelStrideScaled = new int[dwidth]; for (int i = 0; i < dwidth; i++) srcPixelStrideScaled[i] = (int)Math.floor(i/scaleX)*srcPixelStride; int[] srcScanlineStrideScaled = new int[dheight]; for (int i = 0; i < dheight; i++) srcScanlineStrideScaled[i] = (int)Math.floor(i/scaleY)*srcScanlineStride; // Cache the product of the block dimensions. float denom = blockX*blockY; for (int k = 0; k < dnumBands; k++) { short[] dstData = dstDataArrays[k]; short[] srcData = srcDataArrays[k]; int srcScanlineOffset0 = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; int srcScanlineOffset = srcScanlineOffset0; for (int j = 0; j < dheight; j++) { int srcPixelOffset0 = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; int srcPixelOffset = srcPixelOffset0; for (int i = 0; i < dwidth; i++) { int imageVerticalOffset = srcPixelOffset; // Average the source over the scale-dependent window. long sum = 0; for (int u = 0; u < blockY; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < blockX; v++) { sum += (long)(srcData[imageOffset]&0xffff); imageOffset += srcPixelStride; } imageVerticalOffset += srcScanlineStride; } dstData[dstPixelOffset] = ImageUtil.clampRoundUShort(sum / denom); srcPixelOffset = srcPixelOffset0 + srcPixelStrideScaled[i]; dstPixelOffset += dstPixelStride; } srcScanlineOffset = srcScanlineOffset0 + srcScanlineStrideScaled[j]; dstScanlineOffset += dstScanlineStride; } } } private void computeRectShort(RasterAccessor src, RasterAccessor dst) { // Get dimensions. int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); // Get destination data array references and strides. short[][] dstDataArrays = dst.getShortDataArrays(); int[] dstBandOffsets = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); // Get source data array references and strides. short[][] srcDataArrays = src.getShortDataArrays(); int[] srcBandOffsets = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); // Compute scaled source strides. int[] srcPixelStrideScaled = new int[dwidth]; for (int i = 0; i < dwidth; i++) srcPixelStrideScaled[i] = (int)Math.floor(i/scaleX)*srcPixelStride; int[] srcScanlineStrideScaled = new int[dheight]; for (int i = 0; i < dheight; i++) srcScanlineStrideScaled[i] = (int)Math.floor(i/scaleY)*srcScanlineStride; // Cache the product of the block dimensions. float denom = blockX*blockY; for (int k = 0; k < dnumBands; k++) { short[] dstData = dstDataArrays[k]; short[] srcData = srcDataArrays[k]; int srcScanlineOffset0 = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; int srcScanlineOffset = srcScanlineOffset0; for (int j = 0; j < dheight; j++) { int srcPixelOffset0 = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; int srcPixelOffset = srcPixelOffset0; for (int i = 0; i < dwidth; i++) { int imageVerticalOffset = srcPixelOffset; // Average the source over the scale-dependent window. long sum = 0; for (int u = 0; u < blockY; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < blockX; v++) { sum += srcData[imageOffset]; imageOffset += srcPixelStride; } imageVerticalOffset += srcScanlineStride; } dstData[dstPixelOffset] = ImageUtil.clampRoundShort(sum / denom); srcPixelOffset = srcPixelOffset0 + srcPixelStrideScaled[i]; dstPixelOffset += dstPixelStride; } srcScanlineOffset = srcScanlineOffset0 + srcScanlineStrideScaled[j]; dstScanlineOffset += dstScanlineStride; } } } private void computeRectInt(RasterAccessor src, RasterAccessor dst) { // Get dimensions. int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); // Get destination data array references and strides. int[][] dstDataArrays = dst.getIntDataArrays(); int[] dstBandOffsets = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); // Get source data array references and strides. int[][] srcDataArrays = src.getIntDataArrays(); int[] srcBandOffsets = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); // Compute scaled source strides. int[] srcPixelStrideScaled = new int[dwidth]; for (int i = 0; i < dwidth; i++) srcPixelStrideScaled[i] = (int)Math.floor(i/scaleX)*srcPixelStride; int[] srcScanlineStrideScaled = new int[dheight]; for (int i = 0; i < dheight; i++) srcScanlineStrideScaled[i] = (int)Math.floor(i/scaleY)*srcScanlineStride; // Cache the product of the block dimensions. float denom = blockX*blockY; for (int k = 0; k < dnumBands; k++) { int[] dstData = dstDataArrays[k]; int[] srcData = srcDataArrays[k]; int srcScanlineOffset0 = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; int srcScanlineOffset = srcScanlineOffset0; for (int j = 0; j < dheight; j++) { int srcPixelOffset0 = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; int srcPixelOffset = srcPixelOffset0; for (int i = 0; i < dwidth; i++) { int imageVerticalOffset = srcPixelOffset; // Average the source over the scale-dependent window. double sum = 0; for (int u = 0; u < blockY; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < blockX; v++) { sum += srcData[imageOffset]; imageOffset += srcPixelStride; } imageVerticalOffset += srcScanlineStride; } dstData[dstPixelOffset] = ImageUtil.clampRoundInt(sum / denom); srcPixelOffset = srcPixelOffset0 + srcPixelStrideScaled[i]; dstPixelOffset += dstPixelStride; } srcScanlineOffset = srcScanlineOffset0 + srcScanlineStrideScaled[j]; dstScanlineOffset += dstScanlineStride; } } } private void computeRectFloat(RasterAccessor src, RasterAccessor dst) { // Get dimensions. int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); // Get destination data array references and strides. float[][] dstDataArrays = dst.getFloatDataArrays(); int[] dstBandOffsets = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); // Get source data array references and strides. float[][] srcDataArrays = src.getFloatDataArrays(); int[] srcBandOffsets = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); // Compute scaled source strides. int[] srcPixelStrideScaled = new int[dwidth]; for (int i = 0; i < dwidth; i++) srcPixelStrideScaled[i] = (int)Math.floor(i/scaleX)*srcPixelStride; int[] srcScanlineStrideScaled = new int[dheight]; for (int i = 0; i < dheight; i++) srcScanlineStrideScaled[i] = (int)Math.floor(i/scaleY)*srcScanlineStride; // Cache the product of the block dimensions. float denom = blockX*blockY; for (int k = 0; k < dnumBands; k++) { float[] dstData = dstDataArrays[k]; float[] srcData = srcDataArrays[k]; int srcScanlineOffset0 = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; int srcScanlineOffset = srcScanlineOffset0; for (int j = 0; j < dheight; j++) { int srcPixelOffset0 = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; int srcPixelOffset = srcPixelOffset0; for (int i = 0; i < dwidth; i++) { int imageVerticalOffset = srcPixelOffset; // Average the source over the scale-dependent window. double sum = 0; for (int u = 0; u < blockY; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < blockX; v++) { sum += srcData[imageOffset]; imageOffset += srcPixelStride; } imageVerticalOffset += srcScanlineStride; } dstData[dstPixelOffset] = ImageUtil.clampFloat(sum / denom); srcPixelOffset = srcPixelOffset0 + srcPixelStrideScaled[i]; dstPixelOffset += dstPixelStride; } srcScanlineOffset = srcScanlineOffset0 + srcScanlineStrideScaled[j]; dstScanlineOffset += dstScanlineStride; } } } private void computeRectDouble(RasterAccessor src, RasterAccessor dst) { // Get dimensions. int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); // Get destination data array references and strides. double[][] dstDataArrays = dst.getDoubleDataArrays(); int[] dstBandOffsets = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); // Get source data array references and strides. double[][] srcDataArrays = src.getDoubleDataArrays(); int[] srcBandOffsets = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); // Compute scaled source strides. int[] srcPixelStrideScaled = new int[dwidth]; for (int i = 0; i < dwidth; i++) srcPixelStrideScaled[i] = (int)Math.floor(i/scaleX)*srcPixelStride; int[] srcScanlineStrideScaled = new int[dheight]; for (int i = 0; i < dheight; i++) srcScanlineStrideScaled[i] = (int)Math.floor(i/scaleY)*srcScanlineStride; // Cache the product of the block dimensions. double denom = blockX*blockY; for (int k = 0; k < dnumBands; k++) { double[] dstData = dstDataArrays[k]; double[] srcData = srcDataArrays[k]; int srcScanlineOffset0 = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; int srcScanlineOffset = srcScanlineOffset0; for (int j = 0; j < dheight; j++) { int srcPixelOffset0 = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; int srcPixelOffset = srcPixelOffset0; for (int i = 0; i < dwidth; i++) { int imageVerticalOffset = srcPixelOffset; // Average the source over the scale-dependent window. double sum = 0; for (int u = 0; u < blockY; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < blockX; v++) { sum += srcData[imageOffset]; imageOffset += srcPixelStride; } imageVerticalOffset += srcScanlineStride; } dstData[dstPixelOffset] = sum / denom; srcPixelOffset = srcPixelOffset0 + srcPixelStrideScaled[i]; dstPixelOffset += dstPixelStride; } srcScanlineOffset = srcScanlineOffset0 + srcScanlineStrideScaled[j]; dstScanlineOffset += dstScanlineStride; } } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/RescaleOpImage.java0000644000175000017500000003637010203035544026724 0ustar mathieumathieu/* * $RCSfile: RescaleOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:41 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.ColormapOpImage; import javax.media.jai.ImageLayout; import javax.media.jai.PointOpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; import com.sun.media.jai.util.ImageUtil; /** * An OpImage implementing the "Rescale" operation. * *

The "Rescale" operation maps the pixel values of an image from * one range to another range by multiplying each pixel value by one * of a set of constants and then adding another constant to the * result of the multiplication. The pixel values of the destination * image are defined by the pseudocode: * *

 *     for (int h = 0; h < dstHeight; h++) {
 *         for (int w = 0; w < dstWidth; w++) {
 *             for (int b = 0; b < dstNumBands; b++) {
 *		   scale = (scales.length < dstNumBands)? 
 *			    scales[0]:scales[b];
 *		   offset = (offsets.length < dstNumBands)?
 *			    offsets[0]:offsets[b];
 *                 dst[h][w][b] = srcs[h][w][b] * scale + offset;
 *             }
 *         }
 *     }
 * 
* * @see javax.media.jai.operator.RescaleDescriptor * @see RescaleCRIF * * * @since EA3 */ final class RescaleOpImage extends ColormapOpImage { /** The constants to be multiplied, one for each band. */ protected double[] constants; protected double[] offsets; private byte[][] byteTable = null; private synchronized void initByteTable() { if (byteTable != null) { return; } int nbands = constants.length; byteTable = new byte[nbands][256]; // Initialize table which implements Rescale and clamp for(int band=0; bandRenderingHints.Keys and image properties indexed * by Strings or CaselessStringKeys. * This is simply forwarded to the superclass constructor. * @param layout The destination image layout. * @param constants The constants to be multiplied, stored as reference. * @param offsets The offsets to be added, stored as reference. */ public RescaleOpImage(RenderedImage source, Map config, ImageLayout layout, double[] constants, double[] offsets) { super(source, layout, config, true); int numBands = getSampleModel().getNumBands(); if (constants.length < numBands) { this.constants = new double[numBands]; for (int i = 0; i < numBands; i++) { this.constants[i] = constants[0]; } } else { this.constants = constants; } if (offsets.length < numBands) { this.offsets = new double[numBands]; for (int i = 0; i < numBands; i++) { this.offsets[i] = offsets[0]; } } else { this.offsets = offsets; } // Set flag to permit in-place operation. permitInPlaceOperation(); // Initialize the colormap if necessary. initializeColormapOperation(); } /** * Transform the colormap according to the rescaling parameters. */ protected void transformColormap(byte[][] colormap) { for (int b = 0; b < 3; b++) { byte[] map = colormap[b]; int mapSize = map.length; float c = (float)(b < constants.length ? constants[b] : constants[0]); float o = (float)(b < constants.length ? offsets[b] : offsets[0]); for (int i = 0; i < mapSize; i++) { map[i] = ImageUtil.clampRoundByte((map[i] & 0xFF) * c + o); } } } /** * Rescales to the pixel values within a specified rectangle. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); Rectangle srcRect = mapDestRect(destRect, 0); RasterAccessor dst = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); RasterAccessor src = new RasterAccessor(sources[0], srcRect, formatTags[0], getSource(0).getColorModel()); switch (dst.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(src, dst); break; case DataBuffer.TYPE_USHORT: computeRectUShort(src, dst); break; case DataBuffer.TYPE_SHORT: computeRectShort(src, dst); break; case DataBuffer.TYPE_INT: computeRectInt(src, dst); break; case DataBuffer.TYPE_FLOAT: computeRectFloat(src, dst); break; case DataBuffer.TYPE_DOUBLE: computeRectDouble(src, dst); break; } if (dst.needsClamping()) { /* Further clamp down to underlying raster data type. */ dst.clampDataArrays(); } dst.copyDataToRaster(); } private void computeRectByte(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); byte[][] dstData = dst.getByteDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); byte[][] srcData = src.getByteDataArrays(); initByteTable(); for (int b = 0; b < dstBands; b++) { byte[] d = dstData[b]; byte[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; byte[] clamp = byteTable[b]; double c = constants[b]; double o = offsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = clamp[s[srcPixelOffset] & 0xFF]; dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectUShort(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); short[][] dstData = dst.getShortDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); short[][] srcData = src.getShortDataArrays(); for (int b = 0; b < dstBands; b++) { float c = (float)constants[b]; float o = (float)offsets[b]; short[] d = dstData[b]; short[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = ImageUtil.clampRoundUShort( (s[srcPixelOffset] & 0xFFFF) * c + o); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectShort(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); short[][] dstData = dst.getShortDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); short[][] srcData = src.getShortDataArrays(); for (int b = 0; b < dstBands; b++) { float c = (float)constants[b]; float o = (float)offsets[b]; short[] d = dstData[b]; short[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = ImageUtil.clampRoundShort(s[srcPixelOffset] * c + o); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectInt(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); int[][] dstData = dst.getIntDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); int[][] srcData = src.getIntDataArrays(); for (int b = 0; b < dstBands; b++) { double c = constants[b]; double o = offsets[b]; int[] d = dstData[b]; int[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = ImageUtil.clampRoundInt(s[srcPixelOffset] * c + o); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectFloat(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); float[][] dstData = dst.getFloatDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); float[][] srcData = src.getFloatDataArrays(); for (int b = 0; b < dstBands; b++) { double c = constants[b]; double o = offsets[b]; float[] d = dstData[b]; float[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = ImageUtil.clampFloat(s[srcPixelOffset] * c + o); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectDouble(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); double[][] dstData = dst.getDoubleDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); double[][] srcData = src.getDoubleDataArrays(); for (int b = 0; b < dstBands; b++) { double c = constants[b]; double o = offsets[b]; double[] d = dstData[b]; double[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = s[srcPixelOffset] * c + o; dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ScaleNearestBinaryOpImage.java0000644000175000017500000005155410203035544031065 0ustar mathieumathieu/* * $RCSfile: ScaleNearestBinaryOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:42 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferInt; import java.awt.image.DataBufferUShort; import java.awt.image.IndexColorModel; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationNearest; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PlanarImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.ScaleOpImage; import java.util.Map; import javax.media.jai.BorderExtender; import com.sun.media.jai.util.Rational; /** * An OpImage subclass that performs nearest-neighbor scaling * for binary images with a MultiPixelPackedSampleModel * and byte, short, or int DataBuffers. * */ final class ScaleNearestBinaryOpImage extends ScaleOpImage { long invScaleXInt, invScaleXFrac; long invScaleYInt, invScaleYFrac; /** * Constructs a ScaleNearestBinaryOpImage from a RenderedImage source, * * @param source a RenderedImage. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param xScale scale factor along x axis. * @param yScale scale factor along y axis. * @param xTrans translation factor along x axis. * @param yTrans translation factor along y axis. * @param interp an Interpolation object to use for resampling. */ public ScaleNearestBinaryOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, float xScale, float yScale, float xTrans, float yTrans, Interpolation interp) { super(source, layout, config, true, extender, interp, xScale, yScale, xTrans, yTrans); // Propagate source's ColorModel if (layout != null) { colorModel = layout.getColorModel(source); } else { colorModel = source.getColorModel(); } sampleModel = source.getSampleModel().createCompatibleSampleModel(tileWidth, tileHeight); if (invScaleXRational.num > invScaleXRational.denom) { invScaleXInt = invScaleXRational.num / invScaleXRational.denom; invScaleXFrac = invScaleXRational.num % invScaleXRational.denom; } else { invScaleXInt = 0; invScaleXFrac = invScaleXRational.num; } if (invScaleYRational.num > invScaleYRational.denom) { invScaleYInt = invScaleYRational.num / invScaleYRational.denom; invScaleYFrac = invScaleYRational.num % invScaleYRational.denom; } else { invScaleYInt = 0; invScaleYFrac = invScaleYRational.num; } } /** * Performs a scale operation on a specified rectangle. The sources are * cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; // Get the source rectangle Rectangle srcRect = source.getBounds(); int srcRectX = srcRect.x; int srcRectY = srcRect.y; // Destination rectangle dimensions. int dx = destRect.x; int dy = destRect.y; int dwidth = destRect.width; int dheight = destRect.height; // Precalculate the x positions and store them in an array. int[] xvalues = new int[dwidth]; long sxNum = dx, sxDenom = 1; // Subtract the X translation factor sx -= transX sxNum = sxNum * transXRationalDenom - transXRationalNum * sxDenom; sxDenom *= transXRationalDenom; // Add 0.5 sxNum = 2*sxNum + sxDenom; sxDenom *= 2; // Multply by invScaleX sxNum *= invScaleXRationalNum; sxDenom *= invScaleXRationalDenom; // Separate the x source coordinate into integer and fractional part // int part is floor(sx), frac part is sx - floor(sx) int srcXInt = Rational.floor(sxNum , sxDenom); long srcXFrac = sxNum % sxDenom; if (srcXInt < 0) { srcXFrac = sxDenom + srcXFrac; } // Normalize - Get a common denominator for the fracs of // src and invScaleX long commonXDenom = sxDenom*invScaleXRationalDenom; srcXFrac *= invScaleXRationalDenom; long newInvScaleXFrac = invScaleXFrac*sxDenom; for (int i = 0; i < dwidth; i++) { // Calculate the position xvalues[i] = srcXInt; // Move onto the next source pixel. // Add the integral part of invScaleX to the integral part // of srcX srcXInt += invScaleXInt; // Add the fractional part of invScaleX to the fractional part // of srcX srcXFrac += newInvScaleXFrac; // If the fractional part is now greater than equal to the // denominator, divide so as to reduce the numerator to be less // than the denominator and add the overflow to the integral part. if (srcXFrac >= commonXDenom) { srcXInt += 1; srcXFrac -= commonXDenom; } } // Precalculate the y positions and store them in an array. int[] yvalues = new int[dheight]; long syNum = dy, syDenom = 1; // Subtract the X translation factor sy -= transY syNum = syNum*transYRationalDenom - transYRationalNum*syDenom; syDenom *= transYRationalDenom; // Add 0.5 syNum = 2*syNum + syDenom; syDenom *= 2; // Multply by invScaleX syNum *= invScaleYRationalNum; syDenom *= invScaleYRationalDenom; // Separate the x source coordinate into integer and fractional part int srcYInt = Rational.floor(syNum, syDenom); long srcYFrac = syNum % syDenom; if (srcYInt < 0) { srcYFrac = syDenom + srcYFrac; } // Normalize - Get a common denominator for the fracs of // src and invScaleY long commonYDenom = syDenom * invScaleYRationalDenom; srcYFrac *= invScaleYRationalDenom; long newInvScaleYFrac = invScaleYFrac * syDenom; for (int i = 0; i < dheight; i++) { // Calculate the position yvalues[i] = srcYInt; // Move onto the next source pixel. // Add the integral part of invScaleY to the integral part // of srcY srcYInt += invScaleYInt; // Add the fractional part of invScaleY to the fractional part // of srcY srcYFrac += newInvScaleYFrac; // If the fractional part is now greater than equal to the // denominator, divide so as to reduce the numerator to be less // than the denominator and add the overflow to the integral part. if (srcYFrac >= commonYDenom) { srcYInt += 1; srcYFrac -= commonYDenom; } } switch (source.getSampleModel().getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(source, dest, destRect, xvalues, yvalues); break; case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_USHORT: shortLoop(source, dest, destRect, xvalues, yvalues); break; case DataBuffer.TYPE_INT: intLoop(source, dest, destRect, xvalues, yvalues); break; default: throw new RuntimeException(JaiI18N.getString("OrderedDitherOpImage0")); } } private void byteLoop(Raster source, WritableRaster dest, Rectangle destRect, int xvalues[], int yvalues[]) { int dx = destRect.x; int dy = destRect.y; int dwidth = destRect.width; int dheight = destRect.height; MultiPixelPackedSampleModel sourceSM = (MultiPixelPackedSampleModel)source.getSampleModel(); DataBufferByte sourceDB = (DataBufferByte)source.getDataBuffer(); int sourceTransX = source.getSampleModelTranslateX(); int sourceTransY = source.getSampleModelTranslateY(); int sourceDataBitOffset = sourceSM.getDataBitOffset(); int sourceScanlineStride = sourceSM.getScanlineStride(); MultiPixelPackedSampleModel destSM = (MultiPixelPackedSampleModel)dest.getSampleModel(); DataBufferByte destDB = (DataBufferByte)dest.getDataBuffer(); int destMinX = dest.getMinX(); int destMinY = dest.getMinY(); int destTransX = dest.getSampleModelTranslateX(); int destTransY = dest.getSampleModelTranslateY(); int destDataBitOffset = destSM.getDataBitOffset(); int destScanlineStride = destSM.getScanlineStride(); byte[] sourceData = sourceDB.getData(); int sourceDBOffset = sourceDB.getOffset(); byte[] destData = destDB.getData(); int destDBOffset = destDB.getOffset(); int[] sbytenum = new int[dwidth]; int[] sshift = new int[dwidth]; for (int i = 0; i < dwidth; i++) { int x = xvalues[i]; int sbitnum = sourceDataBitOffset + (x - sourceTransX); sbytenum[i] = sbitnum >> 3; sshift[i] = 7 - (sbitnum & 7); } for (int j = 0; j < dheight; j++) { int y = yvalues[j]; int sourceYOffset = (y - sourceTransY)*sourceScanlineStride + sourceDBOffset; int destYOffset = (j + dy - destTransY)*destScanlineStride + destDBOffset; int dbitnum = destDataBitOffset + (dx - destTransX); int selement, val, dindex, dshift, delement; int i = 0; while ((i < dwidth) && ((dbitnum & 7) != 0)) { selement = sourceData[sourceYOffset + sbytenum[i]]; val = (selement >> sshift[i]) & 0x1; dindex = destYOffset + (dbitnum >> 3); dshift = 7 - (dbitnum & 7); delement = destData[dindex]; delement |= val << dshift; destData[dindex] = (byte)delement; ++dbitnum; ++i; } dindex = destYOffset + (dbitnum >> 3); int nbytes = (dwidth - i + 1) >>3; if (nbytes > 0 && (j > 0) && (y == yvalues[j - 1])) { // Copy central portion of previous scanline System.arraycopy(destData, dindex - destScanlineStride, destData, dindex, nbytes); i += nbytes * 8; dbitnum += nbytes * 8; } else { while (i < dwidth - 7) { selement = sourceData[sourceYOffset + sbytenum[i]]; val = (selement >> sshift[i]) & 0x1; delement = val << 7; // Set initial value ++i; selement = sourceData[sourceYOffset + sbytenum[i]]; val = (selement >> sshift[i]) & 0x1; delement |= val << 6; ++i; selement = sourceData[sourceYOffset + sbytenum[i]]; val = (selement >> sshift[i]) & 0x1; delement |= val << 5; ++i; selement = sourceData[sourceYOffset + sbytenum[i]]; val = (selement >> sshift[i]) & 0x1; delement |= val << 4; ++i; selement = sourceData[sourceYOffset + sbytenum[i]]; val = (selement >> sshift[i]) & 0x1; delement |= val << 3; ++i; selement = sourceData[sourceYOffset + sbytenum[i]]; val = (selement >> sshift[i]) & 0x1; delement |= val << 2; ++i; selement = sourceData[sourceYOffset + sbytenum[i]]; val = (selement >> sshift[i]) & 0x1; delement |= val << 1; ++i; selement = sourceData[sourceYOffset + sbytenum[i]]; val = (selement >> sshift[i]) & 0x1; delement |= val; ++i; destData[dindex++] = (byte)delement; dbitnum += 8; } } if (i < dwidth) { dindex = destYOffset + (dbitnum >> 3); delement = destData[dindex]; while (i < dwidth) { selement = sourceData[sourceYOffset + sbytenum[i]]; val = (selement >> sshift[i]) & 0x1; dshift = 7 - (dbitnum & 7); delement |= val << dshift; ++dbitnum; ++i; } destData[dindex] = (byte)delement; } } } private void shortLoop(Raster source, WritableRaster dest, Rectangle destRect, int xvalues[], int yvalues[]) { int dx = destRect.x; int dy = destRect.y; int dwidth = destRect.width; int dheight = destRect.height; MultiPixelPackedSampleModel sourceSM = (MultiPixelPackedSampleModel)source.getSampleModel(); int sourceTransX = source.getSampleModelTranslateX(); int sourceTransY = source.getSampleModelTranslateY(); int sourceDataBitOffset = sourceSM.getDataBitOffset(); int sourceScanlineStride = sourceSM.getScanlineStride(); MultiPixelPackedSampleModel destSM = (MultiPixelPackedSampleModel)dest.getSampleModel(); int destMinX = dest.getMinX(); int destMinY = dest.getMinY(); int destTransX = dest.getSampleModelTranslateX(); int destTransY = dest.getSampleModelTranslateY(); int destDataBitOffset = destSM.getDataBitOffset(); int destScanlineStride = destSM.getScanlineStride(); DataBufferUShort sourceDB = (DataBufferUShort)source.getDataBuffer(); short[] sourceData = sourceDB.getData(); int sourceDBOffset = sourceDB.getOffset(); DataBufferUShort destDB = (DataBufferUShort)dest.getDataBuffer(); short[] destData = destDB.getData(); int destDBOffset = destDB.getOffset(); int[] sshortnum = new int[dwidth]; int[] sshift = new int[dwidth]; for (int i = 0; i < dwidth; i++) { int x = xvalues[i]; int sbitnum = sourceDataBitOffset + (x - sourceTransX); sshortnum[i] = sbitnum >> 4; sshift[i] = 15 - (sbitnum & 15); } for (int j = 0; j < dheight; j++) { int y = yvalues[j]; int sourceYOffset = (y - sourceTransY)*sourceScanlineStride + sourceDBOffset; int destYOffset = (j + dy - destTransY)*destScanlineStride + destDBOffset; int dbitnum = destDataBitOffset + (dx - destTransX); int selement, val, dindex, dshift, delement; int i = 0; while ((i < dwidth) && ((dbitnum & 15) != 0)) { selement = sourceData[sourceYOffset + sshortnum[i]]; val = (selement >> sshift[i]) & 0x1; dindex = destYOffset + (dbitnum >> 4); dshift = 15 - (dbitnum & 15); delement = destData[dindex]; delement |= val << dshift; destData[dindex] = (short)delement; ++dbitnum; ++i; } dindex = destYOffset + (dbitnum >> 4); int nshorts = (dwidth - i) >> 4; if (nshorts > 0 && (j > 0) && (y == yvalues[j - 1])) { // Copy previous scanline int offset = destYOffset + (dbitnum >> 4); System.arraycopy(destData, offset - destScanlineStride, destData, offset, nshorts); i += nshorts >> 4; dbitnum += nshorts >> 4; } else { while (i < dwidth - 15) { delement = 0; for (int b = 15; b >= 0; b--) { selement = sourceData[sourceYOffset + sshortnum[i]]; val = (selement >> sshift[i]) & 0x1; delement |= val << b; ++i; } destData[dindex++] = (short)delement; dbitnum += 16; } } if (i < dwidth) { dindex = destYOffset + (dbitnum >> 4); delement = destData[dindex]; while (i < dwidth) { selement = sourceData[sourceYOffset + sshortnum[i]]; val = (selement >> sshift[i]) & 0x1; dshift = 15 - (dbitnum & 15); delement |= val << dshift; ++dbitnum; ++i; } destData[dindex] = (short)delement; } } } private void intLoop(Raster source, WritableRaster dest, Rectangle destRect, int xvalues[], int yvalues[]) { int dx = destRect.x; int dy = destRect.y; int dwidth = destRect.width; int dheight = destRect.height; MultiPixelPackedSampleModel sourceSM = (MultiPixelPackedSampleModel)source.getSampleModel(); DataBufferInt sourceDB = (DataBufferInt)source.getDataBuffer(); int sourceTransX = source.getSampleModelTranslateX(); int sourceTransY = source.getSampleModelTranslateY(); int sourceDataBitOffset = sourceSM.getDataBitOffset(); int sourceScanlineStride = sourceSM.getScanlineStride(); MultiPixelPackedSampleModel destSM = (MultiPixelPackedSampleModel)dest.getSampleModel(); DataBufferInt destDB = (DataBufferInt)dest.getDataBuffer(); int destMinX = dest.getMinX(); int destMinY = dest.getMinY(); int destTransX = dest.getSampleModelTranslateX(); int destTransY = dest.getSampleModelTranslateY(); int destDataBitOffset = destSM.getDataBitOffset(); int destScanlineStride = destSM.getScanlineStride(); int[] sourceData = sourceDB.getData(); int sourceDBOffset = sourceDB.getOffset(); int[] destData = destDB.getData(); int destDBOffset = destDB.getOffset(); int[] sintnum = new int[dwidth]; int[] sshift = new int[dwidth]; for (int i = 0; i < dwidth; i++) { int x = xvalues[i]; int sbitnum = sourceDataBitOffset + (x - sourceTransX); sintnum[i] = sbitnum >> 5; sshift[i] = 31 - (sbitnum & 31); } for (int j = 0; j < dheight; j++) { int y = yvalues[j]; int sourceYOffset = (y - sourceTransY)*sourceScanlineStride + sourceDBOffset; int destYOffset = (j + dy - destTransY)*destScanlineStride + destDBOffset; int dbitnum = destDataBitOffset + (dx - destTransX); int selement, val, dindex, dshift, delement; int i = 0; while ((i < dwidth) && ((dbitnum & 31) != 0)) { selement = sourceData[sourceYOffset + sintnum[i]]; val = (selement >> sshift[i]) & 0x1; dindex = destYOffset + (dbitnum >> 5); dshift = 31 - (dbitnum & 31); delement = destData[dindex]; delement |= val << dshift; destData[dindex] = delement; ++dbitnum; ++i; } dindex = destYOffset + (dbitnum >> 5); int nints = (dwidth - i) >> 5; if (nints > 0 && (j > 0) && (y == yvalues[j - 1])) { // Copy previous scanline int offset = destYOffset + (dbitnum >> 5); System.arraycopy(destData, offset - destScanlineStride, destData, offset, nints); i += nints >> 5; dbitnum += nints >> 5; } else { while (i < dwidth - 31) { delement = 0; for (int b = 31; b >= 0; b--) { selement = sourceData[sourceYOffset + sintnum[i]]; val = (selement >> sshift[i]) & 0x1; delement |= val << b; ++i; } destData[dindex++] = delement; dbitnum += 32; } } if (i < dwidth) { dindex = destYOffset + (dbitnum >> 5); delement = destData[dindex]; while (i < dwidth) { selement = sourceData[sourceYOffset + sintnum[i]]; val = (selement >> sshift[i]) & 0x1; dshift = 31 - (dbitnum & 31); delement |= val << dshift; ++dbitnum; ++i; } destData[dindex] = delement; } } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ErodeOpImage.java0000644000175000017500000005160010203035544026375 0ustar mathieumathieu/* * $RCSfile: ErodeOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:24 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; // import com.sun.media.jai.test.OpImageTester; /** * * An OpImage class to perform erosion on a source image. * *

This class implements an erosion operation. * *

Grey Scale Erosion * is a spatial operation that computes * each output sample by subtract elements of a kernel to the samples * surrounding a particular source sample with some care. * A mathematical expression is: * *

For a kernel K with a key position (xKey, yKey), the erosion * of image I at (x,y) is given by: *

 *     max{a:  a + K(xKey+i, yKey+j) <= I(x+i,y+j): all (i,j) }
 *
 *      all possible (i,j) means that both I(x+i,y+j) and K(xKey+i, yKey+j)
 *      are in bounds. Otherwise, the value is set to 0.
 *
 * 
*

Intuitively, the kernel is like an unbrella and the key point * is the handle. At every point, you try to push the umbrella up as high * as possible but still underneath the image surface. The final height * of the handle is the value after erosion. Thus if you want the image * to erode from the upper right to bottom left, the following would do. * *

* * * * *
00X
0X0
X00
* *

Note that zero kernel erosion has effects on the image, the * location of the key position and size of kernel all matter. * *

Pseudo code for the erosion operation is as follows. * Assuming the kernel K is of size M rows x N cols * and the key position is (xKey, yKey). * *

 * 
 * // erosion
 * for every dst pixel location (x,y){
 *    tmp = infinity;
 *    for (i = -xKey; i < M - xKey; i++){
 *       for (j = -yKey; j < N - yKey; j++){
 *          if((x+i, y+j) are in bounds of src){
 *             tmp = min{tmp, src[x + i][y + j] - K[xKey + i][yKey + j]};
 *          }
 *       }
 *    }
 *    dst[x][y] = tmp;
 *    if (dst[x][y] == infinity)
 *        dst[x][y] = 0;
 * }
 * 
* *

The kernel cannot be bigger in any dimension than the image data. * *

Binary Image Erosion * requires the kernel to be binary as well. * Intuitively, binary erosion slides the kernel * key position and place it at every point (x,y) in the src image. * The dst value at this position is set to 1 if all the kernel * are fully supported by the src image, and the src image value is 1 * whenever the kernel has value 1. * Otherwise, the value after erosion at (x,y) is set to 0. * Erosion usually shrinks images, but it can fill holes * with kernels like *

 [1 0 1] 
* and the key position at the center. * *

Pseudo code for the erosion operation is as follows. * *

 * // erosion
 * for every dst pixel location (x,y){
 *    dst[x][y] = 1;
 *    for (i = -xKey; i < M - xKey; i++){
 *       for (j = -yKey; j < N - yKey; j++){
 *         if((x+i,y+j) is out of bounds of src ||
 *             src(x+i, y+j)==0 && Key(xKey+i, yKey+j)==1){
 *            dst[x][y] = 0; break;
 *          }
 *       }
 *    }
 * }
 * 
* *

Reference: An Introduction to Nonlinear Image Processing, * by Edward R. Bougherty and Jaakko Astola, * Spie Optical Engineering Press, 1994. * * * @see KernelJAI */ final class ErodeOpImage extends AreaOpImage { /** * The kernel with which to do the erode operation. */ protected KernelJAI kernel; /** Kernel variables. */ private int kw, kh, kx, ky; private float[] kdata; /** * Creates a ErodeOpImage given a ParameterBlock containing the image * source and pre-rotated erosion kernel. The image dimensions are * derived * from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout * object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param kernel the pre-rotated erosion KernelJAI. */ public ErodeOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, KernelJAI kernel) { super(source, layout, config, true, extender, kernel.getLeftPadding(), kernel.getRightPadding(), kernel.getTopPadding(), kernel.getBottomPadding()); this.kernel = kernel; kw = kernel.getWidth(); kh = kernel.getHeight(); kx = kernel.getXOrigin(); ky = kernel.getYOrigin(); kdata = kernel.getKernelData(); } /** * Performs erosion on a specified rectangle. The sources are * cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); Raster source = sources[0]; Rectangle srcRect = mapDestRect(destRect, 0); RasterAccessor srcAccessor = new RasterAccessor(source, srcRect, formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor dstAccessor = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(srcAccessor, dstAccessor); break; case DataBuffer.TYPE_INT: intLoop(srcAccessor, dstAccessor); break; case DataBuffer.TYPE_SHORT: shortLoop(srcAccessor, dstAccessor); break; case DataBuffer.TYPE_USHORT: ushortLoop(srcAccessor, dstAccessor); break; case DataBuffer.TYPE_FLOAT: floatLoop(srcAccessor, dstAccessor); break; case DataBuffer.TYPE_DOUBLE: doubleLoop(srcAccessor, dstAccessor); break; default: } // If the RasterAccessor object set up a temporary buffer for the // op to write to, tell the RasterAccessor to write that data // to the raster no that we're done with it. if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } private void byteLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); byte dstDataArrays[][] = dst.getByteDataArrays(); byte srcDataArrays[][] = src.getByteDataArrays(); for (int k = 0; k < dnumBands; k++) { byte dstData[] = dstDataArrays[k]; byte srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int kernelVerticalOffset = 0; int imageVerticalOffset = srcPixelOffset; float f = Float.POSITIVE_INFINITY; for (int u = 0; u < kh; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < kw; v++) { float tmpIK = ((int)srcData[imageOffset]&0xff) - kdata[kernelVerticalOffset + v]; if(tmpIK < f){ f = tmpIK; } imageOffset += srcPixelStride; } kernelVerticalOffset += kw; imageVerticalOffset += srcScanlineStride; } if (Float.isInfinite(f)){ f = 0; } int val = (int)f; if (val < 0) { val = 0; } else if (val > 255) { val = 255; } dstData[dstPixelOffset] = (byte)val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } private void shortLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); short dstDataArrays[][] = dst.getShortDataArrays(); short srcDataArrays[][] = src.getShortDataArrays(); for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int kernelVerticalOffset = 0; int imageVerticalOffset = srcPixelOffset; float f = Float.POSITIVE_INFINITY; for (int u = 0; u < kh; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < kw; v++) { float tmpIK = srcData[imageOffset] - kdata[kernelVerticalOffset + v]; if(tmpIK < f){ f = tmpIK; } imageOffset += srcPixelStride; } kernelVerticalOffset += kw; imageVerticalOffset += srcScanlineStride; } if (Float.isInfinite(f)){ f = 0.0F; } int val = (int)f; if (val < Short.MIN_VALUE) { val = Short.MIN_VALUE; } else if (val > Short.MAX_VALUE) { val = Short.MAX_VALUE; } dstData[dstPixelOffset] = (short)val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } private void ushortLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); short dstDataArrays[][] = dst.getShortDataArrays(); short srcDataArrays[][] = src.getShortDataArrays(); for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int kernelVerticalOffset = 0; int imageVerticalOffset = srcPixelOffset; float f = Float.POSITIVE_INFINITY; for (int u = 0; u < kh; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < kw; v++) { float tmpIK = (srcData[imageOffset] & 0xffff) - kdata[kernelVerticalOffset + v]; if(tmpIK < f){ f = tmpIK; } imageOffset += srcPixelStride; } kernelVerticalOffset += kw; imageVerticalOffset += srcScanlineStride; } if (Float.isInfinite(f)){ f = 0.0F; } int val = (int)f; if (val < 0) { val = 0; } else if (val > 0xffff) { val = 0xffff; } dstData[dstPixelOffset] = (short)val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } private void intLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dstDataArrays[][] = dst.getIntDataArrays(); int srcDataArrays[][] = src.getIntDataArrays(); for (int k = 0; k < dnumBands; k++) { int dstData[] = dstDataArrays[k]; int srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int kernelVerticalOffset = 0; int imageVerticalOffset = srcPixelOffset; float f = Float.POSITIVE_INFINITY; for (int u = 0; u < kh; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < kw; v++) { float tmpIK = (int)srcData[imageOffset] - kdata[kernelVerticalOffset + v]; if(tmpIK < f){ f = tmpIK; } imageOffset += srcPixelStride; } kernelVerticalOffset += kw; imageVerticalOffset += srcScanlineStride; } if (Float.isInfinite(f)){ f = 0.0F; } dstData[dstPixelOffset] = (int)f; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } private void floatLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); float dstDataArrays[][] = dst.getFloatDataArrays(); float srcDataArrays[][] = src.getFloatDataArrays(); for (int k = 0; k < dnumBands; k++) { float dstData[] = dstDataArrays[k]; float srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int kernelVerticalOffset = 0; int imageVerticalOffset = srcPixelOffset; float f = Float.POSITIVE_INFINITY; for (int u = 0; u < kh; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < kw; v++) { float tmpIK = srcData[imageOffset] - kdata[kernelVerticalOffset + v]; if(tmpIK < f){ f = tmpIK; } imageOffset += srcPixelStride; } kernelVerticalOffset += kw; imageVerticalOffset += srcScanlineStride; } if (Float.isInfinite(f)){ f = 0.0F; } dstData[dstPixelOffset] = f; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } private void doubleLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); double dstDataArrays[][] = dst.getDoubleDataArrays(); double srcDataArrays[][] = src.getDoubleDataArrays(); for (int k = 0; k < dnumBands; k++) { double dstData[] = dstDataArrays[k]; double srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int kernelVerticalOffset = 0; int imageVerticalOffset = srcPixelOffset; double f = Double.POSITIVE_INFINITY; for (int u = 0; u < kh; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < kw; v++) { double tmpIK = srcData[imageOffset] - kdata[kernelVerticalOffset + v]; if(tmpIK < f){ f = tmpIK; } imageOffset += srcPixelStride; } kernelVerticalOffset += kw; imageVerticalOffset += srcScanlineStride; } if (Double.isInfinite(f)){ f = 0.0D; } dstData[dstPixelOffset] = f; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/SubtractConstCRIF.java0000644000175000017500000000352210203035544027337 0ustar mathieumathieu/* * $RCSfile: SubtractConstCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:44 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "SubtractConst" operation in the rendered * and renderable image layers. * * @see javax.media.jai.operator.SubtractConstDescriptor * @see AddConstOpImage * */ public class SubtractConstCRIF extends CRIFImpl { /** Constructor. */ public SubtractConstCRIF() { super("subtractconst"); } /** * Creates a new instance of SubtractConstOpImage in the * rendered layer. This method satisfies the implementation of RIF. * * @param args The source image and the constants. * @param hints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { // Get ImageLayout from redering hints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(hints); // Negate the constants vector double[] constants = (double[])args.getObjectParameter(0); int length = constants.length; double[] negConstants = new double[length]; for (int i = 0; i < length; i++) { negConstants[i] = -constants[i]; } return new AddConstOpImage(args.getRenderedSource(0), hints, layout, negConstants); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MinFilterSquareOpImage.java0000644000175000017500000003541110203035544030413 0ustar mathieumathieu/* * $RCSfile: MinFilterSquareOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:35 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import java.util.Map; import javax.media.jai.operator.MinFilterDescriptor; import com.sun.media.jai.opimage.MinFilterOpImage; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class to perform min filtering on a source image. * */ final class MinFilterSquareOpImage extends MinFilterOpImage { /** * Creates a MinFilterSquareOpImage with the given source and * maskSize. The image dimensions are derived from the source * image. The tile grid layout, SampleModel, and ColorModel may * optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param maskSize the mask size. */ public MinFilterSquareOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, int maskSize) { super(source, extender, config, layout, MinFilterDescriptor.MIN_MASK_SQUARE, maskSize); } protected void byteLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); byte dstDataArrays[][] = dst.getByteDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); byte srcDataArrays[][] = src.getByteDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int minval, val; int wp = filterSize; for (int k = 0; k < dnumBands; k++) { byte dstData[] = dstDataArrays[k]; byte srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageVerticalOffset = srcPixelOffset; minval = Integer.MAX_VALUE; for (int u = 0; u < wp; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < wp; v++) { val = (int)(srcData[imageOffset])&0xff; imageOffset += srcPixelStride; minval = (val < minval) ? val : minval; } imageVerticalOffset += srcScanlineStride; } dstData[dstPixelOffset] = (byte)minval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void shortLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int minval, val; int wp = filterSize; for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageVerticalOffset = srcPixelOffset; minval = Integer.MAX_VALUE; for (int u = 0; u < wp; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < wp; v++) { val = (int)(srcData[imageOffset]); imageOffset += srcPixelStride; minval = (val < minval) ? val : minval; } imageVerticalOffset += srcScanlineStride; } dstData[dstPixelOffset] = (short)minval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void ushortLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int minval, val; int wp = filterSize; for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageVerticalOffset = srcPixelOffset; minval = Integer.MAX_VALUE; for (int u = 0; u < wp; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < wp; v++) { val = (int)(srcData[imageOffset])&0xffff; imageOffset += srcPixelStride; minval = (val < minval) ? val : minval; } imageVerticalOffset += srcScanlineStride; } dstData[dstPixelOffset] = (short)minval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void intLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); int dstDataArrays[][] = dst.getIntDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcDataArrays[][] = src.getIntDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int minval, val; int wp = filterSize; for (int k = 0; k < dnumBands; k++) { int dstData[] = dstDataArrays[k]; int srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageVerticalOffset = srcPixelOffset; minval = Integer.MAX_VALUE; for (int u = 0; u < wp; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += srcPixelStride; minval = (val < minval) ? val : minval; } imageVerticalOffset += srcScanlineStride; } dstData[dstPixelOffset] = minval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void floatLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); float dstDataArrays[][] = dst.getFloatDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); float srcDataArrays[][] = src.getFloatDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); float minval, val; int wp = filterSize; for (int k = 0; k < dnumBands; k++) { float dstData[] = dstDataArrays[k]; float srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageVerticalOffset = srcPixelOffset; minval = Float.MAX_VALUE; for (int u = 0; u < wp; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += srcPixelStride; minval = (val < minval) ? val : minval; } imageVerticalOffset += srcScanlineStride; } dstData[dstPixelOffset] = minval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void doubleLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); double dstDataArrays[][] = dst.getDoubleDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); double srcDataArrays[][] = src.getDoubleDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); double minval, val; int wp = filterSize; for (int k = 0; k < dnumBands; k++) { double dstData[] = dstDataArrays[k]; double srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageVerticalOffset = srcPixelOffset; minval = Double.MAX_VALUE; for (int u = 0; u < wp; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += srcPixelStride; minval = (val < minval) ? val : minval; } imageVerticalOffset += srcScanlineStride; } dstData[dstPixelOffset] = minval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } // public static OpImage createTestImage(OpImageTester oit) { // return new MinFilterSquareOpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource()), // 3); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MedianFilterPlusOpImage.java0000644000175000017500000004442610203035544030556 0ustar mathieumathieu/* * $RCSfile: MedianFilterPlusOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:34 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import java.util.Map; import javax.media.jai.operator.MedianFilterDescriptor; import com.sun.media.jai.opimage.MedianFilterOpImage; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class to perform median filtering on a source image. * * */ final class MedianFilterPlusOpImage extends MedianFilterOpImage { /** * Creates a MedianFilterPlusOpImage with the given source and * maskSize. The image dimensions are derived from the source * image. The tile grid layout, SampleModel, and ColorModel may * optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param maskSize the mask size. */ public MedianFilterPlusOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, int maskSize) { super(source, extender, config, layout, MedianFilterDescriptor.MEDIAN_MASK_PLUS, maskSize); } protected void byteLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); byte dstDataArrays[][] = dst.getByteDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); byte srcDataArrays[][] = src.getByteDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int values[] = new int[filterSize*2-1]; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { byte dstData[] = dstDataArrays[k]; byte srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int valueCount = 0; // figure out where the top of the plus starts int imageOffset = srcPixelOffset + srcPixelStride*offset; for (int u = 0; u < wp; u++) { values[valueCount++] = (int)(srcData[imageOffset]&0xff); imageOffset += srcScanlineStride; } // remove the center element so it doesn't get counted // twice when we do the horizontal piece valueCount--; values[offset] = values[valueCount]; // figure out where the left side of plus starts imageOffset = srcPixelOffset + srcScanlineStride*offset; for (int v = 0; v < wp; v++) { values[valueCount++] = (int)(srcData[imageOffset]&0xff); imageOffset += srcPixelStride; } int val = medianFilter(values); dstData[dstPixelOffset] = (byte)val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void shortLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int values[] = new int[filterSize*2-1]; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int valueCount = 0; // figure out where the top of the plus starts int imageOffset = srcPixelOffset + srcPixelStride*offset; for (int u = 0; u < wp; u++) { values[valueCount++] = (int)(srcData[imageOffset]); imageOffset += srcScanlineStride; } // remove the center element so it doesn't get counted // twice when we do the horizontal piece valueCount--; values[offset] = values[valueCount]; // figure out where the left side of plus starts imageOffset = srcPixelOffset + srcScanlineStride*offset; for (int v = 0; v < wp; v++) { values[valueCount++] = srcData[imageOffset]; imageOffset += srcPixelStride; } int val = medianFilter(values); dstData[dstPixelOffset] = (short)val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void ushortLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int values[] = new int[filterSize*2-1]; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int valueCount = 0; // figure out where the top of the plus starts int imageOffset = srcPixelOffset + srcPixelStride*offset; for (int u = 0; u < wp; u++) { values[valueCount++] = (int)(srcData[imageOffset]&0xffff); imageOffset += srcScanlineStride; } // remove the center element so it doesn't get counted // twice when we do the horizontal piece valueCount--; values[offset] = values[valueCount]; // figure out where the left side of plus starts imageOffset = srcPixelOffset + srcScanlineStride*offset; for (int v = 0; v < wp; v++) { values[valueCount++] = (int)(srcData[imageOffset]&0xffff); imageOffset += srcPixelStride; } int val = medianFilter(values); dstData[dstPixelOffset] = (short)val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void intLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); int dstDataArrays[][] = dst.getIntDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcDataArrays[][] = src.getIntDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int values[] = new int[filterSize*2-1]; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { int dstData[] = dstDataArrays[k]; int srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int valueCount = 0; // figure out where the top of the plus starts int imageOffset = srcPixelOffset + srcPixelStride*offset; for (int u = 0; u < wp; u++) { values[valueCount++] = srcData[imageOffset]; imageOffset += srcScanlineStride; } // remove the center element so it doesn't get counted // twice when we do the horizontal piece valueCount--; values[offset] = values[valueCount]; // figure out where the left side of plus starts imageOffset = srcPixelOffset + srcScanlineStride*offset; for (int v = 0; v < wp; v++) { values[valueCount++] = srcData[imageOffset]; imageOffset += srcPixelStride; } int val = medianFilter(values); dstData[dstPixelOffset] = val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void floatLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); float dstDataArrays[][] = dst.getFloatDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); float srcDataArrays[][] = src.getFloatDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); float values[] = new float[filterSize*2-1]; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { float dstData[] = dstDataArrays[k]; float srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int valueCount = 0; // figure out where the top of the plus starts int imageOffset = srcPixelOffset + srcPixelStride*offset; for (int u = 0; u < wp; u++) { values[valueCount++] = srcData[imageOffset]; imageOffset += srcScanlineStride; } // remove the center element so it doesn't get counted // twice when we do the horizontal piece valueCount--; values[offset] = values[valueCount]; // figure out where the left side of plus starts imageOffset = srcPixelOffset + srcScanlineStride*offset; for (int v = 0; v < wp; v++) { values[valueCount++] = srcData[imageOffset]; imageOffset += srcPixelStride; } float val = medianFilterFloat(values); dstData[dstPixelOffset] = val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void doubleLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); double dstDataArrays[][] = dst.getDoubleDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); double srcDataArrays[][] = src.getDoubleDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); double values[] = new double[filterSize*2-1]; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { double dstData[] = dstDataArrays[k]; double srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int valueCount = 0; // figure out where the top of the plus starts int imageOffset = srcPixelOffset + srcPixelStride*offset; for (int u = 0; u < wp; u++) { values[valueCount++] = srcData[imageOffset]; imageOffset += srcScanlineStride; } // remove the center element so it doesn't get counted // twice when we do the horizontal piece valueCount--; values[offset] = values[valueCount]; // figure out where the left side of plus starts imageOffset = srcPixelOffset + srcScanlineStride*offset; for (int v = 0; v < wp; v++) { values[valueCount++] = srcData[imageOffset]; imageOffset += srcPixelStride; } double val = medianFilterDouble(values); dstData[dstPixelOffset] = val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } // public static OpImage createTestImage(OpImageTester oit) { // return new MedianFilterPlusOpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource()), // 3); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/DivideIntoConstOpImage.java0000644000175000017500000003264610203035544030415 0ustar mathieumathieu/* * $RCSfile: DivideIntoConstOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:24 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import javax.media.jai.ColormapOpImage; import com.sun.media.jai.util.ImageUtil; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; /** * An OpImage implementing the "DivideIntoConst" operation. * *

This OpImage divides the pixels of a rendered * image into a set of constants, one for each band of the source image. * The destination pixel values are calculated as: *

 *     for (int h = 0; h < dstHeight; h++) {
 *         for (int w = 0; w < dstWidth; w++) {
 *             for (int b = 0; b < dstNumBands; b++) {
 *                 if (constants.length < dstNumBands) {
 *                     dst[h][w][b] = constants[0] / srcs[h][w][b];
 *                 } else {
 *                     dst[h][w][b] = constants[b] / srcs[h][w][b];
 *                 }
 *             }
 *         }
 *     }
 * 
* * @see javax.media.jai.operator.DivideIntoConstDescriptor * @see DivideIntoConstCRIF * * * @since EA2 */ final class DivideIntoConstOpImage extends ColormapOpImage { /** The constants to be subtracted from, one for each band. */ protected double[] constants; /** * Constructor. * * @param source The source image. * @param layout The destination image layout. * @param constants The constants to be divided into. */ public DivideIntoConstOpImage(RenderedImage source, Map config, ImageLayout layout, double[] constants) { super(source, layout, config, true); int numBands = getSampleModel().getNumBands(); if (constants.length < numBands) { this.constants = new double[numBands]; for (int i = 0; i < numBands; i++) { this.constants[i] = constants[0]; } } else { this.constants = (double[])constants.clone(); } // Set flag to permit in-place operation. permitInPlaceOperation(); // Initialize the colormap if necessary. initializeColormapOperation(); } /** * Transform the colormap according to the rescaling parameters. */ protected void transformColormap(byte[][] colormap) { for(int b = 0; b < 3; b++) { byte[] map = colormap[b]; int mapSize = map.length; double c = b < constants.length ? constants[b] : constants[0]; for(int i = 0; i < mapSize; i++) { map[i] = ImageUtil.clampRoundByte(c / (map[i] & 0xFF)); } } } /** * Divides the pixel values within a specified rectangle into a constant. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); Rectangle srcRect = mapDestRect(destRect, 0); RasterAccessor dst = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); RasterAccessor src = new RasterAccessor(sources[0], srcRect, formatTags[0], getSourceImage(0).getColorModel()); switch (dst.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(src, dst); break; case DataBuffer.TYPE_USHORT: computeRectUShort(src, dst); break; case DataBuffer.TYPE_SHORT: computeRectShort(src, dst); break; case DataBuffer.TYPE_INT: computeRectInt(src, dst); break; case DataBuffer.TYPE_FLOAT: computeRectFloat(src, dst); break; case DataBuffer.TYPE_DOUBLE: computeRectDouble(src, dst); break; } if (dst.needsClamping()) { /* Further clamp down to underlying raster data type. */ dst.clampDataArrays(); } dst.copyDataToRaster(); } private void computeRectByte(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); byte[][] dstData = dst.getByteDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); byte[][] srcData = src.getByteDataArrays(); for (int b = 0; b < dstBands; b++) { double c = constants[b]; byte[] d = dstData[b]; byte[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { double t = s[srcPixelOffset] & 0xFF; d[dstPixelOffset] = ImageUtil.clampRoundByte(c / t); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectUShort(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); short[][] dstData = dst.getShortDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); short[][] srcData = src.getShortDataArrays(); for (int b = 0; b < dstBands; b++) { double c = constants[b]; short[] d = dstData[b]; short[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { double t = s[srcPixelOffset] & 0xFFFF; d[dstPixelOffset] = ImageUtil.clampRoundUShort(c / t); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectShort(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); short[][] dstData = dst.getShortDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); short[][] srcData = src.getShortDataArrays(); for (int b = 0; b < dstBands; b++) { double c = constants[b]; short[] d = dstData[b]; short[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = ImageUtil.clampRoundShort(c / s[srcPixelOffset]); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectInt(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); int[][] dstData = dst.getIntDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); int[][] srcData = src.getIntDataArrays(); for (int b = 0; b < dstBands; b++) { double c = constants[b]; int[] d = dstData[b]; int[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = ImageUtil.clampRoundInt(c / s[srcPixelOffset]); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectFloat(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); float[][] dstData = dst.getFloatDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); float[][] srcData = src.getFloatDataArrays(); for (int b = 0; b < dstBands; b++) { double c = constants[b]; float[] d = dstData[b]; float[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = ImageUtil.clampFloat(c / s[srcPixelOffset]); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectDouble(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); double[][] dstData = dst.getDoubleDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); double[][] srcData = src.getDoubleDataArrays(); for (int b = 0; b < dstBands; b++) { double c = constants[b]; double[] d = dstData[b]; double[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = c / s[srcPixelOffset]; dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/AddCRIF.java0000644000175000017500000000324010203035544025226 0ustar mathieumathieu/* * $RCSfile: AddCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:11 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D.Float; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import java.awt.image.renderable.RenderContext; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "Add" operation in the rendered * and renderable image layers. * * @see javax.media.jai.operator.AddDescriptor * @see AddOpImage * */ public class AddCRIF extends CRIFImpl { /** Constructor. */ public AddCRIF() { super("add"); } /** * Creates a new instance of AddOpImage in the rendered * layer. This method satisfies the implementation of RIF. * * @param paramBlock The two source images to be added. * @param renderHints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new AddOpImage(paramBlock.getRenderedSource(0), paramBlock.getRenderedSource(1), renderHints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/CompositeOpImage.java0000644000175000017500000016737710345702471027333 0ustar mathieumathieu/* * $RCSfile: CompositeOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-12-08 00:58:33 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.IndexColorModel; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.util.Map; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PointOpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.RasterFactory; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.util.JDKWorkarounds; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage implementing the "Composite" operation as * described in javax.media.jai.operator.CompositeDescriptor. * *

For two source images src1 and src2, * this OpImage places the foreground src1 * in front of the background src2. This is what commonly * known as the "over" composite. * *

The destination image contains both the alpha channel and the * color channels. The alpha channel index is determined by the parameter * alphaFirst: if true, alpha is the first channel; if false, * alpha is the last channel. The formulas used to calculate destination * alpha and color values are: *

 * dstAlpha = src1Alpha + src2Alpha * (1 - src1Alpha)
 * dstColor = src1Color + src2Color * (1 - src1Alpha)
 * 
* where alpha values are in fraction format, and color values are alpha * pre-multiplied. * *

The following assumptions are made: *

  • The source images, their alpha images, and the destination image all * have the same data type.
  • *
  • The two source images have the same number of bands and should only * contain the color channels.
  • *
  • The alpha images are of the same dimension as their cooresponding source * image and should be single-banded. If a multi-banded image is supplied, the * default band (band 0) is used.
  • *
  • If alphaPremultiplied is true, both the source and * destination images have alpha pre-multiplied, and vice versa.
  • *
  • The destination image must have at least one extra band than the two * sources, which represents the alpha channel. It may be user-specified * to be the first or the last band of the pixel data.
  • * * @see javax.media.jai.operator.CompositeDescriptor * @see CompositeCRIF * */ final class CompositeOpImage extends PointOpImage { /** The alpha image that overrides the alpha for source1. */ protected RenderedImage source1Alpha; /** The alpha image that overrides the alpha for source2. */ protected RenderedImage source2Alpha; /** Indicates whether alpha has been premultiplied. */ protected boolean alphaPremultiplied; /** The alpha and color band offset. */ private int aOffset; // alpha channel offset private int cOffset; // color channels offset /** Maximum Value supported by the data type if it's integral types. */ private byte maxValueByte; private short maxValueShort; private int maxValue; private float invMaxValue; // 1 / maxValue /** * Constructs an CompositeOpImage. * * @param source1 The foreground source image. * @param source2 The background source image. * @param layout The destination image layout. * @param source1Alpha The alpha image that overrides the * alpha for source1; may not be null. * @param source2Alpha The alpha image that overrides the alpha for * source2; may be null, in which case source2 * is considered completely opaque. * @param alphaPremultiplied Indicates whether alpha has been * premultiplied to both sources. * @param alphaFirst If true, alpha is the first band (band 0) in * destination image; if false, alpha is the * last band. */ public CompositeOpImage(RenderedImage source1, RenderedImage source2, Map config, ImageLayout layout, RenderedImage source1Alpha, RenderedImage source2Alpha, boolean alphaPremultiplied, boolean alphaFirst) { super(source1, source2, layout, config, true); this.source1Alpha = source1Alpha; this.source2Alpha = source2Alpha; this.alphaPremultiplied = alphaPremultiplied; SampleModel sm = source1.getSampleModel(); ColorModel cm = source1.getColorModel(); int dtype = sm.getTransferType(); int bands; if (cm instanceof IndexColorModel) { bands = cm.getNumComponents(); } else { bands = sm.getNumBands(); } bands += 1; // one additional alpha channel if (sampleModel.getTransferType() != dtype || sampleModel.getNumBands() != bands) { /* * The current destination sampleModel is not suitable for the * two sources and their alpha images. * Create a suitable sampleModel for the destination image. */ sampleModel = RasterFactory.createComponentSampleModel(sampleModel, dtype, tileWidth, tileHeight, bands); if(colorModel != null && !JDKWorkarounds.areCompatibleDataModels(sampleModel, colorModel)) { colorModel = ImageUtil.getCompatibleColorModel(sampleModel, config); } } aOffset = alphaFirst ? 0 : bands - 1; cOffset = alphaFirst ? 1 : 0; switch (dtype) { case DataBuffer.TYPE_BYTE: maxValue = 0xFF; // byte is unsigned maxValueByte = (byte)0xFF; break; case DataBuffer.TYPE_USHORT: maxValue = 0xFFFF; maxValueShort = (short)0xFFFF; break; case DataBuffer.TYPE_SHORT: maxValue = Short.MAX_VALUE; maxValueShort = Short.MAX_VALUE; break; case DataBuffer.TYPE_INT: maxValue = Integer.MAX_VALUE; break; default: } invMaxValue = 1.0F / maxValue; } /** * Composites two images within a specified rectangle. * * @param sources Cobbled source, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { /* For PointOpImage, srcRect = destRect. */ RenderedImage[] renderedSources = source2Alpha == null ? new RenderedImage[3] : new RenderedImage[4]; renderedSources[0] = getSourceImage(0); renderedSources[1] = getSourceImage(1); renderedSources[2] = source1Alpha; Raster source1AlphaRaster = source1Alpha.getData(destRect); Raster source2AlphaRaster = null; if (source2Alpha != null) { renderedSources[3] = source2Alpha; source2AlphaRaster = source2Alpha.getData(destRect); } RasterFormatTag tags[] = RasterAccessor.findCompatibleTags(renderedSources, this); RasterAccessor s1 = new RasterAccessor(sources[0], destRect, tags[0],getSourceImage(0).getColorModel()); RasterAccessor s2 = new RasterAccessor(sources[1], destRect, tags[1],getSourceImage(1).getColorModel()); RasterAccessor a1 = new RasterAccessor(source1AlphaRaster, destRect, tags[2], source1Alpha.getColorModel()); RasterAccessor a2=null,d=null; if (source2Alpha != null) { a2 = new RasterAccessor(source2AlphaRaster, destRect, tags[3], source2Alpha.getColorModel()); d = new RasterAccessor(dest, destRect, tags[4], this.getColorModel()); } else { a2 = null; d = new RasterAccessor(dest, destRect, tags[3], this.getColorModel()); } switch (d.getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(s1, s2, a1, a2, d); break; case DataBuffer.TYPE_USHORT: ushortLoop(s1, s2, a1, a2, d); break; case DataBuffer.TYPE_SHORT: shortLoop(s1, s2, a1, a2, d); break; case DataBuffer.TYPE_INT: intLoop(s1, s2, a1, a2, d); break; case DataBuffer.TYPE_FLOAT: floatLoop(s1, s2, a1, a2, d); break; case DataBuffer.TYPE_DOUBLE: doubleLoop(s1, s2, a1, a2, d); break; } d.copyDataToRaster(); } /* * Formulas for integral data types: * * d[alpha] = dstAlpha * maxValue * = (src1Alpha + src2Alpha * (1 - src1Alpha)) * maxValue * if (source2 is opaque (src2Alpha = 1)) { * d[alpha] = (src1Alpha + 1 * (1 - src1Alpha)) * maxValue * = maxValue * } else { * d[alpha] = (a1/maxValue + a2/maxValue * (1 - a1/maxValue)) * maxValue * = a1 + a2 * (1 - a1/maxValue) * } * * if (alpha pre-multiplied to sources and destination) { * d[color] = dstColor * = src1Color + src2Color * (1 - src1Alpha) * = s1 + s2 * (1 - a1/maxValue) * } else { * if (source2 is opaque (src2Alpha = 1 & dstAlpha = 1)) { * d[color] = dstColor / dstAlpha * = (src1Color + src2Color * (1 - src1Alpha)) * = s1 * a1/maxValue + s2 * (1 - a1/maxValue) * } else { * d[color] = dstColor / dstAlpha * = (src1Color + src2Color * (1 - src1Alpha)) / dstAlpha * = (s1 * a1/maxValue + s2 * a2/maxValue * * (1 - a1/maxValue)) / (d[alpha]/maxValue) * = (s1 * a1 + s2 * a2 * (1 - a1/maxValue)) / d[alpha] * } * } */ private void byteLoop(RasterAccessor src1, RasterAccessor src2, RasterAccessor afa1, RasterAccessor afa2, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int numBands = src1.getNumBands(); /* First source color channels. */ byte[][] s1 = src1.getByteDataArrays(); int s1ss = src1.getScanlineStride(); // scanline stride int s1ps = src1.getPixelStride(); // pixel stride int[] s1bo = src1.getBandOffsets(); // band offsets /* Second source color channels. */ byte[][] s2 = src2.getByteDataArrays(); int s2ss = src2.getScanlineStride(); // scanline stride int s2ps = src2.getPixelStride(); // pixel stride int[] s2bo = src2.getBandOffsets(); // band offsets /* First source alpha channel. */ byte[] a1 = afa1.getByteDataArray(0); // use band 0 int a1ss = afa1.getScanlineStride(); // scanline stride int a1ps = afa1.getPixelStride(); // pixel stride int a1bo = afa1.getBandOffset(0); // band 0 offsets /* Second source alpha channel (if any). */ byte[] a2 = null; int a2ss = 0; int a2ps = 0; int a2bo = 0; if (afa2 != null) { a2 = afa2.getByteDataArray(0); // use band 0 a2ss = afa2.getScanlineStride(); // scanline stride a2ps = afa2.getPixelStride(); // pixel stride a2bo = afa2.getBandOffset(0); // band 0 offset } /* Destination color and alpha channels. */ byte[][] d = dst.getByteDataArrays(); int dss = dst.getScanlineStride(); // scanline stride int dps = dst.getPixelStride(); // pixel stride int[] dbo = dst.getBandOffsets(); // band offsets int s1so = 0, s2so = 0, a1so = 0, a2so = 0, dso = 0; int s1po, s2po, a1po, a2po, dpo; // po = pixel offset if (alphaPremultiplied) { if (afa2 == null) { for (int h = 0; h < dheight; h++) { s1po = s1so; s2po = s2so; a1po = a1so; dpo = dso; for (int w = 0; w < dwidth; w++) { float t = 1.0F - (a1[a1po+a1bo] & 0xFF) * invMaxValue; /* Destination alpha channel. */ d[aOffset][dpo+dbo[aOffset]] = maxValueByte; /* Destination color channels. */ for (int b = 0; b < numBands; b++) { int i = b + cOffset; d[i][dpo+dbo[i]] = (byte) ((s1[b][s1po+s1bo[b]] & 0xFF) + (s2[b][s2po+s2bo[b]] & 0xFF) * t); } s1po += s1ps; s2po += s2ps; a1po += a1ps; dpo += dps; } s1so += s1ss; s2so += s2ss; a1so += a1ss; dso += dss; } } else { for (int h = 0; h < dheight; h++) { s1po = s1so; s2po = s2so; a1po = a1so; a2po = a2so; dpo = dso; for (int w = 0; w < dwidth; w++) { int t1 = a1[a1po+a1bo] & 0xFF; // a1 float t2 = 1.0F - t1 * invMaxValue; // 1-a1/maxValue /* Destination alpha channel. */ d[aOffset][dpo+dbo[aOffset]] = (byte) (t1 + (a2[a2po+a2bo] & 0xFF) * t2); /* Destination color channels. */ for (int b = 0; b < numBands; b++) { int i = b + cOffset; d[i][dpo+dbo[i]] = (byte) ((s1[b][s1po+s1bo[b]] & 0xFF) + (s2[b][s2po+s2bo[b]] & 0xFF) * t2); } s1po += s1ps; s2po += s2ps; a1po += a1ps; a2po += a2ps; dpo += dps; } s1so += s1ss; s2so += s2ss; a1so += a1ss; a2so += a2ss; dso += dss; } } } else { if (afa2 == null) { for (int h = 0; h < dheight; h++) { s1po = s1so; s2po = s2so; a1po = a1so; dpo = dso; for (int w = 0; w < dwidth; w++) { float t1 = (a1[a1po+a1bo] & 0xFF) * invMaxValue; float t2 = 1.0F - t1; // 1-a1/maxValue /* Destination alpha channel. */ d[aOffset][dpo+dbo[aOffset]] = maxValueByte; /* Destination color channels. */ for (int b = 0; b < numBands; b++) { int i = b + cOffset; d[i][dpo+dbo[i]] = (byte) ((s1[b][s1po+s1bo[b]] & 0xFF) * t1 + (s2[b][s2po+s2bo[b]] & 0xFF) * t2); } s1po += s1ps; s2po += s2ps; a1po += a1ps; dpo += dps; } s1so += s1ss; s2so += s2ss; a1so += a1ss; dso += dss; } } else { for (int h = 0; h < dheight; h++) { s1po = s1so; s2po = s2so; a1po = a1so; a2po = a2so; dpo = dso; for (int w = 0; w < dwidth; w++) { int t1 = a1[a1po+a1bo] & 0xFF; // a1 float t2 = (1.0F - t1 * invMaxValue) * (a2[a2po+a2bo] & 0xFF); // a2*(1-a1/maxValue) float t3 = t1 + t2; // d[alpha] float t4, t5; if (t3 == 0.0F) { t4 = 0.0F; t5 = 0.0F; } else { t4 = t1 / t3; t5 = t2 / t3; } /* Destination alpha channel. */ d[aOffset][dpo+dbo[aOffset]] = (byte)t3; /* Destination color channels. */ for (int b = 0; b < numBands; b++) { int i = b + cOffset; d[i][dpo+dbo[i]] = (byte) ((s1[b][s1po+s1bo[b]] & 0xFF) * t4 + (s2[b][s2po+s2bo[b]] & 0xFF) * t5); } s1po += s1ps; s2po += s2ps; a1po += a1ps; a2po += a2ps; dpo += dps; } s1so += s1ss; s2so += s2ss; a1so += a1ss; a2so += a2ss; dso += dss; } } } } private void ushortLoop(RasterAccessor src1, RasterAccessor src2, RasterAccessor afa1, RasterAccessor afa2, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int numBands = src1.getNumBands(); /* First source color channels. */ short[][] s1 = src1.getShortDataArrays(); int s1ss = src1.getScanlineStride(); // scanline stride int s1ps = src1.getPixelStride(); // pixel stride int[] s1bo = src1.getBandOffsets(); // band offsets /* Second source color channels. */ short[][] s2 = src2.getShortDataArrays(); int s2ss = src2.getScanlineStride(); // scanline stride int s2ps = src2.getPixelStride(); // pixel stride int[] s2bo = src2.getBandOffsets(); // band offsets /* First source alpha channel. */ short[] a1 = afa1.getShortDataArray(0); // use band 0 int a1ss = afa1.getScanlineStride(); // scanline stride int a1ps = afa1.getPixelStride(); // pixel stride int a1bo = afa1.getBandOffset(0); // band 0 offsets /* Second source alpha channel (if any). */ short[] a2 = null; int a2ss = 0; int a2ps = 0; int a2bo = 0; if (afa2 != null) { a2 = afa2.getShortDataArray(0); // use band 0 a2ss = afa2.getScanlineStride(); // scanline stride a2ps = afa2.getPixelStride(); // pixel stride a2bo = afa2.getBandOffset(0); // band 0 offset } /* Destination color and alpha channels. */ short[][] d = dst.getShortDataArrays(); int dss = dst.getScanlineStride(); // scanline stride int dps = dst.getPixelStride(); // pixel stride int[] dbo = dst.getBandOffsets(); // band offsets int s1so = 0, s2so = 0, a1so = 0, a2so = 0, dso = 0; int s1po, s2po, a1po, a2po, dpo; // po = pixel offset if (alphaPremultiplied) { if (afa2 == null) { for (int h = 0; h < dheight; h++) { s1po = s1so; s2po = s2so; a1po = a1so; dpo = dso; for (int w = 0; w < dwidth; w++) { float t = 1.0F - (a1[a1po+a1bo] & 0xFFFF) * invMaxValue; /* Destination alpha channel. */ d[aOffset][dpo+dbo[aOffset]] = maxValueShort; /* Destination color channels. */ for (int b = 0; b < numBands; b++) { int i = b + cOffset; d[i][dpo+dbo[i]] = (short) ((s1[b][s1po+s1bo[b]] & 0xFFFF) + (s2[b][s2po+s2bo[b]] & 0xFFFF) * t); } s1po += s1ps; s2po += s2ps; a1po += a1ps; dpo += dps; } s1so += s1ss; s2so += s2ss; a1so += a1ss; dso += dss; } } else { for (int h = 0; h < dheight; h++) { s1po = s1so; s2po = s2so; a1po = a1so; a2po = a2so; dpo = dso; for (int w = 0; w < dwidth; w++) { int t1 = a1[a1po+a1bo] & 0xFFFF; // a1 float t2 = 1.0F - t1 * invMaxValue; // 1-a1/maxValue /* Destination alpha channel. */ d[aOffset][dpo+dbo[aOffset]] = (short) (t1 + (a2[a2po+a2bo] & 0xFFFF) * t2); /* Destination color channels. */ for (int b = 0; b < numBands; b++) { int i = b + cOffset; d[i][dpo+dbo[i]] = (short) ((s1[b][s1po+s1bo[b]] & 0xFFFF) + (s2[b][s2po+s2bo[b]] & 0xFFFF) * t2); } s1po += s1ps; s2po += s2ps; a1po += a1ps; a2po += a2ps; dpo += dps; } s1so += s1ss; s2so += s2ss; a1so += a1ss; a2so += a2ss; dso += dss; } } } else { if (afa2 == null) { for (int h = 0; h < dheight; h++) { s1po = s1so; s2po = s2so; a1po = a1so; dpo = dso; for (int w = 0; w < dwidth; w++) { float t1 = (a1[a1po+a1bo] & 0xFFFF) * invMaxValue; float t2 = 1.0F - t1; // 1-a1/maxValue /* Destination alpha channel. */ d[aOffset][dpo+dbo[aOffset]] = maxValueShort; /* Destination color channels. */ for (int b = 0; b < numBands; b++) { int i = b + cOffset; d[i][dpo+dbo[i]] = (short) ((s1[b][s1po+s1bo[b]] & 0xFFFF) * t1 + (s2[b][s2po+s2bo[b]] & 0xFFFF) * t2); } s1po += s1ps; s2po += s2ps; a1po += a1ps; dpo += dps; } s1so += s1ss; s2so += s2ss; a1so += a1ss; dso += dss; } } else { for (int h = 0; h < dheight; h++) { s1po = s1so; s2po = s2so; a1po = a1so; a2po = a2so; dpo = dso; for (int w = 0; w < dwidth; w++) { int t1 = a1[a1po+a1bo] & 0xFFFF; // a1 float t2 = (1.0F - t1 * invMaxValue) * (a2[a2po+a2bo] & 0xFFFF); // a2*(1-a1/maxValue) float t3 = t1 + t2; // d[alpha] float t4, t5; if (t3 == 0.0F) { t4 = 0.0F; t5 = 0.0F; } else { t4 = t1 / t3; t5 = t2 / t3; } /* Destination alpha channel. */ d[aOffset][dpo+dbo[aOffset]] = (short)t3; /* Destination color channels. */ for (int b = 0; b < numBands; b++) { int i = b + cOffset; d[i][dpo+dbo[i]] = (short) ((s1[b][s1po+s1bo[b]] & 0xFFFF) * t4 + (s2[b][s2po+s2bo[b]] & 0xFFFF) * t5); } s1po += s1ps; s2po += s2ps; a1po += a1ps; a2po += a2ps; dpo += dps; } s1so += s1ss; s2so += s2ss; a1so += a1ss; a2so += a2ss; dso += dss; } } } } private void shortLoop(RasterAccessor src1, RasterAccessor src2, RasterAccessor afa1, RasterAccessor afa2, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int numBands = src1.getNumBands(); /* First source color channels. */ short[][] s1 = src1.getShortDataArrays(); int s1ss = src1.getScanlineStride(); // scanline stride int s1ps = src1.getPixelStride(); // pixel stride int[] s1bo = src1.getBandOffsets(); // band offsets /* Second source color channels. */ short[][] s2 = src2.getShortDataArrays(); int s2ss = src2.getScanlineStride(); // scanline stride int s2ps = src2.getPixelStride(); // pixel stride int[] s2bo = src2.getBandOffsets(); // band offsets /* First source alpha channel. */ short[] a1 = afa1.getShortDataArray(0); // use band 0 int a1ss = afa1.getScanlineStride(); // scanline stride int a1ps = afa1.getPixelStride(); // pixel stride int a1bo = afa1.getBandOffset(0); // band 0 offsets /* Second source alpha channel (if any). */ short[] a2 = null; int a2ss = 0; int a2ps = 0; int a2bo = 0; if (afa2 != null) { a2 = afa2.getShortDataArray(0); // use band 0 a2ss = afa2.getScanlineStride(); // scanline stride a2ps = afa2.getPixelStride(); // pixel stride a2bo = afa2.getBandOffset(0); // band 0 offset } /* Destination color and alpha channels. */ short[][] d = dst.getShortDataArrays(); int dss = dst.getScanlineStride(); // scanline stride int dps = dst.getPixelStride(); // pixel stride int[] dbo = dst.getBandOffsets(); // band offsets int s1so = 0, s2so = 0, a1so = 0, a2so = 0, dso = 0; int s1po, s2po, a1po, a2po, dpo; // po = pixel offset if (alphaPremultiplied) { if (afa2 == null) { for (int h = 0; h < dheight; h++) { s1po = s1so; s2po = s2so; a1po = a1so; dpo = dso; for (int w = 0; w < dwidth; w++) { float t = 1.0F - a1[a1po+a1bo] * invMaxValue; /* Destination alpha channel. */ d[aOffset][dpo+dbo[aOffset]] = maxValueShort; /* Destination color channels. */ for (int b = 0; b < numBands; b++) { int i = b + cOffset; d[i][dpo+dbo[i]] = (short) (s1[b][s1po+s1bo[b]] + s2[b][s2po+s2bo[b]] * t); } s1po += s1ps; s2po += s2ps; a1po += a1ps; dpo += dps; } s1so += s1ss; s2so += s2ss; a1so += a1ss; dso += dss; } } else { for (int h = 0; h < dheight; h++) { s1po = s1so; s2po = s2so; a1po = a1so; a2po = a2so; dpo = dso; for (int w = 0; w < dwidth; w++) { int t1 = a1[a1po+a1bo]; // a1 float t2 = 1.0F - t1 * invMaxValue; // 1-a1/maxValue /* Destination alpha channel. */ d[aOffset][dpo+dbo[aOffset]] = (short) (t1 + a2[a2po+a2bo] * t2); /* Destination color channels. */ for (int b = 0; b < numBands; b++) { int i = b + cOffset; d[i][dpo+dbo[i]] = (short) (s1[b][s1po+s1bo[b]] + s2[b][s2po+s2bo[b]] * t2); } s1po += s1ps; s2po += s2ps; a1po += a1ps; a2po += a2ps; dpo += dps; } s1so += s1ss; s2so += s2ss; a1so += a1ss; a2so += a2ss; dso += dss; } } } else { if (afa2 == null) { for (int h = 0; h < dheight; h++) { s1po = s1so; s2po = s2so; a1po = a1so; dpo = dso; for (int w = 0; w < dwidth; w++) { float t1 = a1[a1po+a1bo] * invMaxValue; float t2 = 1.0F - t1; // 1-a1/maxValue /* Destination alpha channel. */ d[aOffset][dpo+dbo[aOffset]] = maxValueShort; /* Destination color channels. */ for (int b = 0; b < numBands; b++) { int i = b + cOffset; d[i][dpo+dbo[i]] = (short) (s1[b][s1po+s1bo[b]] * t1 + s2[b][s2po+s2bo[b]] * t2); } s1po += s1ps; s2po += s2ps; a1po += a1ps; dpo += dps; } s1so += s1ss; s2so += s2ss; a1so += a1ss; dso += dss; } } else { for (int h = 0; h < dheight; h++) { s1po = s1so; s2po = s2so; a1po = a1so; a2po = a2so; dpo = dso; for (int w = 0; w < dwidth; w++) { int t1 = a1[a1po+a1bo]; // a1 float t2 = (1.0F - t1 * invMaxValue) * a2[a2po+a2bo]; // a2*(1-a1/maxValue) float t3 = t1 + t2; // d[alpha] float t4, t5; if (t3 == 0.0F) { t4 = 0.0F; t5 = 0.0F; } else { t4 = t1 / t3; t5 = t2 / t3; } /* Destination alpha channel. */ d[aOffset][dpo+dbo[aOffset]] = (short)t3; /* Destination color channels. */ for (int b = 0; b < numBands; b++) { int i = b + cOffset; d[i][dpo+dbo[i]] = (short) (s1[b][s1po+s1bo[b]] * t4 + s2[b][s2po+s2bo[b]] * t5); } s1po += s1ps; s2po += s2ps; a1po += a1ps; a2po += a2ps; dpo += dps; } s1so += s1ss; s2so += s2ss; a1so += a1ss; a2so += a2ss; dso += dss; } } } } private void intLoop(RasterAccessor src1, RasterAccessor src2, RasterAccessor afa1, RasterAccessor afa2, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int numBands = src1.getNumBands(); /* First source color channels. */ int[][] s1 = src1.getIntDataArrays(); int s1ss = src1.getScanlineStride(); // scanline stride int s1ps = src1.getPixelStride(); // pixel stride int[] s1bo = src1.getBandOffsets(); // band offsets /* Second source color channels. */ int[][] s2 = src2.getIntDataArrays(); int s2ss = src2.getScanlineStride(); // scanline stride int s2ps = src2.getPixelStride(); // pixel stride int[] s2bo = src2.getBandOffsets(); // band offsets /* First source alpha channel. */ int[] a1 = afa1.getIntDataArray(0); // use band 0 int a1ss = afa1.getScanlineStride(); // scanline stride int a1ps = afa1.getPixelStride(); // pixel stride int a1bo = afa1.getBandOffset(0); // band 0 offsets /* Second source alpha channel (if any). */ int[] a2 = null; int a2ss = 0; int a2ps = 0; int a2bo = 0; if (afa2 != null) { a2 = afa2.getIntDataArray(0); // use band 0 a2ss = afa2.getScanlineStride(); // scanline stride a2ps = afa2.getPixelStride(); // pixel stride a2bo = afa2.getBandOffset(0); // band 0 offset } /* Destination color and alpha channels. */ int[][] d = dst.getIntDataArrays(); int dss = dst.getScanlineStride(); // scanline stride int dps = dst.getPixelStride(); // pixel stride int[] dbo = dst.getBandOffsets(); // band offsets int s1so = 0, s2so = 0, a1so = 0, a2so = 0, dso = 0; int s1po, s2po, a1po, a2po, dpo; // po = pixel offset if (alphaPremultiplied) { if (afa2 == null) { for (int h = 0; h < dheight; h++) { s1po = s1so; s2po = s2so; a1po = a1so; dpo = dso; for (int w = 0; w < dwidth; w++) { float t = 1.0F - a1[a1po+a1bo] * invMaxValue; /* Destination alpha channel. */ d[aOffset][dpo+dbo[aOffset]] = maxValue; /* Destination color channels. */ for (int b = 0; b < numBands; b++) { int i = b + cOffset; d[i][dpo+dbo[i]] = (int)(s1[b][s1po+s1bo[b]] + s2[b][s2po+s2bo[b]] * t); } s1po += s1ps; s2po += s2ps; a1po += a1ps; dpo += dps; } s1so += s1ss; s2so += s2ss; a1so += a1ss; dso += dss; } } else { for (int h = 0; h < dheight; h++) { s1po = s1so; s2po = s2so; a1po = a1so; a2po = a2so; dpo = dso; for (int w = 0; w < dwidth; w++) { int t1 = a1[a1po+a1bo]; // a1 float t2 = 1.0F - t1 * invMaxValue; // 1-a1/maxValue /* Destination alpha channel. */ d[aOffset][dpo+dbo[aOffset]] = (int) (t1 + a2[a2po+a2bo] * t2); /* Destination color channels. */ for (int b = 0; b < numBands; b++) { int i = b + cOffset; d[i][dpo+dbo[i]] = (int)(s1[b][s1po+s1bo[b]] + s2[b][s2po+s2bo[b]] * t2); } s1po += s1ps; s2po += s2ps; a1po += a1ps; a2po += a2ps; dpo += dps; } s1so += s1ss; s2so += s2ss; a1so += a1ss; a2so += a2ss; dso += dss; } } } else { if (afa2 == null) { for (int h = 0; h < dheight; h++) { s1po = s1so; s2po = s2so; a1po = a1so; dpo = dso; for (int w = 0; w < dwidth; w++) { float t1 = a1[a1po+a1bo] * invMaxValue; // a1/maxValue float t2 = 1.0F - t1; // 1-a1/maxValue /* Destination alpha channel. */ d[aOffset][dpo+dbo[aOffset]] = maxValue; /* Destination color channels. */ for (int b = 0; b < numBands; b++) { int i = b + cOffset; d[i][dpo+dbo[i]] = (int) (s1[b][s1po+s1bo[b]] * t1 + s2[b][s2po+s2bo[b]] * t2); } s1po += s1ps; s2po += s2ps; a1po += a1ps; dpo += dps; } s1so += s1ss; s2so += s2ss; a1so += a1ss; dso += dss; } } else { for (int h = 0; h < dheight; h++) { s1po = s1so; s2po = s2so; a1po = a1so; a2po = a2so; dpo = dso; for (int w = 0; w < dwidth; w++) { int t1 = a1[a1po+a1bo]; // a1 float t2 = (1.0F - t1 * invMaxValue) * a2[a2po+a2bo]; // a2*(1-a1/maxValue) float t3 = t1 + t2; // d[alpha] float t4, t5; if (t3 == 0.0F) { t4 = 0.0F; t5 = 0.0F; } else { t4 = t1 / t3; t5 = t2 / t3; } /* Destination alpha channel. */ d[aOffset][dpo+dbo[aOffset]] = (int)t3; /* Destination color channels. */ for (int b = 0; b < numBands; b++) { int i = b + cOffset; d[i][dpo+dbo[i]] = (int)(s1[b][s1po+s1bo[b]] * t4 + s2[b][s2po+s2bo[b]] * t5); } s1po += s1ps; s2po += s2ps; a1po += a1ps; a2po += a2ps; dpo += dps; } s1so += s1ss; s2so += s2ss; a1so += a1ss; a2so += a2ss; dso += dss; } } } } private void floatLoop(RasterAccessor src1, RasterAccessor src2, RasterAccessor afa1, RasterAccessor afa2, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int numBands = src1.getNumBands(); /* First source color channels. */ float[][] s1 = src1.getFloatDataArrays(); int s1ss = src1.getScanlineStride(); // scanline stride int s1ps = src1.getPixelStride(); // pixel stride int[] s1bo = src1.getBandOffsets(); // band offsets /* Second source color channels. */ float[][] s2 = src2.getFloatDataArrays(); int s2ss = src2.getScanlineStride(); // scanline stride int s2ps = src2.getPixelStride(); // pixel stride int[] s2bo = src2.getBandOffsets(); // band offsets /* First source alpha channel. */ float[] a1 = afa1.getFloatDataArray(0); // use band 0 int a1ss = afa1.getScanlineStride(); // scanline stride int a1ps = afa1.getPixelStride(); // pixel stride int a1bo = afa1.getBandOffset(0); // band 0 offsets /* Second source alpha channel (if any). */ float[] a2 = null; int a2ss = 0; int a2ps = 0; int a2bo = 0; if (afa2 != null) { a2 = afa2.getFloatDataArray(0); // use band 0 a2ss = afa2.getScanlineStride(); // scanline stride a2ps = afa2.getPixelStride(); // pixel stride a2bo = afa2.getBandOffset(0); // band 0 offset } /* Destination color and alpha channels. */ float[][] d = dst.getFloatDataArrays(); int dss = dst.getScanlineStride(); // scanline stride int dps = dst.getPixelStride(); // pixel stride int[] dbo = dst.getBandOffsets(); // band offsets int s1so = 0, s2so = 0, a1so = 0, a2so = 0, dso = 0; int s1po, s2po, a1po, a2po, dpo; // po = pixel offset float invMaxValue = 1.0F / Float.MAX_VALUE; if (alphaPremultiplied) { if (afa2 == null) { for (int h = 0; h < dheight; h++) { s1po = s1so; s2po = s2so; a1po = a1so; dpo = dso; for (int w = 0; w < dwidth; w++) { float t = 1.0F - a1[a1po+a1bo] * invMaxValue; /* Destination alpha channel. */ d[aOffset][dpo+dbo[aOffset]] = Float.MAX_VALUE; /* Destination color channels. */ for (int b = 0; b < numBands; b++) { int i = b + cOffset; d[i][dpo+dbo[i]] = s1[b][s1po+s1bo[b]] + s2[b][s2po+s2bo[b]] * t; } s1po += s1ps; s2po += s2ps; a1po += a1ps; dpo += dps; } s1so += s1ss; s2so += s2ss; a1so += a1ss; dso += dss; } } else { for (int h = 0; h < dheight; h++) { s1po = s1so; s2po = s2so; a1po = a1so; a2po = a2so; dpo = dso; for (int w = 0; w < dwidth; w++) { float t1 = a1[a1po+a1bo]; // a1 float t2 = 1.0F - t1 * invMaxValue; // 1-a1/maxValue /* Destination alpha channel. */ d[aOffset][dpo+dbo[aOffset]] = t1 + a2[a2po+a2bo] * t2; /* Destination color channels. */ for (int b = 0; b < numBands; b++) { int i = b + cOffset; d[i][dpo+dbo[i]] = s1[b][s1po+s1bo[b]] + s2[b][s2po+s2bo[b]] * t2; } s1po += s1ps; s2po += s2ps; a1po += a1ps; a2po += a2ps; dpo += dps; } s1so += s1ss; s2so += s2ss; a1so += a1ss; a2so += a2ss; dso += dss; } } } else { if (afa2 == null) { for (int h = 0; h < dheight; h++) { s1po = s1so; s2po = s2so; a1po = a1so; dpo = dso; for (int w = 0; w < dwidth; w++) { float t1 = a1[a1po+a1bo] * invMaxValue; // a1/maxValue float t2 = 1.0F - t1; // 1-a1/maxValue /* Destination alpha channel. */ d[aOffset][dpo+dbo[aOffset]] = Float.MAX_VALUE; /* Destination color channels. */ for (int b = 0; b < numBands; b++) { int i = b + cOffset; d[i][dpo+dbo[i]] = s1[b][s1po+s1bo[b]] * t1 + s2[b][s2po+s2bo[b]] * t2; } s1po += s1ps; s2po += s2ps; a1po += a1ps; dpo += dps; } s1so += s1ss; s2so += s2ss; a1so += a1ss; dso += dss; } } else { for (int h = 0; h < dheight; h++) { s1po = s1so; s2po = s2so; a1po = a1so; a2po = a2so; dpo = dso; for (int w = 0; w < dwidth; w++) { float t1 = a1[a1po+a1bo]; // a1 float t2 = (1.0F - t1 * invMaxValue) * a2[a2po+a2bo]; // a2*(1-a1/maxValue) float t3 = t1 + t2; // d[alpha] float t4, t5; if (t3 == 0.0F) { t4 = 0.0F; t5 = 0.0F; } else { t4 = t1 / t3; t5 = t2 / t3; } /* Destination alpha channel. */ d[aOffset][dpo+dbo[aOffset]] = t3; /* Destination color channels. */ for (int b = 0; b < numBands; b++) { int i = b + cOffset; d[i][dpo+dbo[i]] = s1[b][s1po+s1bo[b]] * t4 + s2[b][s2po+s2bo[b]] * t5; } s1po += s1ps; s2po += s2ps; a1po += a1ps; a2po += a2ps; dpo += dps; } s1so += s1ss; s2so += s2ss; a1so += a1ss; a2so += a2ss; dso += dss; } } } } private void doubleLoop(RasterAccessor src1, RasterAccessor src2, RasterAccessor afa1, RasterAccessor afa2, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int numBands = src1.getNumBands(); /* First source color channels. */ double[][] s1 = src1.getDoubleDataArrays(); int s1ss = src1.getScanlineStride(); // scanline stride int s1ps = src1.getPixelStride(); // pixel stride int[] s1bo = src1.getBandOffsets(); // band offsets /* Second source color channels. */ double[][] s2 = src2.getDoubleDataArrays(); int s2ss = src2.getScanlineStride(); // scanline stride int s2ps = src2.getPixelStride(); // pixel stride int[] s2bo = src2.getBandOffsets(); // band offsets /* First source alpha channel. */ double[] a1 = afa1.getDoubleDataArray(0); // use band 0 int a1ss = afa1.getScanlineStride(); // scanline stride int a1ps = afa1.getPixelStride(); // pixel stride int a1bo = afa1.getBandOffset(0); // band 0 offsets /* Second source alpha channel (if any). */ double[] a2 = null; int a2ss = 0; int a2ps = 0; int a2bo = 0; if (afa2 != null) { a2 = afa2.getDoubleDataArray(0); // use band 0 a2ss = afa2.getScanlineStride(); // scanline stride a2ps = afa2.getPixelStride(); // pixel stride a2bo = afa2.getBandOffset(0); // band 0 offset } /* Destination color and alpha channels. */ double[][] d = dst.getDoubleDataArrays(); int dss = dst.getScanlineStride(); // scanline stride int dps = dst.getPixelStride(); // pixel stride int[] dbo = dst.getBandOffsets(); // band offsets int s1so = 0, s2so = 0, a1so = 0, a2so = 0, dso = 0; int s1po, s2po, a1po, a2po, dpo; // po = pixel offset double invMaxValue = 1.0D / Double.MAX_VALUE; if (alphaPremultiplied) { if (afa2 == null) { for (int h = 0; h < dheight; h++) { s1po = s1so; s2po = s2so; a1po = a1so; dpo = dso; for (int w = 0; w < dwidth; w++) { double t = 1.0D - a1[a1po+a1bo] * invMaxValue; /* Destination alpha channel. */ d[aOffset][dpo+dbo[aOffset]] = Double.MAX_VALUE; /* Destination color channels. */ for (int b = 0; b < numBands; b++) { int i = b + cOffset; d[i][dpo+dbo[i]] = s1[b][s1po+s1bo[b]] + s2[b][s2po+s2bo[b]] * t; } s1po += s1ps; s2po += s2ps; a1po += a1ps; dpo += dps; } s1so += s1ss; s2so += s2ss; a1so += a1ss; dso += dss; } } else { for (int h = 0; h < dheight; h++) { s1po = s1so; s2po = s2so; a1po = a1so; a2po = a2so; dpo = dso; for (int w = 0; w < dwidth; w++) { double t1 = a1[a1po+a1bo]; // a1 double t2 = 1.0D - t1 * invMaxValue; // 1-a1/maxValue /* Destination alpha channel. */ d[aOffset][dpo+dbo[aOffset]] = t1 + a2[a2po+a2bo] * t2; /* Destination color channels. */ for (int b = 0; b < numBands; b++) { int i = b + cOffset; d[i][dpo+dbo[i]] = s1[b][s1po+s1bo[b]] + s2[b][s2po+s2bo[b]] * t2; } s1po += s1ps; s2po += s2ps; a1po += a1ps; a2po += a2ps; dpo += dps; } s1so += s1ss; s2so += s2ss; a1so += a1ss; a2so += a2ss; dso += dss; } } } else { if (afa2 == null) { for (int h = 0; h < dheight; h++) { s1po = s1so; s2po = s2so; a1po = a1so; dpo = dso; for (int w = 0; w < dwidth; w++) { double t1 = a1[a1po+a1bo] * invMaxValue; // a1/maxValue double t2 = 1.0D - t1; // 1-a1/maxValue /* Destination alpha channel. */ d[aOffset][dpo+dbo[aOffset]] = Double.MAX_VALUE; /* Destination color channels. */ for (int b = 0; b < numBands; b++) { int i = b + cOffset; d[i][dpo+dbo[i]] = s1[b][s1po+s1bo[b]] * t1 + s2[b][s2po+s2bo[b]] * t2; } s1po += s1ps; s2po += s2ps; a1po += a1ps; dpo += dps; } s1so += s1ss; s2so += s2ss; a1so += a1ss; dso += dss; } } else { for (int h = 0; h < dheight; h++) { s1po = s1so; s2po = s2so; a1po = a1so; a2po = a2so; dpo = dso; for (int w = 0; w < dwidth; w++) { double t1 = a1[a1po+a1bo]; // a1 double t2 = (1.0D - t1 * invMaxValue) * a2[a2po+a2bo]; // a2*(1-a1/maxValue) double t3 = t1 + t2; // d[alpha] double t4, t5; if (t3 == 0.0D) { t4 = 0.0D; t5 = 0.0D; } else { t4 = t1 / t3; t5 = t2 / t3; } /* Destination alpha channel. */ d[aOffset][dpo+dbo[aOffset]] = t3; /* Destination color channels. */ for (int b = 0; b < numBands; b++) { int i = b + cOffset; d[i][dpo+dbo[i]] = s1[b][s1po+s1bo[b]] * t4 + s2[b][s2po+s2bo[b]] * t5; } s1po += s1ps; s2po += s2ps; a1po += a1ps; a2po += a2ps; dpo += dps; } s1so += s1ss; s2so += s2ss; a1so += a1ss; a2so += a2ss; dso += dss; } } } } // public static void main(String args[]) { // System.out.println("AddOpImage Test"); // ImageLayout layoutSrc, layoutAlpha; // OpImage src1, src2, afa1, afa2, dst; // Rectangle rect = new Rectangle(0, 0, 10, 5); // layoutAlpha = OpImageTester.createImageLayout( // 0, 0, 800, 800, 0, 0, 200, 200, DataBuffer.TYPE_BYTE, 1, false); // afa1 = OpImageTester.createRandomOpImage(layoutAlpha); // afa2 = OpImageTester.createRandomOpImage(layoutAlpha); // OpImageTester.printPixels("Alpha 1", afa1, rect); // OpImageTester.printPixels("Alpha 2", afa2, rect); // System.out.println("1. PixelInterleaved byte 3-band"); // layoutSrc = OpImageTester.createImageLayout( // 0, 0, 800, 800, 0, 0, 200, 200, DataBuffer.TYPE_BYTE, 3, false); // src1 = OpImageTester.createRandomOpImage(layoutSrc); // src2 = OpImageTester.createRandomOpImage(layoutSrc); // System.out.println("1a. Alpha premultiplied, source2 opaque"); // dst = new CompositeOpImage(src1, src2, null, null, // afa1, null, true, true); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("1b. Alpha premultiplied, source2 not opaque"); // dst = new CompositeOpImage(src1, src2, null, null, // afa1, afa2, true, true); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("1c. Alpha not premultiplied, source2 opaque"); // dst = new CompositeOpImage(src1, src2, null, null, // afa1, null, false, true); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("1d. Alpha not premultiplied, source2 not opaque"); // dst = new CompositeOpImage(src1, src2, null, null, // afa1, afa2, false, true); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("2. Banded byte 3-band"); // layoutSrc = OpImageTester.createImageLayout( // 0, 0, 800, 800, 0, 0, 200, 200, DataBuffer.TYPE_BYTE, 3, true); // src1 = OpImageTester.createRandomOpImage(layoutSrc); // src2 = OpImageTester.createRandomOpImage(layoutSrc); // System.out.println("2b. Alpha premultiplied, source2 not opaque"); // dst = new CompositeOpImage(src1, src2, null, null, // afa1, afa2, true, false); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("2d. Alpha not premultiplied, source2 not opaque"); // dst = new CompositeOpImage(src1, src2, null, null, // afa1, afa2, false, true); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // layoutAlpha = OpImageTester.createImageLayout( // 0, 0, 800, 800, 0, 0, 200, 200, DataBuffer.TYPE_USHORT, 1, true); // afa1 = OpImageTester.createRandomOpImage(layoutAlpha); // afa2 = OpImageTester.createRandomOpImage(layoutAlpha); // OpImageTester.printPixels("Alpha 1", afa1, rect); // OpImageTester.printPixels("Alpha 2", afa2, rect); // System.out.println("3. PixelInterleaved ushort 3-band"); // layoutSrc = OpImageTester.createImageLayout( // 0, 0, 800, 800, 0, 0, 200, 200, DataBuffer.TYPE_USHORT, 3, false); // src1 = OpImageTester.createRandomOpImage(layoutSrc); // src2 = OpImageTester.createRandomOpImage(layoutSrc); // System.out.println("3a. Alpha premultiplied, source2 opaque"); // dst = new CompositeOpImage(src1, src2, null, null, // afa1, null, true, false); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("3d. Alpha not premultiplied, source2 not opaque"); // dst = new CompositeOpImage(src1, src2, null, null, // afa1, afa2, false, false); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/TranslateIntOpImage.java0000644000175000017500000001460310203035544027751 0ustar mathieumathieu/* * $RCSfile: TranslateIntOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:46 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.util.Map; import java.util.Vector; import javax.media.jai.ImageLayout; import javax.media.jai.JAI; import javax.media.jai.OpImage; /** * An OpImage to translate an image by in integral number of pixels. * *

    The translation is accomplished by simply shifting the tile * grid. */ public final class TranslateIntOpImage extends OpImage { private int transX; private int transY; private static ImageLayout layoutHelper(RenderedImage source, int transX, int transY) { ImageLayout layout = new ImageLayout(source.getMinX() + transX, source.getMinY() + transY, source.getWidth(), source.getHeight(), source.getTileGridXOffset() + transX, source.getTileGridYOffset() + transY, source.getTileWidth(), source.getTileHeight(), source.getSampleModel(), source.getColorModel()); return layout; } // Since this operation does not touch the data at all, we do not need // to expand the IndexColorModel private static Map configHelper(Map configuration) { Map config; if (configuration == null) { config = new RenderingHints(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.FALSE); } else { config = configuration; if (!(config.containsKey(JAI.KEY_REPLACE_INDEX_COLOR_MODEL))) { RenderingHints hints = (RenderingHints)configuration; config = (RenderingHints)hints.clone(); config.put(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.FALSE); config.remove(JAI.KEY_TILE_CACHE); } else if (config.containsKey(JAI.KEY_TILE_CACHE)) { RenderingHints hints = (RenderingHints)configuration; config = (RenderingHints)hints.clone(); config.remove(JAI.KEY_TILE_CACHE); } } return config; } /** * Construct an TranslateIntOpImage. * * @param source a RenderedImage. * @param config Configurable attributes of the image including * configuration variables indexed by * RenderingHints.Keys and image properties indexed * by Strings or CaselessStringKeys. * This is simply forwarded to the superclass constructor. * @param transX the number of pixels of horizontal translation. * @param transY the number of pixels of vertical translation. */ public TranslateIntOpImage(RenderedImage source, Map config, int transX, int transY) { super(vectorize(source), layoutHelper(source, transX, transY), configHelper(config), false); this.transX = transX; this.transY = transY; } /** * Returns false as computeTile() invocations * return child Rasters of the RenderedImage * source and are therefore not unique objects in the global sense. */ public boolean computesUniqueTiles() { return false; } /** * Override computeTile() simply to invoke getTile(). Required * so that the TileScheduler may invoke computeTile(). */ public Raster computeTile(int tileX, int tileY) { return getTile(tileX, tileY); } /** * Get a tile. * * @param tileX The X index of the tile. * @param tileY The Y index of the tile. */ public Raster getTile(int tileX, int tileY) { Raster tile = getSource(0).getTile(tileX, tileY); if (tile == null) return null; return tile.createTranslatedChild(tileXToX(tileX), tileYToY(tileY)); } /** * Returns a conservative estimate of the destination region that * can potentially be affected by the pixels of a rectangle of a * given source. * * @param sourceRect the Rectangle in source coordinates. * @param sourceIndex the index of the source image. * @return a Rectangle indicating the potentially affected * destination region. or null if the region is unknown. * @throws IllegalArgumentException if the source index is * negative or greater than that of the last source. * @throws IllegalArgumentException if sourceRect is null. */ public Rectangle mapSourceRect(Rectangle sourceRect, int sourceIndex) { if ( sourceRect == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sourceIndex < 0 || sourceIndex >= getNumSources()) { throw new IllegalArgumentException(JaiI18N.getString("TranslateIntOpImage0")); } Rectangle r = new Rectangle(sourceRect); r.translate(transX, transY); return r; } /** * Returns a conservative estimate of the region of a specified * source that is required in order to compute the pixels of a * given destination rectangle. * * @param destRect the Rectangle in destination coordinates. * @param sourceIndex the index of the source image. * @return a Rectangle indicating the required source region. * @throws IllegalArgumentException if the source index is * negative or greater than that of the last source. * @throws IllegalArgumentException if destRect is null. */ public Rectangle mapDestRect(Rectangle destRect, int sourceIndex) { if ( destRect == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sourceIndex < 0 || sourceIndex >= getNumSources()) { throw new IllegalArgumentException(JaiI18N.getString("TranslateIntOpImage0")); } Rectangle r = new Rectangle(destRect); r.translate(-transX, -transY); return r; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/BoxFilterRIF.java0000644000175000017500000000514410203035544026336 0ustar mathieumathieu/* * $RCSfile: BoxFilterRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:16 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import java.util.Arrays; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; import java.util.Map; /** * A RIF supporting the "BoxFilter" operation in the rendered * image layer. * * @see javax.media.jai.operator.BoxFilterDescriptor * @see com.sun.media.jai.opimage.SeparableConvolveOpImage * * @since EA4 * */ public class BoxFilterRIF implements RenderedImageFactory { /** Constructor. */ public BoxFilterRIF() {} /** * Create a new instance of SeparableConvolveOpImage in the rendered layer. * This method satisfies the implementation of RIF. * * @param paramBlock The source image and the convolution kernel. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); // Get BorderExtender from renderHints if any. BorderExtender extender = RIFUtil.getBorderExtenderHint(renderHints); // Get the operation parameters. int width = paramBlock.getIntParameter(0); int height = paramBlock.getIntParameter(1); int xOrigin = paramBlock.getIntParameter(2); int yOrigin = paramBlock.getIntParameter(3); // Allocate and initialize arrays. float[] dataH = new float[width]; Arrays.fill(dataH, 1.0F/(float)width); float[] dataV = null; if(height == width) { dataV = dataH; } else { dataV = new float[height]; Arrays.fill(dataV, 1.0F/(float)height); } // Construct a separable kernel. KernelJAI kernel = new KernelJAI(width, height, xOrigin, yOrigin, dataH, dataV); // Construct and return the OpImage. return new SeparableConvolveOpImage(paramBlock.getRenderedSource(0), extender, renderHints, layout, kernel); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/DCTOpImage.java0000644000175000017500000002650610203035544025760 0ustar mathieumathieu/* * $RCSfile: DCTOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:22 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.geom.Point2D; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.util.Arrays; import java.util.Map; import javax.media.jai.ImageLayout; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.RasterFactory; import javax.media.jai.UntiledOpImage; import com.sun.media.jai.util.JDKWorkarounds; import com.sun.media.jai.util.MathJAI; /** * An OpImage implementing the forward and inverse even * discrete cosine transform (DCT) operations as described in * javax.media.jai.operator.DCTDescriptor and * javax.media.jai.operator.IDCTDescriptor. * *

    The DCT operation is implemented using a one-dimensional fast cosine * transform (FCT) which is applied successively to the rows and the columns * of the image. All image dimensions are enlarged to the next positive power * of 2 greater than or equal to the respective dimension unless the dimension * is unity in which case it is not modified. Source image values are padded * with zeros when the dimension is smaller than the output power-of-2 * dimension. * * @since EA3 * * @see javax.media.jai.UntiledOpImage * @see javax.media.jai.operator.DCTDescriptor * @see javax.media.jai.operator.IDCTDescriptor * */ public class DCTOpImage extends UntiledOpImage { /** * The Fast Cosine Transform object. */ private FCT fct; /** * Override the dimension specification for the destination such that it * has width and height which are equal to non-negative powers of 2. */ private static ImageLayout layoutHelper(ImageLayout layout, RenderedImage source) { // Create an ImageLayout or clone the one passed in. ImageLayout il = layout == null ? new ImageLayout() : (ImageLayout)layout.clone(); // Force the origin to coincide with that of the source. il.setMinX(source.getMinX()); il.setMinY(source.getMinY()); // Recalculate the non-unity dimensions to be a positive power of 2. // XXX This calculation should not be effected if an implementation // of the FCT which supports arbitrary dimensions is used. boolean createNewSampleModel = false; int w = il.getWidth(source); if(w > 1) { int newWidth = MathJAI.nextPositivePowerOf2(w); if(newWidth != w) { il.setWidth(w = newWidth); createNewSampleModel = true; } } int h = il.getHeight(source); if(h > 1) { int newHeight = MathJAI.nextPositivePowerOf2(h); if(newHeight != h) { il.setHeight(h = newHeight); createNewSampleModel = true; } } // Force the image to contain floating point data. SampleModel sm = il.getSampleModel(source); int dataType = sm.getTransferType(); if(dataType != DataBuffer.TYPE_FLOAT && dataType != DataBuffer.TYPE_DOUBLE) { dataType = DataBuffer.TYPE_FLOAT; createNewSampleModel = true; } // Create a new SampleModel for the destination. if(createNewSampleModel) { sm = RasterFactory.createComponentSampleModel(sm, dataType, w, h, sm.getNumBands()); il.setSampleModel(sm); // Clear the ColorModel mask if needed. ColorModel cm = il.getColorModel(null); if(cm != null && !JDKWorkarounds.areCompatibleDataModels(sm, cm)) { // Clear the mask bit if incompatible. il.unsetValid(ImageLayout.COLOR_MODEL_MASK); } } return il; } /** * Constructs a DCTOpImage object. * *

    The image dimensions are the respective next positive powers of 2 * greater than or equal to the dimensions of the source image. The tile * grid layout, SampleModel, and ColorModel may optionally be specified * by an ImageLayout object. * * @param source A RenderedImage. * @param layout An ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param fct The Fast Cosine Transform object. */ public DCTOpImage(RenderedImage source, Map config, ImageLayout layout, FCT fct) { super(source, config, layoutHelper(layout, source)); // Cache the FCT object. this.fct = fct; } /** * Computes the source point corresponding to the supplied point. * * @param destPt the position in destination image coordinates * to map to source image coordinates. * * @return null. * * @throws IllegalArgumentException if destPt is * null. * * @since JAI 1.1.2 */ public Point2D mapDestPoint(Point2D destPt) { if (destPt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return null; } /** * Computes the destination point corresponding to the supplied point. * * @return null. * * @throws IllegalArgumentException if sourcePt is * null. * * @since JAI 1.1.2 */ public Point2D mapSourcePoint(Point2D sourcePt) { if (sourcePt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return null; } /* * Calculate the discrete cosine transform of the source image. * * @param source The source Raster; should be the whole image. * @param dest The destination WritableRaster; should be the whole image. * @param destRect The destination Rectangle; should be the image bounds. */ protected void computeImage(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; // Degenerate case. if(destRect.width == 1 && destRect.height == 1) { double[] pixel = source.getPixel(destRect.x, destRect.y, (double[])null); dest.setPixel(destRect.x, destRect.y, pixel); return; } // Initialize to first non-unity length to be encountered. fct.setLength(destRect.width > 1 ? getWidth() : getHeight()); // Get some information about the source image. int srcWidth = source.getWidth(); int srcHeight = source.getHeight(); int srcX = source.getMinX(); int srcY = source.getMinY(); // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); RasterAccessor srcAccessor = new RasterAccessor(source, new Rectangle(srcX, srcY, srcWidth, srcHeight), formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor dstAccessor = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); // Set data type flags. int srcDataType = srcAccessor.getDataType(); int dstDataType = dstAccessor.getDataType(); // Set pixel and line strides. int srcPixelStride = srcAccessor.getPixelStride(); int srcScanlineStride = srcAccessor.getScanlineStride(); int dstPixelStride = dstAccessor.getPixelStride(); int dstScanlineStride = dstAccessor.getScanlineStride(); // Loop over the bands. int numBands = sampleModel.getNumBands(); for(int band = 0; band < numBands; band++) { // Get the source and destination arrays for this band. Object srcData = srcAccessor.getDataArray(band); Object dstData = dstAccessor.getDataArray(band); if(destRect.width > 1) { // Set the FCT length. fct.setLength(getWidth()); // Initialize the data offsets for this band. int srcOffset = srcAccessor.getBandOffset(band); int dstOffset = dstAccessor.getBandOffset(band); // Perform the row transforms. for(int row = 0; row < srcHeight; row++) { // Set the input data of the FCT. fct.setData(srcDataType, srcData, srcOffset, srcPixelStride, srcWidth); // Calculate the DFT of the row. fct.transform(); // Get the output data of the FCT. fct.getData(dstDataType, dstData, dstOffset, dstPixelStride); // Increment the data offsets. srcOffset += srcScanlineStride; dstOffset += dstScanlineStride; } } if(destRect.width == 1) { // destRect.height > 1 // Initialize the data offsets for this band. int srcOffset = srcAccessor.getBandOffset(band); int dstOffset = dstAccessor.getBandOffset(band); // Set the input data of the FCT. fct.setData(srcDataType, srcData, srcOffset, srcScanlineStride, srcHeight); // Calculate the DFT of the row. fct.transform(); // Get the output data of the FCT. fct.getData(dstDataType, dstData, dstOffset, dstScanlineStride); } else if(destRect.height > 1) { // destRect.width > 1 // Reset the FCT length. fct.setLength(getHeight()); // Initialize destination offset. int dstOffset = dstAccessor.getBandOffset(band); // Perform the column transforms. for(int col = 0; col < destRect.width; col++) { // Set the input data of the FCT. fct.setData(dstDataType, dstData, dstOffset, dstScanlineStride, destRect.height); // Calculate the DFT of the column. fct.transform(); // Get the output data of the FCT. fct.getData(dstDataType, dstData, dstOffset, dstScanlineStride); // Increment the data offset. dstOffset += dstPixelStride; } } } if (dstAccessor.needsClamping()) { dstAccessor.clampDataArrays(); } // Make sure that the output data is copied to the destination. dstAccessor.copyDataToRaster(); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/TranslateCRIF.java0000644000175000017500000001550510203035544026502 0ustar mathieumathieu/* * $RCSfile: TranslateCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:45 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.geom.Rectangle2D; import java.awt.geom.Point2D; import java.awt.geom.AffineTransform; import java.awt.image.DataBuffer; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.SampleModel; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import java.awt.image.renderable.RenderableImageOp; import java.awt.image.renderable.RenderContext; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationNearest; import javax.media.jai.InterpolationBilinear; import javax.media.jai.InterpolationBicubic; import javax.media.jai.InterpolationBicubic2; import javax.media.jai.TileCache; import javax.media.jai.CRIFImpl; import java.util.Map; /** * This image factory supports image operator TranslateOpImage * in the rendered and renderable image layers. * * @see TranslateOpImage */ public class TranslateCRIF extends CRIFImpl { private static final float TOLERANCE = 0.01F; /** Constructor. */ public TranslateCRIF() { super("translate"); } /** * Creates a new instance of TranslateOpImage * in the rendered layer. This method satisfies the * implementation of RIF. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { RenderedImage source = paramBlock.getRenderedSource(0); float xTrans = paramBlock.getFloatParameter(0); float yTrans = paramBlock.getFloatParameter(1); Interpolation interp = (Interpolation) paramBlock.getObjectParameter(2); // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); // If there is a layout hint, TranslateIntOpImage can't deal with it if ((Math.abs(xTrans - (int)xTrans) < TOLERANCE) && (Math.abs(yTrans - (int)yTrans) < TOLERANCE) && layout == null) { return new TranslateIntOpImage(source, renderHints, (int)xTrans, (int)yTrans); } else { // Get TileCache from renderHints if any. TileCache cache = RIFUtil.getTileCacheHint(renderHints); // Get BorderExtender from renderHints if any. BorderExtender extender = RIFUtil.getBorderExtenderHint(renderHints); // // Call the Scale operation, since it encapsulates Translate // and is better optimized than Affine. // float xScale = 1.0F; float yScale = 1.0F; SampleModel sm = source.getSampleModel(); boolean isBinary = (sm instanceof MultiPixelPackedSampleModel) && (sm.getSampleSize(0) == 1) && (sm.getDataType() == DataBuffer.TYPE_BYTE || sm.getDataType() == DataBuffer.TYPE_USHORT || sm.getDataType() == DataBuffer.TYPE_INT); if (interp instanceof InterpolationNearest) { if (isBinary) { return new ScaleNearestBinaryOpImage(source, extender, renderHints, layout, xScale, yScale, xTrans, yTrans, interp); } else { return new ScaleNearestOpImage(source, extender, renderHints, layout, xScale, yScale, xTrans, yTrans, interp); } } else if (interp instanceof InterpolationBilinear) { if (isBinary) { return new ScaleBilinearBinaryOpImage(source, extender, renderHints, layout, xScale, yScale, xTrans, yTrans, interp); } else { return new ScaleBilinearOpImage(source, extender, renderHints, layout, xScale, yScale, xTrans, yTrans, interp); } } else if ((interp instanceof InterpolationBicubic) || (interp instanceof InterpolationBicubic2)) { return new ScaleBicubicOpImage(source, extender, renderHints, layout, xScale, yScale, xTrans, yTrans, interp); } else { return new ScaleGeneralOpImage(source, extender, renderHints, layout, xScale, yScale, xTrans, yTrans, interp); } } } /** * Creates a new instance of TranslateOpImage * in the renderable layer. This method satisfies the * implementation of CRIF. */ public RenderedImage create(RenderContext renderContext, ParameterBlock paramBlock) { return paramBlock.getRenderedSource(0); } /** * Maps the output RenderContext into the RenderContext for the ith * source. * This method satisfies the implementation of CRIF. * * @param i The index of the source image. * @param renderContext The renderContext being applied to the operation. * @param paramBlock The ParameterBlock containing the sources * and the translation factors. * @param image The RenderableImageOp from which this method * was called. */ public RenderContext mapRenderContext(int i, RenderContext renderContext, ParameterBlock paramBlock, RenderableImage image) { AffineTransform translate = new AffineTransform(); translate.setToTranslation(paramBlock.getFloatParameter(0), paramBlock.getFloatParameter(1)); RenderContext RC = (RenderContext)renderContext.clone(); AffineTransform usr2dev = RC.getTransform(); usr2dev.concatenate(translate); RC.setTransform(usr2dev); return RC; } /** * Gets the bounding box for output of TranslateOpImage. * This method satisfies the implementation of CRIF. */ public Rectangle2D getBounds2D(ParameterBlock paramBlock) { RenderableImage source = paramBlock.getRenderableSource(0); float xTrans = paramBlock.getFloatParameter(0); float yTrans = paramBlock.getFloatParameter(1); return new Rectangle2D.Float(source.getMinX() + xTrans, source.getMinY() + yTrans, source.getWidth(), source.getHeight()); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/PeriodicShiftOpImage.java0000644000175000017500000001604110203035544030073 0ustar mathieumathieu/* * $RCSfile: PeriodicShiftOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:40 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.RasterFactory; import java.util.Map; import com.sun.media.jai.util.JDKWorkarounds; /** * The OpImage implementation of the "PeriodicShift" operation as described in * javax.media.jai.operator.PeriodicShiftDescriptor. * *

    The layout the extended shifted image is copied from its source if * the layout parameter is null. * * @see javax.media.jai.operator.PeriodicShiftDescriptor * @see javax.media.jai.ImageLayout * @see javax.media.jai.OpImage * * @since EA4 */ final class PeriodicShiftOpImage extends OpImage { /** The horizontal translation in pixels for each translated image. */ private int[] xTrans; /** The vertical translation in pixels for each translated image. */ private int[] yTrans; /** The source image translated in four different directions. */ private TranslateIntOpImage[] images; /** The bounds of each of the four translated images. */ private Rectangle[] bounds; /** * Creates a OpImage to return the tiles of a periodic extension. * * @param source a RenderedImage. * @param layout an ImageLayout optionally containing the tile grid * layout, SampleModel, and ColorModel, or null. * @param shiftX the number of pixels of horizontal translation. * @param shiftY the number of pixels of vertical translation. */ public PeriodicShiftOpImage(RenderedImage source, Map config, ImageLayout layout, int shiftX, int shiftY) { super(vectorize(source), layout == null ? new ImageLayout() : (ImageLayout)layout.clone(), config, false); // Calculate the four translation factors. xTrans = new int[] {-shiftX, -shiftX, width - shiftX, width - shiftX}; yTrans = new int[] {-shiftY, height - shiftY, -shiftY, height - shiftY}; // Translate the source image in four separate directions. images = new TranslateIntOpImage[4]; for (int i = 0; i < 4; i++) { images[i] = new TranslateIntOpImage(source, null, xTrans[i], yTrans[i]); } // Compute the intersection of the translated sources with the // destination bounds. Rectangle destBounds = getBounds(); bounds = new Rectangle[4]; for (int i = 0; i < 4; i++) { bounds[i] = destBounds.intersection(images[i].getBounds()); } } /** * Computes a tile of the destination by copying the data which * overlaps the tile in the four translated source images. * * @param tileX the X index of the tile. * @param tileY the Y index of the tile. */ public Raster computeTile(int tileX, int tileY) { // Create a new WritableRaster to represent this tile. Point org = new Point(tileXToX(tileX), tileYToY(tileY)); WritableRaster dest = createWritableRaster(sampleModel, org); // Clip output rectangle to image bounds. Rectangle rect = new Rectangle(org.x, org.y, sampleModel.getWidth(), sampleModel.getHeight()); Rectangle destRect = rect.intersection(getBounds()); // Fill the destination raster. for (int i = 0; i < 4; i++) { // Calculate the overlap with the current translated source. Rectangle overlap = destRect.intersection(bounds[i]); // If the overlap is non-empty, copy the data within it. if (!overlap.isEmpty()) { //dest.setRect(images[i].getData(overlap)); JDKWorkarounds.setRect(dest, images[i].getData(overlap)); } } return dest; } /** * Returns a conservative estimate of the destination region that * can potentially be affected by the pixels of a rectangle of a * given source. * * @param sourceRect the Rectangle in source coordinates. * @param sourceIndex the index of the source image. * @return a Rectangle indicating the potentially affected * destination region. or null if the region is unknown. * @throws IllegalArgumentException if the source index is * negative or greater than that of the last source. * @throws IllegalArgumentException if sourceRect is null. */ public Rectangle mapSourceRect(Rectangle sourceRect, int sourceIndex) { if ( sourceRect == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sourceIndex < 0 || sourceIndex >= getNumSources()) { throw new IllegalArgumentException(JaiI18N.getString("PeriodicShiftOpImage0")); } Rectangle destRect = null; for (int i = 0; i < 4; i++) { Rectangle srcRect = sourceRect; srcRect.translate(xTrans[i], yTrans[i]); Rectangle overlap = srcRect.intersection(getBounds()); if (!overlap.isEmpty()) { destRect = destRect == null ? overlap : destRect.union(overlap); } } return destRect; } /** * Returns a conservative estimate of the region of a specified * source that is required in order to compute the pixels of a * given destination rectangle. * * @param destRect the Rectangle in destination coordinates. * @param sourceIndex the index of the source image. * @return a Rectangle indicating the required source region. * @throws IllegalArgumentException if the source index is * negative or greater than that of the last source. * @throws IllegalArgumentException if destRect is null. */ public Rectangle mapDestRect(Rectangle destRect, int sourceIndex) { if ( destRect == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sourceIndex < 0 || sourceIndex >= getNumSources()) { throw new IllegalArgumentException(JaiI18N.getString("PeriodicShiftOpImage0")); } Rectangle sourceRect = null; for (int i = 0; i < 4; i++) { Rectangle overlap = destRect.intersection(bounds[i]); if (!overlap.isEmpty()) { overlap.translate(-xTrans[i], -yTrans[i]); sourceRect = sourceRect == null ? overlap : sourceRect.union(overlap); } } return sourceRect; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/AffineGeneralOpImage.java0000644000175000017500000011531210203035544030026 0ustar mathieumathieu/* * $RCSfile: AffineGeneralOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:13 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.geom.Point2D; import java.awt.geom.AffineTransform; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; /** * An OpImage subclass that performs general Affine mapping */ final class AffineGeneralOpImage extends AffineOpImage { /* The number of subsampleBits */ private int subsampleBits; private int shiftvalue; private int interp_width, interp_height ; private int interp_left, interp_top, interp_right, interp_bottom; /** * Constructs an AffineGeneralOpImage from a RenderedImage source, * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param interp an Interpolation object to use for resampling * @param transform the desired AffineTransform. */ public AffineGeneralOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, AffineTransform transform, Interpolation interp, double[] backgroundValues) { super(source, extender, config, layout, transform, interp, backgroundValues); subsampleBits = interp.getSubsampleBitsH(); shiftvalue = 1 << subsampleBits; interp_width = interp.getWidth(); interp_height = interp.getHeight(); interp_left = interp.getLeftPadding(); interp_top = interp.getTopPadding(); interp_right = interp_width - interp_left - 1; interp_bottom = interp_height - interp_top - 1; } /** * Performs an affine transform on a specified rectangle. The sources are * cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster [] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); Raster source = sources[0]; Rectangle srcRect = source.getBounds(); int srcRectX = srcRect.x; int srcRectY = srcRect.y; // // Get data for the source rectangle & the destination rectangle // In the first version source Rectangle is the whole source // image always. // // See if we can cache the source to avoid multiple rasteraccesors // RasterAccessor srcAccessor = new RasterAccessor(source, srcRect, formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor dstAccessor = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(srcAccessor, destRect, srcRectX, srcRectY, dstAccessor); break; case DataBuffer.TYPE_INT: intLoop(srcAccessor, destRect, srcRectX, srcRectY, dstAccessor); break; case DataBuffer.TYPE_SHORT: shortLoop(srcAccessor, destRect, srcRectX, srcRectY, dstAccessor); break; case DataBuffer.TYPE_USHORT: ushortLoop(srcAccessor, destRect, srcRectX, srcRectY, dstAccessor); break; case DataBuffer.TYPE_FLOAT: floatLoop(srcAccessor, destRect, srcRectX, srcRectY, dstAccessor); break; case DataBuffer.TYPE_DOUBLE: doubleLoop(srcAccessor, destRect, srcRectX, srcRectY, dstAccessor); break; } // If the RasterAccessor object set up a temporary buffer for the // op to write to, tell the RasterAccessor to write that data // to the raster, that we're done with it. if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } private void byteLoop(RasterAccessor src, Rectangle destRect, int srcRectX, int srcRectY, RasterAccessor dst) { float src_rect_x1 = src.getX(); float src_rect_y1 = src.getY(); float src_rect_x2 = src_rect_x1 + src.getWidth(); float src_rect_y2 = src_rect_y1 + src.getHeight(); float s_x, s_y; float fracx, fracy; int s_ix, s_iy; int p_x, p_y; int s, q; int result; int samples[][] = new int[interp_height][interp_width]; int xfrac, yfrac; int dstPixelOffset; int dstOffset = 0; Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); byte dstDataArrays[][] = dst.getByteDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); byte srcDataArrays[][] = src.getByteDataArrays(); int bandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; byte[] backgroundByte = new byte[dst_num_bands]; for (int i = 0; i < dst_num_bands; i++) backgroundByte[i] = (byte)backgroundValues[i]; for (int y = dst_min_y; y < dst_max_y ; y++) { dstPixelOffset = dstOffset; // Backward map the first point in the line // The energy is at the (pt_x + 0.5, pt_y + 0.5) dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates s_x = (float) src_pt.getX(); s_y = (float) src_pt.getY(); // As per definition of bicubic interpolation s_x -= 0.5; s_y -= 0.5; // Floor to get the integral coordinate s_ix = (int) Math.floor(s_x); s_iy = (int) Math.floor(s_y); fracx = s_x - (float) s_ix; fracy = s_y - (float) s_iy; // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; for (int x = dst_min_x; x < dst_max_x; x++) { // // Check against the source rectangle // if ((s_ix >= src_rect_x1 + interp_left) && (s_ix < (src_rect_x2 - interp_right)) && (s_iy >= (src_rect_y1 + interp_top)) && (s_iy < (src_rect_y2 - interp_bottom))) { for (int k=0; k < dst_num_bands; k++) { byte srcData[] = srcDataArrays[k]; int tmp = bandOffsets[k]; // Get the pixels required for this interpolation int start = interp_left * srcPixelStride + interp_top * srcScanlineStride; start = p_x + p_y - start; int countH = 0, countV = 0; for (int i = 0; i < interp_height; i++) { int startY = start; for (int j = 0; j < interp_width; j++) { samples[countV][countH++] = srcData[start + tmp] & 0xff; start += srcPixelStride; } countV++; countH = 0; start = startY + srcScanlineStride; } // Get the new frac values xfrac = (int) (fracx * shiftvalue); yfrac = (int) (fracy * shiftvalue); // Do the interpolation s = interp.interpolate(samples, xfrac, yfrac); // Clamp if (s < 0) { result = 0; } else if (s > 255) { result = 255; } else { result = s; } // write the result dstDataArrays[k] [dstPixelOffset+dstBandOffsets[k]] = (byte) (result & 0xff); } } else if (setBackground) { for (int k=0; k < dst_num_bands; k++) dstDataArrays[k][dstPixelOffset+dstBandOffsets[k]] = backgroundByte[k]; } // walk if (fracx < fracdx1) { s_ix += incx; fracx += fracdx; } else { s_ix += incx1; fracx -= fracdx1; } if (fracy < fracdy1) { s_iy += incy; fracy += fracdy; } else { s_iy += incy1; fracy -= fracdy1; } // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; dstPixelOffset += dstPixelStride; } dstOffset += dstScanlineStride; } } private void intLoop(RasterAccessor src, Rectangle destRect, int srcRectX, int srcRectY, RasterAccessor dst) { float src_rect_x1 = src.getX(); float src_rect_y1 = src.getY(); float src_rect_x2 = src_rect_x1 + src.getWidth(); float src_rect_y2 = src_rect_y1 + src.getHeight(); float s_x, s_y; float fracx, fracy; int s_ix, s_iy; int p_x, p_y; int s, q; int result; int dstPixelOffset; int dstOffset = 0; int samples[][] = new int[interp_height][interp_width]; int xfrac, yfrac; Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); int dstDataArrays[][] = dst.getIntDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcDataArrays[][] = src.getIntDataArrays(); int bandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; int[] backgroundInt = new int[dst_num_bands]; for (int i = 0; i < dst_num_bands; i++) backgroundInt[i] = (int)backgroundValues[i]; for (int y = dst_min_y; y < dst_max_y ; y++) { dstPixelOffset = dstOffset; // Backward map the first point in the line // The energy is at the (pt_x + 0.5, pt_y + 0.5) dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates s_x = (float) src_pt.getX(); s_y = (float) src_pt.getY(); // As per definition of bicubic interpolation s_x -= 0.5; s_y -= 0.5; // Floor to get the integral coordinate s_ix = (int) Math.floor(s_x); s_iy = (int) Math.floor(s_y); fracx = s_x - (float) s_ix; fracy = s_y - (float) s_iy; // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; for (int x = dst_min_x; x < dst_max_x; x++) { // // Check against the source rectangle // if ((s_ix >= (src_rect_x1 + interp_left)) && (s_ix < (src_rect_x2 - interp_right)) && (s_iy >= (src_rect_y1 + interp_top)) && (s_iy < (src_rect_y2 - interp_bottom))) { for (int k=0; k < dst_num_bands; k++) { int srcData[] = srcDataArrays[k]; int tmp = bandOffsets[k]; // Get the pixels required for this interpolation int start = interp_left * srcPixelStride + interp_top * srcScanlineStride; start = p_x + p_y - start; int countH = 0, countV = 0; for (int i = 0; i < interp_height; i++) { int startY = start; for (int j = 0; j < interp_width; j++) { samples[countV][countH++] = srcData[start + tmp]; start += srcPixelStride; } countV++; countH = 0; start = startY + srcScanlineStride; } // Get the new frac values xfrac = (int) (fracx * shiftvalue); yfrac = (int) (fracy * shiftvalue); // Do the interpolation s = interp.interpolate(samples, xfrac, yfrac); // Clamp if (s < Integer.MIN_VALUE) { result = Integer.MIN_VALUE; } else if (s > Integer.MAX_VALUE) { result = Integer.MAX_VALUE; } else { result = s; } // write the result dstDataArrays[k] [dstPixelOffset+dstBandOffsets[k]] = result; } } else if (setBackground) { for (int k=0; k < dst_num_bands; k++) dstDataArrays[k][dstPixelOffset+dstBandOffsets[k]] = backgroundInt[k]; } // walk if (fracx < fracdx1) { s_ix += incx; fracx += fracdx; } else { s_ix += incx1; fracx -= fracdx1; } if (fracy < fracdy1) { s_iy += incy; fracy += fracdy; } else { s_iy += incy1; fracy -= fracdy1; } // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; dstPixelOffset += dstPixelStride; } dstOffset += dstScanlineStride; } } private void shortLoop(RasterAccessor src, Rectangle destRect, int srcRectX, int srcRectY, RasterAccessor dst) { float src_rect_x1 = src.getX(); float src_rect_y1 = src.getY(); float src_rect_x2 = src_rect_x1 + src.getWidth(); float src_rect_y2 = src_rect_y1 + src.getHeight(); float s_x, s_y; float fracx, fracy; int s_ix, s_iy; int p_x, p_y; int s, q; int samples[][] = new int[interp_height][interp_width]; int xfrac, yfrac; short result; int dstPixelOffset; int dstOffset = 0; Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int bandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; short[] backgroundShort = new short[dst_num_bands]; for (int i = 0; i < dst_num_bands; i++) backgroundShort[i] = (short)backgroundValues[i]; for (int y = dst_min_y; y < dst_max_y ; y++) { dstPixelOffset = dstOffset; // Backward map the first point in the line // The energy is at the (pt_x + 0.5, pt_y + 0.5) dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates s_x = (float) src_pt.getX(); s_y = (float) src_pt.getY(); // As per definition of bicubic interpolation s_x -= 0.5; s_y -= 0.5; // Floor to get the integral coordinate s_ix = (int) Math.floor(s_x); s_iy = (int) Math.floor(s_y); fracx = s_x - (float) s_ix; fracy = s_y - (float) s_iy; // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; for (int x = dst_min_x; x < dst_max_x; x++) { // // Check against the source rectangle // if ((s_ix >= (src_rect_x1 + interp_left)) && (s_ix < (src_rect_x2 - interp_right)) && (s_iy >= (src_rect_y1 + interp_top)) && (s_iy < (src_rect_y2 - interp_bottom))) { for (int k=0; k < dst_num_bands; k++) { short srcData[] = srcDataArrays[k]; int tmp = bandOffsets[k]; // Get the pixels required for this interpolation int start = interp_left * srcPixelStride + interp_top * srcScanlineStride; start = p_x + p_y - start; int countH = 0, countV = 0; for (int i = 0; i < interp_height; i++) { int startY = start; for (int j = 0; j < interp_width; j++) { samples[countV][countH++] = srcData[start + tmp]; start += srcPixelStride; } countV++; countH = 0; start = startY + srcScanlineStride; } // Get the new frac values xfrac = (int) (fracx * shiftvalue); yfrac = (int) (fracy * shiftvalue); // Do the interpolation s = interp.interpolate(samples, xfrac, yfrac); // Round the result if (s < Short.MIN_VALUE) { result = Short.MIN_VALUE; } else if (s > Short.MAX_VALUE) { result = Short.MAX_VALUE; } else { result = (short) s; } // write the result dstDataArrays[k] [dstPixelOffset+dstBandOffsets[k]] = result; } } else if (setBackground) { for (int k=0; k < dst_num_bands; k++) dstDataArrays[k][dstPixelOffset+dstBandOffsets[k]] = backgroundShort[k]; } // walk if (fracx < fracdx1) { s_ix += incx; fracx += fracdx; } else { s_ix += incx1; fracx -= fracdx1; } if (fracy < fracdy1) { s_iy += incy; fracy += fracdy; } else { s_iy += incy1; fracy -= fracdy1; } // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; dstPixelOffset += dstPixelStride; } dstOffset += dstScanlineStride; } } private void ushortLoop(RasterAccessor src, Rectangle destRect, int srcRectX, int srcRectY, RasterAccessor dst) { float src_rect_x1 = src.getX(); float src_rect_y1 = src.getY(); float src_rect_x2 = src_rect_x1 + src.getWidth(); float src_rect_y2 = src_rect_y1 + src.getHeight(); float s_x, s_y; float fracx, fracy; int s_ix, s_iy; int p_x, p_y; int s, q; int samples[][] = new int[interp_height][interp_width]; int xfrac, yfrac; int result; int dstPixelOffset; int dstOffset = 0; Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int bandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; short[] backgroundUShort = new short[dst_num_bands]; for (int i = 0; i < dst_num_bands; i++) backgroundUShort[i] = (short)backgroundValues[i]; for (int y = dst_min_y; y < dst_max_y ; y++) { dstPixelOffset = dstOffset; // Backward map the first point in the line // The energy is at the (pt_x + 0.5, pt_y + 0.5) dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates s_x = (float) src_pt.getX(); s_y = (float) src_pt.getY(); // As per definition of bicubic interpolation s_x -= 0.5; s_y -= 0.5; // Floor to get the integral coordinate s_ix = (int) Math.floor(s_x); s_iy = (int) Math.floor(s_y); fracx = s_x - (float) s_ix; fracy = s_y - (float) s_iy; // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; for (int x = dst_min_x; x < dst_max_x; x++) { // // Check against the source rectangle // if ((s_ix >= (src_rect_x1 + interp_left)) && (s_ix < (src_rect_x2 - interp_right)) && (s_iy >= (src_rect_y1 + interp_top)) && (s_iy < (src_rect_y2 - interp_bottom))) { for (int k=0; k < dst_num_bands; k++) { short srcData[] = srcDataArrays[k]; int tmp = bandOffsets[k]; // Get the pixels required for this interpolation int start = interp_left * srcPixelStride + interp_top * srcScanlineStride; start = p_x + p_y - start; int countH = 0, countV = 0; for (int i = 0; i < interp_height; i++) { int startY = start; for (int j = 0; j < interp_width; j++) { samples[countV][countH++] = srcData[start + tmp] & 0xffff; start += srcPixelStride; } countV++; countH = 0; start = startY + srcScanlineStride; } // Get the new frac values xfrac = (int) (fracx * shiftvalue); yfrac = (int) (fracy * shiftvalue); // Do the interpolation s = interp.interpolate(samples, xfrac, yfrac); // Round if (s < 0) { result = 0; } else if (s > USHORT_MAX) { result = USHORT_MAX; } else { result = s; } // write the result dstDataArrays[k] [dstPixelOffset+dstBandOffsets[k]] = (short)(result & 0xFFFF); } } else if (setBackground) { for (int k=0; k < dst_num_bands; k++) dstDataArrays[k][dstPixelOffset+dstBandOffsets[k]] = backgroundUShort[k]; } // walk if (fracx < fracdx1) { s_ix += incx; fracx += fracdx; } else { s_ix += incx1; fracx -= fracdx1; } if (fracy < fracdy1) { s_iy += incy; fracy += fracdy; } else { s_iy += incy1; fracy -= fracdy1; } // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; dstPixelOffset += dstPixelStride; } dstOffset += dstScanlineStride; } } private void floatLoop(RasterAccessor src, Rectangle destRect, int srcRectX, int srcRectY, RasterAccessor dst) { float src_rect_x1 = src.getX(); float src_rect_y1 = src.getY(); float src_rect_x2 = src_rect_x1 + src.getWidth(); float src_rect_y2 = src_rect_y1 + src.getHeight(); float s_x, s_y; float fracx, fracy; int s_ix, s_iy; int p_x, p_y; float s, q; float samples[][] = new float[interp_height][interp_width]; long xfrac, yfrac; int dstPixelOffset; int dstOffset = 0; Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); float dstDataArrays[][] = dst.getFloatDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); float srcDataArrays[][] = src.getFloatDataArrays(); int bandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; float[] backgroundFloat = new float[dst_num_bands]; for (int i = 0; i < dst_num_bands; i++) backgroundFloat[i] = (float)backgroundValues[i]; for (int y = dst_min_y; y < dst_max_y ; y++) { dstPixelOffset = dstOffset; // Backward map the first point in the line // The energy is at the (pt_x + 0.5, pt_y + 0.5) dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates s_x = (float) src_pt.getX(); s_y = (float) src_pt.getY(); // As per definition of bicubic interpolation s_x -= 0.5; s_y -= 0.5; // Floor to get the integral coordinate s_ix = (int) Math.floor(s_x); s_iy = (int) Math.floor(s_y); fracx = s_x - (float) s_ix; fracy = s_y - (float) s_iy; // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; for (int x = dst_min_x; x < dst_max_x; x++) { // // Check against the source rectangle // if ((s_ix >= (src_rect_x1 + interp_left)) && (s_ix < (src_rect_x2 - interp_right)) && (s_iy >= (src_rect_y1 + interp_top)) && (s_iy < (src_rect_y2 - interp_bottom))) { for (int k=0; k < dst_num_bands; k++) { float srcData[] = srcDataArrays[k]; int tmp = bandOffsets[k]; // Get the pixels required for this interpolation int start = interp_left * srcPixelStride + interp_top * srcScanlineStride; start = p_x + p_y - start; int countH = 0, countV = 0; for (int i = 0; i < interp_height; i++) { int startY = start; for (int j = 0; j < interp_width; j++) { samples[countV][countH++] = srcData[start + tmp]; start += srcPixelStride; } countV++; countH = 0; start = startY + srcScanlineStride; } // Do the interpolation s = interp.interpolate(samples, fracx, fracy); // write the result dstDataArrays[k] [dstPixelOffset+dstBandOffsets[k]] = s; } } else if (setBackground) { for (int k=0; k < dst_num_bands; k++) dstDataArrays[k][dstPixelOffset+dstBandOffsets[k]] = backgroundFloat[k]; } // walk if (fracx < fracdx1) { s_ix += incx; fracx += fracdx; } else { s_ix += incx1; fracx -= fracdx1; } if (fracy < fracdy1) { s_iy += incy; fracy += fracdy; } else { s_iy += incy1; fracy -= fracdy1; } // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; dstPixelOffset += dstPixelStride; } dstOffset += dstScanlineStride; } } private void doubleLoop(RasterAccessor src, Rectangle destRect, int srcRectX, int srcRectY, RasterAccessor dst) { float src_rect_x1 = src.getX(); float src_rect_y1 = src.getY(); float src_rect_x2 = src_rect_x1 + src.getWidth(); float src_rect_y2 = src_rect_y1 + src.getHeight(); double s_x, s_y; double fracx, fracy; int s_ix, s_iy; int p_x, p_y; double s, q; double samples[][] = new double[interp_height][interp_width]; float xfrac, yfrac; int dstPixelOffset; int dstOffset = 0; Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); double dstDataArrays[][] = dst.getDoubleDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); double srcDataArrays[][] = src.getDoubleDataArrays(); int bandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; for (int y = dst_min_y; y < dst_max_y ; y++) { dstPixelOffset = dstOffset; // Backward map the first point in the line // The energy is at the (pt_x + 0.5, pt_y + 0.5) dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates s_x = (double) src_pt.getX(); s_y = (double) src_pt.getY(); // As per definition of bicubic interpolation s_x -= 0.5; s_y -= 0.5; // Floor to get the integral coordinate s_ix = (int) Math.floor(s_x); s_iy = (int) Math.floor(s_y); fracx = s_x - (double) s_ix; fracy = s_y - (double) s_iy; // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; for (int x = dst_min_x; x < dst_max_x; x++) { // // Check against the source rectangle // if ((s_ix >= (src_rect_x1 + interp_left)) && (s_ix < (src_rect_x2 - interp_right)) && (s_iy >= (src_rect_y1 + interp_top)) && (s_iy < (src_rect_y2 - interp_bottom))) { for (int k=0; k < dst_num_bands; k++) { double srcData[] = srcDataArrays[k]; int tmp = bandOffsets[k]; // Get the pixels required for this interpolation int start = interp_left * srcPixelStride + interp_top * srcScanlineStride; start = p_x + p_y - start; int countH = 0, countV = 0; for (int i = 0; i < interp_height; i++) { int startY = start; for (int j = 0; j < interp_width; j++) { samples[countV][countH++] = srcData[start + tmp]; start += srcPixelStride; } countV++; countH = 0; start = startY + srcScanlineStride; } // Do the interpolation s = interp.interpolate(samples, (float)fracx, (float)fracy); // write the result dstDataArrays[k] [dstPixelOffset+dstBandOffsets[k]] = s; } } else if (setBackground) { for (int k=0; k < dst_num_bands; k++) dstDataArrays[k][dstPixelOffset+dstBandOffsets[k]] = backgroundValues[k]; } // walk if (fracx < fracdx1) { s_ix += incx; fracx += fracdx; } else { s_ix += incx1; fracx -= fracdx1; } if (fracy < fracdy1) { s_iy += incy; fracy += fracdy; } else { s_iy += incy1; fracy -= fracdy1; } // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; dstPixelOffset += dstPixelStride; } dstOffset += dstScanlineStride; } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/HistogramOpImage.java0000644000175000017500000000656710203035544027310 0ustar mathieumathieu/* * $RCSfile: HistogramOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:27 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.util.LinkedList; import java.util.ListIterator; import javax.media.jai.Histogram; import javax.media.jai.PixelAccessor; import javax.media.jai.ROI; import javax.media.jai.StatisticsOpImage; import javax.media.jai.UnpackedImageData; /** * An OpImage implementing the "Histogram" operation as * described in javax.media.jai.operator.HistogramDescriptor. * * @see javax.media.jai.Histogram * @see javax.media.jai.operator.HistogramDescriptor * @see HistogramCRIF */ final class HistogramOpImage extends StatisticsOpImage { /** Number of bins per band. */ private int[] numBins; /** The low value checked inclusive for each band. */ private double[] lowValue; /** The high value checked exclusive for each band. */ private double[] highValue; /** The number of bands of the source image. */ private int numBands; private final boolean tileIntersectsROI(int tileX, int tileY) { if (roi == null) { // ROI is entire tile return true; } else { return roi.intersects(tileXToX(tileX), tileYToY(tileY), tileWidth, tileHeight); } } /** * Constructs an HistogramOpImage. * * @param source The source image. */ public HistogramOpImage(RenderedImage source, ROI roi, int xStart, int yStart, int xPeriod, int yPeriod, int[] numBins, double[] lowValue, double[] highValue) { super(source, roi, xStart, yStart, xPeriod, yPeriod); numBands = source.getSampleModel().getNumBands(); this.numBins = new int[numBands]; this.lowValue = new double[numBands]; this.highValue = new double[numBands]; for (int b = 0; b < numBands; b++) { this.numBins[b] = numBins.length == 1 ? numBins[0] : numBins[b]; this.lowValue[b] = lowValue.length == 1 ? lowValue[0] : lowValue[b]; this.highValue[b] = highValue.length == 1 ? highValue[0] : highValue[b]; } } protected String[] getStatisticsNames() { String[] names = new String[1]; names[0] = "histogram"; return names; } protected Object createStatistics(String name) { if (name.equalsIgnoreCase("histogram")) { return new Histogram(numBins, lowValue, highValue); } else { return java.awt.Image.UndefinedProperty; } } protected void accumulateStatistics(String name, Raster source, Object stats) { Histogram histogram = (Histogram)stats; histogram.countPixels(source, roi, xStart, yStart, xPeriod, yPeriod); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ThresholdCRIF.java0000644000175000017500000000325510203035544026500 0ustar mathieumathieu/* * $RCSfile: ThresholdCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:45 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "Threshold" operation in the rendered * and renderable image layers. * * @see javax.media.jai.operator.ThresholdDescriptor * @see ThresholdOpImage * * * @since EA2 */ public class ThresholdCRIF extends CRIFImpl { /** Constructor. */ public ThresholdCRIF() { super("threshold"); } /** * Creates a new instance of ThresholdOpImage in the * rendered layer. * * @param args The source image and the input parameters. * @param hints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new ThresholdOpImage(args.getRenderedSource(0), renderHints, layout, (double[])args.getObjectParameter(0), (double[])args.getObjectParameter(1), (double[])args.getObjectParameter(2)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ScaleBicubicOpImage.java0000644000175000017500000011207710203035544027655 0ustar mathieumathieu/* * $RCSfile: ScaleBicubicOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:42 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationBicubic; import javax.media.jai.InterpolationBicubic2; import javax.media.jai.InterpolationTable; import javax.media.jai.OpImage; import javax.media.jai.PlanarImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.ScaleOpImage; import java.util.Map; import com.sun.media.jai.util.Rational; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage that performs bicubic interpolation scaling. * */ final class ScaleBicubicOpImage extends ScaleOpImage { /* The number of subsampleBits */ private int subsampleBits; /* 2 ^ subsampleBits */ private int one; /** The horizontal coefficient data in fixed-point format. */ private int[] tableDataHi = null; /** The vertical coefficient data in fixed-point format. */ private int[] tableDataVi = null; /** The horizontal coefficient data in floating-point format. */ private float[] tableDataHf = null; /** The vertical coefficient data in floating-point format. */ private float[] tableDataVf = null; /** The horizontal coefficient data in double format. */ private double[] tableDataHd = null; /** The vertical coefficient data in double format. */ private double[] tableDataVd = null; /** Number of fractional bits used to described filter coefficients. */ private int precisionBits; /** The number 1/2 with precisionBits of fractional precision. */ private int round; private Rational half = new Rational(1, 2); // The InterpolationTable superclass. InterpolationTable interpTable; long invScaleYInt, invScaleYFrac; long invScaleXInt, invScaleXFrac; /** * Constructs a ScaleBicubicOpImage from a RenderedImage source, * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param xScale scale factor along x axis. * @param yScale scale factor along y axis. * @param xTrans translation factor along x axis. * @param yTrans translation factor along y axis. * @param interp a Interpolation object to use for resampling. */ public ScaleBicubicOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, float xScale, float yScale, float xTrans, float yTrans, Interpolation interp) { super(source, layout, config, true, extender, interp, xScale, yScale, xTrans, yTrans); subsampleBits = interp.getSubsampleBitsH(); interpTable = (InterpolationTable)interp; // Number of subsample positions one = 1 << subsampleBits; precisionBits = interpTable.getPrecisionBits(); if (precisionBits > 0) { round = 1<< (precisionBits - 1); } if (invScaleYRational.num > invScaleYRational.denom) { invScaleYInt = invScaleYRational.num / invScaleYRational.denom; invScaleYFrac = invScaleYRational.num % invScaleYRational.denom; } else { invScaleYInt = 0; invScaleYFrac = invScaleYRational.num; } if (invScaleXRational.num > invScaleXRational.denom) { invScaleXInt = invScaleXRational.num / invScaleXRational.denom; invScaleXFrac = invScaleXRational.num % invScaleXRational.denom; } else { invScaleXInt = 0; invScaleXFrac = invScaleXRational.num; } } /** * Performs a scale operation on a specified rectangle. The sources are * cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster [] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); Raster source = sources[0]; // Get the source rectangle Rectangle srcRect = source.getBounds(); int srcRectX = srcRect.x; int srcRectY = srcRect.y; RasterAccessor srcAccessor = new RasterAccessor(source, srcRect, formatTags[0], getSource(0).getColorModel()); RasterAccessor dstAccessor = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); // Loop variables based on the destination rectangle to be calculated. int dx = destRect.x; int dy = destRect.y; int dwidth = destRect.width; int dheight = destRect.height; int srcPixelStride = srcAccessor.getPixelStride(); int srcScanlineStride = srcAccessor.getScanlineStride(); int[] ypos = new int[dheight]; int[] xpos = new int[dwidth]; // Precalculate the y positions and store them in an array. int[] yfracvalues = new int[dheight]; // Precalculate the x positions and store them in an array. int[] xfracvalues = new int[dwidth]; long syNum = dy, syDenom = 1; // Subtract the X translation factor sy -= transY syNum = syNum * transYRationalDenom - transYRationalNum * syDenom; syDenom *= transYRationalDenom; // Add 0.5 syNum = 2 * syNum + syDenom; syDenom *= 2; // Multply by invScaleX syNum *= invScaleYRationalNum; syDenom *= invScaleYRationalDenom; // Subtract 0.5 syNum = 2 * syNum - syDenom; syDenom *= 2; // Separate the x source coordinate into integer and fractional part int srcYInt = Rational.floor(syNum , syDenom); long srcYFrac = syNum % syDenom; if (srcYInt < 0) { srcYFrac = syDenom + srcYFrac; } // Normalize - Get a common denominator for the fracs of // src and invScaleY long commonYDenom = syDenom * invScaleYRationalDenom; srcYFrac *= invScaleYRationalDenom; long newInvScaleYFrac = invScaleYFrac * syDenom; long sxNum = dx, sxDenom = 1; // Subtract the X translation factor sx -= transX sxNum = sxNum * transXRationalDenom - transXRationalNum * sxDenom; sxDenom *= transXRationalDenom; // Add 0.5 sxNum = 2 * sxNum + sxDenom; sxDenom *= 2; // Multply by invScaleX sxNum *= invScaleXRationalNum; sxDenom *= invScaleXRationalDenom; // Subtract 0.5 sxNum = 2 * sxNum - sxDenom; sxDenom *= 2; // Separate the x source coordinate into integer and fractional part // int part is floor(sx), frac part is sx - floor(sx) int srcXInt = Rational.floor(sxNum , sxDenom); long srcXFrac = sxNum % sxDenom; if (srcXInt < 0) { srcXFrac = sxDenom + srcXFrac; } // Normalize - Get a common denominator for the fracs of // src and invScaleX long commonXDenom = sxDenom * invScaleXRationalDenom; srcXFrac *= invScaleXRationalDenom; long newInvScaleXFrac = invScaleXFrac * sxDenom; for (int i=0; i= commonXDenom) { srcXInt += 1; srcXFrac -= commonXDenom; } } for (int i = 0; i < dheight; i++) { // Calculate the source position in the source data array. ypos[i] = (srcYInt - srcRectY) * srcScanlineStride; // Calculate the yfrac value yfracvalues[i] = (int)(((float)srcYFrac/(float)commonYDenom) * one); // Move onto the next source pixel. // Add the integral part of invScaleY to the integral part // of srcY srcYInt += invScaleYInt; // Add the fractional part of invScaleY to the fractional part // of srcY srcYFrac += newInvScaleYFrac; // If the fractional part is now greater than equal to the // denominator, divide so as to reduce the numerator to be less // than the denominator and add the overflow to the integral part. if (srcYFrac >= commonYDenom) { srcYInt += 1; srcYFrac -= commonYDenom; } } switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: initTableDataI(); byteLoop(srcAccessor, destRect, dstAccessor, xpos, ypos, xfracvalues, yfracvalues); break; case DataBuffer.TYPE_SHORT: initTableDataI(); shortLoop(srcAccessor, destRect, dstAccessor, xpos, ypos, xfracvalues, yfracvalues); break; case DataBuffer.TYPE_USHORT: initTableDataI(); ushortLoop(srcAccessor, destRect, dstAccessor, xpos, ypos, xfracvalues, yfracvalues); break; case DataBuffer.TYPE_INT: initTableDataI(); intLoop(srcAccessor, destRect, dstAccessor, xpos, ypos, xfracvalues, yfracvalues); break; case DataBuffer.TYPE_FLOAT: initTableDataF(); floatLoop(srcAccessor, destRect, dstAccessor, xpos, ypos, xfracvalues, yfracvalues); break; case DataBuffer.TYPE_DOUBLE: initTableDataD(); doubleLoop(srcAccessor, destRect, dstAccessor, xpos, ypos, xfracvalues, yfracvalues); break; default: throw new RuntimeException(JaiI18N.getString("OrderedDitherOpImage0")); } // If the RasterAccessor object set up a temporary buffer for the // op to write to, tell the RasterAccessor to write that data // to the raster no that we're done with it. if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } private void byteLoop(RasterAccessor src, Rectangle destRect, RasterAccessor dst, int xpos[], int ypos[], int xfracvalues[], int yfracvalues[]) { int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dwidth = destRect.width; int dheight = destRect.height; int dnumBands = dst.getNumBands(); byte dstDataArrays[][] = dst.getByteDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); byte srcDataArrays[][] = src.getByteDataArrays(); int bandOffsets[] = src.getBandOffsets(); int posy, posylow, posyhigh, posyhigh2; int posx, posxlow, posxhigh, posxhigh2; int xfrac, yfrac; int s__, s_0, s_1, s_2; int s0_, s00, s01, s02; int s1_, s10, s11, s12; int s2_, s20, s21, s22; int s, dstOffset = 0; // Putting band loop outside for (int k = 0; k < dnumBands; k++) { byte dstData[] = dstDataArrays[k]; byte srcData[] = srcDataArrays[k]; int dstScanlineOffset = dstBandOffsets[k]; int bandOffset = bandOffsets[k]; for (int j = 0; j < dheight; j++) { int dstPixelOffset = dstScanlineOffset; yfrac = yfracvalues[j]; posy = ypos[j] + bandOffset; posylow = posy - srcScanlineStride; posyhigh = posy + srcScanlineStride; posyhigh2 = posyhigh + srcScanlineStride; for (int i = 0; i < dwidth; i++) { xfrac = xfracvalues[i]; posx = xpos[i]; posxlow = posx - srcPixelStride; posxhigh = posx + srcPixelStride; posxhigh2 = posxhigh + srcPixelStride; // Get the sixteen surrounding pixel values s__ = srcData[posxlow + posylow] & 0xff; s_0 = srcData[posx + posylow] & 0xff; s_1 = srcData[posxhigh + posylow] & 0xff; s_2 = srcData[posxhigh2 + posylow] & 0xff; s0_ = srcData[posxlow + posy] & 0xff; s00 = srcData[posx + posy] & 0xff; s01 = srcData[posxhigh + posy] & 0xff; s02 = srcData[posxhigh2 + posy] & 0xff; s1_ = srcData[posxlow + posyhigh] & 0xff; s10 = srcData[posx + posyhigh] & 0xff; s11 = srcData[posxhigh + posyhigh] & 0xff; s12 = srcData[posxhigh2 + posyhigh] & 0xff; s2_ = srcData[posxlow + posyhigh2] & 0xff; s20 = srcData[posx + posyhigh2] & 0xff; s21 = srcData[posxhigh + posyhigh2] & 0xff; s22 = srcData[posxhigh2 + posyhigh2] & 0xff; // Interpolate in X int offsetX = 4*xfrac; int offsetX1 = offsetX + 1; int offsetX2 = offsetX + 2; int offsetX3 = offsetX + 3; long sum_ = (long)tableDataHi[offsetX]*s__; sum_ += (long)tableDataHi[offsetX1]*s_0; sum_ += (long)tableDataHi[offsetX2]*s_1; sum_ += (long)tableDataHi[offsetX3]*s_2; long sum0 = (long)tableDataHi[offsetX]*s0_; sum0 += (long)tableDataHi[offsetX1]*s00; sum0 += (long)tableDataHi[offsetX2]*s01; sum0 += (long)tableDataHi[offsetX3]*s02; long sum1 = (long)tableDataHi[offsetX]*s1_; sum1 += (long)tableDataHi[offsetX1]*s10; sum1 += (long)tableDataHi[offsetX2]*s11; sum1 += (long)tableDataHi[offsetX3]*s12; long sum2 = (long)tableDataHi[offsetX]*s2_; sum2 += (long)tableDataHi[offsetX1]*s20; sum2 += (long)tableDataHi[offsetX2]*s21; sum2 += (long)tableDataHi[offsetX3]*s22; // Intermediate rounding sum_ = (sum_ + round) >> precisionBits; sum0 = (sum0 + round) >> precisionBits; sum1 = (sum1 + round) >> precisionBits; sum2 = (sum2 + round) >> precisionBits; // Interpolate in Y int offsetY = 4*yfrac; long sum = (long)tableDataVi[offsetY]*sum_; sum += (long)tableDataVi[offsetY + 1]*sum0; sum += (long)tableDataVi[offsetY + 2]*sum1; sum += (long)tableDataVi[offsetY + 3]*sum2; s = (int)((sum + round) >> precisionBits); // clamp the value to byte range if (s > 255) { s = 255; } else if (s < 0) { s = 0; } dstData[dstPixelOffset] = (byte)(s&0xff); dstPixelOffset += dstPixelStride; } dstScanlineOffset += dstScanlineStride; } } } private void shortLoop(RasterAccessor src, Rectangle destRect, RasterAccessor dst, int xpos[], int ypos[], int xfracvalues[], int yfracvalues[]) { int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dwidth = destRect.width; int dheight = destRect.height; int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int bandOffsets[] = src.getBandOffsets(); int dstOffset = 0; int posy, posylow, posyhigh, posyhigh2; int posx, posxlow, posxhigh, posxhigh2; int xfrac, yfrac; int s__, s_0, s_1, s_2; int s0_, s00, s01, s02; int s1_, s10, s11, s12; int s2_, s20, s21, s22; int s; // Putting band loop outside for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int dstScanlineOffset = dstBandOffsets[k]; int bandOffset = bandOffsets[k]; for (int j = 0; j < dheight; j++) { int dstPixelOffset = dstScanlineOffset; yfrac = yfracvalues[j]; posy = ypos[j] + bandOffset; posylow = posy - srcScanlineStride; posyhigh = posy + srcScanlineStride; posyhigh2 = posyhigh + srcScanlineStride; for (int i = 0; i < dwidth; i++) { xfrac = xfracvalues[i]; posx = xpos[i]; posxlow = posx - srcPixelStride; posxhigh = posx + srcPixelStride; posxhigh2 = posxhigh + srcPixelStride; // Get the sixteen surrounding pixel values s__ = srcData[posxlow + posylow]; s_0 = srcData[posx + posylow]; s_1 = srcData[posxhigh + posylow]; s_2 = srcData[posxhigh2 + posylow]; s0_ = srcData[posxlow + posy]; s00 = srcData[posx + posy]; s01 = srcData[posxhigh + posy]; s02 = srcData[posxhigh2 + posy]; s1_ = srcData[posxlow + posyhigh]; s10 = srcData[posx + posyhigh]; s11 = srcData[posxhigh + posyhigh]; s12 = srcData[posxhigh2 + posyhigh]; s2_ = srcData[posxlow + posyhigh2]; s20 = srcData[posx + posyhigh2]; s21 = srcData[posxhigh + posyhigh2]; s22 = srcData[posxhigh2 + posyhigh2]; // Interpolate in X int offsetX = 4*xfrac; int offsetX1 = offsetX + 1; int offsetX2 = offsetX + 2; int offsetX3 = offsetX + 3; long sum_ = (long)tableDataHi[offsetX]*s__; sum_ += (long)tableDataHi[offsetX1]*s_0; sum_ += (long)tableDataHi[offsetX2]*s_1; sum_ += (long)tableDataHi[offsetX3]*s_2; long sum0 = (long)tableDataHi[offsetX]*s0_; sum0 += (long)tableDataHi[offsetX1]*s00; sum0 += (long)tableDataHi[offsetX2]*s01; sum0 += (long)tableDataHi[offsetX3]*s02; long sum1 = (long)tableDataHi[offsetX]*s1_; sum1 += (long)tableDataHi[offsetX1]*s10; sum1 += (long)tableDataHi[offsetX2]*s11; sum1 += (long)tableDataHi[offsetX3]*s12; long sum2 = (long)tableDataHi[offsetX]*s2_; sum2 += (long)tableDataHi[offsetX1]*s20; sum2 += (long)tableDataHi[offsetX2]*s21; sum2 += (long)tableDataHi[offsetX3]*s22; // Intermediate rounding sum_ = (sum_ + round) >> precisionBits; sum0 = (sum0 + round) >> precisionBits; sum1 = (sum1 + round) >> precisionBits; sum2 = (sum2 + round) >> precisionBits; // Interpolate in Y int offsetY = 4*yfrac; long sum = (long)tableDataVi[offsetY]*sum_; sum += (long)tableDataVi[offsetY + 1]*sum0; sum += (long)tableDataVi[offsetY + 2]*sum1; sum += (long)tableDataVi[offsetY + 3]*sum2; s = (int)((sum + round) >> precisionBits); // clamp the value to short range if (s > Short.MAX_VALUE) { s = Short.MAX_VALUE; } else if (s < Short.MIN_VALUE) { s = Short.MIN_VALUE; } dstData[dstPixelOffset] = (short)s; dstPixelOffset += dstPixelStride; } dstScanlineOffset += dstScanlineStride; } } } private void ushortLoop(RasterAccessor src, Rectangle destRect, RasterAccessor dst, int xpos[], int ypos[], int xfracvalues[], int yfracvalues[]) { int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dwidth = destRect.width; int dheight = destRect.height; int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int bandOffsets[] = src.getBandOffsets(); int dstOffset = 0; int posy, posylow, posyhigh, posyhigh2; int posx, posxlow, posxhigh, posxhigh2; int xfrac, yfrac; int s__, s_0, s_1, s_2; int s0_, s00, s01, s02; int s1_, s10, s11, s12; int s2_, s20, s21, s22; int s; // Putting band loop outside for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int dstScanlineOffset = dstBandOffsets[k]; int bandOffset = bandOffsets[k]; for (int j = 0; j < dheight; j++) { int dstPixelOffset = dstScanlineOffset; yfrac = yfracvalues[j]; posy = ypos[j] + bandOffset; posylow = posy - srcScanlineStride; posyhigh = posy + srcScanlineStride; posyhigh2 = posyhigh + srcScanlineStride; for (int i = 0; i < dwidth; i++) { xfrac = xfracvalues[i]; posx = xpos[i]; posxlow = posx - srcPixelStride; posxhigh = posx + srcPixelStride; posxhigh2 = posxhigh + srcPixelStride; // Get the sixteen surrounding pixel values s__ = srcData[posxlow + posylow] & 0xffff; s_0 = srcData[posx + posylow] & 0xffff; s_1 = srcData[posxhigh + posylow] & 0xffff; s_2 = srcData[posxhigh2 + posylow] & 0xffff; s0_ = srcData[posxlow + posy] & 0xffff; s00 = srcData[posx + posy] & 0xffff; s01 = srcData[posxhigh + posy] & 0xffff; s02 = srcData[posxhigh2 + posy] & 0xffff; s1_ = srcData[posxlow + posyhigh] & 0xffff; s10 = srcData[posx + posyhigh] & 0xffff; s11 = srcData[posxhigh + posyhigh] & 0xffff; s12 = srcData[posxhigh2 + posyhigh] & 0xffff; s2_ = srcData[posxlow + posyhigh2] & 0xffff; s20 = srcData[posx + posyhigh2] & 0xffff; s21 = srcData[posxhigh + posyhigh2] & 0xffff; s22 = srcData[posxhigh2 + posyhigh2] & 0xffff; // Interpolate in X int offsetX = 4*xfrac; int offsetX1 = offsetX + 1; int offsetX2 = offsetX + 2; int offsetX3 = offsetX + 3; long sum_ = (long)tableDataHi[offsetX]*s__; sum_ += (long)tableDataHi[offsetX1]*s_0; sum_ += (long)tableDataHi[offsetX2]*s_1; sum_ += (long)tableDataHi[offsetX3]*s_2; long sum0 = (long)tableDataHi[offsetX]*s0_; sum0 += (long)tableDataHi[offsetX1]*s00; sum0 += (long)tableDataHi[offsetX2]*s01; sum0 += (long)tableDataHi[offsetX3]*s02; long sum1 = (long)tableDataHi[offsetX]*s1_; sum1 += (long)tableDataHi[offsetX1]*s10; sum1 += (long)tableDataHi[offsetX2]*s11; sum1 += (long)tableDataHi[offsetX3]*s12; long sum2 = (long)tableDataHi[offsetX]*s2_; sum2 += (long)tableDataHi[offsetX1]*s20; sum2 += (long)tableDataHi[offsetX2]*s21; sum2 += (long)tableDataHi[offsetX3]*s22; // Intermediate rounding sum_ = (sum_ + round) >> precisionBits; sum0 = (sum0 + round) >> precisionBits; sum1 = (sum1 + round) >> precisionBits; sum2 = (sum2 + round) >> precisionBits; // Interpolate in Y int offsetY = 4*yfrac; long sum = (long)tableDataVi[offsetY]*sum_; sum += (long)tableDataVi[offsetY + 1]*sum0; sum += (long)tableDataVi[offsetY + 2]*sum1; sum += (long)tableDataVi[offsetY + 3]*sum2; s = (int)((sum + round) >> precisionBits); // clamp the value to ushort range if (s > 65536) { s = 65536; } else if (s < 0) { s = 0; } dstData[dstPixelOffset] = (short)(s & 0xffff); dstPixelOffset += dstPixelStride; } dstScanlineOffset += dstScanlineStride; } } } // identical to byteLoops, except datatypes have changed. clumsy, // but there's no other way in Java private void intLoop(RasterAccessor src, Rectangle destRect, RasterAccessor dst, int xpos[], int ypos[], int xfracvalues[], int yfracvalues[]) { int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dwidth = destRect.width; int dheight = destRect.height; int dnumBands = dst.getNumBands(); int dstDataArrays[][] = dst.getIntDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcDataArrays[][] = src.getIntDataArrays(); int bandOffsets[] = src.getBandOffsets(); int dstOffset = 0; int posy, posylow, posyhigh, posyhigh2; int posx, posxlow, posxhigh, posxhigh2; long xfrac, yfrac; int s__, s_0, s_1, s_2; int s0_, s00, s01, s02; int s1_, s10, s11, s12; int s2_, s20, s21, s22; int s; // Putting band loop outside for (int k = 0; k < dnumBands; k++) { int dstData[] = dstDataArrays[k]; int srcData[] = srcDataArrays[k]; int dstScanlineOffset = dstBandOffsets[k]; int bandOffset = bandOffsets[k]; for (int j = 0; j < dheight; j++) { int dstPixelOffset = dstScanlineOffset; yfrac = yfracvalues[j]; posy = ypos[j] + bandOffset; posylow = posy - srcScanlineStride; posyhigh = posy + srcScanlineStride; posyhigh2 = posyhigh + srcScanlineStride; for (int i = 0; i < dwidth; i++) { xfrac = xfracvalues[i]; posx = xpos[i]; posxlow = posx - srcPixelStride; posxhigh = posx + srcPixelStride; posxhigh2 = posxhigh + srcPixelStride; // Get the sixteen surrounding pixel values s__ = srcData[posxlow + posylow]; s_0 = srcData[posx + posylow]; s_1 = srcData[posxhigh + posylow]; s_2 = srcData[posxhigh2 + posylow]; s0_ = srcData[posxlow + posy]; s00 = srcData[posx + posy]; s01 = srcData[posxhigh + posy]; s02 = srcData[posxhigh2 + posy]; s1_ = srcData[posxlow + posyhigh]; s10 = srcData[posx + posyhigh]; s11 = srcData[posxhigh + posyhigh]; s12 = srcData[posxhigh2 + posyhigh]; s2_ = srcData[posxlow + posyhigh2]; s20 = srcData[posx + posyhigh2]; s21 = srcData[posxhigh + posyhigh2]; s22 = srcData[posxhigh2 + posyhigh2]; // Interpolate in X int offsetX = (int)(4*xfrac); int offsetX1 = offsetX + 1; int offsetX2 = offsetX + 2; int offsetX3 = offsetX + 3; long sum_ = (long)tableDataHi[offsetX]*s__; sum_ += (long)tableDataHi[offsetX1]*s_0; sum_ += (long)tableDataHi[offsetX2]*s_1; sum_ += (long)tableDataHi[offsetX3]*s_2; long sum0 = (long)tableDataHi[offsetX]*s0_; sum0 += (long)tableDataHi[offsetX1]*s00; sum0 += (long)tableDataHi[offsetX2]*s01; sum0 += (long)tableDataHi[offsetX3]*s02; long sum1 = (long)tableDataHi[offsetX]*s1_; sum1 += (long)tableDataHi[offsetX1]*s10; sum1 += (long)tableDataHi[offsetX2]*s11; sum1 += (long)tableDataHi[offsetX3]*s12; long sum2 = (long)tableDataHi[offsetX]*s2_; sum2 += (long)tableDataHi[offsetX1]*s20; sum2 += (long)tableDataHi[offsetX2]*s21; sum2 += (long)tableDataHi[offsetX3]*s22; // Intermediate rounding sum_ = (sum_ + round) >> precisionBits; sum0 = (sum0 + round) >> precisionBits; sum1 = (sum1 + round) >> precisionBits; sum2 = (sum2 + round) >> precisionBits; // Interpolate in Y int offsetY = (int)(4*yfrac); long sum = (long)tableDataVi[offsetY]*sum_; sum += (long)tableDataVi[offsetY + 1]*sum0; sum += (long)tableDataVi[offsetY + 2]*sum1; sum += (long)tableDataVi[offsetY + 3]*sum2; s = (int)((sum + round) >> precisionBits); dstData[dstPixelOffset] = s; dstPixelOffset += dstPixelStride; } dstScanlineOffset += dstScanlineStride; } } } private void floatLoop(RasterAccessor src, Rectangle destRect, RasterAccessor dst, int xpos[], int ypos[], int xfracvalues[], int yfracvalues[]) { int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dwidth = destRect.width; int dheight = destRect.height; int dnumBands = dst.getNumBands(); float dstDataArrays[][] = dst.getFloatDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); float srcDataArrays[][] = src.getFloatDataArrays(); int bandOffsets[] = src.getBandOffsets(); int dstOffset = 0; int posy, posylow, posyhigh, posyhigh2; int posx, posxlow, posxhigh, posxhigh2; int xfrac, yfrac; float s__, s_0, s_1, s_2; float s0_, s00, s01, s02; float s1_, s10, s11, s12; float s2_, s20, s21, s22; // Putting band loop outside for (int k = 0; k < dnumBands; k++) { float dstData[] = dstDataArrays[k]; float srcData[] = srcDataArrays[k]; int dstScanlineOffset = dstBandOffsets[k]; int bandOffset = bandOffsets[k]; for (int j = 0; j < dheight; j++) { int dstPixelOffset = dstScanlineOffset; yfrac = yfracvalues[j]; posy = ypos[j] + bandOffset; posylow = posy - srcScanlineStride; posyhigh = posy + srcScanlineStride; posyhigh2 = posyhigh + srcScanlineStride; for (int i = 0; i < dwidth; i++) { xfrac = xfracvalues[i]; posx = xpos[i]; posxlow = posx - srcPixelStride; posxhigh = posx + srcPixelStride; posxhigh2 = posxhigh + srcPixelStride; // Get the sixteen surrounding pixel values s__ = srcData[posxlow + posylow]; s_0 = srcData[posx + posylow]; s_1 = srcData[posxhigh + posylow]; s_2 = srcData[posxhigh2 + posylow]; s0_ = srcData[posxlow + posy]; s00 = srcData[posx + posy]; s01 = srcData[posxhigh + posy]; s02 = srcData[posxhigh2 + posy]; s1_ = srcData[posxlow + posyhigh]; s10 = srcData[posx + posyhigh]; s11 = srcData[posxhigh + posyhigh]; s12 = srcData[posxhigh2 + posyhigh]; s2_ = srcData[posxlow + posyhigh2]; s20 = srcData[posx + posyhigh2]; s21 = srcData[posxhigh + posyhigh2]; s22 = srcData[posxhigh2 + posyhigh2]; // Perform the bicubic interpolation // Interpolate in X int offsetX = (int)(4 * xfrac); int offsetX1 = offsetX + 1; int offsetX2 = offsetX + 2; int offsetX3 = offsetX + 3; double sum_ = tableDataHf[offsetX]*s__; sum_ += tableDataHf[offsetX1]*s_0; sum_ += tableDataHf[offsetX2]*s_1; sum_ += tableDataHf[offsetX3]*s_2; double sum0 = tableDataHf[offsetX]*s0_; sum0 += tableDataHf[offsetX1]*s00; sum0 += tableDataHf[offsetX2]*s01; sum0 += tableDataHf[offsetX3]*s02; double sum1 = tableDataHf[offsetX]*s1_; sum1 += tableDataHf[offsetX1]*s10; sum1 += tableDataHf[offsetX2]*s11; sum1 += tableDataHf[offsetX3]*s12; double sum2 = tableDataHf[offsetX]*s2_; sum2 += tableDataHf[offsetX1]*s20; sum2 += tableDataHf[offsetX2]*s21; sum2 += tableDataHf[offsetX3]*s22; // Interpolate in Y int offsetY = (int)(4 * yfrac); double sum = tableDataVf[offsetY]*sum_; sum += tableDataVf[offsetY + 1]*sum0; sum += tableDataVf[offsetY + 2]*sum1; sum += tableDataVf[offsetY + 3]*sum2; if (sum > Float.MAX_VALUE) { sum = Float.MAX_VALUE; } else if (sum < -Float.MAX_VALUE) { sum = -Float.MAX_VALUE; } dstData[dstPixelOffset] = (float)sum; dstPixelOffset += dstPixelStride; } dstScanlineOffset += dstScanlineStride; } } } private void doubleLoop(RasterAccessor src, Rectangle destRect, RasterAccessor dst, int xpos[], int ypos[], int xfracvalues[], int yfracvalues[]) { int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dwidth = destRect.width; int dheight = destRect.height; int dnumBands = dst.getNumBands(); double dstDataArrays[][] = dst.getDoubleDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); double srcDataArrays[][] = src.getDoubleDataArrays(); int bandOffsets[] = src.getBandOffsets(); int dstOffset = 0; int posy, posylow, posyhigh, posyhigh2; int posx, posxlow, posxhigh, posxhigh2; double s__, s_0, s_1, s_2; double s0_, s00, s01, s02; double s1_, s10, s11, s12; double s2_, s20, s21, s22; double s; int xfrac, yfrac; // Putting band loop outside for (int k = 0; k < dnumBands; k++) { double dstData[] = dstDataArrays[k]; double srcData[] = srcDataArrays[k]; int dstScanlineOffset = dstBandOffsets[k]; int bandOffset = bandOffsets[k]; for (int j = 0; j < dheight; j++) { int dstPixelOffset = dstScanlineOffset; yfrac = yfracvalues[j]; posy = ypos[j] + bandOffset; posylow = posy - srcScanlineStride; posyhigh = posy + srcScanlineStride; posyhigh2 = posyhigh + srcScanlineStride; for (int i = 0; i < dwidth; i++) { xfrac = xfracvalues[i]; posx = xpos[i]; posxlow = posx - srcPixelStride; posxhigh = posx + srcPixelStride; posxhigh2 = posxhigh + srcPixelStride; // Get the sixteen surrounding pixel values s__ = srcData[posxlow + posylow]; s_0 = srcData[posx + posylow]; s_1 = srcData[posxhigh + posylow]; s_2 = srcData[posxhigh2 + posylow]; s0_ = srcData[posxlow + posy]; s00 = srcData[posx + posy]; s01 = srcData[posxhigh + posy]; s02 = srcData[posxhigh2 + posy]; s1_ = srcData[posxlow + posyhigh]; s10 = srcData[posx + posyhigh]; s11 = srcData[posxhigh + posyhigh]; s12 = srcData[posxhigh2 + posyhigh]; s2_ = srcData[posxlow + posyhigh2]; s20 = srcData[posx + posyhigh2]; s21 = srcData[posxhigh + posyhigh2]; s22 = srcData[posxhigh2 + posyhigh2]; // Perform the bicubic interpolation // Interpolate in X int offsetX = (int)(4 * xfrac); int offsetX1 = offsetX + 1; int offsetX2 = offsetX + 2; int offsetX3 = offsetX + 3; double sum_ = tableDataHd[offsetX]*s__; sum_ += tableDataHd[offsetX1]*s_0; sum_ += tableDataHd[offsetX2]*s_1; sum_ += tableDataHd[offsetX3]*s_2; double sum0 = tableDataHd[offsetX]*s0_; sum0 += tableDataHd[offsetX1]*s00; sum0 += tableDataHd[offsetX2]*s01; sum0 += tableDataHd[offsetX3]*s02; double sum1 = tableDataHd[offsetX]*s1_; sum1 += tableDataHd[offsetX1]*s10; sum1 += tableDataHd[offsetX2]*s11; sum1 += tableDataHd[offsetX3]*s12; double sum2 = tableDataHd[offsetX]*s2_; sum2 += tableDataHd[offsetX1]*s20; sum2 += tableDataHd[offsetX2]*s21; sum2 += tableDataHd[offsetX3]*s22; // Interpolate in Y int offsetY = (int)(4 * yfrac); s = tableDataVd[offsetY]*sum_; s += tableDataVd[offsetY + 1]*sum0; s += tableDataVd[offsetY + 2]*sum1; s += tableDataVd[offsetY + 3]*sum2; dstData[dstPixelOffset] = s; dstPixelOffset += dstPixelStride; } dstScanlineOffset += dstScanlineStride; } } } private synchronized void initTableDataI() { if (tableDataHi == null || tableDataVi == null) { tableDataHi = interpTable.getHorizontalTableData(); tableDataVi = interpTable.getVerticalTableData(); } } private synchronized void initTableDataF() { if (tableDataHf == null || tableDataVf == null) { tableDataHf = interpTable.getHorizontalTableDataFloat(); tableDataVf = interpTable.getVerticalTableDataFloat(); } } private synchronized void initTableDataD() { if (tableDataHd == null || tableDataVd == null) { tableDataHd = interpTable.getHorizontalTableDataDouble(); tableDataVd = interpTable.getVerticalTableDataDouble(); } } // public static OpImage createTestImage(OpImageTester oit) { // Interpolation interp = // Interpolation.getInstance(Interpolation.INTERP_BICUBIC); // return new ScaleBicubicOpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource()), // 2.5F, 2.5F, 0.0F, 0.0F, // interp); // } // public static void main(String args[]) { // String classname = "com.sun.media.jai.opimage.ScaleBicubicOpImage"; // OpImageTester.performDiagnostics(classname,args); // System.exit(1); // System.out.println("ScaleOpImage Test"); // ImageLayout layout; // OpImage src, dst; // Rectangle rect = new Rectangle(2, 2, 5, 5); // InterpolationBicubic interp = new InterpolationBicubic(8); // System.out.println("1. PixelInterleaved short 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 200, 200, 0, 0, // 64, 64, DataBuffer.TYPE_SHORT, // 3, false); // src = OpImageTester.createRandomOpImage(layout); // dst = new ScaleBicubicOpImage(src, null, null, null, // 2.0F, 2.0F, 0.0F, 0.0F, interp); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("2. PixelInterleaved ushort 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 512, 512, 0, 0, // 200, 200, // DataBuffer.TYPE_USHORT, // 3, false); // src = OpImageTester.createRandomOpImage(layout); // dst = new ScaleBicubicOpImage(src, null, null, null, // 2.0F, 2.0F, 0.0F, 0.0F, interp); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/AddConstOpImage.java0000644000175000017500000003700310203035544027037 0ustar mathieumathieu/* * $RCSfile: AddConstOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:12 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import javax.media.jai.ColormapOpImage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; import com.sun.media.jai.util.ImageUtil; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage implementing the "AddConst" operation. * *

    This OpImage adds a set of constants, one for each * band of the source image, to the pixels of a rendered image. * The destination pixel values are calculated as: *

     *     for (int h = 0; h < dstHeight; h++) {
     *         for (int w = 0; w < dstWidth; w++) {
     *             for (int b = 0; b < dstNumBands; b++) {
     *                 if (constants.length < dstNumBands) {
     *                     dst[h][w][b] = srcs[h][w][b] + constants[0];
     *                 } else {
     *                     dst[h][w][b] = srcs[h][w][b] + constants[b];
     *                 }
     *             }
     *         }
     *     }
     * 
    * * @see javax.media.jai.operator.AddConstDescriptor * @see AddConstCRIF * */ final class AddConstOpImage extends ColormapOpImage { /** The constants to be added, one for each band. */ protected double[] constants; private byte[][] byteTable = null; private synchronized void initByteTable() { if (byteTable != null) { return; } int nbands = constants.length; byteTable = new byte[nbands][256]; // Initialize table which implements AddConst and clamp for(int band=0; band 255) { t[i] = (byte)255; } else { t[i] = (byte)sum; } } } } /** * Constructor. * * @param source The source image. * @param layout The destination image layout. * @param constants The constants to be added, stored as reference. */ public AddConstOpImage(RenderedImage source, Map config, ImageLayout layout, double[] constants) { super(source, layout, config, true); int numBands = getSampleModel().getNumBands(); if (constants.length < numBands) { this.constants = new double[numBands]; for (int i = 0; i < numBands; i++) { this.constants[i] = constants[0]; } } else { this.constants = (double[])constants.clone(); } // Set flag to permit in-place operation. permitInPlaceOperation(); // Initialize the colormap if necessary. initializeColormapOperation(); } /** * Transform the colormap according to the rescaling parameters. */ protected void transformColormap(byte[][] colormap) { initByteTable(); for(int b = 0; b < 3; b++) { byte[] map = colormap[b]; byte[] luTable = byteTable[b >= byteTable.length ? 0 : b]; int mapSize = map.length; for(int i = 0; i < mapSize; i++) { map[i] = luTable[(map[i] & 0xFF)]; } } } /** * Adds a constant to the pixel values within a specified rectangle. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); Rectangle srcRect = mapDestRect(destRect, 0); RasterAccessor dst = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); RasterAccessor src = new RasterAccessor(sources[0], srcRect, formatTags[0], getSourceImage(0).getColorModel()); switch (dst.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(src, dst); break; case DataBuffer.TYPE_USHORT: computeRectUShort(src, dst); break; case DataBuffer.TYPE_SHORT: computeRectShort(src, dst); break; case DataBuffer.TYPE_INT: computeRectInt(src, dst); break; case DataBuffer.TYPE_FLOAT: computeRectFloat(src, dst); break; case DataBuffer.TYPE_DOUBLE: computeRectDouble(src, dst); break; } if (dst.needsClamping()) { /* Further clamp down to underlying raster data type. */ dst.clampDataArrays(); } dst.copyDataToRaster(); } private void computeRectByte(RasterAccessor src, RasterAccessor dst) { initByteTable(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); byte[][] dstData = dst.getByteDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); byte[][] srcData = src.getByteDataArrays(); for (int b = 0; b < dstBands; b++) { byte[] d = dstData[b]; byte[] s = srcData[b]; byte[] clamp = byteTable[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; int dstEnd = dstPixelOffset + dstWidth*dstPixelStride; while(dstPixelOffset < dstEnd) { d[dstPixelOffset] = clamp[s[srcPixelOffset] & 0xFF]; dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectUShort(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); short[][] dstData = dst.getShortDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); short[][] srcData = src.getShortDataArrays(); for (int b = 0; b < dstBands; b++) { int c = ImageUtil.clampRoundInt(constants[b]); short[] d = dstData[b]; short[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = ImageUtil.clampUShort( (s[srcPixelOffset] & 0xFFFF) + c); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectShort(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); short[][] dstData = dst.getShortDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); short[][] srcData = src.getShortDataArrays(); for (int b = 0; b < dstBands; b++) { int c = ImageUtil.clampRoundInt(constants[b]); short[] d = dstData[b]; short[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = ImageUtil.clampShort(s[srcPixelOffset] + c); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectInt(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); int[][] dstData = dst.getIntDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); int[][] srcData = src.getIntDataArrays(); for (int b = 0; b < dstBands; b++) { long c = ImageUtil.clampRoundInt(constants[b]); int[] d = dstData[b]; int[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = ImageUtil.clampInt(s[srcPixelOffset] + c); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectFloat(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); float[][] dstData = dst.getFloatDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); float[][] srcData = src.getFloatDataArrays(); for (int b = 0; b < dstBands; b++) { double c = constants[b]; float[] d = dstData[b]; float[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = ImageUtil.clampFloat(s[srcPixelOffset] + c); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectDouble(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); double[][] dstData = dst.getDoubleDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); double[][] srcData = src.getDoubleDataArrays(); for (int b = 0; b < dstBands; b++) { double c = constants[b]; double[] d = dstData[b]; double[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = s[srcPixelOffset] + c; dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } // public static void main(String args[]) { // System.out.println("AddConstOpImage Test"); // ImageLayout layout; // OpImage src, dst; // Rectangle rect = new Rectangle(0, 0, 5, 5); // double[] constants = new double[3]; // constants[0] = 10.0; // constants[1] = 20.0; // constants[2] = 30.0; // System.out.println("1. PixelInterleaved byte 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 800, 800, 0, 0, 200, 200, DataBuffer.TYPE_BYTE, 3, false); // src = OpImageTester.createRandomOpImage(layout); // dst = new AddConstOpImage(src, null, null, constants); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("3. PixelInterleaved int 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_INT, 3, false); // src = OpImageTester.createRandomOpImage(layout); // dst = new AddConstOpImage(src, null, null, constants); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/UnsharpMaskRIF.java0000644000175000017500000000606410203035544026676 0ustar mathieumathieu/* * $RCSfile: UnsharpMaskRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:46 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import com.sun.media.jai.util.ImageUtil; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; import java.util.Map; /** * @see UnsharpMaskOpImage */ public class UnsharpMaskRIF implements RenderedImageFactory { /** Constructor. */ public UnsharpMaskRIF() {} /** * Create a new instance of UnsharpMaskOpImage in the rendered layer. * This method satisfies the implementation of RIF. * * @param paramBlock The source image, the unsharp mask kernel and * the gain factor. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); // Get BorderExtender from renderHints if any. BorderExtender extender = RIFUtil.getBorderExtenderHint(renderHints); // map the input kernel + gain factor to an equivalent // convolution kernel and then do a normal convolve. KernelJAI unRotatedKernel = ImageUtil.getUnsharpMaskEquivalentKernel( (KernelJAI)paramBlock.getObjectParameter(0), paramBlock.getFloatParameter(1)); KernelJAI kJAI = unRotatedKernel.getRotatedKernel(); RenderedImage source = paramBlock.getRenderedSource(0); int dataType = source.getSampleModel().getDataType(); boolean dataTypeOk = (dataType == DataBuffer.TYPE_BYTE || dataType == DataBuffer.TYPE_SHORT || dataType == DataBuffer.TYPE_INT); if ((kJAI.getWidth() == 3) && (kJAI.getHeight() == 3) && (kJAI.getXOrigin() == 1) && (kJAI.getYOrigin() == 1) && dataTypeOk) { return new Convolve3x3OpImage(source, extender, renderHints, layout, kJAI); } else if (kJAI.isSeparable()) { return new SeparableConvolveOpImage(source, extender, renderHints, layout, kJAI); } else { return new ConvolveOpImage(source, extender, renderHints, layout, kJAI); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ComplexArithmeticOpImage.java0000644000175000017500000011517110203035544030764 0ustar mathieumathieu/* * $RCSfile: ComplexArithmeticOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:18 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.PointOpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.RasterFactory; import java.util.Map; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.util.JDKWorkarounds; /** * An OpImage implementing complex multiplication and division * as described in * javax.media.jai.operator.MultiplyComplexDescriptor and * javax.media.jai.operator.DivideComplexDescriptor. * * @since EA4 * * @see javax.media.jai.PointOpImage * @see javax.media.jai.operator.MultiplyComplexDescriptor * @see javax.media.jai.operator.DivideComplexDescriptor * */ final class ComplexArithmeticOpImage extends PointOpImage { /** Flag indicating division (true) or multiplication (false). */ protected boolean isDivision = false; /* Array of indices into real bands of first source. */ private int[] s1r; /* Array of indices into imaginary bands of first source. */ private int[] s1i; /* Array of indices into real bands of second source. */ private int[] s2r; /* Array of indices into imaginary bands of second source. */ private int[] s2i; /** * Force the destination band count to be even. */ private static ImageLayout layoutHelper(ImageLayout layout, RenderedImage source){ // Create an ImageLayout or clone the one passed in. ImageLayout il; if (layout == null) { il = new ImageLayout(); } else { il = (ImageLayout)layout.clone(); } if(il.isValid(ImageLayout.SAMPLE_MODEL_MASK)) { SampleModel sm = il.getSampleModel(null); int nBands = sm.getNumBands(); if(nBands % 2 != 0) { nBands++; sm = RasterFactory.createComponentSampleModel(sm, sm.getTransferType(), sm.getWidth(), sm.getHeight(), nBands); il.setSampleModel(sm); // newly added // Clear the ColorModel mask if needed. ColorModel cm = layout.getColorModel(null); if(cm != null && !JDKWorkarounds.areCompatibleDataModels(sm, cm)) { // Clear the mask bit if incompatible. il.unsetValid(ImageLayout.COLOR_MODEL_MASK); } } } return il; } /** * Constructs a ComplexArithmeticOpImage object. * *

    The layout parameter may optionally contains the * tile grid layout, sample model, and/or color model. The image * dimension is determined by the intersection of the bounding boxes * of the two source images. * *

    The image layout of the first source image, source1, * is used as the fall-back for the image layout of the destination * image. Any layout parameters not specified in the layout * argument are set to the same value as that of source1. * * @param source1 The first source image. * @param source2 The second source image. * @param layout The destination image layout. * @param isDivision Whether the operation is division; if not, it's * multiplication. */ public ComplexArithmeticOpImage(RenderedImage source1, RenderedImage source2, Map config, ImageLayout layout, boolean isDivision) { super(source1, source2, layoutHelper(layout, source1), config, true); // Cache the division parameter. this.isDivision = isDivision; // Get the source band counts. int numBands1 = source1.getSampleModel().getNumBands(); int numBands2 = source2.getSampleModel().getNumBands(); // Handle the special case (cf. descriptor). int numBandsDst = Math.min(numBands1, numBands2); int numBandsFromHint = 0; if(layout != null) numBandsFromHint = layout.getSampleModel(null).getNumBands(); if(layout != null && layout.isValid(ImageLayout.SAMPLE_MODEL_MASK) && ((numBands1 == 2 && numBands2 > 2) || (numBands2 == 2 && numBands1 > 2) || (numBands1 >= numBandsFromHint && numBands2 >= numBandsFromHint && numBandsFromHint > 0)) ){ if(numBandsFromHint % 2 == 0){ numBandsDst = numBandsFromHint; // Clamp the destination band count to the maximum // number of bands in the sources. numBandsDst = Math.min(Math.max(numBands1, numBands2), numBandsDst); } } if(numBandsDst != sampleModel.getNumBands()){ sampleModel = RasterFactory.createComponentSampleModel( sampleModel, sampleModel.getTransferType(), sampleModel.getWidth(), sampleModel.getHeight(), numBandsDst); if(colorModel != null && !JDKWorkarounds.areCompatibleDataModels(sampleModel, colorModel)) { colorModel = ImageUtil.getCompatibleColorModel(sampleModel, config); } } // Initialize index arrays. int numElements = sampleModel.getNumBands()/2; s1r = new int[numElements]; s1i = new int[numElements]; s2r = new int[numElements]; s2i = new int[numElements]; int s1Inc = numBands1 > 2 ? 2 : 0; int s2Inc = numBands2 > 2 ? 2 : 0; int i1 = 0; int i2 = 0; for(int b = 0; b < numElements; b++) { s1r[b] = i1; s1i[b] = i1 + 1; s2r[b] = i2; s2i[b] = i2 + 1; i1 += s1Inc; i2 += s2Inc; } // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * Calculate the product or quotient of the source images. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); RasterAccessor src1Accessor = new RasterAccessor(sources[0], destRect, formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor src2Accessor = new RasterAccessor(sources[1], destRect, formatTags[1], getSourceImage(1).getColorModel()); RasterAccessor dstAccessor = new RasterAccessor(dest, destRect, formatTags[2], getColorModel()); // Branch to the method appropriate to the accessor data type. switch(dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(src1Accessor, src2Accessor, dstAccessor); break; case DataBuffer.TYPE_SHORT: computeRectShort(src1Accessor, src2Accessor, dstAccessor); break; case DataBuffer.TYPE_USHORT: computeRectUShort(src1Accessor, src2Accessor, dstAccessor); break; case DataBuffer.TYPE_INT: computeRectInt(src1Accessor, src2Accessor, dstAccessor); break; case DataBuffer.TYPE_FLOAT: computeRectFloat(src1Accessor, src2Accessor, dstAccessor); break; case DataBuffer.TYPE_DOUBLE: computeRectDouble(src1Accessor, src2Accessor, dstAccessor); break; default: // NB: This statement should be unreachable. throw new RuntimeException(JaiI18N.getString("ComplexArithmeticOpImage0")); } if (dstAccessor.needsClamping()) { dstAccessor.clampDataArrays(); } // Make sure that the output data is copied to the destination. dstAccessor.copyDataToRaster(); } private void computeRectDouble(RasterAccessor src1Accessor, RasterAccessor src2Accessor, RasterAccessor dstAccessor) { // Set the size of the rectangle. int numRows = dstAccessor.getHeight(); int numCols = dstAccessor.getWidth(); // Set pixel and line strides. int src1PixelStride = src1Accessor.getPixelStride(); int src1ScanlineStride = src1Accessor.getScanlineStride(); int src2PixelStride = src2Accessor.getPixelStride(); int src2ScanlineStride = src2Accessor.getScanlineStride(); int dstPixelStride = dstAccessor.getPixelStride(); int dstScanlineStride = dstAccessor.getScanlineStride(); // Loop over the destination bands. int numElements = sampleModel.getNumBands()/2; for(int element = 0; element < numElements; element++) { // Set band indices. int realBand = 2*element; int imagBand = realBand + 1; // Get the source and destination arrays for this element. double[] src1Real = src1Accessor.getDoubleDataArray(s1r[element]); double[] src1Imag = src1Accessor.getDoubleDataArray(s1i[element]); double[] src2Real = src2Accessor.getDoubleDataArray(s2r[element]); double[] src2Imag = src2Accessor.getDoubleDataArray(s2i[element]); double[] dstReal = dstAccessor.getDoubleDataArray(realBand); double[] dstImag = dstAccessor.getDoubleDataArray(imagBand); // Initialize the data offsets for this element. int src1OffsetReal = src1Accessor.getBandOffset(s1r[element]); int src1OffsetImag = src1Accessor.getBandOffset(s1i[element]); int src2OffsetReal = src2Accessor.getBandOffset(s2r[element]); int src2OffsetImag = src2Accessor.getBandOffset(s2i[element]); int dstOffsetReal = dstAccessor.getBandOffset(realBand); int dstOffsetImag = dstAccessor.getBandOffset(imagBand); // Initialize the line offsets for looping. int src1LineReal = src1OffsetReal; int src1LineImag = src1OffsetImag; int src2LineReal = src2OffsetReal; int src2LineImag = src2OffsetImag; int dstLineReal = dstOffsetReal; int dstLineImag = dstOffsetImag; for(int row = 0; row < numRows; row++) { // Initialize pixel offsets for this row. int src1PixelReal = src1LineReal; int src1PixelImag = src1LineImag; int src2PixelReal = src2LineReal; int src2PixelImag = src2LineImag; int dstPixelReal = dstLineReal; int dstPixelImag = dstLineImag; // Switch per line depending on operation type. if(isDivision) { // Division for(int col = 0; col < numCols; col++) { double a = src1Real[src1PixelReal]; double b = src1Imag[src1PixelImag]; double c = src2Real[src2PixelReal]; double d = src2Imag[src2PixelImag]; double denom = c*c + d*d; dstReal[dstPixelReal] = (a*c + b*d)/denom; dstImag[dstPixelImag] = (b*c - a*d)/denom; src1PixelReal += src1PixelStride; src1PixelImag += src1PixelStride; src2PixelReal += src2PixelStride; src2PixelImag += src2PixelStride; dstPixelReal += dstPixelStride; dstPixelImag += dstPixelStride; } } else { // Multiplication for(int col = 0; col < numCols; col++) { double a = src1Real[src1PixelReal]; double b = src1Imag[src1PixelImag]; double c = src2Real[src2PixelReal]; double d = src2Imag[src2PixelImag]; dstReal[dstPixelReal] = a*c - b*d; dstImag[dstPixelImag] = a*d + b*c; src1PixelReal += src1PixelStride; src1PixelImag += src1PixelStride; src2PixelReal += src2PixelStride; src2PixelImag += src2PixelStride; dstPixelReal += dstPixelStride; dstPixelImag += dstPixelStride; } } // Increment the line offsets. src1LineReal += src1ScanlineStride; src1LineImag += src1ScanlineStride; src2LineReal += src2ScanlineStride; src2LineImag += src2ScanlineStride; dstLineReal += dstScanlineStride; dstLineImag += dstScanlineStride; } } } private void computeRectFloat(RasterAccessor src1Accessor, RasterAccessor src2Accessor, RasterAccessor dstAccessor) { // Set the size of the rectangle. int numRows = dstAccessor.getHeight(); int numCols = dstAccessor.getWidth(); // Set pixel and line strides. int src1PixelStride = src1Accessor.getPixelStride(); int src1ScanlineStride = src1Accessor.getScanlineStride(); int src2PixelStride = src2Accessor.getPixelStride(); int src2ScanlineStride = src2Accessor.getScanlineStride(); int dstPixelStride = dstAccessor.getPixelStride(); int dstScanlineStride = dstAccessor.getScanlineStride(); // Loop over the destination bands. int numElements = sampleModel.getNumBands()/2; for(int element = 0; element < numElements; element++) { // Set band indices. int realBand = 2*element; int imagBand = realBand + 1; // Get the source and destination arrays for this element. float[] src1Real = src1Accessor.getFloatDataArray(s1r[element]); float[] src1Imag = src1Accessor.getFloatDataArray(s1i[element]); float[] src2Real = src2Accessor.getFloatDataArray(s2r[element]); float[] src2Imag = src2Accessor.getFloatDataArray(s2i[element]); float[] dstReal = dstAccessor.getFloatDataArray(realBand); float[] dstImag = dstAccessor.getFloatDataArray(imagBand); // Initialize the data offsets for this element. int src1OffsetReal = src1Accessor.getBandOffset(s1r[element]); int src1OffsetImag = src1Accessor.getBandOffset(s1i[element]); int src2OffsetReal = src2Accessor.getBandOffset(s2r[element]); int src2OffsetImag = src2Accessor.getBandOffset(s2i[element]); int dstOffsetReal = dstAccessor.getBandOffset(realBand); int dstOffsetImag = dstAccessor.getBandOffset(imagBand); // Initialize the line offsets for looping. int src1LineReal = src1OffsetReal; int src1LineImag = src1OffsetImag; int src2LineReal = src2OffsetReal; int src2LineImag = src2OffsetImag; int dstLineReal = dstOffsetReal; int dstLineImag = dstOffsetImag; for(int row = 0; row < numRows; row++) { // Initialize pixel offsets for this row. int src1PixelReal = src1LineReal; int src1PixelImag = src1LineImag; int src2PixelReal = src2LineReal; int src2PixelImag = src2LineImag; int dstPixelReal = dstLineReal; int dstPixelImag = dstLineImag; // Switch per line depending on operation type. if(isDivision) { // Division for(int col = 0; col < numCols; col++) { float a = src1Real[src1PixelReal]; float b = src1Imag[src1PixelImag]; float c = src2Real[src2PixelReal]; float d = src2Imag[src2PixelImag]; float denom = c*c + d*d; dstReal[dstPixelReal] = (a*c + b*d)/denom; dstImag[dstPixelImag] = (b*c - a*d)/denom; src1PixelReal += src1PixelStride; src1PixelImag += src1PixelStride; src2PixelReal += src2PixelStride; src2PixelImag += src2PixelStride; dstPixelReal += dstPixelStride; dstPixelImag += dstPixelStride; } } else { // Multiplication for(int col = 0; col < numCols; col++) { float a = src1Real[src1PixelReal]; float b = src1Imag[src1PixelImag]; float c = src2Real[src2PixelReal]; float d = src2Imag[src2PixelImag]; dstReal[dstPixelReal] = a*c - b*d; dstImag[dstPixelImag] = a*d + b*c; src1PixelReal += src1PixelStride; src1PixelImag += src1PixelStride; src2PixelReal += src2PixelStride; src2PixelImag += src2PixelStride; dstPixelReal += dstPixelStride; dstPixelImag += dstPixelStride; } } // Increment the line offsets. src1LineReal += src1ScanlineStride; src1LineImag += src1ScanlineStride; src2LineReal += src2ScanlineStride; src2LineImag += src2ScanlineStride; dstLineReal += dstScanlineStride; dstLineImag += dstScanlineStride; } } } private void computeRectInt(RasterAccessor src1Accessor, RasterAccessor src2Accessor, RasterAccessor dstAccessor) { // Set the size of the rectangle. int numRows = dstAccessor.getHeight(); int numCols = dstAccessor.getWidth(); // Set pixel and line strides. int src1PixelStride = src1Accessor.getPixelStride(); int src1ScanlineStride = src1Accessor.getScanlineStride(); int src2PixelStride = src2Accessor.getPixelStride(); int src2ScanlineStride = src2Accessor.getScanlineStride(); int dstPixelStride = dstAccessor.getPixelStride(); int dstScanlineStride = dstAccessor.getScanlineStride(); // Loop over the destination bands. int numElements = sampleModel.getNumBands()/2; for(int element = 0; element < numElements; element++) { // Set band indices. int realBand = 2*element; int imagBand = realBand + 1; // Get the source and destination arrays for this element. int[] src1Real = src1Accessor.getIntDataArray(s1r[element]); int[] src1Imag = src1Accessor.getIntDataArray(s1i[element]); int[] src2Real = src2Accessor.getIntDataArray(s2r[element]); int[] src2Imag = src2Accessor.getIntDataArray(s2i[element]); int[] dstReal = dstAccessor.getIntDataArray(realBand); int[] dstImag = dstAccessor.getIntDataArray(imagBand); // Initialize the data offsets for this element. int src1OffsetReal = src1Accessor.getBandOffset(s1r[element]); int src1OffsetImag = src1Accessor.getBandOffset(s1i[element]); int src2OffsetReal = src2Accessor.getBandOffset(s2r[element]); int src2OffsetImag = src2Accessor.getBandOffset(s2i[element]); int dstOffsetReal = dstAccessor.getBandOffset(realBand); int dstOffsetImag = dstAccessor.getBandOffset(imagBand); // Initialize the line offsets for looping. int src1LineReal = src1OffsetReal; int src1LineImag = src1OffsetImag; int src2LineReal = src2OffsetReal; int src2LineImag = src2OffsetImag; int dstLineReal = dstOffsetReal; int dstLineImag = dstOffsetImag; for(int row = 0; row < numRows; row++) { // Initialize pixel offsets for this row. int src1PixelReal = src1LineReal; int src1PixelImag = src1LineImag; int src2PixelReal = src2LineReal; int src2PixelImag = src2LineImag; int dstPixelReal = dstLineReal; int dstPixelImag = dstLineImag; // Switch per line depending on operation type. if(isDivision) { // Division for(int col = 0; col < numCols; col++) { int a = src1Real[src1PixelReal]; int b = src1Imag[src1PixelImag]; int c = src2Real[src2PixelReal]; int d = src2Imag[src2PixelImag]; float denom = c*c + d*d; dstReal[dstPixelReal] = ImageUtil.clampRoundInt((a*c + b*d)/denom); dstImag[dstPixelImag] = ImageUtil.clampRoundInt((b*c - a*d)/denom); src1PixelReal += src1PixelStride; src1PixelImag += src1PixelStride; src2PixelReal += src2PixelStride; src2PixelImag += src2PixelStride; dstPixelReal += dstPixelStride; dstPixelImag += dstPixelStride; } } else { // Multiplication for(int col = 0; col < numCols; col++) { long a = src1Real[src1PixelReal]; long b = src1Imag[src1PixelImag]; long c = src2Real[src2PixelReal]; long d = src2Imag[src2PixelImag]; dstReal[dstPixelReal] = ImageUtil.clampInt(a*c - b*d); dstImag[dstPixelImag] = ImageUtil.clampInt(a*d + b*c); src1PixelReal += src1PixelStride; src1PixelImag += src1PixelStride; src2PixelReal += src2PixelStride; src2PixelImag += src2PixelStride; dstPixelReal += dstPixelStride; dstPixelImag += dstPixelStride; } } // Increment the line offsets. src1LineReal += src1ScanlineStride; src1LineImag += src1ScanlineStride; src2LineReal += src2ScanlineStride; src2LineImag += src2ScanlineStride; dstLineReal += dstScanlineStride; dstLineImag += dstScanlineStride; } } } private void computeRectUShort(RasterAccessor src1Accessor, RasterAccessor src2Accessor, RasterAccessor dstAccessor) { // Set the size of the rectangle. int numRows = dstAccessor.getHeight(); int numCols = dstAccessor.getWidth(); // Set pixel and line strides. int src1PixelStride = src1Accessor.getPixelStride(); int src1ScanlineStride = src1Accessor.getScanlineStride(); int src2PixelStride = src2Accessor.getPixelStride(); int src2ScanlineStride = src2Accessor.getScanlineStride(); int dstPixelStride = dstAccessor.getPixelStride(); int dstScanlineStride = dstAccessor.getScanlineStride(); // Loop over the destination bands. int numElements = sampleModel.getNumBands()/2; for(int element = 0; element < numElements; element++) { // Set band indices. int realBand = 2*element; int imagBand = realBand + 1; // Get the source and destination arrays for this element. short[] src1Real = src1Accessor.getShortDataArray(s1r[element]); short[] src1Imag = src1Accessor.getShortDataArray(s1i[element]); short[] src2Real = src2Accessor.getShortDataArray(s2r[element]); short[] src2Imag = src2Accessor.getShortDataArray(s2i[element]); short[] dstReal = dstAccessor.getShortDataArray(realBand); short[] dstImag = dstAccessor.getShortDataArray(imagBand); // Initialize the data offsets for this element. int src1OffsetReal = src1Accessor.getBandOffset(s1r[element]); int src1OffsetImag = src1Accessor.getBandOffset(s1i[element]); int src2OffsetReal = src2Accessor.getBandOffset(s2r[element]); int src2OffsetImag = src2Accessor.getBandOffset(s2i[element]); int dstOffsetReal = dstAccessor.getBandOffset(realBand); int dstOffsetImag = dstAccessor.getBandOffset(imagBand); // Initialize the line offsets for looping. int src1LineReal = src1OffsetReal; int src1LineImag = src1OffsetImag; int src2LineReal = src2OffsetReal; int src2LineImag = src2OffsetImag; int dstLineReal = dstOffsetReal; int dstLineImag = dstOffsetImag; for(int row = 0; row < numRows; row++) { // Initialize pixel offsets for this row. int src1PixelReal = src1LineReal; int src1PixelImag = src1LineImag; int src2PixelReal = src2LineReal; int src2PixelImag = src2LineImag; int dstPixelReal = dstLineReal; int dstPixelImag = dstLineImag; // Switch per line depending on operation type. if(isDivision) { // Division for(int col = 0; col < numCols; col++) { int a = src1Real[src1PixelReal]&0xffff; int b = src1Imag[src1PixelImag]&0xffff; int c = src2Real[src2PixelReal]&0xffff; int d = src2Imag[src2PixelImag]&0xffff; int denom = c*c + d*d; dstReal[dstPixelReal] = ImageUtil.clampUShort((a*c + b*d)/denom); dstImag[dstPixelImag] = ImageUtil.clampUShort((b*c - a*d)/denom); src1PixelReal += src1PixelStride; src1PixelImag += src1PixelStride; src2PixelReal += src2PixelStride; src2PixelImag += src2PixelStride; dstPixelReal += dstPixelStride; dstPixelImag += dstPixelStride; } } else { // Multiplication for(int col = 0; col < numCols; col++) { int a = src1Real[src1PixelReal]&0xffff; int b = src1Imag[src1PixelImag]&0xffff; int c = src2Real[src2PixelReal]&0xffff; int d = src2Imag[src2PixelImag]&0xffff; dstReal[dstPixelReal] = ImageUtil.clampUShort(a*c - b*d); dstImag[dstPixelImag] = ImageUtil.clampUShort(a*d + b*c); src1PixelReal += src1PixelStride; src1PixelImag += src1PixelStride; src2PixelReal += src2PixelStride; src2PixelImag += src2PixelStride; dstPixelReal += dstPixelStride; dstPixelImag += dstPixelStride; } } // Increment the line offsets. src1LineReal += src1ScanlineStride; src1LineImag += src1ScanlineStride; src2LineReal += src2ScanlineStride; src2LineImag += src2ScanlineStride; dstLineReal += dstScanlineStride; dstLineImag += dstScanlineStride; } } } private void computeRectShort(RasterAccessor src1Accessor, RasterAccessor src2Accessor, RasterAccessor dstAccessor) { // Set the size of the rectangle. int numRows = dstAccessor.getHeight(); int numCols = dstAccessor.getWidth(); // Set pixel and line strides. int src1PixelStride = src1Accessor.getPixelStride(); int src1ScanlineStride = src1Accessor.getScanlineStride(); int src2PixelStride = src2Accessor.getPixelStride(); int src2ScanlineStride = src2Accessor.getScanlineStride(); int dstPixelStride = dstAccessor.getPixelStride(); int dstScanlineStride = dstAccessor.getScanlineStride(); // Loop over the destination bands. int numElements = sampleModel.getNumBands()/2; for(int element = 0; element < numElements; element++) { // Set band indices. int realBand = 2*element; int imagBand = realBand + 1; // Get the source and destination arrays for this element. short[] src1Real = src1Accessor.getShortDataArray(s1r[element]); short[] src1Imag = src1Accessor.getShortDataArray(s1i[element]); short[] src2Real = src2Accessor.getShortDataArray(s2r[element]); short[] src2Imag = src2Accessor.getShortDataArray(s2i[element]); short[] dstReal = dstAccessor.getShortDataArray(realBand); short[] dstImag = dstAccessor.getShortDataArray(imagBand); // Initialize the data offsets for this element. int src1OffsetReal = src1Accessor.getBandOffset(s1r[element]); int src1OffsetImag = src1Accessor.getBandOffset(s1i[element]); int src2OffsetReal = src2Accessor.getBandOffset(s2r[element]); int src2OffsetImag = src2Accessor.getBandOffset(s2i[element]); int dstOffsetReal = dstAccessor.getBandOffset(realBand); int dstOffsetImag = dstAccessor.getBandOffset(imagBand); // Initialize the line offsets for looping. int src1LineReal = src1OffsetReal; int src1LineImag = src1OffsetImag; int src2LineReal = src2OffsetReal; int src2LineImag = src2OffsetImag; int dstLineReal = dstOffsetReal; int dstLineImag = dstOffsetImag; for(int row = 0; row < numRows; row++) { // Initialize pixel offsets for this row. int src1PixelReal = src1LineReal; int src1PixelImag = src1LineImag; int src2PixelReal = src2LineReal; int src2PixelImag = src2LineImag; int dstPixelReal = dstLineReal; int dstPixelImag = dstLineImag; // Switch per line depending on operation type. if(isDivision) { // Division for(int col = 0; col < numCols; col++) { int a = src1Real[src1PixelReal]; int b = src1Imag[src1PixelImag]; int c = src2Real[src2PixelReal]; int d = src2Imag[src2PixelImag]; int denom = c*c + d*d; dstReal[dstPixelReal] = ImageUtil.clampShort((a*c + b*d)/denom); dstImag[dstPixelImag] = ImageUtil.clampShort((b*c - a*d)/denom); src1PixelReal += src1PixelStride; src1PixelImag += src1PixelStride; src2PixelReal += src2PixelStride; src2PixelImag += src2PixelStride; dstPixelReal += dstPixelStride; dstPixelImag += dstPixelStride; } } else { // Multiplication for(int col = 0; col < numCols; col++) { int a = src1Real[src1PixelReal]; int b = src1Imag[src1PixelImag]; int c = src2Real[src2PixelReal]; int d = src2Imag[src2PixelImag]; dstReal[dstPixelReal] = ImageUtil.clampShort(a*c - b*d); dstImag[dstPixelImag] = ImageUtil.clampShort(a*d + b*c); src1PixelReal += src1PixelStride; src1PixelImag += src1PixelStride; src2PixelReal += src2PixelStride; src2PixelImag += src2PixelStride; dstPixelReal += dstPixelStride; dstPixelImag += dstPixelStride; } } // Increment the line offsets. src1LineReal += src1ScanlineStride; src1LineImag += src1ScanlineStride; src2LineReal += src2ScanlineStride; src2LineImag += src2ScanlineStride; dstLineReal += dstScanlineStride; dstLineImag += dstScanlineStride; } } } private void computeRectByte(RasterAccessor src1Accessor, RasterAccessor src2Accessor, RasterAccessor dstAccessor) { // Set the size of the rectangle. int numRows = dstAccessor.getHeight(); int numCols = dstAccessor.getWidth(); // Set pixel and line strides. int src1PixelStride = src1Accessor.getPixelStride(); int src1ScanlineStride = src1Accessor.getScanlineStride(); int src2PixelStride = src2Accessor.getPixelStride(); int src2ScanlineStride = src2Accessor.getScanlineStride(); int dstPixelStride = dstAccessor.getPixelStride(); int dstScanlineStride = dstAccessor.getScanlineStride(); // Loop over the destination bands. int numElements = sampleModel.getNumBands()/2; for(int element = 0; element < numElements; element++) { // Set band indices. int realBand = 2*element; int imagBand = realBand + 1; // Get the source and destination arrays for this element. byte[] src1Real = src1Accessor.getByteDataArray(s1r[element]); byte[] src1Imag = src1Accessor.getByteDataArray(s1i[element]); byte[] src2Real = src2Accessor.getByteDataArray(s2r[element]); byte[] src2Imag = src2Accessor.getByteDataArray(s2i[element]); byte[] dstReal = dstAccessor.getByteDataArray(realBand); byte[] dstImag = dstAccessor.getByteDataArray(imagBand); // Initialize the data offsets for this element. int src1OffsetReal = src1Accessor.getBandOffset(s1r[element]); int src1OffsetImag = src1Accessor.getBandOffset(s1i[element]); int src2OffsetReal = src2Accessor.getBandOffset(s2r[element]); int src2OffsetImag = src2Accessor.getBandOffset(s2i[element]); int dstOffsetReal = dstAccessor.getBandOffset(realBand); int dstOffsetImag = dstAccessor.getBandOffset(imagBand); // Initialize the line offsets for looping. int src1LineReal = src1OffsetReal; int src1LineImag = src1OffsetImag; int src2LineReal = src2OffsetReal; int src2LineImag = src2OffsetImag; int dstLineReal = dstOffsetReal; int dstLineImag = dstOffsetImag; for(int row = 0; row < numRows; row++) { // Initialize pixel offsets for this row. int src1PixelReal = src1LineReal; int src1PixelImag = src1LineImag; int src2PixelReal = src2LineReal; int src2PixelImag = src2LineImag; int dstPixelReal = dstLineReal; int dstPixelImag = dstLineImag; // Switch per line depending on operation type. if(isDivision) { // Division for(int col = 0; col < numCols; col++) { int a = src1Real[src1PixelReal]&0xff; int b = src1Imag[src1PixelImag]&0xff; int c = src2Real[src2PixelReal]&0xff; int d = src2Imag[src2PixelImag]&0xff; int denom = c*c + d*d; dstReal[dstPixelReal] = ImageUtil.clampByte((a*c + b*d)/denom); dstImag[dstPixelImag] = ImageUtil.clampByte((b*c - a*d)/denom); src1PixelReal += src1PixelStride; src1PixelImag += src1PixelStride; src2PixelReal += src2PixelStride; src2PixelImag += src2PixelStride; dstPixelReal += dstPixelStride; dstPixelImag += dstPixelStride; } } else { // Multiplication for(int col = 0; col < numCols; col++) { int a = src1Real[src1PixelReal]&0xff; int b = src1Imag[src1PixelImag]&0xff; int c = src2Real[src2PixelReal]&0xff; int d = src2Imag[src2PixelImag]&0xff; dstReal[dstPixelReal] = ImageUtil.clampByte(a*c - b*d); dstImag[dstPixelImag] = ImageUtil.clampByte(a*d + b*c); src1PixelReal += src1PixelStride; src1PixelImag += src1PixelStride; src2PixelReal += src2PixelStride; src2PixelImag += src2PixelStride; dstPixelReal += dstPixelStride; dstPixelImag += dstPixelStride; } } // Increment the line offsets. src1LineReal += src1ScanlineStride; src1LineImag += src1ScanlineStride; src2LineReal += src2ScanlineStride; src2LineImag += src2ScanlineStride; dstLineReal += dstScanlineStride; dstLineImag += dstScanlineStride; } } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/JPEGRIF.java0000644000175000017500000000243110203035544025161 0ustar mathieumathieu/* * $RCSfile: JPEGRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:30 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.io.InputStream; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.NullOpImage; import javax.media.jai.OpImage; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.ImageDecoder; import com.sun.media.jai.codec.SeekableStream; /** * @see javax.media.jai.operator.JPEGDescriptor * */ public class JPEGRIF implements RenderedImageFactory { /** Constructor. */ public JPEGRIF() {} /** * Creates a RenderedImage representing the contents * of a JPEG-encoded image. * * @param paramBlock A ParameterBlock containing the JPEG * SeekableStream to read. * @param renderHints Ignored. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { return CodecRIFUtil.create("jpeg", paramBlock, renderHints); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MinFilterPlusOpImage.java0000644000175000017500000004110310203035544030071 0ustar mathieumathieu/* * $RCSfile: MinFilterPlusOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:35 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import java.util.Map; import javax.media.jai.operator.MinFilterDescriptor; import com.sun.media.jai.opimage.MinFilterOpImage; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class to perform min filtering on a source image. * */ final class MinFilterPlusOpImage extends MinFilterOpImage { /** * Creates a MinFilterPlusOpImage with the given source and * maskSize. The image dimensions are derived from the source * image. The tile grid layout, SampleModel, and ColorModel may * optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param maskSize the mask size. */ public MinFilterPlusOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, int maskSize) { super(source, extender, config, layout, MinFilterDescriptor.MIN_MASK_PLUS, maskSize); } protected void byteLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); byte dstDataArrays[][] = dst.getByteDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); byte srcDataArrays[][] = src.getByteDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int minval, val; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { byte dstData[] = dstDataArrays[k]; byte srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { // figure out where the top of the plus starts int imageOffset = srcPixelOffset + srcPixelStride*offset; minval = Integer.MAX_VALUE; for (int u = 0; u < wp; u++) { val = (int)(srcData[imageOffset]&0xff); imageOffset += srcScanlineStride; minval = (val < minval) ? val : minval; } // figure out where the left side of plus starts imageOffset = srcPixelOffset + srcScanlineStride*offset; for (int v = 0; v < wp; v++) { val = (int)(srcData[imageOffset]&0xff); imageOffset += srcPixelStride; minval = (val < minval) ? val : minval; } dstData[dstPixelOffset] = (byte)minval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void shortLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int minval, val; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { // figure out where the top of the plus starts int imageOffset = srcPixelOffset + srcPixelStride*offset; minval = Integer.MAX_VALUE; for (int u = 0; u < wp; u++) { val = (int)(srcData[imageOffset]); imageOffset += srcScanlineStride; minval = (val < minval) ? val : minval; } // figure out where the left side of plus starts imageOffset = srcPixelOffset + srcScanlineStride*offset; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += srcPixelStride; minval = (val < minval) ? val : minval; } dstData[dstPixelOffset] = (short)minval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void ushortLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int minval, val; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { // figure out where the top of the plus starts int imageOffset = srcPixelOffset + srcPixelStride*offset; minval = Integer.MAX_VALUE; for (int u = 0; u < wp; u++) { val = (int)(srcData[imageOffset]&0xffff); imageOffset += srcScanlineStride; minval = (val < minval) ? val : minval; } // figure out where the left side of plus starts imageOffset = srcPixelOffset + srcScanlineStride*offset; for (int v = 0; v < wp; v++) { val = (int)(srcData[imageOffset]&0xffff); imageOffset += srcPixelStride; minval = (val < minval) ? val : minval; } dstData[dstPixelOffset] = (short)minval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void intLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); int dstDataArrays[][] = dst.getIntDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcDataArrays[][] = src.getIntDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int minval, val; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { int dstData[] = dstDataArrays[k]; int srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { // figure out where the top of the plus starts int imageOffset = srcPixelOffset + srcPixelStride*offset; minval = Integer.MAX_VALUE; for (int u = 0; u < wp; u++) { val = srcData[imageOffset]; imageOffset += srcScanlineStride; minval = (val < minval) ? val : minval; } // figure out where the left side of plus starts imageOffset = srcPixelOffset + srcScanlineStride*offset; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += srcPixelStride; minval = (val < minval) ? val : minval; } dstData[dstPixelOffset] = minval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void floatLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); float dstDataArrays[][] = dst.getFloatDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); float srcDataArrays[][] = src.getFloatDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); float minval, val; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { float dstData[] = dstDataArrays[k]; float srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { // figure out where the top of the plus starts int imageOffset = srcPixelOffset + srcPixelStride*offset; minval = Float.MAX_VALUE; for (int u = 0; u < wp; u++) { val = srcData[imageOffset]; imageOffset += srcScanlineStride; minval = (val < minval) ? val : minval; } // figure out where the left side of plus starts imageOffset = srcPixelOffset + srcScanlineStride*offset; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += srcPixelStride; minval = (val < minval) ? val : minval; } dstData[dstPixelOffset] = minval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void doubleLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); double dstDataArrays[][] = dst.getDoubleDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); double srcDataArrays[][] = src.getDoubleDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); double minval, val; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { double dstData[] = dstDataArrays[k]; double srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { // figure out where the top of the plus starts int imageOffset = srcPixelOffset + srcPixelStride*offset; minval = Double.MAX_VALUE; for (int u = 0; u < wp; u++) { val = srcData[imageOffset]; imageOffset += srcScanlineStride; minval = (val < minval) ? val : minval; } // figure out where the left side of plus starts imageOffset = srcPixelOffset + srcScanlineStride*offset; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += srcPixelStride; minval = (val < minval) ? val : minval; } dstData[dstPixelOffset] = minval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } // public static OpImage createTestImage(OpImageTester oit) { // return new MinFilterPlusOpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource()), // 3); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/IIPCRIF.java0000644000175000017500000014606310203035544025172 0ustar mathieumathieu/* * $RCSfile: IIPCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:28 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Image; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.color.ColorSpace; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.ColorModel; import java.awt.image.ComponentColorModel; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import java.awt.image.renderable.RenderableImageOp; import java.awt.image.renderable.RenderContext; import java.io.InputStream; import java.net.URL; import java.util.Vector; import javax.media.jai.CRIFImpl; import javax.media.jai.EnumeratedParameter; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.JAI; import javax.media.jai.LookupTableJAI; import javax.media.jai.MultiResolutionRenderableImage; import javax.media.jai.PlanarImage; import javax.media.jai.ROI; import javax.media.jai.ROIShape; import javax.media.jai.TiledImage; import javax.media.jai.operator.TransposeDescriptor; import javax.media.jai.util.ImagingException; import javax.media.jai.util.ImagingListener; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.ImageDecoder; import com.sun.media.jai.codec.MemoryCacheSeekableStream; import com.sun.media.jai.util.ImageUtil; /** * This CRIF implements the "iip" operation in the rendered and renderable * image layers. * *

    In renderable mode this operation is designed to execute on the * server as many composed operations (those specified via parameters) as * the server's capability permits. In the 1.0 implementation all operations * are actually carried out on the client: server-side processing will be * added in a subsequent release. * *

    Rendered operation returns the default rendering of renderable mode * operation. * *

    The actual set of composed operations is described in section 2.2.1.1 * "Composed Image Commands" ot the "Internet Imaging Protocol Specification" * version 1.0.5. The sequence in which these commands are to be applied is * also described in this section. * *

    More detailed information actually required to understand and implement * the composed operations is available in the "FlashPix Format Specification" * version 1.0.1. Of particular interest are Section 2 "Image Data * Representation", Section 5.3.3 "Relationship of NIF RGB to sRGB", * Section 5.4 "Relating PhotoYCC to NIG RGB", Section 7.2 "Viewing Transform * Parameters", and Section 7.3 "Sequence of Viewing Parameter * Transformations". Also of note are Tables 3.6 and 3.7. * * @since 1.0 * * @see IIPDescriptor * @see IIPResolutionRIF * @see IIPResolutionOpImage * @see Digital Imaging Group * */ public class IIPCRIF extends CRIFImpl { // Bitmask constants indicating supplied parameters. private static final int MASK_FILTER = 0x1; private static final int MASK_COLOR_TWIST = 0x2; private static final int MASK_CONTRAST = 0x4; private static final int MASK_ROI_SOURCE = 0x8; private static final int MASK_TRANSFORM = 0x10; private static final int MASK_ASPECT_RATIO = 0x20; private static final int MASK_ROI_DESTINATION = 0x40; private static final int MASK_ROTATION = 0x80; private static final int MASK_MIRROR_AXIS = 0x100; private static final int MASK_ICC_PROFILE = 0x200; private static final int MASK_JPEG_QUALITY = 0x400; private static final int MASK_JPEG_TABLE = 0x800; // Constants indicating server vendors. private static final int VENDOR_HP = 0; private static final int VENDOR_LIVE_PICTURE = 1; private static final int VENDOR_KODAK = 2; private static final int VENDOR_UNREGISTERED = 255; private static final int VENDOR_EXPERIMENTAL = 999; // Bitmask constants indicating server capabilities private static final int SERVER_CVT_JPEG = 0x1; private static final int SERVER_CVT_FPX = 0x2; private static final int SERVER_CVT_MJPEG = 0x4; private static final int SERVER_CVT_MFPX = 0x8; private static final int SERVER_CVT_M2JPEG = 0x10; private static final int SERVER_CVT_M2FPX = 0x20; private static final int SERVER_CVT_JTL = 0x40; // Special bitmask combinations private static final int SERVER_JPEG_PARTIAL = SERVER_CVT_JPEG | SERVER_CVT_MJPEG; private static final int SERVER_JPEG_FULL = SERVER_JPEG_PARTIAL | SERVER_CVT_M2JPEG; private static final int SERVER_FPX_PARTIAL = SERVER_CVT_FPX | SERVER_CVT_MFPX; private static final int SERVER_FPX_FULL = SERVER_FPX_PARTIAL | SERVER_CVT_M2FPX; // --- RGB[A] <-> PhotoYCC[A] metric conversion matrices --- // As stated in the FlashPix specification, these matrices are // sufficient for tone and color correction but should not be // used for actual color space conversion calculations. // PhotoYCCA -> RGBA metric color conversion matrix private static final double[][] YCCA_TO_RGBA = new double[][] {{1.358400, 0.000000, 1.821500, 0.000000}, {1.358400, -0.430300, -0.927100, 0.000000}, {1.358400, 2.217900, 0.000000, 0.000000}, {0.000000, 0.000000, 0.000000, 1.000000}}; // PhotoYCCA -> RGBA metric color conversion constant private static final double[][] YCCA_TO_RGBA_CONST = new double[][] {{-249.55}, {194.14}, {-345.99}, {0.0}}; // RGBA -> PhotoYCCA metric color conversion matrix private static final double[][] RGBA_TO_YCCA = new double[][] {{0.220018, 0.432276, 0.083867, 0.000000}, {-0.134755, -0.264756, 0.399511, 0.000000}, {0.384918, -0.322373, -0.062544, 0.000000}, {0.000000, 0.000000, 0.000000, 1.000000}}; // RGBA -> PhotoYCCA metric color conversion constant private static final double[][] RGBA_TO_YCCA_CONST = new double[][] {{0.0005726}, {155.9984}, {137.0022}, {0.0}}; // PhotoYCC -> RGB metric color conversion matrix private static final double[][] YCC_TO_RGB = new double[][] {{1.358400, 0.000000, 1.821500}, {1.358400, -0.430300, -0.927100}, {1.358400, 2.217900, 0.000000}}; // PhotoYCC -> RGB metric color conversion constant private static final double[][] YCC_TO_RGB_CONST = new double[][] {{-249.55}, {194.14}, {-345.99}}; // RGB -> PhotoYCC metric color conversion matrix private static final double[][] RGB_TO_YCC = new double[][] {{0.220018, 0.432276, 0.083867}, {-0.134755, -0.264756, 0.399511}, {0.384918, -0.322373, -0.062544}}; // RGB -> PhotoYCC metric color conversion constant private static final double[][] RGB_TO_YCC_CONST = new double[][] {{0.0005726}, {155.9984}, {137.0022}}; /** * Returns the operation mask based on the supplied parameters. */ private static final int getOperationMask(ParameterBlock pb) { int opMask = 0; // Initialize the operation mask according to which // parameters are actually supplied. if(pb.getFloatParameter(2) != 0.0F) { opMask |= MASK_FILTER; } if(pb.getObjectParameter(3) != null) { opMask |= MASK_COLOR_TWIST; } if(Math.abs(pb.getFloatParameter(4) - 1.0F) > 0.01F) { opMask |= MASK_CONTRAST; } if(pb.getObjectParameter(5) != null) { opMask |= MASK_ROI_SOURCE; } AffineTransform tf = (AffineTransform)pb.getObjectParameter(6); if(!tf.isIdentity()) { opMask |= MASK_TRANSFORM; } if(pb.getObjectParameter(7) != null) { opMask |= MASK_ASPECT_RATIO; } if(pb.getObjectParameter(8) != null) { opMask |= MASK_ROI_DESTINATION; } if(pb.getIntParameter(9) != 0) { opMask |= MASK_ROTATION; } if(pb.getObjectParameter(10) != null) { opMask |= MASK_MIRROR_AXIS; } if(pb.getObjectParameter(11) != null) { opMask |= MASK_ICC_PROFILE; } if(pb.getObjectParameter(12) != null) { opMask |= MASK_JPEG_QUALITY; } if(pb.getObjectParameter(13) != null) { opMask |= MASK_JPEG_TABLE; } return opMask; } /** * Returns the server capability mask. */ private static final int getServerCapabilityMask(String URLSpec, RenderedImage lowRes) { int vendorID = 255; // Unregistered vendor. int serverMask = 0; // Get the server bitmask from the properties of the thumbnail image. if(lowRes.getProperty("iip-server") != null && lowRes.getProperty("iip-server") != Image.UndefinedProperty) { String serverString = (String)lowRes.getProperty("iip-server"); int dot = serverString.indexOf("."); vendorID = Integer.valueOf(serverString.substring(0, dot)).intValue(); serverMask = Integer.valueOf(serverString.substring(dot + 1)).intValue(); } // If the vendor is not one the three that defined the IIP // specification then assume that the response to the OBJ=IIP-server // command is inaccurate. This may not be true in general but it // is true of the only other IIP server tested with this code. if(serverMask != 127 && vendorID != VENDOR_HP && vendorID != VENDOR_LIVE_PICTURE && vendorID != VENDOR_KODAK) { int[] maxSize = (int[])lowRes.getProperty("max-size"); String rgn = "&RGN=0.0,0.0,"+(64.0F/maxSize[0])+","+(64.0F/maxSize[1]); // Actually test these capabilities if(canDecode(URLSpec, "&CNT=0.9&WID=64&CVT=JPEG", "JPEG")) { // CVT-JPEG && CVT-MJPEG && CVT-M2JPEG serverMask = SERVER_JPEG_FULL; } else if(canDecode(URLSpec, "&CNT=0.9&WID=64&CVT=FPX", "FPX")) { // CVT-FPX && CVT-MFPX && CVT-M2FPX serverMask = SERVER_FPX_FULL; } else if(canDecode(URLSpec, rgn+"&CVT=JPEG", "JPEG")) { // CVT-JPEG && CVT-MJPEG serverMask = SERVER_JPEG_PARTIAL; } else if(canDecode(URLSpec, rgn+"&CVT=FPX", "FPX")) { // CVT-FPX && CVT-MFPX serverMask = SERVER_FPX_PARTIAL; } } return serverMask; } /** * Test whether an image can be decoded from an IIP CVT URL. * * @param base The base IIP URL including the image specification. * @param suffix The IIP URL suffix including the CVT string. * @param fmt The desired format: "JPEG" or "FPX". * @return Whether the returned stream can be dedoced successfully. */ private static boolean canDecode(String base, String suffix, String fmt) { StringBuffer buf = new StringBuffer(base); URL url = null; InputStream stream = null; RenderedImage rendering = null; boolean itWorks = false; try { buf.append(suffix); url = new URL(buf.toString()); stream = url.openStream(); ImageDecoder decoder = ImageCodec.createImageDecoder(fmt, stream, null); rendering = decoder.decodeAsRenderedImage(); itWorks = true; } catch(Exception e) { itWorks = false; // redundant } return itWorks; } /** * Multiply two matrix parameters and return the result. The number of * columns of the first parameter must equal the number of rows of * the second parameter. The result will have the same number of rows * as the first parameter and the same number of columns as the * second parameter. */ private static final double[][] matrixMultiply(double[][] A, double[][] B) { if(A[0].length != B.length) { throw new RuntimeException(JaiI18N.getString("IIPCRIF0")); } int nRows = A.length; int nCols = B[0].length; double[][] C = new double[nRows][nCols]; int nSum = A[0].length; for(int r = 0; r < nRows; r++) { for(int c = 0; c < nCols; c++) { C[r][c] = 0.0; for(int k = 0; k < nSum; k++) { C[r][c] += A[r][k]*B[k][c]; } } } return C; } /** * Compose a matrix A and a vector b into an array suitable for the * "Bandcombine" operation. The number of rows in the matrix must * equal the number of elements in the vector. */ private static final double[][] composeMatrices(double[][] A, double[][] b) { int nRows = A.length; if(nRows != b.length) { throw new RuntimeException(JaiI18N.getString("IIPCRIF1")); } else if(b[0].length != 1) { throw new RuntimeException(JaiI18N.getString("IIPCRIF2")); } int nCols = A[0].length; double[][] bcMatrix = new double[nRows][nCols+1]; for(int r = 0; r < nRows; r++) { for(int c = 0; c < nCols; c++) { bcMatrix[r][c] = A[r][c]; } bcMatrix[r][nCols] = b[r][0]; } return bcMatrix; } /** * Generate a matrix which can perform the composite mapping from the * original color space to normalized Photo YCC, apply the color-twist * transformation, and return normalized Photo YCC to the original * color space including casting down opacity and chroma channels where * appropriate. */ private static final double[][] getColorTwistMatrix(ColorModel colorModel, ParameterBlock pb) { // Convert color-twist matrix to 2D form. float[] ctwParam = (float[])pb.getObjectParameter(3); double[][] ctw = new double[4][4]; int k = 0; for(int r = 0; r < 4; r++) { for(int c = 0; c < 4; c++) { ctw[r][c] = ctwParam[k++]; } } // Calculate composed metric color conversion/color-twist matrix H // and constant d. double[][] H = null; double[][] d = null; int csType = colorModel.getColorSpace().getType(); if(csType == ColorSpace.TYPE_GRAY || csType == ColorSpace.TYPE_RGB) { // Calculate RGBA->YCCA->CTW->RGBA composed matix. H = matrixMultiply(matrixMultiply(YCCA_TO_RGBA, ctw), RGBA_TO_YCCA); d = YCCA_TO_RGBA_CONST; } else { // PYCC H = ctw; d = new double[][] {{0.0}, {0.0}, {0.0}, {0.0}}; } // Calculate matrix A and vector b to cast data upwards to 4 bands. double[][] A = null; double[][] b = null; if(csType == ColorSpace.TYPE_GRAY) { if(colorModel.hasAlpha()) { A = new double[][] {{1.0, 0.0}, {1.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; b = new double[][] {{0.0}, {0.0}, {0.0}, {0.0}}; } else { A = new double[][] {{1.0}, {1.0}, {1.0}, {0.0}}; b = new double[][] {{0.0}, {0.0}, {0.0}, {255.0}}; } } else if(!colorModel.hasAlpha()) { // RGB or YCC (no alpha) A = new double[][] {{1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}, {0.0, 0.0, 0.0}}; b = new double[][] {{0.0}, {0.0}, {0.0}, {255.0}}; } else { // RGBA or YCCA A = new double[][] {{1.0, 0.0, 0.0, 0.0}, {0.0, 1.0, 0.0, 0.0}, {0.0, 0.0, 1.0, 0.0}, {0.0, 0.0, 0.0, 1.0}}; b = new double[][] {{0.0}, {0.0}, {0.0}, {0.0}}; } // Determine whether chroma or opacity may be deleted. boolean truncateChroma = false; if(csType == ColorSpace.TYPE_GRAY && ctwParam[4] == 0.0F && ctwParam[7] == 0.0F && ctwParam[8] == 0.0F && ctwParam[11] == 0.0F) { truncateChroma = true; } boolean truncateAlpha = false; if(!colorModel.hasAlpha() && ctwParam[15] == 1.0F) { truncateAlpha = true; } // Calculate matrix T to truncate data down to alpha-less or // chroma-less data as appropriate. double[][] T = null; if(truncateAlpha && truncateChroma) { T = new double[][] {{1.0, 0.0, 0.0, 0.0}}; } else if(truncateChroma) { T = new double[][] {{1.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 1.0}}; } else if(truncateAlpha) { T = new double[][] {{1.0, 0.0, 0.0, 0.0}, {0.0, 1.0, 0.0, 0.0}, {0.0, 0.0, 1.0, 0.0}}; } else { // Retain all bands T = new double[][] {{1.0, 0.0, 0.0, 0.0}, {0.0, 1.0, 0.0, 0.0}, {0.0, 0.0, 1.0, 0.0}, {0.0, 0.0, 0.0, 1.0}}; } // Combine the matrices and vectors to get the overall transform. double[][] TH = matrixMultiply(T, H); double[][] THA = matrixMultiply(TH, A); double[][] THb = matrixMultiply(TH, b); double[][] THd = matrixMultiply(TH, d); double[][] Td = matrixMultiply(T, d); for(int r = 0; r < THb.length; r++) { for(int c = 0; c < THb[r].length; c++) { THb[r][c] += Td[r][c] - THd[r][c]; } } // Compose the results into a form appropriate for "BandCombine". return composeMatrices(THA, THb); } /** * Creates a lookup table for the contrast operation. This is to be * applied to grayscale or RGB data possibly with an alpha channel. */ private static final LookupTableJAI createContrastLUT(float K, int numBands) { byte[] contrastTable = new byte[256]; double p = 0.43F; // Generate the LUT to be applied to the color band(s). for(int i = 0; i < 256; i++) { float j = (float)(i - 127.5F)/255.0F; float f = 0.0F; if(j < 0.0F) { f = (float)(-p*Math.pow(-j/p, K)); } else if(j > 0.0F) { f = (float)(p*Math.pow(j/p, K)); } int val = (int)(f*255.0F + 127.5F); if(val < 0) { contrastTable[i] = 0; } else if(val > 255) { contrastTable[i] = (byte)255; } else { contrastTable[i] = (byte)(val & 0x000000ff); } } // Allocate LUT memory. byte[][] data = new byte[numBands][]; // Set all LUT color bands to the same previously calculated table. // If alpha is present, set the LUT for it to a ramp. if(numBands % 2 == 1) { // no alpha channel present for(int i = 0; i < numBands; i++) { data[i] = contrastTable; } } else { // alpha channel present for(int i = 0; i < numBands - 1; i++) { data[i] = contrastTable; } data[numBands-1] = new byte[256]; byte[] b = data[numBands-1]; for(int i = 0; i < 256; i++) { b[i] = (byte)i; } } return new LookupTableJAI(data); } /** Constructor. */ public IIPCRIF() { super("IIP"); } /** * Performs all operations on the server. */ private RenderedImage serverProc(int serverMask, RenderContext renderContext, ParameterBlock paramBlock, int opMask, RenderedImage lowRes) { // Ensure that one of the four expected combinations obtains. if((serverMask & SERVER_JPEG_FULL) != SERVER_JPEG_FULL && (serverMask & SERVER_FPX_FULL) != SERVER_FPX_FULL && (serverMask & SERVER_JPEG_PARTIAL) != SERVER_JPEG_PARTIAL && (serverMask & SERVER_FPX_PARTIAL) != SERVER_FPX_PARTIAL) { return null; } ImagingListener listener = ImageUtil.getImagingListener(renderContext); // Set JPEG and full server flags. boolean isJPEG = false; boolean isFull = false; if((serverMask & SERVER_JPEG_FULL) == SERVER_JPEG_FULL) { isJPEG = isFull = true; } else if((serverMask & SERVER_FPX_FULL) == SERVER_FPX_FULL) { isJPEG = false; isFull = true; } else if((serverMask & SERVER_JPEG_PARTIAL) == SERVER_JPEG_PARTIAL) { isJPEG = true; isFull = false; } // Create a StringBuffer for the composed image command URL. StringBuffer buf = new StringBuffer((String)paramBlock.getObjectParameter(0)); //TODO: subImages (how?) // Filtering. if((opMask & MASK_FILTER) != 0) { buf.append("&FTR="+paramBlock.getFloatParameter(2)); } // Color-twist. if((opMask & MASK_COLOR_TWIST) != 0) { buf.append("&CTW="); float[] ctw = (float[])paramBlock.getObjectParameter(3); for(int i = 0; i < ctw.length; i++) { buf.append(ctw[i]); if(i != ctw.length-1) { buf.append(","); } } } // Contrast. if((opMask & MASK_CONTRAST) != 0) { buf.append("&CNT="+paramBlock.getFloatParameter(4)); } // Source rectangle of interest. if((opMask & MASK_ROI_SOURCE) != 0) { Rectangle2D roi = (Rectangle2D)paramBlock.getObjectParameter(5); buf.append("&ROI="+roi.getX()+","+ roi.getY()+","+ roi.getWidth()+","+roi.getHeight()); } // If full support for the CVT command is available, decompose the // AffineTransform specifying the transformation from renderable to // rendered coordinates into a translation, a pure scale, and the // residual transformation. The residual transformation may then be // concatenated with the server-side affine transform (after // inversion), the pure scale may be effected by specifying the WID // and HEI composed image command modifiers, and the translation as // a subsequent operation. If the WID and HEI modifiers are not // available, i.e., the server support is partial, this becomes // more problematic. Fortunately no such servers are known to exist. // Initialize the post-processing transform to the identity. AffineTransform postTransform = new AffineTransform(); // Retrieve (a clone of) the renderable-to-rendered mapping. AffineTransform at = (AffineTransform)renderContext.getTransform().clone(); // If the translation is non-zero set the post-transform. if(at.getTranslateX() != 0.0 || at.getTranslateY() != 0.0) { postTransform.setToTranslation(at.getTranslateX(), at.getTranslateY()); double[] m = new double[6]; at.getMatrix(m); at.setTransform(m[0], m[1], m[2], m[3], 0.0, 0.0); } // Determine the renderable destination region of interest. Rectangle2D rgn = null; if((opMask & MASK_ROI_DESTINATION) != 0) { rgn = (Rectangle2D)paramBlock.getObjectParameter(8); } else { float aspectRatio = 1.0F; if((opMask & MASK_ASPECT_RATIO) != 0) { aspectRatio = paramBlock.getFloatParameter(7); } else { aspectRatio = ((Float)(lowRes.getProperty("aspect-ratio"))).floatValue(); } rgn = new Rectangle2D.Float(0.0F, 0.0F, aspectRatio, 1.0F); } // Apply the renderable-to-rendered mapping to the renderable // destination region of interest. Rectangle dstROI = at.createTransformedShape(rgn).getBounds(); // Calculate the pure scale portion of the // renderable-to-rendered mapping. AffineTransform scale = AffineTransform.getScaleInstance(dstROI.getWidth()/ rgn.getWidth(), dstROI.getHeight()/ rgn.getHeight()); // Determine the residual mapping. try { at.preConcatenate(scale.createInverse()); } catch(Exception e) { String message = JaiI18N.getString("IIPCRIF6"); listener.errorOccurred(message, new ImagingException(message, e), this, false); // throw new RuntimeException(JaiI18N.getString("IIPCRIF6")); } // Compose the inverse residual mapping with the renderable // transform. AffineTransform afn = (AffineTransform)paramBlock.getObjectParameter(6); try { afn.preConcatenate(at.createInverse()); } catch(Exception e) { String message = JaiI18N.getString("IIPCRIF6"); listener.errorOccurred(message, new ImagingException(message, e), this, false); // throw new RuntimeException(JaiI18N.getString("IIPCRIF6")); } if(isFull) { // Append the WID and HEI composed image command modifiers using // the dimensions of the rendered destination region of interest. buf.append("&WID="+dstROI.width+"&HEI="+dstROI.height); /* XXX Begin suppressed section. } else if((opMask & MASK_TRANSFORM) != 0) { Point2D[] dstPts = new Point2D[] {new Point2D.Double(rgn.getMinX(), rgn.getMinY()), new Point2D.Double(rgn.getMaxX(), rgn.getMinY()), new Point2D.Double(rgn.getMinX(), rgn.getMaxY())}; Point2D[] srcPts = new Point2D[3]; afn.transform(dstPts, 0, srcPts, 0, 3); double LLeft = srcPts[0].distance(srcPts[2]); double LTop = srcPts[0].distance(srcPts[1]); int[] maxSize = (int[])lowRes.getProperty("max-size"); double H = maxSize[1]*LLeft; double W = maxSize[1]*LTop; double m = Math.max(H, W*(double)maxSize[1]/(double)maxSize[0]); int Hp = (int)(m + 0.5); int Wp = (int)(m*(double)maxSize[0]/(double)maxSize[1] + 0.5); System.out.println("Estimated dimensions = "+Wp+" x "+Hp); AffineTransform scl = AffineTransform.getScaleInstance(dstROI.getWidth()/Wp, dstROI.getHeight()/Hp); System.out.println("scl = "+scl); afn.preConcatenate(scl); End suppressed section. XXX */ } // Append the affine tranform composed image command. double[] matrix = new double[6]; afn.getMatrix(matrix); buf.append("&AFN="+ matrix[0]+","+matrix[2]+",0,"+matrix[4]+","+ matrix[1]+","+matrix[3]+",0,"+matrix[5]+ ",0,0,1,0,0,0,0,1"); // Destination aspect ratio. if((opMask & MASK_ASPECT_RATIO) != 0) { buf.append("&RAR="+paramBlock.getFloatParameter(7)); } // Destination rectangle of interest. if((opMask & MASK_ROI_DESTINATION) != 0) { Rectangle2D dstRGN = (Rectangle2D)paramBlock.getObjectParameter(8); buf.append("&RGN="+dstRGN.getX()+","+ dstRGN.getY()+","+ dstRGN.getWidth()+","+dstRGN.getHeight()); } // Rotation and mirroring. if(isFull) { if((opMask & MASK_ROTATION) != 0 || (opMask & MASK_MIRROR_AXIS) != 0) { buf.append("&RFM="+paramBlock.getIntParameter(9)); if((opMask & MASK_MIRROR_AXIS) != 0) { String axis = (String)paramBlock.getObjectParameter(10); if(axis.equalsIgnoreCase("x")) { buf.append(",0"); } else { buf.append(",90"); } } } } // ICC profile. if((opMask & MASK_ICC_PROFILE) != 0) { // According to the IIP specification this is not supported // over HTTP connections and that is all that is available from // the vendors right now, i.e., no socket connections are // available (includes LivePicture and TrueSpectra). } // JPEG quality and compression group index. if(isJPEG) { if((opMask & MASK_JPEG_QUALITY) != 0) { buf.append("&QLT="+paramBlock.getIntParameter(12)); } if((opMask & MASK_JPEG_TABLE) != 0) { buf.append("&CIN="+paramBlock.getIntParameter(13)); } } // Set the format string. String format = isJPEG ? "JPEG" : "FPX"; // Append the CVT command. buf.append("&CVT="+format); // Create a URL with the CVT string, open a stream from it, and // decode the image using the appropriate decoder. InputStream stream = null; RenderedImage rendering = null; try { URL url = new URL(buf.toString()); stream = url.openStream(); MemoryCacheSeekableStream sStream = new MemoryCacheSeekableStream(stream); rendering = JAI.create(format, sStream); } catch(Exception e) { String message = JaiI18N.getString("IIPCRIF7") + " " + buf.toString(); listener.errorOccurred(message, new ImagingException(message, e), this, false); // throw new RuntimeException(e.getClass()+" "+e.getMessage()); } // If WID and HEI modifiers are unavailable add scale. if(!isFull) { postTransform.scale(dstROI.getWidth()/rendering.getWidth(), dstROI.getHeight()/rendering.getHeight()); } // Translate (and scale) the result if necessary. if(!postTransform.isIdentity()) { Interpolation interp = Interpolation.getInstance(Interpolation.INTERP_NEAREST); RenderingHints hints = renderContext.getRenderingHints(); if(hints != null && hints.containsKey(JAI.KEY_INTERPOLATION)) { interp = (Interpolation)hints.get(JAI.KEY_INTERPOLATION); } rendering = JAI.create("affine", rendering, postTransform, interp); } return rendering; } /** * Performs all operations on the client. */ private RenderedImage clientProc(RenderContext renderContext, ParameterBlock paramBlock, int opMask, RenderedImage lowRes) { // Cache RenderContext components. AffineTransform at = renderContext.getTransform(); RenderingHints hints = renderContext.getRenderingHints(); ImagingListener listener = ImageUtil.getImagingListener(renderContext); // Obtain the number of levels and the size of the largest one. int[] maxSize = (int[])lowRes.getProperty("max-size"); int maxWidth = maxSize[0]; int maxHeight = maxSize[1]; int numLevels = ((Integer)lowRes.getProperty("resolution-number")).intValue(); // Calculate the aspect ratios. float aspectRatioSource = (float)maxWidth/(float)maxHeight; float aspectRatio = (opMask & MASK_ASPECT_RATIO) != 0 ? paramBlock.getFloatParameter(7) : aspectRatioSource; // Determine the bounds of the destination image. Rectangle2D bounds2D = new Rectangle2D.Float(0.0F, 0.0F, aspectRatio, 1.0F); // Determine the dimensions of the rendered destination image. int width; int height; if(at.isIdentity()) { // Default rendering. AffineTransform afn = (AffineTransform)paramBlock.getObjectParameter(6); Rectangle2D bounds = afn.createTransformedShape(bounds2D).getBounds2D(); double H = maxHeight*bounds.getHeight(); double W = maxHeight*bounds.getWidth(); double m = Math.max(H, W/aspectRatioSource); height = (int)(m + 0.5); width = (int)(aspectRatioSource*m + 0.5); at = AffineTransform.getScaleInstance(width, height); renderContext = (RenderContext)renderContext.clone(); renderContext.setTransform(at); } else { Rectangle bounds = at.createTransformedShape(bounds2D).getBounds(); width = bounds.width; height = bounds.height; } // Determine which resolution level of the IIP image to request. int res = numLevels - 1; int hRes = maxHeight; while(res > 0) { hRes = (int)((hRes + 1.0F)/2.0F); // get the next height if(hRes < height) { // stop if the next height is too small break; } res--; } // Create a RenderableImage from the selected resolution level. int[] subImageArray = (int[])paramBlock.getObjectParameter(1); int subImage = subImageArray.length < res + 1 ? 0 : subImageArray[res]; if(subImage < 0) { subImage = 0; } ParameterBlock pb = new ParameterBlock(); pb.add(paramBlock.getObjectParameter(0)).add(res).add(subImage); RenderedImage iipRes = JAI.create("iipresolution", pb); Vector sources = new Vector(1); sources.add(iipRes); RenderableImage ri = new MultiResolutionRenderableImage(sources, 0.0F, 0.0F, 1.0F); // Filtering. if((opMask & MASK_FILTER) != 0) { float filter = paramBlock.getFloatParameter(2); pb = (new ParameterBlock()).addSource(ri).add(filter); ri = new RenderableImageOp(new FilterCRIF(), pb); } // Color-twist. // Cache the original number of bands in case the number of bands // changes due to addition of chroma and/or alpha channels in the // color-twist procedure. int nBands = iipRes.getSampleModel().getNumBands(); if((opMask & MASK_COLOR_TWIST) != 0) { double[][] ctw = getColorTwistMatrix(iipRes.getColorModel(), paramBlock); pb = (new ParameterBlock()).addSource(ri).add(ctw); ri = JAI.createRenderable("bandcombine", pb); nBands = ctw.length; } // Contrast. if((opMask & MASK_CONTRAST) != 0) { int csType = iipRes.getColorModel().getColorSpace().getType(); boolean isPYCC = csType != ColorSpace.TYPE_GRAY && csType != ColorSpace.TYPE_RGB; if(isPYCC) { double[][] matrix; if(nBands == 3) { // PYCC matrix = composeMatrices(YCC_TO_RGB, YCC_TO_RGB_CONST); } else { // PYCC-A matrix = composeMatrices(YCCA_TO_RGBA, YCCA_TO_RGBA_CONST); } pb = (new ParameterBlock()).addSource(ri).add(matrix); ri = JAI.createRenderable("bandcombine", pb); } float contrast = paramBlock.getFloatParameter(4); LookupTableJAI lut = createContrastLUT(contrast, nBands); pb = (new ParameterBlock()).addSource(ri).add(lut); ri = JAI.createRenderable("lookup", pb); if(isPYCC) { double[][] matrix; if(nBands == 3) { // PYCC matrix = composeMatrices(RGB_TO_YCC, RGB_TO_YCC_CONST); } else { // PYCC-A matrix = composeMatrices(RGBA_TO_YCCA, RGBA_TO_YCCA_CONST); } pb = (new ParameterBlock()).addSource(ri).add(matrix); ri = JAI.createRenderable("bandcombine", pb); } } // Source rectangle of interest. if((opMask & MASK_ROI_SOURCE) != 0) { // Get the source rectangle of interest. Rectangle2D rect = (Rectangle2D)paramBlock.getObjectParameter(5); // Check for intersection with source bounds. if(!rect.intersects(0.0, 0.0, aspectRatioSource, 1.0)) { throw new RuntimeException(JaiI18N.getString("IIPCRIF5")); } // Create the source rectangle. Rectangle2D rectS = new Rectangle2D.Float(0.0F, 0.0F, aspectRatioSource, 1.0F); // Crop out the desired region. if(!rect.equals(rectS)) { // Clip to the source bounds. rect = rect.createIntersection(rectS); // Crop to the clipped rectangle of interest. pb = (new ParameterBlock()).addSource(ri); pb.add((float)rect.getMinX()).add((float)rect.getMinY()); pb.add((float)rect.getWidth()).add((float)rect.getHeight()); ri = JAI.createRenderable("crop", pb); /* XXX // Embed the cropped image in an image the size of the source. pb = (new ParameterBlock()).addSource(ri); pb.add((float)rectS.getMinX()).add((float)rectS.getMinY()); pb.add((float)rectS.getWidth()).add((float)rectS.getHeight()); ri = JAI.createRenderable("crop", pb); */ } } // Spatial orientation. if((opMask & MASK_TRANSFORM) != 0) { AffineTransform afn = (AffineTransform)paramBlock.getObjectParameter(6); try { // The transform parameter is a backward mapping so invert it. afn = afn.createInverse(); } catch(java.awt.geom.NoninvertibleTransformException e) { // This should never happen due to descriptor check. listener.errorOccurred(JaiI18N.getString("AffineNotInvertible"), e, this, false); } pb = (new ParameterBlock()).addSource(ri).add(afn); if(hints != null && hints.containsKey(JAI.KEY_INTERPOLATION)) { pb.add(hints.get(JAI.KEY_INTERPOLATION)); } ri = JAI.createRenderable("affine", pb); } // Destination rectangle of interest. // Set the destination rectangle of interest. Rectangle2D rgn = (opMask & MASK_ROI_DESTINATION) != 0 ? (Rectangle2D)paramBlock.getObjectParameter(8) : bounds2D; // Verify that the region is non-empty. if(rgn.isEmpty()) { throw new RuntimeException(JaiI18N.getString("IIPCRIF3")); } // Create a Rectangle2D for the current image. Rectangle2D riRect = new Rectangle2D.Float((float)ri.getMinX(), (float)ri.getMinY(), (float)ri.getWidth(), (float)ri.getHeight()); // If the current image bounds are not those of the requested // region then crop the image. if(!rgn.equals(riRect)) { // Intersect rgn with source image bounds. rgn = rgn.createIntersection(riRect); // Crop to the rectangle of interest. pb = (new ParameterBlock()).addSource(ri); pb.add((float)rgn.getMinX()).add((float)rgn.getMinY()); pb.add((float)rgn.getWidth()).add((float)rgn.getHeight()); ri = JAI.createRenderable("crop", pb); } // Return the rendering. return ri.createRendering(renderContext); } /** * Returns the default rendering of the RenderableImage produced by * the "iip" operation. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { RenderableImage iipImage = JAI.createRenderable("iip", paramBlock); return iipImage.createDefaultRendering(); } /** * Applies the specified set of operations to the IIP image * and returns a RenderedImage that satisfies the rendering context * provided. */ public RenderedImage create(RenderContext renderContext, ParameterBlock paramBlock) { // Get the operation mask. int opMask = getOperationMask(paramBlock); ImagingListener listener = ImageUtil.getImagingListener(renderContext); // Get the lowest resolution level of the IIP image for property use. ParameterBlock pb = new ParameterBlock(); int[] subImageArray = (int[])paramBlock.getObjectParameter(1); pb.add(paramBlock.getObjectParameter(0)).add(0).add(subImageArray[0]); RenderedImage lowRes = JAI.create("iipresolution", pb); // Get the server capability mask. int serverMask = getServerCapabilityMask((String)paramBlock.getObjectParameter(0), lowRes); RenderedImage rendering = null; // Select the processing path based on the server's capabilities. if((serverMask & SERVER_JPEG_FULL) == SERVER_JPEG_FULL || (serverMask & SERVER_FPX_FULL) == SERVER_FPX_FULL || (serverMask & SERVER_JPEG_PARTIAL) == SERVER_JPEG_PARTIAL || (serverMask & SERVER_FPX_PARTIAL) == SERVER_FPX_PARTIAL) { // All (FULL) or most (PARTIAL) ops on server rendering = serverProc(serverMask, renderContext, paramBlock, opMask, lowRes); } else { // All ops on client rendering = clientProc(renderContext, paramBlock, opMask, lowRes); // Do special processing if source rectangle of interest given. // The following approach works but is rather slow. if((opMask & MASK_ROI_SOURCE) != 0) { // Retrieve the source rectangle of interest. Rectangle2D rgn = (Rectangle2D)paramBlock.getObjectParameter(5); // Retrieve a clone of the renderable transform. AffineTransform at = (AffineTransform) ((AffineTransform)(paramBlock.getObjectParameter(6))).clone(); // If the transform is not the identity, invert it. if(!at.isIdentity()) { try { at = at.createInverse(); } catch(Exception e) { String message = JaiI18N.getString("IIPCRIF6"); listener.errorOccurred(message, new ImagingException(message, e), this, false); // throw new RuntimeException(JaiI18N.getString("IIPCRIF6")); } } // Compose the inverted renderable transform with the // renderable-to-rendered transform to get the transform // from source renderable coordinates to destination // rendered coordinates. at.preConcatenate(renderContext.getTransform()); // Create an ROI in destination rendered space. ROIShape roi = new ROIShape(at.createTransformedShape(rgn)); // Create a TiledImage to contain the masked result. TiledImage ti = new TiledImage(rendering.getMinX(), rendering.getMinY(), rendering.getWidth(), rendering.getHeight(), rendering.getTileGridXOffset(), rendering.getTileGridYOffset(), rendering.getSampleModel(), rendering.getColorModel()); // Set the TiledImage data source to the rendering. ti.set(rendering, roi); // Create a constant-valued image for the background. pb = new ParameterBlock(); pb.add((float)ti.getWidth()); pb.add((float)ti.getHeight()); Byte[] bandValues = new Byte[ti.getSampleModel().getNumBands()]; for(int b = 0; b < bandValues.length; b++) { bandValues[b] = new Byte((byte)255); } pb.add(bandValues); ImageLayout il = new ImageLayout(); il.setSampleModel(ti.getSampleModel()); RenderingHints rh = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, il); PlanarImage constImage = JAI.create("constant", pb, rh); // Compute a complement ROI. ROI complementROI = (new ROIShape(ti.getBounds())).subtract(roi);; // Fill the background. int maxTileY = ti.getMaxTileY(); int maxTileX = ti.getMaxTileX(); for(int j = ti.getMinTileY(); j <= maxTileY; j++) { for(int i = ti.getMinTileX(); i <= maxTileX; i++) { if(!roi.intersects(ti.getTileRect(i, j))) { ti.setData(constImage.getTile(i, j), complementROI); } } } // Set the rendering to the TiledImage. rendering = ti; } } // If the server supports only the first tier of composed image // command modifiers or none at all then the "RFM" modifier // effect must be replicated on the client if this would be // required by the supplied parameters. if((serverMask & SERVER_JPEG_FULL) != SERVER_JPEG_FULL && (serverMask & SERVER_FPX_FULL) != SERVER_FPX_FULL) { if((opMask & MASK_ROTATION) != 0) { // NOTE: The transpose operation uses clockwise rotation // whereas this operation expects counterclockwise. EnumeratedParameter transposeType = null; switch(paramBlock.getIntParameter(9)) { case 90: transposeType = TransposeDescriptor.ROTATE_270; break; case 180: transposeType = TransposeDescriptor.ROTATE_180; break; case 270: transposeType = TransposeDescriptor.ROTATE_90; break; } if(transposeType != null) { // deliberately redundant test rendering = JAI.create("transpose", rendering, transposeType); } } if((opMask & MASK_MIRROR_AXIS) != 0) { String axis = (String)paramBlock.getObjectParameter(10); EnumeratedParameter transposeType = axis.equalsIgnoreCase("x") ? TransposeDescriptor.FLIP_VERTICAL : TransposeDescriptor.FLIP_HORIZONTAL; rendering = JAI.create("transpose", rendering, transposeType); } } return rendering; } /** * Returns the bounds of the RenderableImage. This will be the * rendering-independent destination rectangle of interest if supplied * or the rendering-independent destination image bounds if not. */ public Rectangle2D getBounds2D(ParameterBlock paramBlock) { int opMask = getOperationMask(paramBlock); if((opMask & MASK_ROI_DESTINATION) != 0) { return (Rectangle2D)paramBlock.getObjectParameter(8); } float aspectRatioDestination; if((opMask & MASK_ASPECT_RATIO) != 0) { aspectRatioDestination = paramBlock.getFloatParameter(7); } else { // Get the lowest resolution level of the IIP image. ParameterBlock pb = new ParameterBlock(); int[] subImageArray = (int[])paramBlock.getObjectParameter(1); pb.add(paramBlock.getObjectParameter(0)); pb.add(0).add(subImageArray[0]); RenderedImage lowRes = JAI.create("iipresolution", pb); int[] maxSize = (int[])lowRes.getProperty("max-size"); aspectRatioDestination = (float)maxSize[0]/(float)maxSize[1]; } return new Rectangle2D.Float(0.0F, 0.0F, aspectRatioDestination, 1.0F); } public static void main(String[] args) { int nr = 0; int nc = 0; double[][] x = matrixMultiply(RGBA_TO_YCCA, YCCA_TO_RGBA); nr = x.length; nc = x[0].length; for(int r = 0; r < nr; r++) { for(int c = 0; c < nc; c++) { System.out.print(x[r][c]+" "); } System.out.println(""); } System.out.println(""); x = matrixMultiply(RGB_TO_YCC, YCC_TO_RGB); nr = x.length; nc = x[0].length; for(int r = 0; r < nr; r++) { for(int c = 0; c < nc; c++) { System.out.print(x[r][c]+" "); } System.out.println(""); } System.out.println(""); double[][] b = new double[][] {{1.0}, {2.0}, {3.0}, {4.0}}; double[][] A = composeMatrices(YCCA_TO_RGBA, b); nr = A.length; nc = A[0].length; for(int r = 0; r < nr; r++) { for(int c = 0; c < nc; c++) { System.out.print(A[r][c]+" "); } System.out.println(""); } System.out.println(""); double[][] d4 = matrixMultiply(RGBA_TO_YCCA, YCCA_TO_RGBA_CONST); nr = d4.length; nc = d4[0].length; for(int r = 0; r < nr; r++) { for(int c = 0; c < nc; c++) { System.out.print(-d4[r][c]+" "); } System.out.println(""); } System.out.println(""); double[][] d3 = matrixMultiply(RGB_TO_YCC, YCC_TO_RGB_CONST); nr = d3.length; nc = d3[0].length; for(int r = 0; r < nr; r++) { for(int c = 0; c < nc; c++) { System.out.print(-d3[r][c]+" "); } System.out.println(""); } System.out.println(""); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/AddOpImage.java0000644000175000017500000007236310203035544026040 0ustar mathieumathieu/* * $RCSfile: AddOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:12 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PointOpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.RasterFactory; import javax.media.jai.TileCache; import java.util.Map; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.util.JDKWorkarounds; /// import com.sun.media.jai.test.OpImageTester; /** * An OpImage implementing the "Add" operation as * described in javax.media.jai.operator.AddDescriptor. * *

    This OpImage adds the pixel values of two source * images on a per-band basis. In case the two source images have different * number of bands, the number of bands for the destination image is the * smaller band number of the two source images. That is * dstNumBands = Math.min(src1NumBands, src2NumBands). * In case the two source images have different data types, the data type * for the destination image is the higher data type of the two source * images. * *

    The value of the pixel (x, y) in the destination image is defined as: *

     * for (b = 0; b < numBands; b++) {
     *     dst[y][x][b] = src1[y][x][b] + src2[y][x][b];
     * }
     * 
    * *

    If the result of the addition overflows/underflows the * maximum/minimum value supported by the destination image, then it * will be clamped to the maximum/minimum value respectively. The * data type byte is treated as unsigned, with maximum * value as 255 and minimum value as 0. * * @see javax.media.jai.operator.AddDescriptor * @see AddCRIF * */ final class AddOpImage extends PointOpImage { /* Source 1 band increment */ private int s1bd = 1; /* Source 2 band increment */ private int s2bd = 1; /* Bilevel data flag. */ private boolean areBinarySampleModels = false; /** * Constructs an AddOpImage. * *

    The layout parameter may optionally contains the * tile grid layout, sample model, and/or color model. The image * dimension is determined by the intersection of the bounding boxes * of the two source images. * *

    The image layout of the first source image, source1, * is used as the fall-back for the image layout of the destination * image. Any layout parameters not specified in the layout * argument are set to the same value as that of source1. * * @param source1 The first source image. * @param source2 The second source image. * @param layout The destination image layout. */ public AddOpImage(RenderedImage source1, RenderedImage source2, Map config, ImageLayout layout) { super(source1, source2, layout, config, true); if(ImageUtil.isBinary(getSampleModel()) && ImageUtil.isBinary(source1.getSampleModel()) && ImageUtil.isBinary(source2.getSampleModel())) { // Binary processing case: RasterAccessor areBinarySampleModels = true; } else { // Get the source band counts. int numBands1 = source1.getSampleModel().getNumBands(); int numBands2 = source2.getSampleModel().getNumBands(); // Handle the special case of adding a single band image to // each band of a multi-band image. int numBandsDst; if(layout != null && layout.isValid(ImageLayout.SAMPLE_MODEL_MASK)) { SampleModel sm = layout.getSampleModel(null); numBandsDst = sm.getNumBands(); // One of the sources must be single-banded and the other must // have at most the number of bands in the SampleModel hint. if(numBandsDst > 1 && ((numBands1 == 1 && numBands2 > 1) || (numBands2 == 1 && numBands1 > 1))) { // Clamp the destination band count to the number of // bands in the multi-band source. numBandsDst = Math.min(Math.max(numBands1, numBands2), numBandsDst); // Create a new SampleModel if necessary. if(numBandsDst != sampleModel.getNumBands()) { sampleModel = RasterFactory.createComponentSampleModel( sm, sampleModel.getTransferType(), sampleModel.getWidth(), sampleModel.getHeight(), numBandsDst); if(colorModel != null && !JDKWorkarounds.areCompatibleDataModels(sampleModel, colorModel)) { colorModel = ImageUtil.getCompatibleColorModel(sampleModel, config); } } // Set the source band increments. s1bd = numBands1 == 1 ? 0 : 1; s2bd = numBands2 == 1 ? 0 : 1; } } } // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * Adds the pixel values of two source images within a specified * rectangle. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { if(areBinarySampleModels) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); // For PointOpImage, srcRect = destRect. RasterAccessor s1 = new RasterAccessor(sources[0], destRect, formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor s2 = new RasterAccessor(sources[1], destRect, formatTags[1], getSourceImage(1).getColorModel()); RasterAccessor d = new RasterAccessor(dest, destRect, formatTags[2], getColorModel()); if(d.isBinary()) { byte[] src1Bits = s1.getBinaryDataArray(); byte[] src2Bits = s2.getBinaryDataArray(); byte[] dstBits = d.getBinaryDataArray(); int length = dstBits.length; for(int i = 0; i < length; i++) { // "Add" is equivalent to "Or" when 1+1 is clamped to 1. dstBits[i] = (byte)(src1Bits[i] | src2Bits[i]); } d.copyBinaryDataToRaster(); return; } } // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); RasterAccessor s1 = new RasterAccessor(sources[0], destRect, formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor s2 = new RasterAccessor(sources[1], destRect, formatTags[1], getSourceImage(1).getColorModel()); RasterAccessor d = new RasterAccessor(dest, destRect, formatTags[2], getColorModel()); switch (d.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(s1, s2, d); break; case DataBuffer.TYPE_USHORT: computeRectUShort(s1, s2, d); break; case DataBuffer.TYPE_SHORT: computeRectShort(s1, s2, d); break; case DataBuffer.TYPE_INT: computeRectInt(s1, s2, d); break; case DataBuffer.TYPE_FLOAT: computeRectFloat(s1, s2, d); break; case DataBuffer.TYPE_DOUBLE: computeRectDouble(s1, s2, d); break; } if (d.needsClamping()) { d.clampDataArrays(); } d.copyDataToRaster(); } private void computeRectByte(RasterAccessor src1, RasterAccessor src2, RasterAccessor dst) { int s1LineStride = src1.getScanlineStride(); int s1PixelStride = src1.getPixelStride(); int[] s1BandOffsets = src1.getBandOffsets(); byte[][] s1Data = src1.getByteDataArrays(); int s2LineStride = src2.getScanlineStride(); int s2PixelStride = src2.getPixelStride(); int[] s2BandOffsets = src2.getBandOffsets(); byte[][] s2Data = src2.getByteDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int bands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); byte[][] dData = dst.getByteDataArrays(); for (int b = 0, s1b = 0, s2b = 0; b < bands; b++, s1b += s1bd, s2b += s2bd) { byte[] s1 = s1Data[s1b]; byte[] s2 = s2Data[s2b]; byte[] d = dData[b]; int s1LineOffset = s1BandOffsets[s1b]; int s2LineOffset = s2BandOffsets[s2b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int s1PixelOffset = s1LineOffset; int s2PixelOffset = s2LineOffset; int dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; dLineOffset += dLineStride; int sum = 0; for (int w = 0; w < dwidth; w++) { // // The next two lines are a fast way to do // an add with saturation on U8 elements. // It eliminates the need to do clamping. // sum = (s1[s1PixelOffset]&0xFF) + (s2[s2PixelOffset]&0xFF); d[dPixelOffset] = (byte)((((sum<<23) >> 31) | sum) & 0xFF); s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; dPixelOffset += dPixelStride; } } } } private void computeRectUShort(RasterAccessor src1, RasterAccessor src2, RasterAccessor dst) { int s1LineStride = src1.getScanlineStride(); int s1PixelStride = src1.getPixelStride(); int[] s1BandOffsets = src1.getBandOffsets(); short[][] s1Data = src1.getShortDataArrays(); int s2LineStride = src2.getScanlineStride(); int s2PixelStride = src2.getPixelStride(); int[] s2BandOffsets = src2.getBandOffsets(); short[][] s2Data = src2.getShortDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int bands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); short[][] dData = dst.getShortDataArrays(); for (int b = 0, s1b = 0, s2b = 0; b < bands; b++, s1b += s1bd, s2b += s2bd) { short[] s1 = s1Data[s1b]; short[] s2 = s2Data[s2b]; short[] d = dData[b]; int s1LineOffset = s1BandOffsets[s1b]; int s2LineOffset = s2BandOffsets[s2b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int s1PixelOffset = s1LineOffset; int s2PixelOffset = s2LineOffset; int dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { d[dPixelOffset] = ImageUtil.clampUShortPositive( (int)(s1[s1PixelOffset]&0xFFFF) + (int)(s2[s2PixelOffset]&0xFFFF)); s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; dPixelOffset += dPixelStride; } } } } private void computeRectShort(RasterAccessor src1, RasterAccessor src2, RasterAccessor dst) { int s1LineStride = src1.getScanlineStride(); int s1PixelStride = src1.getPixelStride(); int[] s1BandOffsets = src1.getBandOffsets(); short[][] s1Data = src1.getShortDataArrays(); int s2LineStride = src2.getScanlineStride(); int s2PixelStride = src2.getPixelStride(); int[] s2BandOffsets = src2.getBandOffsets(); short[][] s2Data = src2.getShortDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int bands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); short[][] dData = dst.getShortDataArrays(); for (int b = 0, s1b = 0, s2b = 0; b < bands; b++, s1b += s1bd, s2b += s2bd) { short[] s1 = s1Data[s1b]; short[] s2 = s2Data[s2b]; short[] d = dData[b]; int s1LineOffset = s1BandOffsets[s1b]; int s2LineOffset = s2BandOffsets[s2b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int s1PixelOffset = s1LineOffset; int s2PixelOffset = s2LineOffset; int dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { d[dPixelOffset] = ImageUtil.clampShort((int)s1[s1PixelOffset] + (int)s2[s2PixelOffset]); s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; dPixelOffset += dPixelStride; } } } } private void computeRectInt(RasterAccessor src1, RasterAccessor src2, RasterAccessor dst) { int s1LineStride = src1.getScanlineStride(); int s1PixelStride = src1.getPixelStride(); int[] s1BandOffsets = src1.getBandOffsets(); int[][] s1Data = src1.getIntDataArrays(); int s2LineStride = src2.getScanlineStride(); int s2PixelStride = src2.getPixelStride(); int[] s2BandOffsets = src2.getBandOffsets(); int[][] s2Data = src2.getIntDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int bands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); int[][] dData = dst.getIntDataArrays(); /* * The destination data type may be any of the integral data types. * The "clamp" function must clamp to the appropriate range for * that data type. */ switch (sampleModel.getTransferType()) { case DataBuffer.TYPE_BYTE: for (int b = 0, s1b = 0, s2b = 0; b < bands; b++, s1b += s1bd, s2b += s2bd) { int[] s1 = s1Data[s1b]; int[] s2 = s2Data[s2b]; int[] d = dData[b]; int s1LineOffset = s1BandOffsets[s1b]; int s2LineOffset = s2BandOffsets[s2b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int s1PixelOffset = s1LineOffset; int s2PixelOffset = s2LineOffset; int dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; dLineOffset += dLineStride; int sum = 0; for (int w = 0; w < dwidth; w++) { // // The next two lines are a fast way to do // an add with saturation on U8 elements. // It eliminates the need to do clamping. // sum = (s1[s1PixelOffset]&0xFF) + (s2[s2PixelOffset]&0xFF); d[dPixelOffset] = ((((sum<<23) >> 31) | sum) & 0xFF); s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; dPixelOffset += dPixelStride; } } } break; case DataBuffer.TYPE_USHORT: for (int b = 0, s1b = 0, s2b = 0; b < bands; b++, s1b += s1bd, s2b += s2bd) { int[] s1 = s1Data[s1b]; int[] s2 = s2Data[s2b]; int[] d = dData[b]; int s1LineOffset = s1BandOffsets[s1b]; int s2LineOffset = s2BandOffsets[s2b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int s1PixelOffset = s1LineOffset; int s2PixelOffset = s2LineOffset; int dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { d[dPixelOffset] = ImageUtil.clampUShortPositive( (int)(s1[s1PixelOffset]&0xFFFF) + (int)(s2[s2PixelOffset]&0xFFFF)); s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; dPixelOffset += dPixelStride; } } } break; case DataBuffer.TYPE_SHORT: for (int b = 0, s1b = 0, s2b = 0; b < bands; b++, s1b += s1bd, s2b += s2bd) { int[] s1 = s1Data[s1b]; int[] s2 = s2Data[s2b]; int[] d = dData[b]; int s1LineOffset = s1BandOffsets[s1b]; int s2LineOffset = s2BandOffsets[s2b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int s1PixelOffset = s1LineOffset; int s2PixelOffset = s2LineOffset; int dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { d[dPixelOffset] = ImageUtil.clampShort((int)s1[s1PixelOffset] + (int)s2[s2PixelOffset]); s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; dPixelOffset += dPixelStride; } } } break; case DataBuffer.TYPE_INT: for (int b = 0, s1b = 0, s2b = 0; b < bands; b++, s1b += s1bd, s2b += s2bd) { int[] s1 = s1Data[s1b]; int[] s2 = s2Data[s2b]; int[] d = dData[b]; int s1LineOffset = s1BandOffsets[s1b]; int s2LineOffset = s2BandOffsets[s2b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int s1PixelOffset = s1LineOffset; int s2PixelOffset = s2LineOffset; int dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { d[dPixelOffset] = ImageUtil.clampInt((long)s1[s1PixelOffset] + (long)s2[s2PixelOffset]); s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; dPixelOffset += dPixelStride; } } } break; } } private void computeRectFloat(RasterAccessor src1, RasterAccessor src2, RasterAccessor dst) { int s1LineStride = src1.getScanlineStride(); int s1PixelStride = src1.getPixelStride(); int[] s1BandOffsets = src1.getBandOffsets(); float[][] s1Data = src1.getFloatDataArrays(); int s2LineStride = src2.getScanlineStride(); int s2PixelStride = src2.getPixelStride(); int[] s2BandOffsets = src2.getBandOffsets(); float[][] s2Data = src2.getFloatDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int bands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); float[][] dData = dst.getFloatDataArrays(); for (int b = 0, s1b = 0, s2b = 0; b < bands; b++, s1b += s1bd, s2b += s2bd) { float[] s1 = s1Data[s1b]; float[] s2 = s2Data[s2b]; float[] d = dData[b]; int s1LineOffset = s1BandOffsets[s1b]; int s2LineOffset = s2BandOffsets[s2b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int s1PixelOffset = s1LineOffset; int s2PixelOffset = s2LineOffset; int dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { d[dPixelOffset] = s1[s1PixelOffset] + s2[s2PixelOffset]; s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; dPixelOffset += dPixelStride; } } } } private void computeRectDouble(RasterAccessor src1, RasterAccessor src2, RasterAccessor dst) { int s1LineStride = src1.getScanlineStride(); int s1PixelStride = src1.getPixelStride(); int[] s1BandOffsets = src1.getBandOffsets(); double[][] s1Data = src1.getDoubleDataArrays(); int s2LineStride = src2.getScanlineStride(); int s2PixelStride = src2.getPixelStride(); int[] s2BandOffsets = src2.getBandOffsets(); double[][] s2Data = src2.getDoubleDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int bands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); double[][] dData = dst.getDoubleDataArrays(); for (int b = 0, s1b = 0, s2b = 0; b < bands; b++, s1b += s1bd, s2b += s2bd) { double[] s1 = s1Data[s1b]; double[] s2 = s2Data[s2b]; double[] d = dData[b]; int s1LineOffset = s1BandOffsets[s1b]; int s2LineOffset = s2BandOffsets[s2b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int s1PixelOffset = s1LineOffset; int s2PixelOffset = s2LineOffset; int dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { d[dPixelOffset] = s1[s1PixelOffset] + s2[s2PixelOffset]; s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; dPixelOffset += dPixelStride; } } } } // public static void main(String args[]) { // System.out.println("AddOpImage Test"); // ImageLayout layout; // OpImage src1, src2, dst; // Rectangle rect = new Rectangle(0, 0, 5, 5); // System.out.println("1. PixelInterleaved byte 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 800, 800, 0, 0, 200, 200, DataBuffer.TYPE_BYTE, 3, false); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new AddOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("2. Banded byte 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 800, 800, 0, 0, 200, 200, DataBuffer.TYPE_BYTE, 3, true); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new AddOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("3. PixelInterleaved int 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_INT, 3, false); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new AddOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("4. Banded int 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_INT, 3, true); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new AddOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("5. PixelInterleaved float 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_FLOAT, 3, false); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new AddOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("6. Banded float 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_FLOAT, 3, true); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new AddOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("7. PixelInterleaved double 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_DOUBLE, 3, false); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new AddOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("8. Banded double 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_DOUBLE, 3, true); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new AddOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/BandSelectCRIF.java0000644000175000017500000000303310203035544026542 0ustar mathieumathieu/* * $RCSfile: BandSelectCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:15 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "BandSelect" operation in the * rendered and renderable image layers. * * @see javax.media.jai.operator.BandSelectDescriptor * @see BandSelectOpImage * * * @since EA2 */ public class BandSelectCRIF extends CRIFImpl { /** Constructor. */ public BandSelectCRIF() { super("bandselect"); } /** * Creates a new instance of BandSelectOpImage * in the rendered layer. * * @param args The source image and the constants. * @param hints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new BandSelectOpImage(args.getRenderedSource(0), renderHints, layout, (int[])args.getObjectParameter(0)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/FilteredSubsampleRIF.java0000644000175000017500000000601510203035544030050 0ustar mathieumathieu/* * $RCSfile: FilteredSubsampleRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:27 $ * $State: Exp $ */package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.DataBuffer; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.renderable.RenderedImageFactory; import java.awt.image.renderable.ParameterBlock; import java.util.Map; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationNearest; import javax.media.jai.InterpolationBilinear; import javax.media.jai.InterpolationBicubic; import javax.media.jai.InterpolationBicubic2; import javax.media.jai.JAI; /** *

    Class implementing the RIF interface for the FilteredSubsample * operator. An instance of this class should be registered with the * OperationRegistry with operation name "FilteredSubsample." */ public class FilteredSubsampleRIF implements RenderedImageFactory { /**

    Default constructor (there is no input). */ public FilteredSubsampleRIF() {} /** *

    Creates a new instance of SubsampleOpImage in the rendered layer. * This method satisfies the implementation of RIF. * * @param paramBlock The source image, the X and Y scale factors. * @param renderHints RenderingHints. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { RenderedImage source = paramBlock.getRenderedSource(0); BorderExtender extender = renderHints == null ? null : (BorderExtender)renderHints.get(JAI.KEY_BORDER_EXTENDER); ImageLayout layout = renderHints == null ? null : (ImageLayout)renderHints.get(JAI.KEY_IMAGE_LAYOUT); int scaleX = paramBlock.getIntParameter(0); int scaleY = paramBlock.getIntParameter(1); float [] qsFilter = (float [])paramBlock.getObjectParameter(2); Interpolation interp = (Interpolation)paramBlock.getObjectParameter(3); // check if binary and interpolation type allowed SampleModel sm = source.getSampleModel(); int dataType = sm.getDataType(); // Determine the interpolation type, if not supported throw exception boolean validInterp = (interp instanceof InterpolationNearest) || (interp instanceof InterpolationBilinear) || (interp instanceof InterpolationBicubic) || (interp instanceof InterpolationBicubic2); if (!validInterp) throw new IllegalArgumentException( JaiI18N.getString("FilteredSubsample3")); return new FilteredSubsampleOpImage(source, extender, (Map)renderHints, layout, scaleX, scaleY, qsFilter, interp); } // create } // FilteredSubsampleRIF jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/DivideOpImage.java0000644000175000017500000007424510203035544026555 0ustar mathieumathieu/* * $RCSfile: DivideOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:24 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PointOpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.RasterFactory; import java.util.Map; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.util.JDKWorkarounds; /// import com.sun.media.jai.test.OpImageTester; /** * An OpImage implementing the "Divide" operation as * described in javax.media.jai.operator.DivideDescriptor. * *

    This OpImage divides the pixel values of the first source * image by the second source image pixels on a per-band basis. In case * the two source images have different number of bands, the number of * bands for the destination image is the smaller of the number of bands * of the two source images. That is * dstNumBands = Math.min(src1NumBands, src2NumBands). * In case the two source images have different data types, the data type * for the destination image is the bigger data type of the two source * images. * *

    The value of the pixel (x, y) in the destination image is defined as: *

     * for (b = 0; b < numBands; b++) {
     *     dst[y][x][b] = src1[y][x][b] / src2[y][x][b];
     * }
     * 
    * *

    If the result of the division overflows/underflows the * maximum/minimum value supported by the destination image, then it * will be clamped to the maximum/minimum value respectively. The * data type byte is treated as unsigned, with maximum * value as 255 and minimum value as 0. * * @since EA2 * @see javax.media.jai.operator.DivideDescriptor * @see DivideCRIF * */ final class DivideOpImage extends PointOpImage { private byte[][] divideTableByte; /* Source 1 band increment */ private int s1bd = 1; /* Source 2 band increment */ private int s2bd = 1; /** * Constructs an DivideOpImage. * *

    The layout parameter may optionally contains the * tile grid layout, sample model, and/or color model. The image * dimension is determined by the intersection of the bounding boxes * of the two source images. * *

    The image layout of the first source image, source1, * is used as the fall-back for the image layout of the destination * image. Any layout parameters not specified in the layout * argument are set to the same value as that of source1. * * @param source1 The first source image. * @param source2 The second source image. * @param layout The destination image layout. */ public DivideOpImage(RenderedImage source1, RenderedImage source2, Map config, ImageLayout layout) { super(source1, source2, layout, config, true); // Get the source band counts. int numBands1 = source1.getSampleModel().getNumBands(); int numBands2 = source2.getSampleModel().getNumBands(); // Handle the special case of dividing each band of an N-band // image by a 1-band image. int numBandsDst; if(layout != null && layout.isValid(ImageLayout.SAMPLE_MODEL_MASK)) { SampleModel sm = layout.getSampleModel(null); numBandsDst = sm.getNumBands(); // The second source must be single-banded and the first must // be multi-banded. if(numBandsDst > 1 && ((numBands1 > 1 && numBands2 == 1) || (numBands1 == 1 && numBands2 > 1))) { // Clamp the destination band count to the number of // bands in the multi-band source. numBandsDst = Math.min(Math.max(numBands1, numBands2), numBandsDst); // Create a new SampleModel if necessary. if(numBandsDst != sampleModel.getNumBands()) { sampleModel = RasterFactory.createComponentSampleModel( sm, sampleModel.getTransferType(), sampleModel.getWidth(), sampleModel.getHeight(), numBandsDst); if(colorModel != null && !JDKWorkarounds.areCompatibleDataModels(sampleModel, colorModel)) { colorModel = ImageUtil.getCompatibleColorModel(sampleModel, config); } } // Set the source band increments. s1bd = numBands1 == 1 ? 0 : 1; s2bd = numBands2 == 1 ? 0 : 1; } } if (sampleModel.getTransferType() == DataBuffer.TYPE_BYTE) { /* Initialize divideTableByte. */ divideTableByte = new byte[256][256]; for (int j = 0; j < 256; j++) { byte[] array = divideTableByte[j]; if (j > 0) { array[0] = (byte)255; } else { array[0] = 0; } for (int i = 1; i < 256; i++) { array[i] = ImageUtil.clampRoundByte((float)j/(float)i); } } } // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * Divides the pixel values of two source images within a specified * rectangle. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); /* For PointOpImage, srcRect = destRect. */ RasterAccessor s1 = new RasterAccessor(sources[0], destRect, formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor s2 = new RasterAccessor(sources[1], destRect, formatTags[1], getSourceImage(1).getColorModel()); RasterAccessor d = new RasterAccessor(dest, destRect, formatTags[2], getColorModel()); if(d.isBinary()) { byte[] dstBits = d.getBinaryDataArray(); // Subtraction in this case boils down to copying image 1. System.arraycopy(s1.getBinaryDataArray(), 0, dstBits, 0, dstBits.length); d.copyBinaryDataToRaster(); return; } int src1LineStride = s1.getScanlineStride(); int src1PixelStride = s1.getPixelStride(); int[] src1BandOffsets = s1.getBandOffsets(); int src2LineStride = s2.getScanlineStride(); int src2PixelStride = s2.getPixelStride(); int[] src2BandOffsets = s2.getBandOffsets(); int dstNumBands = d.getNumBands(); int dstWidth = d.getWidth(); int dstHeight = d.getHeight(); int dstLineStride = d.getScanlineStride(); int dstPixelStride = d.getPixelStride(); int[] dstBandOffsets = d.getBandOffsets(); switch (d.getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(dstNumBands, dstWidth, dstHeight, src1LineStride, src1PixelStride, src1BandOffsets, s1.getByteDataArrays(), src2LineStride, src2PixelStride, src2BandOffsets, s2.getByteDataArrays(), dstLineStride, dstPixelStride, dstBandOffsets, d.getByteDataArrays()); break; case DataBuffer.TYPE_USHORT: ushortLoop(dstNumBands, dstWidth, dstHeight, src1LineStride, src1PixelStride, src1BandOffsets, s1.getShortDataArrays(), src2LineStride, src2PixelStride, src2BandOffsets, s2.getShortDataArrays(), dstLineStride, dstPixelStride, dstBandOffsets, d.getShortDataArrays()); break; case DataBuffer.TYPE_SHORT: shortLoop(dstNumBands, dstWidth, dstHeight, src1LineStride, src1PixelStride, src1BandOffsets, s1.getShortDataArrays(), src2LineStride, src2PixelStride, src2BandOffsets, s2.getShortDataArrays(), dstLineStride, dstPixelStride, dstBandOffsets, d.getShortDataArrays()); break; case DataBuffer.TYPE_INT: intLoop(dstNumBands, dstWidth, dstHeight, src1LineStride, src1PixelStride, src1BandOffsets, s1.getIntDataArrays(), src2LineStride, src2PixelStride, src2BandOffsets, s2.getIntDataArrays(), dstLineStride, dstPixelStride, dstBandOffsets, d.getIntDataArrays()); break; case DataBuffer.TYPE_FLOAT: floatLoop(dstNumBands, dstWidth, dstHeight, src1LineStride, src1PixelStride, src1BandOffsets, s1.getFloatDataArrays(), src2LineStride, src2PixelStride, src2BandOffsets, s2.getFloatDataArrays(), dstLineStride, dstPixelStride, dstBandOffsets, d.getFloatDataArrays()); break; case DataBuffer.TYPE_DOUBLE: doubleLoop(dstNumBands, dstWidth, dstHeight, src1LineStride, src1PixelStride, src1BandOffsets, s1.getDoubleDataArrays(), src2LineStride, src2PixelStride, src2BandOffsets, s2.getDoubleDataArrays(), dstLineStride, dstPixelStride, dstBandOffsets, d.getDoubleDataArrays()); break; } if (d.needsClamping()) { d.clampDataArrays(); } d.copyDataToRaster(); } private void byteLoop(int dstNumBands, int dstWidth, int dstHeight, int src1LineStride, int src1PixelStride, int[] src1BandOffsets, byte[][] src1Data, int src2LineStride, int src2PixelStride, int[] src2BandOffsets, byte[][] src2Data, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, byte[][] dstData) { for (int b = 0, s1b = 0, s2b = 0; b < dstNumBands; b++, s1b += s1bd, s2b += s2bd) { byte[] s1 = src1Data[s1b]; byte[] s2 = src2Data[s2b]; byte[] d = dstData[b]; int src1LineOffset = src1BandOffsets[s1b]; int src2LineOffset = src2BandOffsets[s2b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int src1PixelOffset = src1LineOffset; int src2PixelOffset = src2LineOffset; int dstPixelOffset = dstLineOffset; src1LineOffset += src1LineStride; src2LineOffset += src2LineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = divideTableByte[s1[src1PixelOffset] & 0xFF][s2[src2PixelOffset]&0xFF]; src1PixelOffset += src1PixelStride; src2PixelOffset += src2PixelStride; dstPixelOffset += dstPixelStride; } } } } private void ushortLoop(int dstNumBands, int dstWidth, int dstHeight, int src1LineStride, int src1PixelStride, int[] src1BandOffsets, short[][] src1Data, int src2LineStride, int src2PixelStride, int[] src2BandOffsets, short[][] src2Data, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, short[][] dstData) { for (int b = 0, s1b = 0, s2b = 0; b < dstNumBands; b++, s1b += s1bd, s2b += s2bd) { short[] s1 = src1Data[s1b]; short[] s2 = src2Data[s2b]; short[] d = dstData[b]; float f1, f2; int src1LineOffset = src1BandOffsets[s1b]; int src2LineOffset = src2BandOffsets[s2b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int src1PixelOffset = src1LineOffset; int src2PixelOffset = src2LineOffset; int dstPixelOffset = dstLineOffset; src1LineOffset += src1LineStride; src2LineOffset += src2LineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { f1 = (float)(s1[src1PixelOffset] & 0xffff); f2 = (float)(s2[src2PixelOffset] & 0xffff); if (f1 == 0) { // 0 divided by any value is defined to return 0 d[dstPixelOffset] = 0; } else if (f2 == 0) { // Anything other than 0 divided by zero, returns // the max value d[dstPixelOffset] = (short)0xffff; } else { d[dstPixelOffset] = ImageUtil.clampRoundUShort(f1/f2); } src1PixelOffset += src1PixelStride; src2PixelOffset += src2PixelStride; dstPixelOffset += dstPixelStride; } } } } private void shortLoop(int dstNumBands, int dstWidth, int dstHeight, int src1LineStride, int src1PixelStride, int[] src1BandOffsets, short[][] src1Data, int src2LineStride, int src2PixelStride, int[] src2BandOffsets, short[][] src2Data, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, short[][] dstData) { for (int b = 0, s1b = 0, s2b = 0; b < dstNumBands; b++, s1b += s1bd, s2b += s2bd) { short[] s1 = src1Data[s1b]; short[] s2 = src2Data[s2b]; short[] d = dstData[b]; float f1, f2; int src1LineOffset = src1BandOffsets[s1b]; int src2LineOffset = src2BandOffsets[s2b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int src1PixelOffset = src1LineOffset; int src2PixelOffset = src2LineOffset; int dstPixelOffset = dstLineOffset; src1LineOffset += src1LineStride; src2LineOffset += src2LineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { f1 = (float)s1[src1PixelOffset]; f2 = (float)s2[src2PixelOffset]; if (f1 == 0) { // 0 divided by any value is defined to return 0 d[dstPixelOffset] = 0; } else if (f2 == 0) { if ( f1 < 0 ) { d[dstPixelOffset] = Short.MIN_VALUE; } else { d[dstPixelOffset] = Short.MAX_VALUE; } } else { d[dstPixelOffset] = ImageUtil.clampRoundShort(f1/f2); } src1PixelOffset += src1PixelStride; src2PixelOffset += src2PixelStride; dstPixelOffset += dstPixelStride; } } } } private void intLoop(int dstNumBands, int dstWidth, int dstHeight, int src1LineStride, int src1PixelStride, int[] src1BandOffsets, int[][] src1Data, int src2LineStride, int src2PixelStride, int[] src2BandOffsets, int[][] src2Data, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, int[][] dstData) { /* * The destination data type may be any of the integral data types. * The "clamp" function must clamp to the appropriate range for * that data type. */ switch (sampleModel.getTransferType()) { case DataBuffer.TYPE_BYTE: for (int b = 0, s1b = 0, s2b = 0; b < dstNumBands; b++, s1b += s1bd, s2b += s2bd) { int[] s1 = src1Data[s1b]; int[] s2 = src2Data[s2b]; int[] d = dstData[b]; float f1, f2; int src1LineOffset = src1BandOffsets[s1b]; int src2LineOffset = src2BandOffsets[s2b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int src1PixelOffset = src1LineOffset; int src2PixelOffset = src2LineOffset; int dstPixelOffset = dstLineOffset; src1LineOffset += src1LineStride; src2LineOffset += src2LineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { f1 = (float)(s1[src1PixelOffset] & 0xFF); f2 = (float)(s2[src2PixelOffset] & 0xFF); if ( f1 == 0 ) { // since bytes are unsigned, lowest value is 0 d[dstPixelOffset] = 0; } else if (f2 == 0) { // +ve no divided by 0 = datatype's max value d[dstPixelOffset] = 255; } else { d[dstPixelOffset] = ImageUtil.clampRoundByte(f1/f2); } src1PixelOffset += src1PixelStride; src2PixelOffset += src2PixelStride; dstPixelOffset += dstPixelStride; } } } break; case DataBuffer.TYPE_USHORT: for (int b = 0, s1b = 0, s2b = 0; b < dstNumBands; b++, s1b += s1bd, s2b += s2bd) { int[] s1 = src1Data[s1b]; int[] s2 = src2Data[s2b]; int[] d = dstData[b]; float f1, f2; int src1LineOffset = src1BandOffsets[s1b]; int src2LineOffset = src2BandOffsets[s2b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int src1PixelOffset = src1LineOffset; int src2PixelOffset = src2LineOffset; int dstPixelOffset = dstLineOffset; src1LineOffset += src1LineStride; src2LineOffset += src2LineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { f1 = (float)(s1[src1PixelOffset] & 0xFFFF); f2 = (float)(s2[src2PixelOffset] & 0xFFFF); if (f1 == 0) { // The minimum value for ushort is 0 d[dstPixelOffset] = 0; } else if (f2 == 0) { // +ve no divided by 0 = datatype's max value d[dstPixelOffset] = (short) 0xffff; } else { d[dstPixelOffset] = ImageUtil.clampRoundUShort(f1/f2); } src1PixelOffset += src1PixelStride; src2PixelOffset += src2PixelStride; dstPixelOffset += dstPixelStride; } } } break; case DataBuffer.TYPE_SHORT: for (int b = 0, s1b = 0, s2b = 0; b < dstNumBands; b++, s1b += s1bd, s2b += s2bd) { int[] s1 = src1Data[s1b]; int[] s2 = src2Data[s2b]; int[] d = dstData[b]; float f1, f2; int src1LineOffset = src1BandOffsets[s1b]; int src2LineOffset = src2BandOffsets[s2b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int src1PixelOffset = src1LineOffset; int src2PixelOffset = src2LineOffset; int dstPixelOffset = dstLineOffset; src1LineOffset += src1LineStride; src2LineOffset += src2LineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { f1 = (float)s1[src1PixelOffset]; f2 = (float)s2[src2PixelOffset]; if (f1 == 0 ) { d[dstPixelOffset] = 0; } else if (f2 == 0) { if ( f1 < 0 ) { // 0 divided by 0 is defined to return 0 // -ve no divided by 0 = datatype's min value d[dstPixelOffset] = Short.MIN_VALUE; } else { d[dstPixelOffset] = Short.MAX_VALUE; } } else { d[dstPixelOffset] = ImageUtil.clampRoundShort(f1/f2); } src1PixelOffset += src1PixelStride; src2PixelOffset += src2PixelStride; dstPixelOffset += dstPixelStride; } } } break; case DataBuffer.TYPE_INT: for (int b = 0, s1b = 0, s2b = 0; b < dstNumBands; b++, s1b += s1bd, s2b += s2bd) { int[] s1 = src1Data[s1b]; int[] s2 = src2Data[s2b]; int[] d = dstData[b]; float f1, f2; int src1LineOffset = src1BandOffsets[s1b]; int src2LineOffset = src2BandOffsets[s2b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int src1PixelOffset = src1LineOffset; int src2PixelOffset = src2LineOffset; int dstPixelOffset = dstLineOffset; src1LineOffset += src1LineStride; src2LineOffset += src2LineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { f1 = (float)s1[src1PixelOffset]; f2 = (float)s2[src2PixelOffset]; if (f1 == 0) { // 0 divided by 0 or any number is defined to return 0 d[dstPixelOffset] = 0; } else if (f2 == 0) { if ( f1 < 0 ) { d[dstPixelOffset] = Integer.MIN_VALUE; } else { d[dstPixelOffset] = Integer.MAX_VALUE; } } else { d[dstPixelOffset] = ImageUtil.clampRoundInt(f1/f2); } src1PixelOffset += src1PixelStride; src2PixelOffset += src2PixelStride; dstPixelOffset += dstPixelStride; } } } break; } } private void floatLoop(int dstNumBands, int dstWidth, int dstHeight, int src1LineStride, int src1PixelStride, int[] src1BandOffsets, float[][] src1Data, int src2LineStride, int src2PixelStride, int[] src2BandOffsets, float[][] src2Data, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, float[][] dstData) { for (int b = 0, s1b = 0, s2b = 0; b < dstNumBands; b++, s1b += s1bd, s2b += s2bd) { float[] s1 = src1Data[s1b]; float[] s2 = src2Data[s2b]; float[] d = dstData[b]; int src1LineOffset = src1BandOffsets[s1b]; int src2LineOffset = src2BandOffsets[s2b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int src1PixelOffset = src1LineOffset; int src2PixelOffset = src2LineOffset; int dstPixelOffset = dstLineOffset; src1LineOffset += src1LineStride; src2LineOffset += src2LineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { // follows IEEE-754 standard d[dstPixelOffset] = s1[src1PixelOffset] / s2[src2PixelOffset]; src1PixelOffset += src1PixelStride; src2PixelOffset += src2PixelStride; dstPixelOffset += dstPixelStride; } } } } private void doubleLoop(int dstNumBands, int dstWidth, int dstHeight, int src1LineStride, int src1PixelStride, int[] src1BandOffsets, double[][] src1Data, int src2LineStride, int src2PixelStride, int[] src2BandOffsets, double[][] src2Data, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, double[][] dstData) { for (int b = 0, s1b = 0, s2b = 0; b < dstNumBands; b++, s1b += s1bd, s2b += s2bd) { double[] s1 = src1Data[s1b]; double[] s2 = src2Data[s2b]; double[] d = dstData[b]; int src1LineOffset = src1BandOffsets[s1b]; int src2LineOffset = src2BandOffsets[s2b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int src1PixelOffset = src1LineOffset; int src2PixelOffset = src2LineOffset; int dstPixelOffset = dstLineOffset; src1LineOffset += src1LineStride; src2LineOffset += src2LineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { // follows IEEE-754 standard d[dstPixelOffset] = s1[src1PixelOffset] / s2[src2PixelOffset]; src1PixelOffset += src1PixelStride; src2PixelOffset += src2PixelStride; dstPixelOffset += dstPixelStride; } } } } // public static void main(String args[]) { // System.out.println("DivideOpImage Test"); // ImageLayout layout; // OpImage src1, src2, dst; // Rectangle rect = new Rectangle(0, 0, 5, 5); // System.out.println("1. PixelInterleaved byte 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 800, 800, 0, 0, 200, 200, DataBuffer.TYPE_BYTE, 3, false); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new DivideOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("2. Banded byte 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 800, 800, 0, 0, 200, 200, DataBuffer.TYPE_BYTE, 3, true); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new DivideOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("3. PixelInterleaved int 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_INT, 3, false); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new DivideOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("4. Banded int 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_INT, 3, true); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new DivideOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("5. PixelInterleaved float 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_FLOAT, 3, false); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new DivideOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("6. Banded float 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_FLOAT, 3, true); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new DivideOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("7. PixelInterleaved double 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_DOUBLE, 3, false); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new DivideOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("8. Banded double 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_DOUBLE, 3, true); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new DivideOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/BorderRIF.java0000644000175000017500000000500510203035544025651 0ustar mathieumathieu/* * $RCSfile: BorderRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:16 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.JAI; import java.util.Map; import javax.media.jai.operator.BorderDescriptor; /** * A RIF supporting the "border" operation in the * rendered image layer. * * @see java.awt.image.renderable.RenderedImageFactory * @see javax.media.jai.operator.BorderDescriptor * @see BorderOpImage * */ public class BorderRIF implements RenderedImageFactory { /** Constructor. */ public BorderRIF() {} /** * Creates a new instance of BorderOpImage * in the rendered layer. * * @param args The source image and the border information * @param hints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); RenderedImage source = args.getRenderedSource(0); int leftPad = args.getIntParameter(0); int rightPad = args.getIntParameter(1); int topPad = args.getIntParameter(2); int bottomPad = args.getIntParameter(3); BorderExtender type = (BorderExtender)args.getObjectParameter(4); if (type == BorderExtender.createInstance(BorderExtender.BORDER_WRAP)) { int minX = source.getMinX() - leftPad; int minY = source.getMinY() - topPad; int width = source.getWidth() + leftPad + rightPad; int height = source.getHeight() + topPad + bottomPad; return new PatternOpImage(source.getData(), source.getColorModel(), minX, minY, width, height); } else { return new BorderOpImage(source, renderHints, layout, leftPad, rightPad, topPad, bottomPad, type); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/DivideIntoConstCRIF.java0000644000175000017500000000306610203035544027611 0ustar mathieumathieu/* * $RCSfile: DivideIntoConstCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:23 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "DivideIntoConst" operation in * the rendered and renderable image layers. * * @see javax.media.jai.operator.DivideIntoConstDescriptor * @see DivideIntoConstOpImage * * * @since EA2 */ public class DivideIntoConstCRIF extends CRIFImpl { /** Constructor. */ public DivideIntoConstCRIF() { super("divideintoconst"); } /** * Creates a new instance of DivideIntoConstOpImage * in the rendered layer. * * @param args The source image and the constants. * @param hints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new DivideIntoConstOpImage(args.getRenderedSource(0), renderHints, layout, (double[])args.getObjectParameter(0)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/FileStoreRIF.java0000644000175000017500000001076610343724236026352 0ustar mathieumathieu/* * $RCSfile: FileStoreRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-12-02 01:51:26 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import com.sun.media.jai.codec.ImageEncodeParam; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import java.io.BufferedOutputStream; import java.io.FileOutputStream; import java.io.FileNotFoundException; import java.io.RandomAccessFile; import java.io.IOException; import java.io.OutputStream; import javax.media.jai.JAI; import javax.media.jai.OperationRegistry; import javax.media.jai.PlanarImage; import javax.media.jai.RenderedImageAdapter; import javax.media.jai.registry.RIFRegistry; import javax.media.jai.util.ImagingListener; import com.sun.media.jai.codec.SeekableOutputStream; import com.sun.media.jai.util.ImageUtil; /** * @see javax.media.jai.operator.FileDescriptor * * @since EA4 * */ public class FileStoreRIF implements RenderedImageFactory { /** The default file format. */ private static String DEFAULT_FORMAT = "tiff"; /** Constructor. */ public FileStoreRIF() {} /* * Private class which merely adds a finalize() method to close * the associated stream. */ private class FileStoreImage extends RenderedImageAdapter { private OutputStream stream; /* * Create the object and cache the stream. */ public FileStoreImage(RenderedImage image, OutputStream stream) { super(image); this.stream = stream; } /* * Close the stream. */ public void dispose() { try { stream.close(); } catch(IOException e) { // Ignore it ... } super.dispose(); } } /** * Stores an image to a file. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { ImagingListener listener = ImageUtil.getImagingListener(renderHints); // Retrieve the file path. String fileName = (String)paramBlock.getObjectParameter(0); // Retrieve the file format preference. String format = (String)paramBlock.getObjectParameter(1); // TODO: If format is null get format name from file extension. // If the format is still null use the default format. if(format == null) { format = DEFAULT_FORMAT; } // Retrieve the ImageEncodeParam (which may be null). ImageEncodeParam param = null; if(paramBlock.getNumParameters() > 2) { param = (ImageEncodeParam)paramBlock.getObjectParameter(2); } // Create a FileOutputStream from the file name. OutputStream stream = null; try { if(param == null) { // Use a BufferedOutputStream for greater efficiency // since no compression is occurring. stream = new BufferedOutputStream(new FileOutputStream(fileName)); } else { // Use SeekableOutputStream to avoid temp cache file // in case of compression. stream = new SeekableOutputStream(new RandomAccessFile(fileName, "rw")); } } catch (FileNotFoundException e) { String message = JaiI18N.getString("FileLoadRIF0") + fileName; listener.errorOccurred(message, e, this, false); // e.printStackTrace(); return null; } catch (SecurityException e) { String message = JaiI18N.getString("FileStoreRIF0"); listener.errorOccurred(message, e, this, false); // e.printStackTrace(); return null; } // Add the operation to the DAG. ParameterBlock pb = new ParameterBlock(); pb.addSource(paramBlock.getSource(0)); pb.add(stream).add(format).add(param); // Get the default registry. OperationRegistry registry = (renderHints == null) ? null : (OperationRegistry)renderHints.get(JAI.KEY_OPERATION_REGISTRY); PlanarImage im = new FileStoreImage(RIFRegistry.create (registry, "encode", pb, renderHints), stream); return im; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/CodecRIFUtil.java0000644000175000017500000001243610444643225026325 0ustar mathieumathieu/* * $RCSfile: CodecRIFUtil.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2006-06-17 00:02:28 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import java.io.IOException; import javax.media.jai.ImageLayout; import javax.media.jai.JAI; import javax.media.jai.OpImage; import javax.media.jai.TileCache; import javax.media.jai.util.ImagingException; import javax.media.jai.util.ImagingListener; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.ImageDecoder; import com.sun.media.jai.codec.ImageDecodeParam; import com.sun.media.jai.codec.SeekableStream; import com.sun.media.jai.util.DisposableNullOpImage; import com.sun.media.jai.util.ImageUtil; public class CodecRIFUtil { private CodecRIFUtil() {} public static RenderedImage create(String type, ParameterBlock paramBlock, RenderingHints renderHints) { ImagingListener listener = ImageUtil.getImagingListener(renderHints); SeekableStream source = (SeekableStream)paramBlock.getObjectParameter(0); ImageDecodeParam param = null; if (paramBlock.getNumParameters() > 1) { param = (ImageDecodeParam)paramBlock.getObjectParameter(1); } int page = 0; if (paramBlock.getNumParameters() > 2) { page = paramBlock.getIntParameter(2); } ImageDecoder dec = ImageCodec.createImageDecoder(type, source, param); try { int bound = OpImage.OP_IO_BOUND; ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); if (renderHints != null) { RenderingHints.Key key; key = JAI.KEY_OPERATION_BOUND; if (renderHints.containsKey(key)) { bound = ((Integer)renderHints.get(key)).intValue(); } } // Set flag indicating that a recovery may be attempted if // an OutOfMemoryError occurs during the decodeAsRenderedImage() // call - which is only possible if the stream can seek backwards. boolean canAttemptRecovery = source.canSeekBackwards(); // Save the stream position prior to decodeAsRenderedImage(). long streamPosition = Long.MIN_VALUE; if(canAttemptRecovery) { try { streamPosition = source.getFilePointer(); } catch(IOException ioe) { listener.errorOccurred(JaiI18N.getString("StreamRIF1"), ioe, CodecRIFUtil.class, false); // Unset the recovery attempt flag but otherwise // ignore the exception. canAttemptRecovery = false; } } OpImage image = null; try { // Attempt to create an OpImage from the decoder image. image = new DisposableNullOpImage(dec.decodeAsRenderedImage(page), layout, renderHints, bound); } catch(OutOfMemoryError memoryError) { // Ran out of memory - may be due to the decoder being // obliged to read the entire image when it creates the // RenderedImage it returns. if(canAttemptRecovery) { // First flush the cache if one is defined. TileCache cache = image != null ? image.getTileCache() : RIFUtil.getTileCacheHint(renderHints); if(cache != null) { cache.flush(); } // Force garbage collection. System.gc(); //slow // Reposition the stream before the previous decoding. source.seek(streamPosition); // Retry image decoding. image = new DisposableNullOpImage(dec.decodeAsRenderedImage(page), layout, renderHints, bound); } else { // Re-throw the error. String message = JaiI18N.getString("CodecRIFUtil0"); listener.errorOccurred(message, new ImagingException(message, memoryError), CodecRIFUtil.class, false); // throw memoryError; } } return image; } catch (Exception e) { listener.errorOccurred(JaiI18N.getString("CodecRIFUtil1"), e, CodecRIFUtil.class, false); // e.printStackTrace(); return null; } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ImageFunctionOpImage.java0000644000175000017500000001422110203035544030065 0ustar mathieumathieu/* * $RCSfile: ImageFunctionOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:29 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.lang.ref.SoftReference; import javax.media.jai.ImageFunction; import javax.media.jai.ImageLayout; import javax.media.jai.PlanarImage; import javax.media.jai.RasterFactory; import javax.media.jai.SourcelessOpImage; import java.util.Map; /** * An OpImage class to generate an image from a functional description. * * @see javax.media.jai.operator.ImageFunctionDescriptor * @see javax.media.jai.ImageFunction * @since EA4 */ final class ImageFunctionOpImage extends SourcelessOpImage { /** The functional description of the image. */ protected ImageFunction function; /** The X scale factor. */ protected float xScale; /** The Y scale factor. */ protected float yScale; /** The X translation. */ protected float xTrans; /** The Y translation. */ protected float yTrans; private static SampleModel sampleModelHelper(int numBands, ImageLayout layout) { SampleModel sampleModel; if (layout!= null && layout.isValid(ImageLayout.SAMPLE_MODEL_MASK)) { sampleModel = layout.getSampleModel(null); if (sampleModel.getNumBands() != numBands) { throw new RuntimeException(JaiI18N.getString("ImageFunctionRIF0")); } } else { // Create a SampleModel. // Use a dummy width and height, OpImage will fix them sampleModel = RasterFactory.createBandedSampleModel( DataBuffer.TYPE_FLOAT, 1, 1, numBands); } return sampleModel; } /** * Constructs an ImageFunctionOpImage. * * @param width The output image width. * @param height The output image height. */ public ImageFunctionOpImage(ImageFunction function, int minX, int minY, int width, int height, float xScale, float yScale, float xTrans, float yTrans, Map config, ImageLayout layout) { super(layout, config, sampleModelHelper(function.getNumElements()* (function.isComplex() ? 2 : 1), layout), minX, minY, width, height); // Cache the parameters. this.function = function; this.xScale = xScale; this.yScale = yScale; this.xTrans = xTrans; this.yTrans = yTrans; } /** * Compute a Rectangle of output data based on the ImageFunction. * Note that the sources parameter is not used. */ protected void computeRect(PlanarImage[] sources, WritableRaster dest, Rectangle destRect) { // Cache some info. int dataType = sampleModel.getTransferType(); int numBands = sampleModel.getNumBands(); // Allocate the actual data memory. int length = width*height; Object data; if (dataType == DataBuffer.TYPE_DOUBLE) { data = function.isComplex() ? (Object)new double[2][length] : (Object)new double[length]; } else { data = function.isComplex() ? (Object)new float[2][length] : (Object)new float[length]; } if (dataType == DataBuffer.TYPE_DOUBLE) { double[] real = function.isComplex() ? ((double[][])data)[0] : ((double[])data); double[] imag = function.isComplex() ? ((double[][])data)[1] : null; int element = 0; for (int band = 0; band < numBands; band++) { function.getElements(xScale*(destRect.x - xTrans), yScale*(destRect.y - yTrans), xScale, yScale, destRect.width, destRect.height, element++, real, imag); dest.setSamples(destRect.x, destRect.y, destRect.width, destRect.height, band, (double[])real); if (function.isComplex()) { dest.setSamples(destRect.x, destRect.y, destRect.width, destRect.height, ++band, imag); } } // for (band ... } else { // not double precision float[] real = function.isComplex() ? ((float[][])data)[0] : ((float[])data); float[] imag = function.isComplex() ? ((float[][])data)[1] : null; int element = 0; for (int band = 0; band < numBands; band++) { function.getElements(xScale*(destRect.x - xTrans), yScale*(destRect.y - yTrans), xScale, yScale, destRect.width, destRect.height, element++, real, imag); dest.setSamples(destRect.x, destRect.y, destRect.width, destRect.height, band, real); if (function.isComplex()) { dest.setSamples(destRect.x, destRect.y, destRect.width, destRect.height, ++band, imag); } } // for (band ... } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ConvolveRIF.java0000644000175000017500000000554510203035544026240 0ustar mathieumathieu/* * $RCSfile: ConvolveRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:20 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; import java.util.Map; /** * @see ConvolveOpImage */ public class ConvolveRIF implements RenderedImageFactory { /** Constructor. */ public ConvolveRIF() {} /** * Create a new instance of ConvolveOpImage in the rendered layer. * This method satisfies the implementation of RIF. * * @param paramBlock The source image and the convolution kernel. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); // Get BorderExtender from renderHints if any. BorderExtender extender = RIFUtil.getBorderExtenderHint(renderHints); KernelJAI unRotatedKernel = (KernelJAI)paramBlock.getObjectParameter(0); KernelJAI kJAI = unRotatedKernel.getRotatedKernel(); int dataType = paramBlock.getRenderedSource(0).getSampleModel().getDataType(); boolean dataTypeOk = (dataType == DataBuffer.TYPE_BYTE || dataType == DataBuffer.TYPE_SHORT || dataType == DataBuffer.TYPE_INT); if (kJAI.getWidth() == 3 && kJAI.getHeight() == 3 && kJAI.getXOrigin() == 1 && kJAI.getYOrigin() == 1 && dataTypeOk) { return new Convolve3x3OpImage(paramBlock.getRenderedSource(0), extender, renderHints, layout, kJAI); } else if (kJAI.isSeparable()) { return new SeparableConvolveOpImage(paramBlock.getRenderedSource(0), extender, renderHints, layout, kJAI); } else { return new ConvolveOpImage(paramBlock.getRenderedSource(0), extender, renderHints, layout, kJAI); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/IDCTCRIF.java0000644000175000017500000000246410203035544025270 0ustar mathieumathieu/* * $RCSfile: IDCTCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:28 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "IDCT" operation in the rendered * image layer. * * @since Beta * @see javax.media.jai.operator.IDCTDescriptor * */ public class IDCTCRIF extends CRIFImpl { /** Constructor. */ public IDCTCRIF() { super("idct"); } /** * Creates a new instance of an IDCT operator. * * @param paramBlock The scaling type. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); RenderedImage source = paramBlock.getRenderedSource(0); return new DCTOpImage(source, renderHints, layout, new FCT(false, 2)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MagnitudePhaseOpImage.java0000644000175000017500000007756110203035544030253 0ustar mathieumathieu/* * $RCSfile: MagnitudePhaseOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:31 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.PlanarImage; import javax.media.jai.PointOpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.RasterFactory; import java.util.Map; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.util.JDKWorkarounds; /// XXX Testing /// import java.awt.Point; /// import javax.media.jai.TiledImage; /** * An OpImage implementing magnitude, magnitude squared, * and phase operations as described in * javax.media.jai.operator.MagnitudeDescriptor, * javax.media.jai.operator.MagnitudeSquaredDescriptor, and * javax.media.jai.operator.PhaseDescriptor * *

    This implementation assumes that the number of bands in the source image * is at least two. The number of bands in the destination image is clamped to * half (in the integer division sense) of the number of bands in the source * image. * * @since EA3 * * @see javax.media.jai.UntiledOpImage * @see javax.media.jai.operator.MagnitudeDescriptor * @see javax.media.jai.operator.MagnitudeSquaredDescriptor * @see javax.media.jai.operator.PhaseDescriptor * */ final class MagnitudePhaseOpImage extends PointOpImage { /** A flag indicating a magnitude or modulus operation. */ public static final int MAGNITUDE = 1; /** A flag indicating a magnitude squared or "power spectrum" operation. */ public static final int MAGNITUDE_SQUARED = 2; /** A flag indicating a phase operation. */ public static final int PHASE = 3; /** The type of operation as specified by one of the static final types. */ protected int operationType; /** The gain to be applied to the phase. */ private double phaseGain = 1.0; /** The bias to be applied to the phase. */ private double phaseBias = 0.0; /** * Constructs a MagnitudePhaseOpImage object. * *

    The tile grid layout, SampleModel, and ColorModel may optionally * be specified by an ImageLayout object. * * @param source A RenderedImage. * @param layout An ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param operationType One of the static final flag values defined in * this class which indicates the type of operation to perform. */ public MagnitudePhaseOpImage(RenderedImage source, Map config, ImageLayout layout, int operationType) { super(source, layout, config, true); // Cache the parameter. this.operationType = operationType; // Initialize the SampleModel flag. boolean needNewSampleModel = false; // Reset the data type to that specified by the layout if it // has been modified within the superclass constructor chain. int dataType = sampleModel.getTransferType(); if(layout != null && dataType != layout.getSampleModel(source).getTransferType()) { dataType = layout.getSampleModel(source).getTransferType(); needNewSampleModel = true; } // Force the band count to be at most half that of the source image. int numBands = sampleModel.getNumBands(); if(numBands > source.getSampleModel().getNumBands()/2) { numBands = source.getSampleModel().getNumBands()/2; needNewSampleModel = true; } // Create a new SampleModel for the destination. if(needNewSampleModel) { sampleModel = RasterFactory.createComponentSampleModel(sampleModel, dataType, sampleModel.getWidth(), sampleModel.getHeight(), numBands); if(colorModel != null && !JDKWorkarounds.areCompatibleDataModels(sampleModel, colorModel)) { colorModel = ImageUtil.getCompatibleColorModel(sampleModel, config); } } if(operationType == PHASE) { // Set phase gain and bias as a function of destination data type. switch(dataType) { case DataBuffer.TYPE_BYTE: phaseGain = 255.0/(2.0*Math.PI); phaseBias = Math.PI; break; case DataBuffer.TYPE_SHORT: phaseGain = Short.MAX_VALUE/(2.0*Math.PI); phaseBias = Math.PI; break; case DataBuffer.TYPE_USHORT: phaseGain = (Short.MAX_VALUE - Short.MIN_VALUE)/(2.0*Math.PI); phaseBias = Math.PI; break; case DataBuffer.TYPE_INT: phaseGain = Integer.MAX_VALUE/(2.0*Math.PI); phaseBias = Math.PI; break; default: // A floating point type: do nothing. } } } /* * Calculate the magnitude [squared] or phase of the source image. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); // Construct RasterAccessors. RasterAccessor srcAccessor = new RasterAccessor(sources[0], destRect, formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor dstAccessor = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); // Branch to the method appropriate to the accessor data type. switch(dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(srcAccessor, dstAccessor, destRect.height, destRect.width); break; case DataBuffer.TYPE_SHORT: computeRectShort(srcAccessor, dstAccessor, destRect.height, destRect.width); break; case DataBuffer.TYPE_USHORT: computeRectUShort(srcAccessor, dstAccessor, destRect.height, destRect.width); break; case DataBuffer.TYPE_INT: computeRectInt(srcAccessor, dstAccessor, destRect.height, destRect.width); break; case DataBuffer.TYPE_FLOAT: computeRectFloat(srcAccessor, dstAccessor, destRect.height, destRect.width); break; case DataBuffer.TYPE_DOUBLE: computeRectDouble(srcAccessor, dstAccessor, destRect.height, destRect.width); break; default: // NB: This statement should be unreachable. throw new RuntimeException(JaiI18N.getString("MagnitudePhaseOpImage0")); } if (dstAccessor.needsClamping()) { dstAccessor.clampDataArrays(); } // Make sure that the output data is copied to the destination. dstAccessor.copyDataToRaster(); } private void computeRectDouble(RasterAccessor srcAccessor, RasterAccessor dstAccessor, int numRows, int numCols) { // Set pixel and line strides. int srcPixelStride = srcAccessor.getPixelStride(); int srcScanlineStride = srcAccessor.getScanlineStride(); int dstPixelStride = dstAccessor.getPixelStride(); int dstScanlineStride = dstAccessor.getScanlineStride(); // Loop over the destination bands. int numDstBands = sampleModel.getNumBands(); for(int dstBand = 0; dstBand < numDstBands; dstBand++) { // Set source band indices. int srcBandReal = 2*dstBand; int srcBandImag = srcBandReal + 1; // Get the source and destination arrays for this band. double[] srcReal = srcAccessor.getDoubleDataArray(srcBandReal); double[] srcImag = srcAccessor.getDoubleDataArray(srcBandImag); double[] dstData = dstAccessor.getDoubleDataArray(dstBand); // Initialize the data offsets for this band. int srcOffsetReal = srcAccessor.getBandOffset(srcBandReal); int srcOffsetImag = srcAccessor.getBandOffset(srcBandImag); int dstOffset = dstAccessor.getBandOffset(dstBand); // Initialize the line offsets for looping. int srcLineReal = srcOffsetReal; int srcLineImag = srcOffsetImag; int dstLine = dstOffset; for(int row = 0; row < numRows; row++) { // Initialize pixel offsets for this row. int srcPixelReal = srcLineReal; int srcPixelImag = srcLineImag; int dstPixel = dstLine; // Switch per line based on operation type. switch(operationType) { case MAGNITUDE: for(int col = 0; col < numCols; col++) { double real = srcReal[srcPixelReal]; double imag = srcImag[srcPixelImag]; dstData[dstPixel] = Math.sqrt(real*real+imag*imag); srcPixelReal += srcPixelStride; srcPixelImag += srcPixelStride; dstPixel += dstPixelStride; } break; case MAGNITUDE_SQUARED: for(int col = 0; col < numCols; col++) { double real = srcReal[srcPixelReal]; double imag = srcImag[srcPixelImag]; dstData[dstPixel] = real*real+imag*imag; srcPixelReal += srcPixelStride; srcPixelImag += srcPixelStride; dstPixel += dstPixelStride; } break; case PHASE: for(int col = 0; col < numCols; col++) { double real = srcReal[srcPixelReal]; double imag = srcImag[srcPixelImag]; dstData[dstPixel] = Math.atan2(imag, real); srcPixelReal += srcPixelStride; srcPixelImag += srcPixelStride; dstPixel += dstPixelStride; } break; } // Increment the line offsets. srcLineReal += srcScanlineStride; srcLineImag += srcScanlineStride; dstLine += dstScanlineStride; } } } private void computeRectFloat(RasterAccessor srcAccessor, RasterAccessor dstAccessor, int numRows, int numCols) { // Set pixel and line strides. int srcPixelStride = srcAccessor.getPixelStride(); int srcScanlineStride = srcAccessor.getScanlineStride(); int dstPixelStride = dstAccessor.getPixelStride(); int dstScanlineStride = dstAccessor.getScanlineStride(); // Loop over the destination bands. int numDstBands = sampleModel.getNumBands(); for(int dstBand = 0; dstBand < numDstBands; dstBand++) { // Set source band indices. int srcBandReal = 2*dstBand; int srcBandImag = srcBandReal + 1; // Get the source and destination arrays for this band. float[] srcReal = srcAccessor.getFloatDataArray(srcBandReal); float[] srcImag = srcAccessor.getFloatDataArray(srcBandImag); float[] dstData = dstAccessor.getFloatDataArray(dstBand); // Initialize the data offsets for this band. int srcOffsetReal = srcAccessor.getBandOffset(srcBandReal); int srcOffsetImag = srcAccessor.getBandOffset(srcBandImag); int dstOffset = dstAccessor.getBandOffset(dstBand); // Initialize the line offsets for looping. int srcLineReal = srcOffsetReal; int srcLineImag = srcOffsetImag; int dstLine = dstOffset; for(int row = 0; row < numRows; row++) { // Initialize pixel offsets for this row. int srcPixelReal = srcLineReal; int srcPixelImag = srcLineImag; int dstPixel = dstLine; // Switch per line based on operation type. switch(operationType) { case MAGNITUDE: for(int col = 0; col < numCols; col++) { float real = srcReal[srcPixelReal]; float imag = srcImag[srcPixelImag]; dstData[dstPixel] = ImageUtil.clampFloat(Math.sqrt(real*real+imag*imag)); srcPixelReal += srcPixelStride; srcPixelImag += srcPixelStride; dstPixel += dstPixelStride; } break; case MAGNITUDE_SQUARED: for(int col = 0; col < numCols; col++) { float real = srcReal[srcPixelReal]; float imag = srcImag[srcPixelImag]; dstData[dstPixel] = real*real+imag*imag; srcPixelReal += srcPixelStride; srcPixelImag += srcPixelStride; dstPixel += dstPixelStride; } break; case PHASE: for(int col = 0; col < numCols; col++) { float real = srcReal[srcPixelReal]; float imag = srcImag[srcPixelImag]; dstData[dstPixel] = ImageUtil.clampFloat(Math.atan2(imag, real)); srcPixelReal += srcPixelStride; srcPixelImag += srcPixelStride; dstPixel += dstPixelStride; } break; } // Increment the line offsets. srcLineReal += srcScanlineStride; srcLineImag += srcScanlineStride; dstLine += dstScanlineStride; } } } private void computeRectInt(RasterAccessor srcAccessor, RasterAccessor dstAccessor, int numRows, int numCols) { // Set pixel and line strides. int srcPixelStride = srcAccessor.getPixelStride(); int srcScanlineStride = srcAccessor.getScanlineStride(); int dstPixelStride = dstAccessor.getPixelStride(); int dstScanlineStride = dstAccessor.getScanlineStride(); // Loop over the destination bands. int numDstBands = sampleModel.getNumBands(); for(int dstBand = 0; dstBand < numDstBands; dstBand++) { // Set source band indices. int srcBandReal = 2*dstBand; int srcBandImag = srcBandReal + 1; // Get the source and destination arrays for this band. int[] srcReal = srcAccessor.getIntDataArray(srcBandReal); int[] srcImag = srcAccessor.getIntDataArray(srcBandImag); int[] dstData = dstAccessor.getIntDataArray(dstBand); // Initialize the data offsets for this band. int srcOffsetReal = srcAccessor.getBandOffset(srcBandReal); int srcOffsetImag = srcAccessor.getBandOffset(srcBandImag); int dstOffset = dstAccessor.getBandOffset(dstBand); // Initialize the line offsets for looping. int srcLineReal = srcOffsetReal; int srcLineImag = srcOffsetImag; int dstLine = dstOffset; for(int row = 0; row < numRows; row++) { // Initialize pixel offsets for this row. int srcPixelReal = srcLineReal; int srcPixelImag = srcLineImag; int dstPixel = dstLine; // Switch per line based on operation type. switch(operationType) { case MAGNITUDE: for(int col = 0; col < numCols; col++) { int real = srcReal[srcPixelReal]; int imag = srcImag[srcPixelImag]; dstData[dstPixel] = ImageUtil.clampRoundInt(Math.sqrt(real*real+imag*imag)); srcPixelReal += srcPixelStride; srcPixelImag += srcPixelStride; dstPixel += dstPixelStride; } break; case MAGNITUDE_SQUARED: for(int col = 0; col < numCols; col++) { int real = srcReal[srcPixelReal]; int imag = srcImag[srcPixelImag]; dstData[dstPixel] = real*real+imag*imag; srcPixelReal += srcPixelStride; srcPixelImag += srcPixelStride; dstPixel += dstPixelStride; } break; case PHASE: for(int col = 0; col < numCols; col++) { int real = srcReal[srcPixelReal]; int imag = srcImag[srcPixelImag]; dstData[dstPixel] = ImageUtil.clampRoundInt((Math.atan2(imag, real) + phaseBias)*phaseGain); srcPixelReal += srcPixelStride; srcPixelImag += srcPixelStride; dstPixel += dstPixelStride; } break; } // Increment the line offsets. srcLineReal += srcScanlineStride; srcLineImag += srcScanlineStride; dstLine += dstScanlineStride; } } } private void computeRectUShort(RasterAccessor srcAccessor, RasterAccessor dstAccessor, int numRows, int numCols) { // Set pixel and line strides. int srcPixelStride = srcAccessor.getPixelStride(); int srcScanlineStride = srcAccessor.getScanlineStride(); int dstPixelStride = dstAccessor.getPixelStride(); int dstScanlineStride = dstAccessor.getScanlineStride(); // Loop over the destination bands. int numDstBands = sampleModel.getNumBands(); for(int dstBand = 0; dstBand < numDstBands; dstBand++) { // Set source band indices. int srcBandReal = 2*dstBand; int srcBandImag = srcBandReal + 1; // Get the source and destination arrays for this band. short[] srcReal = srcAccessor.getShortDataArray(srcBandReal); short[] srcImag = srcAccessor.getShortDataArray(srcBandImag); short[] dstData = dstAccessor.getShortDataArray(dstBand); // Initialize the data offsets for this band. int srcOffsetReal = srcAccessor.getBandOffset(srcBandReal); int srcOffsetImag = srcAccessor.getBandOffset(srcBandImag); int dstOffset = dstAccessor.getBandOffset(dstBand); // Initialize the line offsets for looping. int srcLineReal = srcOffsetReal; int srcLineImag = srcOffsetImag; int dstLine = dstOffset; for(int row = 0; row < numRows; row++) { // Initialize pixel offsets for this row. int srcPixelReal = srcLineReal; int srcPixelImag = srcLineImag; int dstPixel = dstLine; // Switch per line based on operation type. switch(operationType) { case MAGNITUDE: for(int col = 0; col < numCols; col++) { int real = srcReal[srcPixelReal]&0xffff; int imag = srcImag[srcPixelImag]&0xffff; dstData[dstPixel] = ImageUtil.clampRoundUShort(Math.sqrt(real*real+imag*imag)); srcPixelReal += srcPixelStride; srcPixelImag += srcPixelStride; dstPixel += dstPixelStride; } break; case MAGNITUDE_SQUARED: for(int col = 0; col < numCols; col++) { int real = srcReal[srcPixelReal]&0xffff; int imag = srcImag[srcPixelImag]&0xffff; dstData[dstPixel] = ImageUtil.clampUShort(real*real+imag*imag); srcPixelReal += srcPixelStride; srcPixelImag += srcPixelStride; dstPixel += dstPixelStride; } break; case PHASE: for(int col = 0; col < numCols; col++) { int real = srcReal[srcPixelReal]&0xffff; int imag = srcImag[srcPixelImag]&0xffff; dstData[dstPixel] = ImageUtil.clampRoundUShort((Math.atan2(imag, real) + phaseBias)*phaseGain); srcPixelReal += srcPixelStride; srcPixelImag += srcPixelStride; dstPixel += dstPixelStride; } break; } // Increment the line offsets. srcLineReal += srcScanlineStride; srcLineImag += srcScanlineStride; dstLine += dstScanlineStride; } } } private void computeRectShort(RasterAccessor srcAccessor, RasterAccessor dstAccessor, int numRows, int numCols) { // Set pixel and line strides. int srcPixelStride = srcAccessor.getPixelStride(); int srcScanlineStride = srcAccessor.getScanlineStride(); int dstPixelStride = dstAccessor.getPixelStride(); int dstScanlineStride = dstAccessor.getScanlineStride(); // Loop over the destination bands. int numDstBands = sampleModel.getNumBands(); for(int dstBand = 0; dstBand < numDstBands; dstBand++) { // Set source band indices. int srcBandReal = 2*dstBand; int srcBandImag = srcBandReal + 1; // Get the source and destination arrays for this band. short[] srcReal = srcAccessor.getShortDataArray(srcBandReal); short[] srcImag = srcAccessor.getShortDataArray(srcBandImag); short[] dstData = dstAccessor.getShortDataArray(dstBand); // Initialize the data offsets for this band. int srcOffsetReal = srcAccessor.getBandOffset(srcBandReal); int srcOffsetImag = srcAccessor.getBandOffset(srcBandImag); int dstOffset = dstAccessor.getBandOffset(dstBand); // Initialize the line offsets for looping. int srcLineReal = srcOffsetReal; int srcLineImag = srcOffsetImag; int dstLine = dstOffset; for(int row = 0; row < numRows; row++) { // Initialize pixel offsets for this row. int srcPixelReal = srcLineReal; int srcPixelImag = srcLineImag; int dstPixel = dstLine; // Switch per line based on operation type. switch(operationType) { case MAGNITUDE: for(int col = 0; col < numCols; col++) { short real = srcReal[srcPixelReal]; short imag = srcImag[srcPixelImag]; dstData[dstPixel] = ImageUtil.clampRoundShort(Math.sqrt(real*real+imag*imag)); srcPixelReal += srcPixelStride; srcPixelImag += srcPixelStride; dstPixel += dstPixelStride; } break; case MAGNITUDE_SQUARED: for(int col = 0; col < numCols; col++) { short real = srcReal[srcPixelReal]; short imag = srcImag[srcPixelImag]; dstData[dstPixel] = ImageUtil.clampShort(real*real+imag*imag); srcPixelReal += srcPixelStride; srcPixelImag += srcPixelStride; dstPixel += dstPixelStride; } break; case PHASE: for(int col = 0; col < numCols; col++) { short real = srcReal[srcPixelReal]; short imag = srcImag[srcPixelImag]; dstData[dstPixel] = ImageUtil.clampRoundShort((Math.atan2(imag, real) + phaseBias)*phaseGain); srcPixelReal += srcPixelStride; srcPixelImag += srcPixelStride; dstPixel += dstPixelStride; } break; } // Increment the line offsets. srcLineReal += srcScanlineStride; srcLineImag += srcScanlineStride; dstLine += dstScanlineStride; } } } private void computeRectByte(RasterAccessor srcAccessor, RasterAccessor dstAccessor, int numRows, int numCols) { // Set pixel and line strides. int srcPixelStride = srcAccessor.getPixelStride(); int srcScanlineStride = srcAccessor.getScanlineStride(); int dstPixelStride = dstAccessor.getPixelStride(); int dstScanlineStride = dstAccessor.getScanlineStride(); // Loop over the destination bands. int numDstBands = sampleModel.getNumBands(); for(int dstBand = 0; dstBand < numDstBands; dstBand++) { // Set source band indices. int srcBandReal = 2*dstBand; int srcBandImag = srcBandReal + 1; // Get the source and destination arrays for this band. byte[] srcReal = srcAccessor.getByteDataArray(srcBandReal); byte[] srcImag = srcAccessor.getByteDataArray(srcBandImag); byte[] dstData = dstAccessor.getByteDataArray(dstBand); // Initialize the data offsets for this band. int srcOffsetReal = srcAccessor.getBandOffset(srcBandReal); int srcOffsetImag = srcAccessor.getBandOffset(srcBandImag); int dstOffset = dstAccessor.getBandOffset(dstBand); // Initialize the line offsets for looping. int srcLineReal = srcOffsetReal; int srcLineImag = srcOffsetImag; int dstLine = dstOffset; for(int row = 0; row < numRows; row++) { // Initialize pixel offsets for this row. int srcPixelReal = srcLineReal; int srcPixelImag = srcLineImag; int dstPixel = dstLine; // Switch per line based on operation type. switch(operationType) { case MAGNITUDE: for(int col = 0; col < numCols; col++) { int real = srcReal[srcPixelReal]&0xff; int imag = srcImag[srcPixelImag]&0xff; dstData[dstPixel] = ImageUtil.clampRoundByte(Math.sqrt(real*real+imag*imag)); srcPixelReal += srcPixelStride; srcPixelImag += srcPixelStride; dstPixel += dstPixelStride; } break; case MAGNITUDE_SQUARED: for(int col = 0; col < numCols; col++) { int real = srcReal[srcPixelReal]&0xff; int imag = srcImag[srcPixelImag]&0xff; dstData[dstPixel] = ImageUtil.clampByte(real*real+imag*imag); srcPixelReal += srcPixelStride; srcPixelImag += srcPixelStride; dstPixel += dstPixelStride; } break; case PHASE: for(int col = 0; col < numCols; col++) { int real = srcReal[srcPixelReal]&0xff; int imag = srcImag[srcPixelImag]&0xff; dstData[dstPixel] = ImageUtil.clampRoundByte((Math.atan2(imag, real) + phaseBias)*phaseGain); srcPixelReal += srcPixelStride; srcPixelImag += srcPixelStride; dstPixel += dstPixelStride; } break; } // Increment the line offsets. srcLineReal += srcScanlineStride; srcLineImag += srcScanlineStride; dstLine += dstScanlineStride; } } } // public static void main(String args[]) { // int opType = args.length > 0 ? // Integer.valueOf(args[0]).intValue() : 1; // int dataType = args.length > 1 ? // Integer.valueOf(args[1]).intValue() : DataBuffer.TYPE_DOUBLE; // double[] pixels = new double[] { 1, 2, 3, 4, // 5, 6, 7, 8, // 9, 10, 11, 12, // 13, 14, 15, 16 }; // WritableRaster pattern = // RasterFactory.createBandedRaster(dataType, 2, 2, 4, // new Point(0, 0)); // pattern.setPixels(0, 0, 2, 2, pixels); // TiledImage src = // TiledImage.createInterleaved(0, 0, // pattern.getWidth(), pattern.getHeight(), // pattern.getSampleModel().getNumBands(), // pattern.getSampleModel().getTransferType(), // pattern.getWidth(), pattern.getHeight(), // new int[] {0, 1, 2, 3}); // src.setData(pattern); // MagnitudePhaseOpImage dst = // new MagnitudePhaseOpImage(src, null, opType); // pixels = dst.getData().getPixels(0, 0, 2, 2, (double[])null); // System.out.println(""); // for(int i = 0; i < pixels.length; i += 2) { // System.out.println(pixels[i] + " " + pixels[i+1]); // } // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MinFilterOpImage.java0000644000175000017500000002026010203035544027226 0ustar mathieumathieu/* * $RCSfile: MinFilterOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:35 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; import javax.media.jai.operator.MinFilterShape; // import com.sun.media.jai.test.OpImageTester; /** * An abstract OpImage class that subclasses will use to perform * MinFiltering with specific masks. * */ abstract class MinFilterOpImage extends AreaOpImage { protected MinFilterShape maskType; protected int maskSize; /** * Creates a MinFilterOpImage given an image source, an * optional BorderExtender, a maskType and maskSize. The image * dimensions are derived the source image. The tile grid layout, * SampleModel, and ColorModel may optionally be specified by an * ImageLayout object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param maskType the filter mask type. * @param maskSize the filter mask size. */ public MinFilterOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, MinFilterShape maskType, int maskSize) { super(source, layout, config, true, extender, (maskSize-1)/2, (maskSize-1)/2, (maskSize/2), (maskSize/2)); this.maskType = maskType; this.maskSize = maskSize; } /** * Performs min filtering on a specified rectangle. The sources are * cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); Raster source = sources[0]; Rectangle srcRect = mapDestRect(destRect, 0); RasterAccessor srcAccessor = new RasterAccessor(source, srcRect, formatTags[0], getSource(0).getColorModel()); RasterAccessor dstAccessor = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(srcAccessor, dstAccessor, maskSize); break; case DataBuffer.TYPE_SHORT: shortLoop(srcAccessor, dstAccessor, maskSize); break; case DataBuffer.TYPE_USHORT: ushortLoop(srcAccessor, dstAccessor, maskSize); break; case DataBuffer.TYPE_INT: intLoop(srcAccessor, dstAccessor, maskSize); break; case DataBuffer.TYPE_FLOAT: floatLoop(srcAccessor, dstAccessor, maskSize); break; case DataBuffer.TYPE_DOUBLE: doubleLoop(srcAccessor, dstAccessor, maskSize); break; } // If the RasterAccessor object set up a temporary buffer for the // op to write to, tell the RasterAccessor to write that data // to the raster no that we're done with it. if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } /** Performs min filtering using the subclass's mask on byte data */ protected abstract void byteLoop(RasterAccessor src, RasterAccessor dst, int filterSize); /** Performs min filtering using the subclass's mask on short data */ protected abstract void shortLoop(RasterAccessor src, RasterAccessor dst, int filterSize); /** Performs min filtering using the subclass's mask on ushort data */ protected abstract void ushortLoop(RasterAccessor src, RasterAccessor dst, int filterSize); /** Performs min filtering using the subclass's mask on int data */ protected abstract void intLoop(RasterAccessor src, RasterAccessor dst, int filterSize); /** Performs min filtering using the subclass's mask on float data */ protected abstract void floatLoop(RasterAccessor src, RasterAccessor dst, int filterSize); /** Performs min filtering using the subclass's mask on double data */ protected abstract void doubleLoop(RasterAccessor src, RasterAccessor dst, int filterSize); /** Returns the min of the input integer array */ static int minFilter(int data[]) { if (data.length == 3) { int a = data[0]; int b = data[1]; int c = data[2]; if (a < b) { return (a < c ? a : c); }else{ return (b < c ? b : c); } } int min = data[0]; for(int i = 1; i < data.length; i++){ if(data[i] < min) min = data[i]; } return min; } /** Returns the min of the input float array */ static float minFilterFloat(float data[]) { if (data.length == 3) { float a = data[0]; float b = data[1]; float c = data[2]; if (a < b) { return (a < c ? a : c); }else{ return (b < c ? b : c); } } float min = data[0]; for(int i = 1; i < data.length; i++){ if(data[i] < min) min = data[i]; } return min; } /** Returns the min of the input double array */ static double minFilterDouble(double data[]) { if (data.length == 3) { double a = data[0]; double b = data[1]; double c = data[2]; if (a < b) { return (a < c ? a : c); }else{ return (b < c ? b : c); } } double min = data[0]; for(int i = 1; i < data.length; i++){ if(data[i] < min) min = data[i]; } return min; } // // Calls a method on OpImage that uses introspection, to make this // // class, discover it's createTestImage() call, call it and then // // benchmark the performance of the created OpImage chain. // public static void main(String args[]) { // String classname = // "com.sun.media.jai.opimage.MinFilterSquareOpImage"; // OpImageTester.performDiagnostics(classname,args); // classname = // "com.sun.media.jai.opimage.MinFilterXOpImage"; // OpImageTester.performDiagnostics(classname,args); // classname = // "com.sun.media.jai.opimage.MinFilterPlusOpImage"; // OpImageTester.performDiagnostics(classname,args); // classname = // "com.sun.media.jai.opimage.MinFilterSeparableOpImage"; // OpImageTester.performDiagnostics(classname,args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/GradientRIF.java0000644000175000017500000000373710203035544026203 0ustar mathieumathieu/* * $RCSfile: GradientRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:27 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.JAI; import javax.media.jai.KernelJAI; import java.awt.image.SampleModel; import java.awt.image.DataBuffer; import java.util.Map; /** * @see GradientOpImage */ public class GradientRIF implements RenderedImageFactory { /** Constructor. */ public GradientRIF() {} /** * Create a new instance of GradientOpImage in the rendered layer. * This method satisfies the implementation of RIF. * * @param paramBlock The source image and the gradient's * horizontal kernel & vertical kernel. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); // Get BorderExtender from renderHints if any. BorderExtender extender = RIFUtil.getBorderExtenderHint(renderHints); RenderedImage source = paramBlock.getRenderedSource(0); // Get the Horizontal & Vertical kernels KernelJAI kern_h = (KernelJAI)paramBlock.getObjectParameter(0); KernelJAI kern_v = (KernelJAI)paramBlock.getObjectParameter(1); return new GradientOpImage(source, extender, renderHints, layout, kern_h, kern_v); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ColorQuantizerRIF.java0000644000175000017500000000657610203035544027433 0ustar mathieumathieu/* * $RCSfile: ColorQuantizerRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:17 $ * $State: Exp $ */package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.DataBuffer; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.renderable.RenderedImageFactory; import java.awt.image.renderable.ParameterBlock; import java.util.Map; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.JAI; import javax.media.jai.ROI; import javax.media.jai.operator.ColorQuantizerDescriptor; import javax.media.jai.operator.ColorQuantizerType; /** *

    Class implementing the RIF interface for the ColorQuantizer * operator. An instance of this class should be registered with the * OperationRegistry with operation name "ColorQuantizer." */ public class ColorQuantizerRIF implements RenderedImageFactory { /**

    Default constructor (there is no input). */ public ColorQuantizerRIF() {} /** *

    Creates a new instance of ColorQuantizerOpImage in the * rendered layer. This method satisfies the implementation of RIF. * * @param paramBlock The source image, the color quantization algorithm * name, the maximum number of colors, the * parameter for training (the histogram size for * median-cut, the cycle for neuquant, and maximum tree * size for oct-tree), and the ROI. * @param renderHints RenderingHints. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { RenderedImage source = paramBlock.getRenderedSource(0); ImageLayout layout = renderHints == null ? null : (ImageLayout)renderHints.get(JAI.KEY_IMAGE_LAYOUT); ColorQuantizerType algorithm = (ColorQuantizerType)paramBlock.getObjectParameter(0); int maxColorNum = paramBlock.getIntParameter(1); int upperBound = paramBlock.getIntParameter(2); ROI roi= (ROI)paramBlock.getObjectParameter(3); int xPeriod = paramBlock.getIntParameter(4); int yPeriod = paramBlock.getIntParameter(5); // check if 3-band byte-type image SampleModel sm = source.getSampleModel(); if (sm.getNumBands() != 3 && sm.getDataType() == DataBuffer.TYPE_BYTE) throw new IllegalArgumentException("ColorQuantizerRIF0"); if (algorithm.equals(ColorQuantizerDescriptor.NEUQUANT)) return new NeuQuantOpImage(source, (Map)renderHints, layout, maxColorNum, upperBound, roi, xPeriod, yPeriod); if (algorithm.equals(ColorQuantizerDescriptor.OCTTREE)) return new OctTreeOpImage(source, (Map)renderHints, layout, maxColorNum, upperBound, roi, xPeriod, yPeriod); else return new MedianCutOpImage(source, (Map)renderHints, layout, maxColorNum, upperBound, roi, xPeriod, yPeriod); } // create } // ColorQuantizerRIF jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/TIFFRIF.java0000644000175000017500000000257210203035544025172 0ustar mathieumathieu/* * $RCSfile: TIFFRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:45 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import java.io.InputStream; import javax.media.jai.JAI; import javax.media.jai.NullOpImage; import javax.media.jai.OpImage; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.ImageDecoder; import com.sun.media.jai.codec.TIFFDecodeParam; import com.sun.media.jai.codec.SeekableStream; /** * @since EA2 */ public class TIFFRIF implements RenderedImageFactory { /** Constructor. */ public TIFFRIF() {} /** * Creates a RenderedImage representing the contents * of a TIFF-encoded image. * * @param paramBlock A ParameterBlock containing the TIFF * SeekableStream to read. * @param renderHints An instance of RenderingHints, * or null. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { return CodecRIFUtil.create("tiff", paramBlock, renderHints); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/PointMapperOpImage.java0000644000175000017500000000351310340447404027601 0ustar mathieumathieu/* * $RCSfile: PointMapperOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-11-21 22:49:40 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.geom.AffineTransform; import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.Point2D; import java.util.Map; import javax.media.jai.NullOpImage; import javax.media.jai.OpImage; import javax.media.jai.PlanarImage; /** * A class which merely wraps another PlanarImage but * uses a supplied AffineTransform object for point mapping. */ public class PointMapperOpImage extends NullOpImage { private AffineTransform transform; private AffineTransform inverseTransform; public PointMapperOpImage(PlanarImage source, Map configuration, AffineTransform transform) throws NoninvertibleTransformException { super(source, null, configuration, OP_COMPUTE_BOUND); if(transform == null) { throw new IllegalArgumentException("transform == null!"); } this.transform = transform; this.inverseTransform = transform.createInverse(); } public Point2D mapDestPoint(Point2D destPt, int sourceIndex) { if(sourceIndex != 0) { throw new IndexOutOfBoundsException("sourceIndex != 0!"); } return inverseTransform.transform(destPt, null); } public Point2D mapSourcePoint(Point2D sourcePt, int sourceIndex) { if(sourceIndex != 0) { throw new IndexOutOfBoundsException("sourceIndex != 0!"); } return inverseTransform.transform(sourcePt, null); } public synchronized void dispose() { getSourceImage(0).dispose(); super.dispose(); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MedianFilterSquareOpImage.java0000644000175000017500000003641010203035544031065 0ustar mathieumathieu/* * $RCSfile: MedianFilterSquareOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:34 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import java.util.Map; import javax.media.jai.operator.MedianFilterDescriptor; import com.sun.media.jai.opimage.MedianFilterOpImage; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class to perform median filtering on a source image. * * */ final class MedianFilterSquareOpImage extends MedianFilterOpImage { /** * Creates a MedianFilterSquareOpImage with the given source and * maskSize. The image dimensions are derived from the source * image. The tile grid layout, SampleModel, and ColorModel may * optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param maskSize the mask size. */ public MedianFilterSquareOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, int maskSize) { super(source, extender, config, layout, MedianFilterDescriptor.MEDIAN_MASK_SQUARE, maskSize); } protected void byteLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); byte dstDataArrays[][] = dst.getByteDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); byte srcDataArrays[][] = src.getByteDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int values[] = new int[filterSize*filterSize]; int wp = filterSize; for (int k = 0; k < dnumBands; k++) { byte dstData[] = dstDataArrays[k]; byte srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageVerticalOffset = srcPixelOffset; int valueCount = 0; for (int u = 0; u < wp; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < wp; v++) { values[valueCount++] = (int)(srcData[imageOffset])&0xff; imageOffset += srcPixelStride; } imageVerticalOffset += srcScanlineStride; } int val = medianFilter(values); dstData[dstPixelOffset] = (byte)val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void shortLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int values[] = new int[filterSize*filterSize]; int wp = filterSize; for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageVerticalOffset = srcPixelOffset; int valueCount = 0; for (int u = 0; u < wp; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < wp; v++) { values[valueCount++] = (int)(srcData[imageOffset]); imageOffset += srcPixelStride; } imageVerticalOffset += srcScanlineStride; } int val = medianFilter(values); dstData[dstPixelOffset] = (short)val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void ushortLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int values[] = new int[filterSize*filterSize]; int wp = filterSize; for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageVerticalOffset = srcPixelOffset; int valueCount = 0; for (int u = 0; u < wp; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < wp; v++) { values[valueCount++] = (int)(srcData[imageOffset])&0xffff; imageOffset += srcPixelStride; } imageVerticalOffset += srcScanlineStride; } int val = medianFilter(values); dstData[dstPixelOffset] = (short)val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void intLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); int dstDataArrays[][] = dst.getIntDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcDataArrays[][] = src.getIntDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int values[] = new int[filterSize*filterSize]; int wp = filterSize; for (int k = 0; k < dnumBands; k++) { int dstData[] = dstDataArrays[k]; int srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageVerticalOffset = srcPixelOffset; int valueCount = 0; for (int u = 0; u < wp; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < wp; v++) { values[valueCount++] = srcData[imageOffset]; imageOffset += srcPixelStride; } imageVerticalOffset += srcScanlineStride; } int val = medianFilter(values); dstData[dstPixelOffset] = val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void floatLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); float dstDataArrays[][] = dst.getFloatDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); float srcDataArrays[][] = src.getFloatDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); float values[] = new float[filterSize*filterSize]; int wp = filterSize; for (int k = 0; k < dnumBands; k++) { float dstData[] = dstDataArrays[k]; float srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageVerticalOffset = srcPixelOffset; int valueCount = 0; for (int u = 0; u < wp; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < wp; v++) { values[valueCount++] = srcData[imageOffset]; imageOffset += srcPixelStride; } imageVerticalOffset += srcScanlineStride; } float val = medianFilterFloat(values); dstData[dstPixelOffset] = val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void doubleLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); double dstDataArrays[][] = dst.getDoubleDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); double srcDataArrays[][] = src.getDoubleDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); double values[] = new double[filterSize*filterSize]; int wp = filterSize; for (int k = 0; k < dnumBands; k++) { double dstData[] = dstDataArrays[k]; double srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageVerticalOffset = srcPixelOffset; int valueCount = 0; for (int u = 0; u < wp; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < wp; v++) { values[valueCount++] = srcData[imageOffset]; imageOffset += srcPixelStride; } imageVerticalOffset += srcScanlineStride; } double val = medianFilterDouble(values); dstData[dstPixelOffset] = val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } // public static OpImage createTestImage(OpImageTester oit) { // return new MedianFilterSquareOpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource()), // 3); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/PeriodicShiftCRIF.java0000644000175000017500000000376710203035544027310 0ustar mathieumathieu/* * $RCSfile: PeriodicShiftCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:40 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.geom.Rectangle2D; import java.awt.geom.AffineTransform; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import java.awt.image.renderable.RenderableImageOp; import java.awt.image.renderable.RenderContext; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationNearest; import javax.media.jai.InterpolationBilinear; import javax.media.jai.InterpolationBicubic; import java.util.Map; /** * This image factory supports image operator PeriodicShiftOpImage * in the rendered and renderable image layers. * * @see PeriodicShiftOpImage */ public class PeriodicShiftCRIF extends CRIFImpl { /** Constructor. */ public PeriodicShiftCRIF() { super("periodicshift"); } /** * Creates a new instance of PeriodicShiftOpImage * in the rendered layer. This method satisfies the * implementation of RIF. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); // Get the source image. RenderedImage source = paramBlock.getRenderedSource(0); // Get the translation parameters. int shiftX = paramBlock.getIntParameter(0); int shiftY = paramBlock.getIntParameter(1); // Return the OpImage. return new PeriodicShiftOpImage(source, renderHints, layout, shiftX, shiftY); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/AddConstToCollectionOpImage.java0000644000175000017500000000363010203035544031355 0ustar mathieumathieu/* * $RCSfile: AddConstToCollectionOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:12 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.util.Collection; import java.util.Iterator; import java.util.Vector; import javax.media.jai.ImageLayout; import javax.media.jai.JAI; import javax.media.jai.CollectionImage; /** * An OpImage implementing the "AddConstToCollection" operation. * * @see javax.media.jai.operator.AddConstToCollectionDescriptor * @see AddConstToCollectionCIF * * * @since EA4 */ final class AddConstToCollectionOpImage extends CollectionImage { /** * Constructor. * * @param sourceCollection A collection of rendered images. * @param hints Optionally contains destination image layout. * @param constants The constants to be added, stored as reference. */ public AddConstToCollectionOpImage(Collection sourceCollection, RenderingHints hints, double[] constants) { /** * Try to create a new instance of the sourceCollection to be * used to store output images. If failed, use a Vector. */ try { imageCollection = (Collection)sourceCollection.getClass().newInstance(); } catch (Exception e) { imageCollection = new Vector(); } Iterator iter = sourceCollection.iterator(); while (iter.hasNext()) { ParameterBlock pb = new ParameterBlock(); pb.addSource(iter.next()); pb.add(constants); imageCollection.add(JAI.create("AddConst", pb, hints)); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/FFT.java0000644000175000017500000006062310203035544024521 0ustar mathieumathieu/* * $RCSfile: FFT.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:26 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.image.DataBuffer; import java.text.NumberFormat; import java.util.Arrays; import java.util.Locale; import javax.media.jai.operator.DFTDescriptor; import com.sun.media.jai.util.MathJAI; /** * The Fast Fourier Transform (FFT) class. * * @since EA3 */ public class FFT { /** * A flag indicating that the transform is not to be scaled. */ public static final int SCALING_NONE = DFTDescriptor.SCALING_NONE.getValue(); /** * A flag indicating that the transform is to be scaled by the square * root of the product of its dimensions. */ public static final int SCALING_UNITARY = DFTDescriptor.SCALING_UNITARY.getValue(); /** * A flag indicating that the transform is to be scaled by the product * of its dimensions. */ public static final int SCALING_DIMENSIONS = DFTDescriptor.SCALING_DIMENSIONS.getValue(); /** Initialization flag. */ protected boolean lengthIsSet = false; /** The sign of the exponential. */ protected int exponentSign; /** The type of scaling. */ protected int scaleType; /** The length of the FFT. */ protected int length; /** The number of bits required to represent the length. */ private int nbits; /** Indices to map between normal and bit reversed order. */ private int[] index; /** The scale factor. */ private double scaleFactor; /** Lookup table of cosines. */ private double[] wr; /** Lookup table of sines. */ private double[] wi; /** Lookup table of cosines for FCT. */ private double[] wrFCT; /** Lookup table of sines for FCT. */ private double[] wiFCT; /** Work array for real part. */ protected double[] real; /** Work array for imaginary part. */ protected double[] imag; /** * Construct a new FFT object. * * @param negatedExponent Whether the exponent is negated. * @param scaleType The type of scaling to be applied. * @param length The length of the FFT; must be a positive power of 2. */ public FFT(boolean negatedExponent, Integer scaleType, int length) { // Set the exponential sign. exponentSign = negatedExponent ? -1 : 1; // Set the scaling type. this.scaleType = scaleType.intValue(); // Set the sequence length and quantities dependent thereon. setLength(length); } /** * Initialize the length-dependent fields. * * @param length The length of the FFT; must be a positive power of 2. */ public void setLength(int length) { // Check whether it's necessary to continue. if(lengthIsSet && length == this.length) { return; } // Ensure that the length is a positive power of two. if(!MathJAI.isPositivePowerOf2(length)) { throw new RuntimeException(JaiI18N.getString("FFT0")); } // Cache the length. this.length = length; // Set the scale factor. if(scaleType == SCALING_NONE) { scaleFactor = 1.0; } else if(scaleType == SCALING_UNITARY) { scaleFactor = 1.0/Math.sqrt(length); } else if(scaleType == SCALING_DIMENSIONS) { scaleFactor = 1.0/length; } else { // NB: This statement should be unreachable if the scaling // type is properly verified in the operation descriptor. throw new RuntimeException(JaiI18N.getString("FFT1")); } // Calculate the number of bits required to represent the length. int power = 1; nbits = 0; while(power < length) { nbits++; power <<= 1; } // Initialize the bit-reversal LUT. initBitReversalLUT(); // Calculate lookup tables of the W values. calculateCoefficientLUTs(); // Allocate work buffer memory. if(!lengthIsSet || length > real.length) { real = new double[length]; imag = new double[length]; } // Set initialization flag. lengthIsSet = true; } /** * Initialize the bit-reversal lookup table. */ private void initBitReversalLUT() { // Calculate elements of index[] and fill. index = new int[length]; for(int i = 0; i < length; ++i) { int l = i; int power = length >> 1; int irev = 0; for(int k = 0; k < nbits; ++k) { int j = (l & 1); if( j != 0 ) { irev = irev + power; } l >>= 1; power >>= 1; index[i] = irev; } } } /** * Calculate the sine and cosine lookup tables. */ private void calculateCoefficientLUTs() { wr = new double[nbits]; wi = new double[nbits]; int inode = 1; double cons = exponentSign*Math.PI; for(int bit = 0; bit < nbits; bit++) { wr[bit] = Math.cos(cons/inode); wi[bit] = Math.sin(cons/inode); inode *= 2; } } /** * Calculate the FCT sine and cosine lookup tables. */ private void calculateFCTLUTs() { wrFCT = new double[length]; wiFCT = new double[length]; for(int i = 0; i < length; i++) { double factor = ((i == 0) ? Math.sqrt(1.0/length) : Math.sqrt(2.0/length)); double freq = Math.PI*i/(2.0*length); wrFCT[i] = factor*Math.cos(freq); wiFCT[i] = factor*Math.sin(freq); } } /** * Set the internal work data arrays of the FFT object. * * @param dataType The data type of the source data according to * one of the DataBuffer TYPE_* flags. This should be either * DataBuffer.TYPE_FLOAT or DataBuffer.TYPE_DOUBLE. * @param realArg Float or double array of real parts. * @param offsetReal Offset into the array of real parts. * @param strideReal The real array stride value. * @param imagArg Float or double array of imaginary parts. * @param offsetImag Offset into the array of imaginary parts. * @param strideImag The imaginary array stride value. * @param count The number of values to copy. */ public void setData(int dataType, Object realArg, int offsetReal, int strideReal, Object imagArg, int offsetImag, int strideImag, int count) { // Copy the parameter arrays. switch(dataType) { case DataBuffer.TYPE_FLOAT: { float[] realFloat = (float[])realArg; if(imagArg != null) { float[] imagFloat = (float[])imagArg; if(offsetReal == offsetImag && strideReal == strideImag) { for(int i = 0; i < count; i++) { real[i] = realFloat[offsetReal]; imag[i] = imagFloat[offsetReal]; offsetReal += strideReal; } } else { for(int i = 0; i < count; i++) { real[i] = realFloat[offsetReal]; imag[i] = imagFloat[offsetImag]; offsetReal += strideReal; offsetImag += strideImag; } } } else { for(int i = 0; i < count; i++) { real[i] = realFloat[offsetReal]; offsetReal += strideReal; } } } break; case DataBuffer.TYPE_DOUBLE: { double[] realDouble = (double[])realArg; if(strideReal == 1 && strideImag == 1) { System.arraycopy(realDouble, offsetReal, real, 0, count); if(imagArg != null) { System.arraycopy((double[])imagArg, offsetImag, imag, 0, count); } } else if(imagArg != null) { double[] imagDouble = (double[])imagArg; if(offsetReal == offsetImag && strideReal == strideImag) { for(int i = 0; i < count; i++) { real[i] = realDouble[offsetReal]; imag[i] = imagDouble[offsetReal]; offsetReal += strideReal; } } else { for(int i = 0; i < count; i++) { real[i] = realDouble[offsetReal]; imag[i] = imagDouble[offsetImag]; offsetReal += strideReal; offsetImag += strideImag; } } } else { for(int i = 0; i < count; i++) { real[i] = realDouble[offsetReal]; offsetReal += strideReal; } } } break; default: // NB: This statement should be unreachable as the destination // image is required to be a floating point type and the // RasterAccessor is supposed to promote the data type of // all rasters to the "minimum" data type of all source // and destination rasters involved. throw new RuntimeException(dataType + JaiI18N.getString("FFT2")); } // If fewer input than target points fill target with zeros. if(count < length) { Arrays.fill(real, count, length, 0.0); if(imagArg != null) { Arrays.fill(imag, count, length, 0.0); } } if(imagArg == null) { Arrays.fill(imag, 0, length, 0.0); } } /** * Get data from the internal work data arrays of the FFT object. * * @param dataType The data type of the source data according to * one of the DataBuffer TYPE_* flags. This should be either * DataBuffer.TYPE_FLOAT or DataBuffer.TYPE_DOUBLE. * @param realArg Float or double array of real parts. * @param offsetReal Offset into the array of real parts. * @param strideReal The real array stride value. * @param imagArg Float or double array of imaginary parts. * @param offsetImag Offset into the array of imaginary parts. * @param strideImag The imaginary array stride value. * @param count The number of values to copy. */ public void getData(int dataType, Object realArg, int offsetReal, int strideReal, Object imagArg, int offsetImag, int strideImag) { switch(dataType) { case DataBuffer.TYPE_FLOAT: { float[] realFloat = (float[])realArg; if(imagArg != null) { float[] imagFloat = (float[])imagArg; if(offsetReal == offsetImag && strideReal == strideImag) { for(int i = 0; i < length; i++) { int idx = index[i]; realFloat[offsetReal] = (float)this.real[idx]; imagFloat[offsetReal] = (float)this.imag[idx]; offsetReal += strideReal; } } else { for(int i = 0; i < length; i++) { int idx = index[i]; realFloat[offsetReal] = (float)this.real[idx]; imagFloat[offsetImag] = (float)this.imag[idx]; offsetReal += strideReal; offsetImag += strideImag; } } } else { // imagArg == null for(int i = 0; i < length; i++) { realFloat[offsetReal] = (float)this.real[index[i]]; offsetReal += strideReal; } } } break; case DataBuffer.TYPE_DOUBLE: { double[] realDouble = (double[])realArg; if(imagArg != null) { double[] imagDouble = (double[])imagArg; if(offsetReal == offsetImag && strideReal == strideImag) { for(int i = 0; i < length; i++) { int idx = index[i]; realDouble[offsetReal] = this.real[idx]; imagDouble[offsetReal] = this.imag[idx]; offsetReal += strideReal; } } else { for(int i = 0; i < length; i++) { int idx = index[i]; realDouble[offsetReal] = this.real[idx]; imagDouble[offsetImag] = this.imag[idx]; offsetReal += strideReal; offsetImag += strideImag; } } } else { // imagArg == null for(int i = 0; i < length; i++) { realDouble[offsetReal] = this.real[index[i]]; offsetReal += strideReal; } } } break; default: // NB: This statement should be unreachable as the destination // image is required to be a floating point type and the // RasterAccessor is supposed to promote the data type of // all rasters to the "minimum" data type of all source // and destination rasters involved. throw new RuntimeException(dataType + JaiI18N.getString("FFT2")); } } /** * Set the internal work data arrays of the FFT object for use with * an FCT operation. * * @param dataType The data type of the source data according to * one of the DataBuffer TYPE_* flags. This should be either * DataBuffer.TYPE_FLOAT or DataBuffer.TYPE_DOUBLE. * @param data The data as a float[] or double[]. * @param offset Offset into the data array. * @param stride The array stride value. * @param count The number of values to copy. */ public void setFCTData(int dataType, Object data, int offset, int stride, int count) { // Copy the parameter arrays. switch(dataType) { case DataBuffer.TYPE_FLOAT: { float[] realFloat = (float[])data; for(int i = 0; i < count; i++) { imag[i] = realFloat[offset]; offset += stride; } for(int i = count; i < length; i++) { imag[i] = 0.0; } int k = length - 1; int j = 0; for(int i = 0; i < k; i++) { real[i] = imag[j++]; real[k--] = imag[j++]; } } break; case DataBuffer.TYPE_DOUBLE: { double[] realDouble = (double[])data; for(int i = 0; i < count; i++) { imag[i] = realDouble[offset]; offset += stride; } for(int i = count; i < length; i++) { imag[i] = 0.0; } int k = length - 1; int j = 0; for(int i = 0; i < k; i++) { real[i] = imag[j++]; real[k--] = imag[j++]; } } break; default: // NB: This statement should be unreachable as the destination // image is required to be a floating point type and the // RasterAccessor is supposed to promote the data type of // all rasters to the "minimum" data type of all source // and destination rasters involved. throw new RuntimeException(dataType + JaiI18N.getString("FFT2")); } // Always clear imaginary part. Arrays.fill(imag, 0, length, 0.0); } /** * Get data from the internal work data arrays of the FFT object after * an IFCT operation. * * @param dataType The data type of the source data according to * one of the DataBuffer TYPE_* flags. This should be either * DataBuffer.TYPE_FLOAT or DataBuffer.TYPE_DOUBLE. * @param data The data as a float[] or double[]. * @param offset Offset into the data array. * @param stride The array stride value. * @param count The number of values to copy. */ public void getFCTData(int dataType, Object data, int offset, int stride) { if(wrFCT == null || wrFCT.length != length) { calculateFCTLUTs(); } switch(dataType) { case DataBuffer.TYPE_FLOAT: { float[] realFloat = (float[])data; for(int i = 0; i < length; i++) { int idx = index[i]; realFloat[offset] = (float)(wrFCT[i]*real[idx] + wiFCT[i]*imag[idx]); offset += stride; } } break; case DataBuffer.TYPE_DOUBLE: { double[] realDouble = (double[])data; for(int i = 0; i < length; i++) { int idx = index[i]; realDouble[offset] = wrFCT[i]*real[idx] + wiFCT[i]*imag[idx]; offset += stride; } } break; default: // NB: This statement should be unreachable as the destination // image is required to be a floating point type and the // RasterAccessor is supposed to promote the data type of // all rasters to the "minimum" data type of all source // and destination rasters involved. throw new RuntimeException(dataType + JaiI18N.getString("FFT2")); } } /** * Set the internal work data arrays of the FFT object for use with * an IFCT operation. * * @param dataType The data type of the source data according to * one of the DataBuffer TYPE_* flags. This should be either * DataBuffer.TYPE_FLOAT or DataBuffer.TYPE_DOUBLE. * @param data The data as a float[] or double[]. * @param offset Offset into the data array. * @param stride The array stride value. * @param count The number of values to copy. */ public void setIFCTData(int dataType, Object data, int offset, int stride, int count) { if(wrFCT == null || wrFCT.length != length) { calculateFCTLUTs(); } // Copy the parameter arrays. switch(dataType) { case DataBuffer.TYPE_FLOAT: { float[] realFloat = (float[])data; for(int i = 0; i < count; i++) { float r = realFloat[offset]; real[i] = r*wrFCT[i]; imag[i] = r*wiFCT[i]; offset += stride; } } break; case DataBuffer.TYPE_DOUBLE: { double[] realDouble = (double[])data; for(int i = 0; i < count; i++) { double r = realDouble[offset]; real[i] = r*wrFCT[i]; imag[i] = r*wiFCT[i]; offset += stride; } } break; default: // NB: This statement should be unreachable as the destination // image is required to be a floating point type and the // RasterAccessor is supposed to promote the data type of // all rasters to the "minimum" data type of all source // and destination rasters involved. throw new RuntimeException(dataType + JaiI18N.getString("FFT2")); } // If fewer input than target points fill target with zeros. if(count < length) { Arrays.fill(real, count, length, 0.0); Arrays.fill(imag, count, length, 0.0); } } /** * Get data from the internal work data arrays of the FFT object after * an IFCT operation. * * @param dataType The data type of the source data according to * one of the DataBuffer TYPE_* flags. This should be either * DataBuffer.TYPE_FLOAT or DataBuffer.TYPE_DOUBLE. * @param data The data as a float[] or double[]. * @param offset Offset into the data array. * @param stride The array stride value. * @param count The number of values to copy. */ public void getIFCTData(int dataType, Object data, int offset, int stride) { switch(dataType) { case DataBuffer.TYPE_FLOAT: { float[] realFloat = (float[])data; int k = length - 1; for(int i = 0; i < k; i++) { realFloat[offset] = (float)real[index[i]]; offset += stride; realFloat[offset] = (float)real[index[k--]]; offset += stride; } } break; case DataBuffer.TYPE_DOUBLE: { double[] realDouble = (double[])data; int k = length - 1; for(int i = 0; i < k; i++) { realDouble[offset] = (float)real[index[i]]; offset += stride; realDouble[offset] = (float)real[index[k--]]; offset += stride; } } break; default: // NB: This statement should be unreachable as the destination // image is required to be a floating point type and the // RasterAccessor is supposed to promote the data type of // all rasters to the "minimum" data type of all source // and destination rasters involved. throw new RuntimeException(dataType + JaiI18N.getString("FFT2")); } } /** * Calculate the DFT of a complex sequence using the FFT algorithm. */ public void transform() { int i, k, j, l; // Index variables Integer i18n = new Integer(length); NumberFormat numberFormatter = NumberFormat.getNumberInstance(Locale.getDefault()); if(real.length < length || imag.length < length) { throw new RuntimeException(numberFormatter.format(i18n) + JaiI18N.getString("FFT3")); } int inode = 1; int ipair; for(l = 0; l < nbits; ++l) { double cosp = 1.0; // initial w values double sinp = 0.0; ipair = 2 * inode; // calc pair separation for(k = 0; k < inode; ++k) {// sequence through array for(i = k; i < length; i += ipair) { j = i + inode; // calc other node index int iIndex = index[i]; int jIndex = index[j]; double rtemp = real[jIndex]*cosp - (imag[jIndex]*sinp); double itemp = imag[jIndex]*cosp + (real[jIndex]*sinp); real[jIndex] = real[iIndex] - rtemp; // calc butterfly imag[jIndex] = imag[iIndex] - itemp; real[iIndex] = real[iIndex] + rtemp; imag[iIndex] = imag[iIndex] + itemp; } double costmp = cosp; cosp = cosp * wr[l] - sinp * wi[l]; // update cosp, sinp sinp = costmp * wi[l] + sinp * wr[l]; } inode = inode * 2; // new nodal dist } if(scaleFactor != 1.0) { // multiply by non-unity scale factor for(i = 0; i < length; ++i) { real[i] = real[i]*scaleFactor; imag[i] = imag[i]*scaleFactor; } } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/OrCRIF.java0000644000175000017500000000322310203035544025117 0ustar mathieumathieu/* * $RCSfile: OrCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:38 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.geom.Rectangle2D; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderContext; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "Or" operation in the * rendered and renderable image layers. * * @since EA2 * @see javax.media.jai.operator.OrDescriptor * @see OrOpImage * */ public class OrCRIF extends CRIFImpl { /** Constructor. */ public OrCRIF() { super("or"); } /** * Creates a new instance of OrOpImage in the * rendered layer. This method satisifies the implementation of RIF. * * @param paramBlock The two source images to be "Ored" together. * @param renderHints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new OrOpImage(paramBlock.getRenderedSource(0), paramBlock.getRenderedSource(1), renderHints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MaxCRIF.java0000644000175000017500000000317710203035544025274 0ustar mathieumathieu/* * $RCSfile: MaxCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:32 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "Max" operation in the * rendered and renderable image layer. * * @see javax.media.jai.operator.MaxDescriptor * @see MaxOpImage * */ public class MaxCRIF extends CRIFImpl { /** Constructor. */ public MaxCRIF() { super("max"); } /** * Creates a new instance of MaxOpImage in the rendered * layer. This method satisfies the implementation of RIF. * * @param paramBlock The two source images from which the maximum * pixel values are chosen. * @param renderHints Optionally contains destination image layout * and tile cache. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new MaxOpImage(paramBlock.getRenderedSource(0), paramBlock.getRenderedSource(1), renderHints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/AWTImageRIF.java0000644000175000017500000000335310203035544026036 0ustar mathieumathieu/* * $RCSfile: AWTImageRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:11 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Image; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import java.util.Map; /** * A RIF supporting the "AWTImage" operation in the rendered * layer. It's used by OperationRegistry to create an * AWTImageOpImage. * * @see javax.media.jai.operator.AWTImageDescriptor * @see AWTImageOpImage * */ public class AWTImageRIF implements RenderedImageFactory { /** Constructor. */ public AWTImageRIF() {} /** * Creates a new instance of AWTImageOpImage * in the rendered layer. * This method satisfies the implementation of RIF. * * @param paramBlock The AWT image. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); // Extract the source AWT Image and cast it. Image awtImage = (Image)paramBlock.getObjectParameter(0); // If it's already a RenderedImage (as for a BufferedImage) just cast. if(awtImage instanceof RenderedImage) { return (RenderedImage)awtImage; } // Create a RenderedImage from the data. return new AWTImageOpImage(renderHints, layout, awtImage); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/FormatCRIF.java0000644000175000017500000001224610240717543026003 0ustar mathieumathieu/* * $RCSfile: FormatCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-05-12 18:24:32 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.ColorModel; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.renderable.ParameterBlock; import java.util.Map; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import javax.media.jai.JAI; import javax.media.jai.OpImage; import javax.media.jai.NullOpImage; import javax.media.jai.RasterFactory; import com.sun.media.jai.util.JDKWorkarounds; /** * A CRIF supporting the "Format" operation in the rendered * and renderable image layers. * * @see javax.media.jai.operator.FormatDescriptor * @see FormatOpImage * * * @since EA4 */ public class FormatCRIF extends CRIFImpl { /** Constructor. */ public FormatCRIF() { super("format"); } /** * Creates a new instance of FormatOpImage in the * rendered layer. * * @param args The source image and data type * @param hints Contains destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints renderHints) { // Get the source image and the data type parameter. RenderedImage src = args.getRenderedSource(0); Integer datatype = (Integer)args.getObjectParameter(0); int type = datatype.intValue(); // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); // If there is no change return the source image directly. if(layout == null && type == src.getSampleModel().getDataType()) { return src; } // Create or clone the ImageLayout. if(layout == null) { layout = new ImageLayout(src); } else { layout = (ImageLayout)layout.clone(); } boolean isDataTypeChange = false; // Get prospective destination SampleModel. SampleModel sampleModel = layout.getSampleModel(src); // Create a new SampleModel if the type is not as desired. if (sampleModel.getDataType() != type) { int tileWidth = layout.getTileWidth(src); int tileHeight = layout.getTileHeight(src); int numBands = src.getSampleModel().getNumBands(); SampleModel csm = RasterFactory.createComponentSampleModel(sampleModel, type, tileWidth, tileHeight, numBands); layout.setSampleModel(csm); isDataTypeChange = true; } // Check ColorModel. ColorModel colorModel = layout.getColorModel(null); if(colorModel != null && !JDKWorkarounds.areCompatibleDataModels(layout.getSampleModel(src), colorModel)) { // Clear the mask bit if incompatible. layout.unsetValid(ImageLayout.COLOR_MODEL_MASK); } // Check whether anything but the ColorModel is changing. if (layout.getSampleModel(src) == src.getSampleModel() && layout.getMinX(src) == src.getMinX() && layout.getMinY(src) == src.getMinY() && layout.getWidth(src) == src.getWidth() && layout.getHeight(src) == src.getHeight() && layout.getTileWidth(src) == src.getTileWidth() && layout.getTileHeight(src) == src.getTileHeight() && layout.getTileGridXOffset(src) == src.getTileGridXOffset() && layout.getTileGridYOffset(src) == src.getTileGridYOffset()) { if(layout.getColorModel(src) == src.getColorModel()) { // Nothing changed: return the source directly. return src; } else { // Remove TileCache hint from RenderingHints if present. RenderingHints hints = renderHints; if(hints != null && hints.containsKey(JAI.KEY_TILE_CACHE)) { hints = new RenderingHints((Map)renderHints); hints.remove(JAI.KEY_TILE_CACHE); } // Only the ColorModel is changing. return new NullOpImage(src, layout, hints, OpImage.OP_IO_BOUND); } } if (isDataTypeChange == true) { // Add JAI.KEY_REPLACE_INDEX_COLOR_MODEL hint to renderHints if (renderHints == null) { renderHints = new RenderingHints(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.TRUE); } else if (!renderHints.containsKey( JAI.KEY_REPLACE_INDEX_COLOR_MODEL)) { // If the user specified a value for this hint, we don't // want to change that renderHints.put(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.TRUE); } } return new CopyOpImage(src, renderHints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ScaleBilinearOpImage.java0000644000175000017500000007565010203035544030047 0ustar mathieumathieu/* * $RCSfile: ScaleBilinearOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:42 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationBilinear; import javax.media.jai.OpImage; import javax.media.jai.PlanarImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.ScaleOpImage; import java.util.Map; import com.sun.media.jai.util.Rational; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage that performs bilinear interpolation scaling. * */ final class ScaleBilinearOpImage extends ScaleOpImage { /** The number of SubsampleBits */ private int subsampleBits; /** Subsampling related variables */ int one, shift2, round2; Rational half = new Rational(1, 2); long invScaleYInt, invScaleYFrac; long invScaleXInt, invScaleXFrac; /** * Constructs a ScaleBilinearOpImage from a RenderedImage source, * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param xScale scale factor along x axis. * @param yScale scale factor along y axis. * @param xTrans translation factor along x axis. * @param yTrans translation factor along y axis. * @param interp a Interpolation object to use for resampling. */ public ScaleBilinearOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, float xScale, float yScale, float xTrans, float yTrans, Interpolation interp) { super(source, layout, config, true, extender, interp, xScale, yScale, xTrans, yTrans); subsampleBits = interp.getSubsampleBitsH(); // Number of subsampling positions one = 1 << subsampleBits; // Subsampling related variables shift2 = 2 * subsampleBits; round2 = 1 << (shift2 - 1); if (invScaleYRational.num > invScaleYRational.denom) { invScaleYInt = invScaleYRational.num / invScaleYRational.denom; invScaleYFrac = invScaleYRational.num % invScaleYRational.denom; } else { invScaleYInt = 0; invScaleYFrac = invScaleYRational.num; } if (invScaleXRational.num > invScaleXRational.denom) { invScaleXInt = invScaleXRational.num / invScaleXRational.denom; invScaleXFrac = invScaleXRational.num % invScaleXRational.denom; } else { invScaleXInt = 0; invScaleXFrac = invScaleXRational.num; } } /** * Performs scale operation on a specified rectangle. The sources are * cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster [] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); Raster source = sources[0]; // Get the source rectangle Rectangle srcRect = source.getBounds(); RasterAccessor srcAccessor = new RasterAccessor(source, srcRect, formatTags[0], getSource(0).getColorModel()); RasterAccessor dstAccessor = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); int dwidth = destRect.width; int dheight = destRect.height; int srcPixelStride = srcAccessor.getPixelStride(); int srcScanlineStride = srcAccessor.getScanlineStride(); int[] ypos = new int[dheight]; int[] xpos = new int[dwidth]; int xfracvalues[] = null, yfracvalues[] = null; float xfracvaluesFloat[] = null, yfracvaluesFloat[] = null; switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_INT: yfracvalues = new int[dheight]; xfracvalues = new int[dwidth]; preComputePositionsInt(destRect, srcRect.x, srcRect.y, srcPixelStride, srcScanlineStride, xpos, ypos, xfracvalues, yfracvalues); break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: yfracvaluesFloat = new float[dheight]; xfracvaluesFloat = new float[dwidth]; preComputePositionsFloat(destRect, srcRect.x, srcRect.y, srcPixelStride, srcScanlineStride, xpos, ypos, xfracvaluesFloat, yfracvaluesFloat); break; default: throw new RuntimeException(JaiI18N.getString("OrderedDitherOpImage0")); } switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(srcAccessor, destRect, dstAccessor, xpos, ypos, xfracvalues, yfracvalues); break; case DataBuffer.TYPE_SHORT: shortLoop(srcAccessor, destRect, dstAccessor, xpos, ypos, xfracvalues, yfracvalues); break; case DataBuffer.TYPE_USHORT: ushortLoop(srcAccessor, destRect, dstAccessor, xpos, ypos, xfracvalues, yfracvalues); break; case DataBuffer.TYPE_INT: intLoop(srcAccessor, destRect, dstAccessor, xpos, ypos, xfracvalues, yfracvalues); break; case DataBuffer.TYPE_FLOAT: floatLoop(srcAccessor, destRect, dstAccessor, xpos, ypos, xfracvaluesFloat, yfracvaluesFloat); break; case DataBuffer.TYPE_DOUBLE: doubleLoop(srcAccessor, destRect, dstAccessor, xpos, ypos, xfracvaluesFloat, yfracvaluesFloat); break; } // If the RasterAccessor object set up a temporary buffer for the // op to write to, tell the RasterAccessor to write that data // to the raster no that we're done with it. if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } private void preComputePositionsInt(Rectangle destRect, int srcRectX, int srcRectY, int srcPixelStride, int srcScanlineStride, int xpos[], int ypos[], int xfracvalues[], int yfracvalues[]) { int dwidth = destRect.width; int dheight = destRect.height; // Loop variables based on the destination rectangle to be calculated. int dx = destRect.x; int dy = destRect.y; long syNum = dy, syDenom = 1; // Subtract the X translation factor sy -= transY syNum = syNum * transYRationalDenom - transYRationalNum * syDenom; syDenom *= transYRationalDenom; // Add 0.5 syNum = 2 * syNum + syDenom; syDenom *= 2; // Multply by invScaleX syNum *= invScaleYRationalNum; syDenom *= invScaleYRationalDenom; // Subtract 0.5 syNum = 2 * syNum - syDenom; syDenom *= 2; // Separate the x source coordinate into integer and fractional part int srcYInt = Rational.floor(syNum , syDenom); long srcYFrac = syNum % syDenom; if (srcYInt < 0) { srcYFrac = syDenom + srcYFrac; } // Normalize - Get a common denominator for the fracs of // src and invScaleY long commonYDenom = syDenom * invScaleYRationalDenom; srcYFrac *= invScaleYRationalDenom; long newInvScaleYFrac = invScaleYFrac * syDenom; // Precalculate the x positions and store them in an array. long sxNum = dx, sxDenom = 1; // Subtract the X translation factor sx -= transX sxNum = sxNum * transXRationalDenom - transXRationalNum * sxDenom; sxDenom *= transXRationalDenom; // Add 0.5 sxNum = 2 * sxNum + sxDenom; sxDenom *= 2; // Multply by invScaleX sxNum *= invScaleXRationalNum; sxDenom *= invScaleXRationalDenom; // Subtract 0.5 sxNum = 2 * sxNum - sxDenom; sxDenom *= 2; // Separate the x source coordinate into integer and fractional part int srcXInt = Rational.floor(sxNum , sxDenom); long srcXFrac = sxNum % sxDenom; if (srcXInt < 0) { srcXFrac = sxDenom + srcXFrac; } // Normalize - Get a common denominator for the fracs of // src and invScaleX long commonXDenom = sxDenom * invScaleXRationalDenom; srcXFrac *= invScaleXRationalDenom; long newInvScaleXFrac = invScaleXFrac * sxDenom; for (int i=0; i= commonXDenom) { srcXInt += 1; srcXFrac -= commonXDenom; } } for (int i = 0; i < dheight; i++) { // Calculate the source position in the source data array. ypos[i] = (srcYInt - srcRectY) * srcScanlineStride; // Calculate the yfrac value yfracvalues[i] = (int)(((float)srcYFrac/(float)commonYDenom) * one); // Move onto the next source pixel. // Add the integral part of invScaleY to the integral part // of srcY srcYInt += invScaleYInt; // Add the fractional part of invScaleY to the fractional part // of srcY srcYFrac += newInvScaleYFrac; // If the fractional part is now greater than equal to the // denominator, divide so as to reduce the numerator to be less // than the denominator and add the overflow to the integral part. if (srcYFrac >= commonYDenom) { srcYInt += 1; srcYFrac -= commonYDenom; } } } private void preComputePositionsFloat(Rectangle destRect, int srcRectX, int srcRectY, int srcPixelStride, int srcScanlineStride, int xpos[], int ypos[], float xfracvaluesFloat[], float yfracvaluesFloat[]) { int dwidth = destRect.width; int dheight = destRect.height; // Loop variables based on the destination rectangle to be calculated. int dx = destRect.x; int dy = destRect.y; long syNum = dy, syDenom = 1; // Subtract the X translation factor sy -= transY syNum = syNum * transYRationalDenom - transYRationalNum * syDenom; syDenom *= transYRationalDenom; // Add 0.5 syNum = 2 * syNum + syDenom; syDenom *= 2; // Multply by invScaleX syNum *= invScaleYRationalNum; syDenom *= invScaleYRationalDenom; // Subtract 0.5 syNum = 2 * syNum - syDenom; syDenom *= 2; // Separate the x source coordinate into integer and fractional part int srcYInt = Rational.floor(syNum , syDenom); long srcYFrac = syNum % syDenom; if (srcYInt < 0) { srcYFrac = syDenom + srcYFrac; } // Normalize - Get a common denominator for the fracs of // src and invScaleY long commonYDenom = syDenom * invScaleYRationalDenom; srcYFrac *= invScaleYRationalDenom; long newInvScaleYFrac = invScaleYFrac * syDenom; // Precalculate the x positions and store them in an array. long sxNum = dx, sxDenom = 1; // Subtract the X translation factor sx -= transX sxNum = sxNum * transXRationalDenom - transXRationalNum * sxDenom; sxDenom *= transXRationalDenom; // Add 0.5 sxNum = 2 * sxNum + sxDenom; sxDenom *= 2; // Multply by invScaleX sxNum *= invScaleXRationalNum; sxDenom *= invScaleXRationalDenom; // Subtract 0.5 sxNum = 2 * sxNum - sxDenom; sxDenom *= 2; // Separate the x source coordinate into integer and fractional part int srcXInt = Rational.floor(sxNum , sxDenom); long srcXFrac = sxNum % sxDenom; if (srcXInt < 0) { srcXFrac = sxDenom + srcXFrac; } // Normalize - Get a common denominator for the fracs of // src and invScaleX long commonXDenom = sxDenom * invScaleXRationalDenom; srcXFrac *= invScaleXRationalDenom; long newInvScaleXFrac = invScaleXFrac * sxDenom; for (int i=0; i= commonXDenom) { srcXInt += 1; srcXFrac -= commonXDenom; } } for (int i = 0; i < dheight; i++) { // Calculate the source position in the source data array. ypos[i] = (srcYInt - srcRectY) * srcScanlineStride; // Calculate the yfrac value yfracvaluesFloat[i] = (float)srcYFrac/(float)commonYDenom; // Move onto the next source pixel. // Add the integral part of invScaleY to the integral part // of srcY srcYInt += invScaleYInt; // Add the fractional part of invScaleY to the fractional part // of srcY srcYFrac += newInvScaleYFrac; // If the fractional part is now greater than equal to the // denominator, divide so as to reduce the numerator to be less // than the denominator and add the overflow to the integral part. if (srcYFrac >= commonYDenom) { srcYInt += 1; srcYFrac -= commonYDenom; } } } private void byteLoop(RasterAccessor src, Rectangle dstRect, RasterAccessor dst, int xpos[], int ypos[], int xfracvalues[], int yfracvalues[]) { int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int srcLastXDataPos = (src.getWidth()-1) * srcPixelStride; int dwidth = dstRect.width; int dheight = dstRect.height; int dnumBands = dst.getNumBands(); byte dstDataArrays[][] = dst.getByteDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); byte srcDataArrays[][] = src.getByteDataArrays(); int bandOffsets[] = src.getBandOffsets(); int dstOffset = 0; /* Four surrounding pixels are needed for Bilinear interpolation. * If the dest pixel to be calculated is at (dx, dy) then the * actual source pixel (sx, sy) required is (dx/scaleX, dy/scaleY). * The four pixels that surround it are at the positions: * s00 = src(sxlow, sylow) * s01 = src(sxhigh, sylow) * s10 = src(sxlow, syhigh) * s11 = src(sxhigh, syhigh) * where sxlow = Math.floor(sx), sxhigh = Math.ceil(sx) * and sylow = Math.floor(sy), syhigh = Math.ceil(sy) * * The value of the destination pixel can now be calculated as: * s0 = (s01 - s00)*xfrac + s00; * s1 = (s11 - s10)*xfrac + s10; * dst(x,y) = (s1 - s0)*yfrac + s0; */ int posylow, posyhigh, posxlow, posxhigh; int s00, s01, s10, s11; // Precalculate the y positions and store them in an array. int xfrac, yfrac; int s, s0, s1; // Putting band loop outside for (int k = 0; k < dnumBands; k++) { byte dstData[] = dstDataArrays[k]; byte srcData[] = srcDataArrays[k]; int dstScanlineOffset = dstBandOffsets[k]; int bandOffset = bandOffsets[k]; for (int j = 0; j < dheight; j++) { int dstPixelOffset = dstScanlineOffset; yfrac = yfracvalues[j]; posylow = ypos[j] + bandOffset; posyhigh = posylow + srcScanlineStride; for (int i = 0; i < dwidth; i++) { xfrac = xfracvalues[i]; posxlow = xpos[i]; posxhigh = posxlow + srcPixelStride; // Get the four surrounding pixel values s00 = srcData[posxlow + posylow] & 0xff; s01 = srcData[posxhigh + posylow] & 0xff; s10 = srcData[posxlow + posyhigh] & 0xff; s11 = srcData[posxhigh + posyhigh] & 0xff; // Perform the bilinear interpolation s0 = (s01 - s00) * xfrac + (s00 << subsampleBits); s1 = (s11 - s10) * xfrac + (s10 << subsampleBits); s = ((s1 - s0) * yfrac + (s0 << subsampleBits) + round2) >> shift2; dstData[dstPixelOffset] = (byte)(s&0xff); dstPixelOffset += dstPixelStride; } dstScanlineOffset += dstScanlineStride; } } } private void shortLoop(RasterAccessor src, Rectangle dstRect, RasterAccessor dst, int xpos[], int ypos[], int xfracvalues[], int yfracvalues[]) { int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int srcLastXDataPos = (src.getWidth()-1) * srcPixelStride; int dwidth = dstRect.width; int dheight = dstRect.height; int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int bandOffsets[] = src.getBandOffsets(); int dstOffset = 0; int posylow, posyhigh, posxlow, posxhigh; int s00, s01, s10, s11, s0, s1, s; int xfrac, yfrac; // Putting band loop outside for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int dstScanlineOffset = dstBandOffsets[k]; int bandOffset = bandOffsets[k]; for (int j = 0; j < dheight; j++) { int dstPixelOffset = dstScanlineOffset; yfrac = yfracvalues[j]; posylow = ypos[j] + bandOffset; posyhigh = posylow + srcScanlineStride; for (int i = 0; i < dwidth; i++) { xfrac = xfracvalues[i]; posxlow = xpos[i]; posxhigh = posxlow + srcPixelStride; // Get the four surrounding pixel values s00 = srcData[posxlow + posylow]; s01 = srcData[posxhigh + posylow]; s10 = srcData[posxlow + posyhigh]; s11 = srcData[posxhigh + posyhigh]; // Perform the bilinear interpolation s0 = (s01 - s00) * xfrac + (s00 << subsampleBits); s1 = (s11 - s10) * xfrac + (s10 << subsampleBits); s = ((s1 - s0) * yfrac + (s0 << subsampleBits) + round2) >> shift2; dstData[dstPixelOffset] = (short)s; dstPixelOffset += dstPixelStride; } dstScanlineOffset += dstScanlineStride; } } } private void ushortLoop(RasterAccessor src, Rectangle dstRect, RasterAccessor dst, int xpos[], int ypos[], int xfracvalues[], int yfracvalues[]) { int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int srcLastXDataPos = (src.getWidth()-1) * srcPixelStride; int dwidth = dstRect.width; int dheight = dstRect.height; int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int bandOffsets[] = src.getBandOffsets(); int dstOffset = 0; int posylow, posyhigh, posxlow, posxhigh; int s00, s01, s10, s11, s0, s1, s; int xfrac, yfrac; // Putting band loop outside for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int dstScanlineOffset = dstBandOffsets[k]; int bandOffset = bandOffsets[k]; for (int j = 0; j < dheight; j++) { int dstPixelOffset = dstScanlineOffset; yfrac = yfracvalues[j]; posylow = ypos[j] + bandOffset; posyhigh = posylow + srcScanlineStride; for (int i = 0; i < dwidth; i++) { xfrac = xfracvalues[i]; posxlow = xpos[i]; posxhigh = posxlow + srcPixelStride; // Get the four surrounding pixel values s00 = srcData[posxlow + posylow] & 0xffff; s01 = srcData[posxhigh + posylow] & 0xffff; s10 = srcData[posxlow + posyhigh] & 0xffff; s11 = srcData[posxhigh + posyhigh] & 0xffff; // Perform the bilinear interpolation s0 = (s01 - s00) * xfrac + (s00 << subsampleBits); s1 = (s11 - s10) * xfrac + (s10 << subsampleBits); s = ((s1 - s0) * yfrac + (s0 << subsampleBits) + round2) >> shift2; dstData[dstPixelOffset] = (short)(s & 0xffff); dstPixelOffset += dstPixelStride; } dstScanlineOffset += dstScanlineStride; } } } // identical to byteLoops, except datatypes have changed. clumsy, // but there's no other way in Java private void intLoop(RasterAccessor src, Rectangle dstRect, RasterAccessor dst, int xpos[], int ypos[], int xfracvalues[], int yfracvalues[]) { int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int srcLastXDataPos = (src.getWidth()-1) * srcPixelStride; int dwidth = dstRect.width; int dheight = dstRect.height; int dnumBands = dst.getNumBands(); int dstDataArrays[][] = dst.getIntDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcDataArrays[][] = src.getIntDataArrays(); int bandOffsets[] = src.getBandOffsets(); int dstOffset = 0; int posylow, posyhigh, posxlow, posxhigh; int s00, s10, s01, s11; long s0, s1; int xfrac, yfrac; int shift = 29 - subsampleBits; // Putting band loop outside for (int k = 0; k < dnumBands; k++) { int dstData[] = dstDataArrays[k]; int srcData[] = srcDataArrays[k]; int dstScanlineOffset = dstBandOffsets[k]; int bandOffset = bandOffsets[k]; for (int j = 0; j < dheight; j++) { int dstPixelOffset = dstScanlineOffset; yfrac = yfracvalues[j]; posylow = ypos[j] + bandOffset; posyhigh = posylow + srcScanlineStride; for (int i = 0; i < dwidth; i++) { xfrac = xfracvalues[i]; posxlow = xpos[i]; posxhigh = posxlow + srcPixelStride; // Get the four surrounding pixel values s00 = srcData[posxlow + posylow]; s01 = srcData[posxhigh + posylow]; s10 = srcData[posxlow + posyhigh]; s11 = srcData[posxhigh + posyhigh]; // Perform the bilinear interpolation if ((s00 | s10) >>> shift == 0) { if ((s01 | s11) >>> shift == 0) { s0 = (s01 - s00) * xfrac + (s00 << subsampleBits); s1 = (s11 - s10) * xfrac + (s10 << subsampleBits); } else { s0 = ((long)s01 - s00) * xfrac + (s00 << subsampleBits); s1 = ((long)s11 - s10) * xfrac + (s10 << subsampleBits); } } else { s0 = ((long)s01 - s00) * xfrac + ((long)s00 << subsampleBits); s1 = ((long)s11 - s10) * xfrac + ((long)s10 << subsampleBits); } dstData[dstPixelOffset] = (int)(((s1 - s0) * yfrac + (s0 << subsampleBits) + round2) >> shift2); dstPixelOffset += dstPixelStride; } dstScanlineOffset += dstScanlineStride; } } } // Interpolation for floating point samples done as specified by the // following formula: // float s0 = (s01 - s00)*xfrac + s00; // float s1 = (s11 - s10)*xfrac + s10; // return (s1 - s0)*yfrac + s0; // Note that xfrac, yfrac are in the range [0.0F, 1.0F) private void floatLoop(RasterAccessor src, Rectangle dstRect, RasterAccessor dst, int xpos[], int ypos[], float xfracvaluesFloat[], float yfracvaluesFloat[]) { int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int srcLastXDataPos = (src.getWidth()-1) * srcPixelStride; int dwidth = dstRect.width; int dheight = dstRect.height; int dnumBands = dst.getNumBands(); float dstDataArrays[][] = dst.getFloatDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); float srcDataArrays[][] = src.getFloatDataArrays(); int bandOffsets[] = src.getBandOffsets(); float s00, s01, s10, s11; float s0, s1; float xfrac, yfrac; int dstOffset = 0; int posylow, posyhigh, posxlow, posxhigh; // Putting band loop outside for (int k = 0; k < dnumBands; k++) { float dstData[] = dstDataArrays[k]; float srcData[] = srcDataArrays[k]; int dstScanlineOffset = dstBandOffsets[k]; int bandOffset = bandOffsets[k]; for (int j = 0; j < dheight; j++) { int dstPixelOffset = dstScanlineOffset; yfrac = yfracvaluesFloat[j]; posylow = ypos[j] + bandOffset; posyhigh = posylow + srcScanlineStride; for (int i = 0; i < dwidth; i++) { xfrac = xfracvaluesFloat[i]; posxlow = xpos[i]; posxhigh = posxlow + srcPixelStride; // Get the four surrounding pixel values s00 = srcData[posxlow + posylow]; s01 = srcData[posxhigh + posylow]; s10 = srcData[posxlow + posyhigh]; s11 = srcData[posxhigh + posyhigh]; // Perform the bilinear interpolation s0 = (s01 - s00) * xfrac + s00; s1 = (s11 - s10) * xfrac + s10; dstData[dstPixelOffset] = (s1 - s0) * yfrac + s0; dstPixelOffset += dstPixelStride; } dstScanlineOffset += dstScanlineStride; } } } private void doubleLoop(RasterAccessor src, Rectangle dstRect, RasterAccessor dst, int xpos[], int ypos[], float xfracvaluesFloat[], float yfracvaluesFloat[]) { int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int srcLastXDataPos = (src.getWidth()-1) * srcPixelStride; int dwidth = dstRect.width; int dheight = dstRect.height; int dnumBands = dst.getNumBands(); double dstDataArrays[][] = dst.getDoubleDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); double srcDataArrays[][] = src.getDoubleDataArrays(); int bandOffsets[] = src.getBandOffsets(); double s00, s01, s10, s11; double s0, s1; double xfrac, yfrac; int dstOffset = 0; int posylow, posyhigh, posxlow, posxhigh; // Putting band loop outside for (int k = 0; k < dnumBands; k++) { double dstData[] = dstDataArrays[k]; double srcData[] = srcDataArrays[k]; int dstScanlineOffset = dstBandOffsets[k]; int bandOffset = bandOffsets[k]; for (int j = 0; j < dheight; j++) { int dstPixelOffset = dstScanlineOffset; yfrac = yfracvaluesFloat[j]; posylow = ypos[j] + bandOffset; posyhigh = posylow + srcScanlineStride; for (int i = 0; i < dwidth; i++) { xfrac = xfracvaluesFloat[i]; posxlow = xpos[i]; posxhigh = posxlow + srcPixelStride; // Get the four surrounding pixel values s00 = srcData[posxlow + posylow]; s01 = srcData[posxhigh + posylow]; s10 = srcData[posxlow + posyhigh]; s11 = srcData[posxhigh + posyhigh]; // Perform the bilinear interpolation s0 = (s01 - s00) * xfrac + s00; s1 = (s11 - s10) * xfrac + s10; dstData[dstPixelOffset] = (s1 - s0) * yfrac + s0; dstPixelOffset += dstPixelStride; } dstScanlineOffset += dstScanlineStride; } } } // public static OpImage createTestImage(OpImageTester oit) { // Interpolation interp = // Interpolation.getInstance(Interpolation.INTERP_BILINEAR); // return new ScaleBilinearOpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource()), // 2.5F, 2.5F, 0.0F, 0.0F, // interp); // } // public static void main(String args[]) { // String classname = "com.sun.media.jai.opimage.ScaleBilinearOpImage"; // OpImageTester.performDiagnostics(classname,args); // System.exit(1); // System.out.println("ScaleOpImage Test"); // ImageLayout layout; // OpImage src, dst; // Rectangle rect = new Rectangle(0, 0, 5, 5); // InterpolationBilinear interp = new InterpolationBilinear(); // System.out.println("1. PixelInterleaved short 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 200, 200, 0, 0, 64, 64, DataBuffer.TYPE_SHORT, 3, false); // src = OpImageTester.createRandomOpImage(layout); // dst = new ScaleBilinearOpImage(src, null, null, null, // 2.0F, 2.0F, 0.0F, 0.0F, interp); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("2. PixelInterleaved ushort 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_USHORT, 3, false); // src = OpImageTester.createRandomOpImage(layout); // dst = new ScaleBilinearOpImage(src, null, null, null, // 2.0F, 2.0F, 0.0F, 0.0F, interp); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("3. PixelInterleaved float 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 512, 512, 0, 0, 200, 200, // DataBuffer.TYPE_FLOAT, 3, // false); // src = OpImageTester.createRandomOpImage(layout); // dst = new ScaleBilinearOpImage(src, null, null, null, // 2.0F, 2.0F, 0.0F, 0.0F, interp); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("4. PixelInterleaved double 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 512, 512, // 0, 0, 200, 200, // DataBuffer.TYPE_DOUBLE, 3, // false); // src = OpImageTester.createRandomOpImage(layout); // dst = new ScaleBilinearOpImage(src, null, null, null, // 2.0F, 2.0F, 0.0F, 0.0F, interp); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ConvolveOpImage.java0000644000175000017500000005060510203035544027136 0ustar mathieumathieu/* * $RCSfile: ConvolveOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:20 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class to perform convolution on a source image. * *

    This class implements a convolution operation. Convolution is a * spatial operation that computes each output sample by multiplying * elements of a kernel with the samples surrounding a particular * source sample. * *

    For each destination sample, the kernel is rotated 180 degrees * and its "key element" is placed over the source pixel corresponding * with the destination pixel. The kernel elements are multiplied * with the source pixels under them, and the resulting products are * summed together to produce the destination sample value. * *

    Example code for the convolution operation on a single sample * dst[x][y] is as follows. First your original kernel is rotated * by 180 degrees, then the following - * assuming the kernel is of size M rows x N columns * and the rotated kernel's key element is at position (xKey, yKey): * *

     * dst[x][y] = 0;
     * for (int i = -xKey; i < M - xKey; i++) {
     *     for (int j = -yKey; j < N - yKey; j++) {
     *         dst[x][y] += src[x + i][y + j] * kernel[xKey + i][yKey + j];
     *     }
     * }
     * 
    *

    Convolution, or any neighborhood operation, leaves a band of * pixels around the edges undefined, i.e., for a 3x3 kernel, only * four kernel elements and four source pixels contribute to the * destination pixel located at (0,0). Such pixels are not includined * in the destination image. A BorderOpImage may be used to add an * appropriate border to the source image in order to avoid shrinkage * of the image boundaries. * *

    The Kernel cannot be bigger in any dimension than the image data. * * * @see KernelJAI */ final class ConvolveOpImage extends AreaOpImage { /** * The kernel with which to do the convolve operation. */ protected KernelJAI kernel; /** Kernel variables. */ private int kw, kh, kx, ky; /** * Creates a ConvolveOpImage given a ParameterBlock containing the image * source and pre-rotated convolution kernel. The image dimensions are * derived * from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout * object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param kernel the pre-rotated convolution KernelJAI. */ public ConvolveOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, KernelJAI kernel) { super(source, layout, config, true, extender, kernel.getLeftPadding(), kernel.getRightPadding(), kernel.getTopPadding(), kernel.getBottomPadding()); this.kernel = kernel; kw = kernel.getWidth(); kh = kernel.getHeight(); kx = kernel.getXOrigin(); ky = kernel.getYOrigin(); } /** * Performs convolution on a specified rectangle. The sources are * cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); Raster source = sources[0]; Rectangle srcRect = mapDestRect(destRect, 0); RasterAccessor srcAccessor = new RasterAccessor(source, srcRect, formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor dstAccessor = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(srcAccessor, dstAccessor); break; case DataBuffer.TYPE_INT: intLoop(srcAccessor, dstAccessor); break; case DataBuffer.TYPE_SHORT: shortLoop(srcAccessor, dstAccessor); break; case DataBuffer.TYPE_USHORT: ushortLoop(srcAccessor, dstAccessor); break; case DataBuffer.TYPE_FLOAT: floatLoop(srcAccessor, dstAccessor); break; case DataBuffer.TYPE_DOUBLE: doubleLoop(srcAccessor, dstAccessor); break; default: } // If the RasterAccessor object set up a temporary buffer for the // op to write to, tell the RasterAccessor to write that data // to the raster no that we're done with it. if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } private void byteLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); float[] kdata = kernel.getKernelData(); int kw = kernel.getWidth(); int kh = kernel.getHeight(); byte dstDataArrays[][] = dst.getByteDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); byte srcDataArrays[][] = src.getByteDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); for (int k = 0; k < dnumBands; k++) { byte dstData[] = dstDataArrays[k]; byte srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { float f = 0.5F; int kernelVerticalOffset = 0; int imageVerticalOffset = srcPixelOffset; for (int u = 0; u < kh; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < kw; v++) { f += ((int)srcData[imageOffset]&0xff) * kdata[kernelVerticalOffset + v]; imageOffset += srcPixelStride; } kernelVerticalOffset += kw; imageVerticalOffset += srcScanlineStride; } int val = (int)f; if (val < 0) { val = 0; } else if (val > 255) { val = 255; } dstData[dstPixelOffset] = (byte)val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } private void shortLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); float[] kdata = kernel.getKernelData(); int kw = kernel.getWidth(); int kh = kernel.getHeight(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { float f = 0.5F; int kernelVerticalOffset = 0; int imageVerticalOffset = srcPixelOffset; for (int u = 0; u < kh; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < kw; v++) { f += (srcData[imageOffset]) * kdata[kernelVerticalOffset + v]; imageOffset += srcPixelStride; } kernelVerticalOffset += kw; imageVerticalOffset += srcScanlineStride; } int val = (int)f; if (val < Short.MIN_VALUE) { val = Short.MIN_VALUE; } else if (val > Short.MAX_VALUE) { val = Short.MAX_VALUE; } dstData[dstPixelOffset] = (short)val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } private void ushortLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); float[] kdata = kernel.getKernelData(); int kw = kernel.getWidth(); int kh = kernel.getHeight(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { float f = 0.5F; int kernelVerticalOffset = 0; int imageVerticalOffset = srcPixelOffset; for (int u = 0; u < kh; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < kw; v++) { f += (srcData[imageOffset] & 0xffff) * kdata[kernelVerticalOffset + v]; imageOffset += srcPixelStride; } kernelVerticalOffset += kw; imageVerticalOffset += srcScanlineStride; } int val = (int)f; if (val < 0) { val = 0; } else if (val > 0xffff) { val = 0xffff; } dstData[dstPixelOffset] = (short)val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } private void intLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); float[] kdata = kernel.getKernelData(); int kw = kernel.getWidth(); int kh = kernel.getHeight(); int dstDataArrays[][] = dst.getIntDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcDataArrays[][] = src.getIntDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); for (int k = 0; k < dnumBands; k++) { int dstData[] = dstDataArrays[k]; int srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { float f = 0.5F; int kernelVerticalOffset = 0; int imageVerticalOffset = srcPixelOffset; for (int u = 0; u < kh; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < kw; v++) { f += ((int)srcData[imageOffset]) * kdata[kernelVerticalOffset + v]; imageOffset += srcPixelStride; } kernelVerticalOffset += kw; imageVerticalOffset += srcScanlineStride; } dstData[dstPixelOffset] = (int)f; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } private void floatLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); float[] kdata = kernel.getKernelData(); int kw = kernel.getWidth(); int kh = kernel.getHeight(); float dstDataArrays[][] = dst.getFloatDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); float srcDataArrays[][] = src.getFloatDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); for (int k = 0; k < dnumBands; k++) { float dstData[] = dstDataArrays[k]; float srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { float f = 0.0F; int kernelVerticalOffset = 0; int imageVerticalOffset = srcPixelOffset; for (int u = 0; u < kh; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < kw; v++) { f += (srcData[imageOffset]) * kdata[kernelVerticalOffset + v]; imageOffset += srcPixelStride; } kernelVerticalOffset += kw; imageVerticalOffset += srcScanlineStride; } dstData[dstPixelOffset] = f; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } private void doubleLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); float[] kdata = kernel.getKernelData(); int kw = kernel.getWidth(); int kh = kernel.getHeight(); double dstDataArrays[][] = dst.getDoubleDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); double srcDataArrays[][] = src.getDoubleDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); for (int k = 0; k < dnumBands; k++) { double dstData[] = dstDataArrays[k]; double srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { double f = 0.5; int kernelVerticalOffset = 0; int imageVerticalOffset = srcPixelOffset; for (int u = 0; u < kh; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < kw; v++) { f += (srcData[imageOffset]) * kdata[kernelVerticalOffset + v]; imageOffset += srcPixelStride; } kernelVerticalOffset += kw; imageVerticalOffset += srcScanlineStride; } dstData[dstPixelOffset] = f; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } // public static OpImage createTestImage(OpImageTester oit) { // float data[] = {0.05f,0.10f,0.05f, // 0.10f,0.40f,0.10f, // 0.05f,0.10f,0.05f}; // KernelJAI kJAI = new KernelJAI(3,3,1,1,data); // return new ConvolveOpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource()), // kJAI); // } // public static void main(String args[]) { // String classname = "com.sun.media.jai.opimage.ConvolveOpImage"; // OpImageTester.performDiagnostics(classname,args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ThresholdOpImage.java0000644000175000017500000004023710203035544027277 0ustar mathieumathieu/* * $RCSfile: ThresholdOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:45 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import javax.media.jai.ColormapOpImage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; /** * An OpImage implementing the "Threshold" operation as * described in javax.media.jai.operator.ThresholdDescriptor. * *

    This OpImage maps all the pixels of an image * whose value falls within a given range to a constant on a per-band basis. * Each of the lower bound, upper bound, and constant arrays may have only * one value in it. If that is the case, that value is used for all bands. * * @see javax.media.jai.operator.ThresholdDescriptor * @see ThresholdCRIF * * * @since EA2 */ final class ThresholdOpImage extends ColormapOpImage { /** The lower bound, one for each band. */ private double[] low; /** The upper bound, one for each band. */ private double[] high; /** The constants to be mapped, one for each band. */ private double[] constants; /** Lookup table for byte data */ private byte[][] byteTable = null; /** * Constructor. * * @param source The source image. * @param layout The destination image layout. * @param low The lower bound of the threshold. * @param high The upper bound of the threshold. * @param constants The constants to be mapped within the threshold. */ public ThresholdOpImage(RenderedImage source, Map config, ImageLayout layout, double[] low, double[] high, double[] constants) { super(source, layout, config, true); int numBands = getSampleModel().getNumBands(); this.low = new double[numBands]; this.high = new double[numBands]; this.constants = new double[numBands]; for (int i = 0; i < numBands; i++) { if (low.length < numBands) { this.low[i] = low[0]; } else { this.low[i] = low[i]; } if (high.length < numBands) { this.high[i] = high[0]; } else { this.high[i] = high[i]; } if (constants.length < numBands) { this.constants[i] = constants[0]; } else { this.constants[i] = constants[i]; } } // Set flag to permit in-place operation. permitInPlaceOperation(); // Initialize the colormap if necessary. initializeColormapOperation(); } /** * Transform the colormap according to the rescaling parameters. */ protected void transformColormap(byte[][] colormap) { initByteTable(); // only create lookup table when necessary for(int b = 0; b < 3; b++) { byte[] map = colormap[b]; byte[] luTable = byteTable[b >= byteTable.length ? 0 : b]; int mapSize = map.length; for(int i = 0; i < mapSize; i++) { map[i] = luTable[(map[i] & 0xFF)]; } } } /** * Map the pixels inside a specified rectangle whose value is within a * rang to a constant on a per-band basis. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); Rectangle srcRect = mapDestRect(destRect, 0); RasterAccessor src = new RasterAccessor(sources[0], srcRect, formatTags[0], getSource(0).getColorModel()); RasterAccessor dst = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); int srcPixelStride = src.getPixelStride(); int srcLineStride = src.getScanlineStride(); int[] srcBandOffsets = src.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstLineStride = dst.getScanlineStride(); int[] dstBandOffsets = dst.getBandOffsets(); int width = dst.getWidth() * dstPixelStride; int height = dst.getHeight() * dstLineStride; int bands = dst.getNumBands(); switch (dst.getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(width, height, bands, srcPixelStride, srcLineStride, srcBandOffsets, src.getByteDataArrays(), dstPixelStride, dstLineStride, dstBandOffsets, dst.getByteDataArrays()); break; case DataBuffer.TYPE_SHORT: shortLoop(width, height, bands, srcPixelStride, srcLineStride, srcBandOffsets, src.getShortDataArrays(), dstPixelStride, dstLineStride, dstBandOffsets, dst.getShortDataArrays()); break; case DataBuffer.TYPE_USHORT: ushortLoop(width, height, bands, srcPixelStride, srcLineStride, srcBandOffsets, src.getShortDataArrays(), dstPixelStride, dstLineStride, dstBandOffsets, dst.getShortDataArrays()); break; case DataBuffer.TYPE_INT: intLoop(width, height, bands, srcPixelStride, srcLineStride, srcBandOffsets, src.getIntDataArrays(), dstPixelStride, dstLineStride, dstBandOffsets, dst.getIntDataArrays()); break; case DataBuffer.TYPE_FLOAT: floatLoop(width, height, bands, srcPixelStride, srcLineStride, srcBandOffsets, src.getFloatDataArrays(), dstPixelStride, dstLineStride, dstBandOffsets, dst.getFloatDataArrays()); break; case DataBuffer.TYPE_DOUBLE: doubleLoop(width, height, bands, srcPixelStride, srcLineStride, srcBandOffsets, src.getDoubleDataArrays(), dstPixelStride, dstLineStride, dstBandOffsets, dst.getDoubleDataArrays()); break; } if (dst.isDataCopy()) { dst.clampDataArrays(); dst.copyDataToRaster(); } } private void byteLoop(int width, int height, int bands, int srcPixelStride, int srcLineStride, int[] srcBandOffsets, byte[][] srcData, int dstPixelStride, int dstLineStride, int[] dstBandOffsets, byte[][] dstData) { initByteTable(); for (int b = 0; b < bands; b++) { byte[] s = srcData[b]; byte[] d = dstData[b]; byte[] t = byteTable[b]; int heightEnd = dstBandOffsets[b] + height; for (int dstLineOffset = dstBandOffsets[b], srcLineOffset = srcBandOffsets[b]; dstLineOffset < heightEnd; dstLineOffset += dstLineStride, srcLineOffset += srcLineStride) { int widthEnd = dstLineOffset + width; for (int dstPixelOffset = dstLineOffset, srcPixelOffset = srcLineOffset; dstPixelOffset < widthEnd; dstPixelOffset += dstPixelStride, srcPixelOffset += srcPixelStride) { d[dstPixelOffset] = t[s[srcPixelOffset]&0xFF]; } } } } private void shortLoop(int width, int height, int bands, int srcPixelStride, int srcLineStride, int[] srcBandOffsets, short[][] srcData, int dstPixelStride, int dstLineStride, int[] dstBandOffsets, short[][] dstData) { for (int b = 0; b < bands; b++) { short[] s = srcData[b]; short[] d = dstData[b]; double l = low[b]; double h = high[b]; short c = (short)constants[b]; int heightEnd = dstBandOffsets[b] + height; for (int dstLineOffset = dstBandOffsets[b], srcLineOffset = srcBandOffsets[b]; dstLineOffset < heightEnd; dstLineOffset += dstLineStride, srcLineOffset += srcLineStride) { int widthEnd = dstLineOffset + width; for (int dstPixelOffset = dstLineOffset, srcPixelOffset = srcLineOffset; dstPixelOffset < widthEnd; dstPixelOffset += dstPixelStride, srcPixelOffset += srcPixelStride) { short p = s[srcPixelOffset]; if (p >= l && p <= h) { d[dstPixelOffset] = c; } else { d[dstPixelOffset] = p; } } } } } private void ushortLoop(int width, int height, int bands, int srcPixelStride, int srcLineStride, int[] srcBandOffsets, short[][] srcData, int dstPixelStride, int dstLineStride, int[] dstBandOffsets, short[][] dstData) { for (int b = 0; b < bands; b++) { short[] s = srcData[b]; short[] d = dstData[b]; double l = low[b]; double h = high[b]; short c = (short)constants[b]; int heightEnd = dstBandOffsets[b] + height; for (int dstLineOffset = dstBandOffsets[b], srcLineOffset = srcBandOffsets[b]; dstLineOffset < heightEnd; dstLineOffset += dstLineStride, srcLineOffset += srcLineStride) { int widthEnd = dstLineOffset + width; for (int dstPixelOffset = dstLineOffset, srcPixelOffset = srcLineOffset; dstPixelOffset < widthEnd; dstPixelOffset += dstPixelStride, srcPixelOffset += srcPixelStride) { int p = s[srcPixelOffset] & 0xFFFF; if (p >= l && p <= h) { d[dstPixelOffset] = c; } else { d[dstPixelOffset] = (short)p; } } } } } private void intLoop(int width, int height, int bands, int srcPixelStride, int srcLineStride, int[] srcBandOffsets, int[][] srcData, int dstPixelStride, int dstLineStride, int[] dstBandOffsets, int[][] dstData) { for (int b = 0; b < bands; b++) { int[] s = srcData[b]; int[] d = dstData[b]; double l = low[b]; double h = high[b]; int c = (int)constants[b]; int heightEnd = dstBandOffsets[b] + height; for (int dstLineOffset = dstBandOffsets[b], srcLineOffset = srcBandOffsets[b]; dstLineOffset < heightEnd; dstLineOffset += dstLineStride, srcLineOffset += srcLineStride) { int widthEnd = dstLineOffset + width; for (int dstPixelOffset = dstLineOffset, srcPixelOffset = srcLineOffset; dstPixelOffset < widthEnd; dstPixelOffset += dstPixelStride, srcPixelOffset += srcPixelStride) { int p = s[srcPixelOffset]; if (p >= l && p <= h) { d[dstPixelOffset] = c; } else { d[dstPixelOffset] = p; } } } } } private void floatLoop(int width, int height, int bands, int srcPixelStride, int srcLineStride, int[] srcBandOffsets, float[][] srcData, int dstPixelStride, int dstLineStride, int[] dstBandOffsets, float[][] dstData) { for (int b = 0; b < bands; b++) { float[] s = srcData[b]; float[] d = dstData[b]; double l = low[b]; double h = high[b]; float c = (float)constants[b]; int heightEnd = dstBandOffsets[b] + height; for (int dstLineOffset = dstBandOffsets[b], srcLineOffset = srcBandOffsets[b]; dstLineOffset < heightEnd; dstLineOffset += dstLineStride, srcLineOffset += srcLineStride) { int widthEnd = dstLineOffset + width; for (int dstPixelOffset = dstLineOffset, srcPixelOffset = srcLineOffset; dstPixelOffset < widthEnd; dstPixelOffset += dstPixelStride, srcPixelOffset += srcPixelStride) { float p = s[srcPixelOffset]; if (p >= l && p <= h) { d[dstPixelOffset] = c; } else { d[dstPixelOffset] = p; } } } } } private void doubleLoop(int width, int height, int bands, int srcPixelStride, int srcLineStride, int[] srcBandOffsets, double[][] srcData, int dstPixelStride, int dstLineStride, int[] dstBandOffsets, double[][] dstData) { for (int b = 0; b < bands; b++) { double[] s = srcData[b]; double[] d = dstData[b]; double l = low[b]; double h = high[b]; double c = constants[b]; int heightEnd = dstBandOffsets[b] + height; for (int dstLineOffset = dstBandOffsets[b], srcLineOffset = srcBandOffsets[b]; dstLineOffset < heightEnd; dstLineOffset += dstLineStride, srcLineOffset += srcLineStride) { int widthEnd = dstLineOffset + width; for (int dstPixelOffset = dstLineOffset, srcPixelOffset = srcLineOffset; dstPixelOffset < widthEnd; dstPixelOffset += dstPixelStride, srcPixelOffset += srcPixelStride) { double p = s[srcPixelOffset]; if (p >= l && p <= h) { d[dstPixelOffset] = c; } else { d[dstPixelOffset] = p; } } } } } private synchronized void initByteTable() { if (byteTable != null) return; /* Build a ramp lookup table for byte datatype. */ int numBands = getSampleModel().getNumBands(); byteTable = new byte[numBands][0x100]; for (int b = 0; b < numBands; b++) { double l = low[b]; double h = high[b]; byte c = (byte)constants[b]; byte[] t = byteTable[b]; for (int i = 0; i < 0x100; i++) { if (i >= l && i <= h) { t[i] = c; } else { t[i] = (byte)i; } } } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/AddCollectionOpImage.java0000644000175000017500000003447610203035544030057 0ustar mathieumathieu/* * $RCSfile: AddCollectionOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:11 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.util.Collection; import java.util.Iterator; import java.util.Vector; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PointOpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.RenderedOp; import java.util.Map; import com.sun.media.jai.util.ImageUtil; /** * An OpImage implementing the "AddCollection" operation. * * @see javax.media.jai.operator.AddCollectionDescriptor * @see AddCollectionCRIF * * * @since EA3 */ final class AddCollectionOpImage extends PointOpImage { private byte[][] byteTable = null; private synchronized void initByteTable() { if (byteTable != null) { return; } /* Initialize byteTable. */ byteTable = new byte[0x100][0x100]; for (int j = 0; j < 0x100; j++) { byte[] t = byteTable[j]; for (int i = 0; i < 0x100; i++) { t[i] = ImageUtil.clampBytePositive(j + i); } } } /** * Constructor. * * @param sources A collection of rendered images to be added. * @param layout The destination image layout. */ public AddCollectionOpImage(Collection sources, Map config, ImageLayout layout) { super(vectorize(sources), layout, config, true); } /** Put the rendered images in a vector. */ private static Vector vectorize(Collection sources) { if (sources instanceof Vector) { return (Vector)sources; } else { Vector v = new Vector(sources.size()); Iterator iter = sources.iterator(); while (iter.hasNext()) { v.add(iter.next()); } return v; } } /** * Adds the pixel values of the source images within a specified rectangle. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); int numSrcs = getNumSources(); RasterAccessor dst = new RasterAccessor(dest, destRect, formatTags[numSrcs], getColorModel()); RasterAccessor[] srcs = new RasterAccessor[numSrcs]; for (int i = 0; i < numSrcs; i++) { Rectangle srcRect = mapDestRect(destRect, i); srcs[i] = new RasterAccessor(sources[i], srcRect, formatTags[i], getSourceImage(i).getColorModel()); } switch (dst.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(srcs, dst); break; case DataBuffer.TYPE_USHORT: computeRectUShort(srcs, dst); break; case DataBuffer.TYPE_SHORT: computeRectShort(srcs, dst); break; case DataBuffer.TYPE_INT: computeRectInt(srcs, dst); break; case DataBuffer.TYPE_FLOAT: computeRectFloat(srcs, dst); break; case DataBuffer.TYPE_DOUBLE: computeRectDouble(srcs, dst); break; } if (dst.needsClamping()) { /* Further clamp down to underlying raster data type. */ dst.clampDataArrays(); } dst.copyDataToRaster(); } private void computeRectByte(RasterAccessor[] srcs, RasterAccessor dst) { initByteTable(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); byte[][] dstData = dst.getByteDataArrays(); int numSrcs = getNumSources(); for (int i = 0; i < numSrcs; i++) { RasterAccessor src = srcs[i]; int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); byte[][] srcData = src.getByteDataArrays(); for (int b = 0; b < dstBands; b++) { int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; byte[] d = dstData[b]; byte[] s = srcData[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = byteTable[d[dstPixelOffset]&0xff][s[srcPixelOffset]&0xff]; dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } } private void computeRectUShort(RasterAccessor[] srcs, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); short[][] dstData = dst.getShortDataArrays(); int numSrcs = getNumSources(); for (int i = 0; i < numSrcs; i++) { RasterAccessor src = srcs[i]; int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); short[][] srcData = src.getShortDataArrays(); for (int b = 0; b < dstBands; b++) { int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; short[] d = dstData[b]; short[] s = srcData[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = ImageUtil.clampUShortPositive( (d[dstPixelOffset] & 0xffff) + (s[srcPixelOffset] & 0xffff)); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } } private void computeRectShort(RasterAccessor[] srcs, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); short[][] dstData = dst.getShortDataArrays(); int numSrcs = getNumSources(); for (int i = 0; i < numSrcs; i++) { RasterAccessor src = srcs[i]; int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); short[][] srcData = src.getShortDataArrays(); for (int b = 0; b < dstBands; b++) { int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; short[] d = dstData[b]; short[] s = srcData[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = ImageUtil.clampShort( d[dstPixelOffset] + s[srcPixelOffset]); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } } private void computeRectInt(RasterAccessor[] srcs, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); int[][] dstData = dst.getIntDataArrays(); int numSrcs = getNumSources(); for (int i = 0; i < numSrcs; i++) { RasterAccessor src = srcs[i]; int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); int[][] srcData = src.getIntDataArrays(); for (int b = 0; b < dstBands; b++) { int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; int[] d = dstData[b]; int[] s = srcData[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = ImageUtil.clampInt( (long)d[dstPixelOffset] + (long)s[srcPixelOffset]); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } } private void computeRectFloat(RasterAccessor[] srcs, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); float[][] dstData = dst.getFloatDataArrays(); int numSrcs = getNumSources(); for (int i = 0; i < numSrcs; i++) { RasterAccessor src = srcs[i]; int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); float[][] srcData = src.getFloatDataArrays(); for (int b = 0; b < dstBands; b++) { int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; float[] d = dstData[b]; float[] s = srcData[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = ImageUtil.clampFloat( (double)d[dstPixelOffset] + (double)s[srcPixelOffset]); dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } } private void computeRectDouble(RasterAccessor[] srcs, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); double[][] dstData = dst.getDoubleDataArrays(); int numSrcs = getNumSources(); for (int i = 0; i < numSrcs; i++) { RasterAccessor src = srcs[i]; int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); double[][] srcData = src.getDoubleDataArrays(); for (int b = 0; b < dstBands; b++) { int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; double[] d = dstData[b]; double[] s = srcData[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = d[dstPixelOffset] + s[srcPixelOffset]; dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/JaiI18N.java0000644000175000017500000000075110203035544025201 0ustar mathieumathieu/* * $RCSfile: JaiI18N.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:30 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import com.sun.media.jai.util.PropertyUtil; class JaiI18N { static String packageName = "com.sun.media.jai.opimage"; public static String getString(String key) { return PropertyUtil.getString(packageName, key); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/AffineNearestBinaryOpImage.java0000644000175000017500000006674310460235767031252 0ustar mathieumathieu/* * $RCSfile: AffineNearestBinaryOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.3 $ * $Date: 2006-07-21 20:41:27 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferInt; import java.awt.image.DataBufferUShort; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationNearest; import javax.media.jai.JAI; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.util.Range; import javax.media.jai.BorderExtender; import java.util.Map; /** * An OpImage subclass that performs nearest-neighbour Affine mapping */ final class AffineNearestBinaryOpImage extends AffineNearestOpImage { // The background private int black = 0; // Since this operation deals with packed binary data, we do not need // to expand the IndexColorModel private static Map configHelper(Map configuration) { Map config; if (configuration == null) { config = new RenderingHints(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.FALSE); } else { config = configuration; if (!(config.containsKey(JAI.KEY_REPLACE_INDEX_COLOR_MODEL))) { RenderingHints hints = (RenderingHints)configuration; config = (RenderingHints)hints.clone(); config.put(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.FALSE); } } return config; } /** * Constructs an AffineNearestBinaryOpImage from a RenderedImage source, * * @param source a RenderedImage. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param interp an Interpolation object to use for resampling * @param transform the desired AffineTransform. */ public AffineNearestBinaryOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, AffineTransform transform, Interpolation interp, double[] backgroundValues) { super(source, extender, configHelper(config), layout, transform, interp, backgroundValues); // Propagate source's ColorModel and SampleModel but change tile size. if (layout != null) { colorModel = layout.getColorModel(source); } else { colorModel = source.getColorModel(); } sampleModel = source.getSampleModel().createCompatibleSampleModel(tileWidth, tileHeight); // Set the background to color to 1 if the color // model specifies that as "black", otherwise 0. // // This cause problem on the background (Bug 4380285): because // (1) if the tile does not intersect with the source, zeroed tile(white // is created; (2) if the line does not intersect with the source, // zeroed-line is created; (3) if the image area does not equal to // the whole destination rectangle, some white line will be displayed. // //if (colorModel.getRGB(1) == 0xff000000) { // black = 1; //} } /** * Performs an affine transform on a specified rectangle. The sources are * cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; switch (source.getSampleModel().getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(source, dest, destRect); break; case DataBuffer.TYPE_INT: intLoop(source, dest, destRect); break; case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_USHORT: shortLoop(source, dest, destRect); break; } } private void byteLoop(Raster source, WritableRaster dest, Rectangle destRect) { float src_rect_x1 = source.getMinX(); float src_rect_y1 = source.getMinY(); float src_rect_x2 = src_rect_x1 + source.getWidth(); float src_rect_y2 = src_rect_y1 + source.getHeight(); MultiPixelPackedSampleModel sourceSM = (MultiPixelPackedSampleModel)source.getSampleModel(); DataBufferByte sourceDB = (DataBufferByte)source.getDataBuffer(); int sourceTransX = source.getSampleModelTranslateX(); int sourceTransY = source.getSampleModelTranslateY(); int sourceDataBitOffset = sourceSM.getDataBitOffset(); int sourceScanlineStride = sourceSM.getScanlineStride(); MultiPixelPackedSampleModel destSM = (MultiPixelPackedSampleModel)dest.getSampleModel(); DataBufferByte destDB = (DataBufferByte)dest.getDataBuffer(); int destMinX = dest.getMinX(); int destMinY = dest.getMinY(); int destTransX = dest.getSampleModelTranslateX(); int destTransY = dest.getSampleModelTranslateY(); int destDataBitOffset = destSM.getDataBitOffset(); int destScanlineStride = destSM.getScanlineStride(); byte[] sourceData = sourceDB.getData(); int sourceDBOffset = sourceDB.getOffset(); byte[] destData = destDB.getData(); int destDBOffset = destDB.getOffset(); Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; int incyStride = incy*sourceScanlineStride; int incy1Stride = incy1*sourceScanlineStride; black = ((int)backgroundValues[0]) & 1; for (int y = dst_min_y; y < dst_max_y; y++) { // Backward map the first point in the line // The energy is at the (pt_x + 0.5, pt_y + 0.5) dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates float s_x = (float)src_pt.getX(); float s_y = (float)src_pt.getY(); // Floor to get the integral coordinate int s_ix = (int)Math.floor(s_x); int s_iy = (int)Math.floor(s_y); double fracx = s_x - (double)s_ix; double fracy = s_y - (double)s_iy; int ifracx = (int) Math.floor(fracx*geom_frac_max); int ifracy = (int) Math.floor(fracy*geom_frac_max); int start_s_ix = s_ix; int start_s_iy = s_iy; int start_ifracx = ifracx; int start_ifracy = ifracy; // Compute clipMinX, clipMinY Range clipRange = performScanlineClipping(src_rect_x1, src_rect_y1, // Last point in the source is // x2 = x1 + width - 1 // y2 = y1 + height - 1 src_rect_x2 - 1, src_rect_y2 - 1, s_ix, s_iy, ifracx, ifracy, dst_min_x, dst_max_x, 0, 0, 0, 0); int clipMinX = ((Integer)clipRange.getMinValue()).intValue(); int clipMaxX = ((Integer)clipRange.getMaxValue()).intValue(); if(clipMinX > clipMaxX) continue; int destYOffset = (y - destTransY)* destScanlineStride + destDBOffset; int destXOffset = destDataBitOffset + (dst_min_x - destTransX); int sourceYOffset = (s_iy - sourceTransY)* sourceScanlineStride + sourceDBOffset; int sourceXOffset = s_ix - sourceTransX + sourceDataBitOffset; for (int x = dst_min_x; x < clipMinX; x++) { if (setBackground) { int dindex = destYOffset + (destXOffset >> 3); int dshift = 7 - (destXOffset & 7); int delement = destData[dindex]; delement |= black << dshift; destData[dindex] = (byte)delement; } // walk if (ifracx < ifracdx1) { /* // DEBUG s_ix += incx; */ ifracx += ifracdx; sourceXOffset += incx; } else { /* // DEBUG s_ix += incx1; */ ifracx -= ifracdx1; sourceXOffset += incx1; } if (ifracy < ifracdy1) { /* // DEBUG s_iy += incy; */ ifracy += ifracdy; sourceYOffset += incyStride; } else { /* // DEBUG s_iy += incy1; */ ifracy -= ifracdy1; sourceYOffset += incy1Stride; } ++destXOffset; } for (int x = clipMinX; x < clipMaxX; x++) { int sindex = sourceYOffset + (sourceXOffset >> 3); byte selement = sourceData[sindex]; int val = (selement >> (7 - (sourceXOffset & 7))) & 1; int dindex = destYOffset + (destXOffset >> 3); int dshift = 7 - (destXOffset & 7); int delement = destData[dindex]; delement |= val << dshift; destData[dindex] = (byte)delement; // walk if (ifracx < ifracdx1) { /* // DEBUG s_ix += incx; */ ifracx += ifracdx; sourceXOffset += incx; } else { /* // DEBUG s_ix += incx1; */ ifracx -= ifracdx1; sourceXOffset += incx1; } if (ifracy < ifracdy1) { /* // DEBUG s_iy += incy; */ ifracy += ifracdy; sourceYOffset += incyStride; } else { /* // DEBUG s_iy += incy1; */ ifracy -= ifracdy1; sourceYOffset += incy1Stride; } ++destXOffset; } for (int x = clipMaxX; x < dst_max_x; x++) { if (setBackground) { int dindex = destYOffset + (destXOffset >> 3); int dshift = 7 - (destXOffset & 7); int delement = destData[dindex]; delement |= black << dshift; destData[dindex] = (byte)delement; } // walk if (ifracx < ifracdx1) { /* // DEBUG s_ix += incx; */ ifracx += ifracdx; sourceXOffset += incx; } else { /* // DEBUG s_ix += incx1; */ ifracx -= ifracdx1; sourceXOffset += incx1; } if (ifracy < ifracdy1) { /* // DEBUG s_iy += incy; */ ifracy += ifracdy; sourceYOffset += incyStride; } else { /* // DEBUG s_iy += incy1; */ ifracy -= ifracdy1; sourceYOffset += incy1Stride; } ++destXOffset; } } } private void shortLoop(Raster source, WritableRaster dest, Rectangle destRect) { float src_rect_x1 = source.getMinX(); float src_rect_y1 = source.getMinY(); float src_rect_x2 = src_rect_x1 + source.getWidth(); float src_rect_y2 = src_rect_y1 + source.getHeight(); MultiPixelPackedSampleModel sourceSM = (MultiPixelPackedSampleModel)source.getSampleModel(); DataBufferUShort sourceDB = (DataBufferUShort)source.getDataBuffer(); int sourceTransX = source.getSampleModelTranslateX(); int sourceTransY = source.getSampleModelTranslateY(); int sourceDataBitOffset = sourceSM.getDataBitOffset(); int sourceScanlineStride = sourceSM.getScanlineStride(); MultiPixelPackedSampleModel destSM = (MultiPixelPackedSampleModel)dest.getSampleModel(); DataBufferUShort destDB = (DataBufferUShort)dest.getDataBuffer(); int destMinX = dest.getMinX(); int destMinY = dest.getMinY(); int destTransX = dest.getSampleModelTranslateX(); int destTransY = dest.getSampleModelTranslateY(); int destDataBitOffset = destSM.getDataBitOffset(); int destScanlineStride = destSM.getScanlineStride(); short[] sourceData = sourceDB.getData(); int sourceDBOffset = sourceDB.getOffset(); short[] destData = destDB.getData(); int destDBOffset = destDB.getOffset(); Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; int incyStride = incy*sourceScanlineStride; int incy1Stride = incy1*sourceScanlineStride; black = ((int)backgroundValues[0]) & 1; for (int y = dst_min_y; y < dst_max_y; y++) { // Backward map the first point in the line // The energy is at the (pt_x + 0.5, pt_y + 0.5) dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates float s_x = (float)src_pt.getX(); float s_y = (float)src_pt.getY(); // Floor to get the integral coordinate int s_ix = (int)Math.floor(s_x); int s_iy = (int)Math.floor(s_y); double fracx = s_x - (double)s_ix; double fracy = s_y - (double)s_iy; int ifracx = (int) Math.floor(fracx*geom_frac_max); int ifracy = (int) Math.floor(fracy*geom_frac_max); int start_s_ix = s_ix; int start_s_iy = s_iy; int start_ifracx = ifracx; int start_ifracy = ifracy; // Compute clipMinX, clipMinY Range clipRange = performScanlineClipping(src_rect_x1, src_rect_y1, // Last point in the source is // x2 = x1 + width - 1 // y2 = y1 + height - 1 src_rect_x2 - 1, src_rect_y2 - 1, s_ix, s_iy, ifracx, ifracy, dst_min_x, dst_max_x, 0, 0, 0, 0); int clipMinX = ((Integer)clipRange.getMinValue()).intValue(); int clipMaxX = ((Integer)clipRange.getMaxValue()).intValue(); if(clipMinX > clipMaxX) continue; int destYOffset = (y - destTransY)* destScanlineStride + destDBOffset; int destXOffset = destDataBitOffset + (dst_min_x - destTransX); int sourceYOffset = (s_iy - sourceTransY)* sourceScanlineStride + sourceDBOffset; int sourceXOffset = s_ix - sourceTransX + sourceDataBitOffset; for (int x = dst_min_x; x < clipMinX; x++) { if (setBackground) { int dindex = destYOffset + (destXOffset >> 4); int dshift = 15 - (destXOffset & 15); int delement = destData[dindex]; delement |= black << dshift; destData[dindex] = (short)delement; } // walk if (ifracx < ifracdx1) { /* // DEBUG s_ix += incx; */ ifracx += ifracdx; sourceXOffset += incx; } else { /* // DEBUG s_ix += incx1; */ ifracx -= ifracdx1; sourceXOffset += incx1; } if (ifracy < ifracdy1) { /* // DEBUG s_iy += incy; */ ifracy += ifracdy; sourceYOffset += incyStride; } else { /* // DEBUG s_iy += incy1; */ ifracy -= ifracdy1; sourceYOffset += incy1Stride; } ++destXOffset; } for (int x = clipMinX; x < clipMaxX; x++) { int sindex = sourceYOffset + (sourceXOffset >> 4); short selement = sourceData[sindex]; int val = (selement >> (15 - (sourceXOffset & 15))) & 1; int dindex = destYOffset + (destXOffset >> 4); int dshift = 15 - (destXOffset & 15); int delement = destData[dindex]; delement |= val << dshift; destData[dindex] = (short)delement; // walk if (ifracx < ifracdx1) { /* // DEBUG s_ix += incx; */ ifracx += ifracdx; sourceXOffset += incx; } else { /* // DEBUG s_ix += incx1; */ ifracx -= ifracdx1; sourceXOffset += incx1; } if (ifracy < ifracdy1) { /* // DEBUG s_iy += incy; */ ifracy += ifracdy; sourceYOffset += incyStride; } else { /* // DEBUG s_iy += incy1; */ ifracy -= ifracdy1; sourceYOffset += incy1Stride; } ++destXOffset; } for (int x = clipMaxX; x < dst_max_x; x++) { if (setBackground) { int dindex = destYOffset + (destXOffset >> 4); int dshift = 15 - (destXOffset & 15); int delement = destData[dindex]; delement |= black << dshift; destData[dindex] = (short)delement; } // walk if (ifracx < ifracdx1) { /* // DEBUG s_ix += incx; */ ifracx += ifracdx; sourceXOffset += incx; } else { /* // DEBUG s_ix += incx1; */ ifracx -= ifracdx1; sourceXOffset += incx1; } if (ifracy < ifracdy1) { /* // DEBUG s_iy += incy; */ ifracy += ifracdy; sourceYOffset += incyStride; } else { /* // DEBUG s_iy += incy1; */ ifracy -= ifracdy1; sourceYOffset += incy1Stride; } ++destXOffset; } } } private void intLoop(Raster source, WritableRaster dest, Rectangle destRect) { float src_rect_x1 = source.getMinX(); float src_rect_y1 = source.getMinY(); float src_rect_x2 = src_rect_x1 + source.getWidth(); float src_rect_y2 = src_rect_y1 + source.getHeight(); MultiPixelPackedSampleModel sourceSM = (MultiPixelPackedSampleModel)source.getSampleModel(); DataBufferInt sourceDB = (DataBufferInt)source.getDataBuffer(); int sourceTransX = source.getSampleModelTranslateX(); int sourceTransY = source.getSampleModelTranslateY(); int sourceDataBitOffset = sourceSM.getDataBitOffset(); int sourceScanlineStride = sourceSM.getScanlineStride(); MultiPixelPackedSampleModel destSM = (MultiPixelPackedSampleModel)dest.getSampleModel(); DataBufferInt destDB = (DataBufferInt)dest.getDataBuffer(); int destMinX = dest.getMinX(); int destMinY = dest.getMinY(); int destTransX = dest.getSampleModelTranslateX(); int destTransY = dest.getSampleModelTranslateY(); int destDataBitOffset = destSM.getDataBitOffset(); int destScanlineStride = destSM.getScanlineStride(); int[] sourceData = sourceDB.getData(); int sourceDBOffset = sourceDB.getOffset(); int[] destData = destDB.getData(); int destDBOffset = destDB.getOffset(); Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; int incyStride = incy*sourceScanlineStride; int incy1Stride = incy1*sourceScanlineStride; black = ((int)backgroundValues[0]) & 1; for (int y = dst_min_y; y < dst_max_y; y++) { // Backward map the first point in the line // The energy is at the (pt_x + 0.5, pt_y + 0.5) dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates float s_x = (float)src_pt.getX(); float s_y = (float)src_pt.getY(); // Floor to get the integral coordinate int s_ix = (int)Math.floor(s_x); int s_iy = (int)Math.floor(s_y); double fracx = s_x - (double)s_ix; double fracy = s_y - (double)s_iy; int ifracx = (int) Math.floor(fracx*geom_frac_max); int ifracy = (int) Math.floor(fracy*geom_frac_max); int start_s_ix = s_ix; int start_s_iy = s_iy; int start_ifracx = ifracx; int start_ifracy = ifracy; // Compute clipMinX, clipMinY Range clipRange = performScanlineClipping(src_rect_x1, src_rect_y1, // Last point in the source is // x2 = x1 + width - 1 // y2 = y1 + height - 1 src_rect_x2 - 1, src_rect_y2 - 1, s_ix, s_iy, ifracx, ifracy, dst_min_x, dst_max_x, 0, 0, 0, 0); int clipMinX = ((Integer)clipRange.getMinValue()).intValue(); int clipMaxX = ((Integer)clipRange.getMaxValue()).intValue(); if(clipMinX > clipMaxX) continue; int destYOffset = (y - destTransY)* destScanlineStride + destDBOffset; int destXOffset = destDataBitOffset + (dst_min_x - destTransX); int sourceYOffset = (s_iy - sourceTransY)* sourceScanlineStride + sourceDBOffset; int sourceXOffset = s_ix - sourceTransX + sourceDataBitOffset; for (int x = dst_min_x; x < clipMinX; x++) { if (setBackground) { int dindex = destYOffset + (destXOffset >> 5); int dshift = 31 - (destXOffset & 31); int delement = destData[dindex]; delement |= black << dshift; destData[dindex] = (int)delement; } // walk if (ifracx < ifracdx1) { /* // DEBUG s_ix += incx; */ ifracx += ifracdx; sourceXOffset += incx; } else { /* // DEBUG s_ix += incx1; */ ifracx -= ifracdx1; sourceXOffset += incx1; } if (ifracy < ifracdy1) { /* // DEBUG s_iy += incy; */ ifracy += ifracdy; sourceYOffset += incyStride; } else { /* // DEBUG s_iy += incy1; */ ifracy -= ifracdy1; sourceYOffset += incy1Stride; } ++destXOffset; } for (int x = clipMinX; x < clipMaxX; x++) { int sindex = sourceYOffset + (sourceXOffset >> 5); int selement = sourceData[sindex]; int val = (selement >> (31 - (sourceXOffset & 31))) & 1; int dindex = destYOffset + (destXOffset >> 5); int dshift = 31 - (destXOffset & 31); int delement = destData[dindex]; delement |= val << dshift; destData[dindex] = (int)delement; // walk if (ifracx < ifracdx1) { /* // DEBUG s_ix += incx; */ ifracx += ifracdx; sourceXOffset += incx; } else { /* // DEBUG s_ix += incx1; */ ifracx -= ifracdx1; sourceXOffset += incx1; } if (ifracy < ifracdy1) { /* // DEBUG s_iy += incy; */ ifracy += ifracdy; sourceYOffset += incyStride; } else { /* // DEBUG s_iy += incy1; */ ifracy -= ifracdy1; sourceYOffset += incy1Stride; } ++destXOffset; } for (int x = clipMaxX; x < dst_max_x; x++) { if (setBackground) { int dindex = destYOffset + (destXOffset >> 5); int dshift = 31 - (destXOffset & 31); int delement = destData[dindex]; delement |= black << dshift; destData[dindex] = (int)delement; } // walk if (ifracx < ifracdx1) { /* // DEBUG s_ix += incx; */ ifracx += ifracdx; sourceXOffset += incx; } else { /* // DEBUG s_ix += incx1; */ ifracx -= ifracdx1; sourceXOffset += incx1; } if (ifracy < ifracdy1) { /* // DEBUG s_iy += incy; */ ifracy += ifracdy; sourceYOffset += incyStride; } else { /* // DEBUG s_iy += incy1; */ ifracy -= ifracdy1; sourceYOffset += incy1Stride; } ++destXOffset; } } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/PolarToComplexCRIF.java0000644000175000017500000000347510203035544027460 0ustar mathieumathieu/* * $RCSfile: PolarToComplexCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:40 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D.Float; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import java.awt.image.renderable.RenderContext; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "PolarToComplex" operation in the * rendered and renderable image layers. * * @see javax.media.jai.operator.PolarToComplexDescriptor * @see PolarToComplexOpImage * * @since EA4 */ public class PolarToComplexCRIF extends CRIFImpl { /** Constructor. */ public PolarToComplexCRIF() { super("polartocomplex"); } /** * Creates a new instance of PolarToComplexOpImage in the * rendered layer. This method satisfies the implementation of RIF. * * @param paramBlock The magnitude and phase images, respectively. * @param renderHints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new PolarToComplexOpImage(paramBlock.getRenderedSource(0), paramBlock.getRenderedSource(1), renderHints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MinFilterXOpImage.java0000644000175000017500000004222710203035544027365 0ustar mathieumathieu/* * $RCSfile: MinFilterXOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:35 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import java.util.Map; import javax.media.jai.operator.MinFilterDescriptor; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class to perform min filtering on a source image. * */ final class MinFilterXOpImage extends MinFilterOpImage { /** * Creates a MinFilterXOpImage with the given source and * maskSize. The image dimensions are derived from the source * image. The tile grid layout, SampleModel, and ColorModel may * optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. */ public MinFilterXOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, int maskSize) { super(source, extender, config, layout, MinFilterDescriptor.MIN_MASK_PLUS, maskSize); } protected void byteLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); byte dstDataArrays[][] = dst.getByteDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); byte srcDataArrays[][] = src.getByteDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int scanPlusPixelStride = srcScanlineStride+srcPixelStride; int scanMinusPixelStride = srcScanlineStride-srcPixelStride; int topRightOffset = srcPixelStride*(filterSize-1); int minval, val; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { byte dstData[] = dstDataArrays[k]; byte srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { minval = Integer.MAX_VALUE; // figure out where the top left of the X starts int imageOffset = srcPixelOffset; for (int u = 0; u < wp; u++) { val = (int)(srcData[imageOffset]&0xff); imageOffset += scanPlusPixelStride; minval = (val < minval) ? val : minval; } // figure out where the top right of the X starts imageOffset = srcPixelOffset + topRightOffset; for (int v = 0; v < wp; v++) { val = (int)(srcData[imageOffset]&0xff); imageOffset += scanMinusPixelStride; minval = (val < minval) ? val : minval; } dstData[dstPixelOffset] = (byte)minval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void shortLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int scanPlusPixelStride = srcScanlineStride+srcPixelStride; int scanMinusPixelStride = srcScanlineStride-srcPixelStride; int topRightOffset = srcPixelStride*(filterSize-1); int minval, val; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { minval = Integer.MAX_VALUE; // figure out where the top left of the X starts int imageOffset = srcPixelOffset; for (int u = 0; u < wp; u++) { val = (int)(srcData[imageOffset]); imageOffset += scanPlusPixelStride; minval = (val < minval) ? val : minval; } // figure out where the top right of the X starts imageOffset = srcPixelOffset + topRightOffset; for (int v = 0; v < wp; v++) { val = (int)(srcData[imageOffset]); imageOffset += scanMinusPixelStride; minval = (val < minval) ? val : minval; } dstData[dstPixelOffset] = (short)minval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void ushortLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int scanPlusPixelStride = srcScanlineStride+srcPixelStride; int scanMinusPixelStride = srcScanlineStride-srcPixelStride; int topRightOffset = srcPixelStride*(filterSize-1); int minval, val; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { minval = Integer.MAX_VALUE; // figure out where the top left of the X starts int imageOffset = srcPixelOffset; for (int u = 0; u < wp; u++) { val = (int)(srcData[imageOffset]&0xffff); imageOffset += scanPlusPixelStride; minval = (val < minval) ? val : minval; } // figure out where the top right of the X starts imageOffset = srcPixelOffset + topRightOffset; for (int v = 0; v < wp; v++) { val = (int)(srcData[imageOffset]&0xffff); imageOffset += scanMinusPixelStride; minval = (val < minval) ? val : minval; } dstData[dstPixelOffset] = (short)minval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void intLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); int dstDataArrays[][] = dst.getIntDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcDataArrays[][] = src.getIntDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int scanPlusPixelStride = srcScanlineStride+srcPixelStride; int scanMinusPixelStride = srcScanlineStride-srcPixelStride; int topRightOffset = srcPixelStride*(filterSize-1); int minval, val; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { int dstData[] = dstDataArrays[k]; int srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { minval = Integer.MAX_VALUE; // figure out where the top left of the X starts int imageOffset = srcPixelOffset; for (int u = 0; u < wp; u++) { val = srcData[imageOffset]; imageOffset += scanPlusPixelStride; minval = (val < minval) ? val : minval; } // figure out where the top right of the X starts imageOffset = srcPixelOffset + topRightOffset; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += scanMinusPixelStride; minval = (val < minval) ? val : minval; } dstData[dstPixelOffset] = minval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void floatLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); float dstDataArrays[][] = dst.getFloatDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); float srcDataArrays[][] = src.getFloatDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int scanPlusPixelStride = srcScanlineStride+srcPixelStride; int scanMinusPixelStride = srcScanlineStride-srcPixelStride; int topRightOffset = srcPixelStride*(filterSize-1); float minval, val; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { float dstData[] = dstDataArrays[k]; float srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { minval = Float.MAX_VALUE; // figure out where the top left of the X starts int imageOffset = srcPixelOffset; for (int u = 0; u < wp; u++) { val = srcData[imageOffset]; imageOffset += scanPlusPixelStride; minval = (val < minval) ? val : minval; } // figure out where the top right of the X starts imageOffset = srcPixelOffset + topRightOffset; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += scanMinusPixelStride; minval = (val < minval) ? val : minval; } dstData[dstPixelOffset] = minval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void doubleLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); double dstDataArrays[][] = dst.getDoubleDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); double srcDataArrays[][] = src.getDoubleDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int scanPlusPixelStride = srcScanlineStride+srcPixelStride; int scanMinusPixelStride = srcScanlineStride-srcPixelStride; int topRightOffset = srcPixelStride*(filterSize-1); double minval, val; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { double dstData[] = dstDataArrays[k]; double srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { minval = Double.MAX_VALUE; // figure out where the top left of the X starts int imageOffset = srcPixelOffset; for (int u = 0; u < wp; u++) { val = srcData[imageOffset]; imageOffset += scanPlusPixelStride; minval = (val < minval) ? val : minval; } // figure out where the top right of the X starts imageOffset = srcPixelOffset + topRightOffset; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += scanMinusPixelStride; minval = (val < minval) ? val : minval; } dstData[dstPixelOffset] = minval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } // public static OpImage createTestImage(OpImageTester oit) { // return new MinFilterXOpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource()), // 3); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MinCRIF.java0000644000175000017500000000316710203035544025271 0ustar mathieumathieu/* * $RCSfile: MinCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:34 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "Min" operation in the * rendered and renderable image layer. * * @see javax.media.jai.operator.MinDescriptor * @see MinOpImage * */ public class MinCRIF extends CRIFImpl { /** Constructor. */ public MinCRIF() { super("min"); } /** * Creates a new instance of MinOpImage in the rendered * layer. This method satisfies the implementation of RIF. * * @param paramBlock The two source images from which the minimum * pixel values are chosen. * @param renderHints Optionally contains destination image layout * and tile cache. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new MinOpImage(paramBlock.getRenderedSource(0), paramBlock.getRenderedSource(1), renderHints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/PiecewiseCRIF.java0000644000175000017500000000266410203035544026464 0ustar mathieumathieu/* * $RCSfile: PiecewiseCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:40 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "Piecewise" operation in the rendered * and renderable image layers. * * @see javax.media.jai.operator.PiecewiseDescriptor * @see PiecewiseOpImage * * * @since EA4 */ public class PiecewiseCRIF extends CRIFImpl { /** Constructor. */ public PiecewiseCRIF() { super("piecewise"); } /** * Creates a new instance of PiecewiseOpImage in the * rendered layer. * * @param args The source image and the breakpoints. * @param hints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new PiecewiseOpImage(args.getRenderedSource(0), renderHints, layout, (float[][][])args.getObjectParameter(0)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/LookupCRIF.java0000644000175000017500000000377710203035544026026 0ustar mathieumathieu/* * $RCSfile: LookupCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:30 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; import javax.media.jai.LookupTableJAI; /** * A CRIF supporting the "Lookup" operation in the * rendered and renderable image layers. * *

    Although Lookup is supported in the renderable layer, it is necessary * to understand that in some situations the operator may not produce smooth * results. This is due to an affine transform being performed on the source * image, which combined with certain types of table data will produce * random/unexpected destination values. In addition, a lookup operation * with the same input source and table in the renderable chain will yield * to different destination from different rendering. * * @see javax.media.jai.operator.LookupDescriptor * @see LookupOpImage * */ public class LookupCRIF extends CRIFImpl { /** Constructor. */ public LookupCRIF() { super("lookup"); } /** * Creates a new instance of LookupOpImage * in the rendered layer. * * @param args The source image and the lookup table. * @param hints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new LookupOpImage(args.getRenderedSource(0), renderHints, layout, (LookupTableJAI)args.getObjectParameter(0)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/InvertOpImage.java0000644000175000017500000002644010203035544026612 0ustar mathieumathieu/* * $RCSfile: InvertOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:29 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import javax.media.jai.ColormapOpImage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; /** * An OpImage implementing the "Invert" operation as * described in javax.media.jai.operator.InvertDescriptor. * *

    This OpImage negates the pixel values of the source * image on a per-band basis by subtracting the pixel values from the * maximum value of the respective data type. Please note that data type * byte is treated as unsigned with a maximum value of 0xFF. The value * of the pixel (x, y) in the destination image is defined as: *

     * for (b = 0; b < dst.numBands; b++) {
     *     dst[y][x][b] = maximumValue - src[y][x][b];
     * }
     * 
    * * @see javax.media.jai.operator.InvertDescriptor * @see InvertRIF * */ final class InvertOpImage extends ColormapOpImage { /** * Constructs an InvertOpImage. * *

    The layout parameter may optionally contains the * tile grid layout, sample model, and/or color model. The image * dimension is set to the same values as that of the source image. * *

    The image layout of the source image is used as the fall-back * for the image layout of the destination image. Any layout parameters * not specified in the layout argument are set to the * same value as that of the source. * * @param source The source image. * @param layout The destination image layout. */ public InvertOpImage(RenderedImage source, Map config, ImageLayout layout) { super(source, layout, config, true); // Set flag to permit in-place operation. permitInPlaceOperation(); // Initialize the colormap if necessary. initializeColormapOperation(); } /** * Transform the colormap according to the rescaling parameters. */ protected void transformColormap(byte[][] colormap) { for(int b = 0; b < 3; b++) { byte[] map = colormap[b]; int mapSize = map.length; for(int i = 0; i < mapSize; i++) { map[i] = (byte)(255 - (map[i] & 0xFF)); } } } /** * Inverts the pixel values within a specified rectangle. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); /* For ColormapOpImage, srcRect = destRect. */ RasterAccessor s = new RasterAccessor(sources[0], destRect, formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor d = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); if(d.isBinary()) { byte[] srcBits = s.getBinaryDataArray(); byte[] dstBits = d.getBinaryDataArray(); int length = dstBits.length; for(int i = 0; i < length; i++) { dstBits[i] = (byte)(~(srcBits[i])); } d.copyBinaryDataToRaster(); } else { switch (d.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(s, d); break; case DataBuffer.TYPE_USHORT: computeRectUShort(s, d); break; case DataBuffer.TYPE_SHORT: computeRectShort(s, d); break; case DataBuffer.TYPE_INT: computeRectInt(s, d); break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: throw new RuntimeException(JaiI18N.getString("InvertOpImage0")); } d.copyDataToRaster(); } } private void computeRectByte(RasterAccessor src, RasterAccessor dst) { int sLineStride = src.getScanlineStride(); int sPixelStride = src.getPixelStride(); int[] sBandOffsets = src.getBandOffsets(); byte[][] sData = src.getByteDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int bands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); byte[][] dData = dst.getByteDataArrays(); for (int b = 0; b < bands; b++) { byte[] s = sData[b]; byte[] d = dData[b]; int sLineOffset = sBandOffsets[b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; sLineOffset += sLineStride; dLineOffset += dLineStride; int dstEnd = dPixelOffset + dwidth*dPixelStride; while (dPixelOffset < dstEnd) { d[dPixelOffset] = (byte)(255 - (s[sPixelOffset]&0xFF)); sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; } } } } private void computeRectUShort(RasterAccessor src, RasterAccessor dst) { int sLineStride = src.getScanlineStride(); int sPixelStride = src.getPixelStride(); int[] sBandOffsets = src.getBandOffsets(); short[][] sData = src.getShortDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int bands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); short[][] dData = dst.getShortDataArrays(); for (int b = 0; b < bands; b++) { short[] s = sData[b]; short[] d = dData[b]; int sLineOffset = sBandOffsets[b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; sLineOffset += sLineStride; dLineOffset += dLineStride; int dstEnd = dPixelOffset + dwidth*dPixelStride; while (dPixelOffset < dstEnd) { d[dPixelOffset] = (short)(65535 - (s[sPixelOffset]&0xFFFF)); sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; } } } } private void computeRectShort(RasterAccessor src, RasterAccessor dst) { int sLineStride = src.getScanlineStride(); int sPixelStride = src.getPixelStride(); int[] sBandOffsets = src.getBandOffsets(); short[][] sData = src.getShortDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int bands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); short[][] dData = dst.getShortDataArrays(); for (int b = 0; b < bands; b++) { short[] s = sData[b]; short[] d = dData[b]; int sLineOffset = sBandOffsets[b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; sLineOffset += sLineStride; dLineOffset += dLineStride; int dstEnd = dPixelOffset + dwidth*dPixelStride; while (dPixelOffset < dstEnd) { d[dPixelOffset] = (short)(Short.MAX_VALUE - s[sPixelOffset]); sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; } } } } private void computeRectInt(RasterAccessor src, RasterAccessor dst) { int sLineStride = src.getScanlineStride(); int sPixelStride = src.getPixelStride(); int[] sBandOffsets = src.getBandOffsets(); int[][] sData = src.getIntDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int bands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); int[][] dData = dst.getIntDataArrays(); /* * For the TAG_INT_COPIED case, the destination data type may * be any of the integral data types. The "clamp" function must * clamp to the appropriate range for that data type. */ int[] s = sData[0]; int[] d = dData[0]; int pixels = d.length; /* * The pixel data array is actually retrieved using getPixels * so there's no need to worry about scanline stride, pixel * stride, band offset, data offset, etc. */ switch (sampleModel.getTransferType()) { case DataBuffer.TYPE_BYTE: for (int i = 0; i < pixels; i++) { d[i] = (~s[i]) & 0xFF; } break; case DataBuffer.TYPE_USHORT: for (int i = 0; i < pixels; i++) { d[i] = (~s[i]) & 0xFFFF; } break; case DataBuffer.TYPE_SHORT: for (int i = 0; i < pixels; i++) { d[i] = Short.MAX_VALUE - s[i]; } break; case DataBuffer.TYPE_INT: for (int b = 0; b < bands; b++) { s = sData[b]; d = dData[b]; int sLineOffset = sBandOffsets[b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; sLineOffset += sLineStride; dLineOffset += dLineStride; int dstEnd = dPixelOffset + dwidth*dPixelStride; while (dPixelOffset < dstEnd) { d[dPixelOffset] = Integer.MAX_VALUE - s[sPixelOffset]; sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; } } } break; } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MaxFilterOpImage.java0000644000175000017500000002024510203035544027233 0ustar mathieumathieu/* * $RCSfile: MaxFilterOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:32 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; import javax.media.jai.operator.MaxFilterShape; // import com.sun.media.jai.test.OpImageTester; /** * An abstract OpImage class that subclasses will use to perform * MaxFiltering with specific masks. * */ abstract class MaxFilterOpImage extends AreaOpImage { protected MaxFilterShape maskType; protected int maskSize; /** * Creates a MaxFilterOpImage given an image source, an * optional BorderExtender, a maskType and maskSize. The image * dimensions are derived the source image. The tile grid layout, * SampleModel, and ColorModel may optionally be specified by an * ImageLayout object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param maskType the filter mask type. * @param maskSize the filter mask size. */ public MaxFilterOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, MaxFilterShape maskType, int maskSize) { super(source, layout, config, true, extender, (maskSize-1)/2, (maskSize-1)/2, (maskSize/2), (maskSize/2)); this.maskType = maskType; this.maskSize = maskSize; } /** * Performs max filtering on a specified rectangle. The sources are * cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); Raster source = sources[0]; Rectangle srcRect = mapDestRect(destRect, 0); RasterAccessor srcAccessor = new RasterAccessor(source, srcRect, formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor dstAccessor = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(srcAccessor, dstAccessor, maskSize); break; case DataBuffer.TYPE_SHORT: shortLoop(srcAccessor, dstAccessor, maskSize); break; case DataBuffer.TYPE_USHORT: ushortLoop(srcAccessor, dstAccessor, maskSize); break; case DataBuffer.TYPE_INT: intLoop(srcAccessor, dstAccessor, maskSize); break; case DataBuffer.TYPE_FLOAT: floatLoop(srcAccessor, dstAccessor, maskSize); break; case DataBuffer.TYPE_DOUBLE: doubleLoop(srcAccessor, dstAccessor, maskSize); break; } // If the RasterAccessor object set up a temporary buffer for the // op to write to, tell the RasterAccessor to write that data // to the raster no that we're done with it. if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } /** Performs max filtering using the subclass's mask on byte data */ protected abstract void byteLoop(RasterAccessor src, RasterAccessor dst, int filterSize); /** Performs max filtering using the subclass's mask on short data */ protected abstract void shortLoop(RasterAccessor src, RasterAccessor dst, int filterSize); /** Performs max filtering using the subclass's mask on ushort data */ protected abstract void ushortLoop(RasterAccessor src, RasterAccessor dst, int filterSize); /** Performs max filtering using the subclass's mask on int data */ protected abstract void intLoop(RasterAccessor src, RasterAccessor dst, int filterSize); /** Performs max filtering using the subclass's mask on float data */ protected abstract void floatLoop(RasterAccessor src, RasterAccessor dst, int filterSize); /** Performs max filtering using the subclass's mask on double data */ protected abstract void doubleLoop(RasterAccessor src, RasterAccessor dst, int filterSize); /** Returns the max of the input integer array */ static final int maxFilter(int data[]) { if (data.length == 3) { int a = data[0]; int b = data[1]; int c = data[2]; if (a < b) { return (b < c ? c : b); } else { return (a < c ? c : a); } } int max = data[0]; for(int i = 1; i < data.length; i++) if(max < data[i]) max = data[i]; return max; } /** Returns the max of the input float array */ static final float maxFilterFloat(float data[]) { if (data.length == 3) { float a = data[0]; float b = data[1]; float c = data[2]; if (a < b) { return (b < c ? c : b); } else { return (a < c ? c : a); } } float max = data[0]; for(int i = 1; i < data.length; i++) if(max < data[i]) max = data[i]; return max; } /** Returns the max of the input double array */ static final double maxFilterDouble(double data[]) { if (data.length == 3) { double a = data[0]; double b = data[1]; double c = data[2]; if (a < b) { return (b < c ? c : b); } else { return (a < c ? c : a); } } double max = data[0]; for(int i = 1; i < data.length; i++) if(max < data[i]) max = data[i]; return max; } // // Calls a method on OpImage that uses introspection, to make this // // class, discover it's createTestImage() call, call it and then // // benchmark the performance of the created OpImage chain. // public static void main(String args[]) { // String classname = // "com.sun.media.jai.opimage.MaxFilterSquareOpImage"; // OpImageTester.performDiagnostics(classname,args); // classname = // "com.sun.media.jai.opimage.MaxFilterXOpImage"; // OpImageTester.performDiagnostics(classname,args); // classname = // "com.sun.media.jai.opimage.MaxFilterPlusOpImage"; // OpImageTester.performDiagnostics(classname,args); // classname = // "com.sun.media.jai.opimage.MaxFilterSeparableOpImage"; // OpImageTester.performDiagnostics(classname,args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/CropOpImage.java0000644000175000017500000001035310203035544026242 0ustar mathieumathieu/* * $RCSfile: CropOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:21 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Point; import java.awt.Rectangle; import java.awt.geom.Rectangle2D; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.util.Vector; import javax.media.jai.ImageLayout; import javax.media.jai.PointOpImage; import javax.media.jai.RasterFactory; /** * An OpImage to crop an image by a rectangular region. * *

    Tiles that are completely inside or intersect the cropped region * (this image's bounds) are simply forwarded from the source. Do NOT * create a child raster even if a tile is not contained in this image's * bounds. Tiles that are outside the croppped region result in a * null return. * *

    This operator maintains the source image's tile grid setting and * sample and color models. * * * @since EA4 */ final class CropOpImage extends PointOpImage { private static ImageLayout layoutHelper(RenderedImage source, float originX, float originY, float width, float height) { // Get minimum bounding box. Rectangle bounds = new Rectangle2D.Float( originX, originY, width, height).getBounds(); return new ImageLayout(bounds.x, bounds.y, bounds.width, bounds.height, source.getTileGridXOffset(), source.getTileGridYOffset(), source.getTileWidth(), source.getTileHeight(), source.getSampleModel(), source.getColorModel()); } /** * Construct an CropOpImage. * * @param source a RenderedImage. * @param originX the new cropping rectangle x origin. * @param originY the new cropping rectangle y origin. * @param width the width of the cropping rectangle. * @param height the width of the cropping rectangle. */ public CropOpImage(RenderedImage source, float originX, float originY, float width, float height) { super(source, layoutHelper(source, originX, originY, width, height), null, false); } /** * Returns false as computeTile() invocations * are forwarded to the RenderedImage source and are * therefore not unique objects in the global sense. */ public boolean computesUniqueTiles() { return false; } /** * Override computeTile() simply to invoke getTile(). Required * so that the TileScheduler may invoke computeTile(). */ public Raster computeTile(int tileX, int tileY) { return getTile(tileX, tileY); } /** * Returns a tile. * *

    For those tiles that are completely contained in or intersect * with this image's bounds (the cropped region), simply returns the * source tile. Tiles that are outside this image's bound result in * null return. * * @param tileX The X index of the tile. * @param tileY The Y index of the tile. */ public Raster getTile(int tileX, int tileY) { Raster tile = null; // the requested tile, to be returned /* Make sure the requested tile is inside this image's boundary. */ if (tileX >= getMinTileX() && tileX <= getMaxTileX() && tileY >= getMinTileY() && tileY <= getMaxTileY()) { /* * Get the source tile. * Do NOT create a child Raster even if the tile is not contained * but merely intersects this image's bounds. */ tile = getSourceImage(0).getTile(tileX, tileY); } return tile; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MeanOpImage.java0000644000175000017500000002660010203035544026221 0ustar mathieumathieu/* * $RCSfile: MeanOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:33 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Image; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.util.Hashtable; import java.util.LinkedList; import java.util.ListIterator; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PixelAccessor; import javax.media.jai.PlanarImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.ROI; import javax.media.jai.StatisticsOpImage; import javax.media.jai.UnpackedImageData; /** * An OpImage implementing the "Mean" operation as * described in javax.media.jai.operator.MeanDescriptor. * * @since EA2 * @see javax.media.jai.operator.MeanDescriptor * @see MeanCRIF * */ public class MeanOpImage extends StatisticsOpImage { private boolean isInitialized = false; /** * Note: For very large images, these two variables may be overflowed. * An alternative would be to have a set for each tile. But then, the * user could specify very large tile size. */ private double[] totalPixelValue; private int totalPixelCount; private PixelAccessor srcPA; private int srcSampleType; private final boolean tileIntersectsROI(int tileX, int tileY) { if (roi == null) { // ROI is entire tile return true; } else { return roi.intersects(tileXToX(tileX), tileYToY(tileY), tileWidth, tileHeight); } } /** * Constructs an MeanOpImage. * * @param source The source image. */ public MeanOpImage(RenderedImage source, ROI roi, int xStart, int yStart, int xPeriod, int yPeriod) { super(source, roi, xStart, yStart, xPeriod, yPeriod); } protected String[] getStatisticsNames() { return new String[] {"mean"}; } protected Object createStatistics(String name) { Object stats; if (name.equalsIgnoreCase("mean")) { stats = new double[sampleModel.getNumBands()]; } else { stats = java.awt.Image.UndefinedProperty; } return stats; } private final int startPosition(int pos, int start, int period) { int t = (pos - start) % period; if (t == 0) { return pos; } else { return (pos + (period - t)); } } protected void accumulateStatistics(String name, Raster source, Object stats) { if(!isInitialized) { srcPA = new PixelAccessor(getSourceImage(0)); srcSampleType = srcPA.sampleType == PixelAccessor.TYPE_BIT ? DataBuffer.TYPE_BYTE : srcPA.sampleType; totalPixelValue = new double[srcPA.numBands]; totalPixelCount = 0; isInitialized = true; } Rectangle srcBounds = getSourceImage(0).getBounds().intersection( source.getBounds()); LinkedList rectList; if (roi == null) { // ROI is the whole Raster rectList = new LinkedList(); rectList.addLast(srcBounds); } else { rectList = roi.getAsRectangleList(srcBounds.x, srcBounds.y, srcBounds.width, srcBounds.height); if (rectList == null) { return; // ROI does not intersect with Raster boundary. } } ListIterator iterator = rectList.listIterator(0); while (iterator.hasNext()) { Rectangle rect = srcBounds.intersection((Rectangle)iterator.next()); int tx = rect.x; int ty = rect.y; /* Find the actual ROI based on start and period. */ rect.x = startPosition(tx, xStart, xPeriod); rect.y = startPosition(ty, yStart, yPeriod); rect.width = tx + rect.width - rect.x; rect.height = ty + rect.height - rect.y; if (rect.isEmpty()) { continue; // no pixel to count in this rectangle } UnpackedImageData uid = srcPA.getPixels(source, rect, srcSampleType, false); switch (uid.type) { case DataBuffer.TYPE_BYTE: accumulateStatisticsByte(uid); break; case DataBuffer.TYPE_USHORT: accumulateStatisticsUShort(uid); break; case DataBuffer.TYPE_SHORT: accumulateStatisticsShort(uid); break; case DataBuffer.TYPE_INT: accumulateStatisticsInt(uid); break; case DataBuffer.TYPE_FLOAT: accumulateStatisticsFloat(uid); break; case DataBuffer.TYPE_DOUBLE: accumulateStatisticsDouble(uid); break; } } if(name.equalsIgnoreCase("mean")) { // This is a totally disgusting hack but no worse than the // code was before ... bpb 1 September 2000 double[] mean = (double[])stats; if (totalPixelCount != 0) { for (int i = 0; i < srcPA.numBands; i++) { mean[i] = totalPixelValue[i] / (double)totalPixelCount; } } } } private void accumulateStatisticsByte(UnpackedImageData uid) { Rectangle rect = uid.rect; byte[][] data = uid.getByteData(); int lineStride = uid.lineStride; int pixelStride = uid.pixelStride; int lineInc = lineStride * yPeriod; int pixelInc = pixelStride * xPeriod; for (int b = 0; b < srcPA.numBands; b++) { byte[] d = data[b]; int lastLine = uid.bandOffsets[b] + rect.height * lineStride; for (int lo = uid.bandOffsets[b]; lo < lastLine; lo += lineInc) { int lastPixel = lo + rect.width * pixelStride; for (int po = lo; po < lastPixel; po += pixelInc) { totalPixelValue[b] += d[po] & 0xff; } } } totalPixelCount += (int)Math.ceil((double)rect.height / yPeriod) * (int)Math.ceil((double)rect.width / xPeriod); } private void accumulateStatisticsUShort(UnpackedImageData uid) { Rectangle rect = uid.rect; short[][] data = uid.getShortData(); int lineStride = uid.lineStride; int pixelStride = uid.pixelStride; int lineInc = lineStride * yPeriod; int pixelInc = pixelStride * xPeriod; for (int b = 0; b < srcPA.numBands; b++) { short[] d = data[b]; int lastLine = uid.bandOffsets[b] + rect.height * lineStride; for (int lo = uid.bandOffsets[b]; lo < lastLine; lo += lineInc) { int lastPixel = lo + rect.width * pixelStride; for (int po = lo; po < lastPixel; po += pixelInc) { totalPixelValue[b] += d[po] & 0xffff; } } } totalPixelCount += (int)Math.ceil((double)rect.height / yPeriod) * (int)Math.ceil((double)rect.width / xPeriod); } private void accumulateStatisticsShort(UnpackedImageData uid) { Rectangle rect = uid.rect; short[][] data = uid.getShortData(); int lineStride = uid.lineStride; int pixelStride = uid.pixelStride; int lineInc = lineStride * yPeriod; int pixelInc = pixelStride * xPeriod; for (int b = 0; b < srcPA.numBands; b++) { short[] d = data[b]; int lastLine = uid.bandOffsets[b] + rect.height * lineStride; for (int lo = uid.bandOffsets[b]; lo < lastLine; lo += lineInc) { int lastPixel = lo + rect.width * pixelStride; for (int po = lo; po < lastPixel; po += pixelInc) { totalPixelValue[b] += d[po]; } } } totalPixelCount += (int)Math.ceil((double)rect.height / yPeriod) * (int)Math.ceil((double)rect.width / xPeriod); } private void accumulateStatisticsInt(UnpackedImageData uid) { Rectangle rect = uid.rect; int[][] data = uid.getIntData(); int lineStride = uid.lineStride; int pixelStride = uid.pixelStride; int lineInc = lineStride * yPeriod; int pixelInc = pixelStride * xPeriod; for (int b = 0; b < srcPA.numBands; b++) { int[] d = data[b]; int lastLine = uid.bandOffsets[b] + rect.height * lineStride; for (int lo = uid.bandOffsets[b]; lo < lastLine; lo += lineInc) { int lastPixel = lo + rect.width * pixelStride; for (int po = lo; po < lastPixel; po += pixelInc) { totalPixelValue[b] += d[po]; } } } totalPixelCount += (int)Math.ceil((double)rect.height / yPeriod) * (int)Math.ceil((double)rect.width / xPeriod); } private void accumulateStatisticsFloat(UnpackedImageData uid) { Rectangle rect = uid.rect; float[][] data = uid.getFloatData(); int lineStride = uid.lineStride; int pixelStride = uid.pixelStride; int lineInc = lineStride * yPeriod; int pixelInc = pixelStride * xPeriod; for (int b = 0; b < srcPA.numBands; b++) { float[] d = data[b]; int lastLine = uid.bandOffsets[b] + rect.height * lineStride; for (int lo = uid.bandOffsets[b]; lo < lastLine; lo += lineInc) { int lastPixel = lo + rect.width * pixelStride; for (int po = lo; po < lastPixel; po += pixelInc) { totalPixelValue[b] += d[po]; } } } totalPixelCount += (int)Math.ceil((double)rect.height / yPeriod) * (int)Math.ceil((double)rect.width / xPeriod); } private void accumulateStatisticsDouble(UnpackedImageData uid) { Rectangle rect = uid.rect; double[][] data = uid.getDoubleData(); int lineStride = uid.lineStride; int pixelStride = uid.pixelStride; int lineInc = lineStride * yPeriod; int pixelInc = pixelStride * xPeriod; for (int b = 0; b < srcPA.numBands; b++) { double[] d = data[b]; int lastLine = uid.bandOffsets[b] + rect.height * lineStride; for (int lo = uid.bandOffsets[b]; lo < lastLine; lo += lineInc) { int lastPixel = lo + rect.width * pixelStride; for (int po = lo; po < lastPixel; po += pixelInc) { totalPixelValue[b] += d[po]; } } } totalPixelCount += (int)Math.ceil((double)rect.height / yPeriod) * (int)Math.ceil((double)rect.width / xPeriod); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/BandMergeOpImage.java0000644000175000017500000005342410445566777027222 0ustar mathieumathieu/* * $RCSfile: BandMergeOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2006-06-19 18:33:35 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.IndexColorModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.util.Vector; import javax.media.jai.ImageLayout; import javax.media.jai.PointOpImage; import java.util.Map; import javax.media.jai.PixelAccessor; import javax.media.jai.UnpackedImageData; import javax.media.jai.RasterFactory; import com.sun.media.jai.util.JDKWorkarounds; /** * An OpImage implementing the "BandMerge" operation as * described in javax.media.jai.operator.BandMergeDescriptor. * *

    This OpImage bandmerges the pixel values of two or * more source images. * * The data type byte is treated as unsigned, with maximum * value as 255 and minimum value as 0. * * There is no attempt to rescale binary images to the approapriate * gray levels, such as 255 or 0. A lookup should be performed first * if so desired. * * @since JAI 1.1 * @see javax.media.jai.operator.BandMergeDescriptor * @see BandMergeCRIF * */ class BandMergeOpImage extends PointOpImage { // list of ColorModels required for IndexColorModel support ColorModel[] colorModels; /** * Constructs a BandMergeOpImage. * *

    The layout parameter may optionally contain the * tile grid layout, sample model, and/or color model. The image * dimension is determined by the intersection of the bounding boxes * of the source images. * *

    The image layout of the first source image, source1, * is used as the fallback for the image layout of the destination * image. The destination number of bands is the sum of all source * image bands. * * @param sources Vector of sources. * @param config Configurable attributes of the image including * configuration variables indexed by * RenderingHints.Keys and image properties indexed * by Strings or CaselessStringKeys. * This is simply forwarded to the superclass constructor. * @param layout The destination image layout. */ public BandMergeOpImage(Vector sources, Map config, ImageLayout layout) { super(sources, layoutHelper(sources, layout), config, true); // Set flag to permit in-place operation. permitInPlaceOperation(); // get ColorModels for IndexColorModel support int numSrcs = sources.size(); colorModels = new ColorModel[numSrcs]; for ( int i = 0; i < numSrcs; i++ ) { colorModels[i] = ((RenderedImage)sources.get(i)).getColorModel(); } } private static int totalNumBands(Vector sources) { int total = 0; for ( int i = 0; i < sources.size(); i++ ) { RenderedImage image = (RenderedImage) sources.get(i); if ( image.getColorModel() instanceof IndexColorModel ) { total += image.getColorModel().getNumComponents(); } else { total += image.getSampleModel().getNumBands(); } } return total; } private static ImageLayout layoutHelper(Vector sources, ImageLayout il) { ImageLayout layout = (il == null) ? new ImageLayout() : (ImageLayout)il.clone(); int numSources = sources.size(); // dest data type is the maximum of transfertype of source image // utilizing the monotonicity of data types. // dest number of bands = sum of source bands int destNumBands = totalNumBands(sources); int destDataType = DataBuffer.TYPE_BYTE; // initialize RenderedImage srci = (RenderedImage)sources.get(0); Rectangle destBounds = new Rectangle(srci.getMinX(), srci.getMinY(), srci.getWidth(), srci.getHeight()); for ( int i = 0; i < numSources; i++ ) { srci = (RenderedImage)sources.get(i); destBounds = destBounds.intersection(new Rectangle(srci.getMinX(), srci.getMinY(), srci.getWidth(), srci.getHeight())); int typei = srci.getSampleModel().getTransferType(); // NOTE: this depends on JDK ordering destDataType = typei > destDataType ? typei : destDataType; } SampleModel sm = layout.getSampleModel((RenderedImage)sources.get(0)); if ( sm.getNumBands() < destNumBands ) { int[] destOffsets = new int[destNumBands]; for(int i=0; i < destNumBands; i++) { destOffsets[i] = i; } // determine the proper width and height to use int destTileWidth = sm.getWidth(); int destTileHeight = sm.getHeight(); if(layout.isValid(ImageLayout.TILE_WIDTH_MASK)) { destTileWidth = layout.getTileWidth((RenderedImage)sources.get(0)); } if(layout.isValid(ImageLayout.TILE_HEIGHT_MASK)) { destTileHeight = layout.getTileHeight((RenderedImage)sources.get(0)); } sm = RasterFactory.createComponentSampleModel(sm, destDataType, destTileWidth, destTileHeight, destNumBands); layout.setSampleModel(sm); } ColorModel cm = layout.getColorModel(null); if ( cm != null && !JDKWorkarounds.areCompatibleDataModels(sm, cm)) { // Clear the mask bit if incompatible. layout.unsetValid(ImageLayout.COLOR_MODEL_MASK); } return layout; } /** * BandMerges the pixel values of two source images within a specified * rectangle. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { int destType = dest.getTransferType(); switch(destType){ case DataBuffer.TYPE_BYTE: byteLoop(sources, dest, destRect); break; case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_USHORT: shortLoop(sources, dest, destRect); break; case DataBuffer.TYPE_INT: intLoop(sources, dest, destRect); break; case DataBuffer.TYPE_FLOAT: floatLoop(sources, dest, destRect); break; case DataBuffer.TYPE_DOUBLE: doubleLoop(sources, dest, destRect); break; default: throw new RuntimeException(); } } private void byteLoop(Raster[] sources, WritableRaster dest, Rectangle destRect) { int nSrcs = sources.length; int[] snbands = new int[nSrcs]; PixelAccessor[] pas = new PixelAccessor[nSrcs]; for ( int i = 0; i < nSrcs; i++ ) { pas[i] = new PixelAccessor(sources[i].getSampleModel(), colorModels[i]); if ( colorModels[i] instanceof IndexColorModel ) { snbands[i] = colorModels[i].getNumComponents(); } else { snbands[i] = sources[i].getNumBands(); } } int dnbands = dest.getNumBands(); int destType = dest.getTransferType(); PixelAccessor d = new PixelAccessor(dest.getSampleModel(), null); UnpackedImageData dimd = d.getPixels(dest, destRect, //liney, destType, true); byte[][] dstdata = (byte[][])dimd.data; for ( int sindex = 0, db = 0; sindex < nSrcs; sindex++ ) { UnpackedImageData simd = colorModels[sindex] instanceof IndexColorModel ? pas[sindex].getComponents(sources[sindex], destRect, sources[sindex].getSampleModel().getTransferType()) : pas[sindex].getPixels(sources[sindex], destRect, sources[sindex].getSampleModel().getTransferType(), false); int srcPixelStride = simd.pixelStride; int srcLineStride = simd.lineStride; int dstPixelStride = dimd.pixelStride; int dstLineStride = dimd.lineStride; int dRectWidth = destRect.width; for ( int sb = 0; sb < snbands[sindex]; sb++, db++ ) { if ( db >= dnbands ) { // exceeding destNumBands; should not have happened break; } byte[] dstdatabandb = dstdata[db]; byte[][] srcdata = (byte[][])simd.data; byte[] srcdatabandsb = srcdata[sb]; int srcstart = simd.bandOffsets[sb]; int dststart = dimd.bandOffsets[db]; for(int y = 0; y < destRect.height; y++, srcstart += srcLineStride, dststart += dstLineStride){ for(int i=0, srcpos = srcstart, dstpos = dststart; i < dRectWidth; i++, srcpos += srcPixelStride, dstpos += dstPixelStride){ dstdatabandb[dstpos] = srcdatabandsb[srcpos]; } } } } d.setPixels(dimd); } private void shortLoop(Raster[] sources, WritableRaster dest, Rectangle destRect) { int nSrcs = sources.length; int[] snbands = new int[nSrcs]; PixelAccessor[] pas = new PixelAccessor[nSrcs]; for ( int i = 0; i < nSrcs; i++ ) { pas[i] = new PixelAccessor(sources[i].getSampleModel(), colorModels[i]); if ( colorModels[i] instanceof IndexColorModel ) { snbands[i] = colorModels[i].getNumComponents(); } else { snbands[i] = sources[i].getNumBands(); } } int dnbands = dest.getNumBands(); int destType = dest.getTransferType(); PixelAccessor d = new PixelAccessor(dest.getSampleModel(),null); UnpackedImageData dimd = d.getPixels(dest, destRect, //liney, destType, true); short[][] dstdata = (short[][])dimd.data; for ( int sindex = 0, db = 0; sindex < nSrcs; sindex++ ) { UnpackedImageData simd = colorModels[sindex] instanceof IndexColorModel ? pas[sindex].getComponents(sources[sindex], destRect, sources[sindex].getSampleModel().getTransferType()) : pas[sindex].getPixels(sources[sindex], destRect, sources[sindex].getSampleModel().getTransferType(), false); int srcPixelStride = simd.pixelStride; int srcLineStride = simd.lineStride; int dstPixelStride = dimd.pixelStride; int dstLineStride = dimd.lineStride; int dRectWidth = destRect.width; for ( int sb = 0; sb < snbands[sindex]; sb++, db++ ) { if ( db < dnbands ) { short[][] srcdata = (short[][])simd.data; int srcstart = simd.bandOffsets[sb]; int dststart = dimd.bandOffsets[db]; for(int y = 0; y < destRect.height; y++, srcstart += srcLineStride, dststart += dstLineStride){ for(int i=0, srcpos = srcstart, dstpos = dststart; i < dRectWidth; i++, srcpos += srcPixelStride, dstpos += dstPixelStride){ dstdata[db][dstpos] = srcdata[sb][srcpos]; } } } } } d.setPixels(dimd); } private void intLoop(Raster[] sources, WritableRaster dest, Rectangle destRect) { int nSrcs = sources.length; int[] snbands = new int[nSrcs]; PixelAccessor[] pas = new PixelAccessor[nSrcs]; for ( int i = 0; i < nSrcs; i++ ) { pas[i] = new PixelAccessor(sources[i].getSampleModel(), colorModels[i]); if ( colorModels[i] instanceof IndexColorModel ) { snbands[i] = colorModels[i].getNumComponents(); } else { snbands[i] = sources[i].getNumBands(); } } int dnbands = dest.getNumBands(); int destType = dest.getTransferType(); PixelAccessor d = new PixelAccessor(dest.getSampleModel(),null); UnpackedImageData dimd = d.getPixels(dest, destRect, //liney, destType, true); int[][] dstdata = (int[][])dimd.data; for ( int sindex = 0, db = 0; sindex < nSrcs; sindex++ ) { UnpackedImageData simd = colorModels[sindex] instanceof IndexColorModel ? pas[sindex].getComponents(sources[sindex], destRect, sources[sindex].getSampleModel().getTransferType()) : pas[sindex].getPixels(sources[sindex], destRect, sources[sindex].getSampleModel().getTransferType(), false); int srcPixelStride = simd.pixelStride; int srcLineStride = simd.lineStride; int dstPixelStride = dimd.pixelStride; int dstLineStride = dimd.lineStride; int dRectWidth = destRect.width; for ( int sb = 0; sb < snbands[sindex]; sb++, db++ ) { if ( db < dnbands ) { int[][] srcdata = (int[][])simd.data; int srcstart = simd.bandOffsets[sb]; int dststart = dimd.bandOffsets[db]; for(int y = 0; y < destRect.height; y++, srcstart += srcLineStride, dststart += dstLineStride){ for(int i=0, srcpos = srcstart, dstpos = dststart; i < dRectWidth; i++, srcpos += srcPixelStride, dstpos += dstPixelStride){ dstdata[db][dstpos] = srcdata[sb][srcpos]; } } } } } d.setPixels(dimd); } private void floatLoop(Raster[] sources, WritableRaster dest, Rectangle destRect) { int nSrcs = sources.length; int[] snbands = new int[nSrcs]; PixelAccessor[] pas = new PixelAccessor[nSrcs]; for ( int i = 0; i < nSrcs; i++ ) { pas[i] = new PixelAccessor(sources[i].getSampleModel(), colorModels[i]); if ( colorModels[i] instanceof IndexColorModel ) { snbands[i] = colorModels[i].getNumComponents(); } else { snbands[i] = sources[i].getNumBands(); } } int dnbands = dest.getNumBands(); int destType = dest.getTransferType(); PixelAccessor d = new PixelAccessor(dest.getSampleModel(), null); UnpackedImageData dimd = d.getPixels(dest, destRect, //liney, destType, true); float[][] dstdata = (float[][])dimd.data; for ( int sindex = 0, db = 0; sindex < nSrcs; sindex++ ) { UnpackedImageData simd = colorModels[sindex] instanceof IndexColorModel ? pas[sindex].getComponents(sources[sindex], destRect, sources[sindex].getSampleModel().getTransferType()) : pas[sindex].getPixels(sources[sindex], destRect, sources[sindex].getSampleModel().getTransferType(), false); int srcPixelStride = simd.pixelStride; int srcLineStride = simd.lineStride; int dstPixelStride = dimd.pixelStride; int dstLineStride = dimd.lineStride; int dRectWidth = destRect.width; for ( int sb = 0; sb < snbands[sindex]; sb++, db++ ) { if ( db < dnbands ) { float[][] srcdata = (float[][])simd.data; int srcstart = simd.bandOffsets[sb]; int dststart = dimd.bandOffsets[db]; for(int y = 0; y < destRect.height; y++, srcstart += srcLineStride, dststart += dstLineStride){ for(int i=0, srcpos = srcstart, dstpos = dststart; i < dRectWidth; i++, srcpos += srcPixelStride, dstpos += dstPixelStride){ dstdata[db][dstpos] = srcdata[sb][srcpos]; } } } } } d.setPixels(dimd); } private void doubleLoop(Raster[] sources, WritableRaster dest, Rectangle destRect) { int nSrcs = sources.length; int[] snbands = new int[nSrcs]; PixelAccessor[] pas = new PixelAccessor[nSrcs]; for ( int i = 0; i < nSrcs; i++ ) { pas[i] = new PixelAccessor(sources[i].getSampleModel(), colorModels[i]); if ( colorModels[i] instanceof IndexColorModel ) { snbands[i] = colorModels[i].getNumComponents(); } else { snbands[i] = sources[i].getNumBands(); } } int dnbands = dest.getNumBands(); int destType = dest.getTransferType(); PixelAccessor d = new PixelAccessor(dest.getSampleModel(), null); UnpackedImageData dimd = d.getPixels(dest, destRect, //liney, destType, true); double[][] dstdata = (double[][])dimd.data; for ( int sindex = 0, db = 0; sindex < nSrcs; sindex++ ) { UnpackedImageData simd = colorModels[sindex] instanceof IndexColorModel ? pas[sindex].getComponents(sources[sindex], destRect, sources[sindex].getSampleModel().getTransferType()) : pas[sindex].getPixels(sources[sindex], destRect, sources[sindex].getSampleModel().getTransferType(), false); int srcPixelStride = simd.pixelStride; int srcLineStride = simd.lineStride; int dstPixelStride = dimd.pixelStride; int dstLineStride = dimd.lineStride; int dRectWidth = destRect.width; for ( int sb = 0; sb < snbands[sindex]; sb++, db++ ) { if ( db < dnbands ) { double[][] srcdata = (double[][])simd.data; int srcstart = simd.bandOffsets[sb]; int dststart = dimd.bandOffsets[db]; for(int y = 0; y < destRect.height; y++, srcstart += srcLineStride, dststart += dstLineStride){ for(int i=0, srcpos = srcstart, dstpos = dststart; i < dRectWidth; i++, srcpos += srcPixelStride, dstpos += dstPixelStride){ dstdata[db][dstpos] = srcdata[sb][srcpos]; } } } } } d.setPixels(dimd); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/URLRIF.java0000644000175000017500000000574610203035544025112 0ustar mathieumathieu/* * $RCSfile: URLRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:46 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import java.io.InputStream; import java.io.IOException; import java.net.URL; import javax.media.jai.JAI; import javax.media.jai.OperationRegistry; import javax.media.jai.OpImage; import javax.media.jai.registry.RIFRegistry; import javax.media.jai.util.ImagingListener; import com.sun.media.jai.codec.ImageDecodeParam; import com.sun.media.jai.codec.SeekableStream; import com.sun.media.jai.util.ImageUtil; /** * @see javax.media.jai.operator.URLDescriptor * * @since EA4 * */ public class URLRIF implements RenderedImageFactory { /** Constructor. */ public URLRIF() {} /** * Creates an image from a URL. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { try { // Create a SeekableStream from the URL (first parameter). URL url = (URL)paramBlock.getObjectParameter(0); InputStream stream = url.openStream(); SeekableStream src = SeekableStream.wrapInputStream(stream, true); ImageDecodeParam param = null; if (paramBlock.getNumParameters() > 1) { param = (ImageDecodeParam)paramBlock.getObjectParameter(1); } ParameterBlock newParamBlock = new ParameterBlock(); newParamBlock.add(src); newParamBlock.add(param); RenderingHints.Key key = JAI.KEY_OPERATION_BOUND; int bound = OpImage.OP_NETWORK_BOUND; if (renderHints == null) { renderHints = new RenderingHints(key, new Integer(bound)); } else if (!renderHints.containsKey(key)) { renderHints.put(key, new Integer(bound)); } // Get the registry from the hints, if any. // Don't check for null hints as it cannot be null here. OperationRegistry registry = (OperationRegistry)renderHints.get(JAI.KEY_OPERATION_REGISTRY); // Create the image using the most preferred RIF for "stream". RenderedImage image = RIFRegistry.create(registry, "stream", newParamBlock, renderHints); // NB: StreamImage is defined in FileLoadRIF.java. return image == null ? null : new StreamImage(image, src); } catch (IOException e) { ImagingListener listener = ImageUtil.getImagingListener(renderHints); String message = JaiI18N.getString("URLRIF0"); listener.errorOccurred(message, e, this, false); // e.printStackTrace(); return null; } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MedianFilterOpImage.java0000644000175000017500000002730110203035544027703 0ustar mathieumathieu/* * $RCSfile: MedianFilterOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:33 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; import javax.media.jai.operator.MedianFilterShape; // import com.sun.media.jai.test.OpImageTester; /** * An abstract OpImage class that subclasses will use to perform * MedianFiltering with specific masks. * * */ abstract class MedianFilterOpImage extends AreaOpImage { protected MedianFilterShape maskType; protected int maskSize; /** * Creates a MedianFilterOpImage given an image source, an * optional BorderExtender, a maskType and maskSize. The image * dimensions are derived the source image. The tile grid layout, * SampleModel, and ColorModel may optionally be specified by an * ImageLayout object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param maskType the filter mask type. * @param maskSize the filter mask size. */ public MedianFilterOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, MedianFilterShape maskType, int maskSize) { super(source, layout, config, true, extender, (maskSize-1)/2, (maskSize-1)/2, (maskSize/2), (maskSize/2)); this.maskType = maskType; this.maskSize = maskSize; } /** * Performs median filtering on a specified rectangle. The sources are * cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); Raster source = sources[0]; Rectangle srcRect = mapDestRect(destRect, 0); RasterAccessor srcAccessor = new RasterAccessor(source, srcRect, formatTags[0], getSource(0).getColorModel()); RasterAccessor dstAccessor = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(srcAccessor, dstAccessor, maskSize); break; case DataBuffer.TYPE_SHORT: shortLoop(srcAccessor, dstAccessor, maskSize); break; case DataBuffer.TYPE_USHORT: ushortLoop(srcAccessor, dstAccessor, maskSize); break; case DataBuffer.TYPE_INT: intLoop(srcAccessor, dstAccessor, maskSize); break; case DataBuffer.TYPE_FLOAT: floatLoop(srcAccessor, dstAccessor, maskSize); break; case DataBuffer.TYPE_DOUBLE: doubleLoop(srcAccessor, dstAccessor, maskSize); break; } // If the RasterAccessor object set up a temporary buffer for the // op to write to, tell the RasterAccessor to write that data // to the raster no that we're done with it. if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } /** Performs median filtering using the subclass's mask on byte data */ protected abstract void byteLoop(RasterAccessor src, RasterAccessor dst, int filterSize); /** Performs median filtering using the subclass's mask on short data */ protected abstract void shortLoop(RasterAccessor src, RasterAccessor dst, int filterSize); /** Performs median filtering using the subclass's mask on ushort data */ protected abstract void ushortLoop(RasterAccessor src, RasterAccessor dst, int filterSize); /** Performs median filtering using the subclass's mask on int data */ protected abstract void intLoop(RasterAccessor src, RasterAccessor dst, int filterSize); /** Performs median filtering using the subclass's mask on float data */ protected abstract void floatLoop(RasterAccessor src, RasterAccessor dst, int filterSize); /** Performs median filtering using the subclass's mask on double data */ protected abstract void doubleLoop(RasterAccessor src, RasterAccessor dst, int filterSize); /** Returns the median of the input integer array */ protected int medianFilter(int data[]) { if (data.length == 3) { int a = data[0]; int b = data[1]; int c = data[2]; if (a < b) { if (b < c) { return b; } else { if (c > a) { return c; } else { return a; } } } else { if (a < c) { return a; } else { if (b < c) { return c; } else { return b; } } } } int left=0; int right=data.length-1; int target = data.length/2; while (true) { int oleft = left; int oright = right; int mid = data[(left+right)/2]; do { while(data[left]= target) { left = oleft; } else if (left a) { return c; } else { return a; } } } else { if (a < c) { return a; } else { if (b < c) { return c; } else { return b; } } } } int left=0; int right=data.length-1; int target = data.length/2; while (true) { int oleft = left; int oright = right; float mid = data[(left+right)/2]; do { while(data[left]= target) { left = oleft; } else if (left a) { return c; } else { return a; } } } else { if (a < c) { return a; } else { if (b < c) { return c; } else { return b; } } } } int left=0; int right=data.length-1; int target = data.length/2; while (true) { int oleft = left; int oright = right; double mid = data[(left+right)/2]; do { while(data[left]= target) { left = oleft; } else if (leftGeometricOpImage to * subsample binary images to gray scale images. Image scaling operations * require rectilinear backwards mapping and padding by the resampling * filter dimensions. * *

    When applying scale factors of scaleX, scaleY to a source image * with width of src_width and height of src_height, the resulting image * is defined to have the following bounds: * * * dst minX = floor(src minX * scaleX + transX) * dst minY = floor(src minY * scaleY + transY) * dst width = floor(src width * scaleX) * dst height = floor(src height * scaleY) * * *

    When interpolations which require padding the source such as Bilinear * or Bicubic interpolation are specified, the source needs to be extended * such that it has the extra pixels needed to compute all the destination * pixels. This extension is performed via the BorderExtender * class. The type of border extension can be specified as a * RenderingHint to the JAI.create method. * *

    If no BorderExtender is specified, the source will * not be extended. The scaled image size is still calculated * according to the formula specified above. However since there is not * enough source to compute all the destination pixels, only that * subset of the destination image's pixels which can be computed, * will be written in the destination. The rest of the destination * will be set to zeros. * * @see ScaleOpImage * */ class SubsampleBinaryToGray4x4OpImage extends GeometricOpImage { private int blockX = 4; private int blockY = 4; /** destination image width */ private int dWidth; /** destination image height*/ private int dHeight; /** the 1st pixel location for destination pixels, i.e., * the source pixel matrix * [yValues[j] yValues[j]+blockY] by [xValues[i] xValues[i]+blockX] * will be condensed to form pixel ith pixel in row j */ private int[] xValues; private int[] yValues; // a look up table; lut[i] counts 1s in binary expression of i private int[] lut; // convert from number of bits on count to gray value, with // scaling, i.e. if invScaleX,Y=3,3, then the possible bit // counts are 0..9, hence the lookup tables are [0..9] * 255/9. // there are 4 kinds of scaling, depending on area size // when invScaleX,Y are non integers, // [floor(invScaleY), ceil(invScaleY)] x [floor(invScaleX), ceil(invScaleX)] private byte[] lutGray; /** * Constructs a SubsampleBinaryToGray4x4OpImage from a RenderedImage * source, an optional BorderExtender, x and y scale * and translation factors, and an Interpolation * object. The image dimensions are determined by forward-mapping * the source bounds, and are passed to the superclass constructor * by means of the layout parameter. Other fields of * the layout are passed through unchanged. If * layout is null, a new * ImageLayout will be constructor to hold the bounds * information. * * Note that the scale factors are represented internally as Rational * numbers in order to workaround inexact device specific representation * of floating point numbers. For instance the floating point number 1.2 * is internally represented as 1.200001, which can throw the * calculations off during a forward/backward map. * *

    The Rational approximation is valid upto the sixth decimal place. * * @param layout an ImageLayout optionally containing * the tile grid layout, SampleModel, and * ColorModel, or null. * @param source a RenderedImage. * from this OpImage, or null. If * null, no caching will be performed. * @param cobbleSources a boolean indicating whether * computeRect expects contiguous sources. * @param extender a BorderExtender, or null. * @param interp an Interpolation object to use for * resampling. * @param scaleX scale factor along x axis. * @param scaleY scale factor along y axis. * * @throws IllegalArgumentException if combining the * source bounds with the layout parameter results in negative * output width or height. */ public SubsampleBinaryToGray4x4OpImage(RenderedImage source, ImageLayout layout, Map config){ super(vectorize(source), SubsampleBinaryToGrayOpImage.layoutHelper(source, 1.0F/4, 1.0F/4, layout, config), config, true, // cobbleSources, null, // extender null, // interpolation null); int srcWidth = source.getWidth(); int srcHeight= source.getHeight(); blockX = blockY = 4; dWidth = srcWidth / blockX; dHeight = srcHeight/ blockY; if (extender == null) { computableBounds = new Rectangle(0, 0, dWidth, dHeight); } else { // If extender is present we can write the entire destination. computableBounds = getBounds(); } // these can be delayed, such as placed in computeRect() buildLookupTables(); // compute the begining bit position of each row and column computeXYValues(dWidth, dHeight); } /** * Computes the source point corresponding to the supplied point. * * @param destPt the position in destination image coordinates * to map to source image coordinates. * * @return a Point2D of the same class as * destPt. * * @throws IllegalArgumentException if destPt is * null. * * @since JAI 1.1.2 */ public Point2D mapDestPoint(Point2D destPt) { if (destPt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } Point2D pt = (Point2D)destPt.clone(); pt.setLocation(destPt.getX()*4.0, destPt.getY()*4.0); return pt; } /** * Computes the destination point corresponding to the supplied point. * * @param sourcePt the position in source image coordinates * to map to destination image coordinates. * * @return a Point2D of the same class as * sourcePt. * * @throws IllegalArgumentException if sourcePt is * null. * * @since JAI 1.1.2 */ public Point2D mapSourcePoint(Point2D sourcePt) { if (sourcePt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } Point2D pt = (Point2D)sourcePt.clone(); pt.setLocation(sourcePt.getX()/4.0, sourcePt.getY()/4.0); return pt; } /** * Returns the minimum bounding box of the region of the destination * to which a particular Rectangle of the specified source * will be mapped. * * @param sourceRect the Rectangle in source coordinates. * @param sourceIndex the index of the source image. * * @return a Rectangle indicating the destination * bounding box, or null if the bounding box * is unknown. * * @throws IllegalArgumentException if sourceIndex is * negative or greater than the index of the last source. * @throws IllegalArgumentException if sourceRect is * null. */ protected Rectangle forwardMapRect(Rectangle sourceRect, int sourceIndex) { if ( sourceRect == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sourceIndex != 0) { throw new IllegalArgumentException(JaiI18N.getString("Generic1")); } // Get the source dimensions int x0 = sourceRect.x; int y0 = sourceRect.y; int dx0 = x0 / blockX; int dy0 = y0 / blockY; int x1 = sourceRect.x + sourceRect.width - 1; int y1 = sourceRect.y + sourceRect.height- 1; int dx1 = x1 / blockX; int dy1 = y1 / blockY; // Return the writable destination area return new Rectangle(dx0, dy0, dx1-dx0+1, dy1-dy0+1); } /** * Returns the minimum bounding box of the region of the specified * source to which a particular Rectangle of the * destination will be mapped. * * @param destRect the Rectangle in destination coordinates. * @param sourceIndex the index of the source image. * * @return a Rectangle indicating the source bounding box, * or null if the bounding box is unknown. * * @throws IllegalArgumentException if sourceIndex is * negative or greater than the index of the last source. * @throws IllegalArgumentException if destRect is * null. */ protected Rectangle backwardMapRect(Rectangle destRect, int sourceIndex) { if ( destRect == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sourceIndex != 0) { throw new IllegalArgumentException(JaiI18N.getString("Generic1")); } // Get the destination rectangle coordinates and dimensions int sx0 = destRect.x * blockX; int sy0 = destRect.y * blockY; int sx1 = (destRect.x + destRect.width -1) * blockX; int sy1 = (destRect.y + destRect.height-1) * blockY; return new Rectangle(sx0, sy0, sx1 - sx0 + blockX, sy1 - sy0 + blockY); } /** * Performs a subsamplebinarytogray operation on a specified rectangle. * The sources are cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; switch (source.getSampleModel().getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_INT: byteLoop4x4(source, dest, destRect); break; default: throw new RuntimeException(JaiI18N.getString("SubsampleBinaryToGrayOpImage0")); } } // speed up for the case of 4x4 // and data buffer bitOffset is 0 or 4 private void byteLoop4x4(Raster source, WritableRaster dest, Rectangle destRect) { PixelAccessor pa = new PixelAccessor(source.getSampleModel(), null); PackedImageData pid = pa.getPackedPixels(source, source.getBounds(), false, false); if(pid.bitOffset % 4 !=0){ // special treatment only for offsets 0 and 4 byteLoop(source, dest, destRect); return; } byte[] sourceData = pid.data; int sourceDBOffset = pid.offset; int dx = destRect.x; int dy = destRect.y; int dwi = destRect.width; int dhi = destRect.height; int sourceTransX = pid.rect.x; // source.getSampleModelTranslateX(); int sourceTransY = pid.rect.y; // source.getSampleModelTranslateY(); int sourceDataBitOffset = pid.bitOffset; int sourceScanlineStride = pid.lineStride; PixelInterleavedSampleModel destSM = (PixelInterleavedSampleModel)dest.getSampleModel(); DataBufferByte destDB = (DataBufferByte)dest.getDataBuffer(); int destTransX = dest.getSampleModelTranslateX(); int destTransY = dest.getSampleModelTranslateY(); int destScanlineStride = destSM.getScanlineStride(); byte[] destData = destDB.getData(); int destDBOffset = destDB.getOffset(); int[] sAreaBitsOn = new int[2]; for(int j = 0; j < dhi; j++) { int y = (dy + j) << 2; //int y = (dy + j) * blockY; int sourceYOffset = (y - sourceTransY)*sourceScanlineStride + sourceDBOffset; int destYOffset = (j + dy - destTransY)*destScanlineStride + destDBOffset; destYOffset += dx - destTransX; int selement, sbitnumi, sstartbiti, sbytenumi; // sbitnumi - the 1st bit position from the minX of the raster // sstartbiti - the 1st bit position in the byte data //sbitnumi = blockX * dx - sourceTransX + sourceDataBitOffset; sbitnumi = (dx<<2) - sourceTransX + sourceDataBitOffset; for(int i=0; i < dwi; ){ sbytenumi = sbitnumi >> 3; sstartbiti = sbitnumi % 8; int byteindex = sourceYOffset + sbytenumi; sAreaBitsOn[0] = sAreaBitsOn[1] = 0; for(int k=0; k < 4; k++, byteindex += sourceScanlineStride){ selement = 0x00ff & (int)sourceData[byteindex]; sAreaBitsOn[1] += lut[selement & 0x000f]; sAreaBitsOn[0] += lut[selement>>4]; } // set dest elements // count in 4s // sstartbiti = 0 means the 0th of sAreaBitsOn is added to // current dest position, ie destYOffset + i; // sstartbiti = 4 means the 1th of sAreaBitsOn is added to // current dest position, ie destYOffset + i; // sstartbiti now means different // sstartbiti = sstartbiti / 4; sstartbiti >>= 2; while (sstartbiti < 2 && i < dwi){ destData[destYOffset + i] = lutGray[sAreaBitsOn[sstartbiti]]; sstartbiti++; i++; sbitnumi += blockX; } } } } // same as SubsampleBinaryToGray, and change byteLoop to protected in superclass? // extends that and save this? using prote private void byteLoop(Raster source, WritableRaster dest, Rectangle destRect) { PixelAccessor pa = new PixelAccessor(source.getSampleModel(), null); PackedImageData pid = pa.getPackedPixels(source, source.getBounds(), false, false); byte[] sourceData = pid.data; int sourceDBOffset = pid.offset; int dx = destRect.x; int dy = destRect.y; int dwi = destRect.width; int dhi = destRect.height; int sourceTransX = pid.rect.x; // source.getSampleModelTranslateX(); int sourceTransY = pid.rect.y; // source.getSampleModelTranslateY(); int sourceDataBitOffset = pid.bitOffset; int sourceScanlineStride = pid.lineStride; PixelInterleavedSampleModel destSM = (PixelInterleavedSampleModel)dest.getSampleModel(); DataBufferByte destDB = (DataBufferByte)dest.getDataBuffer(); int destTransX = dest.getSampleModelTranslateX(); int destTransY = dest.getSampleModelTranslateY(); int destScanlineStride = destSM.getScanlineStride(); byte[] destData = destDB.getData(); int destDBOffset = destDB.getOffset(); int[] sbytenum = new int[dwi]; int[] sstartbit= new int[dwi]; int[] sAreaBitsOn = new int[dwi]; for (int i = 0; i < dwi; i++) { int x = xValues[dx+i]; int sbitnum = sourceDataBitOffset + (x - sourceTransX); sbytenum[i] = sbitnum >> 3; sstartbit[i] = sbitnum % 8; } for(int j = 0; j < dhi; j++) { for(int i=0; i < dwi; i++){ sAreaBitsOn[i] = 0; } for(int y = yValues[dy+j]; y < yValues[dy+j]+blockY; y++){ int sourceYOffset = (y - sourceTransY)*sourceScanlineStride + sourceDBOffset; int delement, selement, sendbiti, sendbytenumi; for(int i=0; i < dwi; i++){ delement = 0; sendbiti = sstartbit[i] + blockX - 1; sendbytenumi = sbytenum[i] + (sendbiti >> 3); // byte num of the end bit sendbiti %= 8; // true src end bit position selement = 0x00ff & (int)sourceData[sourceYOffset + sbytenum[i]]; int swingBits = 24 + sstartbit[i]; if(sbytenum[i]==sendbytenumi){ // selement <<= 24 + sstartbit[i]; selement <<= swingBits; selement >>>= 31 - sendbiti + sstartbit[i]; delement += lut[selement]; }else{ selement <<= swingBits; selement >>>= swingBits; // selement >>>= 24; delement += lut[selement]; for(int b=sbytenum[i]+1; b< sendbytenumi; b++){ selement = 0x00ff & (int)sourceData[sourceYOffset + b]; delement += lut[selement]; } selement = 0x00ff & (int)sourceData[sourceYOffset + sendbytenumi]; selement >>>= 7-sendbiti; delement += lut[selement]; } sAreaBitsOn[i] += delement; } } int destYOffset = (j + dy - destTransY)*destScanlineStride + destDBOffset; destYOffset += dx - destTransX; // update dest values for row j in raster for(int i = 0; i < dwi; i++){ destData[destYOffset + i] = lutGray[sAreaBitsOn[i]]; } } } // buildLookupTables() // initializes variabes bitSet and lut // to be called mainly in the constructor private final void buildLookupTables(){ // lut lut = new int[16]; lut[0] = 0; lut[1] = 1; lut[2] = 1; lut[3] = 2; lut[4] = 1; lut[5] = 2; lut[6] = 2; lut[7] = 3; for(int i =8; i < 16; i++) lut[i] = 1 + lut[i-8]; //for(int i= 16; i < 256; i++) lut[i] = lut[i&(0x0f)] + lut[(i>>4)&(0x0f)]; // lutGray if (lutGray != null) return; lutGray = new byte[blockX * blockY +1]; for (int i=0; i < lutGray.length; i++){ int tmp = (int)Math.round(255.0F*i/(lutGray.length-1.0F)); lutGray[i] = tmp>255? (byte)0xff : (byte)tmp; } // switch black-white if needed if (SubsampleBinaryToGrayOpImage.isMinWhite( this.getSourceImage(0).getColorModel())){ for(int i=0; i < lutGray.length; i++) lutGray[i]= (byte)(255-(0xff &lutGray[i])); } } private void computeXYValues(int dstWidth, int dstHeight){ if (xValues==null || yValues == null){ xValues = new int[dstWidth]; yValues = new int[dstHeight]; } for(int i= 0; i < dstWidth; i++){ //xValues[i] = blockX * i; xValues[i] = i << 2; } for(int i=0; i < dstHeight; i++){ //yValues[i] = blockY * i; yValues[i] = i << 2; } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/OrOpImage.java0000644000175000017500000003104110203035544025714 0ustar mathieumathieu/* * $RCSfile: OrOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:38 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PointOpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage implementing the "Or" operation as * described in javax.media.jai.operator.OrDescriptor. * *

    This OpImage logically "ors" the pixel values of two source * images on a per-band basis. In case the two source images have different * number of bands, the number of bands for the destination image is the * smaller band number of the two source images. That is * dstNumBands = Math.min(src1NumBands, src2NumBands). * In case the two source images have different data types, the data type * for the destination image is the bigger data type of the two source * images. * *

    The value of the pixel (x, y) in the destination image is defined as: *

     * for (b = 0; b < numBands; b++) {
     *     dst[y][x][b] = src1[y][x][b] | src2[y][x][b];
     * }
     * 
    * * The data type byte is treated as unsigned, with maximum * value as 255 and minimum value as 0. * * @since EA2 * @see javax.media.jai.operator.OrDescriptor * @see OrCRIF * */ final class OrOpImage extends PointOpImage { /** * Constructs an OrOpImage. * *

    The layout parameter may optionally contains the * tile grid layout, sample model, and/or color model. The image * dimension is determined by the intersection of the bounding boxes * of the two source images. * *

    The image layout of the first source image, source1, * is used as the fall-back for the image layout of the destination * image. Any layout parameters not specified in the layout * argument are set to the same value as that of source1. * * @param source1 The first source image. * @param source2 The second source image. * @param layout The destination image layout. */ public OrOpImage(RenderedImage source1, RenderedImage source2, Map config, ImageLayout layout) { super(source1, source2, layout, config, true); // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * Ors the pixel values of two source images within a specified * rectangle. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); /* For PointOpImage, srcRect = destRect. */ RasterAccessor s1 = new RasterAccessor(sources[0], destRect, formatTags[0], getSource(0).getColorModel()); RasterAccessor s2 = new RasterAccessor(sources[1], destRect, formatTags[1], getSource(1).getColorModel()); RasterAccessor d = new RasterAccessor(dest, destRect, formatTags[2], getColorModel()); if(d.isBinary()) { byte[] src1Bits = s1.getBinaryDataArray(); byte[] src2Bits = s2.getBinaryDataArray(); byte[] dstBits = d.getBinaryDataArray(); int length = dstBits.length; for(int i = 0; i < length; i++) { dstBits[i] = (byte)(src1Bits[i] | src2Bits[i]); } d.copyBinaryDataToRaster(); return; } int src1LineStride = s1.getScanlineStride(); int src1PixelStride = s1.getPixelStride(); int[] src1BandOffsets = s1.getBandOffsets(); int src2LineStride = s2.getScanlineStride(); int src2PixelStride = s2.getPixelStride(); int[] src2BandOffsets = s2.getBandOffsets(); int dstNumBands = d.getNumBands(); int dstWidth = d.getWidth(); int dstHeight = d.getHeight(); int dstLineStride = d.getScanlineStride(); int dstPixelStride = d.getPixelStride(); int[] dstBandOffsets = d.getBandOffsets(); switch (d.getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(dstNumBands, dstWidth, dstHeight, src1LineStride, src1PixelStride, src1BandOffsets, s1.getByteDataArrays(), src2LineStride, src2PixelStride, src2BandOffsets, s2.getByteDataArrays(), dstLineStride, dstPixelStride, dstBandOffsets, d.getByteDataArrays()); break; case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: shortLoop(dstNumBands, dstWidth, dstHeight, src1LineStride, src1PixelStride, src1BandOffsets, s1.getShortDataArrays(), src2LineStride, src2PixelStride, src2BandOffsets, s2.getShortDataArrays(), dstLineStride, dstPixelStride, dstBandOffsets, d.getShortDataArrays()); break; case DataBuffer.TYPE_INT: intLoop(dstNumBands, dstWidth, dstHeight, src1LineStride, src1PixelStride, src1BandOffsets, s1.getIntDataArrays(), src2LineStride, src2PixelStride, src2BandOffsets, s2.getIntDataArrays(), dstLineStride, dstPixelStride, dstBandOffsets, d.getIntDataArrays()); break; } d.copyDataToRaster(); } private void byteLoop(int dstNumBands, int dstWidth, int dstHeight, int src1LineStride, int src1PixelStride, int[] src1BandOffsets, byte[][] src1Data, int src2LineStride, int src2PixelStride, int[] src2BandOffsets, byte[][] src2Data, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, byte[][] dstData) { for (int b = 0; b < dstNumBands; b++) { byte[] s1 = src1Data[b]; byte[] s2 = src2Data[b]; byte[] d = dstData[b]; int src1LineOffset = src1BandOffsets[b]; int src2LineOffset = src2BandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int src1PixelOffset = src1LineOffset; int src2PixelOffset = src2LineOffset; int dstPixelOffset = dstLineOffset; src1LineOffset += src1LineStride; src2LineOffset += src2LineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = (byte)(s1[src1PixelOffset] | s2[src2PixelOffset]); src1PixelOffset += src1PixelStride; src2PixelOffset += src2PixelStride; dstPixelOffset += dstPixelStride; } } } } private void shortLoop(int dstNumBands, int dstWidth, int dstHeight, int src1LineStride, int src1PixelStride, int[] src1BandOffsets, short[][] src1Data, int src2LineStride, int src2PixelStride, int[] src2BandOffsets, short[][] src2Data, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, short[][] dstData) { for (int b = 0; b < dstNumBands; b++) { short[] s1 = src1Data[b]; short[] s2 = src2Data[b]; short[] d = dstData[b]; int src1LineOffset = src1BandOffsets[b]; int src2LineOffset = src2BandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int src1PixelOffset = src1LineOffset; int src2PixelOffset = src2LineOffset; int dstPixelOffset = dstLineOffset; src1LineOffset += src1LineStride; src2LineOffset += src2LineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = (short)(s1[src1PixelOffset] | s2[src2PixelOffset]); src1PixelOffset += src1PixelStride; src2PixelOffset += src2PixelStride; dstPixelOffset += dstPixelStride; } } } } private void intLoop(int dstNumBands, int dstWidth, int dstHeight, int src1LineStride, int src1PixelStride, int[] src1BandOffsets, int[][] src1Data, int src2LineStride, int src2PixelStride, int[] src2BandOffsets, int[][] src2Data, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, int[][] dstData) { for (int b = 0; b < dstNumBands; b++) { int[] s1 = src1Data[b]; int[] s2 = src2Data[b]; int[] d = dstData[b]; int src1LineOffset = src1BandOffsets[b]; int src2LineOffset = src2BandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int src1PixelOffset = src1LineOffset; int src2PixelOffset = src2LineOffset; int dstPixelOffset = dstLineOffset; src1LineOffset += src1LineStride; src2LineOffset += src2LineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = s1[src1PixelOffset] | s2[src2PixelOffset]; src1PixelOffset += src1PixelStride; src2PixelOffset += src2PixelStride; dstPixelOffset += dstPixelStride; } } } } // public static void main(String args[]) { // System.out.println("OrOpImage Test"); // ImageLayout layout; // OpImage src1, src2, dst; // Rectangle rect = new Rectangle(0, 0, 5, 5); // System.out.println("1. PixelInterleaved byte 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 800, 800, 0, 0, 200, 200, DataBuffer.TYPE_BYTE, 3, false); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new OrOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("2. Banded byte 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 800, 800, 0, 0, 200, 200, DataBuffer.TYPE_BYTE, 3, true); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new OrOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("3. PixelInterleaved int 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_INT, 3, false); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new OrOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("4. Banded int 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_INT, 3, true); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new OrOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ConjugateCRIF.java0000644000175000017500000000256310203035544026464 0ustar mathieumathieu/* * $RCSfile: ConjugateCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:19 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "Conjugate" operation in the * rendered and renderable image layer. * * @see javax.media.jai.operator.ConjugateDescriptor * @see ConjugateOpImage * * @since EA4 */ public class ConjugateCRIF extends CRIFImpl { /** Constructor. */ public ConjugateCRIF() { super("conjugate"); } /** * Creates a new instance of ConjugateOpImage in the rendered * layer. * * @param paramBlock The source image of which to take the conjugate. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new ConjugateOpImage(paramBlock.getRenderedSource(0), renderHints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/XorConstCRIF.java0000644000175000017500000000274010203035544026321 0ustar mathieumathieu/* * $RCSfile: XorConstCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:48 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "XorConst" operation in the rendered * and renderable image layers. * * @see javax.media.jai.operator.XorConstDescriptor * @see XorConstOpImage * * * @since EA2 */ public class XorConstCRIF extends CRIFImpl { /** Constructor. */ public XorConstCRIF() { super("xorconst"); } /** * Creates a new instance of XorConstOpImage in the * rendered layer. * * @param args The source image and the constants. * @param hints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new XorConstOpImage(args.getRenderedSource(0), renderHints, layout, (int[])args.getObjectParameter(0)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/DilateRIF.java0000644000175000017500000000437310203035544025645 0ustar mathieumathieu/* * $RCSfile: DilateRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:23 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; import java.util.Map; /** * @see DilateOpImage */ public class DilateRIF implements RenderedImageFactory { /** Constructor. */ public DilateRIF() {} /** * Create a new instance of DilateOpImage in the rendered layer. * This method satisfies the implementation of RIF. * * @param paramBlock The source image and the dilation kernel. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); // Get BorderExtender from renderHints if any. BorderExtender extender = RIFUtil.getBorderExtenderHint(renderHints); KernelJAI unRotatedKernel = (KernelJAI)paramBlock.getObjectParameter(0); KernelJAI kJAI = unRotatedKernel.getRotatedKernel(); RenderedImage source = paramBlock.getRenderedSource(0); SampleModel sm = source.getSampleModel(); // check dataType and binary int dataType = sm.getDataType(); boolean isBinary = (sm instanceof MultiPixelPackedSampleModel) && (sm.getSampleSize(0) == 1) && (dataType == DataBuffer.TYPE_BYTE || dataType == DataBuffer.TYPE_USHORT || dataType == DataBuffer.TYPE_INT); // possible speed up later: 3x3 with table lookup if (isBinary){ return new DilateBinaryOpImage(source, extender, renderHints, layout, kJAI); }else{ return new DilateOpImage(source, extender, renderHints, layout, kJAI); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/PNGRIF.java0000644000175000017500000000274610203035544025071 0ustar mathieumathieu/* * $RCSfile: PNGRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:39 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import java.io.InputStream; import javax.media.jai.Interpolation; import javax.media.jai.JAI; import javax.media.jai.NullOpImage; import javax.media.jai.OpImage; import javax.media.jai.OperationRegistry; import javax.media.jai.RenderedOp; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.ImageDecoder; import com.sun.media.jai.codec.PNGDecodeParam; import com.sun.media.jai.codec.SeekableStream; /** * @since EA2 */ public class PNGRIF implements RenderedImageFactory { /** Constructor. */ public PNGRIF() {} /** * Creates a RenderedImage representing the contents * of a PNM-encoded image. * * @param paramBlock A ParameterBlock containing the PNM * SeekableStream to read. * @param renderHints An instance of RenderingHints, * or null. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { return CodecRIFUtil.create("png", paramBlock, renderHints); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ScaleGeneralOpImage.java0000644000175000017500000006704310203035544027674 0ustar mathieumathieu/* * $RCSfile: ScaleGeneralOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:42 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.OpImage; import javax.media.jai.PlanarImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.ScaleOpImage; import java.util.Map; import com.sun.media.jai.util.Rational; /** * An OpImage that performs scaling using a general interpolation. * */ final class ScaleGeneralOpImage extends ScaleOpImage { /* The number of subsampleBits */ private int subsampleBits; /* 2 ^ subsampleBits */ private int one; Rational half = new Rational(1, 2); // Interpolation kernel related information. private int interp_width, interp_height, interp_left, interp_top; long invScaleYInt, invScaleYFrac; long invScaleXInt, invScaleXFrac; /** * Constructs a ScaleGeneralOpImage from a RenderedImage source, * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param xScale scale factor along x axis. * @param yScale scale factor along y axis. * @param xTrans translation factor along x axis. * @param yTrans translation factor along y axis. * @param interp a Interpolation object to use for resampling. */ public ScaleGeneralOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, float xScale, float yScale, float xTrans, float yTrans, Interpolation interp) { super(source, layout, config, true, extender, interp, xScale, yScale, xTrans, yTrans); subsampleBits = interp.getSubsampleBitsH(); // Internal precision required for position calculations one = 1 << subsampleBits; // Get the width and height and padding of the Interpolation kernel. interp_width = interp.getWidth(); interp_height = interp.getHeight(); interp_left = interp.getLeftPadding(); interp_top = interp.getTopPadding(); if (invScaleYRational.num > invScaleYRational.denom) { invScaleYInt = invScaleYRational.num / invScaleYRational.denom; invScaleYFrac = invScaleYRational.num % invScaleYRational.denom; } else { invScaleYInt = 0; invScaleYFrac = invScaleYRational.num; } if (invScaleXRational.num > invScaleXRational.denom) { invScaleXInt = invScaleXRational.num / invScaleXRational.denom; invScaleXFrac = invScaleXRational.num % invScaleXRational.denom; } else { invScaleXInt = 0; invScaleXFrac = invScaleXRational.num; } } /** * Performs a scale operation on a specified rectangle. The sources are * cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster [] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); Raster source = sources[0]; // Get the source rectangle Rectangle srcRect = source.getBounds(); RasterAccessor srcAccessor = new RasterAccessor(source, srcRect, formatTags[0], getSource(0).getColorModel()); RasterAccessor dstAccessor = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); int dwidth = destRect.width; int dheight = destRect.height; int srcPixelStride = srcAccessor.getPixelStride(); int srcScanlineStride = srcAccessor.getScanlineStride(); int[] ypos = new int[dheight]; int[] xpos = new int[dwidth]; int xfracvalues[] = null, yfracvalues[] = null; float xfracvaluesFloat[] = null, yfracvaluesFloat[] = null; switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_INT: yfracvalues = new int[dheight]; xfracvalues = new int[dwidth]; preComputePositionsInt(destRect, srcRect.x, srcRect.y, srcPixelStride, srcScanlineStride, xpos, ypos, xfracvalues, yfracvalues); break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: yfracvaluesFloat = new float[dheight]; xfracvaluesFloat = new float[dwidth]; preComputePositionsFloat(destRect, srcRect.x, srcRect.y, srcPixelStride, srcScanlineStride, xpos, ypos, xfracvaluesFloat, yfracvaluesFloat); break; default: throw new RuntimeException(JaiI18N.getString("OrderedDitherOpImage0")); } switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(srcAccessor, destRect, dstAccessor, xpos, ypos, xfracvalues, yfracvalues); break; case DataBuffer.TYPE_SHORT: shortLoop(srcAccessor, destRect, dstAccessor, xpos, ypos, xfracvalues, yfracvalues); break; case DataBuffer.TYPE_USHORT: ushortLoop(srcAccessor, destRect, dstAccessor, xpos, ypos, xfracvalues, yfracvalues); break; case DataBuffer.TYPE_INT: intLoop(srcAccessor, destRect, dstAccessor, xpos, ypos, xfracvalues, yfracvalues); break; case DataBuffer.TYPE_FLOAT: floatLoop(srcAccessor, destRect, dstAccessor, xpos, ypos, xfracvaluesFloat, yfracvaluesFloat); break; case DataBuffer.TYPE_DOUBLE: doubleLoop(srcAccessor, destRect, dstAccessor, xpos, ypos, xfracvaluesFloat, yfracvaluesFloat); break; default: throw new RuntimeException(JaiI18N.getString("OrderedDitherOpImage0")); } // If the RasterAccessor object set up a temporary buffer for the // op to write to, tell the RasterAccessor to write that data // to the raster now that we're done with it. if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } private void preComputePositionsInt(Rectangle destRect, int srcRectX, int srcRectY, int srcPixelStride, int srcScanlineStride, int xpos[], int ypos[], int xfracvalues[], int yfracvalues[]) { int dwidth = destRect.width; int dheight = destRect.height; // Loop variables based on the destination rectangle to be calculated. int dx = destRect.x; int dy = destRect.y; long syNum = dy, syDenom = 1; // Subtract the X translation factor sy -= transY syNum = syNum * transYRationalDenom - transYRationalNum * syDenom; syDenom *= transYRationalDenom; // Add 0.5 syNum = 2 * syNum + syDenom; syDenom *= 2; // Multply by invScaleX syNum *= invScaleYRationalNum; syDenom *= invScaleYRationalDenom; // Subtract 0.5 syNum = 2 * syNum - syDenom; syDenom *= 2; // Separate the x source coordinate into integer and fractional part int srcYInt = Rational.floor(syNum , syDenom); long srcYFrac = syNum % syDenom; if (srcYInt < 0) { srcYFrac = syDenom + srcYFrac; } // Normalize - Get a common denominator for the fracs of // src and invScaleY long commonYDenom = syDenom * invScaleYRationalDenom; srcYFrac *= invScaleYRationalDenom; long newInvScaleYFrac = invScaleYFrac * syDenom; // Precalculate the x positions and store them in an array. long sxNum = dx, sxDenom = 1; // Subtract the X translation factor sx -= transX sxNum = sxNum * transXRationalDenom - transXRationalNum * sxDenom; sxDenom *= transXRationalDenom; // Add 0.5 sxNum = 2 * sxNum + sxDenom; sxDenom *= 2; // Multply by invScaleX sxNum *= invScaleXRationalNum; sxDenom *= invScaleXRationalDenom; // Subtract 0.5 sxNum = 2 * sxNum - sxDenom; sxDenom *= 2; // Separate the x source coordinate into integer and fractional part int srcXInt = Rational.floor(sxNum , sxDenom); long srcXFrac = sxNum % sxDenom; if (srcXInt < 0) { srcXFrac = sxDenom + srcXFrac; } // Normalize - Get a common denominator for the fracs of // src and invScaleX long commonXDenom = sxDenom * invScaleXRationalDenom; srcXFrac *= invScaleXRationalDenom; long newInvScaleXFrac = invScaleXFrac * sxDenom; for (int i=0; i= commonXDenom) { srcXInt += 1; srcXFrac -= commonXDenom; } } for (int i = 0; i < dheight; i++) { // Calculate the source position in the source data array. ypos[i] = (srcYInt - srcRectY) * srcScanlineStride; // Calculate the yfrac value yfracvalues[i] = (int)(((float)srcYFrac/(float)commonYDenom) * one); // Move onto the next source pixel. // Add the integral part of invScaleY to the integral part // of srcY srcYInt += invScaleYInt; // Add the fractional part of invScaleY to the fractional part // of srcY srcYFrac += newInvScaleYFrac; // If the fractional part is now greater than equal to the // denominator, divide so as to reduce the numerator to be less // than the denominator and add the overflow to the integral part. if (srcYFrac >= commonYDenom) { srcYInt += 1; srcYFrac -= commonYDenom; } } } private void preComputePositionsFloat(Rectangle destRect, int srcRectX, int srcRectY, int srcPixelStride, int srcScanlineStride, int xpos[], int ypos[], float xfracvaluesFloat[], float yfracvaluesFloat[]) { int dwidth = destRect.width; int dheight = destRect.height; // Loop variables based on the destination rectangle to be calculated. int dx = destRect.x; int dy = destRect.y; long syNum = dy, syDenom = 1; // Subtract the X translation factor sy -= transY syNum = syNum * transYRationalDenom - transYRationalNum * syDenom; syDenom *= transYRationalDenom; // Add 0.5 syNum = 2 * syNum + syDenom; syDenom *= 2; // Multply by invScaleX syNum *= invScaleYRationalNum; syDenom *= invScaleYRationalDenom; // Subtract 0.5 syNum = 2 * syNum - syDenom; syDenom *= 2; // Separate the x source coordinate into integer and fractional part int srcYInt = Rational.floor(syNum , syDenom); long srcYFrac = syNum % syDenom; if (srcYInt < 0) { srcYFrac = syDenom + srcYFrac; } // Normalize - Get a common denominator for the fracs of // src and invScaleY long commonYDenom = syDenom * invScaleYRationalDenom; srcYFrac *= invScaleYRationalDenom; long newInvScaleYFrac = invScaleYFrac * syDenom; // Precalculate the x positions and store them in an array. long sxNum = dx, sxDenom = 1; // Subtract the X translation factor sx -= transX sxNum = sxNum * transXRationalDenom - transXRationalNum * sxDenom; sxDenom *= transXRationalDenom; // Add 0.5 sxNum = 2 * sxNum + sxDenom; sxDenom *= 2; // Multply by invScaleX sxNum *= invScaleXRationalNum; sxDenom *= invScaleXRationalDenom; // Subtract 0.5 sxNum = 2 * sxNum - sxDenom; sxDenom *= 2; // Separate the x source coordinate into integer and fractional part int srcXInt = Rational.floor(sxNum , sxDenom); long srcXFrac = sxNum % sxDenom; if (srcXInt < 0) { srcXFrac = sxDenom + srcXFrac; } // Normalize - Get a common denominator for the fracs of // src and invScaleX long commonXDenom = sxDenom * invScaleXRationalDenom; srcXFrac *= invScaleXRationalDenom; long newInvScaleXFrac = invScaleXFrac * sxDenom; for (int i=0; i= commonXDenom) { srcXInt += 1; srcXFrac -= commonXDenom; } } for (int i = 0; i < dheight; i++) { // Calculate the source position in the source data array. ypos[i] = (srcYInt - srcRectY) * srcScanlineStride; // Calculate the yfrac value yfracvaluesFloat[i] = (float)srcYFrac/(float)commonYDenom; // Move onto the next source pixel. // Add the integral part of invScaleY to the integral part // of srcY srcYInt += invScaleYInt; // Add the fractional part of invScaleY to the fractional part // of srcY srcYFrac += newInvScaleYFrac; // If the fractional part is now greater than equal to the // denominator, divide so as to reduce the numerator to be less // than the denominator and add the overflow to the integral part. if (srcYFrac >= commonYDenom) { srcYInt += 1; srcYFrac -= commonYDenom; } } } private void byteLoop(RasterAccessor src, Rectangle destRect, RasterAccessor dst, int xpos[], int ypos[], int xfracvalues[], int yfracvalues[]) { int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dwidth = destRect.width; int dheight = destRect.height; int dnumBands = dst.getNumBands(); byte dstDataArrays[][] = dst.getByteDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); byte srcDataArrays[][] = src.getByteDataArrays(); int bandOffsets[] = src.getBandOffsets(); int dstOffset = 0; // Number of samples required for the interpolation int samples[][] = new int[interp_height][interp_width]; int xfrac, yfrac; int s; int posx, posy; // Putting band loop outside for (int k = 0; k < dnumBands; k++) { byte dstData[] = dstDataArrays[k]; byte srcData[] = srcDataArrays[k]; int dstScanlineOffset = dstBandOffsets[k]; int bandOffset = bandOffsets[k]; for (int j = 0; j < dheight; j++) { int dstPixelOffset = dstScanlineOffset; yfrac = yfracvalues[j]; posy = ypos[j] + bandOffset; for (int i = 0; i < dwidth; i++) { xfrac = xfracvalues[i]; posx = xpos[i]; // Get the required number of surrounding sample values // and put them in the samples array int start = interp_left * srcPixelStride + interp_top * srcScanlineStride; start = posx + posy - start; int countH = 0, countV = 0; for (int yloop = 0; yloop < interp_height; yloop++) { int startY = start; for (int xloop = 0; xloop < interp_width; xloop++) { samples[countV][countH++] = srcData[start] & 0xff; start += srcPixelStride; } countV++; countH = 0; start = startY + srcScanlineStride; } // Perform the interpolation s = interp.interpolate(samples, xfrac, yfrac); // clamp the value to byte range if (s > 255) { s = 255; } else if (s < 0) { s = 0; } dstData[dstPixelOffset] = (byte)(s&0xff); dstPixelOffset += dstPixelStride; } dstScanlineOffset += dstScanlineStride; } } } private void shortLoop(RasterAccessor src, Rectangle destRect, RasterAccessor dst, int xpos[], int ypos[], int xfracvalues[], int yfracvalues[]) { int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dwidth = destRect.width; int dheight = destRect.height; int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int bandOffsets[] = src.getBandOffsets(); int dstOffset = 0; // Number of samples required for the interpolation int samples[][] = new int[interp_height][interp_width]; int posy, posx; int xfrac, yfrac; int s; // Putting band loop outside for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int dstScanlineOffset = dstBandOffsets[k]; int bandOffset = bandOffsets[k]; for (int j = 0; j < dheight; j++) { int dstPixelOffset = dstScanlineOffset; yfrac = yfracvalues[j]; posy = ypos[j] + bandOffset; for (int i = 0; i < dwidth; i++) { xfrac = xfracvalues[i]; posx = xpos[i]; // Get the required number of surrounding sample values int start = interp_left * srcPixelStride + interp_top * srcScanlineStride; start = posx + posy - start; int countH = 0, countV = 0; for (int yloop = 0; yloop < interp_height; yloop++) { int startY = start; for (int xloop = 0; xloop < interp_width; xloop++) { samples[countV][countH++] = srcData[start]; start += srcPixelStride; } countV++; countH = 0; start = startY + srcScanlineStride; } s = interp.interpolate(samples, xfrac, yfrac); // clamp the value to short range if (s > Short.MAX_VALUE) { s = Short.MAX_VALUE; } else if (s < Short.MIN_VALUE) { s = Short.MIN_VALUE; } dstData[dstPixelOffset] = (short)s; dstPixelOffset += dstPixelStride; } dstScanlineOffset += dstScanlineStride; } } } private void ushortLoop(RasterAccessor src, Rectangle destRect, RasterAccessor dst, int xpos[], int ypos[], int xfracvalues[], int yfracvalues[]) { int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dwidth = destRect.width; int dheight = destRect.height; int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int bandOffsets[] = src.getBandOffsets(); int dstOffset = 0; // Number of samples required for the interpolation int samples[][] = new int[interp_height][interp_width]; int posy, posx; int xfrac, yfrac; int s; // Putting band loop outside for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int dstScanlineOffset = dstBandOffsets[k]; int bandOffset = bandOffsets[k]; for (int j = 0; j < dheight; j++) { int dstPixelOffset = dstScanlineOffset; yfrac = yfracvalues[j]; posy = ypos[j] + bandOffset; for (int i = 0; i < dwidth; i++) { xfrac = xfracvalues[i]; posx = xpos[i]; // Get the required number of surrounding sample values int start = interp_left * srcPixelStride + interp_top * srcScanlineStride; start = posx + posy - start; int countH = 0, countV = 0; for (int yloop = 0; yloop < interp_height; yloop++) { int startY = start; for (int xloop = 0; xloop < interp_width; xloop++) { samples[countV][countH++] = srcData[start] & 0xffff; start += srcPixelStride; } countV++; countH = 0; start = startY + srcScanlineStride; } s = interp.interpolate(samples, xfrac, yfrac); // clamp the value to ushort range if (s > 65536) { s = 65536; } else if (s < 0) { s = 0; } dstData[dstPixelOffset] = (short)(s & 0xffff); dstPixelOffset += dstPixelStride; } dstScanlineOffset += dstScanlineStride; } } } // identical to byteLoops, except datatypes have changed. clumsy, // but there's no other way in Java private void intLoop(RasterAccessor src, Rectangle destRect, RasterAccessor dst, int xpos[], int ypos[], int xfracvalues[], int yfracvalues[]) { int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dwidth = destRect.width; int dheight = destRect.height; int dnumBands = dst.getNumBands(); int dstDataArrays[][] = dst.getIntDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcDataArrays[][] = src.getIntDataArrays(); int bandOffsets[] = src.getBandOffsets(); int dstOffset = 0; // Number of samples required for the interpolation int samples[][] = new int[interp_height][interp_width]; int posy, posx; int xfrac, yfrac; int s; // Putting band loop outside for (int k = 0; k < dnumBands; k++) { int dstData[] = dstDataArrays[k]; int srcData[] = srcDataArrays[k]; int dstScanlineOffset = dstBandOffsets[k]; int bandOffset = bandOffsets[k]; for (int j = 0; j < dheight; j++) { int dstPixelOffset = dstScanlineOffset; yfrac = yfracvalues[j]; posy = ypos[j] + bandOffset; for (int i = 0; i < dwidth; i++) { xfrac = xfracvalues[i]; posx = xpos[i]; // Get the required number of surrounding sample values int start = interp_left * srcPixelStride + interp_top * srcScanlineStride; start = posx + posy - start; int countH = 0, countV = 0; for (int yloop = 0; yloop < interp_height; yloop++) { int startY = start; for (int xloop = 0; xloop < interp_width; xloop++) { samples[countV][countH++] = srcData[start]; start += srcPixelStride; } countV++; countH = 0; start = startY + srcScanlineStride; } s = interp.interpolate(samples, xfrac, yfrac); dstData[dstPixelOffset] = s; dstPixelOffset += dstPixelStride; } dstScanlineOffset += dstScanlineStride; } } } private void floatLoop(RasterAccessor src, Rectangle destRect, RasterAccessor dst, int xpos[], int ypos[], float xfracvaluesFloat[], float yfracvaluesFloat[]) { int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dwidth = destRect.width; int dheight = destRect.height; int dnumBands = dst.getNumBands(); float dstDataArrays[][] = dst.getFloatDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); float srcDataArrays[][] = src.getFloatDataArrays(); int bandOffsets[] = src.getBandOffsets(); int dstOffset = 0; // Number of samples required for the interpolation float samples[][] = new float[interp_height][interp_width]; int posy, posx; float xfrac, yfrac; float s; // Putting band loop outside for (int k = 0; k < dnumBands; k++) { float dstData[] = dstDataArrays[k]; float srcData[] = srcDataArrays[k]; int dstScanlineOffset = dstBandOffsets[k]; int bandOffset = bandOffsets[k]; for (int j = 0; j < dheight; j++) { int dstPixelOffset = dstScanlineOffset; yfrac = yfracvaluesFloat[j]; posy = ypos[j] + bandOffset; for (int i = 0; i < dwidth; i++) { xfrac = xfracvaluesFloat[i]; posx = xpos[i]; // Get the required number of surrounding sample values int start = interp_left * srcPixelStride + interp_top * srcScanlineStride; start = posx + posy - start; int countH = 0, countV = 0; for (int yloop = 0; yloop < interp_height; yloop++) { int startY = start; for (int xloop = 0; xloop < interp_width; xloop++) { samples[countV][countH++] = srcData[start]; start += srcPixelStride; } countV++; countH = 0; start = startY + srcScanlineStride; } s = interp.interpolate(samples, xfrac, yfrac); if (s > Float.MAX_VALUE) { s = Float.MAX_VALUE; } else if (s < -Float.MAX_VALUE) { s = -Float.MAX_VALUE; } dstData[dstPixelOffset] = (float)s; dstPixelOffset += dstPixelStride; } dstScanlineOffset += dstScanlineStride; } } } private void doubleLoop(RasterAccessor src, Rectangle destRect, RasterAccessor dst, int xpos[], int ypos[], float xfracvaluesFloat[], float yfracvaluesFloat[]) { int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dwidth = destRect.width; int dheight = destRect.height; int dnumBands = dst.getNumBands(); double dstDataArrays[][] = dst.getDoubleDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); double srcDataArrays[][] = src.getDoubleDataArrays(); int bandOffsets[] = src.getBandOffsets(); int dstOffset = 0; // Number of samples required for the interpolation double samples[][] = new double[interp_height][interp_width]; int posy, posx; double s; float xfrac, yfrac; // Putting band loop outside for (int k = 0; k < dnumBands; k++) { double dstData[] = dstDataArrays[k]; double srcData[] = srcDataArrays[k]; int dstScanlineOffset = dstBandOffsets[k]; int bandOffset = bandOffsets[k]; for (int j = 0; j < dheight; j++) { int dstPixelOffset = dstScanlineOffset; yfrac = yfracvaluesFloat[j]; posy = ypos[j] + bandOffset; for (int i = 0; i < dwidth; i++) { xfrac = xfracvaluesFloat[i]; posx = xpos[i]; // Get the required number of surrounding sample values int start = interp_left * srcPixelStride + interp_top * srcScanlineStride; start = posx + posy - start; int countH = 0, countV = 0; for (int yloop = 0; yloop < interp_height; yloop++) { int startY = start; for (int xloop = 0; xloop < interp_width; xloop++) { samples[countV][countH++] = srcData[start]; start += srcPixelStride; } countV++; countH = 0; start = startY + srcScanlineStride; } s = interp.interpolate(samples, xfrac, yfrac); dstData[dstPixelOffset] = s; dstPixelOffset += dstPixelStride; } dstScanlineOffset += dstScanlineStride; } } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/XorCRIF.java0000644000175000017500000000315610203035544025314 0ustar mathieumathieu/* * $RCSfile: XorCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:47 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.geom.Rectangle2D; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderContext; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "Xor" operation in the * rendered and renderable image layers. * * @since EA2 * @see javax.media.jai.operator.XorDescriptor * @see XorOpImage * */ public class XorCRIF extends CRIFImpl { /** Constructor. */ public XorCRIF() { super("xor"); } /** * Creates a new instance of XorOpImage in the * rendered layer. This method satisifies the implementation of RIF. * * @param paramBlock The two source images to be "xored" together. * @param renderHints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new XorOpImage(paramBlock.getRenderedSource(0), paramBlock.getRenderedSource(1), renderHints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/SubtractOpImage.java0000644000175000017500000006420610203035544027134 0ustar mathieumathieu/* * $RCSfile: SubtractOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:45 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.util.Map; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PointOpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.RasterFactory; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.util.JDKWorkarounds; /// import com.sun.media.jai.test.OpImageTester; /** * An OpImage implementing the "Subtract" operation as * described in javax.media.jai.operator.SubtractDescriptor. * *

    This OpImage subtracts the pixel values of the second * source image from the first source images on a per-band basis. In case * the two source images have different number of bands, the number of * bands for the destination image is the smaller band number of the two * source images. That is * dstNumBands = Math.min(src1NumBands, src2NumBands). * In case the two source images have different data types, the data type * for the destination image is the higher data type of the two source * images. * *

    The value of the pixel (x, y) in the destination image is defined as: *

     * for (b = 0; b < numBands; b++) {
     *     dst[y][x][b] = src1[y][x][b] - src2[y][x][b];
     * }
     * 
    * *

    If the result of the subtraction overflows/underflows the * maximum/minimum value supported by the destination image, then it * will be clamped to the maximum/minimum value respectively. The * data type byte is treated as unsigned, with maximum * value as 255 and minimum value as 0. * * @see javax.media.jai.operator.SubtractDescriptor * @see SubtractCRIF * */ final class SubtractOpImage extends PointOpImage { /* Source 1 band increment */ private int s1bd = 1; /* Source 2 band increment */ private int s2bd = 1; /** * Constructs an SubtractOpImage. * *

    The layout parameter may optionally contains the * tile grid layout, sample model, and/or color model. The image * dimension is determined by the intersection of the bounding boxes * of the two source images. * *

    The image layout of the first source image, source1, * is used as the fall-back for the image layout of the destination * image. Any layout parameters not specified in the layout * argument are set to the same value as that of source1. * * @param source1 The first source image. * @param source2 The second source image. * @param layout The destination image layout. */ public SubtractOpImage(RenderedImage source1, RenderedImage source2, Map config, ImageLayout layout) { super(source1, source2, layout, config, true); // Get the source band counts. int numBands1 = source1.getSampleModel().getNumBands(); int numBands2 = source2.getSampleModel().getNumBands(); // Handle the special case of subtracting from each band of an N-band // image a single-band image. int numBandsDst; if(layout != null && layout.isValid(ImageLayout.SAMPLE_MODEL_MASK)) { SampleModel sm = layout.getSampleModel(null); numBandsDst = sm.getNumBands(); // The second source must be single-banded and the first must // be multi-banded. if(numBandsDst > 1 && ((numBands1 > 1 && numBands2 == 1) || (numBands1 == 1 && numBands2 > 1))) { // Clamp the destination band count to the number of // bands in the multi-band source. numBandsDst = Math.min(Math.max(numBands1, numBands2), numBandsDst); // Create a new SampleModel if necessary. if(numBandsDst != sampleModel.getNumBands()) { sampleModel = RasterFactory.createComponentSampleModel( sm, sampleModel.getTransferType(), sampleModel.getWidth(), sampleModel.getHeight(), numBandsDst); if(colorModel != null && !JDKWorkarounds.areCompatibleDataModels(sampleModel, colorModel)) { colorModel = ImageUtil.getCompatibleColorModel(sampleModel, config); } } // Set the source band increments. s1bd = numBands1 == 1 ? 0 : 1; s2bd = numBands2 == 1 ? 0 : 1; } } // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * Subtracts the pixel values of two source images within a specified * rectangle. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); RasterAccessor s1 = new RasterAccessor(sources[0], destRect, formatTags[0], getSource(0).getColorModel()); RasterAccessor s2 = new RasterAccessor(sources[1], destRect, formatTags[1], getSource(1).getColorModel()); RasterAccessor d = new RasterAccessor(dest, destRect, formatTags[2], getColorModel()); if(d.isBinary()) { byte[] src1Bits = s1.getBinaryDataArray(); byte[] src2Bits = s2.getBinaryDataArray(); byte[] dstBits = d.getBinaryDataArray(); int length = dstBits.length; for(int i = 0; i < length; i++) { // "Subtract" is equivalent to the following // when -1 is clamped to 0. dstBits[i] = (byte)(src1Bits[i] & (byte)(~(src2Bits[i]))); } d.copyBinaryDataToRaster(); return; } switch (d.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(s1, s2, d); break; case DataBuffer.TYPE_USHORT: computeRectUShort(s1, s2, d); break; case DataBuffer.TYPE_SHORT: computeRectShort(s1, s2, d); break; case DataBuffer.TYPE_INT: computeRectInt(s1, s2, d); break; case DataBuffer.TYPE_FLOAT: computeRectFloat(s1, s2, d); break; case DataBuffer.TYPE_DOUBLE: computeRectDouble(s1, s2, d); break; } if (d.needsClamping()) { d.clampDataArrays(); } d.copyDataToRaster(); } private void computeRectByte(RasterAccessor src1, RasterAccessor src2, RasterAccessor dst) { int s1LineStride = src1.getScanlineStride(); int s1PixelStride = src1.getPixelStride(); int[] s1BandOffsets = src1.getBandOffsets(); byte[][] s1Data = src1.getByteDataArrays(); int s2LineStride = src2.getScanlineStride(); int s2PixelStride = src2.getPixelStride(); int[] s2BandOffsets = src2.getBandOffsets(); byte[][] s2Data = src2.getByteDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int bands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); byte[][] dData = dst.getByteDataArrays(); for (int b = 0, s1b = 0, s2b = 0; b < bands; b++, s1b += s1bd, s2b += s2bd) { byte[] s1 = s1Data[s1b]; byte[] s2 = s2Data[s2b]; byte[] d = dData[b]; int s1LineOffset = s1BandOffsets[s1b]; int s2LineOffset = s2BandOffsets[s2b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int s1PixelOffset = s1LineOffset; int s2PixelOffset = s2LineOffset; int dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; dLineOffset += dLineStride; int diff = 0; for (int w = 0; w < dwidth; w++) { // // The next two lines are a fast way to do // a subtract with saturation on U8 elements. // It eliminates the need to do clamping. // diff = (s1[s1PixelOffset]&0xFF) - (s2[s2PixelOffset]&0xFF); d[dPixelOffset] = (byte)((diff & ~(diff>>8)) & 0xFF); s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; dPixelOffset += dPixelStride; } } } } private void computeRectUShort(RasterAccessor src1, RasterAccessor src2, RasterAccessor dst) { int s1LineStride = src1.getScanlineStride(); int s1PixelStride = src1.getPixelStride(); int[] s1BandOffsets = src1.getBandOffsets(); short[][] s1Data = src1.getShortDataArrays(); int s2LineStride = src2.getScanlineStride(); int s2PixelStride = src2.getPixelStride(); int[] s2BandOffsets = src2.getBandOffsets(); short[][] s2Data = src2.getShortDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int bands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); short[][] dData = dst.getShortDataArrays(); for (int b = 0, s1b = 0, s2b = 0; b < bands; b++, s1b += s1bd, s2b += s2bd) { short[] s1 = s1Data[s1b]; short[] s2 = s2Data[s2b]; short[] d = dData[b]; int s1LineOffset = s1BandOffsets[s1b]; int s2LineOffset = s2BandOffsets[s2b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int s1PixelOffset = s1LineOffset; int s2PixelOffset = s2LineOffset; int dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { d[dPixelOffset] = ImageUtil.clampUShortNegative( (int)(s1[s1PixelOffset]&0xFFFF) - (int)(s2[s2PixelOffset]&0xFFFF)); s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; dPixelOffset += dPixelStride; } } } } private void computeRectShort(RasterAccessor src1, RasterAccessor src2, RasterAccessor dst) { int s1LineStride = src1.getScanlineStride(); int s1PixelStride = src1.getPixelStride(); int[] s1BandOffsets = src1.getBandOffsets(); short[][] s1Data = src1.getShortDataArrays(); int s2LineStride = src2.getScanlineStride(); int s2PixelStride = src2.getPixelStride(); int[] s2BandOffsets = src2.getBandOffsets(); short[][] s2Data = src2.getShortDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int bands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); short[][] dData = dst.getShortDataArrays(); for (int b = 0, s1b = 0, s2b = 0; b < bands; b++, s1b += s1bd, s2b += s2bd) { short[] s1 = s1Data[s1b]; short[] s2 = s2Data[s2b]; short[] d = dData[b]; int s1LineOffset = s1BandOffsets[s1b]; int s2LineOffset = s2BandOffsets[s2b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int s1PixelOffset = s1LineOffset; int s2PixelOffset = s2LineOffset; int dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { d[dPixelOffset] = ImageUtil.clampShort((int)s1[s1PixelOffset] - (int)s2[s2PixelOffset]); s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; dPixelOffset += dPixelStride; } } } } private void computeRectInt(RasterAccessor src1, RasterAccessor src2, RasterAccessor dst) { int s1LineStride = src1.getScanlineStride(); int s1PixelStride = src1.getPixelStride(); int[] s1BandOffsets = src1.getBandOffsets(); int[][] s1Data = src1.getIntDataArrays(); int s2LineStride = src2.getScanlineStride(); int s2PixelStride = src2.getPixelStride(); int[] s2BandOffsets = src2.getBandOffsets(); int[][] s2Data = src2.getIntDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int bands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); int[][] dData = dst.getIntDataArrays(); /* * The destination data type may be any of the integral data types. * The "clamp" function must clamp to the appropriate range for * that data type. */ switch (sampleModel.getTransferType()) { case DataBuffer.TYPE_BYTE: for (int b = 0, s1b = 0, s2b = 0; b < bands; b++, s1b += s1bd, s2b += s2bd) { int[] s1 = s1Data[s1b]; int[] s2 = s2Data[s2b]; int[] d = dData[b]; int s1LineOffset = s1BandOffsets[s1b]; int s2LineOffset = s2BandOffsets[s2b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int s1PixelOffset = s1LineOffset; int s2PixelOffset = s2LineOffset; int dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; dLineOffset += dLineStride; int diff = 0; for (int w = 0; w < dwidth; w++) { // // The next two lines are a fast way to do // a subtract with saturation on U8 elements. // It eliminates the need to do clamping. // diff = (s1[s1PixelOffset]&0xFF) - (s2[s2PixelOffset]&0xFF); d[dPixelOffset] = ((diff & ~(diff>>8)) & 0xFF); s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; dPixelOffset += dPixelStride; } } } break; case DataBuffer.TYPE_USHORT: for (int b = 0, s1b = 0, s2b = 0; b < bands; b++, s1b += s1bd, s2b += s2bd) { int[] s1 = s1Data[s1b]; int[] s2 = s2Data[s2b]; int[] d = dData[b]; int s1LineOffset = s1BandOffsets[s1b]; int s2LineOffset = s2BandOffsets[s2b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int s1PixelOffset = s1LineOffset; int s2PixelOffset = s2LineOffset; int dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { d[dPixelOffset] = ImageUtil.clampUShortNegative( (s1[s1PixelOffset]&0xFFFF) - (s2[s2PixelOffset]&0xFFFF)); s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; dPixelOffset += dPixelStride; } } } break; case DataBuffer.TYPE_SHORT: for (int b = 0, s1b = 0, s2b = 0; b < bands; b++, s1b += s1bd, s2b += s2bd) { int[] s1 = s1Data[s1b]; int[] s2 = s2Data[s2b]; int[] d = dData[b]; int s1LineOffset = s1BandOffsets[s1b]; int s2LineOffset = s2BandOffsets[s2b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int s1PixelOffset = s1LineOffset; int s2PixelOffset = s2LineOffset; int dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { d[dPixelOffset] = ImageUtil.clampShort(s1[s1PixelOffset] - s2[s2PixelOffset]); s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; dPixelOffset += dPixelStride; } } } break; case DataBuffer.TYPE_INT: for (int b = 0, s1b = 0, s2b = 0; b < bands; b++, s1b += s1bd, s2b += s2bd) { int[] s1 = s1Data[s1b]; int[] s2 = s2Data[s2b]; int[] d = dData[b]; int s1LineOffset = s1BandOffsets[s1b]; int s2LineOffset = s2BandOffsets[s2b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int s1PixelOffset = s1LineOffset; int s2PixelOffset = s2LineOffset; int dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { d[dPixelOffset] = ImageUtil.clampInt((long)s1[s1PixelOffset] - (long)s2[s2PixelOffset]); s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; dPixelOffset += dPixelStride; } } } break; } } private void computeRectFloat(RasterAccessor src1, RasterAccessor src2, RasterAccessor dst) { int s1LineStride = src1.getScanlineStride(); int s1PixelStride = src1.getPixelStride(); int[] s1BandOffsets = src1.getBandOffsets(); float[][] s1Data = src1.getFloatDataArrays(); int s2LineStride = src2.getScanlineStride(); int s2PixelStride = src2.getPixelStride(); int[] s2BandOffsets = src2.getBandOffsets(); float[][] s2Data = src2.getFloatDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int bands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); float[][] dData = dst.getFloatDataArrays(); for (int b = 0, s1b = 0, s2b = 0; b < bands; b++, s1b += s1bd, s2b += s2bd) { float[] s1 = s1Data[s1b]; float[] s2 = s2Data[s2b]; float[] d = dData[b]; int s1LineOffset = s1BandOffsets[s1b]; int s2LineOffset = s2BandOffsets[s2b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int s1PixelOffset = s1LineOffset; int s2PixelOffset = s2LineOffset; int dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { d[dPixelOffset] = s1[s1PixelOffset] - s2[s2PixelOffset]; s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; dPixelOffset += dPixelStride; } } } } private void computeRectDouble(RasterAccessor src1, RasterAccessor src2, RasterAccessor dst) { int s1LineStride = src1.getScanlineStride(); int s1PixelStride = src1.getPixelStride(); int[] s1BandOffsets = src1.getBandOffsets(); double[][] s1Data = src1.getDoubleDataArrays(); int s2LineStride = src2.getScanlineStride(); int s2PixelStride = src2.getPixelStride(); int[] s2BandOffsets = src2.getBandOffsets(); double[][] s2Data = src2.getDoubleDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int bands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); double[][] dData = dst.getDoubleDataArrays(); for (int b = 0, s1b = 0, s2b = 0; b < bands; b++, s1b += s1bd, s2b += s2bd) { double[] s1 = s1Data[s1b]; double[] s2 = s2Data[s2b]; double[] d = dData[b]; int s1LineOffset = s1BandOffsets[s1b]; int s2LineOffset = s2BandOffsets[s2b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int s1PixelOffset = s1LineOffset; int s2PixelOffset = s2LineOffset; int dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { d[dPixelOffset] = s1[s1PixelOffset] - s2[s2PixelOffset]; s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; dPixelOffset += dPixelStride; } } } } // public static void main(String args[]) { // System.out.println("SubtractOpImage Test"); // ImageLayout layout; // OpImage src1, src2, dst; // Rectangle rect = new Rectangle(0, 0, 5, 5); // System.out.println("1. PixelInterleaved byte 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 800, 800, 0, 0, 200, 200, DataBuffer.TYPE_BYTE, 3, false); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new SubtractOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("2. Banded byte 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 800, 800, 0, 0, 200, 200, DataBuffer.TYPE_BYTE, 3, true); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new SubtractOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("3. PixelInterleaved int 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_INT, 3, false); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new SubtractOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("4. Banded int 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_INT, 3, true); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new SubtractOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/LookupOpImage.java0000644000175000017500000001501110203035544026604 0ustar mathieumathieu/* * $RCSfile: LookupOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:31 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.util.Map; import javax.media.jai.ColormapOpImage; import javax.media.jai.ImageLayout; import javax.media.jai.LookupTableJAI; import javax.media.jai.PlanarImage; import javax.media.jai.PointOpImage; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.util.JDKWorkarounds; /** * An OpImage implementing the "Lookup" operation. * *

    This OpImage performs the general table lookup on * a source image by passing it through a lookup table. The source * image may be single- or multi-banded and of any integral data types. * The lookup table may be single- or multi-banded of any JAI supported * data types. The destination image must have the same data type as * the lookup table, and its number of bands is determined based on * the number of bands of the source and the table. * *

    If both the source and the lookup table are multi-banded, they * should have the same number of bands. In case their band numbers * are different, if the source's number of bands is less than the * table's number of bands, the first n bands of the table * data is used, where n is the source's number of bands; * otherwise, the first band (band 0) of the table data is applied to * all the bands of the source. * *

    The application programs may specify the layout of the * destination image via the ImageLayout parameter. If * a SampleModel is supplied, it should be created in * accordance with the source image and the actual lookup table * used in a specific case. It is strongly recommended that a * ComponentSampleModel is be used whenever possible * for better performance. * *

    If the supplied SampleModel is unsuitable for * the source image and the lookup table type, or if no * SampleModel is specified, a default suitable * SampleModel is chosen for this operation based on * the type of source image and lookup table. In this case, a new * ColorModel is chosen based on the SampleModel. * *

    Special case lookup operators should extend this class. * *

    The destination pixel values are determined as: *

     *     if (srcNumBands == 1) {
     *         // dst[y][x] has the same number of bands as the lookup table.
     *         for (b = 0; b < dstNumBands; b++) {
     *             dst[y][x][b] = tableData[b][src[y][x][0] + offsets[b]];
     *         }
     *     } else {
     *         // src[y][x] is multi-banded, dst[y][x] has the same
     *         // number of bands as src[y][x].
     *         if (tableNumBands == 1) {
     *             for (b = 0; b < dstNumBands; b++) {
     *                 dst[y][x][b] = tableData[0][src[y][x][b] + offsets[0]];
     *             }
     *         } else {
     *             for (b = 0; b < dstNumBands; b++) {
     *                 dst[y][x][b] = tableData[b][src[y][x][b] + offsets[b]];
     *             }
     *         }
     *     }
     * 
    * * @see javax.media.jai.operator.LookupDescriptor * @see javax.media.jai.LookupTableJAI * @see LookupCRIF * */ final class LookupOpImage extends ColormapOpImage { /** * The lookup table associated with this operation. * The source image is passed through this table. */ protected LookupTableJAI table; /** * Constructor. * *

    If a SampleModel is supplied in the layout * parameter, it may be ignored in case it's unsuitable for this * operation. In this case the default SampleModel * and ColorModel are used. * * @param source The source image. * @param layout The destination image layout. * @param table The table used to perform the lookup operation, * stored by reference. */ public LookupOpImage(RenderedImage source, Map config, ImageLayout layout, LookupTableJAI table) { super(source, layout, config, true); this.table = table; SampleModel sm = source.getSampleModel(); // source sample model if (sampleModel.getTransferType() != table.getDataType() || sampleModel.getNumBands() != table.getDestNumBands(sm.getNumBands())) { /* * The current SampleModel is not suitable for the supplied * source and lookup table. Create a suitable SampleModel * and ColorModel for the destination image. */ sampleModel = table.getDestSampleModel(sm, tileWidth, tileHeight); if(colorModel != null && !JDKWorkarounds.areCompatibleDataModels(sampleModel, colorModel)) { colorModel = ImageUtil.getCompatibleColorModel(sampleModel, config); } } // Set flag to permit in-place operation. permitInPlaceOperation(); // Initialize the colormap if necessary. initializeColormapOperation(); } /** * Transform the colormap via the lookup table. */ protected void transformColormap(byte[][] colormap) { for(int b = 0; b < 3; b++) { byte[] map = colormap[b]; int mapSize = map.length; int band = table.getNumBands() < 3 ? 0 : b; for(int i = 0; i < mapSize; i++) { int result = table.lookup(band, map[i] & 0xFF); map[i] = ImageUtil.clampByte(result); } } } /** * Performs the table lookup operation within a specified rectangle. * * @param sources Cobbled source, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { table.lookup(sources[0], dest, destRect); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MaxOpImage.java0000644000175000017500000004265510203035544026076 0ustar mathieumathieu/* * $RCSfile: MaxOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:33 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PointOpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; import java.lang.ref.SoftReference; /** * An OpImage implementing the "Max" operation as * described in javax.media.jai.operator.MaxDescriptor. * *

    This OpImage chooses the maximum pixel values of the * two source images on a per-band basis. In case the two source images * have different number of bands, the number of bands for the destination * image is the smaller band number of the two source images. That is * dstNumBands = Math.min(src1NumBands, src2NumBands). * In case the two source images have different data types, the data type * for the destination image is the higher data type of the two source * images. * *

    The value of the pixel (x, y) in the destination image is defined as: *

     * for (b = 0; b < numBands; b++) {
     *     dst[y][x][b] = Math.max(src1[y][x][b], src2[y][x][b]);
     * }
     * 
    * * @see javax.media.jai.operator.MaxDescriptor * @see MaxRIF * */ final class MaxOpImage extends PointOpImage { private static long negativeZeroFloatBits = Float.floatToIntBits(-0.0f); private static long negativeZeroDoubleBits = Double.doubleToLongBits(-0.0d); private static byte[] byteTable = null; private static SoftReference softRef = null; private synchronized void allocByteTable() { if ((softRef == null) || (softRef.get() == null)) { // // First time or reference has been cleared. // Create the table and make a soft reference to it. // byteTable = new byte[256*256]; softRef = new SoftReference(byteTable); // Initialize table which implements Min int idx = 0; for (int i1 = 0; i1 < 256; i1++) { int base = i1 << 8; for(int i2=0; i2MaxOpImage. * * @param source1 The first source image. * @param source2 The second source image. * @param layout The destination image layout. */ public MaxOpImage(RenderedImage source1, RenderedImage source2, Map config, ImageLayout layout) { super(source1, source2, layout, config, true); if (sampleModel.getTransferType() == DataBuffer.TYPE_BYTE) { allocByteTable(); } // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * Return the maximum pixel value of the source images for a * specified rectangle. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); /* For PointOpImage, srcRect = destRect. */ RasterAccessor s1 = new RasterAccessor(sources[0], destRect, formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor s2 = new RasterAccessor(sources[1], destRect, formatTags[1], getSourceImage(1).getColorModel()); RasterAccessor d = new RasterAccessor(dest, destRect, formatTags[2], getColorModel()); switch (d.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(s1, s2, d); break; case DataBuffer.TYPE_USHORT: computeRectUShort(s1, s2, d); break; case DataBuffer.TYPE_SHORT: computeRectShort(s1, s2, d); break; case DataBuffer.TYPE_INT: computeRectInt(s1, s2, d); break; case DataBuffer.TYPE_FLOAT: computeRectFloat(s1, s2, d); break; case DataBuffer.TYPE_DOUBLE: computeRectDouble(s1, s2, d); break; } if (d.isDataCopy()) { d.clampDataArrays(); d.copyDataToRaster(); } } private void computeRectByte(RasterAccessor src1, RasterAccessor src2, RasterAccessor dst) { int s1LineStride = src1.getScanlineStride(); int s1PixelStride = src1.getPixelStride(); int[] s1BandOffsets = src1.getBandOffsets(); byte[][] s1Data = src1.getByteDataArrays(); int s2LineStride = src2.getScanlineStride(); int s2PixelStride = src2.getPixelStride(); int[] s2BandOffsets = src2.getBandOffsets(); byte[][] s2Data = src2.getByteDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int bands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); byte[][] dData = dst.getByteDataArrays(); for (int b = 0; b < bands; b++) { byte[] s1 = s1Data[b]; byte[] s2 = s2Data[b]; byte[] d = dData[b]; int s1LineOffset = s1BandOffsets[b]; int s2LineOffset = s2BandOffsets[b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int s1PixelOffset = s1LineOffset; int s2PixelOffset = s2LineOffset; int dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; dLineOffset += dLineStride; int dstEnd = dPixelOffset + dwidth*dPixelStride; while (dPixelOffset < dstEnd) { int i1 = s1[s1PixelOffset]&0xFF; int i2 = s2[s2PixelOffset]&0xFF; d[dPixelOffset] = byteTable[(i1<<8) + i2]; s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; dPixelOffset += dPixelStride; } } } } private void computeRectUShort(RasterAccessor src1, RasterAccessor src2, RasterAccessor dst) { int s1LineStride = src1.getScanlineStride(); int s1PixelStride = src1.getPixelStride(); int[] s1BandOffsets = src1.getBandOffsets(); short[][] s1Data = src1.getShortDataArrays(); int s2LineStride = src2.getScanlineStride(); int s2PixelStride = src2.getPixelStride(); int[] s2BandOffsets = src2.getBandOffsets(); short[][] s2Data = src2.getShortDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int bands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); short[][] dData = dst.getShortDataArrays(); for (int b = 0; b < bands; b++) { short[] s1 = s1Data[b]; short[] s2 = s2Data[b]; short[] d = dData[b]; int s1LineOffset = s1BandOffsets[b]; int s2LineOffset = s2BandOffsets[b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int s1PixelOffset = s1LineOffset; int s2PixelOffset = s2LineOffset; int dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { d[dPixelOffset] = maxUShort(s1[s1PixelOffset], s2[s2PixelOffset]); s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; dPixelOffset += dPixelStride; } } } } private void computeRectShort(RasterAccessor src1, RasterAccessor src2, RasterAccessor dst) { int s1LineStride = src1.getScanlineStride(); int s1PixelStride = src1.getPixelStride(); int[] s1BandOffsets = src1.getBandOffsets(); short[][] s1Data = src1.getShortDataArrays(); int s2LineStride = src2.getScanlineStride(); int s2PixelStride = src2.getPixelStride(); int[] s2BandOffsets = src2.getBandOffsets(); short[][] s2Data = src2.getShortDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int bands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); short[][] dData = dst.getShortDataArrays(); for (int b = 0; b < bands; b++) { short[] s1 = s1Data[b]; short[] s2 = s2Data[b]; short[] d = dData[b]; int s1LineOffset = s1BandOffsets[b]; int s2LineOffset = s2BandOffsets[b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int s1PixelOffset = s1LineOffset; int s2PixelOffset = s2LineOffset; int dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { d[dPixelOffset] = maxShort(s1[s1PixelOffset], s2[s2PixelOffset]); s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; dPixelOffset += dPixelStride; } } } } private void computeRectInt(RasterAccessor src1, RasterAccessor src2, RasterAccessor dst) { int s1LineStride = src1.getScanlineStride(); int s1PixelStride = src1.getPixelStride(); int[] s1BandOffsets = src1.getBandOffsets(); int[][] s1Data = src1.getIntDataArrays(); int s2LineStride = src2.getScanlineStride(); int s2PixelStride = src2.getPixelStride(); int[] s2BandOffsets = src2.getBandOffsets(); int[][] s2Data = src2.getIntDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int bands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); int[][] dData = dst.getIntDataArrays(); for (int b = 0; b < bands; b++) { int[] s1 = s1Data[b]; int[] s2 = s2Data[b]; int[] d = dData[b]; int s1LineOffset = s1BandOffsets[b]; int s2LineOffset = s2BandOffsets[b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int s1PixelOffset = s1LineOffset; int s2PixelOffset = s2LineOffset; int dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { d[dPixelOffset] = maxInt(s1[s1PixelOffset], s2[s2PixelOffset]); s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; dPixelOffset += dPixelStride; } } } } private void computeRectFloat(RasterAccessor src1, RasterAccessor src2, RasterAccessor dst) { int s1LineStride = src1.getScanlineStride(); int s1PixelStride = src1.getPixelStride(); int[] s1BandOffsets = src1.getBandOffsets(); float[][] s1Data = src1.getFloatDataArrays(); int s2LineStride = src2.getScanlineStride(); int s2PixelStride = src2.getPixelStride(); int[] s2BandOffsets = src2.getBandOffsets(); float[][] s2Data = src2.getFloatDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int bands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); float[][] dData = dst.getFloatDataArrays(); for (int b = 0; b < bands; b++) { float[] s1 = s1Data[b]; float[] s2 = s2Data[b]; float[] d = dData[b]; int s1LineOffset = s1BandOffsets[b]; int s2LineOffset = s2BandOffsets[b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int s1PixelOffset = s1LineOffset; int s2PixelOffset = s2LineOffset; int dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { d[dPixelOffset] = maxFloat(s1[s1PixelOffset], s2[s2PixelOffset]); s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; dPixelOffset += dPixelStride; } } } } private void computeRectDouble(RasterAccessor src1, RasterAccessor src2, RasterAccessor dst) { int s1LineStride = src1.getScanlineStride(); int s1PixelStride = src1.getPixelStride(); int[] s1BandOffsets = src1.getBandOffsets(); double[][] s1Data = src1.getDoubleDataArrays(); int s2LineStride = src2.getScanlineStride(); int s2PixelStride = src2.getPixelStride(); int[] s2BandOffsets = src2.getBandOffsets(); double[][] s2Data = src2.getDoubleDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int bands = dst.getNumBands(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int[] dBandOffsets = dst.getBandOffsets(); double[][] dData = dst.getDoubleDataArrays(); for (int b = 0; b < bands; b++) { double[] s1 = s1Data[b]; double[] s2 = s2Data[b]; double[] d = dData[b]; int s1LineOffset = s1BandOffsets[b]; int s2LineOffset = s2BandOffsets[b]; int dLineOffset = dBandOffsets[b]; for (int h = 0; h < dheight; h++) { int s1PixelOffset = s1LineOffset; int s2PixelOffset = s2LineOffset; int dPixelOffset = dLineOffset; s1LineOffset += s1LineStride; s2LineOffset += s2LineStride; dLineOffset += dLineStride; for (int w = 0; w < dwidth; w++) { d[dPixelOffset] = maxDouble(s1[s1PixelOffset], s2[s2PixelOffset]); s1PixelOffset += s1PixelStride; s2PixelOffset += s2PixelStride; dPixelOffset += dPixelStride; } } } } private final short maxUShort(short a, short b) { return (a&0xFFFF) > (b&0xFFFF) ? a : b; } private final short maxShort(short a, short b) { return a > b ? a : b; } private final int maxInt(int a, int b) { return a > b ? a : b; } private final float maxFloat(float a, float b) { if (a != a) return a; // a is NaN if ((a == 0.0f) && (b == 0.0f) && (Float.floatToIntBits(a) == negativeZeroFloatBits)) { return b; } return (a >= b) ? a : b; } private final double maxDouble(double a, double b) { if (a != a) return a; // a is NaN if ((a == 0.0d) && (b == 0.0d) && (Double.doubleToLongBits(a) == negativeZeroDoubleBits)) { return b; } return (a >= b) ? a : b; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/DFTCRIF.java0000644000175000017500000000330610203035544025156 0ustar mathieumathieu/* * $RCSfile: DFTCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:22 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.CRIFImpl; import javax.media.jai.EnumeratedParameter; import javax.media.jai.ImageLayout; import java.util.Map; import javax.media.jai.operator.DFTDescriptor; /** * A CRIF supporting the "DFT" operation in the rendered * image layer. * * @since Beta * @see javax.media.jai.operator.DFTDescriptor * */ public class DFTCRIF extends CRIFImpl { /** Constructor. */ public DFTCRIF() { super("dft"); } /** * Creates a new instance of a DFT operator according to the scaling type. * * @param paramBlock The scaling type. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); RenderedImage source = paramBlock.getRenderedSource(0); EnumeratedParameter scalingType = (EnumeratedParameter)paramBlock.getObjectParameter(0); EnumeratedParameter dataNature = (EnumeratedParameter)paramBlock.getObjectParameter(1); FFT fft = new FFT(true, new Integer(scalingType.getValue()), 2); return new DFTOpImage(source, renderHints, layout, dataNature, fft); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/HistogramRIF.java0000644000175000017500000000452710203035544026401 0ustar mathieumathieu/* * $RCSfile: HistogramRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:28 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.JAI; import javax.media.jai.ROI; import javax.media.jai.util.ImagingListener; import com.sun.media.jai.util.ImageUtil; /** * A RIF supporting the "Histogram" operation in the * rendered image layer. * * @since EA2 * @see javax.media.jai.operator.HistogramDescriptor * @see HistogramOpImage * */ public class HistogramRIF implements RenderedImageFactory { /** Constructor. */ public HistogramRIF() {} /** * Creates a new instance of HistogramOpImage * in the rendered layer. Any image layout information in * RenderingHints is ignored. * This method satisfies the implementation of RIF. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { RenderedImage src = args.getRenderedSource(0); int xStart = src.getMinX(); // default values int yStart = src.getMinY(); int maxWidth = src.getWidth(); int maxHeight = src.getHeight(); ROI roi = (ROI)args.getObjectParameter(0); int xPeriod = args.getIntParameter(1); int yPeriod = args.getIntParameter(2); int[] numBins = (int[])args.getObjectParameter(3); double[] lowValue = (double[])args.getObjectParameter(4); double[] highValue = (double[])args.getObjectParameter(5); HistogramOpImage op = null; try { op = new HistogramOpImage(src, roi, xStart, yStart, xPeriod, yPeriod, numBins, lowValue, highValue); } catch (Exception e) { ImagingListener listener = ImageUtil.getImagingListener(hints); String message = JaiI18N.getString("HistogramRIF0"); listener.errorOccurred(message, e, this, false); } return op; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MultiplyCRIF.java0000644000175000017500000000322610203035544026361 0ustar mathieumathieu/* * $RCSfile: MultiplyCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:36 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.geom.Rectangle2D; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderContext; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "Multiply" operation in the * rendered and renderable image layers. * * @since EA2 * @see javax.media.jai.operator.MultiplyDescriptor * @see MultiplyOpImage * */ public class MultiplyCRIF extends CRIFImpl { /** Constructor. */ public MultiplyCRIF() { super("multiply"); } /** * Creates a new instance of MultiplyOpImage in the * rendered layer. This method satisifies the implementation of RIF. * * @param paramBlock The two source images to be multiplied. * @param renderHints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new MultiplyOpImage(paramBlock.getRenderedSource(0), paramBlock.getRenderedSource(1), renderHints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/XorOpImage.java0000644000175000017500000003105210203035544026106 0ustar mathieumathieu/* * $RCSfile: XorOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:48 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PointOpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage implementing the "Xor" operation as * described in javax.media.jai.operator.XorDescriptor. * *

    This OpImage logically "xors" the pixel values of two source * images on a per-band basis. In case the two source images have different * number of bands, the number of bands for the destination image is the * smaller band number of the two source images. That is * dstNumBands = Math.min(src1NumBands, src2NumBands). * In case the two source images have different data types, the data type * for the destination image is the bigger data type of the two source * images. * *

    The value of the pixel (x, y) in the destination image is defined as: *

     * for (b = 0; b < numBands; b++) {
     *     dst[y][x][b] = src1[y][x][b] ^ src2[y][x][b];
     * }
     * 
    * * The data type byte is treated as unsigned, with maximum * value as 255 and minimum value as 0. * * @since EA2 * @see javax.media.jai.operator.XorDescriptor * @see XorCRIF * */ final class XorOpImage extends PointOpImage { /** * Constructs an XorOpImage. * *

    The layout parameter may optionally contains the * tile grid layout, sample model, and/or color model. The image * dimension is determined by the intersection of the bounding boxes * of the two source images. * *

    The image layout of the first source image, source1, * is used as the fall-back for the image layout of the destination * image. Any layout parameters not specified in the layout * argument are set to the same value as that of source1. * * @param source1 The first source image. * @param source2 The second source image. * @param layout The destination image layout. */ public XorOpImage(RenderedImage source1, RenderedImage source2, Map config, ImageLayout layout) { super(source1, source2, layout, config, true); // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * Xors the pixel values of two source images within a specified * rectangle. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); /* For PointOpImage, srcRect = destRect. */ RasterAccessor s1 = new RasterAccessor(sources[0], destRect, formatTags[0], getSource(0).getColorModel()); RasterAccessor s2 = new RasterAccessor(sources[1], destRect, formatTags[1], getSource(1).getColorModel()); RasterAccessor d = new RasterAccessor(dest, destRect, formatTags[2], getColorModel()); if(d.isBinary()) { byte[] src1Bits = s1.getBinaryDataArray(); byte[] src2Bits = s2.getBinaryDataArray(); byte[] dstBits = d.getBinaryDataArray(); int length = dstBits.length; for(int i = 0; i < length; i++) { dstBits[i] = (byte)(src1Bits[i] ^ src2Bits[i]); } d.copyBinaryDataToRaster(); return; } int src1LineStride = s1.getScanlineStride(); int src1PixelStride = s1.getPixelStride(); int[] src1BandOffsets = s1.getBandOffsets(); int src2LineStride = s2.getScanlineStride(); int src2PixelStride = s2.getPixelStride(); int[] src2BandOffsets = s2.getBandOffsets(); int dstNumBands = d.getNumBands(); int dstWidth = d.getWidth(); int dstHeight = d.getHeight(); int dstLineStride = d.getScanlineStride(); int dstPixelStride = d.getPixelStride(); int[] dstBandOffsets = d.getBandOffsets(); switch (d.getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(dstNumBands, dstWidth, dstHeight, src1LineStride, src1PixelStride, src1BandOffsets, s1.getByteDataArrays(), src2LineStride, src2PixelStride, src2BandOffsets, s2.getByteDataArrays(), dstLineStride, dstPixelStride, dstBandOffsets, d.getByteDataArrays()); break; case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: shortLoop(dstNumBands, dstWidth, dstHeight, src1LineStride, src1PixelStride, src1BandOffsets, s1.getShortDataArrays(), src2LineStride, src2PixelStride, src2BandOffsets, s2.getShortDataArrays(), dstLineStride, dstPixelStride, dstBandOffsets, d.getShortDataArrays()); break; case DataBuffer.TYPE_INT: intLoop(dstNumBands, dstWidth, dstHeight, src1LineStride, src1PixelStride, src1BandOffsets, s1.getIntDataArrays(), src2LineStride, src2PixelStride, src2BandOffsets, s2.getIntDataArrays(), dstLineStride, dstPixelStride, dstBandOffsets, d.getIntDataArrays()); break; } d.copyDataToRaster(); } private void byteLoop(int dstNumBands, int dstWidth, int dstHeight, int src1LineStride, int src1PixelStride, int[] src1BandOffsets, byte[][] src1Data, int src2LineStride, int src2PixelStride, int[] src2BandOffsets, byte[][] src2Data, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, byte[][] dstData) { for (int b = 0; b < dstNumBands; b++) { byte[] s1 = src1Data[b]; byte[] s2 = src2Data[b]; byte[] d = dstData[b]; int src1LineOffset = src1BandOffsets[b]; int src2LineOffset = src2BandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int src1PixelOffset = src1LineOffset; int src2PixelOffset = src2LineOffset; int dstPixelOffset = dstLineOffset; src1LineOffset += src1LineStride; src2LineOffset += src2LineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = (byte)(s1[src1PixelOffset] ^ s2[src2PixelOffset]); src1PixelOffset += src1PixelStride; src2PixelOffset += src2PixelStride; dstPixelOffset += dstPixelStride; } } } } private void shortLoop(int dstNumBands, int dstWidth, int dstHeight, int src1LineStride, int src1PixelStride, int[] src1BandOffsets, short[][] src1Data, int src2LineStride, int src2PixelStride, int[] src2BandOffsets, short[][] src2Data, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, short[][] dstData) { for (int b = 0; b < dstNumBands; b++) { short[] s1 = src1Data[b]; short[] s2 = src2Data[b]; short[] d = dstData[b]; int src1LineOffset = src1BandOffsets[b]; int src2LineOffset = src2BandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int src1PixelOffset = src1LineOffset; int src2PixelOffset = src2LineOffset; int dstPixelOffset = dstLineOffset; src1LineOffset += src1LineStride; src2LineOffset += src2LineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = (short)(s1[src1PixelOffset] ^ s2[src2PixelOffset]); src1PixelOffset += src1PixelStride; src2PixelOffset += src2PixelStride; dstPixelOffset += dstPixelStride; } } } } private void intLoop(int dstNumBands, int dstWidth, int dstHeight, int src1LineStride, int src1PixelStride, int[] src1BandOffsets, int[][] src1Data, int src2LineStride, int src2PixelStride, int[] src2BandOffsets, int[][] src2Data, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, int[][] dstData) { for (int b = 0; b < dstNumBands; b++) { int[] s1 = src1Data[b]; int[] s2 = src2Data[b]; int[] d = dstData[b]; int src1LineOffset = src1BandOffsets[b]; int src2LineOffset = src2BandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int src1PixelOffset = src1LineOffset; int src2PixelOffset = src2LineOffset; int dstPixelOffset = dstLineOffset; src1LineOffset += src1LineStride; src2LineOffset += src2LineStride; dstLineOffset += dstLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = s1[src1PixelOffset] ^ s2[src2PixelOffset]; src1PixelOffset += src1PixelStride; src2PixelOffset += src2PixelStride; dstPixelOffset += dstPixelStride; } } } } // public static void main(String args[]) { // System.out.println("XorOpImage Test"); // ImageLayout layout; // OpImage src1, src2, dst; // Rectangle rect = new Rectangle(0, 0, 5, 5); // System.out.println("1. PixelInterleaved byte 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 800, 800, 0, 0, 200, 200, DataBuffer.TYPE_BYTE, 3, false); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new XorOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("2. Banded byte 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 800, 800, 0, 0, 200, 200, DataBuffer.TYPE_BYTE, 3, true); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new XorOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("3. PixelInterleaved int 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_INT, 3, false); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new XorOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("4. Banded int 3-band"); // layout = OpImageTester.createImageLayout( // 0, 0, 512, 512, 0, 0, 200, 200, DataBuffer.TYPE_INT, 3, true); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new XorOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ExpCRIF.java0000644000175000017500000000315410203035544025276 0ustar mathieumathieu/* * $RCSfile: ExpCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:25 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D.Float; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import java.awt.image.renderable.RenderContext; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * This image factory supports image operator ExpOpImage * in the rendered and renderable image layers. * * @since EA2 * @see javax.media.jai.operator.ExpDescriptor * @see ExpOpImage * */ public class ExpCRIF extends CRIFImpl { /** Constructor. */ public ExpCRIF() { super("exp"); } /** * Creates a new instance of ExpOpImage in the * rendered layer. This method satisfies the implementation of RIF. * * @param paramBlock The source image and the constants. * @param renderHints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock pb, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new ExpOpImage(pb.getRenderedSource(0), renderHints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ImageFunctionRIF.java0000644000175000017500000000567510203035544027201 0ustar mathieumathieu/* * $RCSfile: ImageFunctionRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:29 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderedImageFactory; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.ImageFunction; import javax.media.jai.ImageLayout; import java.util.Map; /** * A RIF supporting the "ImageFunction" operation in the rendered * image layer. * * @see javax.media.jai.operator.ImageFunctionDescriptor * @see javax.media.jai.ImageFunction * @since EA4 */ public class ImageFunctionRIF implements RenderedImageFactory { /** Constructor. */ public ImageFunctionRIF() {} /** * Creates a new instance of ImageFunctionOpImage in the rendered layer. * This method satisfies the implementation of RIF. * * @param paramBlock The source image, the X and Y scale factor, * and the interpolation method for resampling. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); ImageFunction function = (ImageFunction)paramBlock.getObjectParameter(0); // Ascertain that a supplied SampleModel has the requisite // number of bands vis-a-vis the ImageFunction. int numBandsRequired = function.isComplex() ? function.getNumElements() * 2 : function.getNumElements(); if(layout != null && layout.isValid(ImageLayout.SAMPLE_MODEL_MASK) && layout.getSampleModel(null).getNumBands() != numBandsRequired ) { throw new RuntimeException(JaiI18N.getString("ImageFunctionRIF0")); } int minX = 0; int minY = 0; if (layout != null) { if (layout.isValid(ImageLayout.MIN_X_MASK)) { minX = layout.getMinX(null); } if (layout.isValid(ImageLayout.MIN_Y_MASK)) { minY = layout.getMinX(null); } } int width = paramBlock.getIntParameter(1); int height = paramBlock.getIntParameter(2); float xScale = paramBlock.getFloatParameter(3); float yScale = paramBlock.getFloatParameter(4); float xTrans = paramBlock.getFloatParameter(5); float yTrans = paramBlock.getFloatParameter(6); return new ImageFunctionOpImage(function, minX, minY, width, height, xScale, yScale, xTrans, yTrans, renderHints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/AbsoluteCRIF.java0000644000175000017500000000331410203035544026316 0ustar mathieumathieu/* * $RCSfile: AbsoluteCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:11 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D.Float; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import java.awt.image.renderable.RenderContext; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * This image factory supports image operator AbsoluteOpImage * in the rendered and renderable image layers. * * @since EA2 * @see javax.media.jai.operator.AbsoluteDescriptor * @see AbsoluteOpImage * */ public class AbsoluteCRIF extends CRIFImpl { /** Constructor. */ public AbsoluteCRIF() { super("absolute"); } /** * Creates a new instance of AbsoluteOpImage in the * rendered layer. This method satisfies the implementation of RIF. * * @param paramBlock The source image and the constants. * @param renderHints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new AbsoluteOpImage(paramBlock.getRenderedSource(0), renderHints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/AffineBicubic2OpImage.java0000644000175000017500000016473410203035544030107 0ustar mathieumathieu/* * $RCSfile: AffineBicubic2OpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:12 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.geom.Point2D; import java.awt.geom.AffineTransform; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Map; /** * An OpImage subclass that performs bicubic2 Affine mapping */ final class AffineBicubic2OpImage extends AffineOpImage { /* The number of subsampleBits */ private int subsampleBits; private int shiftvalue; /** * Constructs an AffineBicubic2OpImage from a RenderedImage source, * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param interp an Interpolation object to use for resampling * @param transform the desired AffineTransform. */ public AffineBicubic2OpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, AffineTransform transform, Interpolation interp, double[] backgroundValues) { super(source, extender, config, layout, transform, interp, backgroundValues); subsampleBits = interp.getSubsampleBitsH(); shiftvalue = 1 << subsampleBits; } /** * Performs an affine transform on a specified rectangle. The sources are * cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster [] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); Raster source = sources[0]; Rectangle srcRect = source.getBounds(); int srcRectX = srcRect.x; int srcRectY = srcRect.y; // // Get data for the source rectangle & the destination rectangle // In the first version source Rectangle is the whole source // image always. // // See if we can cache the source to avoid multiple rasteraccesors // RasterAccessor srcAccessor = new RasterAccessor(source, srcRect, formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor dstAccessor = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(srcAccessor, destRect, srcRectX, srcRectY, dstAccessor); break; case DataBuffer.TYPE_INT: intLoop(srcAccessor, destRect, srcRectX, srcRectY, dstAccessor); break; case DataBuffer.TYPE_SHORT: shortLoop(srcAccessor, destRect, srcRectX, srcRectY, dstAccessor); break; case DataBuffer.TYPE_USHORT: ushortLoop(srcAccessor, destRect, srcRectX, srcRectY, dstAccessor); break; case DataBuffer.TYPE_FLOAT: floatLoop(srcAccessor, destRect, srcRectX, srcRectY, dstAccessor); break; case DataBuffer.TYPE_DOUBLE: doubleLoop(srcAccessor, destRect, srcRectX, srcRectY, dstAccessor); break; } // If the RasterAccessor object set up a temporary buffer for the // op to write to, tell the RasterAccessor to write that data // to the raster, that we're done with it. if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } private void byteLoop(RasterAccessor src, Rectangle destRect, int srcRectX, int srcRectY, RasterAccessor dst) { float src_rect_x1 = src.getX(); float src_rect_y1 = src.getY(); float src_rect_x2 = src_rect_x1 + src.getWidth(); float src_rect_y2 = src_rect_y1 + src.getHeight(); float s_x, s_y; float fracx, fracy; float float_fracx, float_fracy; float frac_xx, frac_yy; int s_ix, s_iy; int p_x, p_y; int p__, p0_, p1_, p2_; int p_0, p00, p01, p02; int p_1, p10, p11, p12; int p_2, p20, p21, p22; int s__, s0_, s1_, s2_; int s_0, s00, s01, s02; int s_1, s10, s11, s12; int s_2, s20, s21, s22; float s0, s1, s_, s2; float q_, q0, q1, q2; float s, q; int result; int xfrac, yfrac; int dstPixelOffset; int dstOffset = 0; Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); byte dstDataArrays[][] = dst.getByteDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); byte srcDataArrays[][] = src.getByteDataArrays(); int bandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; byte[] backgroundByte = new byte[dst_num_bands]; for (int i = 0; i < dst_num_bands; i++) backgroundByte[i] = (byte)backgroundValues[i]; for (int y = dst_min_y; y < dst_max_y ; y++) { dstPixelOffset = dstOffset; // Backward map the first point in the line // The energy is at the (pt_x + 0.5, pt_y + 0.5) dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates s_x = (float) src_pt.getX(); s_y = (float) src_pt.getY(); // As per definition of bicubic interpolation s_x -= 0.5; s_y -= 0.5; // Floor to get the integral coordinate s_ix = (int) Math.floor(s_x); s_iy = (int) Math.floor(s_y); fracx = s_x - (float) s_ix; fracy = s_y - (float) s_iy; // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; // // Get the 16 neighbouring positions of the // coordinate in question (p00). // // p__ p0_ p1_ p2_ // p_0 p00 p10 p20 // p_1 p01 p11 p21 // p_2 p02 p12 p22 // p__ = p_x + p_y - srcScanlineStride - srcPixelStride; p0_ = p__ + srcPixelStride; p1_ = p0_ + srcPixelStride; p2_ = p1_ + srcPixelStride; p_0 = p__ + srcScanlineStride; p00 = p_0 + srcPixelStride; p10 = p00 + srcPixelStride; p20 = p10 + srcPixelStride; p_1 = p_0 + srcScanlineStride; p01 = p_1 + srcPixelStride; p11 = p01 + srcPixelStride; p21 = p11 + srcPixelStride; p_2 = p_1 + srcScanlineStride; p02 = p_2 + srcPixelStride; p12 = p02 + srcPixelStride; p22 = p12 + srcPixelStride; for (int x = dst_min_x; x < dst_max_x; x++) { // // Check against the source rectangle // if ((s_ix >= src_rect_x1 + 1) && (s_ix < (src_rect_x2 - 2)) && (s_iy >= (src_rect_y1 + 1)) && (s_iy < (src_rect_y2 - 2))) { for (int k2=0; k2 < dst_num_bands; k2++) { // // Get the pixels // byte tmp_row[]; int tmp_col; tmp_row = srcDataArrays[k2]; tmp_col = bandOffsets[k2]; s__ = tmp_row[p__ + tmp_col] & 0xff; s0_ = tmp_row[p0_ + tmp_col] & 0xff; s1_ = tmp_row[p1_ + tmp_col] & 0xff; s2_ = tmp_row[p2_ + tmp_col] & 0xff; s_0 = tmp_row[p_0 + tmp_col] & 0xff; s00 = tmp_row[p00 + tmp_col] & 0xff; s10 = tmp_row[p10 + tmp_col] & 0xff; s20 = tmp_row[p20 + tmp_col] & 0xff; s_1 = tmp_row[p_1 + tmp_col] & 0xff; s01 = tmp_row[p01 + tmp_col] & 0xff; s11 = tmp_row[p11 + tmp_col] & 0xff; s21 = tmp_row[p21 + tmp_col] & 0xff; s_2 = tmp_row[p_2 + tmp_col] & 0xff; s02 = tmp_row[p02 + tmp_col] & 0xff; s12 = tmp_row[p12 + tmp_col] & 0xff; s22 = tmp_row[p22 + tmp_col] & 0xff; // Get the new frac values xfrac = (int) (fracx * shiftvalue); yfrac = (int) (fracy * shiftvalue); // Note that the notations are different from // what's mentioned in the interpolation class // For example s0_ here is actually s_0 in the // Interpolation class s = interp.interpolate(s__, s0_, s1_, s2_, s_0, s00, s10, s20, s_1, s01, s11, s21, s_2, s02, s12, s22, xfrac, yfrac); // Round if (s < 0.5F) { result = 0; } else if (s > 254.5F) { result = 255; } else { result = (int) (s + 0.5F); } // write the result dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = (byte) (result & 0xff); } } else if (setBackground) { for (int k=0; k < dst_num_bands; k++) dstDataArrays[k][dstPixelOffset+dstBandOffsets[k]] = backgroundByte[k]; } // walk if (fracx < fracdx1) { s_ix += incx; fracx += fracdx; if (fracx == 1.0F) { // To avoid overflow in the interpolation table fracx = 0.999999F; } } else { s_ix += incx1; fracx -= fracdx1; } if (fracy < fracdy1) { s_iy += incy; fracy += fracdy; if (fracy == 1.0F) { // To avoid overflow in the interpolation table fracy = 0.999999F; } } else { s_iy += incy1; fracy -= fracdy1; } // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; // // Get the 16 neighbouring positions of the // coordinate in question (p00). // // p__ p0_ p1_ p2_ // p_0 p00 p10 p20 // p_1 p01 p11 p21 // p_2 p02 p12 p22 // p__ = p_x + p_y - srcScanlineStride - srcPixelStride; p0_ = p__ + srcPixelStride; p1_ = p0_ + srcPixelStride; p2_ = p1_ + srcPixelStride; p_0 = p__ + srcScanlineStride; p00 = p_0 + srcPixelStride; p10 = p00 + srcPixelStride; p20 = p10 + srcPixelStride; p_1 = p_0 + srcScanlineStride; p01 = p_1 + srcPixelStride; p11 = p01 + srcPixelStride; p21 = p11 + srcPixelStride; p_2 = p_1 + srcScanlineStride; p02 = p_2 + srcPixelStride; p12 = p02 + srcPixelStride; p22 = p12 + srcPixelStride; dstPixelOffset += dstPixelStride; } dstOffset += dstScanlineStride; } } private void intLoop(RasterAccessor src, Rectangle destRect, int srcRectX, int srcRectY, RasterAccessor dst) { float src_rect_x1 = src.getX(); float src_rect_y1 = src.getY(); float src_rect_x2 = src_rect_x1 + src.getWidth(); float src_rect_y2 = src_rect_y1 + src.getHeight(); float s_x, s_y; float fracx, fracy; float float_fracx, float_fracy; float frac_xx, frac_yy; int s_ix, s_iy; int p_x, p_y; int p__, p0_, p1_, p2_; int p_0, p00, p01, p02; int p_1, p10, p11, p12; int p_2, p20, p21, p22; int s__, s0_, s1_, s2_; int s_0, s00, s01, s02; int s_1, s10, s11, s12; int s_2, s20, s21, s22; float s0, s1, s_, s2; float q_, q0, q1, q2; float s, q; int result; int dstPixelOffset; int dstOffset = 0; int xfrac, yfrac; Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); int dstDataArrays[][] = dst.getIntDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcDataArrays[][] = src.getIntDataArrays(); int bandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; int[] backgroundInt = new int[dst_num_bands]; for (int i = 0; i < dst_num_bands; i++) backgroundInt[i] = (int)backgroundValues[i]; for (int y = dst_min_y; y < dst_max_y ; y++) { dstPixelOffset = dstOffset; // Backward map the first point in the line // The energy is at the (pt_x + 0.5, pt_y + 0.5) dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates s_x = (float) src_pt.getX(); s_y = (float) src_pt.getY(); // As per definition of bicubic interpolation s_x -= 0.5; s_y -= 0.5; // Floor to get the integral coordinate s_ix = (int) Math.floor(s_x); s_iy = (int) Math.floor(s_y); fracx = s_x - (float) s_ix; fracy = s_y - (float) s_iy; // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; // // Get the 16 neighbouring positions of the // coordinate in question (p00). // // p__ p0_ p1_ p2_ // p_0 p00 p10 p20 // p_1 p01 p11 p21 // p_2 p02 p12 p22 // p__ = p_x + p_y - srcScanlineStride - srcPixelStride; p0_ = p__ + srcPixelStride; p1_ = p0_ + srcPixelStride; p2_ = p1_ + srcPixelStride; p_0 = p__ + srcScanlineStride; p00 = p_0 + srcPixelStride; p10 = p00 + srcPixelStride; p20 = p10 + srcPixelStride; p_1 = p_0 + srcScanlineStride; p01 = p_1 + srcPixelStride; p11 = p01 + srcPixelStride; p21 = p11 + srcPixelStride; p_2 = p_1 + srcScanlineStride; p02 = p_2 + srcPixelStride; p12 = p02 + srcPixelStride; p22 = p12 + srcPixelStride; for (int x = dst_min_x; x < dst_max_x; x++) { // // Check against the source rectangle // if ((s_ix >= (src_rect_x1 + 1)) && (s_ix < (src_rect_x2 - 2)) && (s_iy >= (src_rect_y1 + 1)) && (s_iy < (src_rect_y2 - 2))) { for (int k2=0; k2 < dst_num_bands; k2++) { // // Get the pixels // int tmp_row[]; int tmp_col; tmp_row = srcDataArrays[k2]; tmp_col = bandOffsets[k2]; s__ = tmp_row[p__ + tmp_col]; s0_ = tmp_row[p0_ + tmp_col]; s1_ = tmp_row[p1_ + tmp_col]; s2_ = tmp_row[p2_ + tmp_col]; s_0 = tmp_row[p_0 + tmp_col]; s00 = tmp_row[p00 + tmp_col]; s10 = tmp_row[p10 + tmp_col]; s20 = tmp_row[p20 + tmp_col]; s_1 = tmp_row[p_1 + tmp_col]; s01 = tmp_row[p01 + tmp_col]; s11 = tmp_row[p11 + tmp_col]; s21 = tmp_row[p21 + tmp_col]; s_2 = tmp_row[p_2 + tmp_col]; s02 = tmp_row[p02 + tmp_col]; s12 = tmp_row[p12 + tmp_col]; s22 = tmp_row[p22 + tmp_col]; // Get the new frac values xfrac = (int) (fracx * shiftvalue); yfrac = (int) (fracy * shiftvalue); // Note that the notations are different from // what's mentioned in the interpolation class // For example s0_ here is actually s_0 in the // Interpolation class s = interp.interpolate(s__, s0_, s1_, s2_, s_0, s00, s10, s20, s_1, s01, s11, s21, s_2, s02, s12, s22, xfrac, yfrac); // Round the result if (s < (float)(Integer.MIN_VALUE)) { result = Integer.MIN_VALUE; } else if (s > (float)(Integer.MAX_VALUE)) { result = Integer.MAX_VALUE; } else if (s > 0.0) { result = (int) (s + 0.5F); } else { result = (int) (s - 0.5F); } // write the result dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = result; } } else if (setBackground) { for (int k=0; k < dst_num_bands; k++) dstDataArrays[k][dstPixelOffset+dstBandOffsets[k]] = backgroundInt[k]; } // walk if (fracx < fracdx1) { s_ix += incx; fracx += fracdx; if (fracx == 1.0F) { // To avoid overflow in the interpolation table fracx = 0.999999F; } } else { s_ix += incx1; fracx -= fracdx1; } if (fracy < fracdy1) { s_iy += incy; fracy += fracdy; if (fracy == 1.0F) { // To avoid overflow in the interpolation table fracy = 0.999999F; } } else { s_iy += incy1; fracy -= fracdy1; } // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; // // Get the 16 neighbouring positions of the // coordinate in question (p00). // // p__ p0_ p1_ p2_ // p_0 p00 p10 p20 // p_1 p01 p11 p21 // p_2 p02 p12 p22 // p__ = p_x + p_y - srcScanlineStride - srcPixelStride; p0_ = p__ + srcPixelStride; p1_ = p0_ + srcPixelStride; p2_ = p1_ + srcPixelStride; p_0 = p__ + srcScanlineStride; p00 = p_0 + srcPixelStride; p10 = p00 + srcPixelStride; p20 = p10 + srcPixelStride; p_1 = p_0 + srcScanlineStride; p01 = p_1 + srcPixelStride; p11 = p01 + srcPixelStride; p21 = p11 + srcPixelStride; p_2 = p_1 + srcScanlineStride; p02 = p_2 + srcPixelStride; p12 = p02 + srcPixelStride; p22 = p12 + srcPixelStride; dstPixelOffset += dstPixelStride; } dstOffset += dstScanlineStride; } } private void shortLoop(RasterAccessor src, Rectangle destRect, int srcRectX, int srcRectY, RasterAccessor dst) { float src_rect_x1 = src.getX(); float src_rect_y1 = src.getY(); float src_rect_x2 = src_rect_x1 + src.getWidth(); float src_rect_y2 = src_rect_y1 + src.getHeight(); float s_x, s_y; float fracx, fracy; float float_fracx, float_fracy; float frac_xx, frac_yy; int s_ix, s_iy; int p_x, p_y; int p__, p0_, p1_, p2_; int p_0, p00, p01, p02; int p_1, p10, p11, p12; int p_2, p20, p21, p22; short s__, s0_, s1_, s2_; short s_0, s00, s01, s02; short s_1, s10, s11, s12; short s_2, s20, s21, s22; float s0, s1, s_, s2; float q_, q0, q1, q2; float s, q; int xfrac, yfrac; short result; int dstPixelOffset; int dstOffset = 0; Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int bandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; short[] backgroundShort = new short[dst_num_bands]; for (int i = 0; i < dst_num_bands; i++) backgroundShort[i] = (short)backgroundValues[i]; for (int y = dst_min_y; y < dst_max_y ; y++) { dstPixelOffset = dstOffset; // Backward map the first point in the line // The energy is at the (pt_x + 0.5, pt_y + 0.5) dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates s_x = (float) src_pt.getX(); s_y = (float) src_pt.getY(); // As per definition of bicubic interpolation s_x -= 0.5; s_y -= 0.5; // Floor to get the integral coordinate s_ix = (int) Math.floor(s_x); s_iy = (int) Math.floor(s_y); fracx = s_x - (float) s_ix; fracy = s_y - (float) s_iy; // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; // // Get the 16 neighbouring positions of the // coordinate in question (p00). // // p__ p0_ p1_ p2_ // p_0 p00 p10 p20 // p_1 p01 p11 p21 // p_2 p02 p12 p22 // p__ = p_x + p_y - srcScanlineStride - srcPixelStride; p0_ = p__ + srcPixelStride; p1_ = p0_ + srcPixelStride; p2_ = p1_ + srcPixelStride; p_0 = p__ + srcScanlineStride; p00 = p_0 + srcPixelStride; p10 = p00 + srcPixelStride; p20 = p10 + srcPixelStride; p_1 = p_0 + srcScanlineStride; p01 = p_1 + srcPixelStride; p11 = p01 + srcPixelStride; p21 = p11 + srcPixelStride; p_2 = p_1 + srcScanlineStride; p02 = p_2 + srcPixelStride; p12 = p02 + srcPixelStride; p22 = p12 + srcPixelStride; for (int x = dst_min_x; x < dst_max_x; x++) { // // Check against the source rectangle // if ((s_ix >= (src_rect_x1 + 1)) && (s_ix < (src_rect_x2 - 2)) && (s_iy >= (src_rect_y1 + 1)) && (s_iy < (src_rect_y2 - 2))) { for (int k2=0; k2 < dst_num_bands; k2++) { // // Get the pixels // short tmp_row[]; int tmp_col; tmp_row = srcDataArrays[k2]; tmp_col = bandOffsets[k2]; s__ = tmp_row[p__ + tmp_col]; s0_ = tmp_row[p0_ + tmp_col]; s1_ = tmp_row[p1_ + tmp_col]; s2_ = tmp_row[p2_ + tmp_col]; s_0 = tmp_row[p_0 + tmp_col]; s00 = tmp_row[p00 + tmp_col]; s10 = tmp_row[p10 + tmp_col]; s20 = tmp_row[p20 + tmp_col]; s_1 = tmp_row[p_1 + tmp_col]; s01 = tmp_row[p01 + tmp_col]; s11 = tmp_row[p11 + tmp_col]; s21 = tmp_row[p21 + tmp_col]; s_2 = tmp_row[p_2 + tmp_col]; s02 = tmp_row[p02 + tmp_col]; s12 = tmp_row[p12 + tmp_col]; s22 = tmp_row[p22 + tmp_col]; // Get the new frac values xfrac = (int) (fracx * shiftvalue); yfrac = (int) (fracy * shiftvalue); // Note that the notations are different from // what's mentioned in the interpolation class // For example s0_ here is actually s_0 in the // Interpolation class s = interp.interpolate(s__, s0_, s1_, s2_, s_0, s00, s10, s20, s_1, s01, s11, s21, s_2, s02, s12, s22, xfrac, yfrac); // Round the result if (s < (float)Short.MIN_VALUE) { result = Short.MIN_VALUE; } else if (s > (float)Short.MAX_VALUE) { result = Short.MAX_VALUE; } else if (s > 0.0) { result = (short) (s + 0.5F); } else { result = (short) (s - 0.5F); } // write the result dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = result; } } else if (setBackground) { for (int k=0; k < dst_num_bands; k++) dstDataArrays[k][dstPixelOffset+dstBandOffsets[k]] = backgroundShort[k]; } // walk if (fracx < fracdx1) { s_ix += incx; fracx += fracdx; if (fracx == 1.0F) { // To avoid overflow in the interpolation table fracx = 0.999999F; } } else { s_ix += incx1; fracx -= fracdx1; } if (fracy < fracdy1) { s_iy += incy; fracy += fracdy; if (fracy == 1.0F) { // To avoid overflow in the interpolation table fracy = 0.999999F; } } else { s_iy += incy1; fracy -= fracdy1; } // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; // // Get the 16 neighbouring positions of the // coordinate in question (p00). // // p__ p0_ p1_ p2_ // p_0 p00 p10 p20 // p_1 p01 p11 p21 // p_2 p02 p12 p22 // p__ = p_x + p_y - srcScanlineStride - srcPixelStride; p0_ = p__ + srcPixelStride; p1_ = p0_ + srcPixelStride; p2_ = p1_ + srcPixelStride; p_0 = p__ + srcScanlineStride; p00 = p_0 + srcPixelStride; p10 = p00 + srcPixelStride; p20 = p10 + srcPixelStride; p_1 = p_0 + srcScanlineStride; p01 = p_1 + srcPixelStride; p11 = p01 + srcPixelStride; p21 = p11 + srcPixelStride; p_2 = p_1 + srcScanlineStride; p02 = p_2 + srcPixelStride; p12 = p02 + srcPixelStride; p22 = p12 + srcPixelStride; dstPixelOffset += dstPixelStride; } dstOffset += dstScanlineStride; } } private void ushortLoop(RasterAccessor src, Rectangle destRect, int srcRectX, int srcRectY, RasterAccessor dst) { float src_rect_x1 = src.getX(); float src_rect_y1 = src.getY(); float src_rect_x2 = src_rect_x1 + src.getWidth(); float src_rect_y2 = src_rect_y1 + src.getHeight(); float s_x, s_y; float fracx, fracy; float float_fracx, float_fracy; float frac_xx, frac_yy; int s_ix, s_iy; int p_x, p_y; int p__, p0_, p1_, p2_; int p_0, p00, p01, p02; int p_1, p10, p11, p12; int p_2, p20, p21, p22; int s__, s0_, s1_, s2_; int s_0, s00, s01, s02; int s_1, s10, s11, s12; int s_2, s20, s21, s22; float s0, s1, s_, s2; float q_, q0, q1, q2; float s, q; int xfrac, yfrac; int result; int dstPixelOffset; int dstOffset = 0; Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int bandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; short[] backgroundUShort = new short[dst_num_bands]; for (int i = 0; i < dst_num_bands; i++) backgroundUShort[i] = (short)backgroundValues[i]; for (int y = dst_min_y; y < dst_max_y ; y++) { dstPixelOffset = dstOffset; // Backward map the first point in the line // The energy is at the (pt_x + 0.5, pt_y + 0.5) dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates s_x = (float) src_pt.getX(); s_y = (float) src_pt.getY(); // As per definition of bicubic interpolation s_x -= 0.5; s_y -= 0.5; // Floor to get the integral coordinate s_ix = (int) Math.floor(s_x); s_iy = (int) Math.floor(s_y); fracx = s_x - (float) s_ix; fracy = s_y - (float) s_iy; // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; // // Get the 16 neighbouring positions of the // coordinate in question (p00). // // p__ p0_ p1_ p2_ // p_0 p00 p10 p20 // p_1 p01 p11 p21 // p_2 p02 p12 p22 // p__ = p_x + p_y - srcScanlineStride - srcPixelStride; p0_ = p__ + srcPixelStride; p1_ = p0_ + srcPixelStride; p2_ = p1_ + srcPixelStride; p_0 = p__ + srcScanlineStride; p00 = p_0 + srcPixelStride; p10 = p00 + srcPixelStride; p20 = p10 + srcPixelStride; p_1 = p_0 + srcScanlineStride; p01 = p_1 + srcPixelStride; p11 = p01 + srcPixelStride; p21 = p11 + srcPixelStride; p_2 = p_1 + srcScanlineStride; p02 = p_2 + srcPixelStride; p12 = p02 + srcPixelStride; p22 = p12 + srcPixelStride; for (int x = dst_min_x; x < dst_max_x; x++) { // // Check against the source rectangle // if ((s_ix >= (src_rect_x1 + 1)) && (s_ix < (src_rect_x2 - 2)) && (s_iy >= (src_rect_y1 + 1)) && (s_iy < (src_rect_y2 - 2))) { for (int k2=0; k2 < dst_num_bands; k2++) { // // Get the pixels // short tmp_row[]; int tmp_col; tmp_row = srcDataArrays[k2]; tmp_col = bandOffsets[k2]; s__ = tmp_row[p__ + tmp_col] & 0xffff; s0_ = tmp_row[p0_ + tmp_col] & 0xffff; s1_ = tmp_row[p1_ + tmp_col] & 0xffff; s2_ = tmp_row[p2_ + tmp_col] & 0xffff; s_0 = tmp_row[p_0 + tmp_col] & 0xffff; s00 = tmp_row[p00 + tmp_col] & 0xffff; s10 = tmp_row[p10 + tmp_col] & 0xffff; s20 = tmp_row[p20 + tmp_col] & 0xffff; s_1 = tmp_row[p_1 + tmp_col] & 0xffff; s01 = tmp_row[p01 + tmp_col] & 0xffff; s11 = tmp_row[p11 + tmp_col] & 0xffff; s21 = tmp_row[p21 + tmp_col] & 0xffff; s_2 = tmp_row[p_2 + tmp_col] & 0xffff; s02 = tmp_row[p02 + tmp_col] & 0xffff; s12 = tmp_row[p12 + tmp_col] & 0xffff; s22 = tmp_row[p22 + tmp_col] & 0xffff; // Get the new frac values // Get the new frac values xfrac = (int) (fracx * shiftvalue); yfrac = (int) (fracy * shiftvalue); // Note that the notations are different from // what's mentioned in the interpolation class // For example s0_ here is actually s_0 in the // Interpolation class s = interp.interpolate(s__, s0_, s1_, s2_, s_0, s00, s10, s20, s_1, s01, s11, s21, s_2, s02, s12, s22, xfrac, yfrac); // Round if (s < 0.0) { result = 0; } else if (s > (float) USHORT_MAX) { result = USHORT_MAX; } else { result = (int) (s + 0.5F); } // write the result dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = (short)(result & 0xFFFF); } } else if (setBackground) { for (int k=0; k < dst_num_bands; k++) dstDataArrays[k][dstPixelOffset+dstBandOffsets[k]] = backgroundUShort[k]; } // walk if (fracx < fracdx1) { s_ix += incx; fracx += fracdx; if (fracx == 1.0F) { // To avoid overflow in the interpolation table fracx = 0.999999F; } } else { s_ix += incx1; fracx -= fracdx1; } if (fracy < fracdy1) { s_iy += incy; fracy += fracdy; if (fracy == 1.0F) { // To avoid overflow in the interpolation table fracy = 0.999999F; } } else { s_iy += incy1; fracy -= fracdy1; } // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; // // Get the 16 neighbouring positions of the // coordinate in question (p00). // // p__ p0_ p1_ p2_ // p_0 p00 p10 p20 // p_1 p01 p11 p21 // p_2 p02 p12 p22 // p__ = p_x + p_y - srcScanlineStride - srcPixelStride; p0_ = p__ + srcPixelStride; p1_ = p0_ + srcPixelStride; p2_ = p1_ + srcPixelStride; p_0 = p__ + srcScanlineStride; p00 = p_0 + srcPixelStride; p10 = p00 + srcPixelStride; p20 = p10 + srcPixelStride; p_1 = p_0 + srcScanlineStride; p01 = p_1 + srcPixelStride; p11 = p01 + srcPixelStride; p21 = p11 + srcPixelStride; p_2 = p_1 + srcScanlineStride; p02 = p_2 + srcPixelStride; p12 = p02 + srcPixelStride; p22 = p12 + srcPixelStride; dstPixelOffset += dstPixelStride; } dstOffset += dstScanlineStride; } } private void floatLoop(RasterAccessor src, Rectangle destRect, int srcRectX, int srcRectY, RasterAccessor dst) { float src_rect_x1 = src.getX(); float src_rect_y1 = src.getY(); float src_rect_x2 = src_rect_x1 + src.getWidth(); float src_rect_y2 = src_rect_y1 + src.getHeight(); float s_x, s_y; float fracx, fracy; float float_fracx, float_fracy; float frac_xx, frac_yy; int s_ix, s_iy; int p_x, p_y; int p__, p0_, p1_, p2_; int p_0, p00, p01, p02; int p_1, p10, p11, p12; int p_2, p20, p21, p22; float s__, s0_, s1_, s2_; float s_0, s00, s01, s02; float s_1, s10, s11, s12; float s_2, s20, s21, s22; float s0, s1, s_, s2; float q_, q0, q1, q2; float s, q; int dstPixelOffset; int dstOffset = 0; Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); float dstDataArrays[][] = dst.getFloatDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); float srcDataArrays[][] = src.getFloatDataArrays(); int bandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; float[] backgroundFloat = new float[dst_num_bands]; for (int i = 0; i < dst_num_bands; i++) backgroundFloat[i] = (float)backgroundValues[i]; for (int y = dst_min_y; y < dst_max_y ; y++) { dstPixelOffset = dstOffset; // Backward map the first point in the line // The energy is at the (pt_x + 0.5, pt_y + 0.5) dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates s_x = (float) src_pt.getX(); s_y = (float) src_pt.getY(); // As per definition of bicubic interpolation s_x -= 0.5; s_y -= 0.5; // Floor to get the integral coordinate s_ix = (int) Math.floor(s_x); s_iy = (int) Math.floor(s_y); fracx = s_x - (float) s_ix; fracy = s_y - (float) s_iy; // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; // // Get the 16 neighbouring positions of the // coordinate in question (p00). // // p__ p0_ p1_ p2_ // p_0 p00 p10 p20 // p_1 p01 p11 p21 // p_2 p02 p12 p22 // p__ = p_x + p_y - srcScanlineStride - srcPixelStride; p0_ = p__ + srcPixelStride; p1_ = p0_ + srcPixelStride; p2_ = p1_ + srcPixelStride; p_0 = p__ + srcScanlineStride; p00 = p_0 + srcPixelStride; p10 = p00 + srcPixelStride; p20 = p10 + srcPixelStride; p_1 = p_0 + srcScanlineStride; p01 = p_1 + srcPixelStride; p11 = p01 + srcPixelStride; p21 = p11 + srcPixelStride; p_2 = p_1 + srcScanlineStride; p02 = p_2 + srcPixelStride; p12 = p02 + srcPixelStride; p22 = p12 + srcPixelStride; for (int x = dst_min_x; x < dst_max_x; x++) { // // Check against the source rectangle // if ((s_ix >= (src_rect_x1 + 1)) && (s_ix < (src_rect_x2 - 2)) && (s_iy >= (src_rect_y1 + 1)) && (s_iy < (src_rect_y2 - 2))) { for (int k2=0; k2 < dst_num_bands; k2++) { // // Get the pixels // float tmp_row[]; int tmp_col; tmp_row = srcDataArrays[k2]; tmp_col = bandOffsets[k2]; s__ = tmp_row[p__ + tmp_col]; s0_ = tmp_row[p0_ + tmp_col]; s1_ = tmp_row[p1_ + tmp_col]; s2_ = tmp_row[p2_ + tmp_col]; s_0 = tmp_row[p_0 + tmp_col]; s00 = tmp_row[p00 + tmp_col]; s10 = tmp_row[p10 + tmp_col]; s20 = tmp_row[p20 + tmp_col]; s_1 = tmp_row[p_1 + tmp_col]; s01 = tmp_row[p01 + tmp_col]; s11 = tmp_row[p11 + tmp_col]; s21 = tmp_row[p21 + tmp_col]; s_2 = tmp_row[p_2 + tmp_col]; s02 = tmp_row[p02 + tmp_col]; s12 = tmp_row[p12 + tmp_col]; s22 = tmp_row[p22 + tmp_col]; // Note that the notations are different from // what's mentioned in the interpolation class // For example s0_ here is actually s_0 in the // Interpolation class s = interp.interpolate(s__, s0_, s1_, s2_, s_0, s00, s10, s20, s_1, s01, s11, s21, s_2, s02, s12, s22, fracx, fracy); // write the result dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = s; } } else if (setBackground) { for (int k=0; k < dst_num_bands; k++) dstDataArrays[k][dstPixelOffset+dstBandOffsets[k]] = backgroundFloat[k]; } // walk if (fracx < fracdx1) { s_ix += incx; fracx += fracdx; if (fracx == 1.0F) { // To avoid overflow in the interpolation table fracx = 0.999999F; } } else { s_ix += incx1; fracx -= fracdx1; } if (fracy < fracdy1) { s_iy += incy; fracy += fracdy; if (fracy == 1.0F) { // To avoid overflow in the interpolation table fracy = 0.999999F; } } else { s_iy += incy1; fracy -= fracdy1; } // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; // // Get the 16 neighbouring positions of the // coordinate in question (p00). // // p__ p0_ p1_ p2_ // p_0 p00 p10 p20 // p_1 p01 p11 p21 // p_2 p02 p12 p22 // p__ = p_x + p_y - srcScanlineStride - srcPixelStride; p0_ = p__ + srcPixelStride; p1_ = p0_ + srcPixelStride; p2_ = p1_ + srcPixelStride; p_0 = p__ + srcScanlineStride; p00 = p_0 + srcPixelStride; p10 = p00 + srcPixelStride; p20 = p10 + srcPixelStride; p_1 = p_0 + srcScanlineStride; p01 = p_1 + srcPixelStride; p11 = p01 + srcPixelStride; p21 = p11 + srcPixelStride; p_2 = p_1 + srcScanlineStride; p02 = p_2 + srcPixelStride; p12 = p02 + srcPixelStride; p22 = p12 + srcPixelStride; dstPixelOffset += dstPixelStride; } dstOffset += dstScanlineStride; } } private void doubleLoop(RasterAccessor src, Rectangle destRect, int srcRectX, int srcRectY, RasterAccessor dst) { float src_rect_x1 = src.getX(); float src_rect_y1 = src.getY(); float src_rect_x2 = src_rect_x1 + src.getWidth(); float src_rect_y2 = src_rect_y1 + src.getHeight(); double s_x, s_y; double fracx, fracy; double float_fracx, float_fracy; double frac_xx, frac_yy; int s_ix, s_iy; int p_x, p_y; int p__, p0_, p1_, p2_; int p_0, p00, p01, p02; int p_1, p10, p11, p12; int p_2, p20, p21, p22; double s__, s0_, s1_, s2_; double s_0, s00, s01, s02; double s_1, s10, s11, s12; double s_2, s20, s21, s22; double s0, s1, s_, s2; double q_, q0, q1, q2; double s, q; int dstPixelOffset; int dstOffset = 0; Point2D dst_pt = new Point2D.Float(); Point2D src_pt = new Point2D.Float(); double dstDataArrays[][] = dst.getDoubleDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); double srcDataArrays[][] = src.getDoubleDataArrays(); int bandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int dst_num_bands = dst.getNumBands(); int dst_min_x = destRect.x; int dst_min_y = destRect.y; int dst_max_x = destRect.x + destRect.width; int dst_max_y = destRect.y + destRect.height; for (int y = dst_min_y; y < dst_max_y ; y++) { dstPixelOffset = dstOffset; // Backward map the first point in the line // The energy is at the (pt_x + 0.5, pt_y + 0.5) dst_pt.setLocation((double)dst_min_x + 0.5, (double)y + 0.5); mapDestPoint(dst_pt, src_pt); // Get the mapped source coordinates s_x = (double) src_pt.getX(); s_y = (double) src_pt.getY(); // As per definition of bicubic interpolation s_x -= 0.5; s_y -= 0.5; // Floor to get the integral coordinate s_ix = (int) Math.floor(s_x); s_iy = (int) Math.floor(s_y); fracx = s_x - (double) s_ix; fracy = s_y - (double) s_iy; // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; // // Get the 16 neighbouring positions of the // coordinate in question (p00). // // p__ p0_ p1_ p2_ // p_0 p00 p10 p20 // p_1 p01 p11 p21 // p_2 p02 p12 p22 // p__ = p_x + p_y - srcScanlineStride - srcPixelStride; p0_ = p__ + srcPixelStride; p1_ = p0_ + srcPixelStride; p2_ = p1_ + srcPixelStride; p_0 = p__ + srcScanlineStride; p00 = p_0 + srcPixelStride; p10 = p00 + srcPixelStride; p20 = p10 + srcPixelStride; p_1 = p_0 + srcScanlineStride; p01 = p_1 + srcPixelStride; p11 = p01 + srcPixelStride; p21 = p11 + srcPixelStride; p_2 = p_1 + srcScanlineStride; p02 = p_2 + srcPixelStride; p12 = p02 + srcPixelStride; p22 = p12 + srcPixelStride; for (int x = dst_min_x; x < dst_max_x; x++) { // // Check against the source rectangle // if ((s_ix >= (src_rect_x1 + 1)) && (s_ix < (src_rect_x2 - 2)) && (s_iy >= (src_rect_y1 + 1)) && (s_iy < (src_rect_y2 - 2))) { for (int k2=0; k2 < dst_num_bands; k2++) { // // Get the pixels // double tmp_row[]; int tmp_col; tmp_row = srcDataArrays[k2]; tmp_col = bandOffsets[k2]; s__ = tmp_row[p__ + tmp_col]; s0_ = tmp_row[p0_ + tmp_col]; s1_ = tmp_row[p1_ + tmp_col]; s2_ = tmp_row[p2_ + tmp_col]; s_0 = tmp_row[p_0 + tmp_col]; s00 = tmp_row[p00 + tmp_col]; s10 = tmp_row[p10 + tmp_col]; s20 = tmp_row[p20 + tmp_col]; s_1 = tmp_row[p_1 + tmp_col]; s01 = tmp_row[p01 + tmp_col]; s11 = tmp_row[p11 + tmp_col]; s21 = tmp_row[p21 + tmp_col]; s_2 = tmp_row[p_2 + tmp_col]; s02 = tmp_row[p02 + tmp_col]; s12 = tmp_row[p12 + tmp_col]; s22 = tmp_row[p22 + tmp_col]; // Note that the notations are different from // what's mentioned in the interpolation class // For example s0_ here is actually s_0 in the // Interpolation class s = interp.interpolate(s__, s0_, s1_, s2_, s_0, s00, s10, s20, s_1, s01, s11, s21, s_2, s02, s12, s22, (float)fracx, (float)fracy); // write the result dstDataArrays[k2] [dstPixelOffset+dstBandOffsets[k2]] = s; } } else if (setBackground) { for (int k=0; k < dst_num_bands; k++) dstDataArrays[k][dstPixelOffset+dstBandOffsets[k]] = backgroundValues[k]; } // walk if (fracx < fracdx1) { s_ix += incx; fracx += fracdx; if (fracx == 1.0) { // To avoid overflow in the interpolation table fracx = 0.999999; } } else { s_ix += incx1; fracx -= fracdx1; } if (fracy < fracdy1) { s_iy += incy; fracy += fracdy; if (fracy == 1.0) { // To avoid overflow in the interpolation table fracy = 0.999999; } } else { s_iy += incy1; fracy -= fracdy1; } // Translate to/from SampleModel space & Raster space p_x = (s_ix - srcRectX) * srcPixelStride; p_y = (s_iy - srcRectY) * srcScanlineStride; // // Get the 16 neighbouring positions of the // coordinate in question (p00). // // p__ p0_ p1_ p2_ // p_0 p00 p10 p20 // p_1 p01 p11 p21 // p_2 p02 p12 p22 // p__ = p_x + p_y - srcScanlineStride - srcPixelStride; p0_ = p__ + srcPixelStride; p1_ = p0_ + srcPixelStride; p2_ = p1_ + srcPixelStride; p_0 = p__ + srcScanlineStride; p00 = p_0 + srcPixelStride; p10 = p00 + srcPixelStride; p20 = p10 + srcPixelStride; p_1 = p_0 + srcScanlineStride; p01 = p_1 + srcPixelStride; p11 = p01 + srcPixelStride; p21 = p11 + srcPixelStride; p_2 = p_1 + srcScanlineStride; p02 = p_2 + srcPixelStride; p12 = p02 + srcPixelStride; p22 = p12 + srcPixelStride; dstPixelOffset += dstPixelStride; } dstOffset += dstScanlineStride; } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/CopyOpImage.java0000644000175000017500000002741410203035544026257 0ustar mathieumathieu/* * $RCSfile: CopyOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:20 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.ImageLayout; import javax.media.jai.JAI; import javax.media.jai.PointOpImage; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import java.util.Hashtable; import java.util.Map; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class that copies an image from source to dest. * */ public final class CopyOpImage extends PointOpImage { /** * Constructs an CopyOpImage. The image dimensions are copied * from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param layout an ImageLayout optionally containing the tile * grid layout, SampleModel, and ColorModel, or null. */ public CopyOpImage(RenderedImage source, Map config, ImageLayout layout) { super(source, layout, config, true); } /** * Adds the pixel values of a rectangle with a given constant. * The sources are cobbled. * * @param sources an array of sources, guarantee to provide all * necessary source data for computing the rectangle. * @param dest a tile that contains the rectangle to be computed. * @param destRect the rectangle within this OpImage to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); Raster source = sources[0]; Rectangle srcRect = mapDestRect(destRect, 0); RasterAccessor srcAccessor = new RasterAccessor(source,srcRect, formatTags[0], getSourceImage(0).getColorModel()); RasterAccessor dstAccessor = new RasterAccessor(dest,destRect, formatTags[1], getColorModel()); if(dstAccessor.isBinary()) { byte[] srcBits = srcAccessor.getBinaryDataArray(); byte[] dstBits = dstAccessor.getBinaryDataArray(); System.arraycopy(srcBits, 0, dstBits, 0, dstBits.length); dstAccessor.copyBinaryDataToRaster(); } else { switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: byteLoop(srcAccessor,dstAccessor); break; case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_USHORT: shortLoop(srcAccessor,dstAccessor); break; case DataBuffer.TYPE_INT: intLoop(srcAccessor,dstAccessor); break; case DataBuffer.TYPE_FLOAT: floatLoop(srcAccessor,dstAccessor); break; case DataBuffer.TYPE_DOUBLE: doubleLoop(srcAccessor,dstAccessor); break; default: String className = this.getClass().getName(); throw new RuntimeException(JaiI18N.getString("Convolve3x3OpImage1")); } // If the RasterAccessor object set up a temporary buffer for the // op to write to, tell the RasterAccessor to write that data // to the raster no that we're done with it. if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } } private void byteLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); byte dstDataArrays[][] = dst.getByteDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); byte srcDataArrays[][] = src.getByteDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); for (int k = 0; k < dnumBands; k++) { byte dstData[] = dstDataArrays[k]; byte srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { dstData[dstPixelOffset] = srcData[srcPixelOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } private void shortLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { dstData[dstPixelOffset] = srcData[srcPixelOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } // identical to byteLoops, except datatypes have changed. clumsy, // but there's no other way in Java private void intLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); int dstDataArrays[][] = dst.getIntDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcDataArrays[][] = src.getIntDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); for (int k = 0; k < dnumBands; k++) { int dstData[] = dstDataArrays[k]; int srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { dstData[dstPixelOffset] = srcData[srcPixelOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } private void floatLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); float dstDataArrays[][] = dst.getFloatDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); float srcDataArrays[][] = src.getFloatDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); for (int k = 0; k < dnumBands; k++) { float dstData[] = dstDataArrays[k]; float srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { dstData[dstPixelOffset] = srcData[srcPixelOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } private void doubleLoop(RasterAccessor src, RasterAccessor dst) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); double dstDataArrays[][] = dst.getDoubleDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); double srcDataArrays[][] = src.getDoubleDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); for (int k = 0; k < dnumBands; k++) { double dstData[] = dstDataArrays[k]; double srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { dstData[dstPixelOffset] = srcData[srcPixelOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } // public static OpImage createTestImage(OpImageTester oit) { // return new CopyOpImage(oit.getSource(), null, // new ImageLayout(oit.getSource())); // } // // Calls a method on OpImage that uses introspection, to make this // // class, discover it's createTestImage() call, call it and then // // benchmark the performance of the created OpImage chain. // public static void main(String args[]) { // String classname = "com.sun.media.jai.opimage.CopyOpImage"; // OpImageTester.performDiagnostics(classname,args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ShearRIF.java0000644000175000017500000001305510203035544025502 0ustar mathieumathieu/* * $RCSfile: ShearRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:43 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.image.DataBuffer; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.SampleModel; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.EnumeratedParameter; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationBicubic2; import javax.media.jai.InterpolationBicubic; import javax.media.jai.InterpolationBilinear; import javax.media.jai.InterpolationNearest; import javax.media.jai.InterpolationTable; import javax.media.jai.PlanarImage; import javax.media.jai.RenderedOp; import java.util.Map; import javax.media.jai.operator.ShearDescriptor; /** * @see AffineOpimage */ public class ShearRIF implements RenderedImageFactory { /** Constructor. */ public ShearRIF() {} /** * Creates an shear operation as an instance of AffineOpImage. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); // Get BorderExtender from renderHints if any. BorderExtender extender = RIFUtil.getBorderExtenderHint(renderHints); RenderedImage source = paramBlock.getRenderedSource(0); float shear_amt = paramBlock.getFloatParameter(0); EnumeratedParameter shear_dir = (EnumeratedParameter)paramBlock.getObjectParameter(1); float xTrans = paramBlock.getFloatParameter(2); float yTrans = paramBlock.getFloatParameter(3); Object arg1 = paramBlock.getObjectParameter(4); Interpolation interp = (Interpolation)arg1; double[] backgroundValues = (double[])paramBlock.getObjectParameter(5); // Create the affine transform AffineTransform tr = new AffineTransform(); if (shear_dir.equals(ShearDescriptor.SHEAR_HORIZONTAL)) { // SHEAR_HORIZONTAL tr.setTransform(1.0, 0.0, shear_amt, 1.0, xTrans, 0.0); } else { // SHEAR_VERTICAL tr.setTransform(1.0, shear_amt, 0.0, 1.0, 0.0, yTrans); } // Do Affine if (interp instanceof InterpolationNearest) { SampleModel sm = source.getSampleModel(); boolean isBinary = (sm instanceof MultiPixelPackedSampleModel) && (sm.getSampleSize(0) == 1) && (sm.getDataType() == DataBuffer.TYPE_BYTE || sm.getDataType() == DataBuffer.TYPE_USHORT || sm.getDataType() == DataBuffer.TYPE_INT); if(isBinary) { return new AffineNearestBinaryOpImage(source, extender, renderHints, layout, tr, interp, backgroundValues); } else { return new AffineNearestOpImage(source, extender, renderHints, layout, tr, interp, backgroundValues); } } else if (interp instanceof InterpolationBilinear) { return new AffineBilinearOpImage(source, extender, renderHints, layout, tr, interp, backgroundValues); } else if (interp instanceof InterpolationBicubic) { return new AffineBicubicOpImage(source, extender, renderHints, layout, tr, interp, backgroundValues); } else if (interp instanceof InterpolationBicubic2) { return new AffineBicubic2OpImage(source, extender, renderHints, layout, tr, interp, backgroundValues); } else { return new AffineGeneralOpImage(source, extender, renderHints, layout, tr, interp, backgroundValues); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/TransposeCRIF.java0000644000175000017500000000421210203035544026514 0ustar mathieumathieu/* * $RCSfile: TransposeCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:46 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.SampleModel; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderableImage; import java.awt.image.renderable.RenderContext; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.CRIFImpl; import javax.media.jai.EnumeratedParameter; import javax.media.jai.ImageLayout; import javax.media.jai.JAI; import javax.media.jai.PlanarImage; import javax.media.jai.RenderedOp; import java.util.Map; /** * @see TransposeOpImage * @see TransposeBinaryOpImage */ public class TransposeCRIF extends CRIFImpl { /** Constructor. */ public TransposeCRIF() { super("transpose"); } /** * Creates a Tranpose operation. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); RenderedImage source = paramBlock.getRenderedSource(0); EnumeratedParameter type = (EnumeratedParameter)paramBlock.getObjectParameter(0); SampleModel sm = source.getSampleModel(); if ((sm instanceof MultiPixelPackedSampleModel) && (sm.getSampleSize(0) == 1) && (sm.getDataType() == DataBuffer.TYPE_BYTE || sm.getDataType() == DataBuffer.TYPE_USHORT || sm.getDataType() == DataBuffer.TYPE_INT)) { return new TransposeBinaryOpImage(source, renderHints, layout, type.getValue()); } else { return new TransposeOpImage(source, renderHints, layout, type.getValue()); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MaxFilterSquareOpImage.java0000644000175000017500000003540510203035544030420 0ustar mathieumathieu/* * $RCSfile: MaxFilterSquareOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:32 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import java.util.Map; import javax.media.jai.operator.MaxFilterDescriptor; import com.sun.media.jai.opimage.MaxFilterOpImage; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class to perform max filtering on a source image. * */ final class MaxFilterSquareOpImage extends MaxFilterOpImage { /** * Creates a MaxFilterSquareOpImage with the given source and * maskSize. The image dimensions are derived from the source * image. The tile grid layout, SampleModel, and ColorModel may * optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param maskSize the mask size. */ public MaxFilterSquareOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, int maskSize) { super(source, extender, config, layout, MaxFilterDescriptor.MAX_MASK_SQUARE, maskSize); } protected void byteLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); byte dstDataArrays[][] = dst.getByteDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); byte srcDataArrays[][] = src.getByteDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int maxval, val; int wp = filterSize; for (int k = 0; k < dnumBands; k++) { byte dstData[] = dstDataArrays[k]; byte srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageVerticalOffset = srcPixelOffset; maxval = Integer.MIN_VALUE; for (int u = 0; u < wp; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < wp; v++) { val = (int)(srcData[imageOffset])&0xff; imageOffset += srcPixelStride; maxval = (val > maxval) ? val : maxval; } imageVerticalOffset += srcScanlineStride; } dstData[dstPixelOffset] = (byte)maxval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void shortLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int maxval, val; int wp = filterSize; for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageVerticalOffset = srcPixelOffset; maxval = Integer.MIN_VALUE; for (int u = 0; u < wp; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < wp; v++) { val = (int)(srcData[imageOffset]); imageOffset += srcPixelStride; maxval = (val > maxval) ? val : maxval; } imageVerticalOffset += srcScanlineStride; } dstData[dstPixelOffset] = (short)maxval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void ushortLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int maxval, val; int wp = filterSize; for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageVerticalOffset = srcPixelOffset; maxval = Integer.MIN_VALUE; for (int u = 0; u < wp; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < wp; v++) { val = (int)(srcData[imageOffset])&0xffff; imageOffset += srcPixelStride; maxval = (val > maxval) ? val : maxval; } imageVerticalOffset += srcScanlineStride; } dstData[dstPixelOffset] = (short)maxval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void intLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); int dstDataArrays[][] = dst.getIntDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcDataArrays[][] = src.getIntDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int maxval, val; int wp = filterSize; for (int k = 0; k < dnumBands; k++) { int dstData[] = dstDataArrays[k]; int srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageVerticalOffset = srcPixelOffset; maxval = Integer.MIN_VALUE; for (int u = 0; u < wp; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += srcPixelStride; maxval = (val > maxval) ? val : maxval; } imageVerticalOffset += srcScanlineStride; } dstData[dstPixelOffset] = maxval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void floatLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); float dstDataArrays[][] = dst.getFloatDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); float srcDataArrays[][] = src.getFloatDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); float maxval, val; int wp = filterSize; for (int k = 0; k < dnumBands; k++) { float dstData[] = dstDataArrays[k]; float srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageVerticalOffset = srcPixelOffset; maxval = -Float.MAX_VALUE; for (int u = 0; u < wp; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += srcPixelStride; maxval = (val > maxval) ? val : maxval; } imageVerticalOffset += srcScanlineStride; } dstData[dstPixelOffset] = maxval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void doubleLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); double dstDataArrays[][] = dst.getDoubleDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); double srcDataArrays[][] = src.getDoubleDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); double maxval, val; int wp = filterSize; for (int k = 0; k < dnumBands; k++) { double dstData[] = dstDataArrays[k]; double srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int imageVerticalOffset = srcPixelOffset; maxval = -Double.MAX_VALUE; for (int u = 0; u < wp; u++) { int imageOffset = imageVerticalOffset; for (int v = 0; v < wp; v++) { val = srcData[imageOffset]; imageOffset += srcPixelStride; maxval = (val > maxval) ? val : maxval; } imageVerticalOffset += srcScanlineStride; } dstData[dstPixelOffset] = maxval; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } // public static OpImage createTestImage(OpImageTester oit) { // return new MaxFilterSquareOpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource()), // 3); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/OrderedDitherOpImage.java0000644000175000017500000012766210203035544030077 0ustar mathieumathieu/* * $RCSfile: OrderedDitherOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:38 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.IndexColorModel; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.lang.ref.SoftReference; import java.util.Arrays; import java.util.Map; import java.util.Vector; import javax.media.jai.ColorCube; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; import javax.media.jai.OpImage; import javax.media.jai.PointOpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.RasterFactory; import com.sun.media.jai.util.JDKWorkarounds; import com.sun.media.jai.util.ImageUtil; /** * An OpImage implementing the ordered dither operation as * described in javax.media.jai.operator.OrderedDitherDescriptor. * *

    This OpImage performs dithering of its source image into * a single band image using a specified color cube and dither mask. * * @see javax.media.jai.KernelJAI * @see javax.media.jai.ColorCube * * @since EA3 * */ final class OrderedDitherOpImage extends PointOpImage { /** * Flag indicating that the generic implementation is used. */ private static final int TYPE_OD_GENERAL = 0; /** * Flag indicating that the optimized three-band implementation is used * (byte data only). */ private static final int TYPE_OD_BYTE_LUT_3BAND = 1; /** * Flag indicating that the optimized N-band implementation is used * (byte data only). */ private static final int TYPE_OD_BYTE_LUT_NBAND = 2; /** * Maximim dither LUT size: 16x16 4-band byte dither mask. */ private static final int DITHER_LUT_LENGTH_MAX = 16*16*4*256; /** * The maximum number of elements in the DitherLUT cache. */ private static final int DITHER_LUT_CACHE_LENGTH_MAX = 4; /** * A cache of SoftReferences to DitherLUT * inner class instances. */ private static Vector ditherLUTCache = new Vector(0, DITHER_LUT_CACHE_LENGTH_MAX); /** * Flag indicating the implementation to be used. */ private int odType = TYPE_OD_GENERAL; /** * The number of bands in the source image. */ protected int numBands; /** * The array of color cube dimensions-less-one. */ protected int[] dims; /** * The array of color cube multipliers. */ protected int[] mults; /** * The adjusted offset of the color cube. */ protected int adjustedOffset; /** * The width of the dither mask. */ protected int maskWidth; /** * The height of the dither mask. */ protected int maskHeight; /** * The dither mask matrix scaled by 255. */ protected byte[][] maskDataByte; /** * The dither mask matrix scaled to USHORT range. */ protected int[][] maskDataInt; /** * The dither mask matrix scaled to "unsigned int" range. */ protected long[][] maskDataLong; /** * The dither mask matrix. */ protected float[][] maskDataFloat; /** * An inner class instance representing a dither lookup table. Used * for byte data only when the table size is within a specified limit. */ protected DitherLUT odLUT = null; /** * Force the destination image to be single-banded. */ private static ImageLayout layoutHelper(ImageLayout layout, RenderedImage source, ColorCube colorMap) { ImageLayout il; if (layout == null) { il = new ImageLayout(source); } else { il = (ImageLayout)layout.clone(); } // Get the SampleModel. SampleModel sm = il.getSampleModel(source); // Ensure an appropriate SampleModel. if(colorMap.getNumBands() == 1 && colorMap.getNumEntries() == 2 && !ImageUtil.isBinary(il.getSampleModel(source))) { sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, il.getTileWidth(source), il.getTileHeight(source), 1); il.setSampleModel(sm); } // Make sure that this OpImage is single-banded. if (sm.getNumBands() != 1) { // TODO: Force to SHORT or USHORT if FLOAT or DOUBLE? sm = RasterFactory.createComponentSampleModel(sm, sm.getTransferType(), sm.getWidth(), sm.getHeight(), 1); il.setSampleModel(sm); // Clear the ColorModel mask if needed. ColorModel cm = il.getColorModel(null); if(cm != null && !JDKWorkarounds.areCompatibleDataModels(sm, cm)) { // Clear the mask bit if incompatible. il.unsetValid(ImageLayout.COLOR_MODEL_MASK); } } // Set an IndexColorModel on the image if: // a. none is provided in the layout; // b. source, destination, and colormap have byte data type; // c. the colormap has 3 bands; and // d. the source ColorModel is either null or is non-null // and has a ColorSpace equal to CS_sRGB. if((layout == null || !il.isValid(ImageLayout.COLOR_MODEL_MASK)) && source.getSampleModel().getDataType() == DataBuffer.TYPE_BYTE && il.getSampleModel(null).getDataType() == DataBuffer.TYPE_BYTE && colorMap.getDataType() == DataBuffer.TYPE_BYTE && colorMap.getNumBands() == 3) { ColorModel cm = source.getColorModel(); if(cm == null || (cm != null && cm.getColorSpace().isCS_sRGB())) { int size = colorMap.getNumEntries(); byte[][] cmap = new byte[3][256]; for(int i = 0; i < 3; i++) { byte[] band = cmap[i]; byte[] data = colorMap.getByteData(i); int offset = colorMap.getOffset(i); int end = offset + size; for(int j = 0; j < offset; j++) { band[j] = (byte)0; } for(int j = offset; j < end; j++) { band[j] = data[j - offset]; } for(int j = end; j < 256; j++) { band[j] = (byte)0xFF; } } il.setColorModel(new IndexColorModel(8, 256, cmap[0], cmap[1], cmap[2])); } } return il; } /** * Constructs an OrderedDitherOpImage object. May be used to convert a * single- or multi-band image into a single-band image with a color map. * *

    The image dimensions are derived from the source image. The tile * grid layout, SampleModel, and ColorModel may optionally be specified * by an ImageLayout object. * * @param source A RenderedImage. * @param layout An ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param colorMap The color map to use which must have a number of bands * equal to the number of bands in the source image. The offset of this * ColorCube must be the same for all bands. * @param ditherMask An an array of KernelJAI objects the * dimension of which must equal the number of bands in the source image. * The nth element of the array contains a KernelJAI * object which represents the dither mask matrix for the corresponding * band. All KernelJAI objects in the array must have the * same dimensions and contain floating point values between 0.0F and 1.0F. */ public OrderedDitherOpImage(RenderedImage source, Map config, ImageLayout layout, ColorCube colorMap, KernelJAI[] ditherMask) { // Construct as a PointOpImage. super(source, layoutHelper(layout, source, colorMap), config, true); // Initialize the instance variables derived from the color map. numBands = colorMap.getNumBands(); mults = (int[])colorMap.getMultipliers().clone(); dims = (int[])colorMap.getDimsLessOne().clone(); adjustedOffset = colorMap.getAdjustedOffset(); // Initialize the instance variables derived from the dither mask. maskWidth = ditherMask[0].getWidth(); maskHeight = ditherMask[0].getHeight(); // Initialize the data required to effect the operation. // XXX Postpone until first invocation of computeRect()? initializeDitherData(sampleModel.getTransferType(), ditherMask); // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * An inner class represting a lookup table to be used in the optimized * implementations of ordered dithering of byte data. */ private class DitherLUT { // Clones of color cube and dither mask data used to create the // dithering lookup table. private int[] dimsCache; private int[] multsCache; private byte[][] maskDataCache; // Stride values of the dither lookup table. public int ditherLUTBandStride; public int ditherLUTRowStride; public int ditherLUTColStride; // The dither lookup table. public byte[] ditherLUT; /** * Create an inner class object representing an ordered dither * lookup table for byte data. * * @param dims The color cube dimensions less one. * @param mults The color cube multipliers. * @param maskData The dither mask data scaled to byte range. */ DitherLUT(int[] dims, int[] mults, byte[][] maskData) { // Clone the constructor parameters. dimsCache = (int[])dims.clone(); multsCache = (int[])mults.clone(); maskDataCache = new byte[maskData.length][]; for(int i = 0; i < maskData.length; i++) { maskDataCache[i] = (byte[])maskData[i].clone(); } // Set dither lookup table stride values. ditherLUTColStride = 256; ditherLUTRowStride = maskWidth*ditherLUTColStride; ditherLUTBandStride = maskHeight*ditherLUTRowStride; // // Construct the big dither table. If indexed as a // multi-dimensional array this would be equivalent to: // // ditherLUT[band][ditherRow][ditherColumn][grayLevel] // // where ditherRow, Col are modulo the dither mask size. // // To minimize the table construction cost, precalculate // the bin value for a given band and gray level. Then use // the dithermask threshold value to determine whether to bump // the value up one level. Thus most of the work is done in the // outer loops, with a simple comparison left for the inner loop. // ditherLUT = new byte[numBands*ditherLUTBandStride]; int pDithBand = 0; int maskSize2D = maskWidth*maskHeight; for(int band = 0; band < numBands; band++) { int step = dims[band]; int delta = mults[band]; byte[] maskDataBand = maskData[band]; int sum = 0; for(int gray = 0; gray < 256; gray++) { int tmp = sum; int frac = (int)(tmp & 0xff); int bin = tmp >> 8; int lowVal = bin * delta; int highVal = lowVal + delta; int pDith = pDithBand + gray; for(int dcount = 0; dcount < maskSize2D; dcount++) { int threshold = maskDataBand[dcount] & 0xff; if(frac > threshold) { ditherLUT[pDith] = (byte)(highVal & 0xff); } else { ditherLUT[pDith] = (byte)(lowVal & 0xff); } pDith += 256; } // end dithermask entry sum += step; } // end gray level pDithBand += ditherLUTBandStride; } // end band } /** * Determine whether the internal table of this DitherLUT * is the same as that which would be generated using the supplied * parameters. * * @param dims The color cube dimensions less one. * @param mults The color cube multipliers. * @param maskData The dither mask data scaled to byte range. * * @return Value indicating equivalence of dither LUTs. */ public boolean equals(int[] dims, int[] mults, byte[][] maskData) { // Check dimensions. if(dims.length != dimsCache.length) { return false; } for(int i = 0; i < dims.length; i++) { if(dims[i] != dimsCache[i]) return false; } // Check multipliers. if(mults.length != multsCache.length) { return false; } for(int i = 0; i < mults.length; i++) { if(mults[i] != multsCache[i]) return false; } // Check dither mask. if(maskData.length != maskDataByte.length) { return false; } for(int i = 0; i < maskData.length; i++) { if(maskData[i].length != maskDataCache[i].length) return false; byte[] refData = maskDataCache[i]; byte[] data = maskData[i]; for(int j = 0; j < maskData[i].length; j++) { if(data[j] != refData[j]) return false; } } return true; } } // End inner class DitherLUT. /** * Initialize data type-dependent fields including the dither mask data * arrays and, for optimized byte cases, the dither lookup table object. * * @param dataType The data type as defined in DataBuffer. * @param ditherMask The dither mask represented as an array of * KernelJAI objects. */ private void initializeDitherData(int dataType, KernelJAI[] ditherMask) { switch(dataType) { case DataBuffer.TYPE_BYTE: { maskDataByte = new byte[ditherMask.length][]; for(int i = 0; i < maskDataByte.length; i++) { float[] maskData = ditherMask[i].getKernelData(); maskDataByte[i] = new byte[maskData.length]; for(int j = 0; j < maskData.length; j++) { maskDataByte[i][j] = (byte)((int)(maskData[j]*255.0F)&0xff); } } initializeDitherLUT(); } break; case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_USHORT: { int scaleFactor = (int)Short.MAX_VALUE - (int)Short.MIN_VALUE; maskDataInt = new int[ditherMask.length][]; for(int i = 0; i < maskDataInt.length; i++) { float[] maskData = ditherMask[i].getKernelData(); maskDataInt[i] = new int[maskData.length]; for(int j = 0; j < maskData.length; j++) { maskDataInt[i][j] = (int)(maskData[j]*scaleFactor); } } } break; case DataBuffer.TYPE_INT: { long scaleFactor = (long)Integer.MAX_VALUE - (long)Integer.MIN_VALUE; maskDataLong = new long[ditherMask.length][]; for(int i = 0; i < maskDataLong.length; i++) { float[] maskData = ditherMask[i].getKernelData(); maskDataLong[i] = new long[maskData.length]; for(int j = 0; j < maskData.length; j++) { maskDataLong[i][j] = (long)(maskData[j]*scaleFactor); } } } break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: { maskDataFloat = new float[ditherMask.length][]; for(int i = 0; i < maskDataFloat.length; i++) { maskDataFloat[i] = ditherMask[i].getKernelData(); } } break; default: throw new RuntimeException(JaiI18N.getString("OrderedDitherOpImage0")); } } /** * For byte data only, initialize the dither lookup table if it is small * enough and set the type of ordered dither implementation to use. */ private synchronized void initializeDitherLUT() { // Check whether a DitherLUT may be used. if(numBands*maskHeight*maskWidth*256 > DITHER_LUT_LENGTH_MAX) { odType = TYPE_OD_GENERAL; // NB: This is superfluous. return; } // If execution has proceeded to this point then this is one of the // optimized cases so set the type flag accordingly. odType = numBands == 3 ? TYPE_OD_BYTE_LUT_3BAND : TYPE_OD_BYTE_LUT_NBAND; // Check whether an equivalent DitherLUT object already exists. int index = 0; while(index < ditherLUTCache.size()) { SoftReference lutRef = (SoftReference)ditherLUTCache.get(index); DitherLUT lut = (DitherLUT)lutRef.get(); if(lut == null) { // The reference has been cleared: remove the Vector element // but do not increment the loop index. ditherLUTCache.remove(index); } else { if(lut.equals(dims, mults, maskDataByte)) { // Found an equivalent DitherLUT so use it and exit loop. odLUT = lut; break; } // Move on to the next Vector element. index++; } } // Create a new DitherLUT if an equivalent one was not found. if(odLUT == null) { odLUT = new DitherLUT(dims, mults, maskDataByte); // Cache a reference to the DitherLUT if there is room. if(ditherLUTCache.size() < DITHER_LUT_CACHE_LENGTH_MAX) { ditherLUTCache.add(new SoftReference(odLUT)); } } } /** * Computes a tile of the dithered destination image. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Set format tags RasterFormatTag[] formatTags = null; if(ImageUtil.isBinary(getSampleModel()) && !ImageUtil.isBinary(getSourceImage(0).getSampleModel())) { // XXX Workaround for bug 4521097. This branch of the if-block // should be deleted once bug 4668327 is fixed. RenderedImage[] sourceArray = new RenderedImage[] {getSourceImage(0)}; RasterFormatTag[] sourceTags = RasterAccessor.findCompatibleTags(sourceArray, sourceArray[0]); RasterFormatTag[] destTags = RasterAccessor.findCompatibleTags(sourceArray, this); formatTags = new RasterFormatTag[] {sourceTags[0], destTags[1]}; } else { // Retrieve format tags. formatTags = getFormatTags(); } RasterAccessor src = new RasterAccessor(sources[0], destRect, formatTags[0], getSource(0).getColorModel()); RasterAccessor dst = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); switch (src.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(src, dst); break; case DataBuffer.TYPE_SHORT: computeRectShort(src, dst); break; case DataBuffer.TYPE_USHORT: computeRectUShort(src, dst); break; case DataBuffer.TYPE_INT: computeRectInt(src, dst); break; case DataBuffer.TYPE_FLOAT: computeRectFloat(src, dst); break; case DataBuffer.TYPE_DOUBLE: computeRectDouble(src, dst); break; default: throw new RuntimeException(JaiI18N.getString("OrderedDitherOpImage1")); } dst.copyDataToRaster(); } /** * Computes a Rectangle of data for byte imagery. */ private void computeRectByte(RasterAccessor src, RasterAccessor dst) { int sbands = src.getNumBands(); int sLineStride = src.getScanlineStride(); int sPixelStride = src.getPixelStride(); int[] sBandOffsets = src.getBandOffsets(); byte[][] sData = src.getByteDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int dBandOffset = dst.getBandOffset(0); byte[] dData = dst.getByteDataArray(0); int xMod = dst.getX() % maskWidth; int y0 = dst.getY(); switch(odType) { case TYPE_OD_BYTE_LUT_3BAND: case TYPE_OD_BYTE_LUT_NBAND: int[] srcLineOffsets = (int[])sBandOffsets.clone(); int[] srcPixelOffsets = (int[])srcLineOffsets.clone(); int dLineOffset = dBandOffset; for(int h = 0; h < dheight; h++) { int yMod = (y0 + h)%maskHeight; if(odType == TYPE_OD_BYTE_LUT_3BAND) { computeLineByteLUT3(sData, srcPixelOffsets, sPixelStride, dData, dLineOffset, dPixelStride, dwidth, xMod, yMod); } else { computeLineByteLUTN(sData, srcPixelOffsets, sPixelStride, dData, dLineOffset, dPixelStride, dwidth, xMod, yMod); } for(int i = 0; i < sbands; i++) { srcLineOffsets[i] += sLineStride; srcPixelOffsets[i] = srcLineOffsets[i]; } dLineOffset += dLineStride; } break; case TYPE_OD_GENERAL: default: computeRectByteGeneral(sData, sBandOffsets, sLineStride, sPixelStride, dData, dBandOffset, dLineStride, dPixelStride, dwidth, dheight, xMod, y0); } } /** * Dithers a line of 3-band byte data using a DitherLUT. */ private void computeLineByteLUT3(byte[][] sData, int[] sPixelOffsets, int sPixelStride, byte[] dData, int dPixelOffset, int dPixelStride, int dwidth, int xMod, int yMod) { int ditherLUTBandStride = odLUT.ditherLUTBandStride; int ditherLUTRowStride = odLUT.ditherLUTRowStride; int ditherLUTColStride = odLUT.ditherLUTColStride; byte[] ditherLUT = odLUT.ditherLUT; int base = adjustedOffset; int dlut0 = yMod*ditherLUTRowStride; int dlut1 = dlut0 + ditherLUTBandStride; int dlut2 = dlut1 + ditherLUTBandStride; int dlutLimit = dlut0 + ditherLUTRowStride; int xDelta = xMod*ditherLUTColStride; int pDtab0 = dlut0 + xDelta; int pDtab1 = dlut1 + xDelta; int pDtab2 = dlut2 + xDelta; byte[] sData0 = sData[0]; byte[] sData1 = sData[1]; byte[] sData2 = sData[2]; for(int count = dwidth; count > 0; count--) { int idx = (ditherLUT[pDtab0 + (sData0[sPixelOffsets[0]]&0xff)]&0xff) + (ditherLUT[pDtab1 + (sData1[sPixelOffsets[1]]&0xff)]&0xff) + (ditherLUT[pDtab2 + (sData2[sPixelOffsets[2]]&0xff)]&0xff); dData[dPixelOffset] = (byte)((idx + base)&0xff); sPixelOffsets[0] += sPixelStride; sPixelOffsets[1] += sPixelStride; sPixelOffsets[2] += sPixelStride; dPixelOffset += dPixelStride; pDtab0 += ditherLUTColStride; if(pDtab0 >= dlutLimit) { pDtab0 = dlut0; pDtab1 = dlut1; pDtab2 = dlut2; } else { pDtab1 += ditherLUTColStride; pDtab2 += ditherLUTColStride; } } } /** * Dithers a line of N-band byte data using a DitherLUT. */ private void computeLineByteLUTN(byte[][] sData, int[] sPixelOffsets, int sPixelStride, byte[] dData, int dPixelOffset, int dPixelStride, int dwidth, int xMod, int yMod) { int ditherLUTBandStride = odLUT.ditherLUTBandStride; int ditherLUTRowStride = odLUT.ditherLUTRowStride; int ditherLUTColStride = odLUT.ditherLUTColStride; byte[] ditherLUT = odLUT.ditherLUT; int base = adjustedOffset; int dlutRow = yMod*ditherLUTRowStride; int dlutCol = dlutRow + xMod*ditherLUTColStride; int dlutLimit = dlutRow + ditherLUTRowStride; for(int count = dwidth; count > 0; count--) { int dlutBand = dlutCol; int idx = base; for(int i = 0; i < numBands; i++) { idx += (ditherLUT[dlutBand + (sData[i][sPixelOffsets[i]]&0xff)]&0xff); dlutBand += ditherLUTBandStride; sPixelOffsets[i] += sPixelStride; } dData[dPixelOffset] = (byte)(idx & 0xff); dPixelOffset += dPixelStride; dlutCol += ditherLUTColStride; if(dlutCol >= dlutLimit) { dlutCol = dlutRow; } } } /** * Computes a Rectangle of data for byte imagery using the * general, unoptimized algorithm. */ private void computeRectByteGeneral(byte[][] sData, int[] sBandOffsets, int sLineStride, int sPixelStride, byte[] dData, int dBandOffset, int dLineStride, int dPixelStride, int dwidth, int dheight, int xMod, int y0) { if(adjustedOffset > 0) { Arrays.fill(dData, (byte)(adjustedOffset & 0xff)); } int sbands = sBandOffsets.length; for (int b = 0; b < sbands; b++) { byte[] s = sData[b]; byte[] d = dData; byte[] maskData = maskDataByte[b]; int sLineOffset = sBandOffsets[b]; int dLineOffset = dBandOffset; for (int h = 0; h < dheight; h++) { int yMod = (y0 + h)%maskHeight; // Determine the index of the first dither mask point in // this line for the current band. int maskYBase = yMod*maskWidth; // Determine the value one greater than the maximum valid // dither mask index for this band. int maskLimit = maskYBase + maskWidth; // Initialize the dither mask index which is a value // guaranteed to be in range. int maskIndex = maskYBase + xMod; int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; for (int w = 0; w < dwidth; w++) { int tmp = (s[sPixelOffset] & 0xff)*dims[b]; int frac = (int)(tmp & 0xff); tmp >>= 8; if(frac > (int)(maskData[maskIndex]&0xff)) { tmp++; } // Accumulate the value into the destination data array. int result = (d[dPixelOffset] & 0xff) + tmp*mults[b]; d[dPixelOffset] = (byte)(result & 0xff); sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; if(++maskIndex >= maskLimit) { maskIndex = maskYBase; } } sLineOffset += sLineStride; dLineOffset += dLineStride; } } if(adjustedOffset < 0) { // Shift the result by the adjusted offset of the color map. int length = dData.length; for(int i = 0; i < length; i++) { dData[i] = (byte)((dData[i] & 0xff) + adjustedOffset); } } } /** * Computes a Rectangle of data for signed short imagery. */ private void computeRectShort(RasterAccessor src, RasterAccessor dst) { int sbands = src.getNumBands(); int sLineStride = src.getScanlineStride(); int sPixelStride = src.getPixelStride(); int[] sBandOffsets = src.getBandOffsets(); short[][] sData = src.getShortDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int dBandOffset = dst.getBandOffset(0); short[] dData = dst.getShortDataArray(0); // Initialize the destination data to the color cube adjusted offset // to permit accumulation of the result for each band. if(adjustedOffset != 0) { Arrays.fill(dData, (short)(adjustedOffset & 0xffff)); } int xMod = dst.getX() % maskWidth; int y0 = dst.getY(); for (int b = 0; b < sbands; b++) { short[] s = sData[b]; short[] d = dData; int[] maskData = maskDataInt[b]; int sLineOffset = sBandOffsets[b]; int dLineOffset = dBandOffset; for (int h = 0; h < dheight; h++) { int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; sLineOffset += sLineStride; dLineOffset += dLineStride; // Determine the index of the first dither mask point in // this line for the current band. int maskYBase = ((y0 + h) % maskHeight)*maskWidth; // Determine the value one greater than the maximum valid // dither mask index for this band. int maskLimit = maskYBase + maskWidth; // Initialize the dither mask index which is a value // guaranteed to be in range. int maskIndex = maskYBase + xMod; for (int w = 0; w < dwidth; w++) { int tmp = (s[sPixelOffset] - Short.MIN_VALUE)*dims[b]; int frac = (int)(tmp & 0xffff); // Accumulate the value into the destination data array. int result = (int)(d[dPixelOffset]&0xffff) + (tmp >> 16)*mults[b]; if(frac > maskData[maskIndex]) { result += mults[b]; } d[dPixelOffset] = (short)(result & 0xffff); sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; if(++maskIndex >= maskLimit) { maskIndex = maskYBase; } } } } } /** * Computes a Rectangle of data for unsigned short imagery. */ private void computeRectUShort(RasterAccessor src, RasterAccessor dst) { int sbands = src.getNumBands(); int sLineStride = src.getScanlineStride(); int sPixelStride = src.getPixelStride(); int[] sBandOffsets = src.getBandOffsets(); short[][] sData = src.getShortDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int dBandOffset = dst.getBandOffset(0); short[] dData = dst.getShortDataArray(0); // Initialize the destination data to the color cube adjusted offset // to permit accumulation of the result for each band. if(adjustedOffset != 0) { Arrays.fill(dData, (short)(adjustedOffset & 0xffff)); } int xMod = dst.getX() % maskWidth; int y0 = dst.getY(); for (int b = 0; b < sbands; b++) { short[] s = sData[b]; short[] d = dData; int[] maskData = maskDataInt[b]; int sLineOffset = sBandOffsets[b]; int dLineOffset = dBandOffset; for (int h = 0; h < dheight; h++) { int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; sLineOffset += sLineStride; dLineOffset += dLineStride; // Determine the index of the first dither mask point in // this line for the current band. int maskYBase = ((y0 + h) % maskHeight)*maskWidth; // Determine the value one greater than the maximum valid // dither mask index for this band. int maskLimit = maskYBase + maskWidth; // Initialize the dither mask index which is a value // guaranteed to be in range. int maskIndex = maskYBase + xMod; for (int w = 0; w < dwidth; w++) { int tmp = (s[sPixelOffset] & 0xffff)*dims[b]; int frac = (int)(tmp & 0xffff); // Accumulate the value into the destination data array. int result = (int)(d[dPixelOffset] & 0xffff) + (tmp >> 16)*mults[b]; if(frac > maskData[maskIndex]) { result += mults[b]; } d[dPixelOffset] = (short)(result & 0xffff); sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; if(++maskIndex >= maskLimit) { maskIndex = maskYBase; } } } } } /** * Computes a Rectangle of data for integer imagery. */ private void computeRectInt(RasterAccessor src, RasterAccessor dst) { int sbands = src.getNumBands(); int sLineStride = src.getScanlineStride(); int sPixelStride = src.getPixelStride(); int[] sBandOffsets = src.getBandOffsets(); int[][] sData = src.getIntDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int dBandOffset = dst.getBandOffset(0); int[] dData = dst.getIntDataArray(0); // Initialize the destination data to the color cube adjusted offset // to permit accumulation of the result for each band. if(adjustedOffset != 0) { Arrays.fill(dData, adjustedOffset); } int xMod = dst.getX() % maskWidth; int y0 = dst.getY(); for (int b = 0; b < sbands; b++) { int[] s = sData[b]; int[] d = dData; long[] maskData = maskDataLong[b]; int sLineOffset = sBandOffsets[b]; int dLineOffset = dBandOffset; for (int h = 0; h < dheight; h++) { int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; sLineOffset += sLineStride; dLineOffset += dLineStride; // Determine the index of the first dither mask point in // this line for the current band. int maskYBase = ((y0 + h) % maskHeight)*maskWidth; // Determine the value one greater than the maximum valid // dither mask index for this band. int maskLimit = maskYBase + maskWidth; // Initialize the dither mask index which is a value // guaranteed to be in range. int maskIndex = maskYBase + xMod; for (int w = 0; w < dwidth; w++) { long tmp = ((long)s[sPixelOffset] - (long)Integer.MIN_VALUE)* dims[b]; long frac = (long)(tmp & 0xffffffff); // Accumulate the value into the destination data array. int result = d[dPixelOffset] + ((int)(tmp >> 32))*mults[b]; if(frac > maskData[maskIndex]) { result += mults[b]; } d[dPixelOffset] = result; sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; if(++maskIndex >= maskLimit) { maskIndex = maskYBase; } } } } } /** * Computes a Rectangle of data for float imagery. */ private void computeRectFloat(RasterAccessor src, RasterAccessor dst) { int sbands = src.getNumBands(); int sLineStride = src.getScanlineStride(); int sPixelStride = src.getPixelStride(); int[] sBandOffsets = src.getBandOffsets(); float[][] sData = src.getFloatDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int dBandOffset = dst.getBandOffset(0); float[] dData = dst.getFloatDataArray(0); // Initialize the destination data to the color cube adjusted offset // to permit accumulation of the result for each band. if(adjustedOffset != 0) { Arrays.fill(dData, (float)adjustedOffset); } int xMod = dst.getX() % maskWidth; int y0 = dst.getY(); for (int b = 0; b < sbands; b++) { float[] s = sData[b]; float[] d = dData; float[] maskData = maskDataFloat[b]; int sLineOffset = sBandOffsets[b]; int dLineOffset = dBandOffset; for (int h = 0; h < dheight; h++) { int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; sLineOffset += sLineStride; dLineOffset += dLineStride; // Determine the index of the first dither mask point in // this line for the current band. int maskYBase = ((y0 + h) % maskHeight)*maskWidth; // Determine the value one greater than the maximum valid // dither mask index for this band. int maskLimit = maskYBase + maskWidth; // Initialize the dither mask index which is a value // guaranteed to be in range. int maskIndex = maskYBase + xMod; for (int w = 0; w < dwidth; w++) { int tmp = (int)(s[sPixelOffset]*dims[b]); float frac = s[sPixelOffset]*dims[b] - tmp; // Accumulate the value into the destination data array. float result = d[dPixelOffset] + tmp*mults[b]; if(frac > maskData[maskIndex]) { result += mults[b]; } d[dPixelOffset] = result; sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; if(++maskIndex >= maskLimit) { maskIndex = maskYBase; } } } } } /** * Computes a Rectangle of data for double imagery. */ private void computeRectDouble(RasterAccessor src, RasterAccessor dst) { int sbands = src.getNumBands(); int sLineStride = src.getScanlineStride(); int sPixelStride = src.getPixelStride(); int[] sBandOffsets = src.getBandOffsets(); double[][] sData = src.getDoubleDataArrays(); int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dLineStride = dst.getScanlineStride(); int dPixelStride = dst.getPixelStride(); int dBandOffset = dst.getBandOffset(0); double[] dData = dst.getDoubleDataArray(0); // Initialize the destination data to the color cube adjusted offset // to permit accumulation of the result for each band. if(adjustedOffset != 0) { Arrays.fill(dData, (double)adjustedOffset); } int xMod = dst.getX() % maskWidth; int y0 = dst.getY(); for (int b = 0; b < sbands; b++) { double[] s = sData[b]; double[] d = dData; float[] maskData = maskDataFloat[b]; int sLineOffset = sBandOffsets[b]; int dLineOffset = dBandOffset; for (int h = 0; h < dheight; h++) { int sPixelOffset = sLineOffset; int dPixelOffset = dLineOffset; sLineOffset += sLineStride; dLineOffset += dLineStride; // Determine the index of the first dither mask point in // this line for the current band. int maskYBase = ((y0 + h) % maskHeight)*maskWidth; // Determine the value one greater than the maximum valid // dither mask index for this band. int maskLimit = maskYBase + maskWidth; // Initialize the dither mask index which is a value // guaranteed to be in range. int maskIndex = maskYBase + xMod; for (int w = 0; w < dwidth; w++) { int tmp = (int)(s[sPixelOffset]*dims[b]); float frac = (float)(s[sPixelOffset]*dims[b] - tmp); // Accumulate the value into the destination data array. double result = d[dPixelOffset] + tmp*mults[b]; if(frac > maskData[maskIndex]) { result += mults[b]; } d[dPixelOffset] = result; sPixelOffset += sPixelStride; dPixelOffset += dPixelStride; if(++maskIndex >= maskLimit) { maskIndex = maskYBase; } } } } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/IIPResolutionRIF.java0000644000175000017500000000261010203035544027140 0ustar mathieumathieu/* * $RCSfile: IIPResolutionRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:29 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import java.util.Map; /** * A RIF supporting the "IIPResolution" operation in the * rendered image layer. * * @see javax.media.jai.operator.IIPResolutionDescriptor * @see IIPResolutionOpImage * * @since 1.0 * */ public class IIPResolutionRIF implements RenderedImageFactory { /** Constructor. */ public IIPResolutionRIF() {} /** * Creates a new instance of IIPResolutionOpImage * in the rendered layer. Any image layout information in * the RenderingHints is ignored. * This method satisfies the implementation of RIF. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { return new IIPResolutionOpImage(hints, (String)args.getObjectParameter(0), args.getIntParameter(1), args.getIntParameter(2)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/OverlayOpImage.java0000644000175000017500000003443710203035544026771 0ustar mathieumathieu/* * $RCSfile: OverlayOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:39 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.util.JDKWorkarounds; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.util.Map; import java.util.Vector; import javax.media.jai.ImageLayout; import javax.media.jai.PlanarImage; import javax.media.jai.PointOpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFactory; import javax.media.jai.RasterFormatTag; /** * An OpImage implementing the "Overlay" operation. * *

    This OpImage overlays one rendered image (source2) * on top of another (source1). The two sources are required to have * the same data type and number of bands. * * @see javax.media.jai.operator.OverlayDescriptor * @see OverlayCRIF * */ final class OverlayOpImage extends PointOpImage { /** * OverlayOpImage always has the bounds of the sourceUnder image. * The image bounds specified in the "layout" are always ignored. * Hence unset the image bounds so that PointImage.layoutHelper checks * dont fail. */ private static ImageLayout layoutHelper(ImageLayout layout, Vector sources, Map config) { if (layout != null) { layout = (ImageLayout)layout.clone(); layout.unsetImageBounds(); } return layout; } /** * Construct an OverlayOpImage. * * @param sourceUnder The source image on the bottom. * @param sourceOver The source image to be overlayed on top of * sourceUnder. * @param layout The image layout for the destination image. */ public OverlayOpImage(RenderedImage sourceUnder, RenderedImage sourceOver, Map config, ImageLayout layout) { super(sourceUnder, sourceOver, layoutHelper(layout, vectorize(sourceUnder, sourceOver), config), config, true); /* Validate destination sampleModel. */ SampleModel srcSM = sourceUnder.getSampleModel(); if (sampleModel.getTransferType() != srcSM.getTransferType() || sampleModel.getNumBands() != srcSM.getNumBands()) { sampleModel = srcSM.createCompatibleSampleModel( tileWidth, tileHeight); if(colorModel != null && !JDKWorkarounds.areCompatibleDataModels(sampleModel, colorModel)) { colorModel = ImageUtil.getCompatibleColorModel(sampleModel, config); } } /* * Unlike other multi-source OpImages, the image dimension in this * case is set to the same values as that of the source image on * the bottom. */ minX = sourceUnder.getMinX(); minY = sourceUnder.getMinY(); width = sourceUnder.getWidth(); height = sourceUnder.getHeight(); } /** * Computes a tile. This method overrides PointOpImage.computeTile() * * @param tileX The X index of the tile. * @param tileY The Y index of the tile. */ public Raster computeTile(int tileX, int tileY) { /* Create a new WritableRaster to represent this tile. */ WritableRaster dest = createTile(tileX, tileY); /* Clip the raster bound to image bounds. */ Rectangle destRect = dest.getBounds().intersection(getBounds()); PlanarImage srcUnder = getSource(0); PlanarImage srcOver = getSource(1); Rectangle srcUnderBounds = srcUnder.getBounds(); Rectangle srcOverBounds = srcOver.getBounds(); /* In case of PointOpImage, mapDestRect(destRect, i) = destRect). */ Raster[] sources = new Raster[1]; if (srcOverBounds.contains(destRect)) { /* Tile is entirely inside sourceOver. */ sources[0] = srcOver.getData(destRect); computeRect(sources, dest, destRect); // Recycle the source tile if(srcOver.overlapsMultipleTiles(destRect)) { recycleTile(sources[0]); } return dest; } else if (srcUnderBounds.contains(destRect) && !srcOverBounds.intersects(destRect)) { /* Tile is entirely inside sourceUnder. */ sources[0] = srcUnder.getData(destRect); computeRect(sources, dest, destRect); // Recycle the source tile if(srcUnder.overlapsMultipleTiles(destRect)) { recycleTile(sources[0]); } return dest; } else { /* Tile is inside both sources. */ Rectangle isectUnder = destRect.intersection(srcUnderBounds); sources[0] = srcUnder.getData(isectUnder); computeRect(sources, dest, isectUnder); // Recycle the source tile if(srcUnder.overlapsMultipleTiles(isectUnder)) { recycleTile(sources[0]); } if (srcOverBounds.intersects(destRect)) { Rectangle isectOver = destRect.intersection(srcOverBounds); sources[0] = srcOver.getData(isectOver); computeRect(sources, dest, isectOver); // Recycle the source tile if(srcOver.overlapsMultipleTiles(isectOver)) { recycleTile(sources[0]); } } return dest; } } /** * Copies the pixel values of a source image within a specified * rectangle. * * @param sources Cobbled sources, guaranteed to provide all the * source data necessary for computing the rectangle. * @param dest The tile containing the rectangle to be computed. * @param destRect The rectangle within the tile to be computed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); RasterAccessor src = new RasterAccessor(sources[0], destRect, formatTags[0], getSource(0).getColorModel()); RasterAccessor dst = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); switch (dst.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(src, dst); break; case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: computeRectShort(src, dst); break; case DataBuffer.TYPE_INT: computeRectInt(src, dst); break; case DataBuffer.TYPE_FLOAT: computeRectFloat(src, dst); break; case DataBuffer.TYPE_DOUBLE: computeRectDouble(src, dst); break; } dst.copyDataToRaster(); } private void computeRectByte(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); byte[][] dstData = dst.getByteDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); byte[][] srcData = src.getByteDataArrays(); for (int b = 0; b < dstBands; b++) { byte[] d = dstData[b]; byte[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = s[srcPixelOffset]; dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectShort(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); short[][] dstData = dst.getShortDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); short[][] srcData = src.getShortDataArrays(); for (int b = 0; b < dstBands; b++) { short[] d = dstData[b]; short[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = s[srcPixelOffset]; dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectInt(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); int[][] dstData = dst.getIntDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); int[][] srcData = src.getIntDataArrays(); for (int b = 0; b < dstBands; b++) { int[] d = dstData[b]; int[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = s[srcPixelOffset]; dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectFloat(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); float[][] dstData = dst.getFloatDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); float[][] srcData = src.getFloatDataArrays(); for (int b = 0; b < dstBands; b++) { float[] d = dstData[b]; float[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = s[srcPixelOffset]; dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } private void computeRectDouble(RasterAccessor src, RasterAccessor dst) { int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int dstLineStride = dst.getScanlineStride(); int dstPixelStride = dst.getPixelStride(); int[] dstBandOffsets = dst.getBandOffsets(); double[][] dstData = dst.getDoubleDataArrays(); int srcLineStride = src.getScanlineStride(); int srcPixelStride = src.getPixelStride(); int[] srcBandOffsets = src.getBandOffsets(); double[][] srcData = src.getDoubleDataArrays(); for (int b = 0; b < dstBands; b++) { double[] d = dstData[b]; double[] s = srcData[b]; int dstLineOffset = dstBandOffsets[b]; int srcLineOffset = srcBandOffsets[b]; for (int h = 0; h < dstHeight; h++) { int dstPixelOffset = dstLineOffset; int srcPixelOffset = srcLineOffset; dstLineOffset += dstLineStride; srcLineOffset += srcLineStride; for (int w = 0; w < dstWidth; w++) { d[dstPixelOffset] = s[srcPixelOffset]; dstPixelOffset += dstPixelStride; srcPixelOffset += srcPixelStride; } } } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/FilterCRIF.java0000644000175000017500000000627210203035544025773 0ustar mathieumathieu/* * $RCSfile: FilterCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:26 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import java.awt.image.renderable.RenderContext; import java.util.Arrays; import javax.media.jai.CRIFImpl; import javax.media.jai.JAI; import javax.media.jai.KernelJAI; /** * This CRIF implements rendering-independent filtering (blur/sharpen). * * @since 1.0 * @see FilterDescriptor */ final class FilterCRIF extends CRIFImpl { /** * Step size of the filter parameter indicating a step from one kernel * size to the next. */ private static final int STEPSIZE = 5; /** * Create a kernel given the filter parameter. Positive is blur, * negative sharpen. */ private static final KernelJAI createKernel(double p) { int STEPSIZE = 5; if(p == 0.0) { return null; } double pAbs = Math.abs(p); int idx = ((int)pAbs) / STEPSIZE; double frac = (10.0F/STEPSIZE)*(pAbs - idx*STEPSIZE); double blend = 1.0/99.0*(Math.pow(10.0, 0.2*frac) - 1.0); // First create a low-pass kernel. int size; float[] data; if(idx*STEPSIZE == pAbs) { // The parameter is at the left end of an interval so no // blending of kernels is required. size = 2*idx + 1; data = new float[size*size]; float val = 1.0F/(size*size); Arrays.fill(data, val); } else { // Create data for the left and right intervals and blend them. int size1 = 2*idx + 1; size = size1 + 2; data = new float[size*size]; float val1 = (1.0F/(size1*size1))*(1.0F - (float)blend); int row = size; for(int j = 1; j < size - 1; j++) { for(int i = 1; i < size - 1; i++) { data[row + i] = val1; } row += size; } float val2 = (1.0F/(size*size))*(float)blend; for(int i = 0; i < data.length; i++) { data[i] += val2; } } // For positive factor generate a high-pass kernel. if(p > 0.0) { // Subtract the low-pass kernel data from the image. for(int i = 0; i < data.length; i++) { data[i] *= -1.0; } data[data.length/2] += 2.0F; } return new KernelJAI(size, size, data); } /** Constructor. */ public FilterCRIF() { super(); } /** * Implementation of "RIF" create(). */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { KernelJAI kernel = createKernel(paramBlock.getFloatParameter(0)); return kernel == null ? paramBlock.getRenderedSource(0): JAI.create("convolve", paramBlock.getRenderedSource(0), kernel); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MedianFilterXOpImage.java0000644000175000017500000004434610203035544030043 0ustar mathieumathieu/* * $RCSfile: MedianFilterXOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:34 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import java.util.Map; import javax.media.jai.operator.MedianFilterDescriptor; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class to perform median filtering on a source image. * * */ final class MedianFilterXOpImage extends MedianFilterOpImage { /** * Creates a MedianFilterXOpImage with the given source and * maskSize. The image dimensions are derived from the source * image. The tile grid layout, SampleModel, and ColorModel may * optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. */ public MedianFilterXOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, int maskSize) { super(source, extender, config, layout, MedianFilterDescriptor.MEDIAN_MASK_PLUS, maskSize); } protected void byteLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); byte dstDataArrays[][] = dst.getByteDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); byte srcDataArrays[][] = src.getByteDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int values[] = new int[filterSize*2-1]; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { byte dstData[] = dstDataArrays[k]; byte srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int valueCount = 0; // figure out where the top left of the X starts int imageOffset = srcPixelOffset; for (int u = 0; u < wp; u++) { values[valueCount++] = (int)(srcData[imageOffset]&0xff); imageOffset += srcScanlineStride+srcPixelStride; } // remove the center element so it doesn't get counted // twice when we do the second line valueCount--; values[offset] = values[valueCount]; // figure out where the top right of the X starts imageOffset = srcPixelOffset + srcPixelStride*(filterSize-1); for (int v = 0; v < wp; v++) { values[valueCount++] = (int)(srcData[imageOffset]&0xff); imageOffset += srcScanlineStride-srcPixelStride; } int val = medianFilter(values); dstData[dstPixelOffset] = (byte)val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void shortLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int values[] = new int[filterSize*2-1]; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int valueCount = 0; // figure out where the top left of the X starts int imageOffset = srcPixelOffset; for (int u = 0; u < wp; u++) { values[valueCount++] = (int)(srcData[imageOffset]); imageOffset += srcScanlineStride+srcPixelStride; } // remove the center element so it doesn't get counted // twice when we do the second line valueCount--; values[offset] = values[valueCount]; // figure out where the top right of the X starts imageOffset = srcPixelOffset + srcPixelStride*(filterSize-1); for (int v = 0; v < wp; v++) { values[valueCount++] = (int)(srcData[imageOffset]); imageOffset += srcScanlineStride-srcPixelStride; } int val = medianFilter(values); dstData[dstPixelOffset] = (short)val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void ushortLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); short dstDataArrays[][] = dst.getShortDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); short srcDataArrays[][] = src.getShortDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int values[] = new int[filterSize*2-1]; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { short dstData[] = dstDataArrays[k]; short srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int valueCount = 0; // figure out where the top left of the X starts int imageOffset = srcPixelOffset; for (int u = 0; u < wp; u++) { values[valueCount++] = (int)(srcData[imageOffset]&0xffff); imageOffset += srcScanlineStride+srcPixelStride; } // remove the center element so it doesn't get counted // twice when we do the second line valueCount--; values[offset] = values[valueCount]; // figure out where the top right of the X starts imageOffset = srcPixelOffset + srcPixelStride*(filterSize-1); for (int v = 0; v < wp; v++) { values[valueCount++] = (int)(srcData[imageOffset]&0xffff); imageOffset += srcScanlineStride-srcPixelStride; } int val = medianFilter(values); dstData[dstPixelOffset] = (short)val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void intLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); int dstDataArrays[][] = dst.getIntDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); int srcDataArrays[][] = src.getIntDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); int values[] = new int[filterSize*2-1]; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { int dstData[] = dstDataArrays[k]; int srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int valueCount = 0; // figure out where the top left of the X starts int imageOffset = srcPixelOffset; for (int u = 0; u < wp; u++) { values[valueCount++] = srcData[imageOffset]; imageOffset += srcScanlineStride+srcPixelStride; } // remove the center element so it doesn't get counted // twice when we do the second line valueCount--; values[offset] = values[valueCount]; // figure out where the top right of the X starts imageOffset = srcPixelOffset + srcPixelStride*(filterSize-1); for (int v = 0; v < wp; v++) { values[valueCount++] = srcData[imageOffset]; imageOffset += srcScanlineStride-srcPixelStride; } int val = medianFilter(values); dstData[dstPixelOffset] = val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void floatLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); float dstDataArrays[][] = dst.getFloatDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); float srcDataArrays[][] = src.getFloatDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); float values[] = new float[filterSize*2-1]; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { float dstData[] = dstDataArrays[k]; float srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int valueCount = 0; // figure out where the top left of the X starts int imageOffset = srcPixelOffset; for (int u = 0; u < wp; u++) { values[valueCount++] = srcData[imageOffset]; imageOffset += srcScanlineStride+srcPixelStride; } // remove the center element so it doesn't get counted // twice when we do the second line valueCount--; values[offset] = values[valueCount]; // figure out where the top right of the X starts imageOffset = srcPixelOffset + srcPixelStride*(filterSize-1); for (int v = 0; v < wp; v++) { values[valueCount++] = srcData[imageOffset]; imageOffset += srcScanlineStride-srcPixelStride; } float val = medianFilterFloat(values); dstData[dstPixelOffset] = val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } protected void doubleLoop(RasterAccessor src, RasterAccessor dst, int filterSize) { int dwidth = dst.getWidth(); int dheight = dst.getHeight(); int dnumBands = dst.getNumBands(); double dstDataArrays[][] = dst.getDoubleDataArrays(); int dstBandOffsets[] = dst.getBandOffsets(); int dstPixelStride = dst.getPixelStride(); int dstScanlineStride = dst.getScanlineStride(); double srcDataArrays[][] = src.getDoubleDataArrays(); int srcBandOffsets[] = src.getBandOffsets(); int srcPixelStride = src.getPixelStride(); int srcScanlineStride = src.getScanlineStride(); double values[] = new double[filterSize*2-1]; int wp = filterSize; int offset = filterSize/2; for (int k = 0; k < dnumBands; k++) { double dstData[] = dstDataArrays[k]; double srcData[] = srcDataArrays[k]; int srcScanlineOffset = srcBandOffsets[k]; int dstScanlineOffset = dstBandOffsets[k]; for (int j = 0; j < dheight; j++) { int srcPixelOffset = srcScanlineOffset; int dstPixelOffset = dstScanlineOffset; for (int i = 0; i < dwidth; i++) { int valueCount = 0; // figure out where the top left of the X starts int imageOffset = srcPixelOffset; for (int u = 0; u < wp; u++) { values[valueCount++] = srcData[imageOffset]; imageOffset += srcScanlineStride+srcPixelStride; } // remove the center element so it doesn't get counted // twice when we do the second line valueCount--; values[offset] = values[valueCount]; // figure out where the top right of the X starts imageOffset = srcPixelOffset + srcPixelStride*(filterSize-1); for (int v = 0; v < wp; v++) { values[valueCount++] = srcData[imageOffset]; imageOffset += srcScanlineStride-srcPixelStride; } double val = medianFilterDouble(values); dstData[dstPixelOffset] = val; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } srcScanlineOffset += srcScanlineStride; dstScanlineOffset += dstScanlineStride; } } } // public static OpImage createTestImage(OpImageTester oit) { // return new MedianFilterXOpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource()), // 3); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/RIFUtil.java0000644000175000017500000000224710203035544025356 0ustar mathieumathieu/* * $RCSfile: RIFUtil.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:41 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.JAI; import javax.media.jai.TileCache; public class RIFUtil { public static ImageLayout getImageLayoutHint(RenderingHints renderHints) { if (renderHints == null) { return null; } else { return (ImageLayout)renderHints.get(JAI.KEY_IMAGE_LAYOUT); } } public static TileCache getTileCacheHint(RenderingHints renderHints) { if (renderHints == null) { return null; } else { return (TileCache)renderHints.get(JAI.KEY_TILE_CACHE); } } public static BorderExtender getBorderExtenderHint(RenderingHints renderHints) { if (renderHints == null) { return null; } else { return (BorderExtender)renderHints.get(JAI.KEY_BORDER_EXTENDER); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/FileLoadRIF.java0000644000175000017500000001241110444643225026122 0ustar mathieumathieu/* * $RCSfile: FileLoadRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.3 $ * $Date: 2006-06-17 00:02:28 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import java.io.FileNotFoundException; import java.io.InputStream; import java.io.IOException; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Method; import javax.media.jai.JAI; import javax.media.jai.OperationRegistry; import javax.media.jai.OpImage; import javax.media.jai.RenderedImageAdapter; import javax.media.jai.registry.RIFRegistry; import javax.media.jai.util.ImagingListener; import com.sun.media.jai.codec.FileSeekableStream; import com.sun.media.jai.codec.ImageDecodeParam; import com.sun.media.jai.codec.SeekableStream; import com.sun.media.jai.util.ImageUtil; /* * Package-scope class which merely adds a finalize() method to close * the associated stream and a dispose() method to forward the dispose() * call if possible. */ class StreamImage extends RenderedImageAdapter { private InputStream stream; /* * Create the object and cache the stream. */ public StreamImage(RenderedImage image, InputStream stream) { super(image); this.stream = stream; if(image instanceof OpImage) { // Set the properties related to TileCache key as used in // RenderedOp. setProperty("tile_cache_key", image); Object tileCache = ((OpImage)image).getTileCache(); setProperty("tile_cache", tileCache == null ? java.awt.Image.UndefinedProperty : tileCache); } } public void dispose() { // Use relection to invoke dispose(); RenderedImage trueSrc = getWrappedImage(); Method disposeMethod = null; try { Class cls = trueSrc.getClass(); disposeMethod = cls.getMethod("dispose", null); if(!disposeMethod.isAccessible()) { AccessibleObject.setAccessible(new AccessibleObject[] { disposeMethod }, true); } disposeMethod.invoke(trueSrc, null); } catch(Exception e) { // Ignore it. } } /* * Close the stream. */ protected void finalize() throws Throwable { stream.close(); super.finalize(); } } /** * @see javax.media.jai.operator.FileDescriptor * * @since EA3 * */ public class FileLoadRIF implements RenderedImageFactory { /** Constructor. */ public FileLoadRIF() {} /** * Creates an image from a String containing a file name. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { ImagingListener listener = ImageUtil.getImagingListener(hints); try { // Create a SeekableStream from the file name (first parameter). String fileName = (String)args.getObjectParameter(0); SeekableStream src = null; try { src = new FileSeekableStream(fileName); } catch (FileNotFoundException fnfe) { // Try to get the file as an InputStream resource. This would // happen when the application and image file are packaged in // a JAR file InputStream is = this.getClass().getClassLoader().getResourceAsStream(fileName); if (is != null) src = SeekableStream.wrapInputStream(is, true); } ImageDecodeParam param = null; if (args.getNumParameters() > 1) { param = (ImageDecodeParam)args.getObjectParameter(1); } ParameterBlock newArgs = new ParameterBlock(); newArgs.add(src); newArgs.add(param); RenderingHints.Key key = JAI.KEY_OPERATION_BOUND; int bound = OpImage.OP_IO_BOUND; if (hints == null) { hints = new RenderingHints(key, new Integer(bound)); } else if (!hints.containsKey(key)) { hints = (RenderingHints)hints.clone(); hints.put(key, new Integer(bound)); } // Get the registry from the hints, if any. // Don't check for null hints as it cannot be null here. OperationRegistry registry = (OperationRegistry)hints.get(JAI.KEY_OPERATION_REGISTRY); // Create the image using the most preferred RIF for "stream". RenderedImage image = RIFRegistry.create(registry, "stream", newArgs, hints); return image == null ? null : new StreamImage(image, src); } catch (FileNotFoundException e) { String message = JaiI18N.getString("FileLoadRIF0") + args.getObjectParameter(0); listener.errorOccurred(message, e, this, false); // e.printStackTrace(); return null; } catch (Exception e) { String message = JaiI18N.getString("FileLoadRIF1"); listener.errorOccurred(message, e, this, false); // e.printStackTrace(); return null; } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ExtremaRIF.java0000644000175000017500000000335310203035544026045 0ustar mathieumathieu/* * $RCSfile: ExtremaRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:25 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ROI; /** * A RIF supporting the "Extrema" operation in the * rendered image layer. * * @see javax.media.jai.operator.ExtremaDescriptor */ public class ExtremaRIF implements RenderedImageFactory { /** Constructor. */ public ExtremaRIF() {} /** * Creates a new instance of ExtremaOpImage * in the rendered layer. Any image layout information in * RenderingHints is ignored. * This method satisfies the implementation of RIF. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints hints) { RenderedImage src = paramBlock.getRenderedSource(0); int xStart = src.getMinX(); // default values int yStart = src.getMinY(); int maxWidth = src.getWidth(); int maxHeight = src.getHeight(); return new ExtremaOpImage(src, (ROI)paramBlock.getObjectParameter(0), xStart, yStart, paramBlock.getIntParameter(1), paramBlock.getIntParameter(2), ((Boolean)paramBlock.getObjectParameter(3)).booleanValue(), paramBlock.getIntParameter(4)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/MosaicRIF.java0000644000175000017500000000306310203035544025651 0ustar mathieumathieu/* * $RCSfile: MosaicRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:36 $ * $State: Exp $ */package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import java.util.Vector; import javax.media.jai.PlanarImage; import javax.media.jai.ROI; import javax.media.jai.operator.MosaicType; /** * A RIF supporting the "Mosaic" operation in the rendered * image layer. * * @since JAI 1.1.2 * @see javax.media.jai.operator.MosaicDescriptor */ public class MosaicRIF implements RenderedImageFactory { /** Constructor. */ public MosaicRIF() {} /** * Renders a "Mosaic" operation node. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { return new MosaicOpImage(paramBlock.getSources(), RIFUtil.getImageLayoutHint(renderHints), renderHints, (MosaicType)paramBlock.getObjectParameter(0), (PlanarImage[])paramBlock.getObjectParameter(1), (ROI[])paramBlock.getObjectParameter(2), (double[][])paramBlock.getObjectParameter(3), (double[])paramBlock.getObjectParameter(4)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/ClampCRIF.java0000644000175000017500000000307310203035544025576 0ustar mathieumathieu/* * $RCSfile: ClampCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:16 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "Clamp" operation in the rendered * and renderable image layers. * * @see javax.media.jai.operator.ClampDescriptor * @see ClampOpImage * * * @since EA2 */ public class ClampCRIF extends CRIFImpl { /** Constructor. */ public ClampCRIF() { super("clamp"); } /** * Creates a new instance of ClampOpImage in the * rendered layer. * * @param args The source image and the low and high boundary values. * @param hints Optionally contains destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new ClampOpImage(args.getRenderedSource(0), renderHints, layout, (double[])args.getObjectParameter(0), (double[])args.getObjectParameter(1)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/opimage/InvertCRIF.java0000644000175000017500000000256610203035544026017 0ustar mathieumathieu/* * $RCSfile: InvertCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:29 $ * $State: Exp $ */ package com.sun.media.jai.opimage; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.CRIFImpl; import javax.media.jai.ImageLayout; import java.util.Map; /** * A CRIF supporting the "Invert" operation in the * rendered and renderable image layer. * * @see javax.media.jai.operator.InvertDescriptor * @see InvertOpImage * */ public class InvertCRIF extends CRIFImpl { /** Constructor. */ public InvertCRIF() { super("invert"); } /** * Creates a new instance of InvertpImage in the rendered * layer. * * @param paramBlock The source images to be inverted. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); return new InvertOpImage(paramBlock.getRenderedSource(0), renderHints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/widget/0000755000175000017500000000000011633360406023100 5ustar mathieumathieujai-core-1.1.4/src/share/classes/com/sun/media/jai/widget/DisplayJAI.java0000644000175000017500000002160010203035544025665 0ustar mathieumathieu/* * $RCSfile: DisplayJAI.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:03 $ * $State: Exp $ */ package com.sun.media.jai.widget; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.geom.AffineTransform; import java.awt.image.RenderedImage; import javax.swing.JComponent; import javax.swing.JPanel; /** * A JavaTM Foundation Classes {@link JPanel} which is able * to contain an image. The image and its container may have different * sizes. The image may also be positioned within the container. * *

    This class extends JPanel in order to support layout * management. Image tiling is supported as of version 1.3 of the * JavaTM 2 Platform via * {@link Graphics2D#drawRenderedImage(RenderedImage,AffineTransform)}.

    * *

    This class is not a committed part of the JavaTM * Advanced Imaging API per se. It might therefore not be supported by * JAI implementations other than that of Sun Microsystems, Inc.

    * * @see java.awt.Graphics2D * @see java.awt.image.RenderedImage * @see javax.swing.JPanel * @see javax.swing.JComponent */ public class DisplayJAI extends JPanel implements MouseListener, MouseMotionListener { /** The image to display. */ protected RenderedImage source = null; /** Abscissa of image origin relative to panel origin. */ protected int originX = 0; /** Ordinate of image origin relative to panel origin. */ protected int originY = 0; /** * Constructs a DisplayJAI and sets its * the layout to null. */ public DisplayJAI() { super(); setLayout(null); } /** * Constructs a DisplayJAI, sets its layout to * null, and sets its displayed image. * *

    The preferred size is set such that its width is the image * width plus the left and right insets and its height is the image * height plus the top and bottom insets.

    * * @param image The image to display. * @throws IllegalArgumentException if image is * null. */ public DisplayJAI(RenderedImage image) { super(); setLayout(null); if(image == null) { throw new IllegalArgumentException("image == null!"); } source = image; // Swing geometry int w = source.getWidth(); int h = source.getHeight(); Insets insets = getInsets(); Dimension dim = new Dimension(w + insets.left + insets.right, h + insets.top + insets.bottom); setPreferredSize(dim); } /** * Moves the image within it's container. The instance variables * originX and originY are set to the * parameter valus x and y, respectively. * {@link #repaint()} is invoked after the origin values are changed. * * @param x image origin abscissa. * @param y image origin ordinate. */ public void setOrigin(int x, int y) { originX = x; originY = y; repaint(); } /** Retrieves the image origin. */ public Point getOrigin() { return new Point(originX, originY); } /** * Sets a new image to display. * *

    The preferred size is set such that its width is the image * width plus the left and right insets and its height is the image * height plus the top and bottom insets. {@link #revalidate()} and * {@link #repaint()} are invoked after all other changes.

    * * @param im The image to display. * @throws IllegalArgumentException if im is * null. */ public void set(RenderedImage im) { if(im == null) { throw new IllegalArgumentException("im == null!"); } source = im; // Swing geometry int w = source.getWidth(); int h = source.getHeight(); Insets insets = getInsets(); Dimension dim = new Dimension(w + insets.left + insets.right, h + insets.top + insets.bottom); setPreferredSize(dim); revalidate(); repaint(); } /** * Sets a new image to display and its coordinates within the container. * *

    The preferred size is set such that its width is the image * width plus the left and right insets and its height is the image * height plus the top and bottom insets. {@link #revalidate()} and * {@link #repaint()} are invoked after all other changes.

    * * @param im The image to display. * @param x image origin abscissa. * @param y image origin ordinate. * @throws IllegalArgumentException if im is * null. */ public void set(RenderedImage im, int x, int y) { if(im == null) { throw new IllegalArgumentException("im == null!"); } source = im; // Swing geometry int w = source.getWidth(); int h = source.getHeight(); Insets insets = getInsets(); Dimension dim = new Dimension(w + insets.left + insets.right, h + insets.top + insets.bottom); setPreferredSize(dim); originX = x; originY = y; revalidate(); repaint(); } /** * Returns the image to be displayed by this panel. * * @return The value of the {@link #source} instance variable. */ public RenderedImage getSource() { return source; } /** * Draws the image or fills the panel with a background color. * *

    If the current image is null, the rectangle * new Rectangle(0,0,{@link #getWidth()},{@link #getHeight()}) * is filled with the background color returned by * {@link #getBackground()}.

    * *

    If the current image is non-null, the rectangle * returned by {@link Graphics2D#getClipBounds()} is filled with the * background color and the image is drawn using * {@link Graphics2D#drawRenderedImage(RenderedImage,AffineTransform)} * at the location * ({@link #getInsets()}.left+originX, * {@link #getInsets()}.right+originY). * * @param g Graphics context in which to paint. */ public synchronized void paintComponent(Graphics g) { Graphics2D g2d = (Graphics2D)g; // empty component (no image) if ( source == null ) { g2d.setColor(getBackground()); g2d.fillRect(0, 0, getWidth(), getHeight()); return; } // clear damaged component area Rectangle clipBounds = g2d.getClipBounds(); g2d.setColor(getBackground()); g2d.fillRect(clipBounds.x, clipBounds.y, clipBounds.width, clipBounds.height); // account for borders Insets insets = getInsets(); int tx = insets.left + originX; int ty = insets.top + originY; // Translation moves the entire image within the container try { g2d.drawRenderedImage(source, AffineTransform.getTranslateInstance(tx, ty)); } catch( OutOfMemoryError e ) { } } /** * An empty method which should be overridden by subclasses which * respond to mouse presses. */ public void mousePressed(MouseEvent e) { } /** * An empty method which should be overridden by subclasses which * respond to mouse releases. */ public void mouseReleased(MouseEvent e) { } /** * An empty method which should be overridden by subclasses which * respond to mouse movement. */ public void mouseMoved(MouseEvent e) { } /** * An empty method which should be overridden by subclasses which * respond to mouse dragging. */ public void mouseDragged(MouseEvent e) { } /** * An empty method which should be overridden by subclasses which * respond to mouse entrances. */ public void mouseEntered(MouseEvent e) { } /** * An empty method which should be overridden by subclasses which * respond to mouse exits. */ public void mouseExited(MouseEvent e) { } /** * An empty method which should be overridden by subclasses which * respond to mouse clicks. */ public void mouseClicked(MouseEvent e) { } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/0000755000175000017500000000000011633360406022540 5ustar mathieumathieujai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibDFTRIF.java0000644000175000017500000000736210203035544025167 0ustar mathieumathieu/* * $RCSfile: MlibDFTRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:53 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.BorderExtender; import javax.media.jai.EnumeratedParameter; import javax.media.jai.ImageLayout; import javax.media.jai.JAI; import java.util.Map; import javax.media.jai.operator.DFTDescriptor; import com.sun.media.jai.opimage.RIFUtil; import com.sun.media.jai.opimage.DFTOpImage; import com.sun.media.jai.opimage.FFT; import com.sun.media.jai.util.MathJAI; /** * A RIF supporting the "DFT" operation in the * rendered image mode using MediaLib. * * @since EA4 * * @see javax.media.jai.operator.DFTDescriptor * @see com.sun.media.jai.opimage.DFTOpImage * */ public class MlibDFTRIF implements RenderedImageFactory { /** Constructor. */ public MlibDFTRIF() {} /** * Creates a new instance of DFTOpImage in * the rendered image mode. * * @param args The source image. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(new ParameterBlock())) { return null; } RenderedImage source = args.getRenderedSource(0); EnumeratedParameter scalingType = (EnumeratedParameter)args.getObjectParameter(0); EnumeratedParameter dataNature = (EnumeratedParameter)args.getObjectParameter(1); boolean isComplexSource = !dataNature.equals(DFTDescriptor.REAL_TO_COMPLEX); int numSourceBands = source.getSampleModel().getNumBands(); // Use the two-dimensional mediaLib DFT if possible: it supports // only data which have a single component (real or complex) // per pixel and which have dimensions which are equal to a positive // power of 2. if(((isComplexSource && numSourceBands == 2) || (!isComplexSource && numSourceBands == 1)) && MlibDFTOpImage.isAcceptableSampleModel(source.getSampleModel())) { // If necessary, pad the source to ensure that // both dimensions are positive powers of 2. int sourceWidth = source.getWidth(); int sourceHeight = source.getHeight(); if(!MathJAI.isPositivePowerOf2(sourceWidth) || !MathJAI.isPositivePowerOf2(sourceHeight)) { ParameterBlock pb = new ParameterBlock(); pb.addSource(source); pb.add(0); pb.add(MathJAI.nextPositivePowerOf2(sourceWidth) - sourceWidth); pb.add(0); pb.add(MathJAI.nextPositivePowerOf2(sourceHeight) - sourceHeight); pb.add(BorderExtender.createInstance(BorderExtender.BORDER_ZERO)); source = JAI.create("border", pb, null); } return new MlibDFTOpImage(source, hints, layout, dataNature, true, scalingType); } else { // General case FFT fft = new FFTmediaLib(true, new Integer(scalingType.getValue()), 2); return new DFTOpImage(source, hints, layout, dataNature, fft); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibExpRIF.java0000644000175000017500000000306610203035544025303 0ustar mathieumathieu/* * $RCSfile: MlibExpRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:56 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "Exp" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.ExpDescriptor * @see MlibExpOpImage * */ public class MlibExpRIF implements RenderedImageFactory { /** Constructor. */ public MlibExpRIF() {} /** * Creates a new instance of MlibExpOpImage in * the rendered image mode. * * @param args The source image. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout)) { return null; } return new MlibExpOpImage(args.getRenderedSource(0), hints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibUtils.java0000644000175000017500000000604010350333605025303 0ustar mathieumathieu/* * $RCSfile: MlibUtils.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-12-15 18:35:47 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.image.ColorModel; import com.sun.medialib.mlib.Image; import com.sun.medialib.mlib.mediaLibImage; final class MlibUtils { /** * If constants array is less than numBands it is replaced * by an array of length numBands filled with constants[0]. * Otherwise the input array is cloned. */ static final int[] initConstants(int[] constants, int numBands) { int[] c = null; if (constants.length < numBands) { c = new int[numBands]; for (int i = 0; i < numBands; i++) { c[i] = constants[0]; } } else { c = (int[])constants.clone(); } return c; } /** * If constants array is less than numBands it is replaced * by an array of length numBands filled with constants[0]. * Otherwise the input array is cloned. */ static final double[] initConstants(double[] constants, int numBands) { double[] c = null; if (constants.length < numBands) { c = new double[numBands]; for (int i = 0; i < numBands; i++) { c[i] = constants[0]; } } else { c = (double[])constants.clone(); } return c; } /** * If the color depth in bits of any band of the image does not * match the full bit depth as determined from the image type then * clamp the image to the unnomarlized range represented by the * ColorModel. */ static void clampImage(mediaLibImage image, ColorModel colorModel) { if(image == null) { throw new IllegalArgumentException("image == null!"); } if(colorModel != null) { // Set the full bit depth as a function of image type. int fullDepth = 0; switch(image.getType()) { case mediaLibImage.MLIB_BYTE: fullDepth = 8; break; case mediaLibImage.MLIB_INT: fullDepth = 32; break; default: // USHORT and SHORT fullDepth = 16; } // Set the low and high thresholds and the thresholding flag. int[] numBits = colorModel.getComponentSize(); int[] high = new int[numBits.length]; int[] low = new int[numBits.length]; // zero by default boolean applyThreshold = false; for(int j = 0; j < numBits.length; j++) { high[j] = (1 << numBits[j]) - 1; if(numBits[j] != fullDepth) { applyThreshold = true; } } // Apply threshold if color depth of any band is not full depth. if(applyThreshold) { Image.Thresh4(image, high, low, high, low); } } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibMosaicOpImage.java0000644000175000017500000003070310203035544026661 0ustar mathieumathieu/* * $RCSfile: MlibMosaicOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:00 $ * $State: Exp $ */package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.util.ArrayList; import java.util.Arrays; import java.util.Map; import java.util.Vector; import javax.media.jai.ImageLayout; import javax.media.jai.PlanarImage; import javax.media.jai.ROI; import javax.media.jai.operator.MosaicDescriptor; import javax.media.jai.operator.MosaicType; import com.sun.media.jai.opimage.MosaicOpImage; import com.sun.medialib.mlib.Image; import com.sun.medialib.mlib.mediaLibImage; final class MlibMosaicOpImage extends MosaicOpImage { /** The Integral lower bound for Thresh1. */ private int[] glow; /** The Integral upper bound for Thresh1. */ private int[] ghigh; /** The shift value for MulShift and DivShift. */ private int shift; public MlibMosaicOpImage(Vector sources, ImageLayout layout, Map config, MosaicType mosaicType, PlanarImage[] sourceAlpha, ROI[] sourceROI, double[][] sourceThreshold, double[] backgroundValues) { super(sources, layout, config, mosaicType, sourceAlpha, sourceROI, sourceThreshold, backgroundValues); int numSources = sources.size(); int dataType = sampleModel.getDataType(); if(dataType == DataBuffer.TYPE_FLOAT || dataType == DataBuffer.TYPE_DOUBLE) { throw new UnsupportedOperationException(JaiI18N.getString("MlibMosaicOpImage0")); } else { // Decrement integral source thresholds to account for Thresh1 // which uses ">" instead of ">=" as in the "mosaic" spec. for(int i = 0; i < numSources; i++) { for(int j = 0; j < numBands; j++) { this.threshold[i][j]--; } } // Set extrema and shift based on data type. int minValue = -Integer.MAX_VALUE; int maxValue = Integer.MAX_VALUE; switch (dataType) { case DataBuffer.TYPE_BYTE: minValue = 0; maxValue = 0xFF; shift = 8; break; case DataBuffer.TYPE_USHORT: minValue = 0; maxValue = 0xFFFF; shift = 16; break; case DataBuffer.TYPE_SHORT: minValue = Short.MIN_VALUE; maxValue = Short.MAX_VALUE; shift = 16; break; case DataBuffer.TYPE_INT: minValue = Integer.MIN_VALUE; maxValue = Integer.MAX_VALUE; shift = 32; break; default: } // Initialize upper and lower integral bounds for Thresh1. this.glow = new int[numBands]; Arrays.fill(this.glow, minValue); this.ghigh = new int[numBands]; Arrays.fill(this.ghigh, maxValue); } } protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect, Raster[] alphaRaster, Raster[] roiRaster) { // Save the total number of sources. int numSources = sources.length; // Put all non-null sources in a list. ArrayList sourceList = new ArrayList(numSources); for(int i = 0; i < numSources; i++) { if(sources[i] != null) { sourceList.add(sources[i]); } } // Convert the non-null sources to an array. int numNonNullSources = sourceList.size(); Raster[] nonNullSources = null; if(numNonNullSources != 0) { nonNullSources = new Raster[numNonNullSources]; sourceList.toArray((Raster[])nonNullSources); } // Get the format tag. int formatTag = MediaLibAccessor.findCompatibleTag(nonNullSources, dest); // Get dest accessor and image. MediaLibAccessor dstAccessor = new MediaLibAccessor(dest, destRect, formatTag); mediaLibImage[] dst = dstAccessor.getMediaLibImages(); // Re-order the background values as needed. int[] mlibBackground = dstAccessor.getIntParameters(0, background); if(numNonNullSources == 0) { // Fill the destination with the background value. Image.Clear(dst[0], mlibBackground); return; } // Get source accessor(s). MediaLibAccessor[] srcAccessor = new MediaLibAccessor[numSources]; for(int i = 0; i < numSources; i++) { if(sources[i] != null) { srcAccessor[i] = new MediaLibAccessor(sources[i], destRect, formatTag); } } // Get source image(s). int[][] mlibThreshold = new int[numSources][]; mediaLibImage[][] src = new mediaLibImage[numSources][]; for(int i = 0; i < numSources; i++) { if(srcAccessor[i] != null) { src[i] = srcAccessor[i].getMediaLibImages(); mlibThreshold[i] = srcAccessor[i].getIntParameters(0, threshold[i]); } } // Temporary images. mediaLibImage tmpIm1 = null; mediaLibImage tmpImN = null; mediaLibImage[] tmpIm1Array = new mediaLibImage[] {tmpIm1}; mediaLibImage[] tmpImNArray = new mediaLibImage[] {tmpImN}; if(mosaicType == MosaicDescriptor.MOSAIC_TYPE_OVERLAY) { // Fill the destination with the background value. Image.Clear(dst[0], mlibBackground); for(int i = numSources - 1; i >= 0; i--) { if(src[i] == null) { continue; } mediaLibImage weight = getWeightImage(destRect, formatTag, dst[0], src[i][0], sourceAlpha != null && sourceAlpha[i] != null ? alphaRaster[i] : null, sourceROI != null && sourceROI[i] != null ? roiRaster[i] : null, mlibThreshold[i], tmpIm1Array, tmpImNArray); Image.Blend2(dst[0], src[i][0], weight); } } else if(mosaicType == MosaicDescriptor.MOSAIC_TYPE_BLEND) { tmpIm1 = new mediaLibImage(dst[0].getType(), 1, dst[0].getWidth(), dst[0].getHeight()); tmpImN = new mediaLibImage(dst[0].getType(), dst[0].getChannels(), dst[0].getWidth(), dst[0].getHeight()); mediaLibImage[] alphas = new mediaLibImage[numNonNullSources]; mediaLibImage[] srcs = new mediaLibImage[numNonNullSources]; int sourceCount = 0; for(int i = 0; i < numSources; i++) { if(src[i] == null) { continue; } srcs[sourceCount] = src[i][0]; alphas[sourceCount] = getWeightImage(destRect, formatTag, dst[0], src[i][0], sourceAlpha != null && sourceAlpha[i] != null ? alphaRaster[i] : null, sourceROI != null && sourceROI[i] != null ? roiRaster[i] : null, mlibThreshold[i], null, null); sourceCount++; } if(sourceCount != numNonNullSources) { mediaLibImage[] srcsNew = new mediaLibImage[sourceCount]; System.arraycopy(srcs, 0, srcsNew, 0, sourceCount); srcs = srcsNew; mediaLibImage[] alphasNew = new mediaLibImage[sourceCount]; System.arraycopy(alphas, 0, alphasNew, 0, sourceCount); alphas = alphasNew; } Image.BlendMulti(dst[0], srcs, alphas, mlibBackground); } if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } /** * Compute the weight image. */ private mediaLibImage getWeightImage(Rectangle destRect, int formatTag, mediaLibImage dst, mediaLibImage src, Raster alphaRaster, Raster roiRaster, int[] thresh, mediaLibImage[] tmpIm1, mediaLibImage[] tmpImN) { mediaLibImage weight = null; if(alphaRaster != null) { MediaLibAccessor alphaAccessor = new MediaLibAccessor(alphaRaster, destRect, formatTag); mediaLibImage[] alphaML = alphaAccessor.getMediaLibImages(); if(isAlphaBitmask) { if(tmpIm1 == null) { tmpIm1 = new mediaLibImage[] {null}; } if(tmpIm1[0] == null) { tmpIm1[0] = new mediaLibImage(src.getType(), 1, src.getWidth(), src.getHeight()); } Image.Thresh1(tmpIm1[0], alphaML[0], new int[] {0}, new int[] {ghigh[0]}, new int[] {glow[0]}); weight = tmpIm1[0]; } else { weight = alphaML[0]; } } else if(roiRaster != null) { int roiFmtTag = MediaLibAccessor.findCompatibleTag(null, roiRaster); MediaLibAccessor roiAccessor = new MediaLibAccessor(roiRaster, destRect, roiFmtTag, true); mediaLibImage[] roi = roiAccessor.getMediaLibImages(); if(tmpIm1 == null) { tmpIm1 = new mediaLibImage[] {null}; } if(tmpIm1[0] == null) { tmpIm1[0] = new mediaLibImage(src.getType(), 1, src.getWidth(), src.getHeight()); } if(tmpIm1[0].getType() != roi[0].getType()) { if(tmpIm1[0] == null) { tmpIm1[0] = new mediaLibImage(src.getType(), 1, src.getWidth(), src.getHeight()); } Image.DataTypeConvert(tmpIm1[0], roi[0]); } else { // This is safe because the ROI image is bilevel // so the accessor must have copied the data. tmpIm1[0] = roi[0]; } Image.Thresh1(tmpIm1[0], new int[] {0}, new int[] {ghigh[0]}, new int[] {glow[0]}); weight = tmpIm1[0]; } else { if(tmpImN == null) { tmpImN = new mediaLibImage[] {null}; } if(tmpImN[0] == null) { tmpImN[0] = dst.createCompatibleImage(); } weight = tmpImN[0]; Image.Thresh1(weight, src, thresh, ghigh, glow); } return weight; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/FCTmediaLib.java0000644000175000017500000003067110203035544025447 0ustar mathieumathieu/* * $RCSfile: FCTmediaLib.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:46 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.image.DataBuffer; import java.util.Arrays; import com.sun.media.jai.opimage.FCT; import com.sun.media.jai.util.MathJAI; import com.sun.medialib.mlib.*; /** * The Fast Cosine Transform (FCT) class. This classes calculates the FCT * using mediaLib's 1D double precision FFT. * * @since EA4 */ public class FCTmediaLib extends FCT { /** The length of the FCT. */ private int length; /** Initialization flag. */ private boolean lengthIsSet = false; /** Lookup table of cosine scale factors. */ private double[] wr; /** Lookup table of sine scale factors. */ private double[] wi; /** Work array for real part. */ protected double[] real; /** Work array for imaginary part. */ protected double[] imag; /** * Construct a new FCT object. * * @param length The length of the FCT; must be a positive power of 2. */ public FCTmediaLib(boolean isForwardTransform, int length) { this.isForwardTransform = isForwardTransform; setLength(length); } // ***** FCT inner class public methods. ***** /** * Initialize the length-dependent fields. * * @param length The length of the FCT; must be a positive power of 2. */ public void setLength(int length) { // Check whether it's necessary to continue. if(lengthIsSet && length == this.length) { return; } // Ensure that the length is a positive power of two. if(!MathJAI.isPositivePowerOf2(length)) { throw new RuntimeException(JaiI18N.getString("FCTmediaLib0")); } // Cache the length. this.length = length; // Allocate cache memory. if(real == null || length != real.length) { real = new double[length]; imag = new double[length]; } // Calculate lookup tables of the cosine coefficients. calculateFCTLUTs(); // Set initialization flag. lengthIsSet = true; } /** * Calculate the FCT sine and cosine lookup tables. */ private void calculateFCTLUTs() { wr = new double[length]; wi = new double[length]; for(int i = 0; i < length; i++) { double factor = ((i == 0) ? Math.sqrt(1.0/length) : Math.sqrt(2.0/length)); double freq = Math.PI*i/(2.0*length); wr[i] = factor*Math.cos(freq); wi[i] = factor*Math.sin(freq); } } /** * Set the internal work data array of the FCT object. * * @param dataType The data type of the source data according to * one of the DataBuffer TYPE_* flags. This should be either * DataBuffer.TYPE_FLOAT or DataBuffer.TYPE_DOUBLE. * @param data Float or double array of data. * @param offset Offset into the data array. * @param stride The data array stride value. * @param count The number of values to copy. */ public void setData(int dataType, Object data, int offset, int stride, int count) { if(isForwardTransform) { setFCTData(dataType, data, offset, stride, count); } else { setIFCTData(dataType, data, offset, stride, count); } } /** * Get data from the internal work data array of the FCT object. * * @param dataType The data type of the source data according to * one of the DataBuffer TYPE_* flags. This should be either * DataBuffer.TYPE_FLOAT or DataBuffer.TYPE_DOUBLE. * @param data Float or double array of data. * @param offset Offset into the data array. * @param stride The data array stride value. */ public void getData(int dataType, Object data, int offset, int stride) { if(isForwardTransform) { getFCTData(dataType, data, offset, stride); } else { getIFCTData(dataType, data, offset, stride); } } /** * Set the internal work data arrays of the FFT object. * * @param dataType The data type of the source data according to * one of the DataBuffer TYPE_* flags. This should be either * DataBuffer.TYPE_FLOAT or DataBuffer.TYPE_DOUBLE. * @param data Float or double array of data. * @param offset Offset into the data array. * @param stride The data array stride value. * @param count The number of values to copy. */ private void setFCTData(int dataType, Object data, int offset, int stride, int count) { // Copy the parameter arrays. switch(dataType) { case DataBuffer.TYPE_FLOAT: { float[] realFloat = (float[])data; for(int i = 0; i < count; i++) { imag[i] = realFloat[offset]; offset += stride; } for(int i = count; i < length; i++) { imag[i] = 0.0; } int k = length - 1; int j = 0; for(int i = 0; i < k; i++) { real[i] = imag[j++]; real[k--] = imag[j++]; } } break; case DataBuffer.TYPE_DOUBLE: { double[] realDouble = (double[])data; for(int i = 0; i < count; i++) { imag[i] = realDouble[offset]; offset += stride; } for(int i = count; i < length; i++) { imag[i] = 0.0; } int k = length - 1; int j = 0; for(int i = 0; i < k; i++) { real[i] = imag[j++]; real[k--] = imag[j++]; } } break; default: // NB: This statement should be unreachable as the destination // image is required to be a floating point type and the // RasterAccessor is supposed to promote the data type of // all rasters to the "minimum" data type of all source // and destination rasters involved. throw new RuntimeException(dataType + JaiI18N.getString("FCTmediaLib1")); } // Always clear imaginary part. Arrays.fill(imag, 0, length, 0.0); } /** * Get data from the internal work data arrays of the FFT object. * * @param dataType The data type of the source data according to * one of the DataBuffer TYPE_* flags. This should be either * DataBuffer.TYPE_FLOAT or DataBuffer.TYPE_DOUBLE. * @param data The data as a float[] or double[]. * @param offset Offset into the data array. * @param stride The array stride value. * @param count The number of values to copy. */ private void getFCTData(int dataType, Object data, int offset, int stride) { switch(dataType) { case DataBuffer.TYPE_FLOAT: { float[] realFloat = (float[])data; for(int i = 0; i < length; i++) { realFloat[offset] = (float)(wr[i]*real[i] + wi[i]*imag[i]); offset += stride; } } break; case DataBuffer.TYPE_DOUBLE: { double[] realDouble = (double[])data; for(int i = 0; i < length; i++) { realDouble[offset] = wr[i]*real[i] + wi[i]*imag[i]; offset += stride; } } break; default: // NB: This statement should be unreachable as the destination // image is required to be a floating point type and the // RasterAccessor is supposed to promote the data type of // all rasters to the "minimum" data type of all source // and destination rasters involved. throw new RuntimeException(dataType + JaiI18N.getString("FCTmediaLib1")); } } /** * Set the internal work data arrays of the FFT object. * * @param dataType The data type of the source data according to * one of the DataBuffer TYPE_* flags. This should be either * DataBuffer.TYPE_FLOAT or DataBuffer.TYPE_DOUBLE. * @param data The data as a float[] or double[]. * @param offset Offset into the data array. * @param stride The array stride value. * @param count The number of values to copy. */ private void setIFCTData(int dataType, Object data, int offset, int stride, int count) { // Copy the parameter arrays. switch(dataType) { case DataBuffer.TYPE_FLOAT: { float[] realFloat = (float[])data; for(int i = 0; i < count; i++) { float r = realFloat[offset]; real[i] = r*wr[i]; imag[i] = r*wi[i]; offset += stride; } } break; case DataBuffer.TYPE_DOUBLE: { double[] realDouble = (double[])data; for(int i = 0; i < count; i++) { double r = realDouble[offset]; real[i] = r*wr[i]; imag[i] = r*wi[i]; offset += stride; } } break; default: // NB: This statement should be unreachable as the destination // image is required to be a floating point type and the // RasterAccessor is supposed to promote the data type of // all rasters to the "minimum" data type of all source // and destination rasters involved. throw new RuntimeException(dataType + JaiI18N.getString("FCTmediaLib1")); } // If fewer input than target points fill target with zeros. if(count < length) { Arrays.fill(real, count, length, 0.0); Arrays.fill(imag, count, length, 0.0); } } /** * Get data from the internal work data arrays of the FFT object. * * @param dataType The data type of the source data according to * one of the DataBuffer TYPE_* flags. This should be either * DataBuffer.TYPE_FLOAT or DataBuffer.TYPE_DOUBLE. * @param data The data as a float[] or double[]. * @param offset Offset into the data array. * @param stride The array stride value. * @param count The number of values to copy. */ private void getIFCTData(int dataType, Object data, int offset, int stride) { switch(dataType) { case DataBuffer.TYPE_FLOAT: { float[] realFloat = (float[])data; int k = length - 1; for(int i = 0; i < k; i++) { realFloat[offset] = (float)real[i]; offset += stride; realFloat[offset] = (float)real[k--]; offset += stride; } } break; case DataBuffer.TYPE_DOUBLE: { double[] realDouble = (double[])data; int k = length - 1; for(int i = 0; i < k; i++) { realDouble[offset] = (float)real[i]; offset += stride; realDouble[offset] = (float)real[k--]; offset += stride; } } break; default: // NB: This statement should be unreachable as the destination // image is required to be a floating point type and the // RasterAccessor is supposed to promote the data type of // all rasters to the "minimum" data type of all source // and destination rasters involved. throw new RuntimeException(dataType + JaiI18N.getString("FCTmediaLib1")); } } /** * Calculate the DCT of a sequence using the FFT algorithm. */ public void transform() { if(isForwardTransform) { Image.FFT_1(real, imag); } else { Image.IFFT_2(real, imag); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibGradientOpImage.java0000644000175000017500000001631010203035544027201 0ustar mathieumathieu/* * $RCSfile: MlibGradientOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:57 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; import javax.media.jai.OpImage; import java.util.Map; import com.sun.medialib.mlib.*; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class to perform Gradient operation on a source image. * *

    The Kernels cannot be bigger in any dimension than the image data. * * * @see KernelJAI */ final class MlibGradientOpImage extends AreaOpImage { /** * The orthogonal kernels with which to do the Gradient operation. */ protected KernelJAI kernel_h, kernel_v; /** Kernel variables. */ private int kh, kw, kx, ky; float kernel_h_data[], kernel_v_data[]; /** Local copy of kernel's data */ double dbl_kh_data[], dbl_kv_data[]; /** * Creates a MlibGradientOpImage given the image source and * the pair of orthogonal gradient kernels. The image dimensions are * derived from the source image. The tile grid layout, * SampleModel, and ColorModel may optionally be specified by an * ImageLayout object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param kernel_h the horizontal kernel * @param kernel_v the vertical kernel */ public MlibGradientOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, KernelJAI kernel_h, KernelJAI kernel_v) { super(source, layout, config, true, extender, kernel_h.getLeftPadding(), kernel_h.getRightPadding(), kernel_h.getTopPadding(), kernel_h.getBottomPadding()); this.kernel_h = kernel_h; this.kernel_v = kernel_v; // // At this point both kernels should be of same width & height // so it's enough to get the information from one of them // kw = kernel_h.getWidth(); kh = kernel_h.getHeight(); // // center of kernels // kx = kw/2; ky = kh/2; // // Get the kernel data into local double arrays // kernel_h_data = kernel_h.getKernelData(); kernel_v_data = kernel_v.getKernelData(); int count = kw * kh; dbl_kh_data = new double[count]; dbl_kv_data = new double[count]; for (int i = 0; i < count; i++) { dbl_kh_data[i] = (double)kernel_h_data[i]; dbl_kv_data[i] = (double)kernel_v_data[i]; } } /** * Performs Gradient on a specified rectangle. The sources are cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; Rectangle srcRect = mapDestRect(destRect, 0); int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest); MediaLibAccessor srcAccessor = new MediaLibAccessor(source, srcRect, formatTag); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest, destRect, formatTag); int numBands = getSampleModel().getNumBands(); mediaLibImage[] srcML = srcAccessor.getMediaLibImages(); mediaLibImage[] dstML = dstAccessor.getMediaLibImages(); for (int i = 0; i < dstML.length; i++) { switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: Image.GradientMxN(dstML[i], srcML[i], dbl_kh_data, dbl_kv_data, kw, kh, kx, ky, ((1 << numBands)-1) , Constants.MLIB_EDGE_DST_NO_WRITE); break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: Image.GradientMxN_Fp(dstML[i], srcML[i], dbl_kh_data, dbl_kv_data, kw, kh, kx, ky, ((1 << numBands)-1) , Constants.MLIB_EDGE_DST_NO_WRITE); break; default: String className = this.getClass().getName(); throw new RuntimeException(JaiI18N.getString("Generic2")); } } if (dstAccessor.isDataCopy()) { dstAccessor.copyDataToRaster(); } } // public static OpImage createTestImage(OpImageTester oit) { // float data_h[] = {-1.0f, -2.0f, -1.0f, // 0.0f, 0.0f, 0.0f, // 1.0f, 2.0f, 1.0f}; // float data_v[] = {-1.0f, 0.0f, 1.0f, // -2.0f, 0.0f, 2.0f, // -1.0f, 0.0f, 1.0f}; // KernelJAI kern_h = new KernelJAI(3,3,data_h); // KernelJAI kern_v = new KernelJAI(3,3,data_v); // return new MlibGradientOpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource()), // kern_h, kern_v); // } // public static void main (String args[]) { // String classname = "com.sun.media.jai.mlib.MlibGradientOpImage"; // OpImageTester.performDiagnostics(classname,args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibConvolveNxNOpImage.java0000644000175000017500000002322210300230230027646 0ustar mathieumathieu/* * $RCSfile: MlibConvolveNxNOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.4 $ * $Date: 2005-08-16 00:17:28 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; import javax.media.jai.OpImage; import java.util.Map; import com.sun.medialib.mlib.*; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class to perform convolution on a source image. * *

    This class implements a convolution operation. Convolution is a * spatial operation that computes each output sample by multiplying * elements of a kernel with the samples surrounding a particular * source sample. * *

    For each destination sample, the kernel is rotated 180 degrees * and its "key element" is placed over the source pixel corresponding * with the destination pixel. The kernel elements are multiplied * with the source pixels under them, and the resulting products are * summed together to produce the destination sample value. * *

    Example code for the convolution operation on a single sample * dst[x][y] is as follows, assuming the kernel is of size M rows x N * columns and has already been rotated through 180 degrees. The * kernel's key element is located at position (xKey, yKey): * *

     * dst[x][y] = 0;
     * for (int i = -xKey; i < M - xKey; i++) {
     *     for (int j = -yKey; j < N - yKey; j++) {
     *         dst[x][y] += src[x + i][y + j] * kernel[xKey + i][yKey + j];
     *     }
     * }
     * 
    * *

    Convolution, or any neighborhood operation, leaves a band of * pixels around the edges undefined, e.g., for a 3x3 kernel, only * four kernel elements and four source pixels contribute to the * destination pixel located at (0,0). Such pixels are not includined * in the destination image, unless a non-null BorderExtender is provided. * *

    The Kernel cannot be bigger in any dimension than the image data. * * @see KernelJAI */ final class MlibConvolveNxNOpImage extends AreaOpImage { /** * The kernel with which to do the convolve operation. */ protected KernelJAI kernel; /** Kernel variables. */ private int kw, kh; float kData[]; double doublekData[]; int intkData[]; int shift = -1; /** * Creates a MlibConvolveNxNOpImage given the image source and * pre-rotated convolution kernel. The image dimensions are * derived from the source image. The tile grid layout, * SampleModel, and ColorModel may optionally be specified by an * ImageLayout object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * or null. If null, a default cache will be used. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param kernel the pre-rotated convolution KernelJAI. */ public MlibConvolveNxNOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, KernelJAI kernel) { super(source, layout, config, true, extender, kernel.getLeftPadding(), kernel.getRightPadding(), kernel.getTopPadding(), kernel.getBottomPadding()); this.kernel = kernel; kw = kernel.getWidth(); kh = kernel.getHeight(); // kx and ky are centered in AreaOpImage, not here. kData = kernel.getKernelData(); int count = kw*kh; // A little inefficient but figuring out what datatype // mediaLibAccessor will want is tricky. intkData = new int[count]; doublekData = new double[count]; for (int i = 0; i < count; i++) { doublekData[i] = (double)kData[i]; } } private synchronized void setShift(int formatTag) { if (shift == -1) { int mediaLibDataType = MediaLibAccessor.getMediaLibDataType(formatTag); shift = Image.ConvKernelConvert(intkData, doublekData, kw,kh, mediaLibDataType); } } /** * Performs convolution on a specified rectangle. The sources are * cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; Rectangle srcRect = mapDestRect(destRect, 0); int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest); MediaLibAccessor srcAccessor = new MediaLibAccessor(source,srcRect,formatTag,true); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest,destRect,formatTag,true); int numBands = getSampleModel().getNumBands(); mediaLibImage[] srcML = srcAccessor.getMediaLibImages(); mediaLibImage[] dstML = dstAccessor.getMediaLibImages(); for (int i = 0; i < dstML.length; i++) { switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: if (shift == -1) { setShift(formatTag); } if (kw == 2) { Image.Conv2x2(dstML[i], srcML[i], intkData, shift, ((1 << numBands)-1) , Constants.MLIB_EDGE_DST_NO_WRITE); } else if (kw == 3) { Image.Conv3x3(dstML[i], srcML[i], intkData, shift, ((1 << numBands)-1) , Constants.MLIB_EDGE_DST_NO_WRITE); } else if (kw == 4) { Image.Conv4x4(dstML[i], srcML[i], intkData, shift, ((1 << numBands)-1) , Constants.MLIB_EDGE_DST_NO_WRITE); } else if (kw == 5) { Image.Conv5x5(dstML[i], srcML[i], intkData, shift, ((1 << numBands)-1) , Constants.MLIB_EDGE_DST_NO_WRITE); } else if (kw == 7) { Image.Conv7x7(dstML[i], srcML[i], intkData, shift, ((1 << numBands)-1) , Constants.MLIB_EDGE_DST_NO_WRITE); } break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: if (kw == 2) { Image.Conv2x2_Fp(dstML[i], srcML[i], doublekData, ((1 << numBands)-1) , Constants.MLIB_EDGE_DST_NO_WRITE); } else if (kw == 3) { Image.Conv3x3_Fp(dstML[i], srcML[i], doublekData, ((1 << numBands)-1) , Constants.MLIB_EDGE_DST_NO_WRITE); } else if (kw == 4) { Image.Conv4x4_Fp(dstML[i], srcML[i], doublekData, ((1 << numBands)-1) , Constants.MLIB_EDGE_DST_NO_WRITE); } else if (kw == 5) { Image.Conv5x5_Fp(dstML[i], srcML[i], doublekData, ((1 << numBands)-1) , Constants.MLIB_EDGE_DST_NO_WRITE); } else if (kw == 7) { Image.Conv7x7_Fp(dstML[i], srcML[i], doublekData, ((1 << numBands)-1) , Constants.MLIB_EDGE_DST_NO_WRITE); } break; default: String className = this.getClass().getName(); throw new RuntimeException(JaiI18N.getString("Generic2")); } } if (dstAccessor.isDataCopy()) { dstAccessor.copyDataToRaster(); } } // public static OpImage createTestImage(OpImageTester oit) { // float data[] = {0.05f,0.10f,0.05f, // 0.10f,0.40f,0.10f, // 0.05f,0.10f,0.05f}; // KernelJAI kJAI = new KernelJAI(3,3,1,1,data); // return new MlibConvolve3x3OpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource()), // kJAI); // } // public static void main (String args[]) { // String classname = "com.sun.media.jai.mlib.MlibConvolve3x3OpImage"; // OpImageTester.performDiagnostics(classname,args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibAddConstOpImage.java0000644000175000017500000001123410203035544027143 0ustar mathieumathieu/* * $RCSfile: MlibAddConstOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:48 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PointOpImage; import java.util.Map; import com.sun.media.jai.util.ImageUtil; /// import com.sun.media.jai.test.OpImageTester; import com.sun.medialib.mlib.*; /** * An OpImage class that copies an image from source to dest. * */ final class MlibAddConstOpImage extends PointOpImage { private double[] constants; /** * Constructs an MlibAddConstOpImage. The image dimensions are copied * from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param layout an ImageLayout optionally containing the tile * grid layout, SampleModel, and ColorModel, or null. */ public MlibAddConstOpImage(RenderedImage source, Map config, ImageLayout layout, double[] constants) { super(source, layout, config, true); this.constants = MlibUtils.initConstants(constants, getSampleModel().getNumBands()); // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * Add the pixel values of a rectangle with a given constant. * The sources are cobbled. * * @param sources an array of sources, guarantee to provide all * necessary source data for computing the rectangle. * @param dest a tile that contains the rectangle to be computed. * @param destRect the rectangle within this OpImage to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; Rectangle srcRect = mapDestRect(destRect, 0); int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest); MediaLibAccessor srcAccessor = new MediaLibAccessor(source,srcRect,formatTag); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest,destRect,formatTag); mediaLibImage[] srcML = srcAccessor.getMediaLibImages(); mediaLibImage[] dstML = dstAccessor.getMediaLibImages(); switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: int[] constInt = new int[constants.length]; for (int i = 0; i < constants.length; i++) { constInt[i] = ImageUtil.clampRoundInt(constants[i]); } for (int i = 0; i < dstML.length; i++) { int mlconstants[] = dstAccessor.getIntParameters(i, constInt); Image.ConstAdd(dstML[i], srcML[i], mlconstants); } break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: for (int i = 0; i < dstML.length; i++) { double[] mlconstants = dstAccessor.getDoubleParameters(i, constants); Image.ConstAdd_Fp(dstML[i], srcML[i], mlconstants); } break; default: String className = this.getClass().getName(); throw new RuntimeException(className + JaiI18N.getString("Generic2")); } if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } // public static OpImage createTestImage(OpImageTester oit) { // double[] consts = { 5, 5, 5 }; // return new MlibAddConstOpImage(oit.getSource(), null, // new ImageLayout(oit.getSource()), // consts); // } // // Calls a method on OpImage that uses introspection, to make this // // class, discover it's createTestImage() call, call it and then // // benchmark the performance of the created OpImage chain. // public static void main (String args[]) { // String classname = "com.sun.media.jai.mlib.MlibAddConstOpImage"; // OpImageTester.performDiagnostics(classname,args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibHistogramRIF.java0000644000175000017500000001020710341166103026476 0ustar mathieumathieu/* * $RCSfile: MlibHistogramRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-11-23 22:25:07 $ * $State: Exp $ */package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.JAI; import javax.media.jai.ROI; import javax.media.jai.util.ImagingListener; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "Histogram" operation in the * rendered image layer. * * @since EA2 * @see javax.media.jai.operator.HistogramDescriptor * @see MlibHistogramOpImage */ public class MlibHistogramRIF implements RenderedImageFactory { /** Constructor. */ public MlibHistogramRIF() {} /** * Creates a new instance of MlibHistogramOpImage * in the rendered layer. Any image layout information in * RenderingHints is ignored. * This method satisfies the implementation of RIF. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { // Return null of not mediaLib-compatible. if(!MediaLibAccessor.isMediaLibCompatible(args)) { return null; } // Return null if source data type is floating point. RenderedImage src = args.getRenderedSource(0); int dataType = src.getSampleModel().getDataType(); if(dataType == DataBuffer.TYPE_FLOAT || dataType == DataBuffer.TYPE_DOUBLE) { return null; } // Return null if ROI is non-null and not equals to source bounds. ROI roi = (ROI)args.getObjectParameter(0); if(roi != null && !roi.equals(new Rectangle(src.getMinX(), src.getMinY(), src.getWidth(), src.getHeight()))) { return null; } // Get the non-ROI parameters. int xPeriod = args.getIntParameter(1); int yPeriod = args.getIntParameter(2); int[] numBins = (int[])args.getObjectParameter(3); double[] lowValueFP = (double[])args.getObjectParameter(4); double[] highValueFP = (double[])args.getObjectParameter(5); // Return null if lowValueFP or highValueFP is out of dataType range. int minPixelValue; int maxPixelValue; switch (dataType) { case DataBuffer.TYPE_SHORT: minPixelValue = Short.MIN_VALUE; maxPixelValue = Short.MAX_VALUE; break; case DataBuffer.TYPE_USHORT: minPixelValue = 0; maxPixelValue = -((int)Short.MIN_VALUE) + Short.MAX_VALUE; break; case DataBuffer.TYPE_INT: minPixelValue = Integer.MIN_VALUE; maxPixelValue = Integer.MAX_VALUE; break; case DataBuffer.TYPE_BYTE: default: minPixelValue = 0; maxPixelValue = -((int)Byte.MIN_VALUE) + Byte.MAX_VALUE; break; } for (int i = 0; i < lowValueFP.length; i++) { if (lowValueFP[i] < minPixelValue || lowValueFP[i] > maxPixelValue) { return null; } } for (int i = 0; i < highValueFP.length; i++) { if (highValueFP[i] <= minPixelValue || highValueFP[i] > (maxPixelValue + 1)) { return null; } } MlibHistogramOpImage op = null; try { op = new MlibHistogramOpImage(src, xPeriod, yPeriod, numBins, lowValueFP, highValueFP); } catch (Exception e) { ImagingListener listener = ImageUtil.getImagingListener(hints); String message = JaiI18N.getString("MlibHistogramRIF0"); listener.errorOccurred(message, e, this, false); } return op; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibExtremaRIF.java0000644000175000017500000000374310203035544026156 0ustar mathieumathieu/* * $RCSfile: MlibExtremaRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:56 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ROI; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "Extrema" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.ExtremaDescriptor * @see MlibExtremaOpImage * * @since EA4 * */ public class MlibExtremaRIF implements RenderedImageFactory { /** Constructor. */ public MlibExtremaRIF() {} /** * Creates a new instance of MlibExtremaOpImage in * the rendered image mode. * * @param args The source image and the parameters. * @param hints Rendering hints are ignored. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { if (!MediaLibAccessor.isMediaLibCompatible(args)) { return null; } RenderedImage source = args.getRenderedSource(0); ROI roi = (ROI)args.getObjectParameter(0); int xPeriod = args.getIntParameter(1); int yPeriod = args.getIntParameter(2); boolean saveLocations = ((Boolean)args.getObjectParameter(3)).booleanValue(); int maxRuns = args.getIntParameter(4); int xStart = source.getMinX(); // default values int yStart = source.getMinY(); int maxWidth = source.getWidth(); int maxHeight = source.getHeight(); if (roi != null && !roi.contains(xStart, yStart, maxWidth, maxHeight)) { return null; } return new MlibExtremaOpImage(source, roi, xStart, yStart, xPeriod, yPeriod, saveLocations, maxRuns); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibBinarizeOpImage.java0000644000175000017500000001413210203035544027207 0ustar mathieumathieu/* * $RCSfile: MlibBinarizeOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:51 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.image.RenderedImage; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import java.util.Map; import java.awt.Rectangle; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.ColorModel; import java.awt.image.PackedColorModel; import java.awt.image.MultiPixelPackedSampleModel; import javax.media.jai.ImageLayout; import javax.media.jai.PlanarImage; import javax.media.jai.PointOpImage; import com.sun.medialib.mlib.*; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.util.JDKWorkarounds; /** * A mediaLib class extending PointOpImage to * binarize images to bileval images. *

     *   dst(i,j) = src(i,j) > threshvalue? 1: 0;
     *
     * @see com.sun.media.jai.operator.BinarizeOpImage
     * @see javax.media.jai.operator.BinarizeDescriptor
     *
     */
    class MlibBinarizeOpImage extends PointOpImage {
    
      /** 
       * threshold value
       */
        private double thresh;
    
        /**
         * Constructs a MlibBinarizeOpImage 
         * from a RenderedImage source, thresh value.
         * 
         * @param source a RenderedImage.
         * @param layout an ImageLayout optionally containing
         *        the tile grid layout, SampleModel, and
         *        ColorModel, or null.
    
         *        from this OpImage, or null.  If
         *        null, no caching will be performed.
    
         * @param thresh Threshold value.
         *
         * @throws IllegalArgumentException if combining the
         *         source bounds with the layout parameter results in negative
         *         output width or height.
         */
        public MlibBinarizeOpImage(RenderedImage source,
    			       ImageLayout layout,
    			       Map config,
    			       double thresh){
    
            super(source,
                  layoutHelper(source, layout, config),
                  config,
                  true);
    
    	this.thresh = thresh;
        }
    
    
        // set the OpImage's SM to be MultiPixelPackedSampleModel
        private static ImageLayout layoutHelper(RenderedImage source,
                                                ImageLayout il,
                                                Map config) {
    
            ImageLayout layout = (il == null) ?
                new ImageLayout() : (ImageLayout)il.clone();
    
            SampleModel sm = layout.getSampleModel(source);
            if(!ImageUtil.isBinary(sm)) {
                sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE,
                                                     layout.getTileWidth(source),
                                                     layout.getTileHeight(source),
                                                     1);
                layout.setSampleModel(sm);
            }
               
            ColorModel cm = layout.getColorModel(null);
            if(cm == null ||
               !JDKWorkarounds.areCompatibleDataModels(sm, cm)) {
                layout.setColorModel(ImageUtil.getCompatibleColorModel(sm,
                                                                       config));
            }
    
            return layout;
        }
    
        /**
         * 
         * @param sources   an array of sources, guarantee to provide all
         *                  necessary source data for computing the rectangle.
         * @param dest      a tile that contains the rectangle to be computed.
         * @param destRect  the rectangle within this OpImage to be processed.
         */
        protected void computeRect(Raster[] sources,
                                   WritableRaster dest,
                                   Rectangle destRect) {
    
    	Raster source = sources[0];
    
    	Rectangle srcRect = mapDestRect(destRect,0);
    
            // Hack: derive the source format tag as if it was writing to
            // a destination with the same layout as itself.
            int sourceFormatTag =
                MediaLibAccessor.findCompatibleTag(sources, source);
    
            // Hard-code the destination format tag as we know that the image
            // has a binary layout.
            int destFormatTag =
                dest.getSampleModel().getDataType() |
                MediaLibAccessor.BINARY |
                MediaLibAccessor.UNCOPIED;
    
            MediaLibAccessor srcAccessor =
                new MediaLibAccessor(source, srcRect, sourceFormatTag, false);
            MediaLibAccessor dstAccessor =
                new MediaLibAccessor(dest, destRect, destFormatTag, true);
    
    	mediaLibImage srcML[], dstML[];
    
            switch (srcAccessor.getDataType()) {
            case DataBuffer.TYPE_BYTE:
            case DataBuffer.TYPE_USHORT:
            case DataBuffer.TYPE_SHORT:
            case DataBuffer.TYPE_INT:
                srcML = srcAccessor.getMediaLibImages();
                dstML = dstAccessor.getMediaLibImages();
                for (int i = 0 ; i < dstML.length; i++) {
                  //XXX
                  //medialib version is not inclusive
                  //pure java version is inclusive
                  //thus medialib version is the same
                  //as the java version with thresh value decremented by 1
                    Image.Thresh1(dstML[i],
    			      srcML[i],
    			      new int[]{(int)thresh-1},   // decrease by 1
    			      new int[]{1},
    			      new int[]{0});
                }
                break;
            case DataBuffer.TYPE_FLOAT:
            case DataBuffer.TYPE_DOUBLE:
                srcML = srcAccessor.getMediaLibImages();
                dstML = dstAccessor.getMediaLibImages();
                for (int i = 0 ; i < dstML.length; i++) {
                    Image.Thresh1_Fp(dstML[i],
    					       srcML[i],
    					       new double[]{thresh},
    					       new double[]{1.0D},
    					       new double[]{0.0D});
                }
     	    break;
            default:
                String className = this.getClass().getName();
                throw new RuntimeException(JaiI18N.getString("Generic2"));
            }
    
            if (dstAccessor.isDataCopy()) {
                dstAccessor.clampDataArrays();
                dstAccessor.copyDataToRaster();
            }
        }
    
    }
    jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibDFTOpImage.java0000644000175000017500000002516310203035544026067 0ustar  mathieumathieu/*
     * $RCSfile: MlibDFTOpImage.java,v $
     *
     * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
     *
     * Use is subject to license terms.
     *
     * $Revision: 1.1 $
     * $Date: 2005-02-11 04:55:53 $
     * $State: Exp $
     */
    package com.sun.media.jai.mlib;
    
    import java.awt.Rectangle;
    import java.awt.geom.Point2D;
    import java.awt.image.BandedSampleModel;
    import java.awt.image.ColorModel;
    import java.awt.image.ComponentSampleModel;
    import java.awt.image.DataBuffer;
    import java.awt.image.Raster;
    import java.awt.image.RenderedImage;
    import java.awt.image.SampleModel;
    import java.awt.image.WritableRaster;
    import java.util.Map;
    import javax.media.jai.EnumeratedParameter;
    import javax.media.jai.ImageLayout;
    import javax.media.jai.JAI;
    import javax.media.jai.RasterFactory;
    import javax.media.jai.UntiledOpImage;
    import javax.media.jai.operator.DFTDescriptor;
    import com.sun.media.jai.util.JDKWorkarounds;
    import com.sun.media.jai.util.MathJAI;
    import com.sun.medialib.mlib.*;
    
    /**
     * An OpImage class that performs a "DFT" and "IDFT" operations.
     *
     */
    final class MlibDFTOpImage extends UntiledOpImage {
        /** The mediaLib mode of the Fourier transform. */
        private int DFTMode;
    
        /**
         * Override the dimension specification for the destination such that it
         * has width and height which are equal to non-negative powers of 2.
         */
        private static ImageLayout layoutHelper(ImageLayout layout,
                                                RenderedImage source,
                                                EnumeratedParameter dataNature) {
            // Set the complex flags for source and destination.
            boolean isComplexSource =
                !dataNature.equals(DFTDescriptor.REAL_TO_COMPLEX);
            boolean isComplexDest =
                !dataNature.equals(DFTDescriptor.COMPLEX_TO_REAL);
    
            // Get the number of source bands.
            SampleModel srcSampleModel = source.getSampleModel();
            int numSourceBands = srcSampleModel.getNumBands();
    
            // Check for mediaLib support of this source.
            if((isComplexSource && numSourceBands != 2) ||
               (!isComplexSource && numSourceBands != 1)) {
                // This should never occur due to checks in
                // MlibDFTRIF and MlibIDFTRIF.
                throw new RuntimeException(JaiI18N.getString("MlibDFTOpImage0"));
            }
    
            // Create an ImageLayout or clone the one passed in.
            ImageLayout il = layout == null ?
                new ImageLayout() : (ImageLayout)layout.clone();
    
            // Force the origin to coincide with that of the source.
            il.setMinX(source.getMinX());
            il.setMinY(source.getMinY());
    
            // Recalculate the non-unity dimensions to be a positive power of 2.
            // XXX This calculation should not be effected if an implementation
            // of the FFT which supports arbitrary dimensions is used.
            int currentWidth = il.getWidth(source);
            int currentHeight = il.getHeight(source);
            int newWidth;
            int newHeight;
            if(currentWidth == 1 && currentHeight == 1) {
                newWidth = newHeight = 1;
            } else if(currentWidth == 1 && currentHeight > 1) {
                newWidth = 1;
                newHeight = MathJAI.nextPositivePowerOf2(currentHeight);
            } else if(currentWidth > 1 && currentHeight == 1) {
                newWidth = MathJAI.nextPositivePowerOf2(currentWidth);
                newHeight = 1;
            } else { // Neither dimension equal to unity.
                newWidth = MathJAI.nextPositivePowerOf2(currentWidth);
                newHeight = MathJAI.nextPositivePowerOf2(currentHeight);
            }
            il.setWidth(newWidth);
            il.setHeight(newHeight);
    
            // Initialize the SampleModel creation flag.
            boolean createNewSampleModel = false;
    
            // Determine the number of required bands.
            int requiredNumBands = numSourceBands;
            if(isComplexSource && !isComplexDest) {
                requiredNumBands /= 2;
            } else if(!isComplexSource && isComplexDest) {
                requiredNumBands *= 2;
            }
    
            // Set the number of bands.
            SampleModel sm = il.getSampleModel(source);
            int numBands = sm.getNumBands();
            if(numBands != requiredNumBands) {
                numBands = requiredNumBands;
                createNewSampleModel = true;
            }
    
            // Force the image to contain floating point data.
            int dataType = sm.getTransferType();
            if(dataType != DataBuffer.TYPE_FLOAT &&
               dataType != DataBuffer.TYPE_DOUBLE) {
                dataType = DataBuffer.TYPE_FLOAT;
                createNewSampleModel = true;
            }
    
            // Create a new SampleModel for the destination if necessary.
            if(createNewSampleModel) {
                int[] bandOffsets = new int[numBands];
                // Force the band offsets to be monotonically increasing as
                // mediaLib expects the real part to be in band 0 and the
                // imaginary part in band 1.
                for(int b = 0; b < numBands; b++) {
                    bandOffsets[b] = b;
                }
    
                int lineStride = newWidth*numBands;
                sm = RasterFactory.createPixelInterleavedSampleModel(dataType,
                                                                     newWidth,
                                                                     newHeight,
                                                                     numBands,
                                                                     lineStride,
                                                                     bandOffsets);
                il.setSampleModel(sm);
    
                // Clear the ColorModel mask if needed.
                ColorModel cm = il.getColorModel(null);
                if(cm != null &&
                   !JDKWorkarounds.areCompatibleDataModels(sm, cm)) {
                    // Clear the mask bit if incompatible.
                    il.unsetValid(ImageLayout.COLOR_MODEL_MASK);
                }
            }
    
            return il;
        }
    
        /**
         * Constructs an MlibDFTOpImage.
         *
         * @param source    a RenderedImage.
    
         * @param layout    an ImageLayout optionally containing the tile
         *                  grid layout, SampleModel, and ColorModel, or null.
         * @param dataNature the nature of the source/destination data transform
         *                   (real->complex, etc.).
         * @param isForward whether the transform is forward (negative exponent).
         * @param scaleType the scaling type of the transform.
         */
        public MlibDFTOpImage(RenderedImage source,
                              Map config,
                              ImageLayout layout,
                              EnumeratedParameter dataNature,
                              boolean isForward,
                              EnumeratedParameter scaleType) {
            super(source, config, layoutHelper(layout, source, dataNature));
    
            if(scaleType.equals(DFTDescriptor.SCALING_NONE)) {
                DFTMode = isForward ?
                    Image.MLIB_DFT_SCALE_NONE :
                    Image.MLIB_IDFT_SCALE_NONE;
            } else if(scaleType.equals(DFTDescriptor.SCALING_UNITARY)) {
                DFTMode = isForward ?
                    Image.MLIB_DFT_SCALE_SQRT :
                    Image.MLIB_IDFT_SCALE_SQRT;
            } else if(scaleType.equals(DFTDescriptor.SCALING_DIMENSIONS)) {
                DFTMode = isForward ?
                    Image.MLIB_DFT_SCALE_MXN :
                    Image.MLIB_IDFT_SCALE_MXN;
            } else {
                // This should never occur due to checks in DFTDescriptor and
                // IDFTDescriptor.
                throw new RuntimeException(JaiI18N.getString("MlibDFTOpImage1"));
            }
        }
    
        /*
         * This method is required as a workaround to the way MediaLibAccessor
         * works. MediaLibAccessor handles ComponentSampleModels differently
         * from other SampleModels. It does a test of whether the data are
         * consecutive without respect to the band ordering. This assumes that
         * the operation is independent of band ordering. This is not true for
         * the Fourier transform as the real and imaginary parts are in bands
         * 1 and 2, respectively.
         */
        public static boolean isAcceptableSampleModel(SampleModel sm) {
            if(!(sm instanceof ComponentSampleModel)) {
                return true;
            }
    
            ComponentSampleModel csm = (ComponentSampleModel)sm;
    
            int[] bandOffsets = csm.getBandOffsets();
    
            if(bandOffsets.length == 2 &&
               bandOffsets[1] == bandOffsets[0] + 1) {
                return true;
            }
    
            return false;
        }
    
        /**
         * Computes the source point corresponding to the supplied point.
         *
         * @param destPt the position in destination image coordinates
         * to map to source image coordinates.
         *
         * @return null.
         *
         * @throws IllegalArgumentException if destPt is
         * null.
         *
         * @since JAI 1.1.2
         */
        public Point2D mapDestPoint(Point2D destPt) {
            if (destPt == null) {
                throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
            }
    
            return null;
        }
    
        /**
         * Computes the destination point corresponding to the supplied point.
         *
         * @return null.
         *
         * @throws IllegalArgumentException if sourcePt is
         * null.
         *
         * @since JAI 1.1.2
         */
        public Point2D mapSourcePoint(Point2D sourcePt) {
            if (sourcePt == null) {
                throw new IllegalArgumentException(JaiI18N.getString("Generic0"));
            }
    
            return null;
        }
    
        /**
         * Calculate the destination image from the source image.
         *
         * @param source The source Raster; should be the whole image.
         * @param dest The destination WritableRaster; should be the whole image.
         * @param destRect The destination Rectangle; should equal the destination
         * image bounds.
         */
        protected void computeImage(Raster[] sources,
                                    WritableRaster dest,
                                    Rectangle destRect) {
            Raster source = sources[0];
    
            int formatTag =
                MediaLibAccessor.findCompatibleTag(new Raster[] {source}, dest);
    
            MediaLibAccessor srcAccessor =
                new MediaLibAccessor(source, mapDestRect(destRect, 0), formatTag);
            MediaLibAccessor dstAccessor =
                new MediaLibAccessor(dest, destRect, formatTag);
    
            mediaLibImage[] srcML = srcAccessor.getMediaLibImages();
            mediaLibImage[] dstML = dstAccessor.getMediaLibImages();
    
            for (int i = 0; i < dstML.length; i++) {
                Image.FourierTransform(dstML[i], srcML[i],
                                                       DFTMode);
            }
    
            if (dstAccessor.isDataCopy()) {
                dstAccessor.clampDataArrays();
                dstAccessor.copyDataToRaster();
            }
        }
    }
    jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibMosaicRIF.java0000644000175000017500000000550110203035544025756 0ustar  mathieumathieu/*
     * $RCSfile: MlibMosaicRIF.java,v $
     *
     * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
     *
     * Use is subject to license terms.
     *
     * $Revision: 1.1 $
     * $Date: 2005-02-11 04:56:01 $
     * $State: Exp $
     */package com.sun.media.jai.mlib;
    
    import java.awt.RenderingHints;
    import java.awt.image.DataBuffer;
    import java.awt.image.RenderedImage;
    import java.awt.image.SampleModel;
    import java.awt.image.renderable.ParameterBlock;
    import java.awt.image.renderable.RenderedImageFactory;
    import java.util.Vector;
    import javax.media.jai.ImageLayout;
    import javax.media.jai.PlanarImage;
    import javax.media.jai.ROI;
    import javax.media.jai.operator.MosaicType;
    import com.sun.media.jai.opimage.RIFUtil;
    
    /**
     * A RIF supporting the "Mosaic" operation in the rendered
     * image layer.
     *
     * @since JAI 1.1.2
     * @see javax.media.jai.operator.MosaicDescriptor
     */
    public class MlibMosaicRIF implements RenderedImageFactory {
    
        /** Constructor. */
        public MlibMosaicRIF() {}
    
        /**
         * Renders a "Mosaic" operation node.
         */
        public RenderedImage create(ParameterBlock paramBlock,
                                    RenderingHints renderHints) {
            // Get ImageLayout from renderHints if any.
            ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints);
    
            // Return if not mediaLib-compatible.
            if(!MediaLibAccessor.isMediaLibCompatible(paramBlock, layout) ||
               !MediaLibAccessor.hasSameNumBands(paramBlock, layout)) {
                return null;
            }
    
            // Get sources.
            Vector sources = paramBlock.getSources();
    
            // Get target SampleModel.
            SampleModel targetSM = null;
            if(sources.size() > 0) {
                targetSM = ((RenderedImage)sources.get(0)).getSampleModel();
            } else if(layout != null &&
                      layout.isValid(ImageLayout.SAMPLE_MODEL_MASK)) {
                targetSM = layout.getSampleModel(null);
            }
    
            if(targetSM != null) {
                // Return if target data type is floating point. Other more
                // extensive type checking is done in MosaicOpImage constructor.
                int dataType = targetSM.getDataType();
                if(dataType == DataBuffer.TYPE_FLOAT ||
                   dataType == DataBuffer.TYPE_DOUBLE) {
                    return null;
                }
            }
    
            return
                new MlibMosaicOpImage(sources,
                                      layout,
                                      renderHints,
                                      (MosaicType)paramBlock.getObjectParameter(0),
                                      (PlanarImage[])paramBlock.getObjectParameter(1),
                                      (ROI[])paramBlock.getObjectParameter(2),
                                      (double[][])paramBlock.getObjectParameter(3),
                                      (double[])paramBlock.getObjectParameter(4));
        }
    }
    jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibScaleRIF.java0000644000175000017500000001205010203035544025567 0ustar  mathieumathieu/*
     * $RCSfile: MlibScaleRIF.java,v $
     *
     * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
     *
     * Use is subject to license terms.
     *
     * $Revision: 1.1 $
     * $Date: 2005-02-11 04:56:05 $
     * $State: Exp $
     */
    package com.sun.media.jai.mlib;
    import java.awt.RenderingHints;
    import java.awt.geom.AffineTransform;
    import java.awt.image.DataBuffer;
    import java.awt.image.MultiPixelPackedSampleModel;
    import java.awt.image.RenderedImage;
    import java.awt.image.SampleModel;
    import java.awt.image.renderable.RenderedImageFactory;
    import java.awt.image.renderable.ParameterBlock;
    import javax.media.jai.BorderExtender;
    import javax.media.jai.ImageLayout;
    import javax.media.jai.Interpolation;
    import javax.media.jai.InterpolationNearest;
    import javax.media.jai.InterpolationBilinear;
    import javax.media.jai.InterpolationBicubic;
    import javax.media.jai.InterpolationBicubic2;
    import javax.media.jai.InterpolationTable;
    import java.util.Map;
    import com.sun.media.jai.opimage.RIFUtil;
    import com.sun.media.jai.opimage.TranslateIntOpImage;
    
    /**
     * A RIF supporting the "Scale" operation in the
     * rendered image mode using MediaLib.
     *
     * @see javax.media.jai.operator.ScaleDescriptor
     * @see MlibScaleNearestOpImage
     * @see MlibScaleBilinearOpImage
     * @see MlibScaleBicubicOpImage
     *
     */
    public class MlibScaleRIF implements RenderedImageFactory {
    
        private static final float TOLERANCE = 0.01F;
    
        /** Constructor. */
        public MlibScaleRIF() {}
    
        /**
         * Creates a new instance of MlibScaleOpImage in
         * the rendered image mode.
         *
         * @param args  The source image, scale factors,
         *              and the Interpolation.
         * @param hints  May contain rendering hints and destination image layout.
         */
        public RenderedImage create(ParameterBlock args,
                                    RenderingHints hints) {
            /* Get ImageLayout and TileCache from RenderingHints. */
            ImageLayout layout = RIFUtil.getImageLayoutHint(hints);
            
            Interpolation interp = (Interpolation)args.getObjectParameter(4);
    
    	RenderedImage source = args.getRenderedSource(0);
    
            if (!MediaLibAccessor.isMediaLibCompatible(args, layout) ||
                !MediaLibAccessor.hasSameNumBands(args, layout) ||
    	    // Medialib cannot deal with source image having tiles with any
    	    // dimension greater than or equal to 32768
    	    source.getTileWidth() >= 32768 || 
    	    source.getTileHeight() >= 32768) {
                return null;
            }
    
            SampleModel sm = source.getSampleModel();
            boolean isBilevel = (sm instanceof MultiPixelPackedSampleModel) &&
                (sm.getSampleSize(0) == 1) &&
                (sm.getDataType() == DataBuffer.TYPE_BYTE || 
                 sm.getDataType() == DataBuffer.TYPE_USHORT || 
                 sm.getDataType() == DataBuffer.TYPE_INT);
            if (isBilevel) {
                // Let Java code handle it, reformatting is slower
                return null;
            }
    
            // Get BorderExtender from hints if any.
            BorderExtender extender = RIFUtil.getBorderExtenderHint(hints);
    
            float xScale = args.getFloatParameter(0);
            float yScale = args.getFloatParameter(1);
            float xTrans = args.getFloatParameter(2);
            float yTrans = args.getFloatParameter(3);
    
    	// Check and see if we are scaling by 1.0 in both x and y and no
            // translations. If so call the copy operation.
    	if (xScale == 1.0F && yScale == 1.0F && 
    	    xTrans == 0.0F && yTrans == 0.0F) {
    	    return new MlibCopyOpImage(source, hints, layout);
    	}
    
    	// Check to see whether the operation specified is a pure 
    	// integer translation. If so call translate
    	if (xScale == 1.0F && yScale == 1.0F &&
    	    (Math.abs(xTrans - (int)xTrans) < TOLERANCE) &&
    	    (Math.abs(yTrans - (int)yTrans) < TOLERANCE) &&
    	    layout == null) { // TranslateIntOpImage can't deal with ImageLayout hint
    	    /* It's a integer translate. */
                return new TranslateIntOpImage(source,
    					   hints,
    					   (int)xTrans,
    					   (int)yTrans);
    	}
    
    	if (interp instanceof InterpolationNearest)  {
    	    return new MlibScaleNearestOpImage(source, extender,
                                                   hints, layout,
    					       xScale, yScale,
    					       xTrans, yTrans,
    					       interp);
            } else if (interp instanceof InterpolationBilinear) {
    	    return new MlibScaleBilinearOpImage(source,
                                                    extender, hints, layout,
    						xScale, yScale,
    						xTrans, yTrans,
    						interp);
            } else if (interp instanceof InterpolationBicubic ||
    		   interp instanceof InterpolationBicubic2) {
    	    return new MlibScaleBicubicOpImage(source,
                                                   extender, hints, layout,
    					       xScale, yScale,
    					       xTrans, yTrans,
    					       interp);
            } else if (interp instanceof InterpolationTable) {
    	    return new MlibScaleTableOpImage(source, extender, hints, layout,
    					     xScale, yScale,
    					     xTrans, yTrans,
    					     interp);
    	} else {
    	    /* Other kinds of interpolation cannot be handled via mlib. */
    	    return null;
            }
        }
    }
    jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/com.sun.media.jai.mlib.properties0000644000175000017500000000345610203035544031004 0ustar  mathieumathieu#
    # $RCSfile: com.sun.media.jai.mlib.properties,v $
    #
    # Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
    #
    # Use is subject to license terms.
    #
    # $Revision: 1.1 $
    # $Date: 2005-02-11 04:56:10 $
    # $State: Exp $
    #
    FCTmediaLib0=FCT length must be a positive power of 2.
    FCTmediaLib1= : Unexpected data type; should be float or double only.
    FFTmediaLib0=FFT length must be a positive power of 2.
    FFTmediaLib1= : Unexpected data type; should be float or double only.
    Generic0=The input argument(s) may not be null.
    Generic1=The sourceIndex must be non-negative and less than the number of sources this image has.
    Generic2=Unknown or unsupported data type.
    MediaLibAccessor0=MediaLibAccessor does not support DataBuffer.TYPE_USHORT.
    MediaLibAccessor1=MediaLibAccessor does not recognize this datatype.
    MediaLibAccessor2=Could not load mediaLib accelerator wrapper classes. Continuing in pure Java mode.
    MediaLibAccessor3=Could not find mediaLib accelerator wrapper classes. Continuing in pure Java mode.
    MediaLibAccessor4=No permission to load mediaLib accelerator wrapper classes. Continuing in pure Java mode.
    MediaLibAccessor5=mediaLib acceleration disabled. Continuing in pure Java mode.
    MlibUshort=Does not yet implement computeRect for ushort data.
    MlibInterp= : Interpolation of this type is not yet implemented.
    MlibAffineOpImage0=Affine Transform cannot be inverted.
    MlibDFTOpImage0=The source image must have a single component.
    MlibDFTOpImage1=Unknown DFT scaling type.
    MlibHistogramRIF0=Fails to create the rendering of the Histogram operation
    MedianFilterOpImage0=MaskType is unknown.
    MaxFilterOpImage0=MaskType is unknown.
    MinFilterOpImage0=MaskType is unknown.
    MlibOrderedDitherOpImage0=All dither mask component kernels must have the same dimensions.
    MlibMosaicOpImage0=Floating point processing is not supported.
    jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibCompositeRIF.java0000644000175000017500000000530210203035544026504 0ustar  mathieumathieu/*
     * $RCSfile: MlibCompositeRIF.java,v $
     *
     * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
     *
     * Use is subject to license terms.
     *
     * $Revision: 1.1 $
     * $Date: 2005-02-11 04:55:52 $
     * $State: Exp $
     */
    package com.sun.media.jai.mlib;
    import java.awt.RenderingHints;
    import java.awt.image.ComponentSampleModel;
    import java.awt.image.ComponentColorModel;
    import java.awt.image.DataBuffer;
    import java.awt.image.RenderedImage;
    import java.awt.image.SampleModel;
    import java.awt.image.renderable.ParameterBlock;
    import java.awt.image.renderable.RenderedImageFactory;
    import javax.media.jai.EnumeratedParameter;
    import javax.media.jai.ImageLayout;
    import java.util.Map;
    import javax.media.jai.operator.CompositeDescriptor;
    import com.sun.media.jai.opimage.RIFUtil;
    
    /**
     * A RIF supporting the "Composite" operation in the
     * rendered image mode using MediaLib.
     *
     * @see javax.media.jai.operator.CompositeDescriptor
     * @see MlibCompositeOpImage
     *
     * @since 1.0
     *
     */
    public class MlibCompositeRIF implements RenderedImageFactory {
    
        /** Constructor. */
        public MlibCompositeRIF() {}
    
        /**
         * Creates a new instance of MlibCompositeOpImage in
         * the rendered image mode.
         *
         * @param args  The source images.
         * @param hints  May contain rendering hints and destination image layout.
         */
        public RenderedImage create(ParameterBlock args,
                                    RenderingHints hints) {
            /* Get ImageLayout and TileCache from RenderingHints. */
            ImageLayout layout = RIFUtil.getImageLayoutHint(hints);
            
    
            if (!MediaLibAccessor.isMediaLibCompatible(args, layout) ||
                !MediaLibAccessor.hasSameNumBands(args, layout)) {
                return null;
            }
    
            RenderedImage alpha1 = (RenderedImage)args.getObjectParameter(0);
            Object alpha2 = args.getObjectParameter(1);
            boolean premultiplied =
                ((Boolean)args.getObjectParameter(2)).booleanValue();
            EnumeratedParameter destAlpha =
                (EnumeratedParameter)args.getObjectParameter(3);
    
            SampleModel sm = alpha1.getSampleModel();
    
            if (!(sm instanceof ComponentSampleModel) ||
                sm.getNumBands() != 1 ||
                !(alpha1.getColorModel() instanceof ComponentColorModel) ||
                alpha2 != null ||
                premultiplied ||
                !(destAlpha.equals(CompositeDescriptor.NO_DESTINATION_ALPHA))) {
                return null;
            }
                
    
            return new MlibCompositeOpImage(args.getRenderedSource(0),
                                            args.getRenderedSource(1),
                                            hints, layout,
                                            alpha1);
        }
    }
    jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibUnsharpMaskRIF.java0000644000175000017500000000612010300212137026766 0ustar  mathieumathieu/*
     * $RCSfile: MlibUnsharpMaskRIF.java,v $
     *
     * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
     *
     * Use is subject to license terms.
     *
     * $Revision: 1.2 $
     * $Date: 2005-08-15 22:17:03 $
     * $State: Exp $
     */
    package com.sun.media.jai.mlib;
    
    import com.sun.media.jai.opimage.RIFUtil;
    import com.sun.media.jai.util.ImageUtil;
    import java.awt.RenderingHints;
    import java.awt.image.RenderedImage;
    import java.awt.image.renderable.ParameterBlock;
    import java.awt.image.renderable.RenderedImageFactory;
    import javax.media.jai.BorderExtender;
    import javax.media.jai.ImageLayout;
    import javax.media.jai.KernelJAI;
    import java.util.Map;
    
    /**
     * A RIF supporting the "Convolve" operation in the
     * rendered image mode using MediaLib.
     *
     * @see javax.media.jai.operator.ConvolveDescriptor
     * @see MlibConvolveOpImage
     */
    public class MlibUnsharpMaskRIF implements RenderedImageFactory {
    
        /** Constructor. */
        public MlibUnsharpMaskRIF() {}
    
        /**
         * Creates a new instance of MlibConvolveOpImage in
         * the rendered image mode.
         *
         * @param args  The source image and convolution kernel.
         * @param hints  May contain rendering hints and destination image layout.
         */
        public RenderedImage create(ParameterBlock args,
                                    RenderingHints hints) {
    
            /* Get ImageLayout and TileCache from RenderingHints. */
            ImageLayout layout = RIFUtil.getImageLayoutHint(hints);
            
    
            if (!MediaLibAccessor.isMediaLibCompatible(args, layout) ||
                !MediaLibAccessor.hasSameNumBands(args, layout)) {
                return null;
            }
    
            /* Get BorderExtender from hints if any. */
            BorderExtender extender = RIFUtil.getBorderExtenderHint(hints);
    
            RenderedImage source = args.getRenderedSource(0);
    
    	// map the input kernel + gain factor to an equivalent
    	// convolution kernel and then do a normal convolve.
    	KernelJAI unRotatedKernel =
    		ImageUtil.getUnsharpMaskEquivalentKernel(
    			(KernelJAI)args.getObjectParameter(0),
    			args.getFloatParameter(1));
    
            KernelJAI kJAI = unRotatedKernel.getRotatedKernel();
    
            int kWidth = kJAI.getWidth();
            int kHeight = kJAI.getHeight();
    
            /* mediaLib does not handle kernels with either dimension < 2. */
            if (kWidth < 2 || kHeight < 2) {
                return null;
            }
    
            if (kJAI.isSeparable() && kWidth >= 3 && kWidth <= 7 &&  kWidth == kHeight) {
                return new MlibSeparableConvolveOpImage(source,
                                                        extender, hints, layout,
                                                        kJAI);
            } else if ((kWidth == 3 && kHeight == 3) ||
                       (kWidth == 5 && kHeight == 5)) {
                return new MlibConvolveNxNOpImage(source,
                                                  extender, hints, layout,
                                                  kJAI);
            } else {
                return new MlibConvolveOpImage(source,
                                               extender, hints, layout,
                                               kJAI);
            }
        }
    }
    jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibAndConstOpImage.java0000644000175000017500000001015510203035544027156 0ustar  mathieumathieu/*
     * $RCSfile: MlibAndConstOpImage.java,v $
     *
     * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
     *
     * Use is subject to license terms.
     *
     * $Revision: 1.1 $
     * $Date: 2005-02-11 04:55:49 $
     * $State: Exp $
     */
    package com.sun.media.jai.mlib;
    import java.awt.Rectangle;
    import java.awt.image.DataBuffer;
    import java.awt.image.Raster;
    import java.awt.image.RenderedImage;
    import java.awt.image.WritableRaster;
    import javax.media.jai.ImageLayout;
    import javax.media.jai.OpImage;
    import javax.media.jai.PointOpImage;
    import java.util.Map;
    import com.sun.medialib.mlib.*;
    // import com.sun.media.jai.test.OpImageTester;
    
    /**
     * A mediaLib implementation of "AndConst" operator.
     *
     */
    final class MlibAndConstOpImage extends PointOpImage {
        int[] constants;
    
        /**
         * Constructs an MlibAndConstOpImage. The image dimensions are copied
         * from the source image.  The tile grid layout, SampleModel, and
         * ColorModel may optionally be specified by an ImageLayout object.
         *
         * @param source    a RenderedImage.
         * @param layout    an ImageLayout optionally containing the tile
         *                  grid layout, SampleModel, and ColorModel, or null.
         */
        public MlibAndConstOpImage(RenderedImage source,
                                   Map config,
                                   ImageLayout layout,
                                   int[] constants) {
            super(source, layout, config, true);
            this.constants = MlibUtils.initConstants(constants,
                                                getSampleModel().getNumBands());
            // Set flag to permit in-place operation.
            permitInPlaceOperation();
        }
    
        /**
         * And the pixel values of a rectangle with a given constant.
         * The sources are cobbled.
         *
         * @param sources   an array of sources, guarantee to provide all
         *                  necessary source data for computing the rectangle.
         * @param dest      a tile that contains the rectangle to be computed.
         * @param destRect  the rectangle within this OpImage to be processed.
         */
        protected void computeRect(Raster[] sources,
                                   WritableRaster dest,
                                   Rectangle destRect) {
    
            Raster source = sources[0];
            int formatTag = MediaLibAccessor.findCompatibleTag(sources, dest);
    
    	// For PointOpImages, the srcRect and the destRect are the same.
            MediaLibAccessor srcAccessor = new MediaLibAccessor(source, destRect,
    							    formatTag);
            MediaLibAccessor dstAccessor = new MediaLibAccessor(dest, destRect,
    							    formatTag);
    
            switch (dstAccessor.getDataType()) {
            case DataBuffer.TYPE_BYTE:
            case DataBuffer.TYPE_USHORT:
            case DataBuffer.TYPE_SHORT:
            case DataBuffer.TYPE_INT:
                mediaLibImage[] srcML = srcAccessor.getMediaLibImages();
                mediaLibImage[] dstML = dstAccessor.getMediaLibImages();
                for (int i = 0 ; i < dstML.length; i++) {
                    int mlconstants[] = dstAccessor.getIntParameters(i, constants);
                    Image.ConstAnd(dstML[i], srcML[i], mlconstants);
                }
                break;
            default:
                String className = this.getClass().getName();
                throw new RuntimeException(className + JaiI18N.getString("Generic2"));
            }
    
            if (dstAccessor.isDataCopy()) {
                dstAccessor.clampDataArrays();
                dstAccessor.copyDataToRaster();
            }
        }
    
    //     public static OpImage createTestImage(OpImageTester oit) {
    //         int[] consts = {5, 5, 5};
    //         return new MlibAndConstOpImage(oit.getSource(), null,
    //                                        new ImageLayout(oit.getSource()),
    //                                        consts);
    //     }
    
    //     // Calls a method on OpImage that uses introspection, to make this
    //     // class, discover it's createTestImage() call, call it and then
    //     // benchmark the performance of the created OpImage chain.
    //     public static void main (String args[]) {
    //         String classname = "com.sun.media.jai.mlib.MlibAndConstOpImage";
    //         OpImageTester.performDiagnostics(classname,args);
    //     }
    }
    jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibDilate3PlusOpImage.java0000644000175000017500000001330110346146715027605 0ustar  mathieumathieu/*
     * $RCSfile: MlibDilate3PlusOpImage.java,v $
     *
     * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
     *
     * Use is subject to license terms.
     *
     * $Revision: 1.2 $
     * $Date: 2005-12-09 00:20:28 $
     * $State: Exp $
     */ 
    package com.sun.media.jai.mlib;
    import java.awt.Rectangle;
    import java.awt.RenderingHints;
    import java.awt.image.DataBuffer;
    import java.awt.image.SampleModel;
    import java.awt.image.Raster;
    import java.awt.image.RenderedImage;
    import java.awt.image.WritableRaster;
    import java.awt.image.renderable.ParameterBlock;
    import java.awt.image.renderable.RenderedImageFactory;
    import javax.media.jai.AreaOpImage;
    import javax.media.jai.BorderExtender;
    import javax.media.jai.ImageLayout;
    import javax.media.jai.JAI;
    import javax.media.jai.KernelJAI;
    import javax.media.jai.OpImage;
    import java.util.Map;
    import com.sun.medialib.mlib.*;
    // import com.sun.media.jai.test.OpImageTester;
    
    /**
     * An OpImage class to perform dilation on a source image
     * for the specific case when the kernel is a 3x3 plus shape
     * with the key position in the middle,
     * using mediaLib, of course :-).
     *
     * @see javax.media.jai.operator.DilateDescriptor
     * @see KernelJAI
     */
    final class MlibDilate3PlusOpImage extends AreaOpImage {
    
        // Since medialib expects single banded data with IndexColorModel, we
        // should not expand the indexed data
        private static Map configHelper(Map configuration) {
    
            Map config;
    
            if (configuration == null) {
    
                config = new RenderingHints(JAI.KEY_REPLACE_INDEX_COLOR_MODEL,
                                            Boolean.FALSE);
    
            } else {
    
                config = configuration;
    
    	    // If the user has specified a hint for this, then we don't
    	    // want to change it, so change only if this hint is not 
    	    // already specified
                if (!(config.containsKey(JAI.KEY_REPLACE_INDEX_COLOR_MODEL))) {
                    RenderingHints hints = (RenderingHints)configuration;
                    config = (RenderingHints)hints.clone();
                    config.put(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.FALSE);
                }
            }
    
            return config;
        }
    
        /**
         * Creates a MlibDilate3PlusOpImage given the image source
         * The image dimensions are
         * derived from the source image.  The tile grid layout,
         * SampleModel, and ColorModel may optionally be specified by an
         * ImageLayout object.
         *
         * @param source a RenderedImage.
         * @param extender a BorderExtender, or null.
    
         *        or null.  If null, a default cache will be used.
         * @param layout an ImageLayout optionally containing the tile grid layout,
         *        SampleModel, and ColorModel, or null.
         */
        public MlibDilate3PlusOpImage(RenderedImage source,
                                      BorderExtender extender,
                                      Map config,
                                      ImageLayout layout
                                      ) {
    	super(source,
                  layout,
                  configHelper(config),
                  true,
                  extender,
                  1, //kernel.getLeftPadding(),
                  1, //kernel.getRightPadding(),
                  1, //kernel.getTopPadding(),
                  1  //kernel.getBottomPadding()
    	      );
        }
    
    
        /**
         * Performs dilation on a specified rectangle. The sources are
         * cobbled.
         *
         * @param sources an array of source Rasters, guaranteed to provide all
         *                necessary source data for computing the output.
         * @param dest a WritableRaster tile containing the area to be computed.
         * @param destRect the rectangle within dest to be processed.
         */
        protected void computeRect(Raster[] sources,
                                   WritableRaster dest,
                                   Rectangle destRect) {
    
            Raster source = sources[0];
            Rectangle srcRect = mapDestRect(destRect, 0);
    
            int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest);
    
            MediaLibAccessor srcAccessor =
                new MediaLibAccessor(source,srcRect,formatTag,true);
            MediaLibAccessor dstAccessor =
                new MediaLibAccessor(dest,destRect,formatTag,true);
            int numBands = getSampleModel().getNumBands();
    
    
            mediaLibImage[] srcML = srcAccessor.getMediaLibImages();
            mediaLibImage[] dstML = dstAccessor.getMediaLibImages();
            for (int i = 0; i < dstML.length; i++) {
                switch (dstAccessor.getDataType()) {
                case DataBuffer.TYPE_BYTE:
                case DataBuffer.TYPE_USHORT:
                case DataBuffer.TYPE_SHORT:
                case DataBuffer.TYPE_INT:
                    Image.Dilate4(dstML[i], srcML[i]);
                    break;
                case DataBuffer.TYPE_FLOAT:
                case DataBuffer.TYPE_DOUBLE:
                    Image.Dilate4_Fp(dstML[i], srcML[i]);
                    break;
                default:
                    String className = this.getClass().getName();
                    throw new RuntimeException(JaiI18N.getString("Generic2"));
                }
            }
     
            if (dstAccessor.isDataCopy()) {
                dstAccessor.copyDataToRaster();
            }
        }
    
    //     public static OpImage createTestImage(OpImageTester oit) {
    //         float data[] = {0.05f,0.10f,0.05f,
    //                         0.10f,0.40f,0.10f,
    //                         0.05f,0.10f,0.05f};
    //         KernelJAI kJAI = new KernelJAI(3,3,1,1,data);
    //         return new MlibDilate4OpImage(oit.getSource(), null, null,
    //                                           new ImageLayout(oit.getSource()),
    //                                           kJAI);
    //     }
     
    //     public static void main (String args[]) {
    //         String classname = "com.sun.media.jai.mlib.MlibDilate4OpImage";
    //         OpImageTester.performDiagnostics(classname,args);
    //     }
    }
    jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibLookupOpImage.java0000644000175000017500000001653010203035544026721 0ustar  mathieumathieu/*
     * $RCSfile: MlibLookupOpImage.java,v $
     *
     * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
     *
     * Use is subject to license terms.
     *
     * $Revision: 1.1 $
     * $Date: 2005-02-11 04:55:58 $
     * $State: Exp $
     */
    package com.sun.media.jai.mlib;
    import java.awt.Rectangle;
    import java.awt.image.ComponentSampleModel;
    import java.awt.image.DataBuffer;
    import java.awt.image.Raster;
    import java.awt.image.RenderedImage;
    import java.awt.image.SampleModel;
    import java.awt.image.WritableRaster;
    import java.util.Map;
    import javax.media.jai.ImageLayout;
    import javax.media.jai.LookupTableJAI;
    import javax.media.jai.PlanarImage;
    import javax.media.jai.PointOpImage;
    import com.sun.media.jai.util.ImageUtil;
    import com.sun.media.jai.util.JDKWorkarounds;
    import com.sun.medialib.mlib.*;
    
    final class MlibLookupOpImage extends PointOpImage {
        private LookupTableJAI table;
    
        public MlibLookupOpImage(RenderedImage source,
                                 Map config,
                                 ImageLayout layout,
                                 LookupTableJAI table) {
            super(source, layout, config, true);
    
            this.table = table;
    
            SampleModel sm = source.getSampleModel();	// source sample model
    
            if (sampleModel.getTransferType() != table.getDataType() ||
                sampleModel.getNumBands() !=
                    table.getDestNumBands(sm.getNumBands())) {
                /*
                 * The current SampleModel is not suitable for the supplied
                 * source and lookup table. Create a suitable SampleModel
                 * and ColorModel for the destination image.
                 */
                sampleModel = table.getDestSampleModel(sm, tileWidth, tileHeight);
                if(colorModel != null &&
                   !JDKWorkarounds.areCompatibleDataModels(sampleModel,
                                                           colorModel)) {
                    colorModel = ImageUtil.getCompatibleColorModel(sampleModel,
                                                                   config);
                }
            }
            // Set flag to permit in-place operation.
            permitInPlaceOperation();
        }
    
        protected void computeRect(Raster[] sources,
                                   WritableRaster dest,
                                   Rectangle destRect) {
            Raster source = sources[0];
            Rectangle srcRect = mapDestRect(destRect, 0);
    
            int srcTag = MediaLibAccessor.findCompatibleTag(null, source);
            int dstTag = MediaLibAccessor.findCompatibleTag(null, dest);
    
            SampleModel sm = source.getSampleModel();
            if (sm.getNumBands() > 1) {
                int srcCopy = srcTag & MediaLibAccessor.COPY_MASK;
                int dstCopy = dstTag & MediaLibAccessor.COPY_MASK;
    
                int srcDtype = srcTag & MediaLibAccessor.DATATYPE_MASK;
                int dstDtype = dstTag & MediaLibAccessor.DATATYPE_MASK;
    
                if (srcCopy == MediaLibAccessor.UNCOPIED &&
                    dstCopy == MediaLibAccessor.UNCOPIED &&
                    MediaLibAccessor.isPixelSequential(sm) &&
                    MediaLibAccessor.isPixelSequential(sampleModel) &&
                    MediaLibAccessor.hasMatchingBandOffsets(
                                     (ComponentSampleModel)sm,
                                     (ComponentSampleModel)sampleModel)) {
                } else {
                    srcTag = srcDtype | MediaLibAccessor.COPIED;
                    dstTag = dstDtype | MediaLibAccessor.COPIED;
                }
            }
    
            MediaLibAccessor src = new MediaLibAccessor(source, srcRect, srcTag);
            MediaLibAccessor dst = new MediaLibAccessor(dest, destRect, dstTag);
    
            mediaLibImage[] srcMLI = src.getMediaLibImages();
            mediaLibImage[] dstMLI = dst.getMediaLibImages();
    
            if (srcMLI.length < dstMLI.length) {
                mediaLibImage srcMLI0 = srcMLI[0];
                srcMLI = new mediaLibImage[dstMLI.length];
    
                for (int i = 0 ; i < dstMLI.length; i++) {
                    srcMLI[i] = srcMLI0;
                }
            }
    
            int[] bandOffsets = dst.getBandOffsets();
            Object table = getTableData(bandOffsets);
            int[] offsets = getTableOffsets(bandOffsets);
    
            for (int i = 0 ; i < dstMLI.length; i++) {
                Image.LookUp2(dstMLI[i], srcMLI[i],
                                              table, offsets);
            }
    
            if (dst.isDataCopy()) {
                dst.copyDataToRaster();
            }
        }
    
        private Object getTableData(int[] bandOffsets) {
            int tbands = table.getNumBands();
            int dbands = sampleModel.getNumBands();
            Object data = null;
    
            switch (table.getDataType()) {
            case DataBuffer.TYPE_BYTE:
                byte[][] bdata = new byte[dbands][];
                if (tbands < dbands) {
                    for (int i = 0; i < dbands; i++) {
                        bdata[i] = table.getByteData(0);
                    }
                } else {
                    for (int i = 0; i < dbands; i++) {
                            bdata[i] = table.getByteData(bandOffsets[i]);
                    }
                }
                data = bdata;
                break;
            case DataBuffer.TYPE_USHORT:
            case DataBuffer.TYPE_SHORT:
                short[][] sdata = new short[dbands][];
                if (tbands < dbands) {
                    for (int i = 0; i < dbands; i++) {
                        sdata[i] = table.getShortData(0);
                    }
                } else {
                    for (int i = 0; i < dbands; i++) {
                        sdata[i] = table.getShortData(bandOffsets[i]);
                    }
                }
                data = sdata;
                break;
            case DataBuffer.TYPE_INT:
                int[][] idata = new int[dbands][];
                if (tbands < dbands) {
                    for (int i = 0; i < dbands; i++) {
                        idata[i] = table.getIntData(0);
                    }
                } else {
                    for (int i = 0; i < dbands; i++) {
                        idata[i] = table.getIntData(bandOffsets[i]);
                    }
                }
                data = idata;
                break;
            case DataBuffer.TYPE_FLOAT:
                float[][] fdata = new float[dbands][];
                if (tbands < dbands) {
                    for (int i = 0; i < dbands; i++) {
                        fdata[i] = table.getFloatData(0);
                    }
                } else {
                    for (int i = 0; i < dbands; i++) {
                        fdata[i] = table.getFloatData(bandOffsets[i]);
                    }
                }
                data = fdata;
                break;
            case DataBuffer.TYPE_DOUBLE:
                double[][] ddata = new double[dbands][];
                if (tbands < dbands) {
                    for (int i = 0; i < dbands; i++) {
                        ddata[i] = table.getDoubleData(0);
                    }
                } else {
                    for (int i = 0; i < dbands; i++) {
                        ddata[i] = table.getDoubleData(bandOffsets[i]);
                    }
                }
                data = ddata;
                break;
            }
    
            return data;
        }
    
        private int[] getTableOffsets(int[] bandOffsets) {
            int tbands = table.getNumBands();
            int dbands = sampleModel.getNumBands();
            int[] offsets = new int[dbands];
    
            if (tbands < dbands) {
                for (int i = 0; i < dbands; i++) {
                    offsets[i] = table.getOffset(0);
                }
            } else {
                for (int i = 0; i < dbands; i++) {
                    offsets[i] = table.getOffset(bandOffsets[i]);
                }
            }
    
            return offsets;
        }
    }
    jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibDivideRIF.java0000644000175000017500000000324010203035544025745 0ustar  mathieumathieu/*
     * $RCSfile: MlibDivideRIF.java,v $
     *
     * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
     *
     * Use is subject to license terms.
     *
     * $Revision: 1.1 $
     * $Date: 2005-02-11 04:55:55 $
     * $State: Exp $
     */
    package com.sun.media.jai.mlib;
    import java.awt.RenderingHints;
    import java.awt.image.RenderedImage;
    import java.awt.image.renderable.ParameterBlock;
    import java.awt.image.renderable.RenderedImageFactory;
    import javax.media.jai.ImageLayout;
    import java.util.Map;
    import com.sun.media.jai.opimage.RIFUtil;
    
    /**
     * A RIF supporting the "Divide" operation in the
     * rendered image mode using MediaLib.
     *
     * @see javax.media.jai.operator.DivideDescriptor
     * @see MlibDivideOpImage
     *
     */
    public class MlibDivideRIF implements RenderedImageFactory {
    
        /** Constructor. */
        public MlibDivideRIF() {}
    
        /**
         * Creates a new instance of MlibDivideOpImage in
         * the rendered image mode.
         *
         * @param args  The source images to be divided.
         * @param hints  May contain rendering hints and destination image layout.
         */
        public RenderedImage create(ParameterBlock args,
                                    RenderingHints hints) {
            /* Get ImageLayout and TileCache from RenderingHints. */
            ImageLayout layout = RIFUtil.getImageLayoutHint(hints);
            
    
            if (!MediaLibAccessor.isMediaLibCompatible(args, layout) ||
                !MediaLibAccessor.hasSameNumBands(args, layout)) {
                return null;
            }
    
            return new MlibDivideOpImage(args.getRenderedSource(0),
                                         args.getRenderedSource(1),
                                         hints, layout);
        }
    }
    jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibOrRIF.java0000644000175000017500000000403210203035544025121 0ustar  mathieumathieu/*
     * $RCSfile: MlibOrRIF.java,v $
     *
     * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
     *
     * Use is subject to license terms.
     *
     * $Revision: 1.1 $
     * $Date: 2005-02-11 04:56:03 $
     * $State: Exp $
     */
    package com.sun.media.jai.mlib;
    import java.awt.RenderingHints;
    import java.awt.image.DataBuffer;
    import java.awt.image.RenderedImage;
    import java.awt.image.SampleModel;
    import java.awt.image.renderable.ParameterBlock;
    import java.awt.image.renderable.RenderedImageFactory;
    import javax.media.jai.ImageLayout;
    import java.util.Map;
    import com.sun.media.jai.opimage.RIFUtil;
    
    /**
     * A RIF supporting the "Or" operation in the
     * rendered image mode using MediaLib.
     *
     * @see javax.media.jai.operator.OrDescriptor
     * @see MlibOrOpImage
     *
     */
    public class MlibOrRIF implements RenderedImageFactory { // OrRIF {
    
        /** Constructor. */
        public MlibOrRIF() {}
    
        /**
         * Creates a new instance of MlibOrOpImage in
         * the rendered image mode.
         *
         * @param args  The source images.
         * @param hints  May contain rendering hints and destination image layout.
         */
        public RenderedImage create(ParameterBlock args,
                                    RenderingHints hints) {
            ImageLayout layout = RIFUtil.getImageLayoutHint(hints);
            
    
            if (!MediaLibAccessor.isMediaLibCompatible(args, layout) ||
                !MediaLibAccessor.hasSameNumBands(args, layout)) {
                return null;
            }
    
            /* Check whether dest has data type of float or double. */
            if (layout != null) {
                SampleModel sm = layout.getSampleModel(null);
    
                if (sm != null) {
                    int dtype = sm.getDataType();
                    if (dtype == DataBuffer.TYPE_FLOAT ||
                        dtype == DataBuffer.TYPE_DOUBLE) {
                        return null;
                    }
                }
            }
    
            return new MlibOrOpImage(args.getRenderedSource(0),
                                     args.getRenderedSource(1),
                                     hints, layout);
        }
    }
    jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibOrOpImage.java0000644000175000017500000001255010203035544026026 0ustar  mathieumathieu/*
     * $RCSfile: MlibOrOpImage.java,v $
     *
     * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
     *
     * Use is subject to license terms.
     *
     * $Revision: 1.1 $
     * $Date: 2005-02-11 04:56:02 $
     * $State: Exp $
     */
    package com.sun.media.jai.mlib;
    import java.awt.Rectangle;
    import java.awt.image.DataBuffer;
    import java.awt.image.PixelInterleavedSampleModel;
    import java.awt.image.Raster;
    import java.awt.image.RenderedImage;
    import java.awt.image.SampleModel;
    import java.awt.image.WritableRaster;
    import javax.media.jai.ImageLayout;
    import javax.media.jai.OpImage;
    import javax.media.jai.PointOpImage;
    import java.util.Map;
    import com.sun.medialib.mlib.*;
    // import com.sun.media.jai.test.OpImageTester;
    
    /**
     * An OpImage that performs the Or operation on 2 images through mediaLib.
     *
     */
    final class MlibOrOpImage extends PointOpImage {
    
        /**
         * Constructs an MlibOrOpImage. The image dimensions are copied
         * from the source image.  The tile grid layout, SampleModel, and
         * ColorModel may optionally be specified by an ImageLayout object.
         *
         * @param source    a RenderedImage.
         * @param layout    an ImageLayout optionally containing the tile
         *                  grid layout, SampleModel, and ColorModel, or null.
         */
        public MlibOrOpImage(RenderedImage source1, RenderedImage source2,
                             Map config,
                             ImageLayout layout) {
    	super(source1, source2, layout, config, true);
            // Set flag to permit in-place operation.
            permitInPlaceOperation();
        }
    
        /**
         * Or the pixel values of a rectangle from the two sources.
         * The sources are cobbled.
         *
         * @param sources   an array of sources, guarantee to provide all
         *                  necessary source data for computing the rectangle.
         * @param dest      a tile that contains the rectangle to be computed.
         * @param destRect  the rectangle within this OpImage to be processed.
         */
        protected void computeRect(Raster[] sources,
                                   WritableRaster dest,
                                   Rectangle destRect) {
    
    	int formatTag = MediaLibAccessor.findCompatibleTag(sources, dest);
    
            MediaLibAccessor srcAccessor1 = 
                new MediaLibAccessor(sources[0], destRect,formatTag);
    	MediaLibAccessor srcAccessor2 = 
    	    new MediaLibAccessor(sources[1], destRect,formatTag);
            MediaLibAccessor dstAccessor = 
                new MediaLibAccessor(dest, destRect, formatTag);
    
            switch (dstAccessor.getDataType()) {
            case DataBuffer.TYPE_BYTE:
            case DataBuffer.TYPE_USHORT:
            case DataBuffer.TYPE_SHORT:
            case DataBuffer.TYPE_INT:
                mediaLibImage[] srcML1 = srcAccessor1.getMediaLibImages();
    	    mediaLibImage[] srcML2 = srcAccessor2.getMediaLibImages();
                mediaLibImage[] dstML = dstAccessor.getMediaLibImages();
                for (int i = 0 ; i < dstML.length; i++) {
                    Image.Or(dstML[i], srcML1[i], srcML2[i]);
                }
                break;
            default:
                String className = this.getClass().getName();
                throw new RuntimeException(className + JaiI18N.getString("Generic2"));
            }
    
            if (dstAccessor.isDataCopy()) {
                dstAccessor.clampDataArrays();
                dstAccessor.copyDataToRaster();
            }
        }
    
    //     public static void main (String args[]) {
    //         System.out.println("MlibOrOpImage Test");
    //         ImageLayout layout;
    //         OpImage src1, src2, dst;
    //         Rectangle rect = new Rectangle(0, 0, 5, 5);
    
    //         System.out.println("1. PixelInterleaved byte 3-band");
    //         layout = OpImageTester.createImageLayout(0, 0, 800, 800, 0, 0,
    // 						 200, 200, DataBuffer.TYPE_BYTE,
    // 						 3, false);
    //         src1 = OpImageTester.createRandomOpImage(layout);
    //         src2 = OpImageTester.createRandomOpImage(layout);
    //         dst = new MlibOrOpImage(src1, src2, null, null);
    //         OpImageTester.testOpImage(dst, rect);
    //         OpImageTester.timeOpImage(dst, 10);
    
    //         System.out.println("2. Banded byte 3-band");
    //         layout = OpImageTester.createImageLayout(0, 0, 800, 800, 0, 0,
    // 						 200, 200, DataBuffer.TYPE_BYTE,
    // 						 3, true);
    //         src1 = OpImageTester.createRandomOpImage(layout);
    //         src2 = OpImageTester.createRandomOpImage(layout);
    //         dst = new MlibOrOpImage(src1, src2, null, null);
    //         OpImageTester.testOpImage(dst, rect);
    //         OpImageTester.timeOpImage(dst, 10);
    
    //         System.out.println("3. PixelInterleaved int 3-band");
    //         layout = OpImageTester.createImageLayout(0, 0, 512, 512, 0, 0, 200, 200,
    // 						 DataBuffer.TYPE_INT, 3, false);
    //         src1 = OpImageTester.createRandomOpImage(layout);
    //         src2 = OpImageTester.createRandomOpImage(layout);
    //         dst = new MlibOrOpImage(src1, src2, null, null);
    //         OpImageTester.testOpImage(dst, rect);
    //         OpImageTester.timeOpImage(dst, 10);
    
    //         System.out.println("4. Banded int 3-band");
    //         layout = OpImageTester.createImageLayout(0, 0, 512, 512, 0, 0,
    // 						 200, 200, DataBuffer.TYPE_INT,
    // 						 3, true);
    //         src1 = OpImageTester.createRandomOpImage(layout);
    //         src2 = OpImageTester.createRandomOpImage(layout);
    //         dst = new MlibOrOpImage(src1, src2, null, null);
    //         OpImageTester.testOpImage(dst, rect);
    //         OpImageTester.timeOpImage(dst, 10);
    //     }
    }
    jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibLogOpImage.java0000644000175000017500000000643310203035544026172 0ustar  mathieumathieu/*
     * $RCSfile: MlibLogOpImage.java,v $
     *
     * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
     *
     * Use is subject to license terms.
     *
     * $Revision: 1.1 $
     * $Date: 2005-02-11 04:55:58 $
     * $State: Exp $
     */
    package com.sun.media.jai.mlib;
    import java.awt.Rectangle;
    import java.awt.image.DataBuffer;
    import java.awt.image.Raster;
    import java.awt.image.RenderedImage;
    import java.awt.image.WritableRaster;
    import javax.media.jai.ImageLayout;
    import javax.media.jai.OpImage;
    import javax.media.jai.PointOpImage;
    import java.util.Map;
    import com.sun.medialib.mlib.*;
    // import com.sun.media.jai.test.OpImageTester;
    
    /**
     * An OpImage class that divides an image into a constant
     *
     */
    final class MlibLogOpImage extends PointOpImage {
    
        /**
         * Constructs an MlibLogOpImage. The image dimensions are copied
         * from the source image.  The tile grid layout, SampleModel, and
         * ColorModel may optionally be specified by an ImageLayout object.
         *
         * @param source    a RenderedImage.
         * @param layout    an ImageLayout optionally containing the tile
         *                  grid layout, SampleModel, and ColorModel, or null.
         */
        public MlibLogOpImage(RenderedImage source,
                              Map config,
                              ImageLayout layout) {
            super(source, layout, config, true);
            // Set flag to permit in-place operation.
            permitInPlaceOperation();
        }
    
        /**
         * Divide the pixel values of a rectangle into a given constant.
         * The sources are cobbled.
         *
         * @param sources   an array of sources, guarantee to provide all
         *                  necessary source data for computing the rectangle.
         * @param dest      a tile that contains the rectangle to be computed.
         * @param destRect  the rectangle within this OpImage to be processed.
         */
        protected void computeRect(Raster[] sources,
                                   WritableRaster dest,
                                   Rectangle destRect) {
            Raster source = sources[0];
            Rectangle srcRect = mapDestRect(destRect, 0);
    
            int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest);
    
            MediaLibAccessor srcAccessor = 
                new MediaLibAccessor(source,srcRect,formatTag);
            MediaLibAccessor dstAccessor = 
                new MediaLibAccessor(dest,destRect,formatTag);
    
            mediaLibImage[] srcML = srcAccessor.getMediaLibImages();
            mediaLibImage[] dstML = dstAccessor.getMediaLibImages();
    
            switch (dstAccessor.getDataType()) {
            case DataBuffer.TYPE_BYTE:
            case DataBuffer.TYPE_USHORT:
            case DataBuffer.TYPE_SHORT:
            case DataBuffer.TYPE_INT:
                for (int i = 0; i < dstML.length; i++) {
                    Image.Log(dstML[i], srcML[i]);
                }
                break;
    
            case DataBuffer.TYPE_FLOAT:
            case DataBuffer.TYPE_DOUBLE:
                for (int i = 0; i < dstML.length; i++) {
                    Image.Log_Fp(dstML[i], srcML[i]);
                }
                break;
    
            default:
                String className = this.getClass().getName();
                throw new RuntimeException(className + JaiI18N.getString("Generic2"));
            }
    
            if (dstAccessor.isDataCopy()) {
                dstAccessor.clampDataArrays();
                dstAccessor.copyDataToRaster();
            }
        }
    }
    jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibMultiplyOpImage.java0000644000175000017500000001324510203035544027267 0ustar  mathieumathieu/*
     * $RCSfile: MlibMultiplyOpImage.java,v $
     *
     * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
     *
     * Use is subject to license terms.
     *
     * $Revision: 1.1 $
     * $Date: 2005-02-11 04:56:01 $
     * $State: Exp $
     */
    package com.sun.media.jai.mlib;
    import java.awt.Rectangle;
    import java.awt.image.DataBuffer;
    import java.awt.image.PixelInterleavedSampleModel;
    import java.awt.image.Raster;
    import java.awt.image.RenderedImage;
    import java.awt.image.SampleModel;
    import java.awt.image.WritableRaster;
    import javax.media.jai.ImageLayout;
    import javax.media.jai.OpImage;
    import javax.media.jai.PointOpImage;
    import java.util.Map;
    import com.sun.medialib.mlib.*;
    // import com.sun.media.jai.test.OpImageTester;
    
    /**
     * An OpImage that performs the Multiply operation on two images through
     * mediaLib.
     *
     */
    final class MlibMultiplyOpImage extends PointOpImage {
    
        /**
         * Constructs an MlibMultiplyOpImage. The image dimensions are copied
         * from the source image.  The tile grid layout, SampleModel, and
         * ColorModel may optionally be specified by an ImageLayout object.
         *
         * @param source    a RenderedImage.
         * @param layout    an ImageLayout optionally containing the tile
         *                  grid layout, SampleModel, and ColorModel, or null.
         */
        public MlibMultiplyOpImage(RenderedImage source1, RenderedImage source2,
                                   Map config,
                                   ImageLayout layout) {
    	super(source1, source2, layout, config, true);
            // Set flag to permit in-place operation.
            permitInPlaceOperation();
        }
    
        /**
         * Multiply the pixel values of a rectangle from the two sources.
         * The sources are cobbled.
         *
         * @param sources   an array of sources, guarantee to provide all
         *                  necessary source data for computing the rectangle.
         * @param dest      a tile that contains the rectangle to be computed.
         * @param destRect  the rectangle within this OpImage to be processed.
         */
        protected void computeRect(Raster[] sources,
                                   WritableRaster dest,
                                   Rectangle destRect) {
    
            int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest);
    
            MediaLibAccessor srcAccessor1 = 
                new MediaLibAccessor(sources[0], destRect,formatTag);
    	MediaLibAccessor srcAccessor2 = 
    	    new MediaLibAccessor(sources[1], destRect,formatTag);
            MediaLibAccessor dstAccessor = 
                new MediaLibAccessor(dest, destRect, formatTag);
    
            mediaLibImage[] srcML1 = srcAccessor1.getMediaLibImages();
            mediaLibImage[] srcML2 = srcAccessor2.getMediaLibImages();
            mediaLibImage[] dstML  = dstAccessor.getMediaLibImages();
    
            switch (dstAccessor.getDataType()) {
            case DataBuffer.TYPE_BYTE:
            case DataBuffer.TYPE_USHORT:
            case DataBuffer.TYPE_SHORT:
            case DataBuffer.TYPE_INT:
                for (int i = 0; i < dstML.length; i++) {
                    Image.MulShift(dstML[i], srcML1[i], srcML2[i], 0);
                }
                break;
    
            case DataBuffer.TYPE_FLOAT:
            case DataBuffer.TYPE_DOUBLE:
                for (int i = 0; i < dstML.length; i++) {
                    Image.Mul_Fp(dstML[i], srcML1[i], srcML2[i]);
                }
                break;
    
            default:
                String className = this.getClass().getName();
                throw new RuntimeException(className + JaiI18N.getString("Generic2"));
            }
    
            if (dstAccessor.isDataCopy()) {
                dstAccessor.clampDataArrays();
                dstAccessor.copyDataToRaster();
            }
        }
    
    //     public static void main (String args[]) {
    //         System.out.println("MlibMultiplyOpImage Test");
    //         ImageLayout layout;
    //         OpImage src1, src2, dst;
    //         Rectangle rect = new Rectangle(0, 0, 5, 5);
    
    //         System.out.println("1. PixelInterleaved byte 3-band");
    //         layout = OpImageTester.createImageLayout(0, 0, 800, 800, 0, 0,
    // 						 200, 200, DataBuffer.TYPE_BYTE,
    // 						 3, false);
    //         src1 = OpImageTester.createRandomOpImage(layout);
    //         src2 = OpImageTester.createRandomOpImage(layout);
    //         dst = new MlibMultiplyOpImage(src1, src2, null, null);
    //         OpImageTester.testOpImage(dst, rect);
    //         OpImageTester.timeOpImage(dst, 10);
    
    //         System.out.println("2. Banded byte 3-band");
    //         layout = OpImageTester.createImageLayout(0, 0, 800, 800, 0, 0,
    // 						 200, 200, DataBuffer.TYPE_BYTE,
    // 						 3, true);
    //         src1 = OpImageTester.createRandomOpImage(layout);
    //         src2 = OpImageTester.createRandomOpImage(layout);
    //         dst = new MlibMultiplyOpImage(src1, src2, null, null);
    //         OpImageTester.testOpImage(dst, rect);
    //         OpImageTester.timeOpImage(dst, 10);
    
    //         System.out.println("3. PixelInterleaved int 3-band");
    //         layout = OpImageTester.createImageLayout(0, 0, 512, 512, 0, 0, 200, 200,
    // 						 DataBuffer.TYPE_INT, 3, false);
    //         src1 = OpImageTester.createRandomOpImage(layout);
    //         src2 = OpImageTester.createRandomOpImage(layout);
    //         dst = new MlibMultiplyOpImage(src1, src2, null, null);
    //         OpImageTester.testOpImage(dst, rect);
    //         OpImageTester.timeOpImage(dst, 10);
    
    //         System.out.println("4. Banded int 3-band");
    //         layout = OpImageTester.createImageLayout(0, 0, 512, 512, 0, 0,
    // 						 200, 200, DataBuffer.TYPE_INT,
    // 						 3, true);
    //         src1 = OpImageTester.createRandomOpImage(layout);
    //         src2 = OpImageTester.createRandomOpImage(layout);
    //         dst = new MlibMultiplyOpImage(src1, src2, null, null);
    //         OpImageTester.testOpImage(dst, rect);
    //         OpImageTester.timeOpImage(dst, 10);
    //     }
    }
    jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibMultiplyConstOpImage.java0000644000175000017500000001074310203035544030276 0ustar  mathieumathieu/*
     * $RCSfile: MlibMultiplyConstOpImage.java,v $
     *
     * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
     *
     * Use is subject to license terms.
     *
     * $Revision: 1.1 $
     * $Date: 2005-02-11 04:56:01 $
     * $State: Exp $
     */
    package com.sun.media.jai.mlib;
    import java.awt.Rectangle;
    import java.awt.image.DataBuffer;
    import java.awt.image.Raster;
    import java.awt.image.RenderedImage;
    import java.awt.image.WritableRaster;
    import javax.media.jai.ImageLayout;
    import javax.media.jai.OpImage;
    import javax.media.jai.PointOpImage;
    import java.util.Map;
    import com.sun.medialib.mlib.*;
    // import com.sun.media.jai.test.OpImageTester;
    
    /**
     * An OpImage class that multiplies an image by a constant
     *
     */
    final class MlibMultiplyConstOpImage extends PointOpImage {
        private double[] constants;
    
        /**
         * Constructs an MlibMultiplyConstOpImage. The image dimensions are copied
         * from the source image.  The tile grid layout, SampleModel, and
         * ColorModel may optionally be specified by an ImageLayout object.
         *
         * @param source    a RenderedImage.
         * @param layout    an ImageLayout optionally containing the tile
         *                  grid layout, SampleModel, and ColorModel, or null.
         */
        public MlibMultiplyConstOpImage(RenderedImage source,
                                        Map config,
                                        ImageLayout layout,
                                        double[] constants) {
            super(source, layout, config, true);
            this.constants = MlibUtils.initConstants(constants,
                                                getSampleModel().getNumBands());
            // Set flag to permit in-place operation.
            permitInPlaceOperation();
        }
    
        /**
         * Multiply the pixel values of a rectangle with a given constant.
         * The sources are cobbled.
         *
         * @param sources   an array of sources, guarantee to provide all
         *                  necessary source data for computing the rectangle.
         * @param dest      a tile that contains the rectangle to be computed.
         * @param destRect  the rectangle within this OpImage to be processed.
         */
        protected void computeRect(Raster[] sources,
                                   WritableRaster dest,
                                   Rectangle destRect) {
            Raster source = sources[0];
            Rectangle srcRect = mapDestRect(destRect, 0);
    
            int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest);
    
            MediaLibAccessor srcAccessor = 
                new MediaLibAccessor(source,srcRect,formatTag);
            MediaLibAccessor dstAccessor = 
                new MediaLibAccessor(dest,destRect,formatTag);
    
            mediaLibImage[] srcML = srcAccessor.getMediaLibImages();
            mediaLibImage[] dstML = dstAccessor.getMediaLibImages();
    
            switch (dstAccessor.getDataType()) {
            case DataBuffer.TYPE_BYTE:
            case DataBuffer.TYPE_USHORT:
            case DataBuffer.TYPE_SHORT:
            case DataBuffer.TYPE_INT:
                for (int i = 0; i < dstML.length; i++) {
                    double[] mlconstants = dstAccessor.getDoubleParameters(i, constants);
                    Image.ConstMul(dstML[i], srcML[i], mlconstants);
                }
                break;
    
            case DataBuffer.TYPE_FLOAT:
            case DataBuffer.TYPE_DOUBLE:
                for (int i = 0; i < dstML.length; i++) {
                    double[] mlconstants = dstAccessor.getDoubleParameters(i, constants);
                    Image.ConstMul_Fp(dstML[i], srcML[i], mlconstants);
                }
                break;
    
            default:
                String className = this.getClass().getName();
                throw new RuntimeException(className + JaiI18N.getString("Generic2"));
            }
    
            if (dstAccessor.isDataCopy()) {
                dstAccessor.clampDataArrays();
                dstAccessor.copyDataToRaster();
            }
        }
    
    //     public static OpImage createTestImage(OpImageTester oit) {
    //         double[] consts = { 5, 5, 5 };
    //         return new MlibMultiplyConstOpImage(oit.getSource(), null,
    //                                        new ImageLayout(oit.getSource()),
    //                                        consts);
    //     }
    
    //     // Calls a method on OpImage that uses introspection, to make this
    //     // class, discover it's createTestImage() call, call it and then
    //     // benchmark the performance of the created OpImage chain.
    //     public static void main (String args[]) {
    //         String classname = "com.sun.media.jai.mlib.MlibMultiplyConstOpImage";
    //         OpImageTester.performDiagnostics(classname,args);
    //     }
    }
    jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibDivideOpImage.java0000644000175000017500000001320710203035544026652 0ustar  mathieumathieu/*
     * $RCSfile: MlibDivideOpImage.java,v $
     *
     * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
     *
     * Use is subject to license terms.
     *
     * $Revision: 1.1 $
     * $Date: 2005-02-11 04:55:55 $
     * $State: Exp $
     */
    package com.sun.media.jai.mlib;
    import java.awt.Rectangle;
    import java.awt.image.DataBuffer;
    import java.awt.image.PixelInterleavedSampleModel;
    import java.awt.image.Raster;
    import java.awt.image.RenderedImage;
    import java.awt.image.SampleModel;
    import java.awt.image.WritableRaster;
    import javax.media.jai.ImageLayout;
    import javax.media.jai.OpImage;
    import javax.media.jai.PointOpImage;
    import java.util.Map;
    import com.sun.medialib.mlib.*;
    // import com.sun.media.jai.test.OpImageTester;
    
    /**
     * An OpImage that performs the Divide operation on 2 images through mediaLib.
     *
     */
    final class MlibDivideOpImage extends PointOpImage {
    
        /**
         * Constructs an MlibDivideOpImage. The image dimensions are copied
         * from the source image.  The tile grid layout, SampleModel, and
         * ColorModel may optionally be specified by an ImageLayout object.
         *
         * @param source    a RenderedImage.
         * @param layout    an ImageLayout optionally containing the tile
         *                  grid layout, SampleModel, and ColorModel, or null.
         */
        public MlibDivideOpImage(RenderedImage source1, RenderedImage source2,
                                 Map config,
                                 ImageLayout  layout) {
    	super(source1, source2, layout, config, true);
            // Set flag to permit in-place operation.
            permitInPlaceOperation();
        }
    
        /**
         * Divide the pixel values of a rectangle from the two sources.
         * The sources are cobbled.
         *
         * @param sources   an array of sources, guarantee to provide all
         *                  necessary source data for computing the rectangle.
         * @param dest      a tile that contains the rectangle to be computed.
         * @param destRect  the rectangle within this OpImage to be processed.
         */
        protected void computeRect(Raster[] sources,
                                   WritableRaster dest,
                                   Rectangle destRect) {
    
            int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest);
    
            MediaLibAccessor srcAccessor1 = 
                new MediaLibAccessor(sources[0], destRect,formatTag);
    	MediaLibAccessor srcAccessor2 = 
    	    new MediaLibAccessor(sources[1], destRect,formatTag);
            MediaLibAccessor dstAccessor = 
                new MediaLibAccessor(dest, destRect, formatTag);
    
            mediaLibImage[] srcML1 = srcAccessor1.getMediaLibImages();
            mediaLibImage[] srcML2 = srcAccessor2.getMediaLibImages();
            mediaLibImage[] dstML  = dstAccessor.getMediaLibImages();
    
            switch (dstAccessor.getDataType()) {
            case DataBuffer.TYPE_BYTE:
            case DataBuffer.TYPE_USHORT:
            case DataBuffer.TYPE_SHORT:
            case DataBuffer.TYPE_INT:
                for (int i = 0; i < dstML.length; i++) {
                    Image.DivShift(dstML[i], srcML1[i], srcML2[i], 0);
                }
                break;
    
            case DataBuffer.TYPE_FLOAT:
            case DataBuffer.TYPE_DOUBLE:
                for (int i = 0; i < dstML.length; i++) {
                    Image.Div_Fp(dstML[i], srcML1[i], srcML2[i]);
                }
                break;
    
            default:
                String className = this.getClass().getName();
                throw new RuntimeException(className + JaiI18N.getString("Generic2"));
            }
    
            if (dstAccessor.isDataCopy()) {
                dstAccessor.clampDataArrays();
                dstAccessor.copyDataToRaster();
            }
        }
    
    //     public static void main (String args[]) {
    //         System.out.println("MlibDivideOpImage Test");
    //         ImageLayout layout;
    //         OpImage src1, src2, dst;
    //         Rectangle rect = new Rectangle(0, 0, 5, 5);
    
    //         System.out.println("1. PixelInterleaved byte 3-band");
    //         layout = OpImageTester.createImageLayout(0, 0, 800, 800, 0, 0,
    // 						 200, 200, DataBuffer.TYPE_BYTE,
    // 						 3, false);
    //         src1 = OpImageTester.createRandomOpImage(layout);
    //         src2 = OpImageTester.createRandomOpImage(layout);
    //         dst = new MlibDivideOpImage(src1, src2, null, null);
    //         OpImageTester.testOpImage(dst, rect);
    //         OpImageTester.timeOpImage(dst, 10);
    
    //         System.out.println("2. Banded byte 3-band");
    //         layout = OpImageTester.createImageLayout(0, 0, 800, 800, 0, 0,
    // 						 200, 200, DataBuffer.TYPE_BYTE,
    // 						 3, true);
    //         src1 = OpImageTester.createRandomOpImage(layout);
    //         src2 = OpImageTester.createRandomOpImage(layout);
    //         dst = new MlibDivideOpImage(src1, src2, null, null);
    //         OpImageTester.testOpImage(dst, rect);
    //         OpImageTester.timeOpImage(dst, 10);
    
    //         System.out.println("3. PixelInterleaved int 3-band");
    //         layout = OpImageTester.createImageLayout(0, 0, 512, 512, 0, 0, 200, 200,
    // 						 DataBuffer.TYPE_INT, 3, false);
    //         src1 = OpImageTester.createRandomOpImage(layout);
    //         src2 = OpImageTester.createRandomOpImage(layout);
    //         dst = new MlibDivideOpImage(src1, src2, null, null);
    //         OpImageTester.testOpImage(dst, rect);
    //         OpImageTester.timeOpImage(dst, 10);
    
    //         System.out.println("4. Banded int 3-band");
    //         layout = OpImageTester.createImageLayout(0, 0, 512, 512, 0, 0,
    // 						 200, 200, DataBuffer.TYPE_INT,
    // 						 3, true);
    //         src1 = OpImageTester.createRandomOpImage(layout);
    //         src2 = OpImageTester.createRandomOpImage(layout);
    //         dst = new MlibDivideOpImage(src1, src2, null, null);
    //         OpImageTester.testOpImage(dst, rect);
    //         OpImageTester.timeOpImage(dst, 10);
    //     }
    }
    jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibMaxFilterOpImage.java0000644000175000017500000001367010203035544027345 0ustar  mathieumathieu/*
     * $RCSfile: MlibMaxFilterOpImage.java,v $
     *
     * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
     *
     * Use is subject to license terms.
     *
     * $Revision: 1.1 $
     * $Date: 2005-02-11 04:55:59 $
     * $State: Exp $
     */ 
    package com.sun.media.jai.mlib;
    import java.awt.Rectangle;
    import java.awt.image.DataBuffer;
    import java.awt.image.SampleModel;
    import java.awt.image.Raster;
    import java.awt.image.RenderedImage;
    import java.awt.image.WritableRaster;
    import java.awt.image.renderable.ParameterBlock;
    import java.awt.image.renderable.RenderedImageFactory;
    import javax.media.jai.AreaOpImage;
    import javax.media.jai.BorderExtender;
    import javax.media.jai.ImageLayout;
    import javax.media.jai.OpImage;
    import javax.media.jai.RasterAccessor;
    import java.util.Map;
    import javax.media.jai.operator.MaxFilterDescriptor;
    import javax.media.jai.operator.MaxFilterShape;
    import com.sun.medialib.mlib.*;
    
    /**
     * An OpImage class that subclasses will use to perform
     * MaxFiltering with specific masks.
     *
     * @see javax.media.jai.operator.MaxFilterDescriptor
     *
     */
    final class MlibMaxFilterOpImage extends AreaOpImage {
    
        protected int maskType; //XXX using media lib's type for MEDIAN operator!!
        protected int maskSize;
    
        /**
         * Creates a MlibMaxFilterOpImage given an image source, an
         * optional BorderExtender, a maskType and maskSize.  The image
         * dimensions are derived the source image.  The tile grid layout,
         * SampleModel, and ColorModel may optionally be specified by an
         * ImageLayout object.
         *
         * @param source a RenderedImage.
         * @param extender a BorderExtender, or null.
         * @param layout an ImageLayout optionally containing the tile grid layout,
         *        SampleModel, and ColorModel, or null.
         * @param maskType the filter mask type.
         * @param maskSize the filter mask size.
         */
        public MlibMaxFilterOpImage(RenderedImage source,
                                       BorderExtender extender,
                                       Map config,
                                       ImageLayout layout,
                                       MaxFilterShape maskType,
                                       int maskSize) {
    	super(source,
                  layout,
                  config,
                  true,
                  extender,
                  (maskSize-1)/2,
                  (maskSize-1)/2,
                  (maskSize/2),
                  (maskSize/2));
            this.maskType = mapToMlibMaskType(maskType);
            this.maskSize = maskSize;
        }
    
        private static int mapToMlibMaskType(MaxFilterShape maskType) {
            if(maskType.equals(MaxFilterDescriptor.MAX_MASK_SQUARE)) {
                return Constants.MLIB_MEDIAN_MASK_RECT;
            } else if(maskType.equals(MaxFilterDescriptor.MAX_MASK_PLUS)) {
                return Constants.MLIB_MEDIAN_MASK_PLUS;
            } else if(maskType.equals(MaxFilterDescriptor.MAX_MASK_X)) {
                return Constants.MLIB_MEDIAN_MASK_X;
            } else if(maskType.equals(MaxFilterDescriptor.MAX_MASK_SQUARE_SEPARABLE)) {
                return Constants.MLIB_MEDIAN_MASK_RECT_SEPARABLE;
            }
            throw new RuntimeException(JaiI18N.getString("MaxFilterOpImage0")); 
        }
    
        /**
         * Performs median filtering on a specified rectangle. The sources are
         * cobbled.
         *
         * @param sources an array of source Rasters, guaranteed to provide all
         *                necessary source data for computing the output.
         * @param dest a WritableRaster tile containing the area to be computed.
         * @param destRect the rectangle within dest to be processed.
         */
        protected void computeRect(Raster[] sources,
                                   WritableRaster dest,
                                   Rectangle destRect) {
            Raster source = sources[0];
            Rectangle srcRect = mapDestRect(destRect, 0);
     
            int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest);
     
            MediaLibAccessor srcAccessor =
                new MediaLibAccessor(source,srcRect,formatTag);
            MediaLibAccessor dstAccessor =
                new MediaLibAccessor(dest,destRect,formatTag);
            int numBands = getSampleModel().getNumBands();
     
    
            int cmask = (1 << numBands) -1; 
            mediaLibImage[] srcML = srcAccessor.getMediaLibImages();
            mediaLibImage[] dstML = dstAccessor.getMediaLibImages();
    
            for (int i = 0; i < dstML.length; i++) {
                switch (dstAccessor.getDataType()) {
                case DataBuffer.TYPE_BYTE:
                case DataBuffer.TYPE_USHORT:
                case DataBuffer.TYPE_SHORT:
                case DataBuffer.TYPE_INT:
                    if (maskSize == 3) {
                        // Call appropriate Medialib accelerated function
                        Image.MaxFilter3x3(dstML[i], srcML[i]);
                    } else if (maskSize == 5) {
                        // Call appropriate Medialib accelerated function
                        Image.MaxFilter5x5(dstML[i], srcML[i]);
                    } else if (maskSize == 7) {
                        // Call appropriate Medialib accelerated function
                        Image.MaxFilter7x7(dstML[i], srcML[i]);
                    } 
    
                    break;
                case DataBuffer.TYPE_FLOAT:
                case DataBuffer.TYPE_DOUBLE:
                    if (maskSize == 3) {
                        // Call appropriate Medialib accelerated function
                        Image.MaxFilter3x3_Fp(dstML[i], srcML[i]);
                    } else if (maskSize == 5) {
                        // Call appropriate Medialib accelerated function
                        Image.MaxFilter5x5_Fp(dstML[i], srcML[i]);
                    }  else if (maskSize == 7) {
                        // Call appropriate Medialib accelerated function
                        Image.MaxFilter7x7_Fp(dstML[i], srcML[i]);
                    } 
                    break;
                default:
                    String className = this.getClass().getName();
                    throw new RuntimeException(JaiI18N.getString("Generic2"));
                }
            }
     
            if (dstAccessor.isDataCopy()) {
                dstAccessor.copyDataToRaster();
            }
        }
    }
    jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibWarpPolynomialTableOpImage.java0000644000175000017500000004077210350333605031404 0ustar  mathieumathieu/*
     * $RCSfile: MlibWarpPolynomialTableOpImage.java,v $
     *
     * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
     *
     * Use is subject to license terms.
     *
     * $Revision: 1.2 $
     * $Date: 2005-12-15 18:35:49 $
     * $State: Exp $
     */
    package com.sun.media.jai.mlib;
    import java.awt.Point;
    import java.awt.Rectangle;
    import java.awt.image.DataBuffer;
    import java.awt.image.Raster;
    import java.awt.image.RenderedImage;
    import java.awt.image.WritableRaster;
    import javax.media.jai.BorderExtender;
    import javax.media.jai.ImageLayout;
    import javax.media.jai.Interpolation;
    import javax.media.jai.InterpolationTable;
    import java.util.Map;
    import javax.media.jai.RasterFactory;
    import javax.media.jai.WarpOpImage;
    import javax.media.jai.WarpPolynomial;
    
    import com.sun.medialib.mlib.*;
    import com.sun.media.jai.util.ImageUtil;
    
    /**
     * An OpImage implementing the polynomial "Warp" operation
     * using MediaLib.
     *
     * 

    With warp operations, there is no forward mapping (from source to * destination). JAI images are tiled, while mediaLib does not handle * tiles and consider each tile an individual image. For each tile in * destination, in order not to cobble the entire source image, the * computeTile method in this class attemps to do a backward * mapping on the tile region using the pixels along the perimeter of the * rectangular region. The hope is that the mapped source rectangle * should include all source pixels needed for this particular destination * tile. However, with certain unusual warp points, an inner destination * pixel may be mapped outside of the mapped perimeter pixels. In this * case, this destination pixel is not filled, and left black. * * @see javax.media.jai.operator.WarpDescriptor * @see javax.media.jai.InterpolationTable * @see MlibWarpRIF * * @since 1.1 * */ final class MlibWarpPolynomialTableOpImage extends WarpOpImage { /** The x and y coefficients. */ private double[] xCoeffs; private double[] yCoeffs; /** * converting from interpolation to mlib table for * integral, float and double data type */ private mediaLibImageInterpTable mlibInterpTableI; private mediaLibImageInterpTable mlibInterpTableF; private mediaLibImageInterpTable mlibInterpTableD; /** The pre and post scale factors. */ private double preScaleX; private double preScaleY; private double postScaleX; private double postScaleY; /** * Constructs a MlibWarpPolynomialTableOpImage. * * @param source The source image. * @param layout The destination image layout. * @param warp An object defining the warp algorithm. * @param interp An object describing the interpolation method. */ public MlibWarpPolynomialTableOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, WarpPolynomial warp, Interpolation interp, double[] backgroundValues) { super(source, layout, config, true, extender, interp, warp, backgroundValues); mlibInterpTableI = null; mlibInterpTableF = null; mlibInterpTableD = null; float[] xc = warp.getXCoeffs(); float[] yc = warp.getYCoeffs(); int size = xc.length; xCoeffs = new double[size]; // X and Y coefficients as doubles yCoeffs = new double[size]; for (int i = 0; i < size; i++) { xCoeffs[i] = xc[i]; yCoeffs[i] = yc[i]; } preScaleX = warp.getPreScaleX(); // pre/post factors preScaleY = warp.getPreScaleY(); postScaleX = warp.getPostScaleX(); postScaleY = warp.getPostScaleY(); } /** * Returns the minimum bounding box of the region of the specified * source to which a particular Rectangle of the * destination will be mapped. * * @param destRect the Rectangle in destination coordinates. * @param sourceIndex the index of the source image. * * @return a Rectangle indicating the source bounding box, * or null if the bounding box is unknown. * * @throws IllegalArgumentException if sourceIndex is * negative or greater than the index of the last source. * @throws IllegalArgumentException if destRect is * null. */ protected Rectangle backwardMapRect(Rectangle destRect, int sourceIndex) { // Superclass method will throw documented exceptions if needed. Rectangle wrect = super.backwardMapRect(destRect, sourceIndex); // "Dilate" the backwarp mapped rectangle to account for // the lack of being able to know the floating point result of // mapDestRect() and to mimic what is done in AffineOpImage. // See bug 4518223 for more information. wrect.setBounds(wrect.x - 1, wrect.y - 1, wrect.width + 2, wrect.height + 2); return wrect; } /** * Computes a tile. A new WritableRaster is created to * represent the requested tile. Its width and height equals to this * image's tile width and tile height respectively. If the requested * tile lies outside of the image's boundary, the created raster is * returned with all of its pixels set to 0. * *

    This method overrides the method in WarpOpImage * and performs source cobbling when necessary. MediaLib is used to * calculate the actual warping. * * @param tileX The X index of the tile. * @param tileY The Y index of the tile. * * @return The tile as a Raster. */ public Raster computeTile(int tileX, int tileY) { /* The origin of the tile. */ Point org = new Point(tileXToX(tileX), tileYToY(tileY)); /* Create a new WritableRaster to represent this tile. */ WritableRaster dest = createWritableRaster(sampleModel, org); /* Find the intersection between this tile and the writable bounds. */ Rectangle rect = new Rectangle(org.x, org.y, tileWidth, tileHeight); Rectangle destRect = rect.intersection(computableBounds); Rectangle destRect1 = rect.intersection(getBounds()); if (destRect.isEmpty()) { if (setBackground) { ImageUtil.fillBackground(dest, destRect1, backgroundValues); } return dest; // tile completely outside of writable bounds } /* Map destination rectangle to source space. */ Rectangle srcRect = backwardMapRect(destRect, 0).intersection( getSourceImage(0).getBounds()); if (srcRect.isEmpty()) { if (setBackground) { ImageUtil.fillBackground(dest, destRect1, backgroundValues); } return dest; // outside of source bounds } if (!destRect1.equals(destRect)) { // beware that destRect1 contains destRect ImageUtil.fillBordersWithBackgroundValues(destRect1, destRect, dest, backgroundValues); } /* Add the interpolation paddings. */ int l = interp== null ? 0 : interp.getLeftPadding(); int r = interp== null ? 0 : interp.getRightPadding(); int t = interp== null ? 0 : interp.getTopPadding(); int b = interp== null ? 0 : interp.getBottomPadding(); srcRect = new Rectangle(srcRect.x - l, srcRect.y - t, srcRect.width + l + r, srcRect.height + t + b); /* Cobble source into one Raster. */ Raster[] sources = new Raster[1]; sources[0] = getBorderExtender() != null ? getSourceImage(0).getExtendedData(srcRect, extender) : getSourceImage(0).getData(srcRect); computeRect(sources, dest, destRect); // Recycle the source tile if(getSourceImage(0).overlapsMultipleTiles(srcRect)) { recycleTile(sources[0]); } return dest; } /** * Performs the "Warp" operation on a rectangular region of * the same. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; /* Find the mediaLib data tag. */ int formatTag = MediaLibAccessor.findCompatibleTag(sources, dest); MediaLibAccessor srcMA = new MediaLibAccessor(source, source.getBounds(), formatTag); MediaLibAccessor dstMA = new MediaLibAccessor(dest, destRect, formatTag); mediaLibImage[] srcMLI = srcMA.getMediaLibImages(); mediaLibImage[] dstMLI = dstMA.getMediaLibImages(); switch (dstMA.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: if (mlibInterpTableI==null){ InterpolationTable jtable = (InterpolationTable)interp; mlibInterpTableI = new mediaLibImageInterpTable(Constants.MLIB_INT, jtable.getWidth(), jtable.getHeight(), jtable.getLeftPadding(), jtable.getTopPadding(), jtable.getSubsampleBitsH(), jtable.getSubsampleBitsV(), jtable.getPrecisionBits(), jtable.getHorizontalTableData(), jtable.getVerticalTableData()); } if (setBackground) for (int i = 0 ; i < dstMLI.length; i++) { Image.PolynomialWarpTable2(dstMLI[i], srcMLI[i], xCoeffs, yCoeffs, destRect.x, destRect.y, source.getMinX(), source.getMinY(), preScaleX, preScaleY, postScaleX, postScaleY, mlibInterpTableI, Constants.MLIB_EDGE_DST_NO_WRITE, intBackgroundValues); } else for (int i = 0 ; i < dstMLI.length; i++) { Image.PolynomialWarpTable(dstMLI[i], srcMLI[i], xCoeffs, yCoeffs, destRect.x, destRect.y, source.getMinX(), source.getMinY(), preScaleX, preScaleY, postScaleX, postScaleY, mlibInterpTableI, Constants.MLIB_EDGE_DST_NO_WRITE); MlibUtils.clampImage(dstMLI[i], getColorModel()); } break; case DataBuffer.TYPE_FLOAT: if (mlibInterpTableF==null){ InterpolationTable jtable = (InterpolationTable)interp; mlibInterpTableF = new mediaLibImageInterpTable(Constants.MLIB_FLOAT, jtable.getWidth(), jtable.getHeight(), jtable.getLeftPadding(), jtable.getTopPadding(), jtable.getSubsampleBitsH(), jtable.getSubsampleBitsV(), jtable.getPrecisionBits(), jtable.getHorizontalTableDataFloat(), jtable.getVerticalTableDataFloat()); } if (setBackground) for (int i = 0 ; i < dstMLI.length; i++) { Image.PolynomialWarpTable2_Fp(dstMLI[i], srcMLI[i], xCoeffs, yCoeffs, destRect.x, destRect.y, source.getMinX(), source.getMinY(), preScaleX, preScaleY, postScaleX, postScaleY, mlibInterpTableD, Constants.MLIB_EDGE_DST_NO_WRITE, backgroundValues); } else for (int i = 0 ; i < dstMLI.length; i++) { Image.PolynomialWarpTable_Fp(dstMLI[i], srcMLI[i], xCoeffs, yCoeffs, destRect.x, destRect.y, source.getMinX(), source.getMinY(), preScaleX, preScaleY, postScaleX, postScaleY, mlibInterpTableD, Constants.MLIB_EDGE_DST_NO_WRITE); } break; case DataBuffer.TYPE_DOUBLE: if (mlibInterpTableD == null){ InterpolationTable jtable = (InterpolationTable)interp; mlibInterpTableD = new mediaLibImageInterpTable(Constants.MLIB_DOUBLE, jtable.getWidth(), jtable.getHeight(), jtable.getLeftPadding(), jtable.getTopPadding(), jtable.getSubsampleBitsH(), jtable.getSubsampleBitsV(), jtable.getPrecisionBits(), jtable.getHorizontalTableDataDouble(), jtable.getVerticalTableDataDouble()); } if (setBackground) for (int i = 0 ; i < dstMLI.length; i++) { Image.PolynomialWarpTable2_Fp(dstMLI[i], srcMLI[i], xCoeffs, yCoeffs, destRect.x, destRect.y, source.getMinX(), source.getMinY(), preScaleX, preScaleY, postScaleX, postScaleY, mlibInterpTableD, Constants.MLIB_EDGE_DST_NO_WRITE, backgroundValues); } else for (int i = 0 ; i < dstMLI.length; i++) { Image.PolynomialWarpTable_Fp(dstMLI[i], srcMLI[i], xCoeffs, yCoeffs, destRect.x, destRect.y, source.getMinX(), source.getMinY(), preScaleX, preScaleY, postScaleX, postScaleY, mlibInterpTableD, Constants.MLIB_EDGE_DST_NO_WRITE); } break; default: throw new RuntimeException(JaiI18N.getString("Generic2")); } if (dstMA.isDataCopy()) { dstMA.clampDataArrays(); dstMA.copyDataToRaster(); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibScaleTableOpImage.java0000644000175000017500000001532710350333605027454 0ustar mathieumathieu/* * $RCSfile: MlibScaleTableOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-12-15 18:35:47 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationTable; import javax.media.jai.OpImage; import java.util.Map; import com.sun.medialib.mlib.*; /** * An OpImage class that scales an image using interpolation coefficients * specified in a table format. * */ final class MlibScaleTableOpImage extends MlibScaleOpImage { /** * Constructs an MlibScaleTableOpImage. The image dimensions are copied * from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile * grid layout, SampleModel, and ColorModel, or null. * @param xScale the x scaling factor. * @param yScale the y scaling factor. * @param xTrans the x translation factor. * @param yTrans the y translation factor. * @param interp the InterpolationTable object. */ public MlibScaleTableOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, float xScale, float yScale, float xTrans, float yTrans, Interpolation interp) { super(source, extender, config, layout, xScale, yScale, xTrans, yTrans, interp, true); } /** * Scale the given rectangle by the specified scale and translation * factors. The sources are cobbled. * * @param sources an array of sources, guarantee to provide all * necessary source data for computing the rectangle. * @param dest a tile that contains the rectangle to be computed. * @param destRect the rectangle within this OpImage to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Cast the Interpolation object to InterpolationTable object InterpolationTable jtable = (InterpolationTable)interp; // The Medialib InterpolationTable class equivalent mediaLibImageInterpTable mlibInterpTable; Raster source = sources[0]; Rectangle srcRect = source.getBounds(); int formatTag = MediaLibAccessor.findCompatibleTag(sources, dest); MediaLibAccessor srcAccessor = new MediaLibAccessor(source, srcRect, formatTag); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest, destRect, formatTag); // Get the floating point scale factors float mlibScaleX = (float)scaleXRationalNum / (float)scaleXRationalDenom; float mlibScaleY = (float)scaleYRationalNum / (float)scaleYRationalDenom; // Translation to be specified to Medialib. Note that we have to // specify an additional translation since all images are 0 based // in Medialib. // Calculate intermediate values. float tempDX = (float)(srcRect.x * scaleXRationalNum) / (float)scaleXRationalDenom; float tempDY = (float)(srcRect.y * scaleYRationalNum) / (float)scaleYRationalDenom; float tx = transX - destRect.x + tempDX; float ty = transY - destRect.y + tempDY; mediaLibImage srcML[], dstML[]; switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: mlibInterpTable = new mediaLibImageInterpTable(Constants.MLIB_INT, jtable.getWidth(), jtable.getHeight(), jtable.getLeftPadding(), jtable.getTopPadding(), jtable.getSubsampleBitsH(), jtable.getSubsampleBitsV(), jtable.getPrecisionBits(), jtable.getHorizontalTableData(), jtable.getVerticalTableData()); srcML = srcAccessor.getMediaLibImages(); dstML = dstAccessor.getMediaLibImages(); for (int i = 0 ; i < dstML.length; i++) { Image.ZoomTranslateTable(dstML[i], srcML[i], (double)mlibScaleX, (double)mlibScaleY, (double)tx, (double)ty, mlibInterpTable, Constants.MLIB_EDGE_DST_NO_WRITE); MlibUtils.clampImage(dstML[i], getColorModel()); } break; case DataBuffer.TYPE_FLOAT: mlibInterpTable = new mediaLibImageInterpTable( Constants.MLIB_FLOAT, jtable.getWidth(), jtable.getHeight(), jtable.getLeftPadding(), jtable.getTopPadding(), jtable.getSubsampleBitsH(), jtable.getSubsampleBitsV(), jtable.getPrecisionBits(), jtable.getHorizontalTableDataFloat(), jtable.getVerticalTableDataFloat()); srcML = srcAccessor.getMediaLibImages(); dstML = dstAccessor.getMediaLibImages(); for (int i = 0 ; i < dstML.length; i++) { Image.ZoomTranslateTable_Fp(dstML[i], srcML[i], (double)mlibScaleX, (double)mlibScaleY, (double)tx, (double)ty, mlibInterpTable, Constants.MLIB_EDGE_DST_NO_WRITE); } break; case DataBuffer.TYPE_DOUBLE: mlibInterpTable = new mediaLibImageInterpTable( Constants.MLIB_DOUBLE, jtable.getWidth(), jtable.getHeight(), jtable.getLeftPadding(), jtable.getTopPadding(), jtable.getSubsampleBitsH(), jtable.getSubsampleBitsV(), jtable.getPrecisionBits(), jtable.getHorizontalTableDataDouble(), jtable.getVerticalTableDataDouble()); srcML = srcAccessor.getMediaLibImages(); dstML = dstAccessor.getMediaLibImages(); for (int i = 0 ; i < dstML.length; i++) { Image.ZoomTranslateTable_Fp(dstML[i], srcML[i], (double)mlibScaleX, (double)mlibScaleY, (double)tx, (double)ty, mlibInterpTable, Constants.MLIB_EDGE_DST_NO_WRITE); } break; default: String className = this.getClass().getName(); throw new RuntimeException(JaiI18N.getString("Generic2")); } if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibMeanRIF.java0000644000175000017500000000366610203035544025435 0ustar mathieumathieu/* * $RCSfile: MlibMeanRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:59 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ROI; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "Mean" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.MeanDescriptor * @see MlibMeanOpImage * * @since EA3 * */ public class MlibMeanRIF implements RenderedImageFactory { /** Constructor. */ public MlibMeanRIF() {} /** * Creates a new instance of MlibMeanOpImage in * the rendered image mode. * * @param args The source image and the parameters. * @param hints Rendering hints are ignored. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { if (!MediaLibAccessor.isMediaLibCompatible(args)) { return null; } RenderedImage source = args.getRenderedSource(0); ROI roi = (ROI)args.getObjectParameter(0); int xPeriod = args.getIntParameter(1); int yPeriod = args.getIntParameter(2); int xStart = source.getMinX(); // default values int yStart = source.getMinY(); int maxWidth = source.getWidth(); int maxHeight = source.getHeight(); if (roi != null && !roi.contains(xStart, yStart, maxWidth, maxHeight)) { return null; } // mediaLib supports only a sampling period of 1 if ((xPeriod != 1) || (yPeriod != 1)) { return null; } return new MlibMeanOpImage(source, roi, xStart, yStart, xPeriod, yPeriod); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibBandCombineOpImage.java0000644000175000017500000001516210203035544027611 0ustar mathieumathieu/* * $RCSfile: MlibBandCombineOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:50 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.ComponentSampleModel; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.util.Map; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PlanarImage; import javax.media.jai.PointOpImage; import javax.media.jai.RasterFactory; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.util.JDKWorkarounds; import com.sun.medialib.mlib.*; /** * An OpImage class that performs a "BandCombine" operation for 3x3 images. * */ final class MlibBandCombineOpImage extends PointOpImage { /* Color conversion matrix. */ private double[] cmat = new double[9]; /* Offset vector. */ private double[] offset = new double[3]; /* Flag indicating whether the offset is non-zero. */ private boolean isOffsetNonZero = false; /** * Constructs an MlibBandCombineOpImage. The image dimensions are copied * from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param layout an ImageLayout optionally containing the tile * grid layout, SampleModel, and ColorModel, or null. */ public MlibBandCombineOpImage(RenderedImage source, Map config, ImageLayout layout, double[][] matrix) { super(source, layout, config, true); int numBands = matrix.length; // matrix height is dst numBands if (getSampleModel().getNumBands() != numBands) { sampleModel = RasterFactory.createComponentSampleModel(sampleModel, sampleModel.getDataType(), tileWidth, tileHeight, numBands); if(colorModel != null && !JDKWorkarounds.areCompatibleDataModels(sampleModel, colorModel)) { colorModel = ImageUtil.getCompatibleColorModel(sampleModel, config); } } // Class of SampleModel should have been verified in the RIF // by isMediaLibCompatible(). ComponentSampleModel csm = (ComponentSampleModel)source.getSampleModel(); int[] bankIndices = csm.getBankIndices(); int[] bandOffsets = csm.getBandOffsets(); // The matrix must be 3-by-4 for this to work: the check is done // in the RIF. Note that the matrix may be modified to simulate the // required interchange of bands 1 and 3. if(bankIndices[0] == bankIndices[1] && bankIndices[0] == bankIndices[2] && bandOffsets[0] > bandOffsets[2]) { for(int j = 0; j < 3; j++) { int k = 8 - 3*j; for(int i = 0; i < 3; i++) { cmat[k--] = matrix[j][i]; } offset[2-j] = matrix[j][3]; if(offset[j] != 0.0) { isOffsetNonZero = true; } } } else { for(int j = 0; j < 3; j++) { int k = 3*j; for(int i = 0; i < 3; i++) { cmat[k++] = matrix[j][i]; } offset[j] = matrix[j][3]; if(offset[j] != 0.0) { isOffsetNonZero = true; } } } } /** * Combine the selected bands. * The sources are cobbled. * * @param sources an array of sources, guarantee to provide all * necessary source data for computing the rectangle. * @param dest a tile that contains the rectangle to be computed. * @param destRect the rectangle within this OpImage to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; Rectangle srcRect = mapDestRect(destRect, 0); int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest); MediaLibAccessor srcAccessor = new MediaLibAccessor(source,srcRect,formatTag); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest,destRect,formatTag); mediaLibImage[] srcML = srcAccessor.getMediaLibImages(); mediaLibImage[] dstML = dstAccessor.getMediaLibImages(); switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: for (int i = 0; i < dstML.length; i++) { if(isOffsetNonZero) { Image.ColorConvert2(dstML[i], srcML[i], cmat, offset); } else { Image.ColorConvert1(dstML[i], srcML[i], cmat); } } break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: for (int i = 0; i < dstML.length; i++) { if(isOffsetNonZero) { Image.ColorConvert2_Fp(dstML[i], srcML[i], cmat, offset); } else { Image.ColorConvert1_Fp(dstML[i], srcML[i], cmat); } } break; default: String className = this.getClass().getName(); throw new RuntimeException(className + JaiI18N.getString("Generic2")); } if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibErrorDiffusionOpImage.java0000644000175000017500000002427510203035544030415 0ustar mathieumathieu/* * $RCSfile: MlibErrorDiffusionOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:55 $ * $State: Exp $ */package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.IndexColorModel; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.util.Map; import javax.media.jai.ImageLayout; import javax.media.jai.ColorCube; import javax.media.jai.KernelJAI; import javax.media.jai.LookupTableJAI; import javax.media.jai.RasterFactory; import javax.media.jai.UntiledOpImage; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.util.JDKWorkarounds; import com.sun.medialib.mlib.Image; import com.sun.medialib.mlib.mediaLibImage; import com.sun.medialib.mlib.mediaLibImageColormap; /** * An OpImage class that performs an "ErrorDiffusion" operation. * */ final class MlibErrorDiffusionOpImage extends UntiledOpImage { /** * The integer-to-float scale factor for kernel elements. */ private static final int KERNEL_SCALE_EXPONENT = 16; /** * The mediaLib colormap. */ protected mediaLibImageColormap mlibColormap; /** * The scaled values of the error kernel in row major order. */ protected int[] kernel; /** * The width of the kernel. */ protected int kernelWidth; /** * The height of the kernel. */ protected int kernelHeight; /** * The X position of the key element in the kernel. */ protected int kernelKeyX; /** * The Y position of the key element in the kernel. */ protected int kernelKeyY; /** * Scale factor to convert kernel from floating point to integer. */ protected int kernelScale; /** * Force the destination image to be single-banded. */ // Copied whole hog from ErrorDiffusionOpImage. static ImageLayout layoutHelper(ImageLayout layout, RenderedImage source, LookupTableJAI colormap) { // Create or clone the layout. ImageLayout il = layout == null ? new ImageLayout() : (ImageLayout)layout.clone(); // Force the destination and source origins and dimensions to coincide. il.setMinX(source.getMinX()); il.setMinY(source.getMinY()); il.setWidth(source.getWidth()); il.setHeight(source.getHeight()); // Get the SampleModel. SampleModel sm = il.getSampleModel(source); // Ensure an appropriate SampleModel. if(colormap.getNumBands() == 1 && colormap.getNumEntries() == 2 && !ImageUtil.isBinary(il.getSampleModel(source))) { sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, il.getTileWidth(source), il.getTileHeight(source), 1); il.setSampleModel(sm); } // Make sure that this OpImage is single-banded. if (sm.getNumBands() != 1) { sm = RasterFactory.createComponentSampleModel(sm, sm.getTransferType(), sm.getWidth(), sm.getHeight(), 1); il.setSampleModel(sm); // Clear the ColorModel mask if needed. ColorModel cm = il.getColorModel(null); if(cm != null && !JDKWorkarounds.areCompatibleDataModels(sm, cm)) { // Clear the mask bit if incompatible. il.unsetValid(ImageLayout.COLOR_MODEL_MASK); } } // Set an IndexColorModel on the image if: // a. none is provided in the layout; // b. source, destination, and colormap have byte data type; // c. the colormap has 3 bands. if((layout == null || !il.isValid(ImageLayout.COLOR_MODEL_MASK)) && source.getSampleModel().getDataType() == DataBuffer.TYPE_BYTE && sm.getDataType() == DataBuffer.TYPE_BYTE && colormap.getDataType() == DataBuffer.TYPE_BYTE && colormap.getNumBands() == 3) { ColorModel cm = source.getColorModel(); if(cm == null || (cm != null && cm.getColorSpace().isCS_sRGB())) { int size = colormap.getNumEntries(); byte[][] cmap = new byte[3][256]; for(int i = 0; i < 3; i++) { byte[] band = cmap[i]; byte[] data = colormap.getByteData(i); int offset = colormap.getOffset(i); int end = offset + size; for(int j = 0; j < offset; j++) { band[j] = (byte)0; } for(int j = offset; j < end; j++) { band[j] = data[j - offset]; } for(int j = end; j < 256; j++) { band[j] = (byte)0xFF; } } il.setColorModel(new IndexColorModel(8, 256, cmap[0], cmap[1], cmap[2])); } } return il; } /** * Constructs an MlibErrorDiffusionOpImage. * * @param source a RenderedImage. * @param config configuration settings. * @param layout an ImageLayout optionally containing the tile * grid layout, SampleModel, and ColorModel, or null. * @param colormap the target color quantization mapping. * @param errorKernel the error diffusion coefficients. */ public MlibErrorDiffusionOpImage(RenderedImage source, Map config, ImageLayout layout, LookupTableJAI colormap, KernelJAI errorKernel) { // Pass the ImageLayout up directly as it was pre-proceseed // in MlibErrorDiffusionRIF. super(source, config, layout); // Initialize the mediaLib colormap. It is implicitly assumed that // all data type and dimension checking was performed in the RIF. this.mlibColormap = Image.ColorDitherInit(colormap instanceof ColorCube ? ((ColorCube)colormap).getDimension() : null, Image.MLIB_BYTE, ImageUtil.isBinary(sampleModel) ? Image.MLIB_BIT : Image.MLIB_BYTE, colormap.getNumBands(), colormap.getNumEntries(), colormap.getOffset(), colormap.getByteData()); // Initialize kernel constants. this.kernelWidth = errorKernel.getWidth(); this.kernelHeight = errorKernel.getHeight(); this.kernelKeyX = errorKernel.getXOrigin(); this.kernelKeyY = errorKernel.getYOrigin(); this.kernelScale = 0x1 << KERNEL_SCALE_EXPONENT; // Initialize the integral kernel coefficients. float[] kernelData = errorKernel.getKernelData(); int numElements = kernelData.length; this.kernel = new int[numElements]; for(int i = 0; i < numElements; i++) { kernel[i] = (int)(kernelData[i]*kernelScale); } /* XXX kernelWidth = kernelHeight = 3; kernelKeyX = kernelKeyY = 1; kernel = new int[] {0, 0, 0, 0, 0, 7, 3, 5, 1}; kernelScale = 4; */ } /** * Calculate the destination image from the source image. * * @param source The source Raster; should be the whole image. * @param dest The destination WritableRaster; should be the whole image. * @param destRect The destination Rectangle; should equal the destination * image bounds. */ protected void computeImage(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; int sourceFormatTag; int destFormatTag; if(ImageUtil.isBinary(dest.getSampleModel())) { // Hack: derive the source format tag as if it was writing to // a destination with the same layout as itself. sourceFormatTag = MediaLibAccessor.findCompatibleTag(sources, source); // Hard-code the destination format tag as we know that the image // has a bilevel layout. destFormatTag = dest.getSampleModel().getDataType() | MediaLibAccessor.BINARY | MediaLibAccessor.UNCOPIED; } else { sourceFormatTag = destFormatTag = MediaLibAccessor.findCompatibleTag(sources, dest); } MediaLibAccessor srcAccessor = new MediaLibAccessor(sources[0], destRect, sourceFormatTag, false); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest, destRect, destFormatTag, true); mediaLibImage[] srcML = srcAccessor.getMediaLibImages(); mediaLibImage[] dstML = dstAccessor.getMediaLibImages(); Image.ColorErrorDiffusionMxN(dstML[0], srcML[0], kernel, kernelWidth, kernelHeight, kernelKeyX, kernelKeyY, KERNEL_SCALE_EXPONENT, mlibColormap); if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibNotRIF.java0000644000175000017500000000374710203035544025315 0ustar mathieumathieu/* * $RCSfile: MlibNotRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:02 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "Not" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.NotDescriptor * @see MlibNotOpImage * */ public class MlibNotRIF implements RenderedImageFactory { // NotRIF { /** Constructor. */ public MlibNotRIF() {} /** * Creates a new instance of MlibNotOpImage in * the rendered image mode. * * @param args The source image. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout)) { return null; } /* Check whether dest has data type of float or double. */ if (layout != null) { SampleModel sm = layout.getSampleModel(null); if (sm != null) { int dtype = sm.getDataType(); if (dtype == DataBuffer.TYPE_FLOAT || dtype == DataBuffer.TYPE_DOUBLE) { return null; } } } return new MlibNotOpImage(args.getRenderedSource(0), hints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibScaleOpImage.java0000644000175000017500000000753510203035544026504 0ustar mathieumathieu/* * $RCSfile: MlibScaleOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:04 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.RenderedImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.ScaleOpImage; import java.util.Map; /** * MlibScaleOpImage extends ScaleOpImage for use by further extension * classes. * * @see ScaleOpImage * */ abstract class MlibScaleOpImage extends ScaleOpImage { /** * Constructs a MlibScaleOpImage from a RenderedImage source, * Interpolation object, x and y scale values. The image * dimensions are determined by forward-mapping the source bounds. * The tile grid layout, SampleModel, and ColorModel are specified * by the image source, possibly overridden by values from the * ImageLayout parameter. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * or null. If null, a default cache will be used. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param scaleX scale factor along x axis. * @param scaleY scale factor along y axis. * @param transX translation factor along x axis. * @param transY translation factor along y axis. * @param interp an Interpolation object to use for resampling. * @param cobbleSources a boolean indicating whether computeRect() * expects contiguous sources. */ public MlibScaleOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, float scaleX, float scaleY, float transX, float transY, Interpolation interp, boolean cobbleSources) { super(source, layout, config, cobbleSources, extender, interp, scaleX, scaleY, transX, transY); // If the user did not provide a BorderExtender, attach a // BorderExtenderCopy to Medialib such that when Medialib // ask for additional source which may lie outside the // bounds, it always gets it. this.extender = (extender == null) ? BorderExtender.createInstance(BorderExtender.BORDER_COPY) : extender; } // Override backwardMapRect to pad the source by one extra pixel // in all directions for non Nearest Neighbor interpolations, so // that precision issues don't cause Medialib to not write areas // in the destination rectangle. /** * Returns the minimum bounding box of the region of the specified * source to which a particular Rectangle of the * destination will be mapped. * * @param destRect the Rectangle in destination coordinates. * @param sourceIndex the index of the source image. * * @return a Rectangle indicating the source bounding box, * or null if the bounding box is unknown. * * @throws IllegalArgumentException if sourceIndex is * negative or greater than the index of the last source. * @throws IllegalArgumentException if destRect is * null. */ protected Rectangle backwardMapRect(Rectangle destRect, int sourceIndex) { Rectangle srcRect = super.backwardMapRect(destRect, sourceIndex); Rectangle paddedSrcRect = new Rectangle(srcRect.x - 1, srcRect.y - 1, srcRect.width + 2, srcRect.height + 2); return paddedSrcRect; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibInvertRIF.java0000644000175000017500000000316110203035544026012 0ustar mathieumathieu/* * $RCSfile: MlibInvertRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:58 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "Invert" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.InvertDescriptor * @see MlibInvertOpImage * * @since 1.0 * */ public class MlibInvertRIF implements RenderedImageFactory { /** Constructor. */ public MlibInvertRIF() {} /** * Creates a new instance of MlibInvertOpImage in * the rendered image mode. * * @param args The source image to be inverted. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout)) { return null; } return new MlibInvertOpImage(args.getRenderedSource(0), hints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibClampOpImage.java0000644000175000017500000000776710203035544026520 0ustar mathieumathieu/* * $RCSfile: MlibClampOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:51 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.PointOpImage; import java.util.Map; import com.sun.media.jai.util.ImageUtil; import com.sun.medialib.mlib.*; /** * An OpImage implementing the "Clamp" operation * using MediaLib. * * @see javax.media.jai.operator.ClampDescriptor * @see MlibClampRIF * * @since 1.0 * */ final class MlibClampOpImage extends PointOpImage { /** The lower bound, one for each band. */ private double[] low; /** The integer version of lower bound. */ private int[] lowInt; /** The upper bound, one for each band. */ private double[] high; /** The integer version of upper bound. */ private int[] highInt; /** Constructor. */ public MlibClampOpImage(RenderedImage source, Map config, ImageLayout layout, double[] low, double[] high) { super(source, layout, config, true); int numBands = getSampleModel().getNumBands(); this.low = new double[numBands]; this.lowInt = new int[numBands]; this.high = new double[numBands]; this.highInt = new int[numBands]; for (int i = 0; i < numBands; i++) { if (low.length < numBands) { this.low[i] = low[0]; } else { this.low[i] = low[i]; } this.lowInt[i] = ImageUtil.clampRoundInt(this.low[i]); if (high.length < numBands) { this.high[i] = high[0]; } else { this.high[i] = high[i]; } this.highInt[i] = ImageUtil.clampRoundInt(this.high[i]); } // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * Performs the "Clamp" operation on a rectangular region of * the same. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { int formatTag = MediaLibAccessor.findCompatibleTag(sources, dest); MediaLibAccessor srcMA = new MediaLibAccessor(sources[0], destRect, formatTag); MediaLibAccessor dstMA = new MediaLibAccessor(dest, destRect, formatTag); mediaLibImage[] srcMLI = srcMA.getMediaLibImages(); mediaLibImage[] dstMLI = dstMA.getMediaLibImages(); switch (dstMA.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: for (int i = 0 ; i < dstMLI.length; i++) { int[] mlLow = dstMA.getIntParameters(i, lowInt); int[] mlHigh = dstMA.getIntParameters(i, highInt); Image.Thresh4(dstMLI[i], srcMLI[i], mlHigh, mlLow, mlHigh, mlLow); } break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: for (int i = 0 ; i < dstMLI.length; i++) { double[] mlLow = dstMA.getDoubleParameters(i, low); double[] mlHigh = dstMA.getDoubleParameters(i, high); Image.Thresh4_Fp(dstMLI[i], srcMLI[i], mlHigh, mlLow, mlHigh, mlLow); } break; default: throw new RuntimeException(JaiI18N.getString("Generic2")); } if (dstMA.isDataCopy()) { dstMA.clampDataArrays(); dstMA.copyDataToRaster(); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibTranslateRIF.java0000644000175000017500000001154410203035544026504 0ustar mathieumathieu/* * $RCSfile: MlibTranslateRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:08 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationNearest; import javax.media.jai.InterpolationBilinear; import javax.media.jai.InterpolationBicubic; import javax.media.jai.InterpolationBicubic2; import javax.media.jai.InterpolationTable; import java.util.Map; import javax.media.jai.BorderExtender; import com.sun.media.jai.opimage.RIFUtil; import com.sun.media.jai.opimage.TranslateIntOpImage; /** * A RIF supporting the "Translate" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.TranslateDescriptor * @see MlibScaleOpImage */ public class MlibTranslateRIF implements RenderedImageFactory { private static final float TOLERANCE = 0.01F; /** Constructor. */ public MlibTranslateRIF() {} /** * Creates a new instance of MlibAffineOpImage in * the rendered image mode. * * @param args The source image, the AffineTransform, * and the Interpolation. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { RenderedImage source = args.getRenderedSource(0); float xTrans = args.getFloatParameter(0); float yTrans = args.getFloatParameter(1); Interpolation interp = (Interpolation) args.getObjectParameter(2); /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if ((Math.abs(xTrans - (int)xTrans) < TOLERANCE) && (Math.abs(yTrans - (int)yTrans) < TOLERANCE) && layout == null) { // TranslateIntOpImage can't deal with ImageLayout hint return new TranslateIntOpImage(source, hints, (int)xTrans, (int)yTrans); } else { if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout) || // Medialib cannot deal with source image having tiles with any // dimension greater than or equal to 32768 source.getTileWidth() >= 32768 || source.getTileHeight() >= 32768) { return null; } /* Get BorderExtender from hints if any. */ BorderExtender extender = RIFUtil.getBorderExtenderHint(hints); /* * Call the Scale operation, since it encapsulates Translate * and is better optimized than Affine. */ float xScale = 1.0F; float yScale = 1.0F; if (interp instanceof InterpolationNearest) { return new MlibScaleNearestOpImage(source, extender, hints, layout, xScale, yScale, xTrans, yTrans, interp); } else if (interp instanceof InterpolationBilinear) { return new MlibScaleBilinearOpImage(source, extender, hints, layout, xScale, yScale, xTrans, yTrans, interp); } else if (interp instanceof InterpolationBicubic || interp instanceof InterpolationBicubic2) { return new MlibScaleBicubicOpImage(source, extender, hints, layout, xScale, yScale, xTrans, yTrans, interp); } else if (interp instanceof InterpolationTable) { return new MlibScaleTableOpImage(source, extender, hints, layout, xScale, yScale, xTrans, yTrans, interp); } else { return null; } } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibTransposeOpImage.java0000644000175000017500000001477210347635673027456 0ustar mathieumathieu/* * $RCSfile: MlibTransposeOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-12-13 21:23:07 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.GeometricOpImage; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PlanarImage; import javax.media.jai.RasterFactory; import java.util.Map; import javax.media.jai.RasterAccessor; import com.sun.media.jai.opimage.TransposeOpImage; import com.sun.medialib.mlib.*; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class to perform a transpose (flip) of an image. * * @since EA3 * */ final class MlibTransposeOpImage extends TransposeOpImage { /** * Constructs an TransposeOpImage from a RenderedImage source, * and Transpose type. The image dimensions are determined by * forward-mapping the source bounds. * The tile grid layout, SampleModel, and ColorModel are specified * by the image source, possibly overridden by values from the * ImageLayout parameter. * * @param source a RenderedImage. * or null. If null, a default cache will be used. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param type the desired Tranpose type. */ public MlibTransposeOpImage(RenderedImage source, Map config, ImageLayout layout, int type) { super(source, config, layout, type); } public Raster computeTile(int tileX, int tileY) { // // Create a new WritableRaster to represent this tile. // Point org = new Point(tileXToX(tileX), tileYToY(tileY)); WritableRaster dest = createWritableRaster(sampleModel, org); // // Clip output rectangle to image bounds. // Rectangle destRect = getTileRect(tileX, tileY).intersection(getBounds()); // // Get source // PlanarImage src = getSourceImage(0); // // Determine effective source bounds. // Rectangle srcRect = mapDestRect(destRect, 0).intersection(src.getBounds()); Raster[] sources = new Raster[1]; sources[0] = src.getData(srcRect); // // Compute the destination. // computeRect(sources, dest, destRect); // Recycle the source tile if(src.overlapsMultipleTiles(srcRect)) { recycleTile(sources[0]); } return dest; } protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; Rectangle srcRect = source.getBounds(); int formatTag = MediaLibAccessor.findCompatibleTag(sources, dest); MediaLibAccessor srcAccessor = new MediaLibAccessor(source,srcRect,formatTag); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest,destRect,formatTag); int numBands = getSampleModel().getNumBands(); mediaLibImage srcML[], dstML[]; switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: srcML = srcAccessor.getMediaLibImages(); dstML = dstAccessor.getMediaLibImages(); switch (type) { case 0: // FLIP_VERTICAL Image.FlipX(dstML[0], srcML[0]); break; case 1: // FLIP_HORIZONTAL Image.FlipY(dstML[0], srcML[0]); break; case 2: // FLIP_DIAGONAL Image.FlipMainDiag(dstML[0], srcML[0]); break; case 3: // FLIP_ANTIDIAGONAL Image.FlipAntiDiag(dstML[0], srcML[0]); break; case 4: // ROTATE_90 Image.Rotate90(dstML[0], srcML[0]); break; case 5: // ROTATE_180 Image.Rotate180(dstML[0], srcML[0]); break; case 6: // ROTATE_270 Image.Rotate270(dstML[0], srcML[0]); break; } break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: srcML = srcAccessor.getMediaLibImages(); dstML = dstAccessor.getMediaLibImages(); switch (type) { case 0: // FLIP_VERTICAL Image.FlipX_Fp(dstML[0], srcML[0]); break; case 1: // FLIP_HORIZONTAL Image.FlipY_Fp(dstML[0], srcML[0]); break; case 2: // FLIP_DIAGONAL Image.FlipMainDiag_Fp(dstML[0], srcML[0]); break; case 3: // FLIP_ANTIDIAGONAL Image.FlipAntiDiag_Fp(dstML[0], srcML[0]); break; case 4: // ROTATE_90 Image.Rotate90_Fp(dstML[0], srcML[0]); break; case 5: // ROTATE_180 Image.Rotate180_Fp(dstML[0], srcML[0]); break; case 6: // ROTATE_270 Image.Rotate270_Fp(dstML[0], srcML[0]); break; } break; default: throw new RuntimeException(JaiI18N.getString("Generic2")); } // // If the RasterAccessor object set up a temporary buffer for the // op to write to, tell the RasterAccessor to write that data // to the raster, that we're done with it. // if (dstAccessor.isDataCopy()) { dstAccessor.copyDataToRaster(); } } // public static OpImage createTestImage(OpImageTester oit) { // int type = 1; // return new MlibTransposeOpImage(oit.getSource(), null, // new ImageLayout(oit.getSource()), // type); // } // public static void main (String args[]) { // String classname = "com.sun.media.jai.mlib.MlibTransposeOpImage"; // OpImageTester.performDiagnostics(classname,args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibAffineOpImage.java0000644000175000017500000004437610203035544026651 0ustar mathieumathieu/* * $RCSfile: MlibAffineOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:49 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import javax.media.jai.BorderExtender; import javax.media.jai.GeometricOpImage; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationNearest; import javax.media.jai.InterpolationBilinear; import javax.media.jai.InterpolationBicubic; import javax.media.jai.InterpolationBicubic2; import javax.media.jai.KernelJAI; import javax.media.jai.OpImage; import javax.media.jai.PlanarImage; import javax.media.jai.util.ImagingException; import javax.media.jai.util.ImagingListener; import java.util.Map; import com.sun.medialib.mlib.*; import com.sun.media.jai.util.ImageUtil; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class to perform AffineTransform on a source image. * */ class MlibAffineOpImage extends GeometricOpImage { /** * The transformation in matrix form (medialib expects this form) */ protected double f_transform[]; protected double m_transform[]; protected double medialib_tr[]; protected AffineTransform transform; protected AffineTransform i_transform; /** The Interpolation object. */ protected Interpolation interp; /** Store source & padded rectangle info */ private Rectangle srcimg, padimg; /** The BorderExtender */ protected BorderExtender extender; /** The true writable area */ private Rectangle theDest; /** Cache the ImagingListener */ private ImagingListener listener; /** * Padding values for interpolation */ public int lpad, rpad, tpad, bpad; private static ImageLayout layoutHelper(ImageLayout layout, RenderedImage source, AffineTransform forward_tr) { ImageLayout newLayout; if (layout != null) { newLayout = (ImageLayout)layout.clone(); } else { newLayout = new ImageLayout(); } // // Get sx0,sy0 coordinates & width & height of the source. // float sx0 = (float)source.getMinX(); float sy0 = (float)source.getMinY(); float sw = (float)source.getWidth(); float sh = (float)source.getHeight(); // // The 4 points (clockwise order) are // (sx0, sy0), (sx0+sw, sy0) // (sx0, sy0+sh), (sx0+sw, sy0+sh) // Point2D[] pts = new Point2D[4]; pts[0] = new Point2D.Float(sx0, sy0); pts[1] = new Point2D.Float((sx0+sw), sy0); pts[2] = new Point2D.Float((sx0+sw), (sy0+sh)); pts[3] = new Point2D.Float(sx0, (sy0+sh)); // Forward map forward_tr.transform(pts, 0, pts, 0, 4); float dx0 = Float.MAX_VALUE; float dy0 = Float.MAX_VALUE; float dx1 = -Float.MAX_VALUE; float dy1 = -Float.MAX_VALUE; for (int i = 0; i < 4; i++) { float px = (float)pts[i].getX(); float py = (float)pts[i].getY(); dx0 = Math.min(dx0, px); dy0 = Math.min(dy0, py); dx1 = Math.max(dx1, px); dy1 = Math.max(dy1, py); } // // Get the width & height of the resulting bounding box. // This is set on the layout // int lw = (int)(dx1 - dx0); int lh = (int)(dy1 - dy0); // // Set the starting integral coordinate // with the following criterion. // If it's greater than 0.5, set it to the next integral value (ceil) // else set it to the integral value (floor). // int lx0, ly0; int i_dx0 = (int)Math.floor(dx0); if (Math.abs(dx0 - i_dx0) <= 0.5) { lx0 = i_dx0; } else { lx0 = (int) Math.ceil(dx0); } int i_dy0 = (int)Math.floor(dy0); if (Math.abs(dy0 - i_dy0) <= 0.5) { ly0 = i_dy0; } else { ly0 = (int) Math.ceil(dy0); } // // Create the layout // newLayout.setMinX(lx0); newLayout.setMinY(ly0); newLayout.setWidth(lw); newLayout.setHeight(lh); return newLayout; } /** * Creates a MlibAffineOpImage given a ParameterBlock containing the * image source and the AffineTransform and the interpolation. * The image dimensions are derived from the source image. The tile * grid layout, SampleModel, and ColorModel may optionally be specified * by an ImageLayout object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * or null. If null, a default cache will be used. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param kernel the convolution KernelJAI. */ public MlibAffineOpImage(RenderedImage source, ImageLayout layout, Map config, BorderExtender extender, AffineTransform transform, Interpolation interp, double[] backgroundValues) { super(vectorize(source), layoutHelper(layout, source, transform), config, true, extender, interp, backgroundValues); // store the interp and extender objects this.interp = interp; // the extender this.extender = extender; // cache the listener listener = ImageUtil.getImagingListener((java.awt.RenderingHints)config); // Store the padding values lpad = interp.getLeftPadding(); rpad = interp.getRightPadding(); tpad = interp.getTopPadding(); bpad = interp.getBottomPadding(); // // Store source bounds rectangle // and the padded rectangle (for extension cases) // srcimg = new Rectangle(getSourceImage(0).getMinX(), getSourceImage(0).getMinY(), getSourceImage(0).getWidth(), getSourceImage(0).getHeight()); padimg = new Rectangle(srcimg.x - lpad, srcimg.y - tpad, srcimg.width + lpad + rpad, srcimg.height + tpad + bpad); if (extender == null) { // // Source has to be shrunk as per interpolation // as a result the destination produced could // be different from the layout // // // Get sx0,sy0 coordinates and width & height of the source // float sx0 = (float) srcimg.x; float sy0 = (float) srcimg.y; float sw = (float) srcimg.width; float sh = (float) srcimg.height; // // get padding amounts as per interpolation // float f_lpad = (float)lpad; float f_rpad = (float)rpad; float f_tpad = (float)tpad; float f_bpad = (float)bpad; // // As per pixel defined to be at (0.5, 0.5) // if ((interp instanceof InterpolationBilinear) || (interp instanceof InterpolationBicubic) || (interp instanceof InterpolationBicubic2)) { f_lpad += 0.5; f_tpad += 0.5; f_rpad += 0.5; f_bpad += 0.5; } // // Shrink the source by padding amount prior to forward map // This is the maxmimum available source than can be mapped // sx0 += f_lpad; sy0 += f_tpad; sw -= (f_lpad + f_rpad); sh -= (f_tpad + f_bpad); // // The 4 points are (x0, y0), (x0+w, y0) // (x0+w, y0+h), (x0, y0+h) // Point2D[] pts = new Point2D[4]; pts[0] = new Point2D.Float(sx0, sy0); pts[1] = new Point2D.Float((sx0 + sw), sy0); pts[2] = new Point2D.Float((sx0 + sw), (sy0 + sh)); pts[3] = new Point2D.Float(sx0, (sy0 + sh)); // Forward map transform.transform(pts, 0, pts, 0, 4); float dx0 = Float.MAX_VALUE; float dy0 = Float.MAX_VALUE; float dx1 = -Float.MAX_VALUE; float dy1 = -Float.MAX_VALUE; for (int i = 0; i < 4; i++) { float px = (float)pts[i].getX(); float py = (float)pts[i].getY(); dx0 = Math.min(dx0, px); dy0 = Math.min(dy0, py); dx1 = Math.max(dx1, px); dy1 = Math.max(dy1, py); } // // The layout is the wholly contained integer area of the // corresponding floating point bounding box. // We cannot round the corners of the floating rect because it // would increase the size of the rect, so we need to ceil the // upper corner and floor the lower corner. // int lx0 = (int)Math.ceil(dx0); int ly0 = (int)Math.ceil(dy0); int lx1 = (int)Math.floor(dx1); int ly1 = (int)Math.floor(dy1); theDest = new Rectangle(lx0, ly0, lx1 - lx0, ly1 - ly0); } else { theDest = getBounds(); } // Store the inverse and forward transform try { this.i_transform = transform.createInverse(); } catch (java.awt.geom.NoninvertibleTransformException e) { String message = JaiI18N.getString("MlibAffineOpImage0"); listener.errorOccurred(message, new ImagingException(message, e), this, false); // throw new RuntimeException(JaiI18N.getString("MlibAffineOpImage0")); } this.transform = (AffineTransform)transform.clone(); // // Get the forward transform into an array // Java returns the values into the array as // {m00 m10 m01 m11 m02 m12} // this.f_transform = new double[6]; transform.getMatrix(this.f_transform); // // Rearrange the transform to medialib specifications // J2D's transform is [m00 m01 m02] [m10 m11 m12] // Medialib's transform is [a b tx] [c d ty] // this.medialib_tr = new double[6]; medialib_tr[0] = f_transform[0]; // a <---> m00 medialib_tr[1] = f_transform[2]; // b <---> m01 medialib_tr[2] = f_transform[4]; // tx <---> m02 medialib_tr[3] = f_transform[1]; // c <---> m10 medialib_tr[4] = f_transform[3]; // d <---> m11 medialib_tr[5] = f_transform[5]; // ty <---> m12 // // Make a copy for our internal use // this.m_transform = new double[6]; m_transform[0] = f_transform[0]; // a <---> m00 m_transform[1] = f_transform[2]; // b <---> m01 m_transform[2] = f_transform[4]; // tx <---> m02 m_transform[3] = f_transform[1]; // c <---> m10 m_transform[4] = f_transform[3]; // d <---> m11 m_transform[5] = f_transform[5]; // ty <---> m12 } /** * Computes the source point corresponding to the supplied point. * * @param destPt the position in destination image coordinates * to map to source image coordinates. * * @return a Point2D of the same class as * destPt. * * @throws IllegalArgumentException if destPt is * null. * * @since JAI 1.1.2 */ public Point2D mapDestPoint(Point2D destPt) { if (destPt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } Point2D dpt = (Point2D)destPt.clone(); dpt.setLocation(dpt.getX() + 0.5, dpt.getY() + 0.5); Point2D spt = i_transform.transform(dpt, null); spt.setLocation(spt.getX() - 0.5, spt.getY() - 0.5); return spt; } /** * Computes the destination point corresponding to the supplied point. * * @param sourcePt the position in source image coordinates * to map to destination image coordinates. * * @return a Point2D of the same class as * sourcePt. * * @throws IllegalArgumentException if destPt is * null. * * @since JAI 1.1.2 */ public Point2D mapSourcePoint(Point2D sourcePt) { if (sourcePt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } Point2D spt = (Point2D)sourcePt.clone(); spt.setLocation(spt.getX() + 0.5, spt.getY() + 0.5); Point2D dpt = transform.transform(spt, null); dpt.setLocation(dpt.getX() - 0.5, dpt.getY() - 0.5); return dpt; } /** * Forward map the source Rectangle. */ protected Rectangle forwardMapRect(Rectangle sourceRect, int sourceIndex) { return transform.createTransformedShape(sourceRect).getBounds(); } /** * Backward map the destination Rectangle. */ protected Rectangle backwardMapRect(Rectangle destRect, int sourceIndex) { // // Backward map the destination rectangle to get the // corresponding source rectangle // float dx0 = (float) destRect.x; float dy0 = (float) destRect.y; float dw = (float) (destRect.width); float dh = (float) (destRect.height); Point2D[] pts = new Point2D[4]; pts[0] = new Point2D.Float(dx0, dy0); pts[1] = new Point2D.Float((dx0 + dw), dy0); pts[2] = new Point2D.Float((dx0 + dw), (dy0 + dh)); pts[3] = new Point2D.Float(dx0, (dy0 + dh)); i_transform.transform(pts, 0, pts, 0, 4); float f_sx0 = Float.MAX_VALUE; float f_sy0 = Float.MAX_VALUE; float f_sx1 = -Float.MAX_VALUE; float f_sy1 = -Float.MAX_VALUE; for (int i = 0; i < 4; i++) { float px = (float)pts[i].getX(); float py = (float)pts[i].getY(); f_sx0 = Math.min(f_sx0, px); f_sy0 = Math.min(f_sy0, py); f_sx1 = Math.max(f_sx1, px); f_sy1 = Math.max(f_sy1, py); } int s_x0 = 0, s_y0 = 0, s_x1 = 0, s_y1 = 0; // Find the bounding box of the source rectangle if (interp instanceof InterpolationNearest) { s_x0 = (int) Math.floor(f_sx0); s_y0 = (int) Math.floor(f_sy0); s_x1 = (int) Math.ceil(f_sx1); s_y1 = (int) Math.ceil(f_sy1); } else { s_x0 = (int) Math.floor(f_sx0 - 0.5); s_y0 = (int) Math.floor(f_sy0 - 0.5); s_x1 = (int) Math.ceil(f_sx1); s_y1 = (int) Math.ceil(f_sy1); } // // Return the new rectangle // return new Rectangle(s_x0, s_y0, s_x1 - s_x0, s_y1 - s_y0); } /* * Compute a given tile */ public Raster computeTile(int tileX, int tileY) { // // Create a new WritableRaster to represent this tile. // Point org = new Point(tileXToX(tileX), tileYToY(tileY)); WritableRaster dest = createWritableRaster(sampleModel, org); // // Clip output rectangle to image bounds. // Rectangle rect = new Rectangle(org.x, org.y, tileWidth, tileHeight); // // Clip destination tile against the writable destination // area. This is either the layout or a smaller area if // no extension is specified. // Rectangle destRect = rect.intersection(theDest); Rectangle destRect1 = rect.intersection(getBounds()); if ((destRect.width <= 0) || (destRect.height <= 0)) { if (setBackground) { ImageUtil.fillBackground(dest, destRect1, backgroundValues); } // No area to write return dest; } // // determine the source rectangle needed to compute the destRect // Rectangle srcRect = mapDestRect(destRect, 0); if (extender == null) { srcRect = srcRect.intersection(srcimg); } else { srcRect = srcRect.intersection(padimg); } if (srcRect.width <= 0 || srcRect.height <= 0) { // destRect backward mapped outside the source if (setBackground) { ImageUtil.fillBackground(dest, destRect1, backgroundValues); } return dest; } if (!destRect1.equals(destRect)) { // beware that destRect1 contains destRect ImageUtil.fillBordersWithBackgroundValues(destRect1, destRect, dest, backgroundValues); } Raster[] sources = new Raster[1]; // Get the source data if (extender == null) { sources[0] = getSourceImage(0).getData(srcRect); } else { sources[0] = getSourceImage(0).getExtendedData(srcRect, extender); } computeRect(sources, dest, destRect); // Recycle the source tile if(getSourceImage(0).overlapsMultipleTiles(srcRect)) { recycleTile(sources[0]); } return dest; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibAddRIF.java0000644000175000017500000000317110203035544025234 0ustar mathieumathieu/* * $RCSfile: MlibAddRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:48 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "Add" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.AddDescriptor * @see MlibAddOpImage * */ public class MlibAddRIF implements RenderedImageFactory { /** Constructor. */ public MlibAddRIF() {} /** * Creates a new instance of MlibAddOpImage in * the rendered image mode. * * @param args The source images to be added. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout)) { return null; } return new MlibAddOpImage(args.getRenderedSource(0), args.getRenderedSource(1), hints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibOrderedDitherRIF.java0000644000175000017500000000636510203035544027300 0ustar mathieumathieu/* * $RCSfile: MlibOrderedDitherRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:03 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ColorCube; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; import com.sun.media.jai.util.ImageUtil; /** * A RIF supporting the "OrderedDither" operation in the * rendered image mode using mediaLib. * * @see javax.media.jai.operator.OrderedDitherDescriptor * @see MlibOrderedDitherOpImage */ public class MlibOrderedDitherRIF implements RenderedImageFactory { /** Constructor. */ public MlibOrderedDitherRIF() {} /** * Creates a new instance of MlibOrderedDitherOpImage in * the rendered image mode. * * @param args The source image and lookup table. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { // Get source and parameters. RenderedImage source = args.getRenderedSource(0); ColorCube colorMap = (ColorCube)args.getObjectParameter(0); KernelJAI[] ditherMask = (KernelJAI[])args.getObjectParameter(1); // Check colorMap compatibility. if(colorMap.getNumBands() != 1 && colorMap.getNumBands() != 3) { // 1 or 3 band colorMaps only. return null; } else if(colorMap.getDataType() != DataBuffer.TYPE_BYTE) { // byte colorMaps only return null; } // Check source compatibility. SampleModel sourceSM = source.getSampleModel(); if(sourceSM.getDataType() != DataBuffer.TYPE_BYTE) { // byte source images only return null; } else if(sourceSM.getNumBands() != colorMap.getNumBands()) { // band counts must match return null; } // Get ImageLayout from RenderingHints if any. ImageLayout layoutHint = RIFUtil.getImageLayoutHint(hints); // Calculate the final ImageLayout. ImageLayout layout = MlibOrderedDitherOpImage.layoutHelper(layoutHint, source, colorMap); // Check for source and destination compatibility. The ColorModel // is suppressed in the second test because it will be an // IndexColorModel which would cause the test to fail. SampleModel destSM = layout.getSampleModel(null); if (!MediaLibAccessor.isMediaLibCompatible(args) || (!MediaLibAccessor.isMediaLibCompatible(destSM, null) && !ImageUtil.isBinary(destSM))) { return null; } return new MlibOrderedDitherOpImage(source, hints, layout, colorMap, ditherMask); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibAffineBilinearOpImage.java0000644000175000017500000001600610350333605030306 0ustar mathieumathieu/* * $RCSfile: MlibAffineBilinearOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.3 $ * $Date: 2005-12-15 18:35:45 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationBilinear; import javax.media.jai.KernelJAI; import javax.media.jai.OpImage; import java.util.Map; import com.sun.medialib.mlib.*; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class to perform Bilinear AffineTransform * on a source image. * */ public class MlibAffineBilinearOpImage extends MlibAffineOpImage { /** * Creates a MlibAffineBilinearOpImage given a ParameterBlock containing * the image source and the AffineTransform. The image dimensions are * derived from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout * object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param tr the AffineTransform. * @param interp the Interpolation to be used (Bilinear) */ public MlibAffineBilinearOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, AffineTransform tr, Interpolation interp, double[] backgroundValues) { super(source, layout, config, extender, tr, interp, backgroundValues); } /** * Performs bilinear affine transformation on a specified * rectangle. The sources are cobbled and extended. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; Rectangle srcRect = source.getBounds(); int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest); MediaLibAccessor srcAccessor = new MediaLibAccessor(source,srcRect,formatTag); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest,destRect,formatTag); // // The AffineTransform needs to be readjusted as per the // location of the current source & destination rectangles // // Clone the global transform so as not to write to an instance // variable as this method may be called from different threads. double[] medialib_tr = (double[])this.medialib_tr.clone(); medialib_tr[2] = m_transform[0] * srcRect.x + m_transform[1] * srcRect.y + m_transform[2] - destRect.x; medialib_tr[5] = m_transform[3] * srcRect.x + m_transform[4] * srcRect.y + m_transform[5] - destRect.y; mediaLibImage srcML[], dstML[]; switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: srcML = srcAccessor.getMediaLibImages(); dstML = dstAccessor.getMediaLibImages(); if (setBackground) Image.Affine2(dstML[0], srcML[0], medialib_tr, Constants.MLIB_BILINEAR, Constants.MLIB_EDGE_DST_NO_WRITE, intBackgroundValues); else Image.Affine(dstML[0], srcML[0], medialib_tr, Constants.MLIB_BILINEAR, Constants.MLIB_EDGE_DST_NO_WRITE); MlibUtils.clampImage(dstML[0], getColorModel()); break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: srcML = srcAccessor.getMediaLibImages(); dstML = dstAccessor.getMediaLibImages(); if (setBackground) Image.Affine2_Fp(dstML[0], srcML[0], medialib_tr, Constants.MLIB_BILINEAR, Constants.MLIB_EDGE_DST_NO_WRITE, backgroundValues); else Image.Affine_Fp(dstML[0], srcML[0], medialib_tr, Constants.MLIB_BILINEAR, Constants.MLIB_EDGE_DST_NO_WRITE); break; default: String className = this.getClass().getName(); throw new RuntimeException(JaiI18N.getString("Generic2")); } if (dstAccessor.isDataCopy()) { dstAccessor.copyDataToRaster(); } } // public static OpImage createTestImage(OpImageTester oit) { // Interpolation interp = new InterpolationBilinear(); // AffineTransform tr = new AffineTransform(0.707107, // -0.707106, // 0.707106, // 0.707107, // 0.0, // 0.0); // return new MlibAffineBilinearOpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource()), // tr, // interp); // } // // Calls a method on OpImage that uses introspection, to make this // // class, discover it's createTestImage() call, call it and then // // benchmark the performance of the created OpImage chain. // public static void main (String args[]) { // String classname = "com.sun.media.jai.mlib.MlibAffineBilinearOpImage"; // OpImageTester.performDiagnostics(classname, args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibFilteredSubsampleOpImage.java0000644000175000017500000001471410203035544031064 0ustar mathieumathieu/* * $RCSfile: MlibFilteredSubsampleOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:56 $ * $State: Exp $ */package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationNearest; import javax.media.jai.OpImage; import java.util.Map; import javax.media.jai.BorderExtender; import com.sun.media.jai.opimage.FilteredSubsampleOpImage; import com.sun.medialib.mlib.*; /** *

    A class extending FilteredSubsampleOpImage to * subsample and antialias filter images using medialib. * * @see FilteredSubsampleOpImage */ final class MlibFilteredSubsampleOpImage extends FilteredSubsampleOpImage { protected double [] m_hKernel; protected double [] m_vKernel; private static final boolean DEBUG = false; /**

    MlibFilteredSubsampleOpImage constructs an OpImage representing * filtered integral subsampling. The scale factors represent the ratio of * source to destination dimensions. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param config a Map object possibly holding tile cache information * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param interp a Interpolation object to use for resampling. * @param scaleX downsample factor along x axis. * @param scaleY downsample factor along y axis. * @param qsFilter symmetric filter coefficients (partial kernel). * @throws IllegalArgumentException if the interp type is not one of: * INTERP_NEAREST, INTERP_BILINEAR, INTERP_BICUBIC, or INTERP_BICUBIC_2 */ public MlibFilteredSubsampleOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, int scaleX, int scaleY, float [] qsFilter, Interpolation interp) { // Propagate to FilteredSubsampleOpImage constructor super(source, extender, config, layout, scaleX, scaleY, qsFilter, interp); // If enabled, print debug information if (DEBUG) System.out.println("Object: MlibFilteredSubsampleOpImage"); // Copy floating arrays to medialib double arrays m_hKernel = new double [hKernel.length]; m_vKernel = new double [vKernel.length]; if (DEBUG) System.out.print("\n hParity: " + hParity + " hKernel: "); for (int i=0 ; i Performs a combined subsample/filter operation on a specified rectangle. * The sources are cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ public void computeRect(Raster [] sources, WritableRaster dest, Rectangle destRect) { // Get RasterAccessor tag int formatTag = MediaLibAccessor.findCompatibleTag(sources, dest); // Get destination accessor. MediaLibAccessor dst = new MediaLibAccessor(dest, destRect, formatTag); // Get source accessor. MediaLibAccessor src = new MediaLibAccessor(sources[0], mapDestRect(destRect, 0), formatTag); // Medialib requires translation terms (java version doesn't) int transX = m_hKernel.length - (scaleX + 1)/2 - (hParity*(1+scaleX))%2; int transY = m_vKernel.length - (scaleY + 1)/2 - (vParity*(1+scaleY))%2; mediaLibImage srcML[], dstML[]; switch (dst.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: srcML = src.getMediaLibImages(); dstML = dst.getMediaLibImages(); for (int i = 0 ; i < dstML.length; i++) { Image.FilteredSubsample(dstML[i], srcML[i], scaleX, scaleY, transX, transY, m_hKernel, m_vKernel, hParity, vParity, Constants.MLIB_EDGE_DST_NO_WRITE); } // for break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: srcML = src.getMediaLibImages(); dstML = dst.getMediaLibImages(); for (int i = 0 ; i < dstML.length; i++) { Image.FilteredSubsample_Fp(dstML[i], srcML[i], scaleX, scaleY, transX, transY, m_hKernel, m_vKernel, hParity, vParity, Constants.MLIB_EDGE_DST_NO_WRITE); } // for break; default: throw new IllegalArgumentException( JaiI18N.getString("Generic2")); } // switch getDataType // If the RasterAccessor set up a temporary write buffer for the // operator, tell it to copy that data to the destination Raster. if (dst.isDataCopy()) { dst.clampDataArrays(); dst.copyDataToRaster(); } // if isDataCopy } // computeRect } // class MlibFilteredSubsampleOpImage jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibAndConstRIF.java0000644000175000017500000000400110203035544026246 0ustar mathieumathieu/* * $RCSfile: MlibAndConstRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:50 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "AndConst" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.AndConstDescriptor * @see MlibAndConstOpImage * */ public class MlibAndConstRIF implements RenderedImageFactory { /** Constructor. */ public MlibAndConstRIF() {} /** * Creates a new instance of MlibAndConstOpImage in * the rendered image mode. * * @param args The source image and the constants. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout)) { return null; } /* Check whether dest has data type of float or double. */ if (layout != null) { SampleModel sm = layout.getSampleModel(null); if (sm != null) { int dtype = sm.getDataType(); if (dtype == DataBuffer.TYPE_FLOAT || dtype == DataBuffer.TYPE_DOUBLE) { return null; } } } return new MlibAndConstOpImage(args.getRenderedSource(0), hints, layout, (int[])args.getObjectParameter(0)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibCompositeOpImage.java0000644000175000017500000000624310203035544027412 0ustar mathieumathieu/* * $RCSfile: MlibCompositeOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:52 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.PointOpImage; import java.util.Map; import com.sun.medialib.mlib.*; /** * An OpImage implementing the "Composite" operation * using MediaLib. * * @see javax.media.jai.operator.CompositeDescriptor * @see MlibCompositeRIF * * @since 1.0 * */ final class MlibCompositeOpImage extends PointOpImage { /** The alpha image. */ private RenderedImage alpha; /** Constructor. */ public MlibCompositeOpImage(RenderedImage source1, RenderedImage source2, Map config, ImageLayout layout, RenderedImage alpha) { super(source1, source2, layout, config, true); this.alpha = alpha; } /** * Performs the "Composite" operation on a rectangular region of * the same. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { int formatTag = MediaLibAccessor.findCompatibleTag(sources, dest); MediaLibAccessor srcMA1 = new MediaLibAccessor(sources[0], destRect, formatTag); MediaLibAccessor srcMA2 = new MediaLibAccessor(sources[1], destRect, formatTag); MediaLibAccessor dstMA = new MediaLibAccessor(dest, destRect, formatTag); MediaLibAccessor alphaMA = new MediaLibAccessor(alpha.getData(destRect), destRect, formatTag); mediaLibImage[] srcMLI1 = srcMA1.getMediaLibImages(); mediaLibImage[] srcMLI2 = srcMA2.getMediaLibImages(); mediaLibImage[] dstMLI = dstMA.getMediaLibImages(); mediaLibImage[] alphaMLI = alphaMA.getMediaLibImages(); switch (dstMA.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: for (int i = 0 ; i < dstMLI.length; i++) { Image.Blend(dstMLI[i], srcMLI1[i], srcMLI2[i], alphaMLI[0]); } break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: for (int i = 0 ; i < dstMLI.length; i++) { Image.Blend_Fp(dstMLI[i], srcMLI1[i], srcMLI2[i], alphaMLI[0]); } break; default: throw new RuntimeException(JaiI18N.getString("Generic2")); } if (dstMA.isDataCopy()) { dstMA.clampDataArrays(); dstMA.copyDataToRaster(); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibMinFilterRIF.java0000644000175000017500000000440010203035544026431 0ustar mathieumathieu/* * $RCSfile: MlibMinFilterRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:00 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; import java.util.Map; import javax.media.jai.operator.MinFilterDescriptor; import javax.media.jai.operator.MinFilterShape; import com.sun.media.jai.opimage.RIFUtil; /** * Creates a MlibMinFilterOpImage subclass for the given input * mask type * @see MlibMinFilterOpImage */ public class MlibMinFilterRIF implements RenderedImageFactory { /** Constructor. */ public MlibMinFilterRIF() {} /** * Create a new instance of MlibMinFilterOpImage in the rendered layer. * This method satisfies the implementation of RIF. * * @param paramBlock The source image and the convolution kernel. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); if (!MediaLibAccessor.isMediaLibCompatible(paramBlock, layout) || !MediaLibAccessor.hasSameNumBands(paramBlock, layout)) { return null; } // Get BorderExtender from renderHints if any. BorderExtender extender = RIFUtil.getBorderExtenderHint(renderHints); MinFilterShape maskType = (MinFilterShape)paramBlock.getObjectParameter(0); int maskSize = paramBlock.getIntParameter(1); RenderedImage ri = paramBlock.getRenderedSource(0); if(maskType.equals(MinFilterDescriptor.MIN_MASK_SQUARE) && (maskSize==3 || maskSize==5 || maskSize == 7) && ri.getSampleModel().getNumBands() == 1){ return new MlibMinFilterOpImage(ri, extender, renderHints, layout, maskType, maskSize); }else{ return null; } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibXorConstRIF.java0000644000175000017500000000414410203035544026324 0ustar mathieumathieu/* * $RCSfile: MlibXorConstRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:10 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "XorConst" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.XorConstDescriptor * @see MlibXorConstOpImage * */ public class MlibXorConstRIF implements RenderedImageFactory { /** Constructor. */ public MlibXorConstRIF() {} /** * Creates a new instance of MlibXorConstOpImage in * the rendered image mode. * * @param args The source image and the constants. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout)) { return null; } /* Check whether dest has data type of float or double. */ if (layout != null) { SampleModel sm = layout.getSampleModel(null); if (sm != null) { int dtype = sm.getDataType(); if (dtype == DataBuffer.TYPE_FLOAT || dtype == DataBuffer.TYPE_DOUBLE) { return null; } } } return new MlibXorConstOpImage(args.getRenderedSource(0), hints, layout, (int[])args.getObjectParameter(0)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibLogRIF.java0000644000175000017500000000306610203035544025270 0ustar mathieumathieu/* * $RCSfile: MlibLogRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:58 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "Log" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.LogDescriptor * @see MlibLogOpImage * */ public class MlibLogRIF implements RenderedImageFactory { /** Constructor. */ public MlibLogRIF() {} /** * Creates a new instance of MlibLogOpImage in * the rendered image mode. * * @param args The source image. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout)) { return null; } return new MlibLogOpImage(args.getRenderedSource(0), hints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibMaxRIF.java0000644000175000017500000000320510203035544025267 0ustar mathieumathieu/* * $RCSfile: MlibMaxRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:59 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "Max" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.MaxDescriptor * @see MlibMaxOpImage * * @since 1.0 * */ public class MlibMaxRIF implements RenderedImageFactory { /** Constructor. */ public MlibMaxRIF() {} /** * Creates a new instance of MlibMaxOpImage in * the rendered image mode. * * @param args The source images. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout)) { return null; } return new MlibMaxOpImage(args.getRenderedSource(0), args.getRenderedSource(1), hints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibWarpGridOpImage.java0000644000175000017500000003767210350333605027203 0ustar mathieumathieu/* * $RCSfile: MlibWarpGridOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-12-15 18:35:47 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import java.util.Map; import javax.media.jai.RasterFactory; import javax.media.jai.WarpOpImage; import javax.media.jai.WarpGrid; import com.sun.medialib.mlib.*; import com.sun.media.jai.util.ImageUtil; /** * An OpImage implementing the Grid "Warp" operation * using mediaLib. * *

    With warp operations, there is no forward mapping (from source to * destination). JAI images are tiled, while mediaLib does not handle * tiles and consider each tile an individual image. For each tile in * destination, in order not to cobble the entire source image, the * computeTile method in this class attemps to do a backward * mapping on the tile region using the pixels along the perimeter of the * rectangular region. The hope is that the mapped source rectangle * should include all source pixels needed for this particular destination * tile. However, with certain unusual warp points, an inner destination * pixel may be mapped outside of the mapped perimeter pixels. In this * case, this destination pixel is not filled, and left black. * * @see javax.media.jai.operator.WarpDescriptor * @see MlibWarpRIF * * @since 1.0 * */ final class MlibWarpGridOpImage extends WarpOpImage { /** X grid settings. */ private int xStart; private int xStep; private int xNumCells; private int xEnd; /** Y grid settings. */ private int yStart; private int yStep; private int yNumCells; private int yEnd; /** Grid points. */ private float[] xWarpPos; private float[] yWarpPos; /** * Indicates what kind of interpolation to use; may be * Constants.MLIB_NEAREST, * Constants.MLIB_BILINEAR, * or Constants.MLIB_BICUBIC, * and was determined in MlibWarpRIF.create(). */ private int filter; /** * Returns the minimum bounding box of the region of the specified * source to which a particular Rectangle of the * destination will be mapped. * * @param destRect the Rectangle in destination coordinates. * @param sourceIndex the index of the source image. * * @return a Rectangle indicating the source bounding box, * or null if the bounding box is unknown. * * @throws IllegalArgumentException if sourceIndex is * negative or greater than the index of the last source. * @throws IllegalArgumentException if destRect is * null. */ protected Rectangle backwardMapRect(Rectangle destRect, int sourceIndex) { // Superclass method will throw documented exceptions if needed. Rectangle wrect = super.backwardMapRect(destRect, sourceIndex); // "Dilate" the backwarp mapped rectangle to account for // the lack of being able to know the floating point result of // mapDestRect() and to mimic what is done in AffineOpImage. // See bug 4518223 for more information. wrect.setBounds(wrect.x - 1, wrect.y - 1, wrect.width + 2, wrect.height + 2); return wrect; } /** * Constructs a MlibWarpGridOpImage. * * @param source The source image. * @param layout The destination image layout. * @param warp An object defining the warp algorithm. * @param interp An object describing the interpolation method. */ public MlibWarpGridOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, WarpGrid warp, Interpolation interp, int filter, double[] backgroundValues) { super(source, layout, config, true, extender, interp, warp, backgroundValues); this.filter = filter; // interpolation xStart = warp.getXStart(); xStep = warp.getXStep(); xNumCells = warp.getXNumCells(); xEnd = xStart + xStep * xNumCells; yStart = warp.getYStart(); yStep = warp.getYStep(); yNumCells = warp.getYNumCells(); yEnd = yStart + yStep * yNumCells; xWarpPos = warp.getXWarpPos(); yWarpPos = warp.getYWarpPos(); } /** * Computes a tile. A new WritableRaster is created to * represent the requested tile. Its width and height equals to this * image's tile width and tile height respectively. If the requested * tile lies outside of the image's boundary, the created raster is * returned with all of its pixels set to 0. * *

    This method overrides the method in WarpOpImage * and performs source cobbling when necessary. MediaLib is used to * calculate the actual warping. * * @param tileX The X index of the tile. * @param tileY The Y index of the tile. * * @return The tile as a Raster. */ public Raster computeTile(int tileX, int tileY) { /* The origin of the tile. */ Point org = new Point(tileXToX(tileX), tileYToY(tileY)); /* Create a new WritableRaster to represent this tile. */ WritableRaster dest = createWritableRaster(sampleModel, org); /* Find the intersection between this tile and the writable bounds. */ Rectangle rect0 = new Rectangle(org.x, org.y, tileWidth, tileHeight); Rectangle destRect = rect0.intersection(computableBounds); Rectangle destRect1 = rect0.intersection(getBounds()); if (destRect.isEmpty()) { if (setBackground) { ImageUtil.fillBackground(dest, destRect1, backgroundValues); } return dest; // tile completely outside of writable bounds } if (!destRect1.equals(destRect)) { // beware that destRect1 contains destRect ImageUtil.fillBordersWithBackgroundValues(destRect1, destRect, dest, backgroundValues); } Raster[] sources = new Raster[1]; Rectangle srcBounds = getSourceImage(0).getBounds(); int x0 = destRect.x; // first x point int x1 = x0 + destRect.width - 1; // last x point int y0 = destRect.y; // first y point int y1 = y0 + destRect.height - 1; // last y point if (x0 >= xEnd || x1 < xStart || y0 >= yEnd || y1 < yStart) { /* Tile is completely outside of warp grid; do copy. */ Rectangle rect = srcBounds.intersection(destRect); if (!rect.isEmpty()) { sources[0] = getSourceImage(0).getData(rect); copyRect(sources, dest, rect); // Recycle the source tile if(getSourceImage(0).overlapsMultipleTiles(rect)) { recycleTile(sources[0]); } } return dest; } if (x0 < xStart) { // region left of warp grid Rectangle rect = srcBounds.intersection(new Rectangle(x0, y0, xStart - x0, y1 - y0 + 1)); if (!rect.isEmpty()) { sources[0] = getSourceImage(0).getData(rect); copyRect(sources, dest, rect); // Recycle the source tile if(getSourceImage(0).overlapsMultipleTiles(rect)) { recycleTile(sources[0]); } } x0 = xStart; } if (x1 >= xEnd) { // region right of warp grid Rectangle rect = srcBounds.intersection(new Rectangle(xEnd, y0, x1 - xEnd + 1, y1 - y0 + 1)); if (!rect.isEmpty()) { sources[0] = getSourceImage(0).getData(rect); copyRect(sources, dest, rect); // Recycle the source tile if(getSourceImage(0).overlapsMultipleTiles(rect)) { recycleTile(sources[0]); } } x1 = xEnd - 1; } if (y0 < yStart) { // region above warp grid Rectangle rect = srcBounds.intersection(new Rectangle(x0, y0, x1 - x0 + 1, yStart - y0)); if (!rect.isEmpty()) { sources[0] = getSourceImage(0).getData(rect); copyRect(sources, dest, rect); // Recycle the source tile if(getSourceImage(0).overlapsMultipleTiles(rect)) { recycleTile(sources[0]); } } y0 = yStart; } if (y1 >= yEnd) { // region below warp grid Rectangle rect = srcBounds.intersection(new Rectangle(x0, yEnd, x1 - x0 + 1, y1 - yEnd + 1)); if (!rect.isEmpty()) { sources[0] = getSourceImage(0).getData(rect); copyRect(sources, dest, rect); // Recycle the source tile if(getSourceImage(0).overlapsMultipleTiles(rect)) { recycleTile(sources[0]); } } y1 = yEnd -1; } /* The region within the warp grid. */ destRect = new Rectangle(x0, y0, x1 - x0 + 1, y1 - y0 + 1); /* Map destination rectangle to source space. */ Rectangle srcRect = backwardMapRect(destRect, 0).intersection(srcBounds); if (!srcRect.isEmpty()) { /* Add the interpolation paddings. */ int l = interp == null ? 0 : interp.getLeftPadding(); int r = interp == null ? 0 : interp.getRightPadding(); int t = interp == null ? 0 : interp.getTopPadding(); int b = interp == null ? 0 : interp.getBottomPadding(); srcRect = new Rectangle(srcRect.x - l, srcRect.y - t, srcRect.width + l + r, srcRect.height + t + b); sources[0] = getBorderExtender() != null ? getSourceImage(0).getExtendedData(srcRect, extender) : getSourceImage(0).getData(srcRect); computeRect(sources, dest, destRect); // Recycle the source tile if(getSourceImage(0).overlapsMultipleTiles(srcRect)) { recycleTile(sources[0]); } } return dest; } /** * Performs the "grid warp" operation on a rectangular region of * the image. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { int formatTag = MediaLibAccessor.findCompatibleTag(sources, dest); Raster source = sources[0]; MediaLibAccessor srcMA = new MediaLibAccessor(source, source.getBounds(), formatTag); MediaLibAccessor dstMA = new MediaLibAccessor(dest, destRect, formatTag); mediaLibImage[] srcMLI = srcMA.getMediaLibImages(); mediaLibImage[] dstMLI = dstMA.getMediaLibImages(); switch (dstMA.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: if (setBackground) for (int i = 0 ; i < dstMLI.length; i++) { Image.GridWarp2(dstMLI[i], srcMLI[i], xWarpPos, yWarpPos, source.getMinX(), source.getMinY(), xStart - destRect.x, xStep, xNumCells, yStart - destRect.y, yStep, yNumCells, filter, Constants.MLIB_EDGE_DST_NO_WRITE, intBackgroundValues); } else for (int i = 0 ; i < dstMLI.length; i++) { Image.GridWarp(dstMLI[i], srcMLI[i], xWarpPos, yWarpPos, source.getMinX(), source.getMinY(), xStart - destRect.x, xStep, xNumCells, yStart - destRect.y, yStep, yNumCells, filter, Constants.MLIB_EDGE_DST_NO_WRITE); MlibUtils.clampImage(dstMLI[i], getColorModel()); } break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: if (setBackground) for (int i = 0 ; i < dstMLI.length; i++) { Image.GridWarp2_Fp(dstMLI[i], srcMLI[i], xWarpPos, yWarpPos, source.getMinX(), source.getMinY(), xStart - destRect.x, xStep, xNumCells, yStart - destRect.y, yStep, yNumCells, filter, Constants.MLIB_EDGE_DST_NO_WRITE, backgroundValues); } else for (int i = 0 ; i < dstMLI.length; i++) { Image.GridWarp_Fp(dstMLI[i], srcMLI[i], xWarpPos, yWarpPos, source.getMinX(), source.getMinY(), xStart - destRect.x, xStep, xNumCells, yStart - destRect.y, yStep, yNumCells, filter, Constants.MLIB_EDGE_DST_NO_WRITE); } break; default: throw new RuntimeException(JaiI18N.getString("Generic2")); } if (dstMA.isDataCopy()) { dstMA.clampDataArrays(); dstMA.copyDataToRaster(); } } /** * Copies the pixels of a rectangle from source Raster * to destination Raster using mediaLib. This method * is used to copy pixels outside of the warp grid. */ private void copyRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { int formatTag = MediaLibAccessor.findCompatibleTag(sources, dest); MediaLibAccessor srcMA = new MediaLibAccessor(sources[0], destRect, formatTag); MediaLibAccessor dstMA = new MediaLibAccessor(dest, destRect, formatTag); mediaLibImage[] srcMLI = srcMA.getMediaLibImages(); mediaLibImage[] dstMLI = dstMA.getMediaLibImages(); for (int i = 0 ; i < dstMLI.length; i++) { Image.Copy(dstMLI[i], srcMLI[i]); } if (dstMA.isDataCopy()) { dstMA.copyDataToRaster(); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibExtremaOpImage.java0000644000175000017500000002677710203035544027073 0ustar mathieumathieu/* * $RCSfile: MlibExtremaOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:56 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.DataBuffer; import java.awt.image.PixelInterleavedSampleModel; import java.util.ArrayList; import javax.media.jai.OpImage; import javax.media.jai.ImageLayout; import javax.media.jai.PlanarImage; import javax.media.jai.ROI; import javax.media.jai.StatisticsOpImage; import com.sun.media.jai.opimage.ExtremaOpImage; import com.sun.medialib.mlib.*; /** * An OpImage that performs the Extrema operation on an image through mediaLib. * */ final class MlibExtremaOpImage extends ExtremaOpImage { /** Buffer for the minimum location count. */ private int[] minCount; /** Buffer for the maximum location count. */ private int[] maxCount; /** Buffer for the minimum location. */ private int[][] minLocs; /** Buffer for the maximum location. */ private int[][] maxLocs; /** * Constructs an MlibExtremaOpImage. The image dimensions are copied * from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout object. * * @param source The source image. */ public MlibExtremaOpImage(RenderedImage source, ROI roi, int xStart, int yStart, int xPeriod, int yPeriod, boolean saveLocations, int maxRuns) { super(source, roi, xStart, yStart, xPeriod, yPeriod, saveLocations, maxRuns); } protected void accumulateStatistics(String name, Raster source, Object stats) { int numBands = sampleModel.getNumBands(); initializeState(source); // Determine the required region of this tile. (Note // that getTileRect() instersects tile and image bounds.) Rectangle tileRect = source.getBounds(); // Set the starting offset in the mediaLib image. int offsetX = (xPeriod - ((tileRect.x - xStart) % xPeriod)) % xPeriod; int offsetY = (yPeriod - ((tileRect.y - yStart) % yPeriod)) % yPeriod; // If this offset specifies a position outside of the // tile then go to the next tile. This test is likely // redundant to a similar test in StatisticsOpImage. if(offsetX >= tileRect.width || offsetY >= tileRect.height) { return; } // Determine the format tag and create an accessor. int formatTag = MediaLibAccessor.findCompatibleTag(null, source); MediaLibAccessor srcAccessor = new MediaLibAccessor(source, tileRect, formatTag); // Get the mediaLib image. mediaLibImage[] srcML = srcAccessor.getMediaLibImages(); // NOTE: currently srcML.length always equals 1 if (!saveLocations) { switch (srcAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: int[] imin = new int[numBands]; int[] imax = new int[numBands]; for (int i = 0 ; i < srcML.length; i++) { Image.Extrema2(imin, imax, srcML[i], offsetX, offsetY, xPeriod, yPeriod); } imin = srcAccessor.getIntParameters(0, imin); imax = srcAccessor.getIntParameters(0, imax); // Update the extrema. for ( int i = 0; i < numBands; i++ ) { extrema[0][i] = Math.min((double)imin[i], extrema[0][i]); extrema[1][i] = Math.max((double)imax[i], extrema[1][i]); } break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: double[] dmin = new double[numBands]; double[] dmax = new double[numBands]; for (int i = 0 ; i < srcML.length; i++) { Image.Extrema2_Fp(dmin, dmax, srcML[i], offsetX, offsetY, xPeriod, yPeriod); } dmin = srcAccessor.getDoubleParameters(0, dmin); dmax = srcAccessor.getDoubleParameters(0, dmax); // Update the extrema. for ( int i = 0; i < numBands; i++ ) { extrema[0][i] = Math.min((double)dmin[i], extrema[0][i]); extrema[1][i] = Math.max((double)dmax[i], extrema[1][i]); } break; } } else { Rectangle loc = source.getBounds(); int xOffset = loc.x; int yOffset = loc.y; switch (srcAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: int[] imin = new int[numBands]; int[] imax = new int[numBands]; for ( int i = 0; i < numBands; i++ ) { imin[i] = (int)extrema[0][i]; imax[i] = (int)extrema[1][i]; } for (int i = 0 ; i < srcML.length; i++) { Image.ExtremaLocations(imin, imax, srcML[i], offsetX, offsetY, xPeriod, yPeriod, saveLocations, maxRuns, minCount, maxCount, minLocs, maxLocs); } imin = srcAccessor.getIntParameters(0, imin); imax = srcAccessor.getIntParameters(0, imax); minCount = srcAccessor.getIntParameters(0, minCount); maxCount = srcAccessor.getIntParameters(0, maxCount); minLocs = srcAccessor.getIntArrayParameters(0, minLocs); maxLocs = srcAccessor.getIntArrayParameters(0, maxLocs); // Update the extrema. for ( int i = 0; i < numBands; i++ ) { ArrayList minList = minLocations[i]; ArrayList maxList = maxLocations[i]; if (imin[i] < extrema[0][i]) { minList.clear(); extrema[0][i] = imin[i]; } int[] minBuf = minLocs[i]; int[] maxBuf = maxLocs[i]; for (int j = 0, k = 0; j < minCount[i]; j++) minList.add(new int[]{minBuf[k++] + xOffset, minBuf[k++] + yOffset, minBuf[k++]}); if (imax[i] > extrema[1][i]) { maxList.clear(); extrema[1][i] = imax[i]; } for (int j = 0, k = 0; j < maxCount[i]; j++) maxList.add(new int[]{maxBuf[k++] + xOffset, maxBuf[k++] + yOffset, maxBuf[k++]}); } break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: double[] dmin = new double[numBands]; double[] dmax = new double[numBands]; for (int i = 0; i < numBands; i++ ) { dmin[i] = extrema[0][i]; dmax[i] = extrema[1][i]; } for (int i = 0 ; i < srcML.length; i++) { Image.ExtremaLocations_Fp(dmin, dmax, srcML[i], offsetX, offsetY, xPeriod, yPeriod, saveLocations, maxRuns, minCount, maxCount, minLocs, maxLocs); } dmin = srcAccessor.getDoubleParameters(0, dmin); dmax = srcAccessor.getDoubleParameters(0, dmax); minCount = srcAccessor.getIntParameters(0, minCount); maxCount = srcAccessor.getIntParameters(0, maxCount); minLocs = srcAccessor.getIntArrayParameters(0, minLocs); maxLocs = srcAccessor.getIntArrayParameters(0, maxLocs); // Update the extrema. for ( int i = 0; i < numBands; i++ ) { ArrayList minList = minLocations[i]; ArrayList maxList = maxLocations[i]; if (dmin[i] < extrema[0][i]) { minList.clear(); extrema[0][i] = dmin[i]; } int[] minBuf = minLocs[i]; int[] maxBuf = maxLocs[i]; for (int j = 0, k = 0; j < minCount[i]; j++) minList.add(new int[]{minBuf[k++] + xOffset, minBuf[k++] + yOffset, minBuf[k++]}); if (dmax[i] > extrema[1][i]) { maxList.clear(); extrema[1][i] = dmax[i]; } for (int j = 0, k = 0; j < maxCount[i]; j++) maxList.add(new int[]{maxBuf[k++] + xOffset, maxBuf[k++] + yOffset, maxBuf[k++]}); } break; } } if (name.equalsIgnoreCase("extrema")) { double[][] ext = (double[][])stats; for (int i = 0; i < numBands; i++) { ext[0][i] = extrema[0][i]; ext[1][i] = extrema[1][i]; } } else if (name.equalsIgnoreCase("minimum")) { double[] min = (double[])stats; for (int i = 0; i < numBands; i++ ) { min[i] = extrema[0][i]; } } else if (name.equalsIgnoreCase("maximum")) { double[] max = (double[])stats; for (int i = 0; i < numBands; i++ ) { max[i] = extrema[1][i]; } } else if (name.equalsIgnoreCase("minLocations")) { ArrayList[] minLoc = (ArrayList[])stats; for (int i = 0; i < numBands; i++) { minLoc[i] = minLocations[i]; } } else if (name.equalsIgnoreCase("maxLocations")) { ArrayList[] maxLoc = (ArrayList[])stats; for (int i = 0; i < numBands; i++) maxLoc[i] = maxLocations[i]; } } protected void initializeState(Raster source) { if (extrema == null) { int numBands = sampleModel.getNumBands(); minCount = new int[numBands]; maxCount = new int[numBands]; minLocs = new int[numBands][]; maxLocs = new int[numBands][]; int size = (getTileWidth() + 1) / 2 * getTileHeight(); for (int i = 0; i < numBands; i++) { minLocs[i] = new int[size]; maxLocs[i] = new int[size]; } super.initializeState(source); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibBandCombineRIF.java0000644000175000017500000000370010203035544026703 0ustar mathieumathieu/* * $RCSfile: MlibBandCombineRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:50 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "BandCombine" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.BandCombineDescriptor * @see MlibBandCombineOpImage * */ public class MlibBandCombineRIF implements RenderedImageFactory { /** Constructor. */ public MlibBandCombineRIF() {} /** * Creates a new instance of MlibBandCombineOpImage in * the rendered image mode. * * @param args The source image and the matrix. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { // Get ImageLayout and TileCache from RenderingHints. ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(args, layout)) { return null; } // Fall back to Java code if the matrix is not 3-by-4. double[][] matrix = (double[][])args.getObjectParameter(0); if(matrix.length != 3) { return null; } for(int i = 0; i < 3; i++) { if(matrix[i].length != 4) { return null; } } return new MlibBandCombineOpImage(args.getRenderedSource(0), hints, layout, matrix); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibWarpRIF.java0000644000175000017500000001133010203035544025451 0ustar mathieumathieu/* * $RCSfile: MlibWarpRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:09 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationNearest; import javax.media.jai.InterpolationBilinear; import javax.media.jai.InterpolationBicubic; import javax.media.jai.InterpolationBicubic2; import javax.media.jai.InterpolationTable; import java.util.Map; import javax.media.jai.Warp; import javax.media.jai.WarpGrid; import javax.media.jai.WarpPolynomial; import com.sun.media.jai.opimage.RIFUtil; import com.sun.medialib.mlib.*; /** * A RIF supporting the "Warp" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.WarpDescriptor * @see MlibWarpNearestOpImage * @see MlibWarpBilinearOpImage * @see MlibWarpBicubicOpImage * * @since 1.0 * */ public class MlibWarpRIF implements RenderedImageFactory { /** Constructor. */ public MlibWarpRIF() {} /** * Creates a new instance of MlibWarpOpImage in * the rendered image mode. * * @param args The source images. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); RenderedImage source = args.getRenderedSource(0); if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout) || // Medialib cannot deal with source image having tiles with any // dimension greater than or equal to 32768 source.getTileWidth() >= 32768 || source.getTileHeight() >= 32768) { return null; } /* Get BorderExtender from hints if any. */ BorderExtender extender = RIFUtil.getBorderExtenderHint(hints); Warp warp = (Warp)args.getObjectParameter(0); Interpolation interp = (Interpolation)args.getObjectParameter(1); double[] backgroundValues = (double[])args.getObjectParameter(2); int filter = -1; if (interp instanceof InterpolationNearest) { filter = Constants.MLIB_NEAREST; } else if (interp instanceof InterpolationBilinear) { filter = Constants.MLIB_BILINEAR; } else if (interp instanceof InterpolationBicubic) { filter = Constants.MLIB_BICUBIC; } else if (interp instanceof InterpolationBicubic2) { filter = Constants.MLIB_BICUBIC2; } else if (interp instanceof InterpolationTable) { ; // filter = Constants.MLIB_TABLE; not defined yet; } else { /* Other kinds of interpolation cannot be handled via mlib. */ return null; } if (warp instanceof WarpGrid) { if (interp instanceof InterpolationTable){ return new MlibWarpGridTableOpImage(source, extender, hints, layout, (WarpGrid)warp, interp, backgroundValues); }else{ return new MlibWarpGridOpImage(source, extender, hints, layout, (WarpGrid)warp, interp, filter, backgroundValues); } } else if (warp instanceof WarpPolynomial) { if (interp instanceof InterpolationTable){ return new MlibWarpPolynomialTableOpImage(source, extender, hints, layout, (WarpPolynomial)warp, interp, backgroundValues); }else{ return new MlibWarpPolynomialOpImage(source, extender, hints, layout, (WarpPolynomial)warp, interp, filter, backgroundValues); } } else { return null; } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibDivideByConstRIF.java0000644000175000017500000000413010203035544027246 0ustar mathieumathieu/* * $RCSfile: MlibDivideByConstRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:54 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "DivideByConst" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.DivideByConstDescriptor * @see MlibMultiplyConstOpImage * */ public class MlibDivideByConstRIF implements RenderedImageFactory { /** Constructor. */ public MlibDivideByConstRIF() {} /** * Creates a new instance of MlibMultiplyConstOpImage in * the rendered image mode. By inverting the constants, the result * of "DivideByConst" is obtained. * * @param args The source image and the constants. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout)) { return null; } /* Negate the constants vector. */ double[] constants = (double[])args.getObjectParameter(0); int length = constants.length; double[] invConstants = new double[length]; for (int i = 0; i < length; i++) { invConstants[i] = 1.0D / constants[i]; } return new MlibMultiplyConstOpImage(args.getRenderedSource(0), hints, layout, invConstants); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MediaLibAccessor.java0000644000175000017500000017266410341155114026545 0ustar mathieumathieu/* * $RCSfile: MediaLibAccessor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-11-23 21:08:28 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.ComponentSampleModel; import java.awt.image.ComponentColorModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferShort; import java.awt.image.DataBufferInt; import java.awt.image.DataBufferUShort; import java.awt.image.Raster; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.io.FileNotFoundException; import java.io.FilePermission; import java.io.InputStream; import java.io.IOException; import java.lang.NoClassDefFoundError; import java.security.AccessControlException; import java.security.AccessController; import java.security.PrivilegedAction; import javax.media.jai.ImageLayout; import javax.media.jai.JAI; import javax.media.jai.RasterAccessor; import javax.media.jai.util.ImagingListener; import com.sun.media.jai.util.DataBufferUtils; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.util.PropertyUtil; import com.sun.medialib.mlib.*; /** * An adapter class for presenting image data in a mediaLibImage * format, even if the data isn't stored that way. MediaLibAccessor * is meant to make the common case (ComponentRasters) and allow * them to be accelerated via medialib. Note that unlike RasterAccessor, * MediaLibAccessor does not work with all cases. In the event that * MediaLibAccessor can not deal with a give collection of Rasters, * findCompatibleTag will return the value MediaLibAccessor.TAG_INCOMPATIBLE. * OpImages that use MediaLibAccessor should be paired with RIF's * which check that findCompatibleTag returns a valid tag before * actually constructing the Mlib OpImage. * */ public class MediaLibAccessor { /** * Value indicating how far COPY_MASK info is shifted to avoid * interfering with the data type info */ private static final int COPY_MASK_SHIFT = 7; /* Value indicating how many bits the COPY_MASK is */ private static final int COPY_MASK_SIZE = 1; /** The bits of a FormatTag associated with how dataArrays are obtained. */ public static final int COPY_MASK = 0x1 << COPY_MASK_SHIFT; /** Flag indicating data is raster's data. */ public static final int UNCOPIED = 0x0 << COPY_MASK_SHIFT; /** Flag indicating data is a copy of the raster's data. */ public static final int COPIED = 0x01 << COPY_MASK_SHIFT; /** The bits of a FormatTag associated with pixel datatype. */ public static final int DATATYPE_MASK = (0x1 << COPY_MASK_SHIFT) - 1; /** * Value indicating how far BINARY_MASK info is shifted to avoid * interfering with the data type and copying info. */ private static final int BINARY_MASK_SHIFT = COPY_MASK_SHIFT+COPY_MASK_SIZE; /** Value indicating how many bits the BINARY_MASK is */ private static final int BINARY_MASK_SIZE = 1; /** The bits of a FormatTag associated with binary data. */ public static final int BINARY_MASK = ((1 << BINARY_MASK_SIZE) - 1) << BINARY_MASK_SHIFT; /** Flag indicating data are not binary. */ public static final int NONBINARY = 0x0 << BINARY_MASK_SHIFT; /** Flag indicating data are binary. */ public static final int BINARY = 0x1 << BINARY_MASK_SHIFT; /** FormatTag indicating data in byte arrays and uncopied. */ public static final int TAG_BYTE_UNCOPIED = DataBuffer.TYPE_BYTE | UNCOPIED; /** FormatTag indicating data in unsigned short arrays and uncopied. */ public static final int TAG_USHORT_UNCOPIED = DataBuffer.TYPE_USHORT | UNCOPIED; /** FormatTag indicating data in short arrays and uncopied. */ public static final int TAG_SHORT_UNCOPIED = DataBuffer.TYPE_SHORT | UNCOPIED; /** FormatTag indicating data in integer arrays and uncopied. */ public static final int TAG_INT_UNCOPIED = DataBuffer.TYPE_INT | UNCOPIED; /** FormatTag indicating data in float arrays and uncopied. */ public static final int TAG_FLOAT_UNCOPIED = DataBuffer.TYPE_FLOAT | UNCOPIED; /** FormatTag indicating data in double arrays and uncopied. */ public static final int TAG_DOUBLE_UNCOPIED = DataBuffer.TYPE_DOUBLE | UNCOPIED; /** FormatTag indicating data in byte arrays and uncopied. */ public static final int TAG_BYTE_COPIED = DataBuffer.TYPE_BYTE | COPIED; /** FormatTag indicating data in unsigned short arrays and copied. */ public static final int TAG_USHORT_COPIED = DataBuffer.TYPE_USHORT | COPIED; /** FormatTag indicating data in short arrays and copied. */ public static final int TAG_SHORT_COPIED = DataBuffer.TYPE_SHORT | COPIED; /** FormatTag indicating data in short arrays and copied. */ public static final int TAG_INT_COPIED = DataBuffer.TYPE_INT | COPIED; /** FormatTag indicating data in float arrays and copied. */ public static final int TAG_FLOAT_COPIED = DataBuffer.TYPE_FLOAT | COPIED; /** FormatTag indicating data in double arrays and copied. */ public static final int TAG_DOUBLE_COPIED = DataBuffer.TYPE_DOUBLE | COPIED; /** The raster that is the source of pixel data. */ protected Raster raster; /** The rectangle of the raster that MediaLibAccessor addresses. */ protected Rectangle rect; /** The number of bands per pixel in the data array. */ protected int numBands; /** The offsets of each band in the src image */ protected int bandOffsets[]; /** Tag indicating the data type of the data and whether its copied */ protected int formatTag; /** Area of mediaLib images that represent image data */ protected mediaLibImage mlimages[] = null; /** * Whether packed data are preferred when processing binary images. * This tag is ignored if the data are not binary. */ private boolean areBinaryDataPacked = false; private static boolean useMlibVar = false; private static boolean useMlibVarSet = false; private static synchronized boolean useMlib() { if (!useMlibVarSet) { setUseMlib(); useMlibVarSet = true; } return useMlibVar; } private static void setUseMlib() { // Fix of 4726600: Disable medialib before the searching of // medialib library if the property is set. boolean disableMediaLib = false; try { disableMediaLib = Boolean.getBoolean("com.sun.media.jai.disableMediaLib"); } catch (java.security.AccessControlException e) { // Because the property com.sun.media.jai.disableMediaLib isn't // defined as public, the users shouldn't know it. In most of // the cases, it isn't defined, and thus no access permission // is granted to it in the policy file. When JAI is utilized in // a security environment, AccessControlException will be thrown. // In this case, we suppose that the users would like to use // medialib accelaration. So, the medialib won't be disabled. // The fix of 4531501 } // If mediaLib usage has been explicity disabled. if (disableMediaLib) { useMlibVar = false; return; } try { SecurityManager securityManager = System.getSecurityManager(); if (securityManager != null && MediaLibAccessor.class.getClassLoader() != null) { // a non-null security manager means we're in an applet // if this.classLoader == null, we're an installed extension // and the doPrivleged block should be ok. // native code doesn't currently load on Wintel regardless // of where the dll's are even if this code is removed. // At some point we'll need to come up with a better // solution. For now this is a good work around because // on Sparc it will cause // a SecurityException to be thrown instead of an // ExceptionInInitializerError in the doPriviliged block // which can't be caught. // If MediaLib is rewritten so that the Exception is thrown // after the class is loaded this chunk of code can be // removed. String osName = System.getProperty("os.name"); String osArch = System.getProperty("os.arch"); // The fix of 4531469 if ((osName.equals("Solaris") || osName.equals("SunOS")) && osArch.equals("sparc")) { FilePermission fp = new FilePermission("/usr/bin/uname","execute"); securityManager.checkPermission(fp); } } Boolean result = (Boolean) AccessController.doPrivileged(new PrivilegedAction() { public Object run() { return new Boolean(Image.isAvailable()); } }); useMlibVar = result.booleanValue(); if (!useMlibVar) { forwardToListener(JaiI18N.getString("MediaLibAccessor2"), new MediaLibLoadException()); } } catch (NoClassDefFoundError ncdfe) { // If mediaLib jar file is not found, fall back to Java code. useMlibVar = false; forwardToListener(JaiI18N.getString("MediaLibAccessor3"), ncdfe); } catch (ClassFormatError cfe) { // If mediaLib jar file is not found, fall back to Java code. useMlibVar = false; forwardToListener(JaiI18N.getString("MediaLibAccessor3"), cfe); } catch (SecurityException se) { // If mediaLib jar file is not found, fall back to Java code. useMlibVar = false; forwardToListener(JaiI18N.getString("MediaLibAccessor4"), se); } if (useMlibVar == false) return; } /** * Forwards the supplied message and exception to the * ImagingListener set on the default JAI instance. * If none is set (which should not happen) the message is simply * printed to System.err. */ private static void forwardToListener(String message, Throwable thrown) { ImagingListener listener = JAI.getDefaultInstance().getImagingListener(); if(listener != null) { listener.errorOccurred(message, thrown, MediaLibAccessor.class, false); } else { System.err.println(message); } } /** * Returns true if mediaLib is able to handle the * source(s) and destination image format. Currently, all of the * following conditions must be met in order for this method to * return true. *

      *
    • MediaLib is available.
    • *
    • All sources must be RenderedImage.
    • *
    • All sources and destination must have * ComponentSampleModel and * ComponentColorModel.
    • *
    • All sources and destination must have less than or equal * to 4 bands of pixel data.
    • *
    * Additional checks for each individual OpImage * should be done in its corresponding RIF. * * @param args Input arguments that include sources. * @param layout Destination image layout; may be null. */ public static boolean isMediaLibCompatible(ParameterBlock args, ImageLayout layout) { if (!isMediaLibCompatible(args)) { // sources not supported return false; } if (layout != null) { // validate destination SampleModel sm = layout.getSampleModel(null); if (sm != null) { if (!(sm instanceof ComponentSampleModel) || sm.getNumBands() > 4) { return false; } } ColorModel cm = layout.getColorModel(null); if (cm != null && (!(cm instanceof ComponentColorModel))) { return false; } } return true; } /** * Returns true if mediaLib is able to handle the * source(s) image format. Currently, all of the following * conditions must be met in order for this method to return * true. *
      *
    • MediaLib is available.
    • *
    • All sources must be RenderedImage.
    • *
    • All sources must have ComponentSampleModel and * ComponentColorModel.
    • *
    • All sources must have less than or equal to 4 bands of pixel * data.
    • *
    * Additional checks for each individual OpImage * should be done in its corresponding RIF. * * @param args Input arguments that include sources. */ public static boolean isMediaLibCompatible(ParameterBlock args) { if (!useMlib()) { // mediaLib is not available return false; } int numSrcs = args.getNumSources(); for (int i = 0; i < numSrcs; i++) { Object src = args.getSource(i); if (!(src instanceof RenderedImage) || !isMediaLibCompatible((RenderedImage)src)) { return false; } } return true; } /** * Returns true if mediaLib is able to handle the * image. Currently, all of the following conditions must be * met in order for this method to return true. *
      *
    • MediaLib is available.
    • *
    • The image must have ComponentSampleModel and * ComponentColorModel.
    • *
    • The image must have less than or equal to 4 bands of pixel * data.
    • *
    * Additional checks for each individual OpImage * should be done in its corresponding RIF. * * @param image The image the compatibility of which is to be checked. */ public static boolean isMediaLibCompatible(RenderedImage image) { if (!useMlib()) { // mediaLib is not available return false; } SampleModel sm = image.getSampleModel(); ColorModel cm = image.getColorModel(); return (sm instanceof ComponentSampleModel && sm.getNumBands() <= 4 && (cm == null || cm instanceof ComponentColorModel)); } /** * Returns true if mediaLib is able to handle * an image having the supplied SampleModel and * ColorModel. Currently, all of the following conditions * must be met in order for this method to return true: *
      *
    • mediaLib is available.
    • *
    • The SampleModel is an instance of * ComponentSampleModel or one of its subclasses.
    • *
    • The ColorModel is null or an * instance of ComponentColorModel or a subclass thereof.
    • *
    • The image must have no more than 4 bands of pixel data.
    • *
    * * @param sm The image SampleModel. * @param cm The image ColorModel. * * @throws NullPointerException if sm is null. */ public static boolean isMediaLibCompatible(SampleModel sm, ColorModel cm) { if (!useMlib()) { // mediaLib is not available return false; } return (sm instanceof ComponentSampleModel && sm.getNumBands() <= 4 && (cm == null || cm instanceof ComponentColorModel)); } /** * Returns true if mediaLib is able to handle the * source(s) and destination image format as binary (also known * as bit or bilevel) image data. Currently, all of the * following conditions must be met in order for this method to * return true. *
      *
    • MediaLib is available.
    • *
    • All sources must be RenderedImages.
    • *
    • All sources and destination must have a * MultiPixelPackedSampleModel.
    • *
    • All sources and destination must have represent * single-bit data.
    • *
    • All sources and destination must have * a single band of pixel data.
    • *
    * Additional checks for each individual OpImage * should be done in its corresponding RIF. * * @param args Input arguments that include sources. * @param layout Destination image layout; may be null. */ public static boolean isMediaLibBinaryCompatible(ParameterBlock args, ImageLayout layout) { if (!useMlib()) { // mediaLib is not available return false; } SampleModel sm = null; int numSrcs = args.getNumSources(); for (int i = 0; i < numSrcs; i++) { // sources not supported Object src = args.getSource(i); if (!(src instanceof RenderedImage) || (sm = ((RenderedImage)src).getSampleModel()) == null || !ImageUtil.isBinary(sm)) { return false; } } if (layout != null) { // validate destination if ((sm = layout.getSampleModel(null)) != null && !ImageUtil.isBinary(sm)) { return false; } } return true; } /** * Returns true if the number of bands of all the * RenderedImage sources and destination are the same. * * @throws ClassCastException if any source is not * RenderedImage. */ public static boolean hasSameNumBands(ParameterBlock args, ImageLayout layout) { int numSrcs = args.getNumSources(); if (numSrcs > 0) { RenderedImage src = args.getRenderedSource(0); int numBands = src.getSampleModel().getNumBands(); for (int i = 1; i < numSrcs; i++) { src = args.getRenderedSource(i); if (src.getSampleModel().getNumBands() != numBands) { return false; } } if (layout != null) { SampleModel sm = layout.getSampleModel(null); if (sm != null && sm.getNumBands() != numBands) { return false; } } } return true; } /** * Returns the most efficient FormatTag that is compatible with * the destination raster and all source rasters. * * @param srcs the source Raster; may be null. * @param dst the destination Raster. */ public static int findCompatibleTag(Raster srcs[], Raster dst) { SampleModel dstSM = dst.getSampleModel(); int dstDT = dstSM.getDataType(); int defaultDataType = dstSM.getDataType(); boolean allComponentSampleModel = dstSM instanceof ComponentSampleModel; boolean allBinary = ImageUtil.isBinary(dstSM); // use highest precision datatype of all srcs & dst if (srcs != null) { int numSources = srcs.length; int i; for (i = 0; i < numSources; i++) { SampleModel srcSampleModel = srcs[i].getSampleModel(); if (!(srcSampleModel instanceof ComponentSampleModel)) { allComponentSampleModel = false; } if (!ImageUtil.isBinary(srcSampleModel)) { allBinary = false; } int srcDataType = srcSampleModel.getTransferType(); if (srcDataType > defaultDataType) { defaultDataType = srcDataType; } } } if(allBinary) { // The copy flag is not set until the mediaLibImage is // created as knowing this information requires too much // processing to determine here. return DataBuffer.TYPE_BYTE | BINARY; } if (!allComponentSampleModel) { if ((defaultDataType == DataBuffer.TYPE_BYTE) || (defaultDataType == DataBuffer.TYPE_USHORT) || (defaultDataType == DataBuffer.TYPE_SHORT)) { defaultDataType = DataBuffer.TYPE_INT; } } int tag = defaultDataType | COPIED; if (!allComponentSampleModel) { return tag; } // see if they all have same DT and are pixelSequential SampleModel srcSM[]; if (srcs == null) { srcSM = new SampleModel[0]; } else { srcSM = new SampleModel[srcs.length]; } for (int i = 0; i < srcSM.length; i++) { srcSM[i] = srcs[i].getSampleModel(); if (dstDT != srcSM[i].getDataType()) { return tag; } } if (isPixelSequential(dstSM)) { for (int i = 0; i < srcSM.length; i++) { if (!isPixelSequential(srcSM[i])) { return tag; } } for (int i = 0; i < srcSM.length; i++) { if (!hasMatchingBandOffsets((ComponentSampleModel)dstSM, (ComponentSampleModel)srcSM[i])) { return tag; } } return dstDT | UNCOPIED; } return tag; } /** * Determines if the SampleModel stores data in a way that can * be represented by a mediaLibImage without copying */ public static boolean isPixelSequential(SampleModel sm) { ComponentSampleModel csm = null; if (sm instanceof ComponentSampleModel) { csm = (ComponentSampleModel)sm; } else { return false; } int pixelStride = csm.getPixelStride(); int bandOffsets[] = csm.getBandOffsets(); int bankIndices[] = csm.getBankIndices(); if (pixelStride != bandOffsets.length) { return false; } for (int i = 0; i < bandOffsets.length; i++) { if (bandOffsets[i] >= pixelStride || bankIndices[i] != bankIndices[0]) { return false; } for (int j = i+1; j < bandOffsets.length; j++) { if (bandOffsets[i] == bandOffsets[j]) { return false; } } } return true; } /** * Determines if the src ComponentSampleModel and dst * ComponentSampleModel have matching band offsets. If they * don't mediaLib can't deal with the image without a copy. */ public static boolean hasMatchingBandOffsets(ComponentSampleModel dst, ComponentSampleModel src) { int srcBandOffsets[] = dst.getBandOffsets(); int dstBandOffsets[] = src.getBandOffsets(); if (srcBandOffsets.length != dstBandOffsets.length) { return false; } for (int i = 0; i < srcBandOffsets.length; i++) { if (srcBandOffsets[i] != dstBandOffsets[i]) { return false; } } return true; } public static int getMediaLibDataType(int formatTag) { int dataType = formatTag & DATATYPE_MASK; switch (dataType) { case DataBuffer.TYPE_BYTE: return Constants.MLIB_BYTE; case DataBuffer.TYPE_USHORT: return Constants.MLIB_USHORT; case DataBuffer.TYPE_SHORT: return Constants.MLIB_SHORT; case DataBuffer.TYPE_INT: return Constants.MLIB_INT; case DataBuffer.TYPE_DOUBLE: return Constants.MLIB_DOUBLE; case DataBuffer.TYPE_FLOAT: return Constants.MLIB_FLOAT; } return -1; } /** * Constructs a MediaLibAccessor object out of a Raster, Rectangle * and formatTag returned from MediaLibAccessor.findCompatibleTag(). * * In the case of binary data the copy mask bits of the formatTag * will be reset within the constructor according to whether the * data are in fact copied. This cannot be easily determined before * the data are actually copied. */ public MediaLibAccessor(Raster raster, Rectangle rect, int formatTag, boolean preferPacked) { areBinaryDataPacked = preferPacked; this.raster = raster; this.rect = new Rectangle(rect); this.formatTag = formatTag; if(isBinary()) { // Set binary-specific fields and return. numBands = 1; bandOffsets = new int[] {0}; int mlibType; int scanlineStride; byte[] bdata; mlimages = new mediaLibImage[1]; if(areBinaryDataPacked) { mlibType = Constants.MLIB_BIT; scanlineStride = (rect.width+7)/8; bdata = ImageUtil.getPackedBinaryData(raster, rect); // Update format tag depending on whether the data were copied. if(bdata == ((DataBufferByte)raster.getDataBuffer()).getData()) { this.formatTag |= UNCOPIED; } else { this.formatTag |= COPIED; } } else { // unpacked mlibType = Constants.MLIB_BYTE; scanlineStride = rect.width; bdata = ImageUtil.getUnpackedBinaryData(raster, rect); this.formatTag |= COPIED; } mlimages[0] = new mediaLibImage(mlibType, 1, rect.width, rect.height, scanlineStride, 0, bdata); return; } if ((formatTag & COPY_MASK) == UNCOPIED) { ComponentSampleModel csm = (ComponentSampleModel)raster.getSampleModel(); numBands = csm.getNumBands(); bandOffsets = csm.getBandOffsets(); int dataOffset = raster.getDataBuffer().getOffset(); dataOffset += (rect.y-raster.getSampleModelTranslateY())*csm.getScanlineStride()+ (rect.x-raster.getSampleModelTranslateX())*csm.getPixelStride(); // dataoffset should and is in terms of dataElements // scanline stride should be in terms of dataElements int scanlineStride = csm.getScanlineStride(); switch (formatTag & DATATYPE_MASK) { case DataBuffer.TYPE_BYTE: DataBufferByte dbb = (DataBufferByte)raster.getDataBuffer(); mlimages = new mediaLibImage[1]; mlimages[0] = new mediaLibImage(Constants.MLIB_BYTE, numBands, rect.width, rect.height, scanlineStride, dataOffset, dbb.getData()); break; case DataBuffer.TYPE_USHORT: DataBufferUShort dbus = (DataBufferUShort)raster.getDataBuffer(); mlimages = new mediaLibImage[1]; mlimages[0] = new mediaLibImage(Constants.MLIB_USHORT, numBands, rect.width, rect.height, scanlineStride, dataOffset, dbus.getData()); break; case DataBuffer.TYPE_SHORT: DataBufferShort dbs = (DataBufferShort)raster.getDataBuffer(); mlimages = new mediaLibImage[1]; mlimages[0] = new mediaLibImage(Constants.MLIB_SHORT, numBands, rect.width, rect.height, scanlineStride, dataOffset, dbs.getData()); break; case DataBuffer.TYPE_INT: DataBufferInt dbi = (DataBufferInt)raster.getDataBuffer(); mlimages = new mediaLibImage[1]; mlimages[0] = new mediaLibImage(Constants.MLIB_INT, numBands, rect.width, rect.height, scanlineStride, dataOffset, dbi.getData()); break; case DataBuffer.TYPE_FLOAT: DataBuffer dbf = raster.getDataBuffer(); mlimages = new mediaLibImage[1]; mlimages[0] = new mediaLibImage(Constants.MLIB_FLOAT, numBands, rect.width, rect.height, scanlineStride, dataOffset, DataBufferUtils.getDataFloat(dbf)); break; case DataBuffer.TYPE_DOUBLE: DataBuffer dbd = raster.getDataBuffer(); mlimages = new mediaLibImage[1]; mlimages[0] = new mediaLibImage(Constants.MLIB_DOUBLE, numBands, rect.width, rect.height, scanlineStride, dataOffset, DataBufferUtils.getDataDouble(dbd)); break; default: throw new IllegalArgumentException((formatTag & DATATYPE_MASK) +JaiI18N.getString("MediaLibAccessor1")); } } else { // Copying the data because we can't deal with it numBands = raster.getNumBands(); bandOffsets = new int[numBands]; for (int i = 0; i < numBands; i++) { bandOffsets[i] = i; } int scanlineStride = rect.width*numBands; switch (formatTag & DATATYPE_MASK) { case DataBuffer.TYPE_BYTE: byte bdata[] = new byte[rect.width*rect.height*numBands]; mlimages = new mediaLibImage[1]; mlimages[0] = new mediaLibImage(Constants.MLIB_BYTE, numBands, rect.width, rect.height, scanlineStride, 0, bdata); break; case DataBuffer.TYPE_USHORT: short usdata[] = new short[rect.width*rect.height*numBands]; mlimages = new mediaLibImage[1]; mlimages[0] = new mediaLibImage(Constants.MLIB_USHORT, numBands, rect.width, rect.height, scanlineStride, 0, usdata); break; case DataBuffer.TYPE_SHORT: short sdata[] = new short[rect.width*rect.height*numBands]; mlimages = new mediaLibImage[1]; mlimages[0] = new mediaLibImage(Constants.MLIB_SHORT, numBands, rect.width, rect.height, scanlineStride, 0, sdata); break; case DataBuffer.TYPE_INT: int idata[] = new int[rect.width*rect.height*numBands]; mlimages = new mediaLibImage[1]; mlimages[0] = new mediaLibImage(Constants.MLIB_INT, numBands, rect.width, rect.height, scanlineStride, 0, idata); break; case DataBuffer.TYPE_FLOAT: float fdata[] = new float[rect.width*rect.height*numBands]; mlimages = new mediaLibImage[1]; mlimages[0] = new mediaLibImage(Constants.MLIB_FLOAT, numBands, rect.width, rect.height, scanlineStride, 0, fdata); break; case DataBuffer.TYPE_DOUBLE: double ddata[] = new double[rect.width*rect.height*numBands]; mlimages = new mediaLibImage[1]; mlimages[0] = new mediaLibImage(Constants.MLIB_DOUBLE, numBands, rect.width, rect.height, scanlineStride, 0, ddata); break; default: throw new IllegalArgumentException((formatTag & DATATYPE_MASK) + JaiI18N.getString("MediaLibAccessor1")); } copyDataFromRaster(); } } /** * Constructs a MediaLibAccessor object out of a Raster, Rectangle * and formatTag returned from MediaLibAccessor.findCompatibleTag(). */ public MediaLibAccessor(Raster raster, Rectangle rect, int formatTag) { this(raster, rect, formatTag, false); } /** * Returns true if the MediaLibAccessor * represents binary data. */ public boolean isBinary() { return ((formatTag & BINARY_MASK) == BINARY); } /** * Returns an array of mediaLibImages which represents the input raster. * An array is returned instead of a single mediaLibImage because * in some cases, an input Raster can't be represented by one * mediaLibImage (unless copying is done) but can be represented * by several mediaLibImages without copying. */ public mediaLibImage[] getMediaLibImages() { return mlimages; } /** * Returns the data type of the RasterAccessor object. Note that * this datatype is not necessarily the same data type as the * underlying raster. */ public int getDataType() { return formatTag & DATATYPE_MASK; } /** * Returns true if the MediaLibAccessors's data is copied from it's * raster. */ public boolean isDataCopy() { return ((formatTag & COPY_MASK) == COPIED); } /** Returns the bandOffsets. */ public int[] getBandOffsets() { return bandOffsets; } /** * Returns parameters in the appropriate order if MediaLibAccessor * has reordered the bands or is attempting to make a * BandSequential image look like multiple PixelSequentialImages */ public int[] getIntParameters(int band, int params[]) { int returnParams[] = new int[numBands]; for (int i = 0; i < numBands; i++) { returnParams[i] = params[bandOffsets[i+band]]; } return returnParams; } /** * Returns parameters in the appropriate order if MediaLibAccessor * has reordered the bands or is attempting to make a * BandSequential image look like multiple PixelSequentialImages */ public int[][] getIntArrayParameters(int band, int[][] params) { int returnParams[][] = new int[numBands][]; for (int i = 0; i < numBands; i++) { returnParams[i] = params[bandOffsets[i+band]]; } return returnParams; } /** * Returns parameters in the appropriate order if MediaLibAccessor * has reordered the bands or is attempting to make a * BandSequential image look like multiple PixelSequentialImages */ public double[] getDoubleParameters(int band, double params[]) { double returnParams[] = new double[numBands]; for (int i = 0; i < numBands; i++) { returnParams[i] = params[bandOffsets[i+band]]; } return returnParams; } /** * Copy data from Raster to MediaLib image */ private void copyDataFromRaster() { // Writeback should only be necessary on destRasters which // should be writable so this cast should succeed. if (raster.getSampleModel() instanceof ComponentSampleModel) { ComponentSampleModel csm = (ComponentSampleModel)raster.getSampleModel(); int rasScanlineStride = csm.getScanlineStride(); int rasPixelStride = csm.getPixelStride(); int subRasterOffset = (rect.y-raster.getSampleModelTranslateY())*rasScanlineStride+ (rect.x-raster.getSampleModelTranslateX())*rasPixelStride; int rasBankIndices[] = csm.getBankIndices(); int rasBandOffsets[] = csm.getBandOffsets(); int rasDataOffsets[] = raster.getDataBuffer().getOffsets(); if (rasDataOffsets.length == 1) { for (int i = 0; i < numBands; i++) { rasBandOffsets[i] += rasDataOffsets[0] + subRasterOffset; } } else if (rasDataOffsets.length == rasBandOffsets.length) { for (int i = 0; i < numBands; i++) { rasBandOffsets[i] += rasDataOffsets[i] + subRasterOffset; } } Object mlibDataArray = null; switch (getDataType()) { case DataBuffer.TYPE_BYTE: byte bArray[][] = new byte[numBands][]; for (int i = 0; i < numBands; i++) { bArray[i] = mlimages[0].getByteData(); } mlibDataArray = bArray; break; case DataBuffer.TYPE_USHORT: short usArray[][] = new short[numBands][]; for (int i = 0; i < numBands; i++) { usArray[i] = mlimages[0].getUShortData(); } mlibDataArray = usArray; break; case DataBuffer.TYPE_SHORT: short sArray[][] = new short[numBands][]; for (int i = 0; i < numBands; i++) { sArray[i] = mlimages[0].getShortData(); } mlibDataArray = sArray; break; case DataBuffer.TYPE_INT: int iArray[][] = new int[numBands][]; for (int i = 0; i < numBands; i++) { iArray[i] = mlimages[0].getIntData(); } mlibDataArray = iArray; break; case DataBuffer.TYPE_FLOAT: float fArray[][] = new float[numBands][]; for (int i = 0; i < numBands; i++) { fArray[i] = mlimages[0].getFloatData(); } mlibDataArray = fArray; break; case DataBuffer.TYPE_DOUBLE: double dArray[][] = new double[numBands][]; for (int i = 0; i < numBands; i++) { dArray[i] = mlimages[0].getDoubleData(); } mlibDataArray = dArray; break; } Object rasDataArray = null; switch (csm.getDataType()) { case DataBuffer.TYPE_BYTE: { DataBufferByte dbb = (DataBufferByte)raster.getDataBuffer(); byte rasByteDataArray[][] = new byte[numBands][]; for (int i = 0; i < numBands; i++) { rasByteDataArray[i] = dbb.getData(rasBankIndices[i]); } rasDataArray = rasByteDataArray; } break; case DataBuffer.TYPE_USHORT: { DataBufferUShort dbus = (DataBufferUShort)raster.getDataBuffer(); short rasUShortDataArray[][] = new short[numBands][]; for (int i = 0; i < numBands; i++) { rasUShortDataArray[i] = dbus.getData(rasBankIndices[i]); } rasDataArray = rasUShortDataArray; } break; case DataBuffer.TYPE_SHORT: { DataBufferShort dbs = (DataBufferShort)raster.getDataBuffer(); short rasShortDataArray[][] = new short[numBands][]; for (int i = 0; i < numBands; i++) { rasShortDataArray[i] = dbs.getData(rasBankIndices[i]); } rasDataArray = rasShortDataArray; } break; case DataBuffer.TYPE_INT: { DataBufferInt dbi = (DataBufferInt)raster.getDataBuffer(); int rasIntDataArray[][] = new int[numBands][]; for (int i = 0; i < numBands; i++) { rasIntDataArray[i] = dbi.getData(rasBankIndices[i]); } rasDataArray = rasIntDataArray; } break; case DataBuffer.TYPE_FLOAT: { DataBuffer dbf = raster.getDataBuffer(); float rasFloatDataArray[][] = new float[numBands][]; for (int i = 0; i < numBands; i++) { rasFloatDataArray[i] = DataBufferUtils.getDataFloat(dbf, rasBankIndices[i]); } rasDataArray = rasFloatDataArray; } break; case DataBuffer.TYPE_DOUBLE: { DataBuffer dbd = raster.getDataBuffer(); double rasDoubleDataArray[][] = new double[numBands][]; for (int i = 0; i < numBands; i++) { rasDoubleDataArray[i] = DataBufferUtils.getDataDouble(dbd, rasBankIndices[i]); } rasDataArray = rasDoubleDataArray; } break; } // dst = mlib && src = ras Image.Reformat( mlibDataArray, rasDataArray, numBands, rect.width,rect.height, getMediaLibDataType(this.getDataType()), bandOffsets, rect.width*numBands, numBands, getMediaLibDataType(csm.getDataType()), rasBandOffsets, rasScanlineStride, rasPixelStride); } else { // If COPIED and the raster doesn't have ComponentSampleModel // data is moved with getPixel/setPixel (even byte/short) switch (getDataType()) { case DataBuffer.TYPE_INT: raster.getPixels(rect.x,rect.y, rect.width,rect.height, mlimages[0].getIntData()); break; case DataBuffer.TYPE_FLOAT: raster.getPixels(rect.x,rect.y, rect.width,rect.height, mlimages[0].getFloatData()); break; case DataBuffer.TYPE_DOUBLE: raster.getPixels(rect.x,rect.y, rect.width,rect.height, mlimages[0].getDoubleData()); break; } } } /** * Copies data back into the MediaLibAccessor's raster. Note that * the data is casted from the intermediate data format to * the raster's format. If clamping is needed, the call * clampDataArrays() method needs to be called before * calling the copyDataToRaster() method. */ public void copyDataToRaster() { if (isDataCopy()) { if(isBinary()) { if(areBinaryDataPacked) { ImageUtil.setPackedBinaryData(mlimages[0].getBitData(), (WritableRaster)raster, rect); } else { // unpacked ImageUtil.setUnpackedBinaryData(mlimages[0].getByteData(), (WritableRaster)raster, rect); } return; } // Writeback should only be necessary on destRasters which // should be writable so this cast should succeed. WritableRaster wr = (WritableRaster)raster; if (wr.getSampleModel() instanceof ComponentSampleModel) { ComponentSampleModel csm = (ComponentSampleModel)wr.getSampleModel(); int rasScanlineStride = csm.getScanlineStride(); int rasPixelStride = csm.getPixelStride(); int subRasterOffset = (rect.y-raster.getSampleModelTranslateY())*rasScanlineStride+ (rect.x-raster.getSampleModelTranslateX())*rasPixelStride; int rasBankIndices[] = csm.getBankIndices(); int rasBandOffsets[] = csm.getBandOffsets(); int rasDataOffsets[] = raster.getDataBuffer().getOffsets(); if (rasDataOffsets.length == 1) { for (int i = 0; i < numBands; i++) { rasBandOffsets[i] += rasDataOffsets[0] + subRasterOffset; } } else if (rasDataOffsets.length == rasBandOffsets.length) { for (int i = 0; i < numBands; i++) { rasBandOffsets[i] += rasDataOffsets[i] + subRasterOffset; } } Object mlibDataArray = null; switch (getDataType()) { case DataBuffer.TYPE_BYTE: byte bArray[][] = new byte[numBands][]; for (int i = 0; i < numBands; i++) { bArray[i] = mlimages[0].getByteData(); } mlibDataArray = bArray; break; case DataBuffer.TYPE_USHORT: short usArray[][] = new short[numBands][]; for (int i = 0; i < numBands; i++) { usArray[i] = mlimages[0].getUShortData(); } mlibDataArray = usArray; break; case DataBuffer.TYPE_SHORT: short sArray[][] = new short[numBands][]; for (int i = 0; i < numBands; i++) { sArray[i] = mlimages[0].getShortData(); } mlibDataArray = sArray; break; case DataBuffer.TYPE_INT: int iArray[][] = new int[numBands][]; for (int i = 0; i < numBands; i++) { iArray[i] = mlimages[0].getIntData(); } mlibDataArray = iArray; break; case DataBuffer.TYPE_FLOAT: float fArray[][] = new float[numBands][]; for (int i = 0; i < numBands; i++) { fArray[i] = mlimages[0].getFloatData(); } mlibDataArray = fArray; break; case DataBuffer.TYPE_DOUBLE: double dArray[][] = new double[numBands][]; for (int i = 0; i < numBands; i++) { dArray[i] = mlimages[0].getDoubleData(); } mlibDataArray = dArray; break; } byte tmpDataArray[] = null; Object rasDataArray = null; switch (csm.getDataType()) { case DataBuffer.TYPE_BYTE: { DataBufferByte dbb = (DataBufferByte)raster.getDataBuffer(); byte rasByteDataArray[][] = new byte[numBands][]; for (int i = 0; i < numBands; i++) { rasByteDataArray[i] = dbb.getData(rasBankIndices[i]); } tmpDataArray = rasByteDataArray[0]; rasDataArray = rasByteDataArray; } break; case DataBuffer.TYPE_USHORT: { DataBufferUShort dbus = (DataBufferUShort)raster.getDataBuffer(); short rasUShortDataArray[][] = new short[numBands][]; for (int i = 0; i < numBands; i++) { rasUShortDataArray[i] = dbus.getData(rasBankIndices[i]); } rasDataArray = rasUShortDataArray; } break; case DataBuffer.TYPE_SHORT: { DataBufferShort dbs = (DataBufferShort)raster.getDataBuffer(); short rasShortDataArray[][] = new short[numBands][]; for (int i = 0; i < numBands; i++) { rasShortDataArray[i] = dbs.getData(rasBankIndices[i]); } rasDataArray = rasShortDataArray; } break; case DataBuffer.TYPE_INT: { DataBufferInt dbi = (DataBufferInt)raster.getDataBuffer(); int rasIntDataArray[][] = new int[numBands][]; for (int i = 0; i < numBands; i++) { rasIntDataArray[i] = dbi.getData(rasBankIndices[i]); } rasDataArray = rasIntDataArray; } break; case DataBuffer.TYPE_FLOAT: { DataBuffer dbf = raster.getDataBuffer(); float rasFloatDataArray[][] = new float[numBands][]; for (int i = 0; i < numBands; i++) { rasFloatDataArray[i] = DataBufferUtils.getDataFloat(dbf, rasBankIndices[i]); } rasDataArray = rasFloatDataArray; } break; case DataBuffer.TYPE_DOUBLE: { DataBuffer dbd = raster.getDataBuffer(); double rasDoubleDataArray[][] = new double[numBands][]; for (int i = 0; i < numBands; i++) { rasDoubleDataArray[i] = DataBufferUtils.getDataDouble(dbd, rasBankIndices[i]); } rasDataArray = rasDoubleDataArray; } break; } // src = mlib && dst = ras Image.Reformat( rasDataArray, mlibDataArray, numBands, rect.width,rect.height, getMediaLibDataType(csm.getDataType()), rasBandOffsets, rasScanlineStride, rasPixelStride, getMediaLibDataType(this.getDataType()), bandOffsets, rect.width*numBands, numBands); } else { // If COPIED and the raster doesn't have ComponentSampleModel // data is moved with getPixel/setPixel (even byte/short) switch (getDataType()) { case DataBuffer.TYPE_INT: wr.setPixels(rect.x,rect.y, rect.width,rect.height, mlimages[0].getIntData()); break; case DataBuffer.TYPE_FLOAT: wr.setPixels(rect.x,rect.y, rect.width,rect.height, mlimages[0].getFloatData()); break; case DataBuffer.TYPE_DOUBLE: wr.setPixels(rect.x,rect.y, rect.width,rect.height, mlimages[0].getDoubleData()); break; } } } } /** * Clamps data array values to a range that the underlying raster * can deal with. For example, if the underlying raster stores * data as bytes, but the samples ares unpacked into integer arrays by * the RasterAccessor object for an operation, the operation will * need to call clampDataArrays() so that the data in the int * arrays is restricted to the range 0..255 before a setPixels() * call is made on the underlying raster. Note that some * operations (for example, lookup) can guarantee that their * results don't need clamping so they can call * RasterAccessor.copyDataToRaster() without first calling this * function. */ public void clampDataArrays () { if (!isDataCopy()) { return; } // additonal medialib check: If it's a componentSampleModel // we get a free cast when we call medialibWrapper.Reformat // to copy the data to the source. So we don't need to cast // here. if (raster.getSampleModel() instanceof ComponentSampleModel) { return; } int bits[] = raster.getSampleModel().getSampleSize(); // Do we even need a clamp? We do if there's any band // of the source image stored in that's less than 32 bits // and is stored in a byte, short or int format. (The automatic // cast's between floats/doubles and 32-bit ints in setPixel() // generall do what we want.) boolean needClamp = false; boolean uniformBitSize = true; for (int i = 0; i < bits.length; i++) { int bitSize = bits[0]; if (bits[i] < 32) { needClamp = true; } if (bits[i] != bitSize) { uniformBitSize = false; } } if (!needClamp) { return; } int dataType = raster.getDataBuffer().getDataType(); double hiVals[] = new double[bits.length]; double loVals[] = new double[bits.length]; if (dataType == DataBuffer.TYPE_USHORT && uniformBitSize && bits[0] == 16) { for (int i = 0; i < bits.length; i++) { hiVals[i] = (double)0xFFFF; loVals[i] = (double)0; } } else if (dataType == DataBuffer.TYPE_SHORT && uniformBitSize && bits[0] == 16) { for (int i = 0; i < bits.length; i++) { hiVals[i] = (double)Short.MAX_VALUE; loVals[i] = (double)Short.MIN_VALUE; } } else if (dataType == DataBuffer.TYPE_INT && uniformBitSize && bits[0] == 32) { for (int i = 0; i < bits.length; i++) { hiVals[i] = (double)Integer.MAX_VALUE; loVals[i] = (double)Integer.MIN_VALUE; } } else { for (int i = 0; i < bits.length; i++) { hiVals[i] = (double)((1 << bits[i]) - 1); loVals[i] = (double)0; } } clampDataArray(hiVals,loVals); } private void clampDataArray(double hiVals[], double loVals[]) { switch (getDataType()) { case DataBuffer.TYPE_INT: clampIntArrays(toIntArray(hiVals),toIntArray(loVals)); break; case DataBuffer.TYPE_FLOAT: clampFloatArrays(toFloatArray(hiVals),toFloatArray(loVals)); break; case DataBuffer.TYPE_DOUBLE: clampDoubleArrays(hiVals,loVals); break; } } private int[] toIntArray(double vals[]) { int returnVals[] = new int[vals.length]; for (int i = 0; i < vals.length; i++) { returnVals[i] = (int)vals[i]; } return returnVals; } private float[] toFloatArray(double vals[]) { float returnVals[] = new float[vals.length]; for (int i = 0; i < vals.length; i++) { returnVals[i] = (float)vals[i]; } return returnVals; } private void clampIntArrays(int hiVals[], int loVals[]) { int width = rect.width; int height = rect.height; int scanlineStride = numBands*width; for (int k = 0; k < numBands; k++) { int data[] = mlimages[0].getIntData(); int scanlineOffset = k; int hiVal = hiVals[k]; int loVal = loVals[k]; for (int j = 0; j < height; j++) { int pixelOffset = scanlineOffset; for (int i = 0; i < width; i++) { int tmp = data[pixelOffset]; if (tmp < loVal) { data[pixelOffset] = loVal; } else if (tmp > hiVal) { data[pixelOffset] = hiVal; } pixelOffset += numBands; } scanlineOffset += scanlineStride; } } } private void clampFloatArrays(float hiVals[], float loVals[]) { int width = rect.width; int height = rect.height; int scanlineStride = numBands*width; for (int k = 0; k < numBands; k++) { float data[] = mlimages[0].getFloatData(); int scanlineOffset = k; float hiVal = hiVals[k]; float loVal = loVals[k]; for (int j = 0; j < height; j++) { int pixelOffset = scanlineOffset; for (int i = 0; i < width; i++) { float tmp = data[pixelOffset]; if (tmp < loVal) { data[pixelOffset] = loVal; } else if (tmp > hiVal) { data[pixelOffset] = hiVal; } pixelOffset += numBands; } scanlineOffset += scanlineStride; } } } private void clampDoubleArrays(double hiVals[], double loVals[]) { int width = rect.width; int height = rect.height; int scanlineStride = numBands*width; for (int k = 0; k < numBands; k++) { double data[] = mlimages[0].getDoubleData(); int scanlineOffset = k; double hiVal = hiVals[k]; double loVal = loVals[k]; for (int j = 0; j < height; j++) { int pixelOffset = scanlineOffset; for (int i = 0; i < width; i++) { double tmp = data[pixelOffset]; if (tmp < loVal) { data[pixelOffset] = loVal; } else if (tmp > hiVal) { data[pixelOffset] = hiVal; } pixelOffset += numBands; } scanlineOffset += scanlineStride; } } } } class MediaLibLoadException extends Exception { MediaLibLoadException() { super(); } public synchronized Throwable fillInStackTrace() { return this; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibScaleNearestOpImage.java0000644000175000017500000001362710203035544030025 0ustar mathieumathieu/* * $RCSfile: MlibScaleNearestOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:04 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationNearest; import javax.media.jai.OpImage; import java.util.Map; import javax.media.jai.BorderExtender; import com.sun.medialib.mlib.*; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class that scales an image using nearest-neighbor * interpolation. * */ final class MlibScaleNearestOpImage extends MlibScaleOpImage { /** * Constructs an MlibScaleNearestOpImage. The image dimensions are copied * from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param layout an ImageLayout optionally containing the tile * grid layout, SampleModel, and ColorModel, or null. * @param xScale the x scaling factor. * @param yScale the y scaling factor. * @param xTrans the x translation factor. * @param yTrans the y translation factor. * @param interp the Nearest interpolation object. */ public MlibScaleNearestOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, float xScale, float yScale, float xTrans, float yTrans, Interpolation interp) { super(source, extender, config, layout, xScale, yScale, xTrans, yTrans, interp, true); } /** * Scale the given rectangle by the specified scale and translation * factors. The sources are cobbled. * * @param sources an array of sources, guarantee to provide all * necessary source data for computing the rectangle. * @param dest a tile that contains the rectangle to be computed. * @param destRect the rectangle within this OpImage to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; Rectangle srcRect = source.getBounds(); int formatTag = MediaLibAccessor.findCompatibleTag(sources, dest); MediaLibAccessor srcAccessor = new MediaLibAccessor(source, srcRect, formatTag); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest, destRect, formatTag); // Get the floating point scale factors float mlibScaleX = scaleX; float mlibScaleY = scaleY; // Translation to be specified to Medialib. Note that we have to // specify an additional translation since all images are 0 based // in Medialib. Note that scale and translation scalars have // rational representations. // Calculate intermediate values using rational arithmetic. long tempDenomX = scaleXRationalDenom * transXRationalDenom; long tempDenomY = scaleYRationalDenom * transYRationalDenom; long tempNumerX = (srcRect.x * scaleXRationalNum * transXRationalDenom) + (transXRationalNum * scaleXRationalDenom) - (destRect.x * tempDenomX); long tempNumerY = (srcRect.y * scaleYRationalNum * transYRationalDenom) + (transYRationalNum * scaleYRationalDenom) - (destRect.y * tempDenomY); float tx = (float)tempNumerX/(float)tempDenomX; float ty = (float)tempNumerY/(float)tempDenomY; mediaLibImage srcML[], dstML[]; switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: srcML = srcAccessor.getMediaLibImages(); dstML = dstAccessor.getMediaLibImages(); for (int i = 0 ; i < dstML.length; i++) { Image.ZoomTranslate(dstML[i], srcML[i], (double)mlibScaleX, (double)mlibScaleY, (double)tx, (double)ty, Constants.MLIB_NEAREST, Constants.MLIB_EDGE_DST_NO_WRITE); } break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: srcML = srcAccessor.getMediaLibImages(); dstML = dstAccessor.getMediaLibImages(); for (int i = 0 ; i < dstML.length; i++) { Image.ZoomTranslate_Fp(dstML[i], srcML[i], (double)mlibScaleX, (double)mlibScaleY, (double)tx, (double)ty, Constants.MLIB_NEAREST, Constants.MLIB_EDGE_DST_NO_WRITE); } break; default: String className = this.getClass().getName(); throw new RuntimeException(JaiI18N.getString("Generic2")); } if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } // public static OpImage createTestImage(OpImageTester oit) { // Interpolation interp = new InterpolationNearest(); // return new MlibScaleNearestOpImage(oit.getSource(), null, // new ImageLayout(oit.getSource()), // 2, 2, 0, 0, interp); // } // // Calls a method on OpImage that uses introspection, to make this // // class, discover it's createTestImage() call, call it and then // // benchmark the performance of the created OpImage chain. // public static void main (String args[]) { // String classname = "com.sun.media.jai.mlib.MlibScaleNearestOpImage"; // OpImageTester.performDiagnostics(classname, args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibHistogramOpImage.java0000644000175000017500000002076710203035544027414 0ustar mathieumathieu/* * $RCSfile: MlibHistogramOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:57 $ * $State: Exp $ */package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.ComponentSampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import javax.media.jai.Histogram; import javax.media.jai.StatisticsOpImage; import java.util.Iterator; import java.util.TreeMap; import com.sun.medialib.mlib.Image; import com.sun.medialib.mlib.mediaLibImage; /** * An OpImage implementing the "Histogram" operation as * described in javax.media.jai.operator.HistogramDescriptor. * * @see javax.media.jai.Histogram * @see javax.media.jai.operator.HistogramDescriptor */ final class MlibHistogramOpImage extends StatisticsOpImage { /** Number of bins per band. */ private int[] numBins; /** The low value checked inclusive for each band. */ private double[] lowValueFP; /** The high value checked exclusive for each band. */ private double[] highValueFP; /** The low value checked inclusive for each band. */ private int[] lowValue; /** The high value checked exclusive for each band. */ private int[] highValue; /** The number of bands of the source image. */ private int numBands; private int[] bandIndexMap; private boolean reorderBands = false; /** * Constructs an MlibHistogramOpImage. * * @param source The source image. */ public MlibHistogramOpImage(RenderedImage source, int xPeriod, int yPeriod, int[] numBins, double[] lowValueFP, double[] highValueFP) { super(source, null, // ROI source.getMinX(), // xStart source.getMinY(), // yStart xPeriod, yPeriod); // Save the band count. numBands = sampleModel.getNumBands(); // Allocate memory to copy parameters. this.numBins = new int[numBands]; this.lowValueFP = new double[numBands]; this.highValueFP = new double[numBands]; // Copy parameters. for (int b = 0; b < numBands; b++) { this.numBins[b] = numBins.length == 1 ? numBins[0] : numBins[b]; this.lowValueFP[b] = lowValueFP.length == 1 ? lowValueFP[0] : lowValueFP[b]; this.highValueFP[b] = highValueFP.length == 1 ? highValueFP[0] : highValueFP[b]; } // Convert low values to integers. ceil() is used because the // pixel values are integral and the comparison is inclusive // so a floor() might include unwanted values if the low // value is floating point. this.lowValue = new int[this.lowValueFP.length]; for(int i = 0; i < this.lowValueFP.length; i++) { this.lowValue[i] = (int)Math.ceil(this.lowValueFP[i]); } // Convert high values to integers. ceil() is used because the // pixel values are integral and the comparison is exclusive // so a floor might cause desired values to be excluded as // only those through floor(high) - 1 would be included. this.highValue = new int[this.highValueFP.length]; for(int i = 0; i < this.highValueFP.length; i++) { this.highValue[i] = (int)Math.ceil(this.highValueFP[i]); } // Set up the band re-index map if needed. if(numBands > 1) { ComponentSampleModel csm = (ComponentSampleModel)sampleModel; TreeMap indexMap = new TreeMap(); // Determine whether there is more than one bank. int[] indices = csm.getBankIndices(); boolean checkBanks = false; for(int i = 1; i < numBands; i++) { if(indices[i] != indices[i-1]) { checkBanks = true; break; } } // Check the banks for ordering. if(checkBanks) { for(int i = 0; i < numBands; i++) { indexMap.put(new Integer(indices[i]), new Integer(i)); } bandIndexMap = new int[numBands]; Iterator bankIter = indexMap.keySet().iterator(); int k = 0; while(bankIter.hasNext()) { int idx = ((Integer)indexMap.get(bankIter.next())).intValue(); if(idx != k) { reorderBands = true; } bandIndexMap[k++] = idx; } } // If band re-ordering not needed on basis of bank indices // then check ordering of band offsets. if(!reorderBands) { indexMap.clear(); if(bandIndexMap == null) { bandIndexMap = new int[numBands]; } int[] offsets = csm.getBandOffsets(); for(int i = 0; i < numBands; i++) { indexMap.put(new Integer(offsets[i]), new Integer(i)); } Iterator offsetIter = indexMap.keySet().iterator(); int k = 0; while(offsetIter.hasNext()) { int idx = ((Integer)indexMap.get(offsetIter.next())).intValue(); if(idx != k) { reorderBands = true; } bandIndexMap[k++] = idx; } } } } protected String[] getStatisticsNames() { String[] names = new String[1]; names[0] = "histogram"; return names; } protected Object createStatistics(String name) { if (name.equalsIgnoreCase("histogram")) { return new Histogram(numBins, lowValueFP, highValueFP); } else { return java.awt.Image.UndefinedProperty; } } protected void accumulateStatistics(String name, Raster source, Object stats) { // Get the JAI histogram. Histogram histogram = (Histogram)stats; int numBands = histogram.getNumBands(); int[][] histJAI = histogram.getBins(); // Get the tile bounds. Rectangle tileRect = source.getBounds(); // Get the tile bins. int[][] histo; if(!reorderBands && tileRect.equals(getBounds())) { // Entire image: use the global histogram bins directly. histo = histJAI; } else { // Sub-image: save results for this tile only. histo = new int[numBands][]; for(int i = 0; i < numBands; i++) { histo[i] = new int[histogram.getNumBins(i)]; } } // Get the mlib image. int formatTag = MediaLibAccessor.findCompatibleTag(null, source); MediaLibAccessor accessor = new MediaLibAccessor(source, tileRect, formatTag); mediaLibImage[] img = accessor.getMediaLibImages(); // Determine the offset within the tile. int offsetX = (xPeriod - ((tileRect.x - xStart) % xPeriod)) % xPeriod; int offsetY = (yPeriod - ((tileRect.y - yStart) % yPeriod)) % yPeriod; if(histo == histJAI) { synchronized(histogram) { // Compute the histogram into the global array. Image.Histogram2(histo, img[0], lowValue, highValue, offsetX, offsetY, xPeriod, yPeriod); } } else { // Compute the histogram into the local array. Image.Histogram2(histo, img[0], lowValue, highValue, offsetX, offsetY, xPeriod, yPeriod); // Accumulate values if not using the global histogram. synchronized(histogram) { for(int i = 0; i < numBands; i++) { int numBins = histo[i].length; int[] binsBandJAI = reorderBands ? histJAI[bandIndexMap[i]] : histJAI[i]; int[] binsBand = histo[i]; for(int j = 0; j < numBins; j++) { binsBandJAI[j] += binsBand[j]; } } } } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibLookupRIF.java0000644000175000017500000000364410203035544026022 0ustar mathieumathieu/* * $RCSfile: MlibLookupRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:58 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import javax.media.jai.LookupTableJAI; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "Lookup" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.LookupDescriptor * @see MlibLookupOpImage * */ public class MlibLookupRIF implements RenderedImageFactory { /** Constructor. */ public MlibLookupRIF() {} /** * Creates a new instance of MlibLookupOpImage in * the rendered image mode. * * @param args The source image and lookup table. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(args)) { return null; } /* The table should be less than or equal to 4 bands. */ LookupTableJAI table = (LookupTableJAI)args.getObjectParameter(0); if (table.getNumBands() > 4 || table.getDataType() == DataBuffer.TYPE_USHORT) { return null; } return new MlibLookupOpImage(args.getRenderedSource(0), hints, layout, table); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibExpOpImage.java0000644000175000017500000000643310203035544026205 0ustar mathieumathieu/* * $RCSfile: MlibExpOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:56 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PointOpImage; import java.util.Map; import com.sun.medialib.mlib.*; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class that divides an image into a constant * */ final class MlibExpOpImage extends PointOpImage { /** * Constructs an MlibExpOpImage. The image dimensions are copied * from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param layout an ImageLayout optionally containing the tile * grid layout, SampleModel, and ColorModel, or null. */ public MlibExpOpImage(RenderedImage source, Map config, ImageLayout layout) { super(source, layout, config, true); // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * Divide the pixel values of a rectangle into a given constant. * The sources are cobbled. * * @param sources an array of sources, guarantee to provide all * necessary source data for computing the rectangle. * @param dest a tile that contains the rectangle to be computed. * @param destRect the rectangle within this OpImage to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; Rectangle srcRect = mapDestRect(destRect, 0); int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest); MediaLibAccessor srcAccessor = new MediaLibAccessor(source,srcRect,formatTag); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest,destRect,formatTag); mediaLibImage[] srcML = srcAccessor.getMediaLibImages(); mediaLibImage[] dstML = dstAccessor.getMediaLibImages(); switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: for (int i = 0; i < dstML.length; i++) { Image.Exp(dstML[i], srcML[i]); } break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: for (int i = 0; i < dstML.length; i++) { Image.Exp_Fp(dstML[i], srcML[i]); } break; default: String className = this.getClass().getName(); throw new RuntimeException(className + JaiI18N.getString("Generic2")); } if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibDivideIntoConstRIF.java0000644000175000017500000000337610203035544027620 0ustar mathieumathieu/* * $RCSfile: MlibDivideIntoConstRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:54 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "DivideIntoConst" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.DivideIntoConstDescriptor * @see MlibDivideIntoConstOpImage * */ public class MlibDivideIntoConstRIF implements RenderedImageFactory { /** Constructor. */ public MlibDivideIntoConstRIF() {} /** * Creates a new instance of MlibDivideIntoConstOpImage in * the rendered image mode. * * @param args The source image and the constants. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout)) { return null; } return new MlibDivideIntoConstOpImage(args.getRenderedSource(0), hints, layout, (double[])args.getObjectParameter(0)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibSubsampleBinaryToGrayOpImage.java0000644000175000017500000001626210203035544031700 0ustar mathieumathieu/* * $RCSfile: MlibSubsampleBinaryToGrayOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:06 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.image.RenderedImage; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import java.util.Map; import java.awt.Rectangle; import java.awt.geom.Point2D; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.awt.image.DataBuffer; import java.awt.image.ColorModel; import java.awt.image.IndexColorModel; import java.awt.image.SampleModel; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.PixelInterleavedSampleModel; import java.awt.image.SinglePixelPackedSampleModel; import java.awt.image.BandedSampleModel; import java.awt.image.PixelInterleavedSampleModel; import javax.media.jai.ImageLayout; import java.util.Map; import javax.media.jai.GeometricOpImage; import com.sun.medialib.mlib.*; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.util.JDKWorkarounds; import javax.media.jai.PlanarImage; import com.sun.media.jai.opimage.SubsampleBinaryToGrayOpImage; /** * A mediaLib class extending GeometricOpImage to * subsample binary images to gray scale images. Image scaling operations * require rectilinear backwards mapping and padding by the resampling * filter dimensions. * *

    When applying scale factors of scaleX, scaleY to a source image * with width of src_width and height of src_height, the resulting image * is defined to have the following bounds: * *

    * dst minX = floor(src minX * scaleX) * dst minY = floor(src minY * scaleY) * dst width = floor(src width * scaleX) * dst height = floor(src height * scaleY) * * * @see ScaleOpImage * @see com.sun.media.jai.opimage.SubsampleBinaryToGrayOpImage * */ class MlibSubsampleBinaryToGrayOpImage extends SubsampleBinaryToGrayOpImage { /** * Constructs a MlibSubsampleBinaryToGrayOpImage * from a RenderedImage source, x and y scale * object. The image dimensions are determined by forward-mapping * the source bounds, and are passed to the superclass constructor * by means of the layout parameter. Other fields of * the layout are passed through unchanged. If * layout is null, a new * ImageLayout will be constructor to hold the bounds * information. * * The float rounding errors, such as 1.2 being * internally represented as 1.200001, are dealt with * the floatTol, which is set up so that only 1/10 of pixel * error will occur at the end of a line, which yields correct * results with Math.round() operation. * The repeatability is guaranteed with a one-time computed * tables for x-values and y-values. * * @param source a RenderedImage. * @param layout an ImageLayout optionally containing * the tile grid layout, SampleModel, and * ColorModel, or null. * from this OpImage, or null. If * null, no caching will be performed. * @param scaleX scale factor along x axis. * @param scaleY scale factor along y axis. * * @throws IllegalArgumentException if combining the * source bounds with the layout parameter results in negative * output width or height. */ public MlibSubsampleBinaryToGrayOpImage(RenderedImage source, ImageLayout layout, Map config, float scaleX, float scaleY){ super(source, layout, config, scaleX, scaleY); } /** * Returns the minimum bounding box of the region of the specified * source to which a particular Rectangle of the * destination will be mapped. * * @param destRect the Rectangle in destination coordinates. * @param sourceIndex the index of the source image. * * @return a Rectangle indicating the source bounding box, * or null if the bounding box is unknown. * * @throws IllegalArgumentException if sourceIndex is * negative or greater than the index of the last source. * @throws IllegalArgumentException if destRect is * null. */ protected Rectangle backwardMapRect(Rectangle destRect, int sourceIndex) { Rectangle sourceRect = super.backwardMapRect(destRect, sourceIndex); // Increment dimensions (fix for 4643583). sourceRect.width += (int)invScaleX; sourceRect.height += (int)invScaleY; // Clamp rectangle to source bounds (fix for 4696977). return sourceRect.intersection(getSourceImage(0).getBounds()); } /** * Subsample (and condense) the given rectangle by the specified scale. * The sources are cobbled. * * @param sources an array of sources, guarantee to provide all * necessary source data for computing the rectangle. * @param dest a tile that contains the rectangle to be computed. * @param destRect the rectangle within this OpImage to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; Rectangle srcRect = source.getBounds(); // Hard-code the source format tag as we know that the image // has a binary layout. int sourceFormatTag = dest.getSampleModel().getDataType() | MediaLibAccessor.BINARY | MediaLibAccessor.UNCOPIED; // Derive format tag for the destination only by providing a // null-valued srcs[] parameter. int destFormatTag = MediaLibAccessor.findCompatibleTag(null, dest); MediaLibAccessor srcAccessor = new MediaLibAccessor(source, srcRect, sourceFormatTag, true); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest, destRect, destFormatTag); mediaLibImage srcML[], dstML[]; switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: srcML = srcAccessor.getMediaLibImages(); dstML = dstAccessor.getMediaLibImages(); for (int i = 0 ; i < dstML.length; i++) { Image.SubsampleBinaryToGray(dstML[i], srcML[i], (double)scaleX, (double)scaleY, lutGray); } break; default: String className = this.getClass().getName(); throw new RuntimeException(JaiI18N.getString("Generic2")); } if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibIDFTRIF.java0000644000175000017500000000744210203035544025277 0ustar mathieumathieu/* * $RCSfile: MlibIDFTRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:58 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.BorderExtender; import javax.media.jai.EnumeratedParameter; import javax.media.jai.ImageLayout; import javax.media.jai.JAI; import java.util.Map; import javax.media.jai.operator.DFTDescriptor; import com.sun.media.jai.opimage.DFTOpImage; import com.sun.media.jai.opimage.FFT; import com.sun.media.jai.opimage.RIFUtil; import com.sun.media.jai.util.MathJAI; /** * A RIF supporting the "IDFT" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.DFTDescriptor * @see javax.media.jai.operator.IDFTDescriptor * @see com.sun.media.jai.opimage.DFTOpImage * * @since EA4 * */ public class MlibIDFTRIF implements RenderedImageFactory { /** Constructor. */ public MlibIDFTRIF() {} /** * Creates a new instance of DFTOpImage in * the rendered image mode. * * @param args The source image. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(new ParameterBlock())) { return null; } RenderedImage source = args.getRenderedSource(0); EnumeratedParameter scalingType = (EnumeratedParameter)args.getObjectParameter(0); EnumeratedParameter dataNature = (EnumeratedParameter)args.getObjectParameter(1); boolean isComplexSource = !dataNature.equals(DFTDescriptor.REAL_TO_COMPLEX); int numSourceBands = source.getSampleModel().getNumBands(); // Use the two-dimensional mediaLib DFT if possible: it supports // only data which have a single component (real or complex) // per pixel and which have dimensions which are equal to a positive // power of 2. if(((isComplexSource && numSourceBands == 2) || (!isComplexSource && numSourceBands == 1)) && MlibDFTOpImage.isAcceptableSampleModel(source.getSampleModel())) { // If necessary, pad the source to ensure that // both dimensions are positive powers of 2. int sourceWidth = source.getWidth(); int sourceHeight = source.getHeight(); if(!MathJAI.isPositivePowerOf2(sourceWidth) || !MathJAI.isPositivePowerOf2(sourceHeight)) { ParameterBlock pb = new ParameterBlock(); pb.addSource(source); pb.add(0); pb.add(MathJAI.nextPositivePowerOf2(sourceWidth) - sourceWidth); pb.add(0); pb.add(MathJAI.nextPositivePowerOf2(sourceHeight) - sourceHeight); pb.add(BorderExtender.createInstance(BorderExtender.BORDER_ZERO)); source = JAI.create("border", pb); } return new MlibDFTOpImage(source, hints, layout, dataNature, false, scalingType); } else { // General case FFT fft = new FFTmediaLib(false, new Integer(scalingType.getValue()), 2); return new DFTOpImage(source, hints, layout, dataNature, fft); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibInvertOpImage.java0000644000175000017500000000471110203035544026715 0ustar mathieumathieu/* * $RCSfile: MlibInvertOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:58 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.PointOpImage; import java.util.Map; import com.sun.medialib.mlib.*; /** * An OpImage implementing the "Invert" operation * using MediaLib. * * @see javax.media.jai.operator.InvertDescriptor * @see MlibInvertRIF * * @since 1.0 * */ final class MlibInvertOpImage extends PointOpImage { /** Constructor. */ public MlibInvertOpImage(RenderedImage source, Map config, ImageLayout layout) { super(source, layout, config, true); // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * Performs the "Invert" operation on a rectangular region of * the same. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { int formatTag = MediaLibAccessor.findCompatibleTag(sources, dest); MediaLibAccessor srcMA = new MediaLibAccessor(sources[0], destRect, formatTag); MediaLibAccessor dstMA = new MediaLibAccessor(dest, destRect, formatTag); mediaLibImage[] srcMLI = srcMA.getMediaLibImages(); mediaLibImage[] dstMLI = dstMA.getMediaLibImages(); switch (dstMA.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: for (int i = 0 ; i < dstMLI.length; i++) { Image.Invert(dstMLI[i], srcMLI[i]); } break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: for (int i = 0 ; i < dstMLI.length; i++) { Image.Invert_Fp(dstMLI[i], srcMLI[i]); } break; default: throw new RuntimeException(JaiI18N.getString("Generic2")); } if (dstMA.isDataCopy()) { dstMA.clampDataArrays(); dstMA.copyDataToRaster(); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibSubsampleBinaryToGrayRIF.java0000644000175000017500000000644610203035544031002 0ustar mathieumathieu/* * $RCSfile: MlibSubsampleBinaryToGrayRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:06 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.image.DataBuffer; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.renderable.RenderedImageFactory; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationNearest; import java.util.Map; import com.sun.media.jai.opimage.CopyOpImage; import com.sun.media.jai.opimage.RIFUtil; import com.sun.media.jai.util.ImageUtil; /** * A RIF supporting the "SubsampleBinaryToGray" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.SubsampleBinaryToGrayDescriptor */ public class MlibSubsampleBinaryToGrayRIF implements RenderedImageFactory { /** * The width and height of blocks to be condensed into one gray pixel. * They are expected to be computed in the same way as in * import com.sun.media.jai.opimage.SubsampleBinaryToGrayOpImage; */ private int blockX; private int blockY; /** Constructor. */ public MlibSubsampleBinaryToGrayRIF() {} /** * Creates a new instance of MlibSubsampleBinaryToGrayOpImage in * the rendered image mode. * * @param args The source image, scale factors, * and the Interpolation. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { RenderedImage source = args.getRenderedSource(0); // Verify that the source is mediaLib-compatible. if (!MediaLibAccessor.isMediaLibBinaryCompatible(args, null)) { return null; } // Get ImageLayout from RenderingHints. ImageLayout layout = RIFUtil.getImageLayoutHint(hints); // Verify that the destination is mediaLib-compatible and has // the same number of bands as the source. if ((layout != null && layout.isValid(ImageLayout.SAMPLE_MODEL_MASK) && !MediaLibAccessor.isMediaLibCompatible( layout.getSampleModel(null), layout.getColorModel(null))) || !MediaLibAccessor.hasSameNumBands(args, layout)) { return null; } // Get BorderExtender from hints if any. // BorderExtender extender = RIFUtil.getBorderExtenderHint(hints); float xScale = args.getFloatParameter(0); float yScale = args.getFloatParameter(1); // When scaling by 1.0 in both x and y, a copy is all we need if (xScale == 1.0F && yScale == 1.0F){ // Use CopyOpImage as MlibCopyOpImage doesn't handle // binary-to-gray case. return new CopyOpImage(source, hints, layout); } return new MlibSubsampleBinaryToGrayOpImage(source, layout, hints, xScale, yScale); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibOrderedDitherOpImage.java0000644000175000017500000002434710203035544030201 0ustar mathieumathieu/* * $RCSfile: MlibOrderedDitherOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:03 $ * $State: Exp $ */package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.IndexColorModel; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.lang.ref.SoftReference; import java.util.Arrays; import java.util.Map; import java.util.Vector; import javax.media.jai.ColorCube; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; import javax.media.jai.OpImage; import javax.media.jai.PointOpImage; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.RasterFactory; import com.sun.media.jai.util.JDKWorkarounds; import com.sun.media.jai.util.ImageUtil; import com.sun.medialib.mlib.Image; import com.sun.medialib.mlib.mediaLibImage; import com.sun.medialib.mlib.mediaLibImageColormap; /** * An OpImage that performs the OrderedDither operation on 1 image * through mediaLib. */ final class MlibOrderedDitherOpImage extends PointOpImage { /** * The integer-to-float scale factor for dither mask elements. */ private static final int DMASK_SCALE_EXPONENT = 16; /** * The mediaLib colormap. */ protected mediaLibImageColormap mlibColormap; /** * The scaled values of the dither mask. */ protected int[][] dmask; /** * The width of the mask. */ protected int dmaskWidth; /** * The height of the mask. */ protected int dmaskHeight; /** * Scale factor to convert mask from floating point to integer. */ protected int dmaskScale; /** * Force the destination image to be single-banded. */ static ImageLayout layoutHelper(ImageLayout layout, RenderedImage source, ColorCube colormap) { ImageLayout il; if (layout == null) { il = new ImageLayout(source); } else { il = (ImageLayout)layout.clone(); } // Get the SampleModel. SampleModel sm = il.getSampleModel(source); // Ensure an appropriate SampleModel. if(colormap.getNumBands() == 1 && colormap.getNumEntries() == 2 && !ImageUtil.isBinary(il.getSampleModel(source))) { sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, il.getTileWidth(source), il.getTileHeight(source), 1); il.setSampleModel(sm); } // Make sure that this OpImage is single-banded. if (sm.getNumBands() != 1) { // TODO: Force to SHORT or USHORT if FLOAT or DOUBLE? sm = RasterFactory.createComponentSampleModel(sm, sm.getTransferType(), sm.getWidth(), sm.getHeight(), 1); il.setSampleModel(sm); // Clear the ColorModel mask if needed. ColorModel cm = il.getColorModel(null); if(cm != null && !JDKWorkarounds.areCompatibleDataModels(sm, cm)) { // Clear the mask bit if incompatible. il.unsetValid(ImageLayout.COLOR_MODEL_MASK); } } // Set an IndexColorModel on the image if: // a. none is provided in the layout; // b. source, destination, and colormap have byte data type; // c. the colormap has 3 bands; and // d. the source ColorModel is either null or is non-null // and has a ColorSpace equal to CS_sRGB. if((layout == null || !il.isValid(ImageLayout.COLOR_MODEL_MASK)) && source.getSampleModel().getDataType() == DataBuffer.TYPE_BYTE && il.getSampleModel(null).getDataType() == DataBuffer.TYPE_BYTE && colormap.getDataType() == DataBuffer.TYPE_BYTE && colormap.getNumBands() == 3) { ColorModel cm = source.getColorModel(); if(cm == null || (cm != null && cm.getColorSpace().isCS_sRGB())) { int size = colormap.getNumEntries(); byte[][] cmap = new byte[3][256]; for(int i = 0; i < 3; i++) { byte[] band = cmap[i]; byte[] data = colormap.getByteData(i); int offset = colormap.getOffset(i); int end = offset + size; for(int j = 0; j < offset; j++) { band[j] = (byte)0; } for(int j = offset; j < end; j++) { band[j] = data[j - offset]; } for(int j = end; j < 256; j++) { band[j] = (byte)0xFF; } } il.setColorModel(new IndexColorModel(8, 256, cmap[0], cmap[1], cmap[2])); } } return il; } /** * Constructs an MlibOrderedDitherOpImage. The image dimensions are copied * from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param layout an ImageLayout optionally containing the tile * grid layout, SampleModel, and ColorModel, or null. */ public MlibOrderedDitherOpImage(RenderedImage source, Map config, ImageLayout layout, ColorCube colormap, KernelJAI[] ditherMask) { // Construct as a PointOpImage. super(source, layoutHelper(layout, source, colormap), config, true); // Initialize the mediaLib colormap. It is implicitly assumed that // all data type and dimension checking was performed in the RIF. this.mlibColormap = Image.ColorDitherInit(colormap.getDimension(), Image.MLIB_BYTE, ImageUtil.isBinary(sampleModel) ? Image.MLIB_BIT : Image.MLIB_BYTE, colormap.getNumBands(), colormap.getNumEntries(), colormap.getOffset(), colormap.getByteData()); // Initialize dither mask constants. this.dmaskWidth = ditherMask[0].getWidth(); this.dmaskHeight = ditherMask[0].getHeight(); this.dmaskScale = 0x1 << DMASK_SCALE_EXPONENT; int numMasks = ditherMask.length; this.dmask = new int[numMasks][]; for(int k = 0; k < numMasks; k++) { KernelJAI mask = ditherMask[k]; if(mask.getWidth() != dmaskWidth || mask.getHeight() != dmaskHeight) { throw new IllegalArgumentException (JaiI18N.getString("MlibOrderedDitherOpImage0")); } // Initialize the integral dither mask coefficients. float[] dmaskData = ditherMask[k].getKernelData(); int numElements = dmaskData.length; this.dmask[k] = new int[numElements]; int[] dm = this.dmask[k]; for(int i = 0; i < numElements; i++) { dm[i] = (int)(dmaskData[i]*dmaskScale); } } // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * OrderedDither the pixel values of a rectangle from the source. * The source is cobbled. * * @param sources an array of sources, guarantee to provide all * necessary source data for computing the rectangle. * @param dest a tile that contains the rectangle to be computed. * @param destRect the rectangle within this OpImage to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; int sourceFormatTag; int destFormatTag; if(ImageUtil.isBinary(dest.getSampleModel())) { // Hack: derive the source format tag as if it was writing to // a destination with the same layout as itself. sourceFormatTag = MediaLibAccessor.findCompatibleTag(sources, source); // Hard-code the destination format tag as we know that the image // has a bilevel layout. destFormatTag = dest.getSampleModel().getDataType() | MediaLibAccessor.BINARY | MediaLibAccessor.UNCOPIED; } else { sourceFormatTag = destFormatTag = MediaLibAccessor.findCompatibleTag(sources, dest); } MediaLibAccessor srcAccessor = new MediaLibAccessor(sources[0], destRect, sourceFormatTag, false); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest, destRect, destFormatTag, true); mediaLibImage[] srcML = srcAccessor.getMediaLibImages(); mediaLibImage[] dstML = dstAccessor.getMediaLibImages(); Image.ColorOrderedDitherMxN(dstML[0], srcML[0], dmask, dmaskWidth, dmaskHeight, DMASK_SCALE_EXPONENT, mlibColormap); if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibConvolveOpImage.java0000644000175000017500000001763410203035544027251 0ustar mathieumathieu/* * $RCSfile: MlibConvolveOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:52 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; import javax.media.jai.OpImage; import java.util.Map; import com.sun.medialib.mlib.*; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class to perform convolution on a source image. * *

    This class implements a convolution operation. Convolution is a * spatial operation that computes each output sample by multiplying * elements of a kernel with the samples surrounding a particular * source sample. * *

    For each destination sample, the kernel is rotated 180 degrees * and its "key element" is placed over the source pixel corresponding * with the destination pixel. The kernel elements are multiplied * with the source pixels under them, and the resulting products are * summed together to produce the destination sample value. * *

    Example code for the convolution operation on a single sample * dst[x][y] is as follows, assuming the kernel is of size M rows x N * columns and has already been rotated through 180 degrees. The * kernel's key element is located at position (xKey, yKey): * *

     * dst[x][y] = 0;
     * for (int i = -xKey; i < M - xKey; i++) {
     *     for (int j = -yKey; j < N - yKey; j++) {
     *         dst[x][y] += src[x + i][y + j] * kernel[xKey + i][yKey + j];
     *     }
     * }
     * 
    * *

    Convolution, or any neighborhood operation, leaves a band of * pixels around the edges undefined, i.e., for a 3x3 kernel, only * four kernel elements and four source pixels contribute to the * destination pixel located at (0,0). Such pixels are not includined * in the destination image, unless a non-null BorderExtender is provided. * *

    The Kernel cannot be bigger in any dimension than the image data. * * * @see KernelJAI */ final class MlibConvolveOpImage extends AreaOpImage { /** * The kernel with which to do the convolve operation. */ protected KernelJAI kernel; /** Kernel variables. */ private int kw, kh, kx, ky; float kData[]; double doublekData[]; int intkData[]; int shift = -1; /** * Creates a MlibConvolveOpImage given the image source and * pre-rotated convolution kernel. The image dimensions are * derived from the source image. The tile grid layout, * SampleModel, and ColorModel may optionally be specified by an * ImageLayout object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * or null. If null, a default cache will be used. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param kernel the pre-rotated convolution KernelJAI. */ public MlibConvolveOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, KernelJAI kernel) { super(source, layout, config, true, extender, kernel.getLeftPadding(), kernel.getRightPadding(), kernel.getTopPadding(), kernel.getBottomPadding()); this.kernel = kernel; kw = kernel.getWidth(); kh = kernel.getHeight(); // this looks wrong, but it's right. AreaOpImage chops the image // up so that the kernels are "centered" by selecting the // appropriate source chunk (translating the source instead of the // kernel). The X and Y offsets are taken care of there, not here. kx = kw/2; ky = kh/2; kData = kernel.getKernelData(); int count = kw*kh; // A little inefficient but figuring out what datatype // mediaLibAccessor will want is tricky. intkData = new int[count]; doublekData = new double[count]; for (int i = 0; i < count; i++) { doublekData[i] = (double)kData[i]; } } private synchronized void setShift(int formatTag) { if (shift == -1) { int mediaLibDataType = MediaLibAccessor.getMediaLibDataType(formatTag); shift = Image.ConvKernelConvert(intkData, doublekData, kw,kh, mediaLibDataType); } } /** * Performs convolution on a specified rectangle. The sources are * cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; Rectangle srcRect = mapDestRect(destRect, 0); int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest); MediaLibAccessor srcAccessor = new MediaLibAccessor(source,srcRect,formatTag); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest,destRect,formatTag); int numBands = getSampleModel().getNumBands(); mediaLibImage[] srcML = srcAccessor.getMediaLibImages(); mediaLibImage[] dstML = dstAccessor.getMediaLibImages(); for (int i = 0; i < dstML.length; i++) { switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: if (shift == -1) { setShift(formatTag); } Image.ConvMxN(dstML[i], srcML[i], intkData, kw, kh, kx, ky, shift, ((1 << numBands)-1) , Constants.MLIB_EDGE_DST_NO_WRITE); break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: Image.ConvMxN_Fp(dstML[i], srcML[i], doublekData, kw, kh, kx, ky, ((1 << numBands)-1) , Constants.MLIB_EDGE_DST_NO_WRITE); break; default: String className = this.getClass().getName(); throw new RuntimeException(JaiI18N.getString("Generic2")); } } if (dstAccessor.isDataCopy()) { dstAccessor.copyDataToRaster(); } } // public static OpImage createTestImage(OpImageTester oit) { // float data[] = {0.05f,0.10f,0.05f, // 0.10f,0.40f,0.10f, // 0.05f,0.10f,0.05f}; // KernelJAI k1 = new KernelJAI(3,3,1,1,data); // return new MlibConvolveOpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource()), // k1); // } // public static void main (String args[]) { // String classname = "com.sun.media.jai.mlib.MlibConvolveOpImage"; // OpImageTester.performDiagnostics(classname,args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibRescaleOpImage.java0000644000175000017500000001234110203035544027022 0ustar mathieumathieu/* * $RCSfile: MlibRescaleOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:03 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PointOpImage; import java.util.Map; import com.sun.medialib.mlib.*; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class that multiplies pixels in an * image by a constant, then adds an offset. * */ final class MlibRescaleOpImage extends PointOpImage { private double[] constants; private double[] offsets; // XXX This cloning of the ImageLayout object should be centraliZed // into the superclass PointOpImage and removed from the mlib subclasses. private static ImageLayout layoutHelper(ImageLayout layout) { if (layout == null) { return null; } else { return (ImageLayout)layout.clone(); } } /** * Constructs an MlibRescaleOpImage. The image dimensions are copied * from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param layout an ImageLayout optionally containing the tile * grid layout, SampleModel, and ColorModel, or null. */ public MlibRescaleOpImage(RenderedImage source, Map config, ImageLayout layout, double[] constants, double[] offsets) { super(source, layoutHelper(layout), config, true); int numBands = getSampleModel().getNumBands(); this.constants = MlibUtils.initConstants(constants, numBands); this.offsets = MlibUtils.initConstants(offsets, numBands); // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * Multiply the pixel values of a rectangle with a given constant, * then add an offset. The sources are cobbled. * * @param sources an array of sources, guarantee to provide all * necessary source data for computing the rectangle. * @param dest a tile that contains the rectangle to be computed. * @param destRect the rectangle within this OpImage to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; Rectangle srcRect = mapDestRect(destRect, 0); int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest); MediaLibAccessor srcAccessor = new MediaLibAccessor(source,srcRect,formatTag); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest,destRect,formatTag); mediaLibImage[] srcML = srcAccessor.getMediaLibImages(); mediaLibImage[] dstML = dstAccessor.getMediaLibImages(); switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: for (int i = 0; i < dstML.length; i++) { double[] mlconstants = dstAccessor.getDoubleParameters(i, constants); double[] mloffsets = dstAccessor.getDoubleParameters(i, offsets); Image.Scale2(dstML[i], srcML[i], mlconstants, mloffsets); } break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: for (int i = 0; i < dstML.length; i++) { double[] mlconstants = dstAccessor.getDoubleParameters(i, constants); double[] mloffsets = dstAccessor.getDoubleParameters(i, offsets); Image.Scale_Fp(dstML[i], srcML[i], mlconstants, mloffsets); } break; default: String className = this.getClass().getName(); throw new RuntimeException(className + JaiI18N.getString("Generic2")); } if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } // public static OpImage createTestImage(OpImageTester oit) { // double[] consts = { 5.0D, 5.0D, 5.0D }; // double[] offsets = { 0.0D, 0.0D, 0.0D }; // return new MlibRescaleOpImage(oit.getSource(), null, // new ImageLayout(oit.getSource()), // consts, // offsets); // } // // Calls a method on OpImage that uses introspection, to make this // // class, discover it's createTestImage() call, call it and then // // benchmark the performance of the created OpImage chain. // public static void main (String args[]) { // String classname = "com.sun.media.jai.mlib.MlibRescaleOpImage"; // OpImageTester.performDiagnostics(classname,args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibRescaleRIF.java0000644000175000017500000000341410203035544026122 0ustar mathieumathieu/* * $RCSfile: MlibRescaleRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:03 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "Rescale" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.RescaleDescriptor * @see MlibRescaleOpImage * */ public class MlibRescaleRIF implements RenderedImageFactory { /** Constructor. */ public MlibRescaleRIF() {} /** * Creates a new instance of MlibRescaleOpImage in * the rendered image mode. * * @param args The source image to be rescaled and the constants. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout)) { return null; } return new MlibRescaleOpImage(args.getRenderedSource(0), hints, layout, (double[])args.getObjectParameter(0), (double[])args.getObjectParameter(1)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibIDCTRIF.java0000644000175000017500000000325410203035544025271 0ustar mathieumathieu/* * $RCSfile: MlibIDCTRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:57 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.DCTOpImage; import com.sun.media.jai.opimage.FCT; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "IDCT" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.IDCTDescriptor * @see com.sun.media.jai.opimage.DCTOpImage * * @since EA4 * */ public class MlibIDCTRIF implements RenderedImageFactory { /** Constructor. */ public MlibIDCTRIF() {} /** * Creates a new instance of IDCTOpImage in * the rendered image mode. * * @param args The source image. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(new ParameterBlock())) { return null; } return new DCTOpImage(args.getRenderedSource(0), hints, layout, new FCTmediaLib(false, 2)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibErode3PlusOpImage.java0000644000175000017500000001327510346146715027453 0ustar mathieumathieu/* * $RCSfile: MlibErode3PlusOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-12-09 00:20:28 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.JAI; import javax.media.jai.KernelJAI; import javax.media.jai.OpImage; import java.util.Map; import com.sun.medialib.mlib.*; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class to perform erosion on a source image * for the specific case when the kernel is a 3x3 plus shape * with the key position in the middle, * using mediaLib, of course :-). * * @see javax.media.jai.operator.ErodeDescriptor * @see KernelJAI */ final class MlibErode3PlusOpImage extends AreaOpImage { // Since medialib expects single banded data with IndexColorModel, we // should not expand the indexed data private static Map configHelper(Map configuration) { Map config; if (configuration == null) { config = new RenderingHints(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.FALSE); } else { config = configuration; // If the user has specified a hint for this, then we don't // want to change it, so change only if this hint is not // already specified if (!(config.containsKey(JAI.KEY_REPLACE_INDEX_COLOR_MODEL))) { RenderingHints hints = (RenderingHints)configuration; config = (RenderingHints)hints.clone(); config.put(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.FALSE); } } return config; } /** * Creates a MlibErode3PlusOpImage given the image source * The image dimensions are * derived from the source image. The tile grid layout, * SampleModel, and ColorModel may optionally be specified by an * ImageLayout object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * or null. If null, a default cache will be used. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. */ public MlibErode3PlusOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout ) { super(source, layout, configHelper(config), true, extender, 1, //kernel.getLeftPadding(), 1, //kernel.getRightPadding(), 1, //kernel.getTopPadding(), 1 //kernel.getBottomPadding() ); } /** * Performs erosion on a specified rectangle. The sources are * cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; Rectangle srcRect = mapDestRect(destRect, 0); int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest); MediaLibAccessor srcAccessor = new MediaLibAccessor(source, srcRect, formatTag, true); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest, destRect, formatTag, true); int numBands = getSampleModel().getNumBands(); mediaLibImage[] srcML = srcAccessor.getMediaLibImages(); mediaLibImage[] dstML = dstAccessor.getMediaLibImages(); for (int i = 0; i < dstML.length; i++) { switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: Image.Erode4(dstML[i], srcML[i]); break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: Image.Erode4_Fp(dstML[i], srcML[i]); break; default: String className = this.getClass().getName(); throw new RuntimeException(JaiI18N.getString("Generic2")); } } if (dstAccessor.isDataCopy()) { dstAccessor.copyDataToRaster(); } } // public static OpImage createTestImage(OpImageTester oit) { // float data[] = {0.05f,0.10f,0.05f, // 0.10f,0.40f,0.10f, // 0.05f,0.10f,0.05f}; // KernelJAI kJAI = new KernelJAI(3,3,1,1,data); // return new MlibErode4OpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource()), // kJAI); // } // public static void main (String args[]) { // String classname = "com.sun.media.jai.mlib.MlibErode4OpImage"; // OpImageTester.performDiagnostics(classname,args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibAbsoluteOpImage.java0000644000175000017500000001223010203035544027217 0ustar mathieumathieu/* * $RCSfile: MlibAbsoluteOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:47 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.PixelInterleavedSampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PointOpImage; import java.util.Map; import com.sun.medialib.mlib.*; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage that performs the Absolute operation on 1 image through mediaLib. * */ final class MlibAbsoluteOpImage extends PointOpImage { /** * Constructs an MlibAbsoluteOpImage. The image dimensions are copied * from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param layout an ImageLayout optionally containing the tile * grid layout, SampleModel, and ColorModel, or null. */ public MlibAbsoluteOpImage(RenderedImage source1, Map config, ImageLayout layout) { super(source1, layout, config, true); // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * Absolute the pixel values of a rectangle from the source. * The source is cobbled. * * @param sources an array of sources, guarantee to provide all * necessary source data for computing the rectangle. * @param dest a tile that contains the rectangle to be computed. * @param destRect the rectangle within this OpImage to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest); MediaLibAccessor srcAccessor = new MediaLibAccessor(sources[0], destRect,formatTag); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest, destRect, formatTag); mediaLibImage[] srcML = srcAccessor.getMediaLibImages(); mediaLibImage[] dstML = dstAccessor.getMediaLibImages(); switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: for (int i = 0 ; i < dstML.length; i++) { Image.Abs(dstML[i], srcML[i]); } break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: for (int i = 0 ; i < dstML.length; i++) { Image.Abs_Fp(dstML[i], srcML[i]); } break; default: String className = this.getClass().getName(); throw new RuntimeException(className + JaiI18N.getString("Generic2")); } if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } // public static void main (String args[]) { // System.out.println("MlibAbsoluteOpImage Test"); // ImageLayout layout; // OpImage src, dst; // Rectangle rect = new Rectangle(0, 0, 5, 5); // System.out.println("1. PixelInterleaved byte 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 800, 800, 0, 0, // 200, 200, DataBuffer.TYPE_BYTE, // 3, false); // src = OpImageTester.createRandomOpImage(layout); // dst = new MlibAbsoluteOpImage(src, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("2. Banded byte 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 800, 800, 0, 0, // 200, 200, DataBuffer.TYPE_BYTE, // 3, true); // src = OpImageTester.createRandomOpImage(layout); // dst = new MlibAbsoluteOpImage(src, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("3. PixelInterleaved int 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 512, 512, 0, 0, 200, 200, // DataBuffer.TYPE_INT, 3, false); // src = OpImageTester.createRandomOpImage(layout); // dst = new MlibAbsoluteOpImage(src, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("4. Banded int 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 512, 512, 0, 0, // 200, 200, DataBuffer.TYPE_INT, // 3, true); // src = OpImageTester.createRandomOpImage(layout); // dst = new MlibAbsoluteOpImage(src, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibSubtractOpImage.java0000644000175000017500000001323210203035544027233 0ustar mathieumathieu/* * $RCSfile: MlibSubtractOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:07 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.PixelInterleavedSampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PointOpImage; import java.util.Map; import com.sun.medialib.mlib.*; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage that performs the Subtract operation on 2 images through mediaLib. * */ final class MlibSubtractOpImage extends PointOpImage { /** * Constructs an MlibSubtractOpImage. The image dimensions are copied * from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param layout an ImageLayout optionally containing the tile * grid layout, SampleModel, and ColorModel, or null. */ public MlibSubtractOpImage(RenderedImage source1, RenderedImage source2, Map config, ImageLayout layout) { super(source1, source2, layout, config, true); // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * Subtract the pixel values of a rectangle from the two sources. * The sources are cobbled. * * @param sources an array of sources, guarantee to provide all * necessary source data for computing the rectangle. * @param dest a tile that contains the rectangle to be computed. * @param destRect the rectangle within this OpImage to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest); MediaLibAccessor srcAccessor1 = new MediaLibAccessor(sources[0], destRect,formatTag); MediaLibAccessor srcAccessor2 = new MediaLibAccessor(sources[1], destRect,formatTag); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest, destRect, formatTag); mediaLibImage[] srcML1 = srcAccessor1.getMediaLibImages(); mediaLibImage[] srcML2 = srcAccessor2.getMediaLibImages(); mediaLibImage[] dstML = dstAccessor.getMediaLibImages(); switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: for (int i = 0 ; i < dstML.length; i++) { Image.Sub(dstML[i], srcML1[i], srcML2[i]); } break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: for (int i = 0 ; i < dstML.length; i++) { Image.Sub_Fp(dstML[i], srcML1[i], srcML2[i]); } break; default: String className = this.getClass().getName(); throw new RuntimeException(className + JaiI18N.getString("Generic2")); } if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } // public static void main (String args[]) { // System.out.println("MlibSubtractOpImage Test"); // ImageLayout layout; // OpImage src1, src2, dst; // Rectangle rect = new Rectangle(0, 0, 5, 5); // System.out.println("1. PixelInterleaved byte 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 800, 800, 0, 0, // 200, 200, DataBuffer.TYPE_BYTE, // 3, false); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new MlibSubtractOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("2. Banded byte 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 800, 800, 0, 0, // 200, 200, DataBuffer.TYPE_BYTE, // 3, true); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new MlibSubtractOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("3. PixelInterleaved int 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 512, 512, 0, 0, 200, 200, // DataBuffer.TYPE_INT, 3, false); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new MlibSubtractOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("4. Banded int 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 512, 512, 0, 0, // 200, 200, DataBuffer.TYPE_INT, // 3, true); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new MlibSubtractOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibBandSelectOpImage.java0000644000175000017500000000752210203035544027455 0ustar mathieumathieu/* * $RCSfile: MlibBandSelectOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:50 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.util.Map; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PlanarImage; import javax.media.jai.PointOpImage; import javax.media.jai.RasterFactory; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.util.JDKWorkarounds; import com.sun.medialib.mlib.*; /** * An OpImage class that extracts (a) selected band(s) from an image. * */ final class MlibBandSelectOpImage extends PointOpImage { /* Bitmask for the bands to be extracted. */ private int cmask = 0x00000000; /** * Constructs an MlibBandSelectOpImage. The image dimensions are copied * from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param layout an ImageLayout optionally containing the tile * grid layout, SampleModel, and ColorModel, or null. */ public MlibBandSelectOpImage(RenderedImage source, Map config, ImageLayout layout, int[] bandIndices) { super(source, layout, config, true); int numBands = bandIndices.length; if (getSampleModel().getNumBands() != numBands) { // Create a new SampleModel and ColorModel. sampleModel = RasterFactory.createComponentSampleModel(sampleModel, sampleModel.getDataType(), tileWidth, tileHeight, numBands); if(colorModel != null && !JDKWorkarounds.areCompatibleDataModels(sampleModel, colorModel)) { colorModel = ImageUtil.getCompatibleColorModel(sampleModel, config); } } // Initialize the band selection bitmask. int maxShift = source.getSampleModel().getNumBands() - 1; for(int i = 0; i < bandIndices.length; i++) { cmask |= 0x00000001 << (maxShift - bandIndices[i]); } } /** * Extract the selected bands. * The sources are cobbled. * * @param sources an array of sources, guarantee to provide all * necessary source data for computing the rectangle. * @param dest a tile that contains the rectangle to be computed. * @param destRect the rectangle within this OpImage to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; Rectangle srcRect = mapDestRect(destRect, 0); int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest); MediaLibAccessor srcAccessor = new MediaLibAccessor(source,srcRect,formatTag); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest,destRect,formatTag); mediaLibImage[] srcML = srcAccessor.getMediaLibImages(); mediaLibImage[] dstML = dstAccessor.getMediaLibImages(); for (int i = 0; i < dstML.length; i++) { Image.ChannelExtract(dstML[i], srcML[i], cmask); } if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibAndOpImage.java0000644000175000017500000001242410203035544026150 0ustar mathieumathieu/* * $RCSfile: MlibAndOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:50 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.PixelInterleavedSampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PointOpImage; import java.util.Map; import com.sun.medialib.mlib.*; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage that performs the And operation on 2 images through mediaLib. * */ final class MlibAndOpImage extends PointOpImage { /** * Constructs an MlibAndOpImage. The image dimensions are copied * from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param layout an ImageLayout optionally containing the tile * grid layout, SampleModel, and ColorModel, or null. */ public MlibAndOpImage(RenderedImage source1, RenderedImage source2, Map config, ImageLayout layout) { super(source1, source2, layout, config, true); } /** * And the pixel values of a rectangle from the two sources. * The sources are cobbled. * * @param sources an array of sources, guarantee to provide all * necessary source data for computing the rectangle. * @param dest a tile that contains the rectangle to be computed. * @param destRect the rectangle within this OpImage to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest); MediaLibAccessor srcAccessor1 = new MediaLibAccessor(sources[0], destRect,formatTag); MediaLibAccessor srcAccessor2 = new MediaLibAccessor(sources[1], destRect,formatTag); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest, destRect, formatTag); switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: mediaLibImage[] srcML1 = srcAccessor1.getMediaLibImages(); mediaLibImage[] srcML2 = srcAccessor2.getMediaLibImages(); mediaLibImage[] dstML = dstAccessor.getMediaLibImages(); for (int i = 0 ; i < dstML.length; i++) { Image.And(dstML[i], srcML1[i], srcML2[i]); } break; default: String className = this.getClass().getName(); throw new RuntimeException(className + JaiI18N.getString("Generic2")); } if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } // public static void main (String args[]) { // System.out.println("MlibAndOpImage Test"); // ImageLayout layout; // OpImage src1, src2, dst; // Rectangle rect = new Rectangle(0, 0, 5, 5); // System.out.println("1. PixelInterleaved byte 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 800, 800, 0, 0, // 200, 200, DataBuffer.TYPE_BYTE, // 3, false); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new MlibAndOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("2. Banded byte 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 800, 800, 0, 0, // 200, 200, DataBuffer.TYPE_BYTE, // 3, true); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new MlibAndOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("3. PixelInterleaved int 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 512, 512, 0, 0, 200, 200, // DataBuffer.TYPE_INT, 3, false); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new MlibAndOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("4. Banded int 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 512, 512, 0, 0, // 200, 200, DataBuffer.TYPE_INT, // 3, true); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new MlibAndOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibSubtractFromConstOpImage.java0000644000175000017500000001135010203035544031065 0ustar mathieumathieu/* * $RCSfile: MlibSubtractFromConstOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:07 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PointOpImage; import java.util.Map; import com.sun.media.jai.util.ImageUtil; // import com.sun.media.jai.test.OpImageTester; import com.sun.medialib.mlib.*; /** * A mediaLib implementation of "SubtractFromConst" operator. * */ final class MlibSubtractFromConstOpImage extends PointOpImage { private double[] constants; /** * Constructs an MlibSubtractFromConstOpImage. The image dimensions * are copied from the source image. The tile grid layout, SampleModel, * and ColorModel may optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param layout an ImageLayout optionally containing the tile * grid layout, SampleModel, and ColorModel, or null. */ public MlibSubtractFromConstOpImage(RenderedImage source, Map config, ImageLayout layout, double[] constants) { super(source, layout, config, true); this.constants = MlibUtils.initConstants(constants, getSampleModel().getNumBands()); // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * Subtract the pixel values of a rectangle from a given constant. * The sources are cobbled. * * @param sources an array of sources, guarantee to provide all * necessary source data for computing the rectangle. * @param dest a tile that contains the rectangle to be computed. * @param destRect the rectangle within this OpImage to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; int formatTag = MediaLibAccessor.findCompatibleTag(sources, dest); // For PointOpImages, the srcRect and the destRect are the same. MediaLibAccessor srcAccessor = new MediaLibAccessor(source, destRect, formatTag); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest, destRect, formatTag); mediaLibImage[] srcML = srcAccessor.getMediaLibImages(); mediaLibImage[] dstML = dstAccessor.getMediaLibImages(); switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: int[] constInt = new int[constants.length]; for (int i = 0; i < constants.length; i++) { constInt[i] = ImageUtil.clampRoundInt(constants[i]); } for (int i = 0 ; i < dstML.length; i++) { int mlconstants[] = dstAccessor.getIntParameters(i, constInt); Image.ConstSub(dstML[i], srcML[i], mlconstants); } break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: for (int i = 0 ; i < dstML.length; i++) { double[] mlconstants = dstAccessor.getDoubleParameters(i, constants); Image.ConstSub_Fp(dstML[i], srcML[i], mlconstants); } break; default: String className = this.getClass().getName(); throw new RuntimeException(className + JaiI18N.getString("Generic2")); } if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } // public static OpImage createTestImage(OpImageTester oit) { // double[] consts = {5, 5, 5}; // return new MlibSubtractFromConstOpImage(oit.getSource(), null, // new ImageLayout(oit.getSource()), // consts); // } // // Calls a method on OpImage that uses introspection, to make this // // class, discover it's createTestImage() call, call it and then // // benchmark the performance of the created OpImage chain. // public static void main (String args[]) { // String classname = "com.sun.media.jai.mlib.MlibSubtractFromConstOpImage"; // OpImageTester.performDiagnostics(classname,args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibMinRIF.java0000644000175000017500000000320510203035544025265 0ustar mathieumathieu/* * $RCSfile: MlibMinRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:00 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "Min" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.MinDescriptor * @see MlibMinOpImage * * @since 1.0 * */ public class MlibMinRIF implements RenderedImageFactory { /** Constructor. */ public MlibMinRIF() {} /** * Creates a new instance of MlibMinOpImage in * the rendered image mode. * * @param args The source images. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout)) { return null; } return new MlibMinOpImage(args.getRenderedSource(0), args.getRenderedSource(1), hints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/FFTmediaLib.java0000644000175000017500000002066110203035544025450 0ustar mathieumathieu/* * $RCSfile: FFTmediaLib.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:46 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.image.DataBuffer; import java.text.NumberFormat; import java.util.Arrays; import java.util.Locale; import javax.media.jai.operator.DFTDescriptor; import com.sun.media.jai.opimage.FFT; import com.sun.media.jai.util.MathJAI; import com.sun.medialib.mlib.*; /** * The Fast Fourier Transform (FFT) class interface to mediaLib. * * @since EA4 */ public class FFTmediaLib extends FFT { /* Flag to indicate the special case of unitary scaling with a length equal to an odd power of 2. */ private boolean specialUnitaryScaling = false; /* The square root of 2. */ private static final double SQUARE_ROOT_OF_2 = Math.sqrt(2.0); /** * Construct a new FFTmediaLib object. * * @param negatedExponent Whether the exponent is negated. * @param scaleType The type of scaling to be applied. * @param length The length of the FFT; must be a positive power of 2. */ public FFTmediaLib(boolean negatedExponent, Integer scaleType, int length) { super(negatedExponent, scaleType, length); } /** * Initialize the length-dependent fields. * * @param length The length of the FFT; must be a positive power of 2. */ public void setLength(int length) { // Check whether it's necessary to continue. if(lengthIsSet && length == this.length) { return; } // Ensure that the length is a positive power of two. if(!MathJAI.isPositivePowerOf2(length)) { throw new RuntimeException(JaiI18N.getString("FFTmediaLib0")); } // Cache the length. this.length = length; // Allocate work buffer memory. if(!lengthIsSet || length != real.length) { real = new double[length]; imag = new double[length]; } // Set initialization flag. lengthIsSet = true; // Set flag for special-case: unitary scaling and length = 2**N, N odd. if(scaleType == SCALING_UNITARY) { // The following calculation assumes that the length is a // positive power of 2 which has been verified above. int exponent = 0; int powerOfTwo = 1; while(powerOfTwo < length) { powerOfTwo <<= 1; exponent++; } // Set the special case flag if the exponent is not even. specialUnitaryScaling = exponent % 2 != 0; } } /** * Get data from the internal work data arrays of the FFT object. * * @param dataType The data type of the source data according to * one of the DataBuffer TYPE_* flags. This should be either * DataBuffer.TYPE_FLOAT or DataBuffer.TYPE_DOUBLE. * @param realArg Float or double array of real parts. * @param offsetReal Offset into the array of real parts. * @param strideReal The real array stride value. * @param imagArg Float or double array of imaginary parts. * @param offsetImag Offset into the array of imaginary parts. * @param strideImag The imaginary array stride value. * @param count The number of values to copy. */ public void getData(int dataType, Object realArg, int offsetReal, int strideReal, Object imagArg, int offsetImag, int strideImag) { switch(dataType) { case DataBuffer.TYPE_FLOAT: { float[] realFloat = (float[])realArg; if(imagArg != null) { float[] imagFloat = (float[])imagArg; if(offsetReal == offsetImag && strideReal == strideImag) { for(int i = 0; i < length; i++) { // XXX Should clampFloat() be invoked both // in the next two lines and below? realFloat[offsetReal] = (float)this.real[i]; imagFloat[offsetReal] = (float)this.imag[i]; offsetReal += strideReal; } } else { for(int i = 0; i < length; i++) { realFloat[offsetReal] = (float)this.real[i]; imagFloat[offsetImag] = (float)this.imag[i]; offsetReal += strideReal; offsetImag += strideImag; } } } else { // imagArg == null for(int i = 0; i < length; i++) { realFloat[offsetReal] = (float)this.real[i]; offsetReal += strideReal; } } } break; case DataBuffer.TYPE_DOUBLE: { double[] realDouble = (double[])realArg; if(imagArg != null) { double[] imagDouble = (double[])imagArg; if(offsetReal == offsetImag && strideReal == strideImag) { for(int i = 0; i < length; i++) { realDouble[offsetReal] = this.real[i]; imagDouble[offsetReal] = this.imag[i]; offsetReal += strideReal; } } else { for(int i = 0; i < length; i++) { realDouble[offsetReal] = this.real[i]; imagDouble[offsetImag] = this.imag[i]; offsetReal += strideReal; offsetImag += strideImag; } } } else { // imagArg == null for(int i = 0; i < length; i++) { realDouble[offsetReal] = this.real[i]; offsetReal += strideReal; } } } break; default: // NB: This statement should be unreachable as the destination // image is required to be a floating point type and the // RasterAccessor is supposed to promote the data type of // all rasters to the "minimum" data type of all source // and destination rasters involved. throw new RuntimeException(dataType + JaiI18N.getString("FFTmediaLib1")); } } /** * Calculate the DFT of a complex sequence using the FFT algorithm. */ public void transform() { if(exponentSign < 0) { if(scaleType == SCALING_NONE) { Image.FFT_1(real, imag); } else if(scaleType == SCALING_UNITARY) { Image.FFT_3(real, imag); if(specialUnitaryScaling) { // Divide by Math.sqrt(2.0) to account for the difference // between the definition of mediaLib Group-III forward // transform scaling when the length is an odd power of 2 // and that expected for unitary scaling. for(int i = 0; i < length; i++) { real[i] *= SQUARE_ROOT_OF_2; imag[i] *= SQUARE_ROOT_OF_2; } } } else if(scaleType == SCALING_DIMENSIONS) { Image.FFT_2(real, imag); } } else { if(scaleType == SCALING_NONE) { Image.IFFT_2(real, imag); } else if(scaleType == SCALING_UNITARY) { Image.IFFT_3(real, imag); if(specialUnitaryScaling) { // Multiply by Math.sqrt(2.0) to account for the difference // between the definition of mediaLib Group-III forward // transform scaling when the length is an odd power of 2 // and that expected for unitary scaling. for(int i = 0; i < length; i++) { real[i] /= SQUARE_ROOT_OF_2; imag[i] /= SQUARE_ROOT_OF_2; } } } else if(scaleType == SCALING_DIMENSIONS) { Image.IFFT_1(real, imag); } } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibWarpPolynomialOpImage.java0000644000175000017500000003060510350333605030426 0ustar mathieumathieu/* * $RCSfile: MlibWarpPolynomialOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-12-15 18:35:48 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import java.util.Map; import javax.media.jai.RasterFactory; import javax.media.jai.WarpOpImage; import javax.media.jai.WarpPolynomial; import com.sun.medialib.mlib.*; import com.sun.media.jai.util.ImageUtil; /** * An OpImage implementing the polynomial "Warp" operation * using MediaLib. * *

    With warp operations, there is no forward mapping (from source to * destination). JAI images are tiled, while mediaLib does not handle * tiles and consider each tile an individual image. For each tile in * destination, in order not to cobble the entire source image, the * computeTile method in this class attemps to do a backward * mapping on the tile region using the pixels along the perimeter of the * rectangular region. The hope is that the mapped source rectangle * should include all source pixels needed for this particular destination * tile. However, with certain unusual warp points, an inner destination * pixel may be mapped outside of the mapped perimeter pixels. In this * case, this destination pixel is not filled, and left black. * * @see javax.media.jai.operator.WarpDescriptor * @see MlibWarpRIF * * @since 1.0 * */ final class MlibWarpPolynomialOpImage extends WarpOpImage { /** The x and y coefficients. */ private double[] xCoeffs; private double[] yCoeffs; /** * Indicates what kind of interpolation to use; may be * Constants.MLIB_NEAREST, * Constants.MLIB_BILINEAR, * or Constants.MLIB_BICUBIC, * and was determined in MlibWarpRIF.create(). */ private int filter; /** The pre and post scale factors. */ private double preScaleX; private double preScaleY; private double postScaleX; private double postScaleY; /** * Constructs a MlibWarpPolynomialOpImage. * * @param source The source image. * @param layout The destination image layout. * @param warp An object defining the warp algorithm. * @param interp An object describing the interpolation method. */ public MlibWarpPolynomialOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, WarpPolynomial warp, Interpolation interp, int filter, double[] backgroundValues) { super(source, layout, config, true, extender, interp, warp, backgroundValues); float[] xc = warp.getXCoeffs(); float[] yc = warp.getYCoeffs(); int size = xc.length; xCoeffs = new double[size]; // X and Y coefficients as doubles yCoeffs = new double[size]; for (int i = 0; i < size; i++) { xCoeffs[i] = xc[i]; yCoeffs[i] = yc[i]; } this.filter = filter; // interpolation preScaleX = warp.getPreScaleX(); // pre/post factors preScaleY = warp.getPreScaleY(); postScaleX = warp.getPostScaleX(); postScaleY = warp.getPostScaleY(); } /** * Returns the minimum bounding box of the region of the specified * source to which a particular Rectangle of the * destination will be mapped. * * @param destRect the Rectangle in destination coordinates. * @param sourceIndex the index of the source image. * * @return a Rectangle indicating the source bounding box, * or null if the bounding box is unknown. * * @throws IllegalArgumentException if sourceIndex is * negative or greater than the index of the last source. * @throws IllegalArgumentException if destRect is * null. */ protected Rectangle backwardMapRect(Rectangle destRect, int sourceIndex) { // Superclass method will throw documented exceptions if needed. Rectangle wrect = super.backwardMapRect(destRect, sourceIndex); // "Dilate" the backwarp mapped rectangle to account for // the lack of being able to know the floating point result of // mapDestRect() and to mimic what is done in AffineOpImage. // See bug 4518223 for more information. wrect.setBounds(wrect.x - 1, wrect.y - 1, wrect.width + 2, wrect.height + 2); return wrect; } /** * Computes a tile. A new WritableRaster is created to * represent the requested tile. Its width and height equals to this * image's tile width and tile height respectively. If the requested * tile lies outside of the image's boundary, the created raster is * returned with all of its pixels set to 0. * *

    This method overrides the method in WarpOpImage * and performs source cobbling when necessary. MediaLib is used to * calculate the actual warping. * * @param tileX The X index of the tile. * @param tileY The Y index of the tile. * * @return The tile as a Raster. */ public Raster computeTile(int tileX, int tileY) { /* The origin of the tile. */ Point org = new Point(tileXToX(tileX), tileYToY(tileY)); /* Create a new WritableRaster to represent this tile. */ WritableRaster dest = createWritableRaster(sampleModel, org); /* Find the intersection between this tile and the writable bounds. */ Rectangle rect = new Rectangle(org.x, org.y, tileWidth, tileHeight); Rectangle destRect = rect.intersection(computableBounds); Rectangle destRect1 = rect.intersection(getBounds()); if (destRect.isEmpty()) { if (setBackground) { ImageUtil.fillBackground(dest, destRect1, backgroundValues); } return dest; // tile completely outside of writable bounds } /* Map destination rectangle to source space. */ Rectangle srcRect = backwardMapRect(destRect, 0).intersection( getSourceImage(0).getBounds()); if (srcRect.isEmpty()) { if (setBackground) { ImageUtil.fillBackground(dest, destRect1, backgroundValues); } return dest; // outside of source bounds } if (!destRect1.equals(destRect)) { // beware that destRect1 contains destRect ImageUtil.fillBordersWithBackgroundValues(destRect1, destRect, dest, backgroundValues); } /* Add the interpolation paddings. */ int l = interp== null ? 0 : interp.getLeftPadding(); int r = interp== null ? 0 : interp.getRightPadding(); int t = interp== null ? 0 : interp.getTopPadding(); int b = interp== null ? 0 : interp.getBottomPadding(); srcRect = new Rectangle(srcRect.x - l, srcRect.y - t, srcRect.width + l + r, srcRect.height + t + b); /* Cobble source into one Raster. */ Raster[] sources = new Raster[1]; sources[0] = getBorderExtender() != null ? getSourceImage(0).getExtendedData(srcRect, extender) : getSourceImage(0).getData(srcRect); computeRect(sources, dest, destRect); // Recycle the source tile if(getSourceImage(0).overlapsMultipleTiles(srcRect)) { recycleTile(sources[0]); } return dest; } /** * Performs the "Warp" operation on a rectangular region of * the same. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; /* Find the mediaLib data tag. */ int formatTag = MediaLibAccessor.findCompatibleTag(sources, dest); MediaLibAccessor srcMA = new MediaLibAccessor(source, source.getBounds(), formatTag); MediaLibAccessor dstMA = new MediaLibAccessor(dest, destRect, formatTag); mediaLibImage[] srcMLI = srcMA.getMediaLibImages(); mediaLibImage[] dstMLI = dstMA.getMediaLibImages(); switch (dstMA.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: if (setBackground) for (int i = 0 ; i < dstMLI.length; i++) { Image.PolynomialWarp2(dstMLI[i], srcMLI[i], xCoeffs, yCoeffs, destRect.x, destRect.y, source.getMinX(), source.getMinY(), preScaleX, preScaleY, postScaleX, postScaleY, filter, Constants.MLIB_EDGE_DST_NO_WRITE, intBackgroundValues); } else for (int i = 0 ; i < dstMLI.length; i++) { Image.PolynomialWarp(dstMLI[i], srcMLI[i], xCoeffs, yCoeffs, destRect.x, destRect.y, source.getMinX(), source.getMinY(), preScaleX, preScaleY, postScaleX, postScaleY, filter, Constants.MLIB_EDGE_DST_NO_WRITE); MlibUtils.clampImage(dstMLI[i], getColorModel()); } break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: if (setBackground) for (int i = 0 ; i < dstMLI.length; i++) { Image.PolynomialWarp2_Fp(dstMLI[i], srcMLI[i], xCoeffs, yCoeffs, destRect.x, destRect.y, source.getMinX(), source.getMinY(), preScaleX, preScaleY, postScaleX, postScaleY, filter, Constants.MLIB_EDGE_DST_NO_WRITE, backgroundValues); } else for (int i = 0 ; i < dstMLI.length; i++) { Image.PolynomialWarp_Fp(dstMLI[i], srcMLI[i], xCoeffs, yCoeffs, destRect.x, destRect.y, source.getMinX(), source.getMinY(), preScaleX, preScaleY, postScaleX, postScaleY, filter, Constants.MLIB_EDGE_DST_NO_WRITE); } break; default: throw new RuntimeException(JaiI18N.getString("Generic2")); } if (dstMA.isDataCopy()) { dstMA.clampDataArrays(); dstMA.copyDataToRaster(); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibBinarizeRIF.java0000644000175000017500000000467510203035544026321 0ustar mathieumathieu/* * $RCSfile: MlibBinarizeRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:51 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.image.DataBuffer; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.renderable.RenderedImageFactory; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "Binarize" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.BinarizeDescriptor */ public class MlibBinarizeRIF implements RenderedImageFactory { /** Constructor. */ public MlibBinarizeRIF() {} /** * Creates a new instance of MlibBinarizeOpImage in * the rendered image mode. * * @param args The source image, thresh value * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { // Get the source and its SampleModel. RenderedImage source = args.getRenderedSource(0); SampleModel sm = source.getSampleModel(); // Check that the source is single-banded and mediaLib compatible. // Ignore the layout because if it doesn't specify a bilevel image // then MlibBinarizeOpImage will revise it. if (!MediaLibAccessor.isMediaLibCompatible(args) || sm.getNumBands() > 1) { return null; } // Get the threshold value. double thresh = args.getDoubleParameter(0); // java set all 0's or 1's fast if ((thresh > 255|| thresh <=0) && sm.getDataType()== DataBuffer.TYPE_BYTE || (thresh > Short.MAX_VALUE|| thresh <=0) && sm.getDataType()== DataBuffer.TYPE_SHORT|| (thresh > Integer.MAX_VALUE|| thresh <=0) && sm.getDataType()== DataBuffer.TYPE_INT) return null; // Get ImageLayout from RenderingHints. ImageLayout layout = RIFUtil.getImageLayoutHint(hints); return new MlibBinarizeOpImage(source, layout, hints, thresh); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibSeparableConvolveOpImage.java0000644000175000017500000002264110203035544031062 0ustar mathieumathieu/* * $RCSfile: MlibSeparableConvolveOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:05 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.KernelJAI; import java.util.Map; import com.sun.medialib.mlib.*; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class to perform convolution on a source image. * *

    This class implements a convolution operation. Convolution is a * spatial operation that computes each output sample by multiplying * elements of a kernel with the samples surrounding a particular * source sample. * *

    For each destination sample, the kernel is rotated 180 degrees * and its "key element" is placed over the source pixel corresponding * with the destination pixel. The kernel elements are multiplied * with the source pixels under them, and the resulting products are * summed together to produce the destination sample value. * *

    Example code for the convolution operation on a single sample * dst[x][y] is as follows, assuming the kernel is of size M rows x N * columns and has already been rotated through 180 degrees. The * kernel's key element is located at position (xKey, yKey): * *

     * dst[x][y] = 0;
     * for (int i = -xKey; i < M - xKey; i++) {
     *     for (int j = -yKey; j < N - yKey; j++) {
     *         dst[x][y] += src[x + i][y + j] * kernel[xKey + i][yKey + j];
     *     }
     * }
     * 
    * *

    Convolution, or any neighborhood operation, leaves a band of * pixels around the edges undefined, i.e., for a 3x3 kernel, only * four kernel elements and four source pixels contribute to the * destination pixel located at (0,0). Such pixels are not includined * in the destination image, unless a non-null BorderExtender is provided. * *

    The Kernel cannot be bigger in any dimension than the image data. * * * @see KernelJAI */ final class MlibSeparableConvolveOpImage extends AreaOpImage { /** * The kernel with which to do the convolve operation. */ protected KernelJAI kernel; /** Kernel variables. */ private int kw, kh; float hValues[]; float vValues[]; double hDoubleData[], vDoubleData[]; int hIntData[], vIntData[]; int shift = -1; /** * Creates a MlibSeparableConvolveOpImage given the image source * and pre-rotated convolution kernel. The image dimensions are * derived from the source image. The tile grid layout, * SampleModel, and ColorModel may optionally be specified by an * ImageLayout object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * or null. If null, a default cache will be used. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param kernel the pre-rotated convolution KernelJAI. */ public MlibSeparableConvolveOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, KernelJAI kernel) { super(source, layout, config, true, extender, kernel.getLeftPadding(), kernel.getRightPadding(), kernel.getTopPadding(), kernel.getBottomPadding()); this.kernel = kernel; kw = kernel.getWidth(); kh = kernel.getHeight(); // kx, ky dealt with in AreaOpImage hValues = kernel.getHorizontalKernelData(); vValues = kernel.getVerticalKernelData(); // A little inefficient but figuring out what datatype // mediaLibAccessor will want is tricky. hDoubleData = new double[hValues.length]; for (int i = 0; i < hValues.length; i++) { hDoubleData[i] = (double)hValues[i]; } vDoubleData = new double[vValues.length]; for (int i = 0; i < vValues.length; i++) { vDoubleData[i] = (double)vValues[i]; } hIntData = new int[hValues.length]; vIntData = new int[vValues.length]; } private synchronized void setShift(int formatTag) { if (shift == -1) { int mediaLibDataType = MediaLibAccessor.getMediaLibDataType(formatTag); shift = Image.SConvKernelConvert(hIntData, vIntData, hDoubleData, vDoubleData, kw,kh, mediaLibDataType); } } /** * Performs convolution on a specified rectangle. The sources are * cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; Rectangle srcRect = mapDestRect(destRect, 0); int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest); MediaLibAccessor srcAccessor = new MediaLibAccessor(source,srcRect,formatTag); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest,destRect,formatTag); int numBands = getSampleModel().getNumBands(); mediaLibImage[] srcML = srcAccessor.getMediaLibImages(); mediaLibImage[] dstML = dstAccessor.getMediaLibImages(); for (int i = 0; i < dstML.length; i++) { switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: if (shift == -1) { setShift(formatTag); } switch (kw) { case 3: Image.SConv3x3(dstML[i], srcML[i], hIntData, vIntData, shift, ((1 << numBands)-1) , Constants.MLIB_EDGE_DST_NO_WRITE); break; case 5: Image.SConv5x5(dstML[i], srcML[i], hIntData, vIntData, shift, ((1 << numBands)-1) , Constants.MLIB_EDGE_DST_NO_WRITE); break; case 7: Image.SConv7x7(dstML[i], srcML[i], hIntData, vIntData, shift, ((1 << numBands)-1) , Constants.MLIB_EDGE_DST_NO_WRITE); break; } break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: switch (kw) { case 3: Image.SConv3x3_Fp(dstML[i], srcML[i], hDoubleData, vDoubleData, ((1 << numBands)-1) , Constants.MLIB_EDGE_DST_NO_WRITE); break; case 5: Image.SConv5x5_Fp(dstML[i], srcML[i], hDoubleData, vDoubleData, ((1 << numBands)-1) , Constants.MLIB_EDGE_DST_NO_WRITE); break; case 7: Image.SConv7x7_Fp(dstML[i], srcML[i], hDoubleData, vDoubleData, ((1 << numBands)-1) , Constants.MLIB_EDGE_DST_NO_WRITE); break; } break; default: String className = this.getClass().getName(); throw new RuntimeException(JaiI18N.getString("Generic2")); } } if (dstAccessor.isDataCopy()) { dstAccessor.copyDataToRaster(); } } // public static OpImage createTestImage(OpImageTester oit) { // float hdata[] = {0.33f,0.33f,0.33f}; // float vdata[] = {0.33f,0.33f,0.33f}; // KernelJAI kJAI = new KernelJAI(3,3,1,1,hdata,vdata); // return new MlibSeparableConvolveOpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource()), // kJAI); // } // public static void main (String args[]) { // String classname = "com.sun.media.jai.mlib.MlibSeparableConvolveOpImage"; // OpImageTester.performDiagnostics(classname,args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibRotateRIF.java0000644000175000017500000002071510340447404026011 0ustar mathieumathieu/* * $RCSfile: MlibRotateRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-11-21 22:49:40 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.geom.NoninvertibleTransformException; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationBicubic2; import javax.media.jai.InterpolationBicubic; import javax.media.jai.InterpolationBilinear; import javax.media.jai.InterpolationNearest; import javax.media.jai.InterpolationTable; import javax.media.jai.ImageLayout; import javax.media.jai.JAI; import javax.media.jai.OpImage; import javax.media.jai.PlanarImage; import java.util.Map; import javax.media.jai.BorderExtender; import com.sun.media.jai.opimage.RIFUtil; import com.sun.media.jai.opimage.PointMapperOpImage; import com.sun.media.jai.opimage.TranslateIntOpImage; /** * A RIF supporting the "Rotate" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.RotateDescriptor * @see MlibAffineOpimage * * @since EA4 */ public class MlibRotateRIF implements RenderedImageFactory { /** Constructor. */ public MlibRotateRIF() {} /** * Creates an rotate operation. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); Interpolation interp = (Interpolation)args.getObjectParameter(3); double[] backgroundValues = (double[])args.getObjectParameter(4); RenderedImage source = args.getRenderedSource(0); if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout) || // Medialib cannot deal with source image having tiles with any // dimension greater than or equal to 32768 source.getTileWidth() >= 32768 || source.getTileHeight() >= 32768) { return null; } /* Get BorderExtender from hints if any. */ BorderExtender extender = RIFUtil.getBorderExtenderHint(hints); float x_center = args.getFloatParameter(0); float y_center = args.getFloatParameter(1); float angle = args.getFloatParameter(2); /* * Convert angle to degrees (within some precision) given PI's * transcendantal nature. All this, to check if we can call * simpler methods like Copy or Transpose for certain angles * viz., 0, 90, 180, 270, 360, 450, ..... */ double tmp_angle = 180.0F * angle / Math.PI; double rnd_angle = Math.round(tmp_angle); /* Represent the angle as an AffineTransform. */ AffineTransform transform = AffineTransform.getRotateInstance(angle, x_center, y_center); // Check if angle is (nearly) integral if (Math.abs(rnd_angle - tmp_angle) < 0.0001) { int dangle = (int)rnd_angle % 360; // Shift dangle into the range [0..359]. if (dangle < 0) { dangle += 360; } // // Do a copy if angle is 0 degrees or // multiple of 360 degrees // if (dangle == 0) { return new MlibCopyOpImage(source, hints, layout); } int ix_center = (int)Math.round(x_center); int iy_center = (int)Math.round(y_center); // Do a transpose if angle is mutiple of 270, 180, 90 degrees // and the translation is (nearly) integral. if (((dangle % 90) == 0) && (Math.abs(x_center - ix_center) < 0.0001) && (Math.abs(y_center - iy_center) < 0.0001)) { int transType = -1; int rotMinX = 0; int rotMinY = 0; int sourceMinX = source.getMinX(); int sourceMinY = source.getMinY(); int sourceMaxX = sourceMinX + source.getWidth(); int sourceMaxY = sourceMinY + source.getHeight(); if (dangle == 90) { transType = 4; rotMinX = ix_center - (sourceMaxY - iy_center); rotMinY = iy_center - (ix_center - sourceMinX); } else if (dangle == 180) { transType = 5; rotMinX = 2*ix_center - sourceMaxX; rotMinY = 2*iy_center - sourceMaxY; } else { // dangle == 270 transType = 6; rotMinX = ix_center - (iy_center - sourceMinY); rotMinY = iy_center - (sourceMaxX - ix_center); } RenderedImage trans = new MlibTransposeOpImage(source, hints, layout, transType); // Determine current image origin int imMinX = trans.getMinX(); int imMinY = trans.getMinY(); // Translate image and return it // TranslateIntOpImage can't deal with ImageLayout hint if (layout == null) { OpImage intermediateImage = new TranslateIntOpImage(trans, hints, rotMinX - imMinX, rotMinY - imMinY); try { return new PointMapperOpImage(intermediateImage, hints, transform); } catch(NoninvertibleTransformException nite) { return intermediateImage; } } else { ParameterBlock pbScale = new ParameterBlock(); pbScale.addSource(trans); pbScale.add(0F); pbScale.add(0F); pbScale.add(rotMinX - imMinX); pbScale.add(rotMinY - imMinY); pbScale.add(interp); PlanarImage intermediateImage = JAI.create("scale", pbScale, hints).getRendering(); try { return new PointMapperOpImage(intermediateImage, hints, transform); } catch(NoninvertibleTransformException nite) { return intermediateImage; } } } } /* * At this point we know that we cannot call other operations. * Have to do Affine. */ /* Do the Affine operation. */ if (interp instanceof InterpolationNearest) { return new MlibAffineNearestOpImage(source, extender, hints, layout, transform, interp, backgroundValues); } else if (interp instanceof InterpolationBilinear) { return new MlibAffineBilinearOpImage(source, extender, hints, layout, transform, interp, backgroundValues); } else if (interp instanceof InterpolationBicubic || interp instanceof InterpolationBicubic2) { return new MlibAffineBicubicOpImage(source, extender, hints, layout, transform, interp, backgroundValues); } else if (interp instanceof InterpolationTable) { return new MlibAffineTableOpImage(source, extender, hints, layout, transform, interp, backgroundValues); } else { return null; } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibNotOpImage.java0000644000175000017500000001161010203035544026202 0ustar mathieumathieu/* * $RCSfile: MlibNotOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:02 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.PixelInterleavedSampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PointOpImage; import java.util.Map; import com.sun.medialib.mlib.*; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage that performs the Not operation on 2 images through mediaLib. * */ final class MlibNotOpImage extends PointOpImage { /** * Constructs an MlibNotOpImage. The image dimensions are copied * from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param layout an ImageLayout optionally containing the tile * grid layout, SampleModel, and ColorModel, or null. */ public MlibNotOpImage(RenderedImage source, Map config, ImageLayout layout) { super(source, layout, config, true); // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * Not the pixel values of a rectangle from the source. * The source is cobbled. * * @param sources an array of sources, guarantee to provide all * necessary source data for computing the rectangle. * @param dest a tile that contains the rectangle to be computed. * @param destRect the rectangle within this OpImage to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest); MediaLibAccessor srcAccessor = new MediaLibAccessor(sources[0], destRect,formatTag); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest, destRect, formatTag); switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: mediaLibImage[] srcML = srcAccessor.getMediaLibImages(); mediaLibImage[] dstML = dstAccessor.getMediaLibImages(); for (int i = 0 ; i < dstML.length; i++) { Image.Not(dstML[i], srcML[i]); } break; default: String className = this.getClass().getName(); throw new RuntimeException(className + JaiI18N.getString("Generic2")); } if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } // public static void main (String args[]) { // System.out.println("MlibNotOpImage Test"); // ImageLayout layout; // OpImage src, dst; // Rectangle rect = new Rectangle(0, 0, 5, 5); // System.out.println("1. PixelInterleaved byte 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 800, 800, 0, 0, // 200, 200, DataBuffer.TYPE_BYTE, // 3, false); // src = OpImageTester.createRandomOpImage(layout); // dst = new MlibNotOpImage(src, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("2. Banded byte 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 800, 800, 0, 0, // 200, 200, DataBuffer.TYPE_BYTE, // 3, true); // src = OpImageTester.createRandomOpImage(layout); // dst = new MlibNotOpImage(src, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("3. PixelInterleaved int 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 512, 512, 0, 0, 200, 200, // DataBuffer.TYPE_INT, 3, false); // src = OpImageTester.createRandomOpImage(layout); // dst = new MlibNotOpImage(src, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("4. Banded int 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 512, 512, 0, 0, // 200, 200, DataBuffer.TYPE_INT, // 3, true); // src = OpImageTester.createRandomOpImage(layout); // dst = new MlibNotOpImage(src, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibOrConstRIF.java0000644000175000017500000000413210203035544026131 0ustar mathieumathieu/* * $RCSfile: MlibOrConstRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:02 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "OrConst" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.OrConstDescriptor * @see MlibOrConstOpImage * */ public class MlibOrConstRIF implements RenderedImageFactory { /** Constructor. */ public MlibOrConstRIF() {} /** * Creates a new instance of MlibOrConstOpImage in * the rendered image mode. * * @param args The source image and the constants. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout)) { return null; } /* Check whether dest has data type of float or double. */ if (layout != null) { SampleModel sm = layout.getSampleModel(null); if (sm != null) { int dtype = sm.getDataType(); if (dtype == DataBuffer.TYPE_FLOAT || dtype == DataBuffer.TYPE_DOUBLE) { return null; } } } return new MlibOrConstOpImage(args.getRenderedSource(0), hints, layout, (int[])args.getObjectParameter(0)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibDivideIntoConstOpImage.java0000644000175000017500000001101510203035544030506 0ustar mathieumathieu/* * $RCSfile: MlibDivideIntoConstOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:54 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PointOpImage; import java.util.Map; import com.sun.medialib.mlib.*; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class that divides an image into a constant * */ final class MlibDivideIntoConstOpImage extends PointOpImage { private double[] constants; /** * Constructs an MlibDivideIntoConstOpImage. The image dimensions * are copied from the source image. The tile grid layout, * SampleModel, and ColorModel may optionally be specified by an * ImageLayout object. * * @param source a RenderedImage. * @param layout an ImageLayout optionally containing the tile * grid layout, SampleModel, and ColorModel, or null. */ public MlibDivideIntoConstOpImage(RenderedImage source, Map config, ImageLayout layout, double[] constants) { super(source, layout, config, true); this.constants = MlibUtils.initConstants(constants, getSampleModel().getNumBands()); // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * Divide the pixel values of a rectangle into a given constant. * The sources are cobbled. * * @param sources an array of sources, guarantee to provide all * necessary source data for computing the rectangle. * @param dest a tile that contains the rectangle to be computed. * @param destRect the rectangle within this OpImage to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; Rectangle srcRect = mapDestRect(destRect, 0); int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest); MediaLibAccessor srcAccessor = new MediaLibAccessor(source,srcRect,formatTag); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest,destRect,formatTag); mediaLibImage[] srcML = srcAccessor.getMediaLibImages(); mediaLibImage[] dstML = dstAccessor.getMediaLibImages(); switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: for (int i = 0; i < dstML.length; i++) { double[] mlconstants = dstAccessor.getDoubleParameters(i, constants); Image.ConstDiv(dstML[i], srcML[i], mlconstants); } break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: for (int i = 0; i < dstML.length; i++) { double[] mlconstants = dstAccessor.getDoubleParameters(i, constants); Image.ConstDiv_Fp(dstML[i], srcML[i], mlconstants); } break; default: String className = this.getClass().getName(); throw new RuntimeException(className + JaiI18N.getString("Generic2")); } if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } // public static OpImage createTestImage(OpImageTester oit) { // double[] consts = { 255, 255, 255 }; // return new MlibDivideIntoConstOpImage(oit.getSource(), null, // new ImageLayout(oit.getSource()), // consts); // } // // Calls a method on OpImage that uses introspection, to make this // // class, discover it's createTestImage() call, call it and then // // benchmark the performance of the created OpImage chain. // public static void main (String args[]) { // String classname = "com.sun.media.jai.mlib.MlibDivideIntoConstOpImage"; // OpImageTester.performDiagnostics(classname,args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibMinFilterOpImage.java0000644000175000017500000001371010203035544027336 0ustar mathieumathieu/* * $RCSfile: MlibMinFilterOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:00 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import java.util.Map; import javax.media.jai.operator.MinFilterDescriptor; import javax.media.jai.operator.MinFilterShape; import com.sun.medialib.mlib.*; /** * An OpImage class that subclasses will use to perform * MinFiltering with specific masks. * * @see import javax.media.jai.operator.MinFilterDescriptor */ final class MlibMinFilterOpImage extends AreaOpImage { protected int maskType; //XXX using mediaLib's type for MEDIAN operator!! protected int maskSize; /** * Creates a MlibMinFilterOpImage given an image source, an * optional BorderExtender, a maskType and maskSize. The image * dimensions are derived the source image. The tile grid layout, * SampleModel, and ColorModel may optionally be specified by an * ImageLayout object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param maskType the filter mask type. * @param maskSize the filter mask size. */ public MlibMinFilterOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, MinFilterShape maskType, int maskSize) { super(source, layout, config, true, extender, (maskSize-1)/2, (maskSize-1)/2, (maskSize/2), (maskSize/2)); //this.maskType = mapToMlibMaskType(maskType); this.maskSize = maskSize; } private static int mapToMlibMaskType(MinFilterShape maskType) { if(maskType.equals(MinFilterDescriptor.MIN_MASK_SQUARE)) { return Constants.MLIB_MEDIAN_MASK_RECT; } else if(maskType.equals(MinFilterDescriptor.MIN_MASK_PLUS)) { return Constants.MLIB_MEDIAN_MASK_PLUS; } else if(maskType.equals(MinFilterDescriptor.MIN_MASK_X)) { return Constants.MLIB_MEDIAN_MASK_X; } else if(maskType.equals( MinFilterDescriptor.MIN_MASK_SQUARE_SEPARABLE)) { return Constants.MLIB_MEDIAN_MASK_RECT_SEPARABLE; } throw new RuntimeException(JaiI18N.getString("MinFilterOpImage0")); } /** * Performs median filtering on a specified rectangle. The sources are * cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; Rectangle srcRect = mapDestRect(destRect, 0); int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest); MediaLibAccessor srcAccessor = new MediaLibAccessor(source,srcRect,formatTag); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest,destRect,formatTag); int numBands = getSampleModel().getNumBands(); int cmask = (1 << numBands) -1; mediaLibImage[] srcML = srcAccessor.getMediaLibImages(); mediaLibImage[] dstML = dstAccessor.getMediaLibImages(); for (int i = 0; i < dstML.length; i++) { switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: if (maskSize == 3) { // Call appropriate Medialib accelerated function Image.MinFilter3x3(dstML[i], srcML[i]); } else if (maskSize == 5) { // Call appropriate Medialib accelerated function Image.MinFilter5x5(dstML[i], srcML[i]); } else if (maskSize == 7) { // Call appropriate Medialib accelerated function Image.MinFilter7x7(dstML[i], srcML[i]); } break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: if (maskSize == 3) { // Call appropriate Medialib accelerated function Image.MinFilter3x3_Fp(dstML[i], srcML[i]); } else if (maskSize == 5) { // Call appropriate Medialib accelerated function Image.MinFilter5x5_Fp(dstML[i], srcML[i]); } else if (maskSize == 7) { // Call appropriate Medialib accelerated function Image.MinFilter7x7_Fp(dstML[i], srcML[i]); } break; default: String className = this.getClass().getName(); throw new RuntimeException(JaiI18N.getString("Generic2")); } } if (dstAccessor.isDataCopy()) { dstAccessor.copyDataToRaster(); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibCopyOpImage.java0000644000175000017500000000504110203035544026355 0ustar mathieumathieu/* * $RCSfile: MlibCopyOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:53 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PointOpImage; import java.util.Map; import com.sun.medialib.mlib.*; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class that copies an image from source to dest. * */ final class MlibCopyOpImage extends PointOpImage { /** * Constructs an MlibCopyOpImage. The image dimensions are copied * from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * or null. If null, a default cache will be used. * @param layout an ImageLayout optionally containing the tile * grid layout, SampleModel, and ColorModel, or null. */ public MlibCopyOpImage(RenderedImage source, Map config, ImageLayout layout) { super(source, layout, config, true); } /** * Copy the pixel values of a rectangle with a given constant. * The sources are cobbled. * * @param sources an array of sources, guarantee to provide all * necessary source data for computing the rectangle. * @param dest a tile that contains the rectangle to be computed. * @param destRect the rectangle within this OpImage to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest); MediaLibAccessor srcMA = new MediaLibAccessor(sources[0], destRect, formatTag); MediaLibAccessor dstMA = new MediaLibAccessor(dest, destRect, formatTag); mediaLibImage[] srcMLI = srcMA.getMediaLibImages(); mediaLibImage[] dstMLI = dstMA.getMediaLibImages(); for (int i = 0 ; i < dstMLI.length; i++) { Image.Copy(dstMLI[i], srcMLI[i]); } if (dstMA.isDataCopy()) { dstMA.copyDataToRaster(); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibMaxOpImage.java0000644000175000017500000000511410203035544026171 0ustar mathieumathieu/* * $RCSfile: MlibMaxOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:59 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.PointOpImage; import java.util.Map; import com.sun.medialib.mlib.*; /** * An OpImage implementing the "Max" operation * using MediaLib. * * @see javax.media.jai.operator.MaxDescriptor * @see MlibMaxRIF * * @since 1.0 * */ final class MlibMaxOpImage extends PointOpImage { /** Constructor. */ public MlibMaxOpImage(RenderedImage source1, RenderedImage source2, Map config, ImageLayout layout) { super(source1, source2, layout, config, true); // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * Performs the "Max" operation on a rectangular region of * the same. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { int formatTag = MediaLibAccessor.findCompatibleTag(sources, dest); MediaLibAccessor srcMA1 = new MediaLibAccessor(sources[0], destRect, formatTag); MediaLibAccessor srcMA2 = new MediaLibAccessor(sources[1], destRect, formatTag); MediaLibAccessor dstMA = new MediaLibAccessor(dest, destRect, formatTag); mediaLibImage[] srcMLI1 = srcMA1.getMediaLibImages(); mediaLibImage[] srcMLI2 = srcMA2.getMediaLibImages(); mediaLibImage[] dstMLI = dstMA.getMediaLibImages(); switch (dstMA.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: for (int i = 0 ; i < dstMLI.length; i++) { Image.Max(dstMLI[i], srcMLI1[i], srcMLI2[i]); } break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: for (int i = 0 ; i < dstMLI.length; i++) { Image.Max_Fp(dstMLI[i], srcMLI1[i], srcMLI2[i]); } break; } if (dstMA.isDataCopy()) { dstMA.clampDataArrays(); dstMA.copyDataToRaster(); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibAffineRIF.java0000644000175000017500000002302210203035544025731 0ustar mathieumathieu/* * $RCSfile: MlibAffineRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:49 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.image.DataBuffer; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationBicubic2; import javax.media.jai.InterpolationBicubic; import javax.media.jai.InterpolationBilinear; import javax.media.jai.InterpolationNearest; import javax.media.jai.InterpolationTable; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; import com.sun.media.jai.opimage.TranslateIntOpImage; /** * A RIF supporting the "Affine" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.AffineDescriptor * @see MlibAffineOpImage * @see MlibScaleOpImage * * @since EA4 */ public class MlibAffineRIF implements RenderedImageFactory { private static final float TOLERANCE = 0.01F; /** Constructor. */ public MlibAffineRIF() {} /** * Creates a new instance of MlibAffineOpImage in * the rendered image mode. * * @param args The source image, the AffineTransform, * and the Interpolation. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); // Get operation parameters. AffineTransform transform = (AffineTransform)args.getObjectParameter(0); Interpolation interp = (Interpolation)args.getObjectParameter(1); double[] backgroundValues = (double[])args.getObjectParameter(2); RenderedImage source = args.getRenderedSource(0); if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout) || // Medialib cannot deal with source image having tiles with any // dimension greater than or equal to 32768 source.getTileWidth() >= 32768 || source.getTileHeight() >= 32768) { return null; } SampleModel sm = source.getSampleModel(); boolean isBilevel = (sm instanceof MultiPixelPackedSampleModel) && (sm.getSampleSize(0) == 1) && (sm.getDataType() == DataBuffer.TYPE_BYTE || sm.getDataType() == DataBuffer.TYPE_USHORT || sm.getDataType() == DataBuffer.TYPE_INT); if (isBilevel) { // Let Java code handle it, reformatting is slower return null; } // Get BorderExtender from hints if any. BorderExtender extender = RIFUtil.getBorderExtenderHint(hints); /* Get the affine transform. */ double[] tr = new double[6]; transform.getMatrix(tr); /* * Check and see if the affine transform is doing a copy. * If so call the copy operation. */ if ((tr[0] == 1.0) && (tr[3] == 1.0) && (tr[2] == 0.0) && (tr[1] == 0.0) && (tr[4] == 0.0) && (tr[5] == 0.0)) { /* It's a copy. */ return new MlibCopyOpImage(source, hints, layout); } /* * Check and see if the affine transform is in fact doing * a Translate operation. That is a scale by 1 and no rotation. * In which case call translate. Note that only integer translate * is applicable. For non-integer translate we'll have to do the * affine. */ if ((tr[0] == 1.0) && (tr[3] == 1.0) && (tr[2] == 0.0) && (tr[1] == 0.0) && (Math.abs(tr[4] - (int) tr[4]) < TOLERANCE) && (Math.abs(tr[5] - (int) tr[5]) < TOLERANCE) && layout == null) { // TranslateIntOpImage can't deal with ImageLayout hint /* It's a integer translate. */ return new TranslateIntOpImage(source, hints, (int)tr[4], (int)tr[5]); } /* * Check and see if the affine transform is in fact doing * a Scale operation. In which case call Scale which is more * optimized than Affine. */ if ((tr[0] > 0.0) && (tr[2] == 0.0) && (tr[1] == 0.0) && (tr[3] > 0.0)) { /* It's a scale. */ if (interp instanceof InterpolationNearest) { return new MlibScaleNearestOpImage(source, extender, hints, layout, (float)tr[0], // xScale (float)tr[3], // yScale (float)tr[4], // xTrans (float)tr[5], // yTrans interp); } else if (interp instanceof InterpolationBilinear) { return new MlibScaleBilinearOpImage(source, extender, hints, layout, (float)tr[0], // xScale (float)tr[3], // yScale (float)tr[4], // xTrans (float)tr[5], // yTrans interp); } else if (interp instanceof InterpolationBicubic || interp instanceof InterpolationBicubic2) { return new MlibScaleBicubicOpImage(source, extender, hints, layout, (float)tr[0], // xScale (float)tr[3], // yScale (float)tr[4], // xTrans (float)tr[5], // yTrans interp); } else if (interp instanceof InterpolationTable) { return new MlibScaleTableOpImage(source, extender, hints, layout, (float)tr[0], // xScale (float)tr[3], // yScale (float)tr[4], // xTrans (float)tr[5], // yTrans interp); } else { return null; } } /* Have to do an Affine. */ if (interp instanceof InterpolationNearest) { return new MlibAffineNearestOpImage(source, extender, hints, layout, transform, interp, backgroundValues); } else if (interp instanceof InterpolationBilinear) { return new MlibAffineBilinearOpImage(source, extender, hints, layout, transform, interp, backgroundValues); } else if (interp instanceof InterpolationBicubic || interp instanceof InterpolationBicubic2) { return new MlibAffineBicubicOpImage(source, extender, hints, layout, transform, interp, backgroundValues); } else if (interp instanceof InterpolationTable) { return new MlibAffineTableOpImage(source, extender, hints, layout, transform, interp, backgroundValues); } else { return null; } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibDilate3SquareOpImage.java0000644000175000017500000001332610346146715030131 0ustar mathieumathieu/* * $RCSfile: MlibDilate3SquareOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-12-09 00:20:28 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.JAI; import javax.media.jai.KernelJAI; import javax.media.jai.OpImage; import java.util.Map; import com.sun.medialib.mlib.*; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class to perform dilation on a source image * for the specific case when the kernel is a 3x3 square shape - * with all 1s for binary and 0s for gray image and * the key position in the middle, * using mediaLib, of course :-). * * @see javax.media.jai.operator.DilateDescriptor * @see KernelJAI */ final class MlibDilate3SquareOpImage extends AreaOpImage { // Since medialib expects single banded data with IndexColorModel, we // should not expand the indexed data private static Map configHelper(Map configuration) { Map config; if (configuration == null) { config = new RenderingHints(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.FALSE); } else { config = configuration; // If the user has specified a hint for this, then we don't // want to change it, so change only if this hint is not // already specified if (!(config.containsKey(JAI.KEY_REPLACE_INDEX_COLOR_MODEL))) { RenderingHints hints = (RenderingHints)configuration; config = (RenderingHints)hints.clone(); config.put(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.FALSE); } } return config; } /** * Creates a MlibDilate3SquareOpImage given the image source * The image dimensions are * derived from the source image. The tile grid layout, * SampleModel, and ColorModel may optionally be specified by an * ImageLayout object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * or null. If null, a default cache will be used. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. */ public MlibDilate3SquareOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout ) { super(source, layout, configHelper(config), true, extender, 1, //kernel.getLeftPadding(), 1, //kernel.getRightPadding(), 1, //kernel.getTopPadding(), 1 //kernel.getBottomPadding() ); } /** * Performs dilation on a specified rectangle. The sources are * cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; Rectangle srcRect = mapDestRect(destRect, 0); int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest); MediaLibAccessor srcAccessor = new MediaLibAccessor(source,srcRect,formatTag,true); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest,destRect,formatTag,true); int numBands = getSampleModel().getNumBands(); mediaLibImage[] srcML = srcAccessor.getMediaLibImages(); mediaLibImage[] dstML = dstAccessor.getMediaLibImages(); for (int i = 0; i < dstML.length; i++) { switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: Image.Dilate8(dstML[i], srcML[i]); break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: Image.Dilate8_Fp(dstML[i], srcML[i]); break; default: String className = this.getClass().getName(); throw new RuntimeException(JaiI18N.getString("Generic2")); } } if (dstAccessor.isDataCopy()) { dstAccessor.copyDataToRaster(); } } // public static OpImage createTestImage(OpImageTester oit) { // float data[] = {0.05f,0.10f,0.05f, // 0.10f,0.40f,0.10f, // 0.05f,0.10f,0.05f}; // KernelJAI kJAI = new KernelJAI(3,3,1,1,data); // return new MlibDilate3SquareOpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource())); // } // public static void main (String args[]) { // String classname = "com.sun.media.jai.mlib.MlibDilate3SquareOpImage"; // OpImageTester.performDiagnostics(classname,args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibAbsoluteRIF.java0000644000175000017500000000313410203035544026321 0ustar mathieumathieu/* * $RCSfile: MlibAbsoluteRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:47 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "Absolute" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.AbsoluteDescriptor * @see MlibAbsoluteOpImage * */ public class MlibAbsoluteRIF implements RenderedImageFactory { /** Constructor. */ public MlibAbsoluteRIF() {} /** * Creates a new instance of MlibAbsoluteOpImage in * the rendered image mode. * * @param args The source image. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout)) { return null; } return new MlibAbsoluteOpImage(args.getRenderedSource(0), hints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibScaleBilinearOpImage.java0000644000175000017500000001376310350333605030154 0ustar mathieumathieu/* * $RCSfile: MlibScaleBilinearOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-12-15 18:35:46 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationBilinear; import javax.media.jai.OpImage; import java.util.Map; import com.sun.medialib.mlib.*; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class that scales an image using bilinear interpolation. * */ final class MlibScaleBilinearOpImage extends MlibScaleOpImage { /** * Constructs an MlibScaleBilinearOpImage. The image dimensions are copied * from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile * grid layout, SampleModel, and ColorModel, or null. * @param xScale the x scaling factor. * @param yScale the y scaling factor. * @param xTrans the x translation factor. * @param yTrans the y translation factor. * @param interp the Bilinear interpolation object. */ public MlibScaleBilinearOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, float xScale, float yScale, float xTrans, float yTrans, Interpolation interp) { super(source, extender, config, layout, xScale, yScale, xTrans, yTrans, interp, true); } /** * Scale a given rectangle by the specified scale and translation * factors. The sources are cobbled. * * @param sources an array of sources, guarantee to provide all * necessary source data for computing the rectangle. * @param dest a tile that contains the rectangle to be computed. * @param destRect the rectangle within this OpImage to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; Rectangle srcRect = source.getBounds(); int formatTag = MediaLibAccessor.findCompatibleTag(sources, dest); MediaLibAccessor srcAccessor = new MediaLibAccessor(source, srcRect, formatTag); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest, destRect, formatTag); // Get the scale factors in double precision double mlibScaleX = (double)scaleXRationalNum / (double)scaleXRationalDenom; double mlibScaleY = (double)scaleYRationalNum / (double)scaleYRationalDenom; // Translation to be specified to Medialib. Note that we have to // specify an additional translation since all images are 0 based // in Medialib. Note that scale and translation scalars have // rational representations. // Calculate intermediate values using rational arithmetic. long tempDenomX = scaleXRationalDenom * transXRationalDenom; long tempDenomY = scaleYRationalDenom * transYRationalDenom; long tempNumerX = (srcRect.x * scaleXRationalNum * transXRationalDenom) + (transXRationalNum * scaleXRationalDenom) - (destRect.x * tempDenomX); long tempNumerY = (srcRect.y * scaleYRationalNum * transYRationalDenom) + (transYRationalNum * scaleYRationalDenom) - (destRect.y * tempDenomY); double tx = (double)tempNumerX/(double)tempDenomX; double ty = (double)tempNumerY/(double)tempDenomY; mediaLibImage srcML[], dstML[]; switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: srcML = srcAccessor.getMediaLibImages(); dstML = dstAccessor.getMediaLibImages(); for (int i = 0 ; i < dstML.length; i++) { Image.ZoomTranslate(dstML[i], srcML[i], mlibScaleX, mlibScaleY, tx, ty, Constants.MLIB_BILINEAR, Constants.MLIB_EDGE_DST_NO_WRITE); MlibUtils.clampImage(dstML[i], getColorModel()); } break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: srcML = srcAccessor.getMediaLibImages(); dstML = dstAccessor.getMediaLibImages(); for (int i = 0 ; i < dstML.length; i++) { Image.ZoomTranslate_Fp(dstML[i], srcML[i], mlibScaleX, mlibScaleY, tx, ty, Constants.MLIB_BILINEAR, Constants.MLIB_EDGE_DST_NO_WRITE); } break; default: String className = this.getClass().getName(); throw new RuntimeException(JaiI18N.getString("Generic2")); } if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } // public static OpImage createTestImage(OpImageTester oit) { // Interpolation interp = new InterpolationBilinear(); // return new MlibScaleBilinearOpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource()), // 2, 2, 0, 0, interp); // } // // Calls a method on OpImage that uses introspection, to make this // // class, discover it's createTestImage() call, call it and then // // benchmark the performance of the created OpImage chain. // public static void main (String args[]) { // String classname = "com.sun.media.jai.mlib.MlibScaleBilinearOpImage"; // OpImageTester.performDiagnostics(classname, args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibOrConstOpImage.java0000644000175000017500000001012510203035544027031 0ustar mathieumathieu/* * $RCSfile: MlibOrConstOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:02 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PointOpImage; import java.util.Map; import com.sun.medialib.mlib.*; // import com.sun.media.jai.test.OpImageTester; /** * A mediaLib implementation of "OrConst" operator. * */ final class MlibOrConstOpImage extends PointOpImage { int[] constants; /** * Constructs an MlibOrConstOpImage. The image dimensions are copied * from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param layout an ImageLayout optionally containing the tile * grid layout, SampleModel, and ColorModel, or null. */ public MlibOrConstOpImage(RenderedImage source, Map config, ImageLayout layout, int[] constants) { super(source, layout, config, true); this.constants = MlibUtils.initConstants(constants, getSampleModel().getNumBands()); // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * Or the pixel values of a rectangle with a given constant. * The sources are cobbled. * * @param sources an array of sources, guarantee to provide all * necessary source data for computing the rectangle. * @param dest a tile that contains the rectangle to be computed. * @param destRect the rectangle within this OpImage to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; int formatTag = MediaLibAccessor.findCompatibleTag(sources, dest); // For PointOpImages, srcRect is the same as destRect MediaLibAccessor srcAccessor = new MediaLibAccessor(source, destRect, formatTag); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest, destRect, formatTag); switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: mediaLibImage[] srcML = srcAccessor.getMediaLibImages(); mediaLibImage[] dstML = dstAccessor.getMediaLibImages(); for (int i = 0 ; i < dstML.length; i++) { int mlconstants[] = dstAccessor.getIntParameters(i, constants); Image.ConstOr(dstML[i], srcML[i], mlconstants); } break; default: String className = this.getClass().getName(); throw new RuntimeException(className + JaiI18N.getString("Generic2")); } if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } // public static OpImage createTestImage(OpImageTester oit) { // int[] consts = {5, 5, 5}; // return new MlibOrConstOpImage(oit.getSource(), null, // new ImageLayout(oit.getSource()), // consts); // } // // Calls a method on OpImage that uses introspection, to make this // // class, discover it's createTestImage() call, call it and then // // benchmark the performance of the created OpImage chain. // public static void main (String args[]) { // String classname = "com.sun.media.jai.mlib.MlibOrConstOpImage"; // OpImageTester.performDiagnostics(classname,args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibBandSelectRIF.java0000644000175000017500000000373610203035544026557 0ustar mathieumathieu/* * $RCSfile: MlibBandSelectRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:51 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "BandSelect" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.BandSelectDescriptor * @see MlibBandSelectOpImage * */ public class MlibBandSelectRIF implements RenderedImageFactory { /** Constructor. */ public MlibBandSelectRIF() {} /** * Creates a new instance of MlibBandSelectOpImage in * the rendered image mode. * * @param args The source image and the band indices. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { // Get ImageLayout and TileCache from RenderingHints. ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(args, layout)) { return null; } int[] bandIndices = (int[])args.getObjectParameter(0); // If the band selection is not monotonically increasing // fall back to Java code as mediaLib does not support this. for(int i = 1; i < bandIndices.length; i++) { if(bandIndices[i] <= bandIndices[i-1]) { return null; } } return new MlibBandSelectOpImage(args.getRenderedSource(0), hints, layout, bandIndices); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibErode3SquareOpImage.java0000644000175000017500000001331410346146715027762 0ustar mathieumathieu/* * $RCSfile: MlibErode3SquareOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-12-09 00:20:29 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.JAI; import javax.media.jai.KernelJAI; import javax.media.jai.OpImage; import java.util.Map; import com.sun.medialib.mlib.*; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class to perform erosion on a source image * for the specific case when the kernel is a 3x3 square shape - * with all 1s for binary and 0s for gray image and * the key position in the middle, * using mediaLib, of course :-). * * @see javax.media.jai.operator.ErodeDescriptor * @see KernelJAI */ final class MlibErode3SquareOpImage extends AreaOpImage { // Since medialib expects single banded data with IndexColorModel, we // should not expand the indexed data private static Map configHelper(Map configuration) { Map config; if (configuration == null) { config = new RenderingHints(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.FALSE); } else { config = configuration; // If the user has specified a hint for this, then we don't // want to change it, so change only if this hint is not // already specified if (!(config.containsKey(JAI.KEY_REPLACE_INDEX_COLOR_MODEL))) { RenderingHints hints = (RenderingHints)configuration; config = (RenderingHints)hints.clone(); config.put(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.FALSE); } } return config; } /** * Creates a MlibErode3SquareOpImage given the image source * The image dimensions are * derived from the source image. The tile grid layout, * SampleModel, and ColorModel may optionally be specified by an * ImageLayout object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * or null. If null, a default cache will be used. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. */ public MlibErode3SquareOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout ) { super(source, layout, configHelper(config), true, extender, 1, //kernel.getLeftPadding(), 1, //kernel.getRightPadding(), 1, //kernel.getTopPadding(), 1 //kernel.getBottomPadding() ); } /** * Performs dilation on a specified rectangle. The sources are * cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; Rectangle srcRect = mapDestRect(destRect, 0); int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest); MediaLibAccessor srcAccessor = new MediaLibAccessor(source,srcRect,formatTag,true); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest,destRect,formatTag,true); int numBands = getSampleModel().getNumBands(); mediaLibImage[] srcML = srcAccessor.getMediaLibImages(); mediaLibImage[] dstML = dstAccessor.getMediaLibImages(); for (int i = 0; i < dstML.length; i++) { switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: Image.Erode8(dstML[i], srcML[i]); break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: Image.Erode8_Fp(dstML[i], srcML[i]); break; default: String className = this.getClass().getName(); throw new RuntimeException(JaiI18N.getString("Generic2")); } } if (dstAccessor.isDataCopy()) { dstAccessor.copyDataToRaster(); } } // public static OpImage createTestImage(OpImageTester oit) { // float data[] = {0.05f,0.10f,0.05f, // 0.10f,0.40f,0.10f, // 0.05f,0.10f,0.05f}; // KernelJAI kJAI = new KernelJAI(3,3,1,1,data); // return new MlibErode3SquareOpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource())); // } // public static void main (String args[]) { // String classname = "com.sun.media.jai.mlib.MlibErode3SquareOpImage"; // OpImageTester.performDiagnostics(classname,args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibFilteredSubsampleRIF.java0000644000175000017500000000572110203035544030161 0ustar mathieumathieu/* * $RCSfile: MlibFilteredSubsampleRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:57 $ * $State: Exp $ */package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.renderable.RenderedImageFactory; import java.awt.image.renderable.ParameterBlock; import java.util.Map; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.JAI; /** *

    Class implementing the RIF interface for the MlibFilteredSubsample * operator. An instance of this class should be registered with the * OperationRegistry with operation name "FilteredSubsample." * *

    This code is very similar to FilteredSubsampleRIF. */ public class MlibFilteredSubsampleRIF implements RenderedImageFactory { /** Default constructor (there is no input). */ public MlibFilteredSubsampleRIF() {} /** * Creates a new instance of SubsampleOpImage in the rendered layer. * This method satisfies the implementation of RIF. * * @param paramBlock The source image, the X and Y scale factors. * @param renderHints RenderingHints. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { BorderExtender extender = renderHints == null ? null : (BorderExtender)renderHints.get(JAI.KEY_BORDER_EXTENDER); ImageLayout layout = renderHints == null ? null : (ImageLayout)renderHints.get(JAI.KEY_IMAGE_LAYOUT); // Test for media lib compatibility if (!MediaLibAccessor.isMediaLibCompatible(paramBlock, layout) || !MediaLibAccessor.hasSameNumBands(paramBlock, layout)) { return null; } RenderedImage source = paramBlock.getRenderedSource(0); SampleModel sm = source.getSampleModel(); boolean isBilevel = (sm instanceof MultiPixelPackedSampleModel) && (sm.getSampleSize(0) == 1) && (sm.getDataType() == DataBuffer.TYPE_BYTE || sm.getDataType() == DataBuffer.TYPE_USHORT || sm.getDataType() == DataBuffer.TYPE_INT); if (isBilevel) { // Let Java code handle it, reformatting is slower return null; } int scaleX = paramBlock.getIntParameter(0); int scaleY = paramBlock.getIntParameter(1); float [] qsFilter = (float [])paramBlock.getObjectParameter(2); Interpolation interp = (Interpolation)paramBlock.getObjectParameter(3); return new MlibFilteredSubsampleOpImage(source, extender, (Map)renderHints, layout, scaleX, scaleY, qsFilter, interp); } // create } // MlibFilteredSubsampleRIF jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibXorOpImage.java0000644000175000017500000001254310203035544026220 0ustar mathieumathieu/* * $RCSfile: MlibXorOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:10 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.PixelInterleavedSampleModel; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PointOpImage; import java.util.Map; import com.sun.medialib.mlib.*; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage that performs the Xor operation on 2 images through mediaLib. * */ final class MlibXorOpImage extends PointOpImage { /** * Constructs an MlibXorOpImage. The image dimensions are copied * from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param layout an ImageLayout optionally containing the tile * grid layout, SampleModel, and ColorModel, or null. */ public MlibXorOpImage(RenderedImage source1, RenderedImage source2, Map config, ImageLayout layout) { super(source1, source2, layout, config, true); // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * Xor the pixel values of rectangles from the two sources. * The sources are cobbled. * * @param sources an array of sources, guarantee to provide all * necessary source data for computing the rectangle. * @param dest a tile that contains the rectangle to be computed. * @param destRect the rectangle within this OpImage to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { int formatTag = MediaLibAccessor.findCompatibleTag(sources, dest); MediaLibAccessor srcAccessor1 = new MediaLibAccessor(sources[0], destRect, formatTag); MediaLibAccessor srcAccessor2 = new MediaLibAccessor(sources[1], destRect, formatTag); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest, destRect, formatTag); switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: mediaLibImage[] srcML1 = srcAccessor1.getMediaLibImages(); mediaLibImage[] srcML2 = srcAccessor2.getMediaLibImages(); mediaLibImage[] dstML = dstAccessor.getMediaLibImages(); for (int i = 0 ; i < dstML.length; i++) { Image.Xor(dstML[i], srcML1[i], srcML2[i]); } break; default: String className = this.getClass().getName(); throw new RuntimeException(className + JaiI18N.getString("Generic2")); } if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } // public static void main (String args[]) { // System.out.println("MlibXorOpImage Test"); // ImageLayout layout; // OpImage src1, src2, dst; // Rectangle rect = new Rectangle(0, 0, 5, 5); // System.out.println("1. PixelInterleaved byte 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 800, 800, 0, 0, // 200, 200, DataBuffer.TYPE_BYTE, // 3, false); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new MlibXorOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("2. Banded byte 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 800, 800, 0, 0, // 200, 200, DataBuffer.TYPE_BYTE, // 3, true); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new MlibXorOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("3. PixelInterleaved int 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 512, 512, 0, 0, 200, 200, // DataBuffer.TYPE_INT, 3, false); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new MlibXorOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("4. Banded int 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 512, 512, 0, 0, // 200, 200, DataBuffer.TYPE_INT, // 3, true); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new MlibXorOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibDCTRIF.java0000644000175000017500000000324510203035544025160 0ustar mathieumathieu/* * $RCSfile: MlibDCTRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:53 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.DCTOpImage; import com.sun.media.jai.opimage.FCT; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "DCT" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.DCTDescriptor * @see com.sun.media.jai.opimage.DCTOpImage * * @since EA4 * */ public class MlibDCTRIF implements RenderedImageFactory { /** Constructor. */ public MlibDCTRIF() {} /** * Creates a new instance of DCTOpImage in * the rendered image mode. * * @param args The source image. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(new ParameterBlock())) { return null; } return new DCTOpImage(args.getRenderedSource(0), hints, layout, new FCTmediaLib(true, 2)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibAndRIF.java0000644000175000017500000000403610203035544025247 0ustar mathieumathieu/* * $RCSfile: MlibAndRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:50 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "And" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.AndDescriptor * @see MlibAndOpImage * */ public class MlibAndRIF implements RenderedImageFactory { // AndRIF { /** Constructor. */ public MlibAndRIF() {} /** * Creates a new instance of MlibAndOpImage in * the rendered image mode. * * @param args The source images. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout)) { return null; } /* Check whether dest has data type of float or double. */ if (layout != null) { SampleModel sm = layout.getSampleModel(null); if (sm != null) { int dtype = sm.getDataType(); if (dtype == DataBuffer.TYPE_FLOAT || dtype == DataBuffer.TYPE_DOUBLE) { return null; } } } return new MlibAndOpImage(args.getRenderedSource(0), args.getRenderedSource(1), hints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibSubtractConstRIF.java0000644000175000017500000000407710203035544027350 0ustar mathieumathieu/* * $RCSfile: MlibSubtractConstRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:07 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "SubtractConst" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.SubtractConstDescriptor * @see MlibAddConstOpImage * */ public class MlibSubtractConstRIF implements RenderedImageFactory { /** Constructor. */ public MlibSubtractConstRIF() {} /** * Creates a new instance of MlibAddConstOpImage in * the rendered image mode. By negating the constants, the result * of "SubtractConst" is obtained. * * @param args The source image and the constants. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout)) { return null; } /* Negate the constants vector. */ double[] constants = (double[])args.getObjectParameter(0); int length = constants.length; double[] negConstants = new double[length]; for (int i = 0; i < length; i++) { negConstants[i] = -constants[i]; } return new MlibAddConstOpImage(args.getRenderedSource(0), hints, layout, negConstants); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibTransposeRIF.java0000644000175000017500000000475510203035544026533 0ustar mathieumathieu/* * $RCSfile: MlibTransposeRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:08 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.EnumeratedParameter; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "Transpose" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.TransposeDescriptor * @see MlibTransposeOpImage * * @since EA3 */ public class MlibTransposeRIF implements RenderedImageFactory { /** Constructor. */ public MlibTransposeRIF() {} /** * Creates a new instance of MlibTransposeOpImage in * the rendered image mode. * * @param args The source image and the transpose type. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout)) { return null; } RenderedImage source = args.getRenderedSource(0); SampleModel sm = source.getSampleModel(); boolean isBilevel = (sm instanceof MultiPixelPackedSampleModel) && (sm.getSampleSize(0) == 1) && (sm.getDataType() == DataBuffer.TYPE_BYTE || sm.getDataType() == DataBuffer.TYPE_USHORT || sm.getDataType() == DataBuffer.TYPE_INT); if (isBilevel) { // Let Java code handle it, reformatting is slower return null; } EnumeratedParameter transposeType = (EnumeratedParameter)args.getObjectParameter(0); return new MlibTransposeOpImage(args.getRenderedSource(0), hints, layout, transposeType.getValue()); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibAffineNearestOpImage.java0000644000175000017500000001544710336211611030166 0ustar mathieumathieu/* * $RCSfile: MlibAffineNearestOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-11-14 22:45:29 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import java.awt.geom.AffineTransform; import javax.media.jai.AreaOpImage; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationNearest; import javax.media.jai.KernelJAI; import javax.media.jai.OpImage; import java.util.Map; import javax.media.jai.BorderExtender; import com.sun.medialib.mlib.*; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class to perform Nearest-Neighbour AffineTransform * on a source image. * */ public class MlibAffineNearestOpImage extends MlibAffineOpImage { /** * Creates a MlibAffineNearestOpImage given a ParameterBlock containing the * image source and the AffineTransform. The image dimensions are derived * from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout * object. * * @param source a RenderedImage. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param tr the AffineTransform. * @param interp the Interpolation to be used (Nearest-Neighbour) */ public MlibAffineNearestOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, AffineTransform tr, Interpolation interp, double[] backgroundValues) { super(source, layout, config, extender, tr, interp, backgroundValues); } /** * Performs nearest-neighbour affine transformation on a specified * rectangle. The sources are cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; Rectangle srcRect = source.getBounds(); int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest); MediaLibAccessor srcAccessor = new MediaLibAccessor(source,srcRect,formatTag); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest,destRect,formatTag); // // The AffineTransform needs to be readjusted as per the // location of the current source & destination rectangles // // Clone the global transform so as not to write to an instance // variable as this method may be called from different threads. double[] medialib_tr = (double[])this.medialib_tr.clone(); medialib_tr[2] = m_transform[0] * srcRect.x + m_transform[1] * srcRect.y + m_transform[2] - destRect.x; medialib_tr[5] = m_transform[3] * srcRect.x + m_transform[4] * srcRect.y + m_transform[5] - destRect.y; mediaLibImage srcML[], dstML[]; switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: srcML = srcAccessor.getMediaLibImages(); dstML = dstAccessor.getMediaLibImages(); if (setBackground) Image.Affine2(dstML[0], srcML[0], medialib_tr, Constants.MLIB_NEAREST, Constants.MLIB_EDGE_DST_NO_WRITE, intBackgroundValues); else Image.Affine(dstML[0], srcML[0], medialib_tr, Constants.MLIB_NEAREST, Constants.MLIB_EDGE_DST_NO_WRITE); break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: srcML = srcAccessor.getMediaLibImages(); dstML = dstAccessor.getMediaLibImages(); if (setBackground) Image.Affine2_Fp(dstML[0], srcML[0], medialib_tr, Constants.MLIB_NEAREST, Constants.MLIB_EDGE_DST_NO_WRITE, backgroundValues); else Image.Affine_Fp(dstML[0], srcML[0], medialib_tr, Constants.MLIB_NEAREST, Constants.MLIB_EDGE_DST_NO_WRITE); break; default: String className = this.getClass().getName(); throw new RuntimeException(JaiI18N.getString("Generic2")); } if (dstAccessor.isDataCopy()) { dstAccessor.copyDataToRaster(); } } // public static OpImage createTestImage(OpImageTester oit) { // Interpolation interp = new InterpolationNearest(); // AffineTransform tr = new AffineTransform(0.707107, // -0.707106, // 0.707106, // 0.707107, // 0.0, // 0.0); // return new MlibAffineNearestOpImage(oit.getSource(), null, // new ImageLayout(oit.getSource()), // tr, // interp); // } // // Calls a method on OpImage that uses introspection, to make this // // class, discover it's createTestImage() call, call it and then // // benchmark the performance of the created OpImage chain. // public static void main (String args[]) { // String classname = "com.sun.media.jai.mlib.MlibAffineNearestOpImage"; // OpImageTester.performDiagnostics(classname, args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibSubtractRIF.java0000644000175000017500000000326710203035544026341 0ustar mathieumathieu/* * $RCSfile: MlibSubtractRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:07 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "Subtract" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.SubtractDescriptor * @see MlibSubtractOpImage * */ public class MlibSubtractRIF implements RenderedImageFactory { /** Constructor. */ public MlibSubtractRIF() {} /** * Creates a new instance of MlibSubtractOpImage in * the rendered image mode. * * @param args The source images to be subtracted. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout)) { return null; } return new MlibSubtractOpImage(args.getRenderedSource(0), args.getRenderedSource(1), hints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibSubtractFromConstRIF.java0000644000175000017500000000342610203035544030171 0ustar mathieumathieu/* * $RCSfile: MlibSubtractFromConstRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:07 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "SubtractFromConst" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.SubtractFromConstDescriptor * @see MlibSubtractFromConstOpImage * */ public class MlibSubtractFromConstRIF implements RenderedImageFactory { /** Constructor. */ public MlibSubtractFromConstRIF() {} /** * Creates a new instance of MlibSubtractFromConstOpImage in * the rendered image mode. * * @param args The source image and the constants. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout)) { return null; } return new MlibSubtractFromConstOpImage(args.getRenderedSource(0), hints, layout, (double[])args.getObjectParameter(0)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibXorConstOpImage.java0000644000175000017500000001012510203035544027221 0ustar mathieumathieu/* * $RCSfile: MlibXorConstOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:09 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PointOpImage; import java.util.Map; import com.sun.medialib.mlib.*; // import com.sun.media.jai.test.OpImageTester; /** * A mediaLib implementation of "XorConst" operator. * */ final class MlibXorConstOpImage extends PointOpImage { int[] constants; /** * Constructs an MlibXorConstOpImage. The image dimensions are copied * from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param layout an ImageLayout optionally containing the tile * grid layout, SampleModel, and ColorModel, or null. */ public MlibXorConstOpImage(RenderedImage source, Map config, ImageLayout layout, int[] constants) { super(source, layout, config, true); this.constants = MlibUtils.initConstants(constants, getSampleModel().getNumBands()); // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * Xor the pixel values of a rectangle with a given constant. * The sources are cobbled. * * @param sources an array of sources, guarantee to provide all * necessary source data for computing the rectangle. * @param dest a tile that contains the rectangle to be computed. * @param destRect the rectangle within this OpImage to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; int formatTag = MediaLibAccessor.findCompatibleTag(sources, dest); // For PointOpImages, srcRect is the same as destRect MediaLibAccessor srcAccessor = new MediaLibAccessor(source, destRect, formatTag); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest, destRect, formatTag); switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: mediaLibImage[] srcML = srcAccessor.getMediaLibImages(); mediaLibImage[] dstML = dstAccessor.getMediaLibImages(); for (int i = 0 ; i < dstML.length; i++) { int mlconstants[] = dstAccessor.getIntParameters(i, constants); Image.ConstXor(dstML[i], srcML[i], mlconstants); } break; default: String className = this.getClass().getName(); throw new RuntimeException(JaiI18N.getString("Generic2")); } if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } // public static OpImage createTestImage(OpImageTester oit) { // int[] consts = {5, 5, 5}; // return new MlibXorConstOpImage(oit.getSource(), null, // new ImageLayout(oit.getSource()), // consts); // } // // Calls a method on OpImage that uses introspection, to make this // // class, discover it's createTestImage() call, call it and then // // benchmark the performance of the created OpImage chain. // public static void main (String args[]) { // String classname = "com.sun.media.jai.mlib.MlibXorConstOpImage"; // OpImageTester.performDiagnostics(classname,args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibShearRIF.java0000644000175000017500000001124610203035544025610 0ustar mathieumathieu/* * $RCSfile: MlibShearRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:06 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.EnumeratedParameter; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationBicubic2; import javax.media.jai.InterpolationBicubic; import javax.media.jai.InterpolationBilinear; import javax.media.jai.InterpolationNearest; import javax.media.jai.InterpolationTable; import java.util.Map; import javax.media.jai.BorderExtender; import javax.media.jai.operator.ShearDescriptor; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "Shear" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.ShearDescriptor * @see MlibAffineOpImage */ public class MlibShearRIF implements RenderedImageFactory { /** Constructor. */ public MlibShearRIF() {} /** * Creates a new instance of MlibAffineOpImage in * the rendered image mode. * * @param args The source image, the AffineTransform, * and the Interpolation. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); Interpolation interp = (Interpolation)args.getObjectParameter(4); RenderedImage source = args.getRenderedSource(0); if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout) || // Medialib cannot deal with source image having tiles with any // dimension greater than or equal to 32768 source.getTileWidth() >= 32768 || source.getTileHeight() >= 32768) { return null; } /* Get BorderExtender from hints if any. */ BorderExtender extender = RIFUtil.getBorderExtenderHint(hints); float shear_amt = args.getFloatParameter(0); EnumeratedParameter shear_dir = (EnumeratedParameter)args.getObjectParameter(1); float xTrans = args.getFloatParameter(2); float yTrans = args.getFloatParameter(3); double[] backgroundValues = (double[])args.getObjectParameter(5); // Create the affine transform AffineTransform tr = new AffineTransform(); if (shear_dir.equals(ShearDescriptor.SHEAR_HORIZONTAL)) { // SHEAR_HORIZONTAL tr.setTransform(1.0, 0.0, shear_amt, 1.0, xTrans, 0.0); } else { // SHEAR_VERTICAL tr.setTransform(1.0, shear_amt, 0.0, 1.0, 0.0, yTrans); } if (interp instanceof InterpolationNearest) { return new MlibAffineNearestOpImage(source, extender, hints, layout, tr, interp, backgroundValues); } else if (interp instanceof InterpolationBilinear) { return new MlibAffineBilinearOpImage(source, extender, hints, layout, tr, interp, backgroundValues); } else if (interp instanceof InterpolationBicubic || interp instanceof InterpolationBicubic2) { return new MlibAffineBicubicOpImage(source, extender, hints, layout, tr, interp, backgroundValues); } else if (interp instanceof InterpolationTable) { return new MlibAffineTableOpImage(source, extender, hints, layout, tr, interp, backgroundValues); } else { return null; } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibClampRIF.java0000644000175000017500000000335510203035544025604 0ustar mathieumathieu/* * $RCSfile: MlibClampRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:51 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "Clamp" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.ClampDescriptor * @see MlibClampOpImage * * @since 1.0 * */ public class MlibClampRIF implements RenderedImageFactory { /** Constructor. */ public MlibClampRIF() {} /** * Creates a new instance of MlibClampOpImage in * the rendered image mode. * * @param args The source image. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout)) { return null; } return new MlibClampOpImage(args.getRenderedSource(0), hints, layout, (double[])args.getObjectParameter(0), (double[])args.getObjectParameter(1)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibErrorDiffusionRIF.java0000644000175000017500000000641510203035544027510 0ustar mathieumathieu/* * $RCSfile: MlibErrorDiffusionRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:56 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; import javax.media.jai.LookupTableJAI; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; import com.sun.media.jai.util.ImageUtil; /** * A RIF supporting the "ErrorDiffusion" operation in the * rendered image mode using mediaLib. * * @see javax.media.jai.operator.ErrorDiffusionDescriptor * @see MlibErrorDiffusionOpImage */ public class MlibErrorDiffusionRIF implements RenderedImageFactory { /** Constructor. */ public MlibErrorDiffusionRIF() {} /** * Creates a new instance of MlibErrorDiffusionOpImage in * the rendered image mode. * * @param args The source image and lookup table. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { // Get source and parameters. RenderedImage source = args.getRenderedSource(0); LookupTableJAI colorMap = (LookupTableJAI)args.getObjectParameter(0); KernelJAI errorKernel = (KernelJAI)args.getObjectParameter(1); // Check colorMap compatibility. if(colorMap.getNumBands() != 1 && colorMap.getNumBands() != 3) { // 1 or 3 band colorMaps only. return null; } else if(colorMap.getDataType() != DataBuffer.TYPE_BYTE) { // byte colorMaps only return null; } // Check source compatibility. SampleModel sourceSM = source.getSampleModel(); if(sourceSM.getDataType() != DataBuffer.TYPE_BYTE) { // byte source images only return null; } else if(sourceSM.getNumBands() != colorMap.getNumBands()) { // band counts must match return null; } // Get ImageLayout from RenderingHints if any. ImageLayout layoutHint = RIFUtil.getImageLayoutHint(hints); // Calculate the final ImageLayout. ImageLayout layout = MlibErrorDiffusionOpImage.layoutHelper(layoutHint, source, colorMap); // Check for source and destination compatibility. The ColorModel // is suppressed in the second test because it will be an // IndexColorModel which would cause the test to fail. SampleModel destSM = layout.getSampleModel(null); if (!MediaLibAccessor.isMediaLibCompatible(args) || (!MediaLibAccessor.isMediaLibCompatible(destSM, null) && !ImageUtil.isBinary(destSM))) { return null; } return new MlibErrorDiffusionOpImage(source, hints, layout, colorMap, errorKernel); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/JaiI18N.java0000644000175000017500000000074310203035544024504 0ustar mathieumathieu/* * $RCSfile: JaiI18N.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:47 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import com.sun.media.jai.util.PropertyUtil; class JaiI18N { static String packageName = "com.sun.media.jai.mlib"; public static String getString(String key) { return PropertyUtil.getString(packageName, key); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibMeanOpImage.java0000644000175000017500000000651010203035544026325 0ustar mathieumathieu/* * $RCSfile: MlibMeanOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:59 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.DataBuffer; import java.awt.image.PixelInterleavedSampleModel; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PlanarImage; import javax.media.jai.ROI; import javax.media.jai.StatisticsOpImage; import com.sun.media.jai.opimage.MeanOpImage; import com.sun.medialib.mlib.*; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage that performs the Mean operation on an image through mediaLib. * */ final class MlibMeanOpImage extends MeanOpImage { /** * Constructs an MlibMeanOpImage. The image dimensions are copied * from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout object. * * @param source The source image. */ public MlibMeanOpImage(RenderedImage source, ROI roi, int xStart, int yStart, int xPeriod, int yPeriod) { super(source, roi, xStart, yStart, xPeriod, yPeriod); } protected void accumulateStatistics(String name, Raster source, Object stats) { // Get image and band count. PlanarImage sourceImage = getSourceImage(0); int numBands = sourceImage.getSampleModel().getNumBands(); // Determine the format tag and create an accessor. int formatTag = MediaLibAccessor.findCompatibleTag(null, source); MediaLibAccessor srcAccessor = new MediaLibAccessor(source, source.getBounds(), formatTag); // Get the mediaLib image. mediaLibImage[] srcML = srcAccessor.getMediaLibImages(); // NOTE: currently srcML.length always equals 1 double[] dmean = new double[numBands]; switch (srcAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: for (int i = 0 ; i < srcML.length; i++) { Image.Mean(dmean, srcML[i]); } break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: for (int i = 0 ; i < srcML.length; i++) { Image.Mean_Fp(dmean, srcML[i]); } break; default: throw new RuntimeException(JaiI18N.getString("Generic2")); } dmean = srcAccessor.getDoubleParameters(0, dmean); // Update the mean. double[] mean = (double[])stats; double weight = (double)(source.getWidth()*source.getHeight())/ (double)(width*height); for ( int i = 0; i < numBands; i++ ) { mean[i] += dmean[i]*weight; } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibScaleBicubicOpImage.java0000644000175000017500000001445510350333605027766 0ustar mathieumathieu/* * $RCSfile: MlibScaleBicubicOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-12-15 18:35:46 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationBicubic; import javax.media.jai.InterpolationBicubic2; import javax.media.jai.OpImage; import java.util.Map; import com.sun.medialib.mlib.*; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class that scales an image using bicubic interpolation. * */ final class MlibScaleBicubicOpImage extends MlibScaleOpImage { /** * Constructs an MlibScaleBicubicOpImage. The image dimensions are copied * from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile * grid layout, SampleModel, and ColorModel, or null. * @param xScale the x scaling factor. * @param yScale the y scaling factor. * @param xTrans the x translation factor. * @param yTrans the y translation factor. * @param interp the Bicubic interpolation object. */ public MlibScaleBicubicOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, float xScale, float yScale, float xTrans, float yTrans, Interpolation interp) { super(source, extender, config, layout, xScale, yScale, xTrans, yTrans, interp, true); } /** * Scale the given rectangle by the specified scale and translation * factors. The sources are cobbled. * * @param sources an array of sources, guarantee to provide all * necessary source data for computing the rectangle. * @param dest a tile that contains the rectangle to be computed. * @param destRect the rectangle within this OpImage to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // The default interpolation for this class is Bicubic int mlibInterpType = Constants.MLIB_BICUBIC; // Unless a Bicubic2 object is passed in as the interpolation if (interp instanceof InterpolationBicubic2) { mlibInterpType = Constants.MLIB_BICUBIC2; } Raster source = sources[0]; Rectangle srcRect = source.getBounds(); int formatTag = MediaLibAccessor.findCompatibleTag(sources, dest); MediaLibAccessor srcAccessor = new MediaLibAccessor(source, srcRect, formatTag); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest, destRect, formatTag); // Get the scale factors in double precision double mlibScaleX = (double)scaleXRationalNum / (double)scaleXRationalDenom; double mlibScaleY = (double)scaleYRationalNum / (double)scaleYRationalDenom; // Translation to be specified to Medialib. Note that we have to // specify an additional translation since all images are 0 based // in Medialib. Note that scale and translation scalars have // rational representations. // Calculate intermediate values using rational arithmetic. long tempDenomX = scaleXRationalDenom * transXRationalDenom; long tempDenomY = scaleYRationalDenom * transYRationalDenom; long tempNumerX = (srcRect.x * scaleXRationalNum * transXRationalDenom) + (transXRationalNum * scaleXRationalDenom) - (destRect.x * tempDenomX); long tempNumerY = (srcRect.y * scaleYRationalNum * transYRationalDenom) + (transYRationalNum * scaleYRationalDenom) - (destRect.y * tempDenomY); double tx = (double)tempNumerX/(double)tempDenomX; double ty = (double)tempNumerY/(double)tempDenomY; mediaLibImage srcML[], dstML[]; switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: srcML = srcAccessor.getMediaLibImages(); dstML = dstAccessor.getMediaLibImages(); for (int i = 0 ; i < dstML.length; i++) { Image.ZoomTranslate(dstML[i], srcML[i], mlibScaleX, mlibScaleY, tx, ty, mlibInterpType, Constants.MLIB_EDGE_DST_NO_WRITE); MlibUtils.clampImage(dstML[i], getColorModel()); } break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: srcML = srcAccessor.getMediaLibImages(); dstML = dstAccessor.getMediaLibImages(); for (int i = 0 ; i < dstML.length; i++) { Image.ZoomTranslate_Fp(dstML[i], srcML[i], mlibScaleX, mlibScaleY, tx, ty, mlibInterpType, Constants.MLIB_EDGE_DST_NO_WRITE); } break; default: String className = this.getClass().getName(); throw new RuntimeException(JaiI18N.getString("Generic2")); } if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } // public static OpImage createTestImage(OpImageTester oit) { // Interpolation interp = new InterpolationBicubic(8); // return new MlibScaleBicubicOpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource()), // 2, 2, 0, 0, interp); // } // // Calls a method on OpImage that uses introspection, to make this // // class, discover it's createTestImage() call, call it and then // // benchmark the performance of the created OpImage chain. // public static void main (String args[]) { // String classname = "com.sun.media.jai.mlib.MlibScaleBicubicOpImage"; // OpImageTester.performDiagnostics(classname, args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibMedianFilterOpImage.java0000644000175000017500000002130610203035544030010 0ustar mathieumathieu/* * $RCSfile: MlibMedianFilterOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:59 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.RasterAccessor; import java.util.Map; import javax.media.jai.operator.MedianFilterDescriptor; import javax.media.jai.operator.MedianFilterShape; import com.sun.medialib.mlib.*; /** * An OpImage class that subclasses will use to perform * MedianFiltering with specific masks. * * */ final class MlibMedianFilterOpImage extends AreaOpImage { protected int maskType; protected int maskSize; /** * Creates a MlibMedianFilterOpImage given an image source, an * optional BorderExtender, a maskType and maskSize. The image * dimensions are derived the source image. The tile grid layout, * SampleModel, and ColorModel may optionally be specified by an * ImageLayout object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param maskType the filter mask type. * @param maskSize the filter mask size. */ public MlibMedianFilterOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, MedianFilterShape maskType, int maskSize) { super(source, layout, config, true, extender, (maskSize-1)/2, (maskSize-1)/2, (maskSize/2), (maskSize/2)); this.maskType = mapToMlibMaskType(maskType); this.maskSize = maskSize; } private static int mapToMlibMaskType(MedianFilterShape maskType) { if(maskType.equals(MedianFilterDescriptor.MEDIAN_MASK_SQUARE)) { return Constants.MLIB_MEDIAN_MASK_RECT; } else if(maskType.equals(MedianFilterDescriptor.MEDIAN_MASK_PLUS)) { return Constants.MLIB_MEDIAN_MASK_PLUS; } else if(maskType.equals(MedianFilterDescriptor.MEDIAN_MASK_X)) { return Constants.MLIB_MEDIAN_MASK_X; } else if(maskType.equals(MedianFilterDescriptor.MEDIAN_MASK_SQUARE_SEPARABLE)) { return Constants.MLIB_MEDIAN_MASK_RECT_SEPARABLE; } throw new RuntimeException(JaiI18N.getString("MedianFilterOpImage0")); } /** * Performs median filtering on a specified rectangle. The sources are * cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; Rectangle srcRect = mapDestRect(destRect, 0); int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest); MediaLibAccessor srcAccessor = new MediaLibAccessor(source,srcRect,formatTag); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest,destRect,formatTag); int numBands = getSampleModel().getNumBands(); int cmask = (1 << numBands) -1; mediaLibImage[] srcML = srcAccessor.getMediaLibImages(); mediaLibImage[] dstML = dstAccessor.getMediaLibImages(); for (int i = 0; i < dstML.length; i++) { switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: if (maskSize == 3) { // Call appropriate Medialib accelerated function Image.MedianFilter3x3(dstML[i], srcML[i], maskType, cmask, Constants.MLIB_EDGE_DST_NO_WRITE); } else if (maskSize == 5) { // Call appropriate Medialib accelerated function Image.MedianFilter5x5(dstML[i], srcML[i], maskType, cmask, Constants.MLIB_EDGE_DST_NO_WRITE); } else if (maskSize == 7) { // Call appropriate Medialib accelerated function Image.MedianFilter7x7(dstML[i], srcML[i], maskType, cmask, Constants.MLIB_EDGE_DST_NO_WRITE); } else { // Call the generic version Image.MedianFilterMxN(dstML[i], srcML[i], maskSize, maskSize, maskType, cmask, Constants.MLIB_EDGE_DST_NO_WRITE); } break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: if (maskSize == 3) { // Call appropriate Medialib accelerated function Image.MedianFilter3x3_Fp(dstML[i], srcML[i], maskType, cmask, Constants.MLIB_EDGE_DST_NO_WRITE); } else if (maskSize == 5) { // Call appropriate Medialib accelerated function Image.MedianFilter5x5_Fp(dstML[i], srcML[i], maskType, cmask, Constants.MLIB_EDGE_DST_NO_WRITE); } else if (maskSize == 7) { // Call appropriate Medialib accelerated function Image.MedianFilter7x7_Fp(dstML[i], srcML[i], maskType, cmask, Constants.MLIB_EDGE_DST_NO_WRITE); } else { // Call the generic version Image.MedianFilterMxN_Fp(dstML[i], srcML[i], maskSize, maskSize, maskType, cmask, Constants.MLIB_EDGE_DST_NO_WRITE); } break; default: String className = this.getClass().getName(); throw new RuntimeException(JaiI18N.getString("Generic2")); } } if (dstAccessor.isDataCopy()) { dstAccessor.copyDataToRaster(); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibMinOpImage.java0000644000175000017500000000524410203035544026173 0ustar mathieumathieu/* * $RCSfile: MlibMinOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:00 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.PointOpImage; import java.util.Map; import com.sun.medialib.mlib.*; /** * An OpImage implementing the "Min" operation * using MediaLib. * * @see javax.media.jai.operator.MinDescriptor * @see MlibMinRIF * * @since 1.0 * */ final class MlibMinOpImage extends PointOpImage { /** Constructor. */ public MlibMinOpImage(RenderedImage source1, RenderedImage source2, Map config, ImageLayout layout) { super(source1, source2, layout, config, true); // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * Performs the "Min" operation on a rectangular region of * the same. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { int formatTag = MediaLibAccessor.findCompatibleTag(sources, dest); MediaLibAccessor srcMA1 = new MediaLibAccessor(sources[0], destRect, formatTag); MediaLibAccessor srcMA2 = new MediaLibAccessor(sources[1], destRect, formatTag); MediaLibAccessor dstMA = new MediaLibAccessor(dest, destRect, formatTag); mediaLibImage[] srcMLI1 = srcMA1.getMediaLibImages(); mediaLibImage[] srcMLI2 = srcMA2.getMediaLibImages(); mediaLibImage[] dstMLI = dstMA.getMediaLibImages(); switch (dstMA.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: for (int i = 0 ; i < dstMLI.length; i++) { Image.Min(dstMLI[i], srcMLI1[i], srcMLI2[i]); } break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: for (int i = 0 ; i < dstMLI.length; i++) { Image.Min_Fp(dstMLI[i], srcMLI1[i], srcMLI2[i]); } break; default: throw new RuntimeException(JaiI18N.getString("Generic2")); } if (dstMA.isDataCopy()) { dstMA.clampDataArrays(); dstMA.copyDataToRaster(); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibMultiplyConstRIF.java0000644000175000017500000000336210203035544027374 0ustar mathieumathieu/* * $RCSfile: MlibMultiplyConstRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:01 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "MultiplyConst" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.MultiplyConstDescriptor * @see MlibMultiplyConstOpImage * */ public class MlibMultiplyConstRIF implements RenderedImageFactory { /** Constructor. */ public MlibMultiplyConstRIF() {} /** * Creates a new instance of MlibMultiplyConstOpImage in * the rendered image mode. * * @param args The source image and the constants. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout)) { return null; } return new MlibMultiplyConstOpImage(args.getRenderedSource(0), hints, layout, (double[])args.getObjectParameter(0)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibMaxFilterRIF.java0000644000175000017500000000437710203035544026450 0ustar mathieumathieu/* * $RCSfile: MlibMaxFilterRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:59 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; import java.util.Map; import javax.media.jai.operator.MaxFilterDescriptor; import javax.media.jai.operator.MaxFilterShape; import com.sun.media.jai.opimage.RIFUtil; /** * Creates a MlibMaxFilterOpImage subclass for the given input * mask type * @see MlibMaxFilterOpImage */ public class MlibMaxFilterRIF implements RenderedImageFactory { /** Constructor. */ public MlibMaxFilterRIF() {} /** * Create a new instance of MlibMaxFilterOpImage in the rendered layer. * This method satisfies the implementation of RIF. * * @param paramBlock The source image and the convolution kernel. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); if (!MediaLibAccessor.isMediaLibCompatible(paramBlock, layout) || !MediaLibAccessor.hasSameNumBands(paramBlock, layout)) { return null; } // Get BorderExtender from renderHints if any. BorderExtender extender = RIFUtil.getBorderExtenderHint(renderHints); MaxFilterShape maskType = (MaxFilterShape)paramBlock.getObjectParameter(0); int maskSize = paramBlock.getIntParameter(1); RenderedImage ri = paramBlock.getRenderedSource(0); if(maskType.equals(MaxFilterDescriptor.MAX_MASK_SQUARE) && (maskSize==3 || maskSize==5 || maskSize == 7) && ri.getSampleModel().getNumBands() == 1){ return new MlibMaxFilterOpImage(ri, extender, renderHints, layout, maskType, maskSize); }else{ return null; } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibConvolveRIF.java0000644000175000017500000000551110300212137026330 0ustar mathieumathieu/* * $RCSfile: MlibConvolveRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-08-15 22:17:03 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "Convolve" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.ConvolveDescriptor * @see MlibConvolveOpImage */ public class MlibConvolveRIF implements RenderedImageFactory { /** Constructor. */ public MlibConvolveRIF() {} /** * Creates a new instance of MlibConvolveOpImage in * the rendered image mode. * * @param args The source image and convolution kernel. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { // Get ImageLayout and TileCache from RenderingHints. ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout)) { return null; } // Get BorderExtender from hints if any. BorderExtender extender = RIFUtil.getBorderExtenderHint(hints); RenderedImage source = args.getRenderedSource(0); KernelJAI unRotatedKernel = (KernelJAI)args.getObjectParameter(0); KernelJAI kJAI = unRotatedKernel.getRotatedKernel(); int kWidth = kJAI.getWidth(); int kHeight = kJAI.getHeight(); // mediaLib does not handle kernels with either dimension < 2. if (kWidth < 2 || kHeight < 2) { return null; } if (kJAI.isSeparable() && (kWidth == kHeight) && (kWidth == 3 || kWidth == 5 || kWidth == 7)) { return new MlibSeparableConvolveOpImage(source, extender, hints, layout, kJAI); } else if ((kWidth == kHeight) && (kWidth == 2 || kWidth == 3 || kWidth == 4 || kWidth == 5 || kWidth == 7)) { return new MlibConvolveNxNOpImage(source, extender, hints, layout, kJAI); } else { return new MlibConvolveOpImage(source, extender, hints, layout, kJAI); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibSobelOpImage.java0000644000175000017500000001226510203035544026515 0ustar mathieumathieu/* * $RCSfile: MlibSobelOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:06 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; import javax.media.jai.OpImage; import java.util.Map; import com.sun.medialib.mlib.*; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class to perform Sobel (Gradient) on a source image. * * * * @see KernelJAI */ final class MlibSobelOpImage extends AreaOpImage { /** * Creates a MlibSobelOpImage given the image source. * The image dimensions are derived from the source image. * The tile grid layout, SampleModel, and ColorModel may * optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. */ public MlibSobelOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, KernelJAI kernel) { // // Both the orthogonal pair of kernels have the same width & height // Hence either of them can be used here // super(source, layout, config, true, extender, kernel.getLeftPadding(), kernel.getRightPadding(), kernel.getTopPadding(), kernel.getBottomPadding()); } /** * Performs Sobel on a specified rectangle. The sources are cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; Rectangle srcRect = mapDestRect(destRect, 0); int formatTag = MediaLibAccessor.findCompatibleTag(sources, dest); MediaLibAccessor srcAccessor = new MediaLibAccessor(source, srcRect, formatTag); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest, destRect, formatTag); int numBands = getSampleModel().getNumBands(); mediaLibImage[] srcML = srcAccessor.getMediaLibImages(); mediaLibImage[] dstML = dstAccessor.getMediaLibImages(); for (int i = 0; i < dstML.length; i++) { switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: Image.Sobel(dstML[i], srcML[i], ((1 << numBands)-1) , Constants.MLIB_EDGE_DST_NO_WRITE); break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: Image.Sobel_Fp(dstML[i], srcML[i], ((1 << numBands)-1) , Constants.MLIB_EDGE_DST_NO_WRITE); break; default: String className = this.getClass().getName(); throw new RuntimeException(JaiI18N.getString("Generic2")); } } if (dstAccessor.isDataCopy()) { dstAccessor.copyDataToRaster(); } } // public static OpImage createTestImage(OpImageTester oit) { // float data_h[] = {-1.0f, -2.0f, -1.0f, // 0.0f, 0.0f, 0.0f, // 1.0f, 2.0f, 1.0f}; // float data_v[] = {-1.0f, 0.0f, 1.0f, // -2.0f, 0.0f, 2.0f, // -1.0f, 0.0f, 1.0f}; // KernelJAI kern_h = new KernelJAI(3,3,data_h); // KernelJAI kern_v = new KernelJAI(3,3,data_v); // return new MlibSobelOpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource()), // kern_h); // } // public static void main (String args[]) { // String classname = "com.sun.media.jai.mlib.MlibSobelOpImage"; // OpImageTester.performDiagnostics(classname,args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibSubsampleAverageRIF.java0000644000175000017500000000424610203035544027776 0ustar mathieumathieu/* * $RCSfile: MlibSubsampleAverageRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:06 $ * $State: Exp $ */package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderedImageFactory; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.ImageLayout; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "SubsampleAverage" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.SubsampleaverageDescriptor * @see MlibSubsampleAverageOpImage */ public class MlibSubsampleAverageRIF implements RenderedImageFactory { /** Constructor. */ public MlibSubsampleAverageRIF() {} /** * Creates a new instance of MlibSubsampleAverageOpImage in * the rendered image mode. * * @param args The source image, scale factors, * and the Interpolation. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { // Get the scale factors. double scaleX = args.getDoubleParameter(0); double scaleY = args.getDoubleParameter(1); // If unity scaling return the source directly. if(scaleX == 1.0 && scaleY == 1.0) { return args.getRenderedSource(0); } // Get the layout. ImageLayout layout = RIFUtil.getImageLayoutHint(hints); // Check mediaLib compatibility. if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout)) { // Return null to indicate to fallback to next RIF. return null; } // Create and return the OpImage. return new MlibSubsampleAverageOpImage(args.getRenderedSource(0), layout, hints, scaleX, scaleY); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibMedianFilterRIF.java0000644000175000017500000000440410203035544027107 0ustar mathieumathieu/* * $RCSfile: MlibMedianFilterRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:00 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; import java.util.Map; import javax.media.jai.operator.MedianFilterDescriptor; import javax.media.jai.operator.MedianFilterShape; import com.sun.media.jai.opimage.RIFUtil; /** * Creates a MlibMedianFilterOpImage subclass for the given input * mask type * @see MlibMedianFilterOpImage */ public class MlibMedianFilterRIF implements RenderedImageFactory { /** Constructor. */ public MlibMedianFilterRIF() {} /** * Create a new instance of MlibMedianFilterOpImage in the rendered layer. * This method satisfies the implementation of RIF. * * @param paramBlock The source image and the convolution kernel. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get ImageLayout from renderHints if any. ImageLayout layout = RIFUtil.getImageLayoutHint(renderHints); if (!MediaLibAccessor.isMediaLibCompatible(paramBlock, layout) || !MediaLibAccessor.hasSameNumBands(paramBlock, layout)) { return null; } // Get BorderExtender from renderHints if any. BorderExtender extender = RIFUtil.getBorderExtenderHint(renderHints); MedianFilterShape maskType = (MedianFilterShape)paramBlock.getObjectParameter(0); int maskSize = paramBlock.getIntParameter(1); RenderedImage ri = paramBlock.getRenderedSource(0); return new MlibMedianFilterOpImage(ri, extender, renderHints, layout, maskType, maskSize); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibXorRIF.java0000644000175000017500000000403110203035544025310 0ustar mathieumathieu/* * $RCSfile: MlibXorRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:10 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "Xor" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.XorDescriptor * @see MlibXorOpImage * */ public class MlibXorRIF implements RenderedImageFactory { /** Constructor. */ public MlibXorRIF() {} /** * Creates a new instance of MlibXorOpImage in * the rendered image mode. * * @param args The source images. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout)) { return null; } /* Check whether dest has data type of float or double. */ if (layout != null) { SampleModel sm = layout.getSampleModel(null); if (sm != null) { int dtype = sm.getDataType(); if (dtype == DataBuffer.TYPE_FLOAT || dtype == DataBuffer.TYPE_DOUBLE) { return null; } } } return new MlibXorOpImage(args.getRenderedSource(0), args.getRenderedSource(1), hints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibWarpGridTableOpImage.java0000644000175000017500000004771110350333605030146 0ustar mathieumathieu/* * $RCSfile: MlibWarpGridTableOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-12-15 18:35:48 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationTable; import java.util.Map; import javax.media.jai.RasterFactory; import javax.media.jai.WarpOpImage; import javax.media.jai.WarpGrid; import com.sun.medialib.mlib.*; import com.sun.media.jai.util.ImageUtil; /** * An OpImage implementing the Grid "Warp" operation * using mediaLib for the case of InterpolationTable. * *

    With warp operations, there is no forward mapping (from source to * destination). JAI images are tiled, while mediaLib does not handle * tiles and consider each tile an individual image. For each tile in * destination, in order not to cobble the entire source image, the * computeTile method in this class attemps to do a backward * mapping on the tile region using the pixels along the perimeter of the * rectangular region. The hope is that the mapped source rectangle * should include all source pixels needed for this particular destination * tile. However, with certain unusual warp points, an inner destination * pixel may be mapped outside of the mapped perimeter pixels. In this * case, this destination pixel is not filled, and left black. * * @see javax.media.jai.operator.WarpDescriptor * @see javax.media.jai.InterpolationTable * @see MlibWarpRIF * * @since 1.1 * */ final class MlibWarpGridTableOpImage extends WarpOpImage { /** X grid settings. */ private int xStart; private int xStep; private int xNumCells; private int xEnd; /** Y grid settings. */ private int yStart; private int yStep; private int yNumCells; private int yEnd; /** Grid points. */ private float[] xWarpPos; private float[] yWarpPos; /** * converting from interpolation to mlib table for * integral, float and double data type */ private mediaLibImageInterpTable mlibInterpTableI; private mediaLibImageInterpTable mlibInterpTableF; private mediaLibImageInterpTable mlibInterpTableD; /** * Constructs a MlibWarpGridTableOpImage. * * @param source The source image. * @param layout The destination image layout. * @param warp An object defining the warp algorithm. * @param interp An object describing the interpolation method. */ public MlibWarpGridTableOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, WarpGrid warp, Interpolation interp, double[] backgroundValues) { super(source, layout, config, true, extender, interp, warp, backgroundValues); mlibInterpTableI = null; mlibInterpTableF = null; mlibInterpTableD = null; xStart = warp.getXStart(); xStep = warp.getXStep(); xNumCells = warp.getXNumCells(); xEnd = xStart + xStep * xNumCells; yStart = warp.getYStart(); yStep = warp.getYStep(); yNumCells = warp.getYNumCells(); yEnd = yStart + yStep * yNumCells; xWarpPos = warp.getXWarpPos(); yWarpPos = warp.getYWarpPos(); } /** * Returns the minimum bounding box of the region of the specified * source to which a particular Rectangle of the * destination will be mapped. * * @param destRect the Rectangle in destination coordinates. * @param sourceIndex the index of the source image. * * @return a Rectangle indicating the source bounding box, * or null if the bounding box is unknown. * * @throws IllegalArgumentException if sourceIndex is * negative or greater than the index of the last source. * @throws IllegalArgumentException if destRect is * null. */ protected Rectangle backwardMapRect(Rectangle destRect, int sourceIndex) { // Superclass method will throw documented exceptions if needed. Rectangle wrect = super.backwardMapRect(destRect, sourceIndex); // "Dilate" the backwarp mapped rectangle to account for // the lack of being able to know the floating point result of // mapDestRect() and to mimic what is done in AffineOpImage. // See bug 4518223 for more information. wrect.setBounds(wrect.x - 1, wrect.y - 1, wrect.width + 2, wrect.height + 2); return wrect; } /** * Computes a tile. A new WritableRaster is created to * represent the requested tile. Its width and height equals to this * image's tile width and tile height respectively. If the requested * tile lies outside of the image's boundary, the created raster is * returned with all of its pixels set to 0. * *

    This method overrides the method in WarpOpImage * and performs source cobbling when necessary. MediaLib is used to * calculate the actual warping. * * @param tileX The X index of the tile. * @param tileY The Y index of the tile. * * @return The tile as a Raster. */ public Raster computeTile(int tileX, int tileY) { /* The origin of the tile. */ Point org = new Point(tileXToX(tileX), tileYToY(tileY)); /* Create a new WritableRaster to represent this tile. */ WritableRaster dest = createWritableRaster(sampleModel, org); /* Find the intersection between this tile and the writable bounds. */ Rectangle rect0 = new Rectangle(org.x, org.y, tileWidth, tileHeight); Rectangle destRect = rect0.intersection(computableBounds); Rectangle destRect1 = rect0.intersection(getBounds()); if (destRect.isEmpty()) { if (setBackground) { ImageUtil.fillBackground(dest, destRect1, backgroundValues); } return dest; // tile completely outside of writable bounds } if (!destRect1.equals(destRect)) { // beware that destRect1 contains destRect ImageUtil.fillBordersWithBackgroundValues(destRect1, destRect, dest, backgroundValues); } Raster[] sources = new Raster[1]; Rectangle srcBounds = getSourceImage(0).getBounds(); int x0 = destRect.x; // first x point int x1 = x0 + destRect.width - 1; // last x point int y0 = destRect.y; // first y point int y1 = y0 + destRect.height - 1; // last y point if (x0 >= xEnd || x1 < xStart || y0 >= yEnd || y1 < yStart) { /* Tile is completely outside of warp grid; do copy. */ Rectangle rect = srcBounds.intersection(destRect); if (!rect.isEmpty()) { sources[0] = getSourceImage(0).getData(rect); copyRect(sources, dest, rect); // Recycle the source tile if(getSourceImage(0).overlapsMultipleTiles(rect)) { recycleTile(sources[0]); } } return dest; } if (x0 < xStart) { // region left of warp grid Rectangle rect = srcBounds.intersection(new Rectangle(x0, y0, xStart - x0, y1 - y0 + 1)); if (!rect.isEmpty()) { sources[0] = getSourceImage(0).getData(rect); copyRect(sources, dest, rect); // Recycle the source tile if(getSourceImage(0).overlapsMultipleTiles(rect)) { recycleTile(sources[0]); } } x0 = xStart; } if (x1 >= xEnd) { // region right of warp grid Rectangle rect = srcBounds.intersection(new Rectangle(xEnd, y0, x1 - xEnd + 1, y1 - y0 + 1)); if (!rect.isEmpty()) { sources[0] = getSourceImage(0).getData(rect); copyRect(sources, dest, rect); // Recycle the source tile if(getSourceImage(0).overlapsMultipleTiles(rect)) { recycleTile(sources[0]); } } x1 = xEnd - 1; } if (y0 < yStart) { // region above warp grid Rectangle rect = srcBounds.intersection(new Rectangle(x0, y0, x1 - x0 + 1, yStart - y0)); if (!rect.isEmpty()) { sources[0] = getSourceImage(0).getData(rect); copyRect(sources, dest, rect); // Recycle the source tile if(getSourceImage(0).overlapsMultipleTiles(rect)) { recycleTile(sources[0]); } } y0 = yStart; } if (y1 >= yEnd) { // region below warp grid Rectangle rect = srcBounds.intersection(new Rectangle(x0, yEnd, x1 - x0 + 1, y1 - yEnd + 1)); if (!rect.isEmpty()) { sources[0] = getSourceImage(0).getData(rect); copyRect(sources, dest, rect); // Recycle the source tile if(getSourceImage(0).overlapsMultipleTiles(rect)) { recycleTile(sources[0]); } } y1 = yEnd -1; } /* The region within the warp grid. */ destRect = new Rectangle(x0, y0, x1 - x0 + 1, y1 - y0 + 1); /* Map destination rectangle to source space. */ Rectangle srcRect = backwardMapRect(destRect, 0).intersection(srcBounds); if (!srcRect.isEmpty()) { /* Add the interpolation paddings. */ int l = interp== null ? 0 : interp.getLeftPadding(); int r = interp== null ? 0 : interp.getRightPadding(); int t = interp== null ? 0 : interp.getTopPadding(); int b = interp== null ? 0 : interp.getBottomPadding(); srcRect = new Rectangle(srcRect.x - l, srcRect.y - t, srcRect.width + l + r, srcRect.height + t + b); sources[0] = getBorderExtender() != null ? getSourceImage(0).getExtendedData(srcRect, extender) : getSourceImage(0).getData(srcRect); computeRect(sources, dest, destRect); // Recycle the source tile if(getSourceImage(0).overlapsMultipleTiles(srcRect)) { recycleTile(sources[0]); } } return dest; } /** * Performs the "grid warp" operation on a rectangular region of * the image. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { int formatTag = MediaLibAccessor.findCompatibleTag(sources, dest); Raster source = sources[0]; MediaLibAccessor srcMA = new MediaLibAccessor(source, source.getBounds(), formatTag); MediaLibAccessor dstMA = new MediaLibAccessor(dest, destRect, formatTag); mediaLibImage[] srcMLI = srcMA.getMediaLibImages(); mediaLibImage[] dstMLI = dstMA.getMediaLibImages(); switch (dstMA.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: // computing medialibtable and calc for integral type and call if (mlibInterpTableI==null){ InterpolationTable jtable = (InterpolationTable)interp; mlibInterpTableI = new mediaLibImageInterpTable(Constants.MLIB_INT, jtable.getWidth(), jtable.getHeight(), jtable.getLeftPadding(), jtable.getTopPadding(), jtable.getSubsampleBitsH(), jtable.getSubsampleBitsV(), jtable.getPrecisionBits(), jtable.getHorizontalTableData(), jtable.getVerticalTableData()); } if (setBackground) for (int i = 0 ; i < dstMLI.length; i++) { Image.GridWarpTable2(dstMLI[i], srcMLI[i], xWarpPos, yWarpPos, source.getMinX(), source.getMinY(), xStart - destRect.x, xStep, xNumCells, yStart - destRect.y, yStep, yNumCells, mlibInterpTableI, Constants.MLIB_EDGE_DST_NO_WRITE, intBackgroundValues); } else for (int i = 0 ; i < dstMLI.length; i++) { Image.GridWarpTable(dstMLI[i], srcMLI[i], xWarpPos, yWarpPos, source.getMinX(), source.getMinY(), xStart - destRect.x, xStep, xNumCells, yStart - destRect.y, yStep, yNumCells, mlibInterpTableI, Constants.MLIB_EDGE_DST_NO_WRITE); MlibUtils.clampImage(dstMLI[i], getColorModel()); } break; case DataBuffer.TYPE_FLOAT: if (mlibInterpTableF==null){ InterpolationTable jtable = (InterpolationTable)interp; mlibInterpTableF = new mediaLibImageInterpTable(Constants.MLIB_FLOAT, jtable.getWidth(), jtable.getHeight(), jtable.getLeftPadding(), jtable.getTopPadding(), jtable.getSubsampleBitsH(), jtable.getSubsampleBitsV(), jtable.getPrecisionBits(), jtable.getHorizontalTableDataFloat(), jtable.getVerticalTableDataFloat()); } if (setBackground) for (int i = 0 ; i < dstMLI.length; i++) { Image.GridWarpTable2_Fp(dstMLI[i], srcMLI[i], xWarpPos, yWarpPos, source.getMinX(), source.getMinY(), xStart - destRect.x, xStep, xNumCells, yStart - destRect.y, yStep, yNumCells, mlibInterpTableF, Constants.MLIB_EDGE_DST_NO_WRITE, backgroundValues); } else for (int i = 0 ; i < dstMLI.length; i++) { Image.GridWarpTable_Fp(dstMLI[i], srcMLI[i], xWarpPos, yWarpPos, source.getMinX(), source.getMinY(), xStart - destRect.x, xStep, xNumCells, yStart - destRect.y, yStep, yNumCells, mlibInterpTableF, Constants.MLIB_EDGE_DST_NO_WRITE); } break; case DataBuffer.TYPE_DOUBLE: if (mlibInterpTableD == null){ InterpolationTable jtable = (InterpolationTable)interp; mlibInterpTableD = new mediaLibImageInterpTable(Constants.MLIB_DOUBLE, jtable.getWidth(), jtable.getHeight(), jtable.getLeftPadding(), jtable.getTopPadding(), jtable.getSubsampleBitsH(), jtable.getSubsampleBitsV(), jtable.getPrecisionBits(), jtable.getHorizontalTableDataDouble(), jtable.getVerticalTableDataDouble()); } if (setBackground) for (int i = 0 ; i < dstMLI.length; i++) { Image.GridWarpTable2_Fp(dstMLI[i], srcMLI[i], xWarpPos, yWarpPos, source.getMinX(), source.getMinY(), xStart - destRect.x, xStep, xNumCells, yStart - destRect.y, yStep, yNumCells, mlibInterpTableD, Constants.MLIB_EDGE_DST_NO_WRITE, backgroundValues); } else for (int i = 0 ; i < dstMLI.length; i++) { Image.GridWarpTable_Fp(dstMLI[i], srcMLI[i], xWarpPos, yWarpPos, source.getMinX(), source.getMinY(), xStart - destRect.x, xStep, xNumCells, yStart - destRect.y, yStep, yNumCells, mlibInterpTableD, Constants.MLIB_EDGE_DST_NO_WRITE); } break; default: throw new RuntimeException(JaiI18N.getString("Generic2")); } if (dstMA.isDataCopy()) { dstMA.clampDataArrays(); dstMA.copyDataToRaster(); } } /** * Copies the pixels of a rectangle from source Raster * to destination Raster using mediaLib. This method * is used to copy pixels outside of the warp grid. */ private void copyRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { int formatTag = MediaLibAccessor.findCompatibleTag(sources, dest); MediaLibAccessor srcMA = new MediaLibAccessor(sources[0], destRect, formatTag); MediaLibAccessor dstMA = new MediaLibAccessor(dest, destRect, formatTag); mediaLibImage[] srcMLI = srcMA.getMediaLibImages(); mediaLibImage[] dstMLI = dstMA.getMediaLibImages(); for (int i = 0 ; i < dstMLI.length; i++) { Image.Copy(dstMLI[i], srcMLI[i]); } if (dstMA.isDataCopy()) { dstMA.copyDataToRaster(); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibThresholdRIF.java0000644000175000017500000000354710203035544026507 0ustar mathieumathieu/* * $RCSfile: MlibThresholdRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:07 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "Threshold" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.ThresholdDescriptor * @see MlibThresholdOpImage * * @since 1.0 * */ public class MlibThresholdRIF implements RenderedImageFactory { /** Constructor. */ public MlibThresholdRIF() {} /** * Creates a new instance of MlibThresholdOpImage in * the rendered image mode. * * @param args The source image. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout)) { return null; } return new MlibThresholdOpImage(args.getRenderedSource(0), hints, layout, (double[])args.getObjectParameter(0), (double[])args.getObjectParameter(1), (double[])args.getObjectParameter(2)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibAffineBicubicOpImage.java0000644000175000017500000001661610350333605030130 0ustar mathieumathieu/* * $RCSfile: MlibAffineBicubicOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.4 $ * $Date: 2005-12-15 18:35:45 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import java.awt.geom.AffineTransform; import javax.media.jai.AreaOpImage; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationBicubic; import javax.media.jai.InterpolationBicubic2; import javax.media.jai.KernelJAI; import javax.media.jai.OpImage; import java.util.Map; import com.sun.medialib.mlib.*; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class to perform Bicubic AffineTransform * on a source image. * */ public class MlibAffineBicubicOpImage extends MlibAffineOpImage { /** * Creates a MlibAffineNearestOpImage given a ParameterBlock containing the * image source and the AffineTransform. The image dimensions are derived * from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout * object. * * @param source a RenderedImage. * @param extender a BorderExtender, or null. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param tr the AffineTransform. * @param interp the Interpolation to be used (Nearest-Neighbour) * @param backgroundValues the user provided background colors */ public MlibAffineBicubicOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, AffineTransform tr, Interpolation interp, double[] backgroundValues) { super(source, layout, config, extender, tr, interp, backgroundValues); } /** * Performs bicubic affine transformation on a specified * rectangle. The sources are cobbled and extended. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // The default interpolation for this class is Bicubic int mlibInterpType = Constants.MLIB_BICUBIC; // Unless a Bicubic2 object is passed in as the interpolation if (interp instanceof InterpolationBicubic2) { mlibInterpType = Constants.MLIB_BICUBIC2; } Raster source = sources[0]; Rectangle srcRect = source.getBounds(); int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest); MediaLibAccessor srcAccessor = new MediaLibAccessor(source,srcRect,formatTag); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest,destRect,formatTag); // // The AffineTransform needs to be readjusted as per the // location of the current source & destination rectangle // // Clone the global transform so as not to write to an instance // variable as this method may be called from different threads. double[] medialib_tr = (double[])this.medialib_tr.clone(); medialib_tr[2] = m_transform[0] * srcRect.x + m_transform[1] * srcRect.y + m_transform[2] - destRect.x; medialib_tr[5] = m_transform[3] * srcRect.x + m_transform[4] * srcRect.y + m_transform[5] - destRect.y; mediaLibImage srcML[], dstML[]; switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: srcML = srcAccessor.getMediaLibImages(); dstML = dstAccessor.getMediaLibImages(); if (setBackground) Image.Affine2(dstML[0], srcML[0], medialib_tr, mlibInterpType, Constants.MLIB_EDGE_DST_NO_WRITE, intBackgroundValues); else Image.Affine(dstML[0], srcML[0], medialib_tr, mlibInterpType, Constants.MLIB_EDGE_DST_NO_WRITE); MlibUtils.clampImage(dstML[0], getColorModel()); break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: srcML = srcAccessor.getMediaLibImages(); dstML = dstAccessor.getMediaLibImages(); if (setBackground) Image.Affine2_Fp(dstML[0], srcML[0], medialib_tr, mlibInterpType, Constants.MLIB_EDGE_DST_NO_WRITE, backgroundValues); else Image.Affine_Fp(dstML[0], srcML[0], medialib_tr, mlibInterpType, Constants.MLIB_EDGE_DST_NO_WRITE); break; default: String className = this.getClass().getName(); throw new RuntimeException(JaiI18N.getString("Generic2")); } if (dstAccessor.isDataCopy()) { dstAccessor.copyDataToRaster(); } } // public static OpImage createTestImage(OpImageTester oit) { // Interpolation interp = new InterpolationBicubic(8); // AffineTransform tr = new AffineTransform(0.707107, // -0.707106, // 0.707106, // 0.707107, // 0.0, // 0.0); // return new MlibAffineBicubicOpImage(oit.getSource(), null, null, // new ImageLayout(oit.getSource()), // tr, // interp, null); // } // // Calls a method on OpImage that uses introspection, to make this // // class, discover it's createTestImage() call, call it and then // // benchmark the performance of the created OpImage chain. // public static void main (String args[]) { // String classname = "com.sun.media.jai.mlib.MlibAffineBicubicOpImage"; // OpImageTester.performDiagnostics(classname, args); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibMultiplyRIF.java0000644000175000017500000000326710203035544026371 0ustar mathieumathieu/* * $RCSfile: MlibMultiplyRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:01 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "Multiply" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.MultiplyDescriptor * @see MlibMultiplyOpImage * */ public class MlibMultiplyRIF implements RenderedImageFactory { /** Constructor. */ public MlibMultiplyRIF() {} /** * Creates a new instance of MlibMultiplyOpImage in * the rendered image mode. * * @param args The source images to be multiplied. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout)) { return null; } return new MlibMultiplyOpImage(args.getRenderedSource(0), args.getRenderedSource(1), hints, layout); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibSubsampleAverageOpImage.java0000644000175000017500000001007210203035544030671 0ustar mathieumathieu/* * $RCSfile: MlibSubsampleAverageOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:06 $ * $State: Exp $ */package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.util.Map; import javax.media.jai.BorderExtender; import javax.media.jai.GeometricOpImage; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import com.sun.media.jai.opimage.SubsampleAverageOpImage; import com.sun.medialib.mlib.Image; import com.sun.medialib.mlib.mediaLibImage; public class MlibSubsampleAverageOpImage extends SubsampleAverageOpImage { /* XXX public static void main(String[] args) throws Throwable { RenderedImage source = javax.media.jai.JAI.create("fileload", args[0]); double scaleX = args.length > 1 ? Double.valueOf(args[1]).doubleValue() : 0.25; double scaleY = args.length > 2 ? Double.valueOf(args[2]).doubleValue() : scaleX; RenderedImage dest = new MlibSubsampleAverageOpImage(source, null, null, scaleX, scaleY); System.out.println(source.getClass().getName()+": "+ new ImageLayout(source)); System.out.println(dest.getClass().getName()+": "+ new ImageLayout(dest)); java.awt.Frame frame = new java.awt.Frame("Mlib Sub-average Test"); frame.setLayout(new java.awt.GridLayout(1, 2)); javax.media.jai.widget.ScrollingImagePanel ps = new javax.media.jai.widget.ScrollingImagePanel(source, 512, 512); javax.media.jai.widget.ScrollingImagePanel pd = new javax.media.jai.widget.ScrollingImagePanel(dest, 512, 512); frame.add(ps); frame.add(pd); frame.pack(); frame.show(); } */ public MlibSubsampleAverageOpImage(RenderedImage source, ImageLayout layout, Map config, double scaleX, double scaleY) { super(source, layout, config, scaleX, scaleY); } protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { Raster source = sources[0]; Rectangle srcRect = source.getBounds(); int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest); MediaLibAccessor srcAccessor = new MediaLibAccessor(source,srcRect,formatTag); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest,destRect,formatTag); mediaLibImage srcML[], dstML[]; switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: srcML = srcAccessor.getMediaLibImages(); dstML = dstAccessor.getMediaLibImages(); Image.SubsampleAverage(dstML[0], srcML[0], scaleX, scaleY); break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: srcML = srcAccessor.getMediaLibImages(); dstML = dstAccessor.getMediaLibImages(); Image.SubsampleAverage_Fp(dstML[0], srcML[0], scaleX, scaleY); break; default: // XXX? } if (dstAccessor.isDataCopy()) { dstAccessor.copyDataToRaster(); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibDilateRIF.java0000644000175000017500000000744010203035544025751 0ustar mathieumathieu/* * $RCSfile: MlibDilateRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:54 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "Dilate" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.DilateDescriptor * @see MlibDilateOpImage */ public class MlibDilateRIF implements RenderedImageFactory { /** Constructor. */ public MlibDilateRIF() {} /** * Creates a new instance of MlibDilateOpImage in * the rendered image mode. * * @param args The source image and dilation kernel. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); boolean isBinary = false; if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout)) { if(!MediaLibAccessor.isMediaLibBinaryCompatible(args, layout)) { return null; } isBinary = true; } /* Get BorderExtender from hints if any. */ BorderExtender extender = RIFUtil.getBorderExtenderHint(hints); RenderedImage source = args.getRenderedSource(0); KernelJAI unRotatedKernel = (KernelJAI)args.getObjectParameter(0); KernelJAI kJAI = unRotatedKernel.getRotatedKernel(); int kWidth = kJAI.getWidth(); int kHeight= kJAI.getHeight(); int xOri = kJAI.getXOrigin(); int yOri = kJAI.getYOrigin(); int numB = source.getSampleModel().getNumBands(); /* mediaLib does not handle kernels with either dimension < 2. */ if (xOri != 1 || yOri != 1 || kWidth != 3 || kHeight != 3 || numB != 1) { return null; } // check for plus and square type of kernel float[] kdata = kJAI.getKernelData(); if (isBinary && isKernel3Square1(kdata) || !isBinary && isKernel3Square0(kdata)){ return new MlibDilate3SquareOpImage(source, extender, hints, layout); } if (isBinary && isKernel3Plus1(kdata)){ // plus shape return new MlibDilate3PlusOpImage(source, extender, hints, layout); } return null; } // check to see if a 3x3 kernel has 1s at the plus positions and 0s elsewhere private boolean isKernel3Plus1(float[] kdata){ return (kdata[0] == 0.0F && kdata[1] == 1.0F && kdata[2] == 0.0F && kdata[3] == 1.0F && kdata[4] == 1.0F && kdata[5] == 1.0F && kdata[6] == 0.0F && kdata[7] == 1.0F && kdata[8] == 0.0F); } // check to see if a 3x3 kernel has 1s at the plus positions private boolean isKernel3Square0(float[] kdata){ return (kdata[0] == 0.0F && kdata[1] == 0.0F && kdata[2] == 0.0F && kdata[3] == 0.0F && kdata[4] == 0.0F && kdata[5] == 0.0F && kdata[6] == 0.0F && kdata[7] == 0.0F && kdata[8] == 0.0F); } // check to see if a 3x3 kernel has 1s at the plus positions private boolean isKernel3Square1(float[] kdata){ return (kdata[0] == 1.0F && kdata[1] == 1.0F && kdata[2] == 1.0F && kdata[3] == 1.0F && kdata[4] == 1.0F && kdata[5] == 1.0F && kdata[6] == 1.0F && kdata[7] == 1.0F && kdata[8] == 1.0F); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibGradientRIF.java0000644000175000017500000000636510203035544026311 0ustar mathieumathieu/* * $RCSfile: MlibGradientRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:57 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "Gradient" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.GradientDescriptor * @see MlibGradientOpImage */ public class MlibGradientRIF implements RenderedImageFactory { /** Constructor. */ public MlibGradientRIF() {} /** * Creates a new instance of MlibGradientOpImage in * the rendered image mode. * * @param args The source image and convolution kernel. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout)) { return null; } /* Get BorderExtender from hints if any. */ BorderExtender extender = RIFUtil.getBorderExtenderHint(hints); RenderedImage source = args.getRenderedSource(0); /* * Get the Horizontal & Vertical kernels. * At this point these kernels should have the same width & height */ KernelJAI kern_h = (KernelJAI)args.getObjectParameter(0); KernelJAI kern_v = (KernelJAI)args.getObjectParameter(1); /* Get the width & height of the kernels. */ int kWidth = kern_h.getWidth(); int kHeight = kern_v.getHeight(); /* Check and see if the operation is a Sobel. */ float khdata[], kvdata[]; khdata = kern_h.getKernelData(); kvdata = kern_v.getKernelData(); if ((khdata[0] == -1.0F && khdata[1] == -2.0F && khdata[2] == -1.0F && khdata[3] == 0.0F && khdata[4] == 0.0F && khdata[5] == 0.0F && khdata[6] == 1.0F && khdata[7] == 2.0F && khdata[8] == 1.0F) && (kvdata[0] == -1.0F && kvdata[1] == 0.0F && kvdata[2] == 1.0F && kvdata[3] == -2.0F && kvdata[4] == 0.0F && kvdata[5] == 2.0F && kvdata[6] == -1.0F && kvdata[7] == 0.0F && kvdata[8] == 1.0F) && kWidth == 3 && kHeight == 3) { return new MlibSobelOpImage(source, extender, hints, layout, kern_h); } /* Call the Generic version. */ return new MlibGradientOpImage(source, extender, hints, layout, kern_h, kern_v); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibAffineTableOpImage.java0000644000175000017500000002200510350333605027604 0ustar mathieumathieu/* * $RCSfile: MlibAffineTableOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.3 $ * $Date: 2005-12-15 18:35:46 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import java.awt.geom.AffineTransform; import javax.media.jai.AreaOpImage; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationTable; import javax.media.jai.KernelJAI; import javax.media.jai.OpImage; import java.util.Map; import javax.media.jai.BorderExtender; import com.sun.medialib.mlib.*; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage class to perform an Affine using interpolation coefficients * represented in a Table, on a source image. * */ public class MlibAffineTableOpImage extends MlibAffineOpImage { /** * Creates a MlibAffineTableOpImage given a ParameterBlock containing the * image source and the AffineTransform. The image dimensions are derived * from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout * object. * * @param source a RenderedImage. * @param layout an ImageLayout optionally containing the tile grid layout, * SampleModel, and ColorModel, or null. * @param tr the AffineTransform. * @param interp the Interpolation to be used (Table-based) */ public MlibAffineTableOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, AffineTransform tr, Interpolation interp, double[] backgroundValues) { super(source, layout, config, extender, tr, interp, backgroundValues); } /** * Performs nearest-neighbour affine transformation on a specified * rectangle. The sources are cobbled. * * @param sources an array of source Rasters, guaranteed to provide all * necessary source data for computing the output. * @param dest a WritableRaster tile containing the area to be computed. * @param destRect the rectangle within dest to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { // Cast the Interpolation object to InterpolationTable object InterpolationTable jtable = (InterpolationTable)interp; // The Medialib InterpolationTable class equivalent mediaLibImageInterpTable mlibInterpTable; Raster source = sources[0]; Rectangle srcRect = source.getBounds(); int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest); MediaLibAccessor srcAccessor = new MediaLibAccessor(source,srcRect,formatTag); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest,destRect,formatTag); // // The AffineTransform needs to be readjusted as per the // location of the current source & destination rectangles // // Clone the global transform so as not to write to an instance // variable as this method may be called from different threads. double[] medialib_tr = (double[])this.medialib_tr.clone(); medialib_tr[2] = m_transform[0] * srcRect.x + m_transform[1] * srcRect.y + m_transform[2] - destRect.x; medialib_tr[5] = m_transform[3] * srcRect.x + m_transform[4] * srcRect.y + m_transform[5] - destRect.y; mediaLibImage srcML[], dstML[]; switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: mlibInterpTable = new mediaLibImageInterpTable(Constants.MLIB_INT, jtable.getWidth(), jtable.getHeight(), jtable.getLeftPadding(), jtable.getTopPadding(), jtable.getSubsampleBitsH(), jtable.getSubsampleBitsV(), jtable.getPrecisionBits(), jtable.getHorizontalTableData(), jtable.getVerticalTableData()); srcML = srcAccessor.getMediaLibImages(); dstML = dstAccessor.getMediaLibImages(); if (setBackground) Image.AffineTable2(dstML[0], srcML[0], medialib_tr, mlibInterpTable, Constants.MLIB_EDGE_DST_NO_WRITE, intBackgroundValues); else Image.AffineTable(dstML[0], srcML[0], medialib_tr, mlibInterpTable, Constants.MLIB_EDGE_DST_NO_WRITE); MlibUtils.clampImage(dstML[0], getColorModel()); break; case DataBuffer.TYPE_FLOAT: mlibInterpTable = new mediaLibImageInterpTable(Constants.MLIB_FLOAT, jtable.getWidth(), jtable.getHeight(), jtable.getLeftPadding(), jtable.getTopPadding(), jtable.getSubsampleBitsH(), jtable.getSubsampleBitsV(), jtable.getPrecisionBits(), jtable.getHorizontalTableDataFloat(), jtable.getVerticalTableDataFloat()); srcML = srcAccessor.getMediaLibImages(); dstML = dstAccessor.getMediaLibImages(); if (setBackground) Image.AffineTable2_Fp(dstML[0], srcML[0], medialib_tr, mlibInterpTable, Constants.MLIB_EDGE_DST_NO_WRITE, backgroundValues); else Image.AffineTable_Fp(dstML[0], srcML[0], medialib_tr, mlibInterpTable, Constants.MLIB_EDGE_DST_NO_WRITE); break; case DataBuffer.TYPE_DOUBLE: mlibInterpTable = new mediaLibImageInterpTable(Constants.MLIB_DOUBLE, jtable.getWidth(), jtable.getHeight(), jtable.getLeftPadding(), jtable.getTopPadding(), jtable.getSubsampleBitsH(), jtable.getSubsampleBitsV(), jtable.getPrecisionBits(), jtable.getHorizontalTableDataDouble(), jtable.getVerticalTableDataDouble()); srcML = srcAccessor.getMediaLibImages(); dstML = dstAccessor.getMediaLibImages(); if (setBackground) Image.AffineTable2_Fp(dstML[0], srcML[0], medialib_tr, mlibInterpTable, Constants.MLIB_EDGE_DST_NO_WRITE, backgroundValues); else Image.AffineTable_Fp(dstML[0], srcML[0], medialib_tr, mlibInterpTable, Constants.MLIB_EDGE_DST_NO_WRITE); break; default: String className = this.getClass().getName(); throw new RuntimeException(JaiI18N.getString("Generic2")); } if (dstAccessor.isDataCopy()) { dstAccessor.copyDataToRaster(); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibAddConstRIF.java0000644000175000017500000000327310203035544026246 0ustar mathieumathieu/* * $RCSfile: MlibAddConstRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:48 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.ImageLayout; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "AddConst" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.AddConstDescriptor * @see MlibAddConstOpImage * */ public class MlibAddConstRIF implements RenderedImageFactory { /** Constructor. */ public MlibAddConstRIF() {} /** * Creates a new instance of MlibAddConstOpImage in * the rendered image mode. * * @param args The source image and the constants. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout)) { return null; } return new MlibAddConstOpImage(args.getRenderedSource(0), hints, layout, (double[])args.getObjectParameter(0)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibThresholdOpImage.java0000644000175000017500000001140410203035544027377 0ustar mathieumathieu/* * $RCSfile: MlibThresholdOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:07 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.PointOpImage; import java.util.Map; import com.sun.media.jai.util.ImageUtil; import com.sun.medialib.mlib.*; /** * An OpImage implementing the "Threshold" operation * using MediaLib. * * @see javax.media.jai.operator.ThresholdDescriptor * @see MlibThresholdRIF * * @since 1.0 * */ final class MlibThresholdOpImage extends PointOpImage { /** The lower bound, one for each band. */ private double[] low; /** The integer version of lower bound. */ private int[] lowInt; /** The upper bound, one for each band. */ private double[] high; /** The integer version of upper bound. */ private int[] highInt; /** The constants to be mapped, one for each band. */ private double[] constants; /** The integer version of constants. */ private int[] constantsInt; /** Constructor. */ public MlibThresholdOpImage(RenderedImage source, Map config, ImageLayout layout, double[] low, double[] high, double[] constants) { super(source, layout, config, true); int numBands = getSampleModel().getNumBands(); this.low = new double[numBands]; this.lowInt = new int[numBands]; this.high = new double[numBands]; this.highInt = new int[numBands]; this.constants = new double[numBands]; this.constantsInt = new int[numBands]; for (int i = 0; i < numBands; i++) { if (low.length < numBands) { this.low[i] = low[0]; } else { this.low[i] = low[i]; } this.lowInt[i] = ImageUtil.clampInt((int)Math.ceil(this.low[i])); if (high.length < numBands) { this.high[i] = high[0]; } else { this.high[i] = high[i]; } this.highInt[i] = ImageUtil.clampInt((int)Math.floor(this.high[i])); if (constants.length < numBands) { this.constants[i] = constants[0]; } else { this.constants[i] = constants[i]; } this.constantsInt[i] = ImageUtil.clampRoundInt(this.constants[i]); } // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * Performs the "Threshold" operation on a rectangular region of * the same. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { int formatTag = MediaLibAccessor.findCompatibleTag(sources, dest); MediaLibAccessor srcMA = new MediaLibAccessor(sources[0], destRect, formatTag); MediaLibAccessor dstMA = new MediaLibAccessor(dest, destRect, formatTag); mediaLibImage[] srcMLI = srcMA.getMediaLibImages(); mediaLibImage[] dstMLI = dstMA.getMediaLibImages(); switch (dstMA.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: for (int i = 0 ; i < dstMLI.length; i++) { int[] mlLow = dstMA.getIntParameters(i, lowInt); int[] mlHigh = dstMA.getIntParameters(i, highInt); int[] mlConstants = dstMA.getIntParameters(i, constantsInt); Image.Thresh5(dstMLI[i], srcMLI[i], mlHigh, mlLow, mlConstants); } break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: for (int i = 0 ; i < dstMLI.length; i++) { double[] mlLow = dstMA.getDoubleParameters(i, low); double[] mlHigh = dstMA.getDoubleParameters(i, high); double[] mlConstants = dstMA.getDoubleParameters(i, constants); Image.Thresh5_Fp(dstMLI[i], srcMLI[i], mlHigh, mlLow, mlConstants); } break; default: throw new RuntimeException(JaiI18N.getString("Generic2")); } if (dstMA.isDataCopy()) { dstMA.clampDataArrays(); dstMA.copyDataToRaster(); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibBoxFilterRIF.java0000644000175000017500000000462110305430255026444 0ustar mathieumathieu/* * $RCSfile: MlibBoxFilterRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-08-31 22:35:25 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import java.util.Arrays; import java.util.Map; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; import com.sun.media.jai.opimage.RIFUtil; import com.sun.medialib.mlib.*; /** * A RIF supporting the "BoxFilter" operation in the rendered * image layer. * * @see javax.media.jai.operator.BoxFilterDescriptor * @see com.sun.media.jai.mlib.MlibSeparableConvolveOpImage * * @since EA4 * */ public class MlibBoxFilterRIF extends MlibConvolveRIF { /** Constructor. */ public MlibBoxFilterRIF() {} /** * Create a new instance of a convolution OpImage * representing a box filtering operation in the rendered mode. * This method satisfies the implementation of RIF. * * @param paramBlock The source image and the convolution kernel. */ public RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints) { // Get the operation parameters. int width = paramBlock.getIntParameter(0); int height = paramBlock.getIntParameter(1); int xOrigin = paramBlock.getIntParameter(2); int yOrigin = paramBlock.getIntParameter(3); // Allocate and initialize arrays. float[] dataH = new float[width]; Arrays.fill(dataH, 1.0F/(float)width); float[] dataV = null; if(height == width) { dataV = dataH; } else { dataV = new float[height]; Arrays.fill(dataV, 1.0F/(float)height); } // Construct a separable kernel. KernelJAI kernel = new KernelJAI(width, height, xOrigin, yOrigin, dataH, dataV); // Construct the parameters for the "Convolve" RIF. ParameterBlock args = new ParameterBlock(paramBlock.getSources()); args.add(kernel); // Return the result of the "Convolve" RIF. return super.create(args, renderHints); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibErodeRIF.java0000644000175000017500000000742610203035544025611 0ustar mathieumathieu/* * $RCSfile: MlibErodeRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:55 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import javax.media.jai.BorderExtender; import javax.media.jai.ImageLayout; import javax.media.jai.KernelJAI; import java.util.Map; import com.sun.media.jai.opimage.RIFUtil; /** * A RIF supporting the "Erode" operation in the * rendered image mode using MediaLib. * * @see javax.media.jai.operator.ErodeDescriptor * @see MlibErodeOpImage */ public class MlibErodeRIF implements RenderedImageFactory { /** Constructor. */ public MlibErodeRIF() {} /** * Creates a new instance of MlibErodeOpImage in * the rendered image mode. * * @param args The source image and erosion kernel. * @param hints May contain rendering hints and destination image layout. */ public RenderedImage create(ParameterBlock args, RenderingHints hints) { /* Get ImageLayout and TileCache from RenderingHints. */ ImageLayout layout = RIFUtil.getImageLayoutHint(hints); boolean isBinary = false; if (!MediaLibAccessor.isMediaLibCompatible(args, layout) || !MediaLibAccessor.hasSameNumBands(args, layout)) { if(!MediaLibAccessor.isMediaLibBinaryCompatible(args, layout)) { return null; } isBinary = true; } /* Get BorderExtender from hints if any. */ BorderExtender extender = RIFUtil.getBorderExtenderHint(hints); RenderedImage source = args.getRenderedSource(0); KernelJAI unRotatedKernel = (KernelJAI)args.getObjectParameter(0); KernelJAI kJAI = unRotatedKernel.getRotatedKernel(); int kWidth = kJAI.getWidth(); int kHeight= kJAI.getHeight(); int xOri = kJAI.getXOrigin(); int yOri = kJAI.getYOrigin(); int numB = source.getSampleModel().getNumBands(); /* mediaLib does not handle kernels with either dimension < 2. */ if (xOri != 1 || yOri != 1 || kWidth != 3 || kHeight != 3 || numB != 1) { return null; } // check for plus and square type of kernel float[] kdata = kJAI.getKernelData(); if (isBinary && isKernel3Square1(kdata) || !isBinary && isKernel3Square0(kdata)){ return new MlibErode3SquareOpImage(source, extender, hints, layout); } if (isBinary && isKernel3Plus1(kdata)){ // plus shape return new MlibErode3PlusOpImage(source, extender, hints, layout); } return null; } // check to see if a 3x3 kernel has 1s at the plus positions and 0s elsewhere private boolean isKernel3Plus1(float[] kdata){ return (kdata[0] == 0.0F && kdata[1] == 1.0F && kdata[2] == 0.0F && kdata[3] == 1.0F && kdata[4] == 1.0F && kdata[5] == 1.0F && kdata[6] == 0.0F && kdata[7] == 1.0F && kdata[8] == 0.0F); } // check to see if a 3x3 kernel has 1s at the plus positions private boolean isKernel3Square0(float[] kdata){ return (kdata[0] == 0.0F && kdata[1] == 0.0F && kdata[2] == 0.0F && kdata[3] == 0.0F && kdata[4] == 0.0F && kdata[5] == 0.0F && kdata[6] == 0.0F && kdata[7] == 0.0F && kdata[8] == 0.0F); } // check to see if a 3x3 kernel has 1s at the plus positions private boolean isKernel3Square1(float[] kdata){ return (kdata[0] == 1.0F && kdata[1] == 1.0F && kdata[2] == 1.0F && kdata[3] == 1.0F && kdata[4] == 1.0F && kdata[5] == 1.0F && kdata[6] == 1.0F && kdata[7] == 1.0F && kdata[8] == 1.0F); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/mlib/MlibAddOpImage.java0000644000175000017500000001362110203035544026136 0ustar mathieumathieu/* * $RCSfile: MlibAddOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:48 $ * $State: Exp $ */ package com.sun.media.jai.mlib; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.PixelInterleavedSampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import javax.media.jai.ImageLayout; import javax.media.jai.OpImage; import javax.media.jai.PointOpImage; import java.util.Map; import com.sun.medialib.mlib.*; // import com.sun.media.jai.test.OpImageTester; /** * An OpImage that performs the Add operation on 2 images through mediaLib. * */ final class MlibAddOpImage extends PointOpImage { // XXX This cloning of the ImageLayout object should be centraliZed // into the superclass PointOpImage and removed from the mlib subclasses. private static ImageLayout layoutHelper(ImageLayout layout) { if (layout == null) { return null; } else { return (ImageLayout)layout.clone(); } } /** * Constructs an MlibAddOpImage. The image dimensions are copied * from the source image. The tile grid layout, SampleModel, and * ColorModel may optionally be specified by an ImageLayout object. * * @param source a RenderedImage. * @param layout an ImageLayout optionally containing the tile * grid layout, SampleModel, and ColorModel, or null. */ public MlibAddOpImage(RenderedImage source1, RenderedImage source2, Map config, ImageLayout layout) { super(source1, source2, layoutHelper(layout), config, true); // Set flag to permit in-place operation. permitInPlaceOperation(); } /** * Add the pixel values of a rectangle from the two sources. * The sources are cobbled. * * @param sources an array of sources, guarantee to provide all * necessary source data for computing the rectangle. * @param dest a tile that contains the rectangle to be computed. * @param destRect the rectangle within this OpImage to be processed. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { int formatTag = MediaLibAccessor.findCompatibleTag(sources,dest); MediaLibAccessor srcAccessor1 = new MediaLibAccessor(sources[0], destRect,formatTag); MediaLibAccessor srcAccessor2 = new MediaLibAccessor(sources[1], destRect,formatTag); MediaLibAccessor dstAccessor = new MediaLibAccessor(dest, destRect, formatTag); mediaLibImage[] srcML1 = srcAccessor1.getMediaLibImages(); mediaLibImage[] srcML2 = srcAccessor2.getMediaLibImages(); mediaLibImage[] dstML = dstAccessor.getMediaLibImages(); switch (dstAccessor.getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_INT: for (int i = 0 ; i < dstML.length; i++) { Image.Add(dstML[i], srcML1[i], srcML2[i]); } break; case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: for (int i = 0 ; i < dstML.length; i++) { Image.Add_Fp(dstML[i], srcML1[i], srcML2[i]); } break; default: String className = this.getClass().getName(); throw new RuntimeException(className + JaiI18N.getString("Generic2")); } if (dstAccessor.isDataCopy()) { dstAccessor.clampDataArrays(); dstAccessor.copyDataToRaster(); } } // public static void main (String args[]) { // System.out.println("MlibAddOpImage Test"); // ImageLayout layout; // OpImage src1, src2, dst; // Rectangle rect = new Rectangle(0, 0, 5, 5); // System.out.println("1. PixelInterleaved byte 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 800, 800, 0, 0, // 200, 200, DataBuffer.TYPE_BYTE, // 3, false); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new MlibAddOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("2. Banded byte 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 800, 800, 0, 0, // 200, 200, DataBuffer.TYPE_BYTE, // 3, true); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new MlibAddOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("3. PixelInterleaved int 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 512, 512, 0, 0, 200, 200, // DataBuffer.TYPE_INT, 3, false); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new MlibAddOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // System.out.println("4. Banded int 3-band"); // layout = OpImageTester.createImageLayout(0, 0, 512, 512, 0, 0, // 200, 200, DataBuffer.TYPE_INT, // 3, true); // src1 = OpImageTester.createRandomOpImage(layout); // src2 = OpImageTester.createRandomOpImage(layout); // dst = new MlibAddOpImage(src1, src2, null, null); // OpImageTester.testOpImage(dst, rect); // OpImageTester.timeOpImage(dst, 10); // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/tilecodec/0000755000175000017500000000000011633360406023550 5ustar mathieumathieujai-core-1.1.4/src/share/classes/com/sun/media/jai/tilecodec/RawTileDecoder.java0000644000175000017500000000611610203035544027246 0ustar mathieumathieu/* * $RCSfile: RawTileDecoder.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:58 $ * $State: Exp $ */package com.sun.media.jai.tilecodec; import java.awt.Point; import java.awt.RenderingHints; import java.awt.image.Raster; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.IOException; import javax.media.jai.JAI; import javax.media.jai.ParameterListDescriptor; import javax.media.jai.tilecodec.TileCodecParameterList; import javax.media.jai.tilecodec.TileDecoderImpl; import javax.media.jai.util.ImagingListener; import com.sun.media.jai.util.ImageUtil; /** * A concrete implementation of the TileDecoderImpl class * for the raw tile codec. */ public class RawTileDecoder extends TileDecoderImpl { /** * Constructs a RawTileDecoder. * RawTileDecoder may throw a * IllegalArgumentException if param's * getParameterListDescriptor() method does not return * the same descriptor as that from the associated * TileCodecDescriptor's * getParameterListDescriptor method for the "tileDecoder" * registry mode. * *

    If param is null, then the default parameter list for decoding * as defined by the associated TileCodecDescriptor's * getDefaultParameters() method will be used for decoding. * * @param input The InputStream to decode data from. * @param param The object containing the tile decoding parameters. * @throws IllegalArgumentException if input is null. * @throws IllegalArgumentException if param is not appropriate. */ public RawTileDecoder(InputStream input, TileCodecParameterList param) { super("raw", input, param); } /** * Returns a Raster that contains the decoded contents * of the InputStream associated with this * TileDecoder. * *

    This method can perform the decoding correctly only when * includesLocationInfo() returns true. * * @throws IOException if an I/O error occurs while reading from the * associated InputStream. * @throws IllegalArgumentException if the associated * TileCodecDescriptor's includesLocationInfo() returns false. */ public Raster decode() throws IOException{ ObjectInputStream ois = new ObjectInputStream(inputStream); try { Object object = ois.readObject(); return TileCodecUtils.deserializeRaster(object); } catch (ClassNotFoundException e) { ImagingListener listener = ImageUtil.getImagingListener((RenderingHints)null); listener.errorOccurred(JaiI18N.getString("ClassNotFound"), e, this, false); // e.printStackTrace(); return null; } finally { ois.close(); } } public Raster decode(Point location) throws IOException{ return decode(); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/tilecodec/JPEGTileDecoderFactory.java0000644000175000017500000001410310203035544030565 0ustar mathieumathieu/* * $RCSfile: JPEGTileDecoderFactory.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:57 $ * $State: Exp $ */package com.sun.media.jai.tilecodec ; import java.io.InputStream; import java.util.Vector; import javax.media.jai.JAI; import javax.media.jai.ParameterListDescriptor; import javax.media.jai.ParameterListDescriptorImpl; import javax.media.jai.remote.NegotiableCapability; import javax.media.jai.remote.NegotiableNumericRange; import javax.media.jai.remote.NegotiableCollection; import javax.media.jai.tilecodec.TileCodecParameterList; import javax.media.jai.tilecodec.TileDecoder; import javax.media.jai.tilecodec.TileDecoderFactory; /** * A factory for creating JPEGTileDecoders. * *

    This class stipulates that the capabilities of the * TileDecoder be specified by implementing the * getDecodingCapability() method. * * @see javax.media.jai.remote.NegotiableCapability */ public class JPEGTileDecoderFactory implements TileDecoderFactory { /** * Creates a JPEGTileDecoder capable of decoding the encoded * data from the given InputStream using the specified * TileCodecParameterList containing the decoding * parameters to be used. * *

    This method can return null if the TileDecoder is not * capable of producing output for the given set of parameters. * For example, if a TileDecoder is only capable of dealing * with a jpeg quality factor of 0.5, and the associated * TileCodecParameterList specifies a quality factor of 0.75, * null should be returned. * *

    It is recommended that the data in the supplied * InputStream not be used as a factor in determining * whether this InputStream can be successfully decoded, * unless the supplied InputStream is known to be rewindable * (i.e. its markSupported() method returns true or it has * additional functionality that allows backward seeking). It is required * that the InputStream contain the same data on * returning from this method as before this method was called. * In other words, the InputStream should only be used as a * discriminator if it can be rewound to its starting position * before returning from this method. Note that wrapping the * incoming InputStream in a PushbackInputStream * and then rewinding the PushbackInputStream before returning * does not rewind the wrapped InputStream. * *

    If the supplied TileCodecParameterList is null, * a default TileCodecParameterList from the * TileCodecDescriptor will be used to create the decoder. * *

    Exceptions thrown by the TileDecoder will be * caught by this method and will not be propagated. * * @param input The InputStream containing the encoded data * to decode. * @param param The parameters to be be used in the decoding process. * @throws IllegalArgumentException if input is null. */ public TileDecoder createDecoder(InputStream input, TileCodecParameterList param) { if(input == null) throw new IllegalArgumentException(JaiI18N.getString("TileDecoder0")); return new JPEGTileDecoder(input, param ) ; } /** * Returns the capabilities of this TileDecoder as a * NegotiableCapability. */ public NegotiableCapability getDecodeCapability() { Vector generators = new Vector(); generators.add(JPEGTileDecoderFactory.class); ParameterListDescriptor jpegPld = JAI.getDefaultInstance().getOperationRegistry().getDescriptor("tileDecoder", "jpeg").getParameterListDescriptor("tileDecoder"); Class paramClasses[] = { NegotiableNumericRange.class, NegotiableCollection.class, // XXX Find a way to create a negotiable representing integer // arrays // integer array, horizontal subsampling // integer array, vertical subsampling // integer array, quantization table mapping // integer array, quantizationTable0 // integer array, quantizationTable1 // integer array, quantizationTable2 // integer array, quantizationTable3 NegotiableNumericRange.class, NegotiableCollection.class, NegotiableCollection.class, NegotiableCollection.class }; String paramNames[] = { "quality", "qualitySet", "restartInterval", "writeImageInfo", "writeTableInfo", "writeJFIFHeader" }; // A collection containing the valid values for a boolean valued // parameters Vector v = new Vector(); v.add(new Boolean(true)); v.add(new Boolean(false)); NegotiableCollection negCollection = new NegotiableCollection(v); NegotiableNumericRange nnr1 = new NegotiableNumericRange( jpegPld.getParamValueRange(paramNames[0])); NegotiableNumericRange nnr2 = new NegotiableNumericRange( jpegPld.getParamValueRange(paramNames[2])); // The default values Object defaults[] = { nnr1, negCollection, nnr2, negCollection, negCollection, negCollection }; NegotiableCapability decodeCap = new NegotiableCapability("tileCodec", "jpeg", generators, new ParameterListDescriptorImpl( null, //descriptor paramNames, paramClasses, defaults, null), // validValues false); // non-preference // Set the Negotiables representing the valid values on the capability decodeCap.setParameter(paramNames[0], nnr1); decodeCap.setParameter(paramNames[1], negCollection); decodeCap.setParameter(paramNames[2], nnr2); decodeCap.setParameter(paramNames[3], negCollection); decodeCap.setParameter(paramNames[4], negCollection); decodeCap.setParameter(paramNames[5], negCollection); return decodeCap; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/tilecodec/com.sun.media.jai.tilecodec.properties0000644000175000017500000000130710203035544033015 0ustar mathieumathieu# # $RCSfile: com.sun.media.jai.tilecodec.properties,v $ # # Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. # # Use is subject to license terms. # # $Revision: 1.1 $ # $Date: 2005-02-11 04:56:59 $ # $State: Exp $ # ClassNotFound=Cannot find the class of the read object. JPEGTileDecoder0=Use decode(Point location) because no location is in input stream. JPEGTileEncoder0=Only can encode 1/3/4 bands raster. JPEGTileEncoder1=Only can encode BYTE raster. TileCodec0=ParameterListDescriptor is not the same as registered. TileCodec1=Class "{0}" is not serializable. TileDecoder0=Inputstream must not be null. TileEncoder0=Outputstream must not be null. TileEncoder1=Raster must not be null. jai-core-1.1.4/src/share/classes/com/sun/media/jai/tilecodec/GZIPTileEncoder.java0000644000175000017500000000374410203035544027304 0ustar mathieumathieu/* * $RCSfile: GZIPTileEncoder.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:57 $ * $State: Exp $ */package com.sun.media.jai.tilecodec ; import java.awt.image.Raster; import java.io.IOException; import java.io.OutputStream; import java.io.ObjectOutputStream; import java.util.zip.GZIPOutputStream ; import javax.media.jai.JAI ; import javax.media.jai.ParameterListDescriptor ; import javax.media.jai.tilecodec.TileEncoderImpl ; import javax.media.jai.tilecodec.TileCodecParameterList ; /** * A concrete implementation of the TileEncoderImpl class * for the gzip tile codec. */ public class GZIPTileEncoder extends TileEncoderImpl { /** * Constructs an GZIPTileEncoder. * * @param output The OutputStream to write encoded data to. * @param param The object containing the tile encoding parameters. * @throws IllegalArgumentException if param is not the appropriate * Class type. * @throws IllegalArgumentException is output is null. */ public GZIPTileEncoder(OutputStream output, TileCodecParameterList param) { super("gzip", output, param) ; } /** * Encodes a Raster and writes the output * to the OutputStream associated with this * TileEncoder. * * @param ras the Raster to encode. * @throws IOException if an I/O error occurs while writing to the * OutputStream. * @throws IllegalArgumentException if ras is null. */ public void encode(Raster ras) throws IOException { if(ras == null) throw new IllegalArgumentException( JaiI18N.getString("TileEncoder1")) ; ObjectOutputStream oos = new ObjectOutputStream(new GZIPOutputStream(outputStream)) ; Object object = TileCodecUtils.serializeRaster(ras); oos.writeObject(object); oos.close(); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/tilecodec/RawTileDecoderFactory.java0000644000175000017500000000753510203035544030604 0ustar mathieumathieu/* * $RCSfile: RawTileDecoderFactory.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:58 $ * $State: Exp $ */package com.sun.media.jai.tilecodec ; import java.io.InputStream; import java.util.Vector; import javax.media.jai.ParameterListDescriptorImpl; import javax.media.jai.remote.NegotiableCapability; import javax.media.jai.tilecodec.TileCodecParameterList ; import javax.media.jai.tilecodec.TileDecoder ; import javax.media.jai.tilecodec.TileDecoderFactory ; /** * A factory for creating RawTileDecoders. * *

    This class stipulates that the capabilities of the * TileDecoder be specified by implementing the * getDecodingCapability() method. * * @see javax.media.jai.remote.NegotiableCapability */ public class RawTileDecoderFactory implements TileDecoderFactory { /** * Creates a RawTileDecoder capable of decoding the encoded * data from the given InputStream using the specified * TileCodecParameterList containing the decoding * parameters to be used. * *

    This method can return null if the TileDecoder is not * capable of producing output for the given set of parameters. * For example, if a TileDecoder is only capable of dealing * with a jpeg quality factor of 0.5, and the associated * TileCodecParameterList specifies a quality factor of 0.75, * null should be returned. * *

    It is recommended that the data in the supplied * InputStream not be used as a factor in determining * whether this InputStream can be successfully decoded, * unless the supplied InputStream is known to be rewindable * (i.e. its markSupported() method returns true or it has * additional functionality that allows backward seeking). It is required * that the InputStream contain the same data on * returning from this method as before this method was called. * In other words, the InputStream should only be used as a * discriminator if it can be rewound to its starting position * before returning from this method. Note that wrapping the * incoming InputStream in a PushbackInputStream * and then rewinding the PushbackInputStream before returning * does not rewind the wrapped InputStream. * *

    If the supplied TileCodecParameterList is null, * a default TileCodecParameterList from the * TileCodecDescriptor will be used to create the decoder. * *

    Exceptions thrown by the TileDecoder will be * caught by this method and will not be propagated. * * @param input The InputStream containing the encoded data * to decode. * @param param The parameters to be be used in the decoding process. * @throws IllegalArgumentException if input is null. */ public TileDecoder createDecoder(InputStream input, TileCodecParameterList param) { if(input == null) throw new IllegalArgumentException(JaiI18N.getString("TileDecoder0")); return (TileDecoder)(new RawTileDecoder(input, param)) ; } /** * Returns the capabilities of this TileDecoder as a * NegotiableCapability. */ public NegotiableCapability getDecodeCapability() { Vector generators = new Vector(); generators.add(RawTileDecoderFactory.class); return new NegotiableCapability("tileCodec", "raw", generators, new ParameterListDescriptorImpl(null, null, null, null, null), false); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/tilecodec/RawTileEncoder.java0000644000175000017500000000502610203035544027257 0ustar mathieumathieu/* * $RCSfile: RawTileEncoder.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:58 $ * $State: Exp $ */package com.sun.media.jai.tilecodec ; import java.awt.image.Raster; import java.io.IOException; import java.io.OutputStream; import java.io.ObjectOutputStream; import javax.media.jai.JAI ; import javax.media.jai.ParameterListDescriptor ; import javax.media.jai.tilecodec.TileCodecParameterList ; import javax.media.jai.tilecodec.TileEncoderImpl ; /** * A concrete implementation of the TileEncoderImpl class * for the raw tile codec. */ public class RawTileEncoder extends TileEncoderImpl { /** * Constructs an RawTileEncoder. Concrete implementations * of TileEncoder may throw an * IllegalArgumentException if the * param's getParameterListDescriptor() method * does not return the same descriptor as that from the associated * TileCodecDescriptor's * getParameterListDescriptor method for the "tileEncoder" * registry mode. * *

    If param is null, then the default parameter list for encoding * as defined by the associated TileCodecDescriptor's * getDefaultParameters() method will be used for encoding. * * @param output The OutputStream to write encoded data to. * @param param The object containing the tile encoding parameters. * @throws IllegalArgumentException if param is not the appropriate * Class type. * @throws IllegalArgumentException is output is null. */ public RawTileEncoder(OutputStream output, TileCodecParameterList param) { super("raw", output, param) ; } /** * Encodes a Raster and writes the output * to the OutputStream associated with this * TileEncoder. * * @param ras the Raster to encode. * @throws IOException if an I/O error occurs while writing to the * OutputStream. * @throws IllegalArgumentException if ras is null. */ public void encode(Raster ras) throws IOException { if(ras == null) throw new IllegalArgumentException( JaiI18N.getString("TileEncoder1")) ; ObjectOutputStream oos = new ObjectOutputStream(outputStream) ; Object object = TileCodecUtils.serializeRaster(ras) ; oos.writeObject(object) ; oos.close() ; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/tilecodec/RawTileEncoderFactory.java0000644000175000017500000000654310203035544030614 0ustar mathieumathieu/* * $RCSfile: RawTileEncoderFactory.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:58 $ * $State: Exp $ */package com.sun.media.jai.tilecodec ; import java.awt.image.SampleModel; import java.io.OutputStream; import java.util.Vector; import javax.media.jai.ParameterListDescriptorImpl; import javax.media.jai.remote.NegotiableCapability; import javax.media.jai.tilecodec.TileCodecParameterList ; import javax.media.jai.tilecodec.TileEncoder ; import javax.media.jai.tilecodec.TileEncoderFactory ; /** * A factory for creating RawTileEncoders. * *

    This class stipulates that the capabilities of the * TileEncoder be specified by implementing the * getEncodingCapability() method. * * @see javax.media.jai.remote.NegotiableCapability */ public class RawTileEncoderFactory implements TileEncoderFactory { /** * Creates a TileEncoder capable of encoding a * Raster with the specified SampleModel * using the specified TileCodecParameterList * containing the encoding parameters to the given OutputStream. * *

    This method can return null if the TileEncoder is not * capable of producing output for the given set of parameters. * For example, if a TileEncoder is only capable of dealing * with a PixelInterleavedSampleModel, and the supplied * SampleModel is not an instance of * PixelInterleavedSampleModel, null should be * returned. The supplied SampleModel should be used to * decide whether it can be encoded by this class, and is not needed * to actually construct a TileEncoder. * *

    If the supplied TileCodecParameterList is null, * a default TileCodecParameterList from the * TileCodecDescriptor will be used to create the encoder. * *

    Exceptions thrown by the TileEncoder * will be caught by this method and will not be propagated. * * @param output The OutputStream to write the encoded * data to. * @param paramList The TileCodecParameterList containing * the encoding parameters. * @param sampleModel The SampleModel of the encoded * Rasters. * @throws IllegalArgumentException if output is null. */ public TileEncoder createEncoder(OutputStream output, TileCodecParameterList paramList, SampleModel sampleModel) { if(output == null) throw new IllegalArgumentException(JaiI18N.getString("TileEncoder0")); return new RawTileEncoder(output, paramList) ; } /** * Returns the capabilities of this TileEncoder as a * NegotiableCapability. */ public NegotiableCapability getEncodeCapability() { Vector generators = new Vector(); generators.add(RawTileEncoderFactory.class); return new NegotiableCapability("tileCodec", "raw", generators, new ParameterListDescriptorImpl(null, null, null, null, null), false); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/tilecodec/JPEGTileDecoder.java0000644000175000017500000001531210203035544027240 0ustar mathieumathieu/* * $RCSfile: JPEGTileDecoder.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:57 $ * $State: Exp $ */package com.sun.media.jai.tilecodec; import java.awt.Point; import java.awt.RenderingHints; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.awt.image.SampleModel; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ByteArrayInputStream; import java.io.IOException; import javax.media.jai.JAI; import javax.media.jai.ParameterListDescriptor; import javax.media.jai.RasterFactory; import javax.media.jai.tilecodec.TileCodecDescriptor; import javax.media.jai.tilecodec.TileCodecParameterList; import javax.media.jai.tilecodec.TileDecoderImpl; import javax.media.jai.util.ImagingListener; import com.sun.image.codec.jpeg.JPEGDecodeParam; import com.sun.image.codec.jpeg.JPEGImageDecoder; import com.sun.image.codec.jpeg.JPEGCodec; import com.sun.image.codec.jpeg.JPEGQTable; import sun.awt.image.codec.JPEGParam; import com.sun.media.jai.util.ImageUtil; /** * A concrete implementation of the TileDecoderImpl class * for the jpeg tile codec. */ public class JPEGTileDecoder extends TileDecoderImpl { /* The associated TileCodecDescriptor */ private TileCodecDescriptor tcd = null; /** * Constructs a JPEGTileDecoder. * JPEGTileDecoder may throw a * IllegalArgumentException if param's * getParameterListDescriptor() method does not return * the same descriptor as that from the associated * TileCodecDescriptor's * getParameterListDescriptor method for the "tileDecoder" * registry mode. * *

    If param is null, then the default parameter list for decoding * as defined by the associated TileCodecDescriptor's * getDefaultParameters() method will be used for decoding. * * @param input The InputStream to decode data from. * @param param The object containing the tile decoding parameters. * @throws IllegalArgumentException if input is null. * @throws IllegalArgumentException if param is not appropriate. */ public JPEGTileDecoder(InputStream input, TileCodecParameterList param) { super("jpeg", input, param); tcd = TileCodecUtils.getTileCodecDescriptor("tileDecoder", "jpeg"); } /** * Returns a Raster that contains the decoded contents * of the InputStream associated with this * TileDecoder. * *

    This method can perform the decoding correctly only when * includesLocationInfoInfo() returns true. * * @throws IOException if an I/O error occurs while reading from the * associated InputStream. * @throws IllegalArgumentException if the associated * TileCodecDescriptor's includesLocationInfoInfo() returns false. */ public Raster decode() throws IOException{ if (!tcd.includesLocationInfo()) throw new IllegalArgumentException( JaiI18N.getString("JPEGTileDecoder0") ); return decode(null); } public Raster decode(Point location) throws IOException{ SampleModel sm = null; byte[] data = null; ObjectInputStream ois = new ObjectInputStream(inputStream); try { // read the quality and qualitySet from the stream paramList.setParameter("quality", ois.readFloat()); paramList.setParameter("qualitySet", ois.readBoolean()); sm = TileCodecUtils.deserializeSampleModel(ois.readObject()); location = (Point)ois.readObject(); data = (byte[]) ois.readObject(); } catch (ClassNotFoundException e) { ImagingListener listener = ImageUtil.getImagingListener((RenderingHints)null); listener.errorOccurred(JaiI18N.getString("ClassNotFound"), e, this, false); // e.printStackTrace(); return null; } finally { ois.close(); } ByteArrayInputStream bais = new ByteArrayInputStream(data); JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(bais); Raster ras = decoder.decodeAsRaster() .createTranslatedChild(location.x, location.y); extractParameters(decoder.getJPEGDecodeParam(), ras.getSampleModel().getNumBands()); // set the original sample model to the decoded raster if (sm != null) { int minX = ras.getMinX(); int minY = ras.getMinY(); int h = ras.getHeight(); int w = ras.getWidth(); double[] buf = ras.getPixels(minX, minY, w, h, (double[])null); ras = RasterFactory.createWritableRaster(sm, new Point(minX, minY)); ((WritableRaster)ras).setPixels(minX, minY, w, h, buf); } return ras; } private void extractParameters(JPEGDecodeParam jdp, int bandNum) { // extract the horizontal subsampling rates int[] horizontalSubsampling = new int[bandNum]; for (int i = 0; i < bandNum; i++) horizontalSubsampling[i] = jdp.getHorizontalSubsampling(i); paramList.setParameter("horizontalSubsampling", horizontalSubsampling); // extract the vertical subsampling rates int[] verticalSubsampling = new int[bandNum]; for (int i = 0; i < bandNum; i++) verticalSubsampling[i] = jdp.getVerticalSubsampling(i); paramList.setParameter("verticalSubsampling", verticalSubsampling); // if the quality is not set, extract the quantization tables from // the stream; otherwise, define them with the default values. if (!paramList.getBooleanParameter("qualitySet")) for (int i = 0; i < 4; i++) { JPEGQTable table = jdp.getQTable(i); paramList.setParameter("quantizationTable"+i, (table == null) ? null : table.getTable()); } else { ParameterListDescriptor pld = paramList.getParameterListDescriptor(); for (int i = 0; i < 4; i++) { paramList.setParameter("quantizationTable"+i, pld.getParamDefaultValue("quantizationTable"+i)); } } // extract the quantizationTableMapping int[] quanTableMapping = new int[bandNum]; for (int i = 0; i < bandNum; i++) quanTableMapping[i] = jdp.getQTableComponentMapping(i); paramList.setParameter("quantizationTableMapping", quanTableMapping); // extract the writeTableInfo and writeImageInfo paramList.setParameter("writeTableInfo", jdp.isTableInfoValid()); paramList.setParameter("writeImageInfo", jdp.isImageInfoValid()); // extract the restart interval paramList.setParameter("restartInterval", jdp.getRestartInterval()); // define writeJFIFHeader by examing the APP0_MARKER is set or not paramList.setParameter("writeJFIFHeader", jdp.getMarker(JPEGDecodeParam.APP0_MARKER)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/tilecodec/GZIPTileDecoder.java0000644000175000017500000000622610203035544027270 0ustar mathieumathieu/* * $RCSfile: GZIPTileDecoder.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:56 $ * $State: Exp $ */package com.sun.media.jai.tilecodec; import java.awt.Point; import java.awt.RenderingHints; import java.awt.image.Raster; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.IOException; import java.util.zip.GZIPInputStream; import javax.media.jai.JAI; import javax.media.jai.ParameterListDescriptor; import javax.media.jai.tilecodec.TileDecoderImpl; import javax.media.jai.tilecodec.TileCodecParameterList; import javax.media.jai.util.ImagingListener; import com.sun.media.jai.util.ImageUtil; /** * A concrete implementation of the TileDecoderImpl class * for the gzip tile codec. */ public class GZIPTileDecoder extends TileDecoderImpl { /** * Constructs a GZIPTileDecoder. * GZIPTileDecoder may throw a * IllegalArgumentException if param's * getParameterListDescriptor() method does not return * the same descriptor as that from the associated * TileCodecDescriptor's * getParameterListDescriptor method for the "tileDecoder" * registry mode. * *

    If param is null, then the default parameter list for decoding * as defined by the associated TileCodecDescriptor's * getDefaultParameters() method will be used for decoding. * * @param input The InputStream to decode data from. * @param param The object containing the tile decoding parameters. * @throws IllegalArgumentException if input is null. * @throws IllegalArgumentException if param is not appropriate. */ public GZIPTileDecoder(InputStream input, TileCodecParameterList param) { super("gzip", input, param); } /** * Returns a Raster that contains the decoded contents * of the InputStream associated with this * TileDecoder. * *

    This method can perform the decoding correctly only when * includesLocationInfo() returns true. * * @throws IOException if an I/O error occurs while reading from the * associated InputStream. * @throws IllegalArgumentException if the associated * TileCodecDescriptor's includesLocationInfo() returns false. */ public Raster decode() throws IOException{ ObjectInputStream ois = new ObjectInputStream(new GZIPInputStream(inputStream)); try { Object object = ois.readObject(); return TileCodecUtils.deserializeRaster(object); } catch (ClassNotFoundException e) { ImagingListener listener = ImageUtil.getImagingListener((RenderingHints)null); listener.errorOccurred(JaiI18N.getString("ClassNotFound"), e, this, false); // e.printStackTrace(); return null; } finally { ois.close(); } } public Raster decode(Point location) throws IOException{ return decode(); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/tilecodec/JPEGTileEncoderFactory.java0000644000175000017500000001406210203035544030603 0ustar mathieumathieu/* * $RCSfile: JPEGTileEncoderFactory.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:57 $ * $State: Exp $ */package com.sun.media.jai.tilecodec ; import java.awt.image.SampleModel ; import java.awt.image.DataBuffer ; import java.io.OutputStream; import java.util.Vector; import javax.media.jai.JAI; import javax.media.jai.ParameterListDescriptor; import javax.media.jai.ParameterListDescriptorImpl; import javax.media.jai.remote.NegotiableCapability; import javax.media.jai.remote.NegotiableNumericRange; import javax.media.jai.remote.NegotiableCollection; import javax.media.jai.tilecodec.TileCodecParameterList ; import javax.media.jai.tilecodec.TileEncoder ; import javax.media.jai.tilecodec.TileEncoderFactory ; /** * A factory for creating JPEGTileEncoders. * *

    This class stipulates that the capabilities of the * TileEncoder be specified by implementing the * getEncodingCapability() method. * * @see javax.media.jai.remote.NegotiableCapability */ public class JPEGTileEncoderFactory implements TileEncoderFactory { /** * Creates a TileEncoder capable of encoding a * Raster with the specified SampleModel * using the specified TileCodecParameterList * containing the encoding parameters to the given OutputStream. * *

    This method can return null if the TileEncoder is not * capable of producing output for the given set of parameters. * For example, if a TileEncoder is only capable of dealing * with a PixelInterleavedSampleModel, and the supplied * SampleModel is not an instance of * PixelInterleavedSampleModel, null should be * returned. The supplied SampleModel should be used to * decide whether it can be encoded by this class, and is not needed * to actually construct a TileEncoder. * *

    If the supplied TileCodecParameterList is null, * a default TileCodecParameterList from the * TileCodecDescriptor will be used to create the encoder. * *

    Exceptions thrown by the TileEncoder * will be caught by this method and will not be propagated. * * @param output The OutputStream to write the encoded * data to. * @param paramList The TileCodecParameterList containing * the encoding parameters. * @param sampleModel The SampleModel of the encoded * Rasters. * @throws IllegalArgumentException if output is null. */ public TileEncoder createEncoder(OutputStream output, TileCodecParameterList paramList, SampleModel sampleModel) { if(output == null) throw new IllegalArgumentException(JaiI18N.getString("TileEncoder0")); int nbands = sampleModel.getNumBands() ; if(nbands != 1 && nbands != 3 && nbands != 4) throw new IllegalArgumentException( JaiI18N.getString("JPEGTileEncoder0")) ; if(sampleModel.getDataType() != DataBuffer.TYPE_BYTE) throw new IllegalArgumentException( JaiI18N.getString("JPEGTileEncoder1")) ; return new JPEGTileEncoder(output, paramList) ; } /** * Returns the capabilities of this TileEncoder as a * NegotiableCapability. */ public NegotiableCapability getEncodeCapability() { Vector generators = new Vector(); generators.add(JPEGTileEncoderFactory.class); ParameterListDescriptor jpegPld = JAI.getDefaultInstance().getOperationRegistry().getDescriptor("tileEncoder", "jpeg").getParameterListDescriptor("tileEncoder"); Class paramClasses[] = { javax.media.jai.remote.NegotiableNumericRange.class, javax.media.jai.remote.NegotiableCollection.class, // XXX How should a negotiable be created to represent int arrays // integer array, horizontal subsampling // integer array, vertical subsampling // integer array, quantization table mapping // integer array, quantizationTable0 // integer array, quantizationTable1 // integer array, quantizationTable2 // integer array, quantizationTable3 javax.media.jai.remote.NegotiableNumericRange.class, javax.media.jai.remote.NegotiableCollection.class, javax.media.jai.remote.NegotiableCollection.class, javax.media.jai.remote.NegotiableCollection.class }; String paramNames[] = { "quality", "qualitySet", "restartInterval", "writeImageInfo", "writeTableInfo", "writeJFIFHeader" }; // A collection containing the valid values for a boolean valued // parameters Vector v = new Vector(); v.add(new Boolean(true)); v.add(new Boolean(false)); NegotiableCollection negCollection = new NegotiableCollection(v); NegotiableNumericRange nnr1 = new NegotiableNumericRange( jpegPld.getParamValueRange(paramNames[0])); NegotiableNumericRange nnr2 = new NegotiableNumericRange( jpegPld.getParamValueRange(paramNames[2])); // The default values Object defaults[] = { nnr1, negCollection, nnr2, negCollection, negCollection, negCollection }; NegotiableCapability encodeCap = new NegotiableCapability("tileCodec", "jpeg", generators, new ParameterListDescriptorImpl( null, // descriptor paramNames, paramClasses, defaults, null), // validValues false); // a non-preference // Set the Negotiables representing the valid values on the capability encodeCap.setParameter(paramNames[0], nnr1); encodeCap.setParameter(paramNames[1], negCollection); encodeCap.setParameter(paramNames[2], nnr2); encodeCap.setParameter(paramNames[3], negCollection); encodeCap.setParameter(paramNames[4], negCollection); encodeCap.setParameter(paramNames[5], negCollection); return encodeCap; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/tilecodec/TileCodecUtils.java0000644000175000017500000000442310203035544027264 0ustar mathieumathieu/* * $RCSfile: TileCodecUtils.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:58 $ * $State: Exp $ */package com.sun.media.jai.tilecodec; import java.awt.image.Raster; import java.awt.image.SampleModel; import java.text.MessageFormat; import javax.media.jai.JAI; import javax.media.jai.tilecodec.TileCodecDescriptor; import javax.media.jai.remote.SerializableState; import javax.media.jai.remote.SerializerFactory; /** * A class containing methods of utility to all TileCodec implementations. */ public class TileCodecUtils { /* Required to I18N compound messages. */ private static MessageFormat formatter = new MessageFormat(""); /** * Get the TileCodecDescriptor associated with the * specified registry mode. */ public static TileCodecDescriptor getTileCodecDescriptor(String registryMode, String formatName) { return (TileCodecDescriptor) JAI.getDefaultInstance().getOperationRegistry() .getDescriptor(registryMode, formatName); } /** Deserialize a Raster from its serialized version */ public static Raster deserializeRaster(Object object) { if (!(object instanceof SerializableState)) return null; SerializableState ss = (SerializableState)object; Class c = ss.getObjectClass(); if (Raster.class.isAssignableFrom(c)) { return (Raster)ss.getObject(); } return null; } /** Deserialize a SampleModel from its serialized version */ public static SampleModel deserializeSampleModel(Object object) { if (!(object instanceof SerializableState)) return null; SerializableState ss = (SerializableState)object; Class c = ss.getObjectClass(); if (SampleModel.class.isAssignableFrom(c)) { return (SampleModel)ss.getObject(); } return null; } /** Serialize a Raster. */ public static Object serializeRaster(Raster ras) { return SerializerFactory.getState(ras, null); } /** Serialize a SampleModel. */ public static Object serializeSampleModel(SampleModel sm) { return SerializerFactory.getState(sm, null); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/tilecodec/GZIPTileEncoderFactory.java0000644000175000017500000000667210203035544030637 0ustar mathieumathieu/* * $RCSfile: GZIPTileEncoderFactory.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:57 $ * $State: Exp $ */package com.sun.media.jai.tilecodec ; import java.awt.image.SampleModel; import java.io.OutputStream; import java.util.Vector; import java.util.zip.GZIPOutputStream ; import javax.media.jai.ParameterListDescriptorImpl; import javax.media.jai.remote.NegotiableCapability; import javax.media.jai.tilecodec.TileCodecParameterList ; import javax.media.jai.tilecodec.TileEncoder ; import javax.media.jai.tilecodec.TileEncoderFactory ; /** * A factory for creating GZIPTileEncoders. * *

    This class stipulates that the capabilities of the * TileEncoder be specified by implementing the * getEncodingCapability() method. * * @see javax.media.jai.remote.NegotiableCapability */ public class GZIPTileEncoderFactory implements TileEncoderFactory { /** * Creates a TileEncoder capable of encoding a * Raster with the specified SampleModel * using the specified TileCodecParameterList * containing the encoding parameters to the given OutputStream. * *

    This method can return null if the TileEncoder is not * capable of producing output for the given set of parameters. * For example, if a TileEncoder is only capable of dealing * with a PixelInterleavedSampleModel, and the supplied * SampleModel is not an instance of * PixelInterleavedSampleModel, null should be * returned. The supplied SampleModel should be used to * decide whether it can be encoded by this class, and is not needed * to actually construct a TileEncoder. * *

    If the supplied TileCodecParameterList is null, * a default TileCodecParameterList from the * TileCodecDescriptor will be used to create the encoder. * *

    Exceptions thrown by the TileEncoder * will be caught by this method and will not be propagated. * * @param output The OutputStream to write the encoded * data to. * @param paramList The TileCodecParameterList containing * the encoding parameters. * @param sampleModel The SampleModel of the encoded * Rasters. * @throws IllegalArgumentException if output is null. */ public TileEncoder createEncoder(OutputStream output, TileCodecParameterList paramList, SampleModel sampleModel) { if(output == null) throw new IllegalArgumentException( JaiI18N.getString("TileEncoder0") ); return new GZIPTileEncoder(output, paramList) ; } /** * Returns the capabilities of this TileEncoder as a * NegotiableCapability. */ public NegotiableCapability getEncodeCapability() { Vector generators = new Vector(); generators.add(GZIPTileEncoderFactory.class); return new NegotiableCapability("tileCodec", "gzip", generators, new ParameterListDescriptorImpl(null, null, null, null, null), false); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/tilecodec/JaiI18N.java0000644000175000017500000000076010203035544025513 0ustar mathieumathieu/* * $RCSfile: JaiI18N.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:58 $ * $State: Exp $ */ package com.sun.media.jai.tilecodec ; import com.sun.media.jai.util.PropertyUtil; class JaiI18N { static String packageName = "com.sun.media.jai.tilecodec" ; public static String getString(String key) { return PropertyUtil.getString(packageName, key); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/tilecodec/GZIPTileDecoderFactory.java0000644000175000017500000000762410203035544030623 0ustar mathieumathieu/* * $RCSfile: GZIPTileDecoderFactory.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:57 $ * $State: Exp $ */package com.sun.media.jai.tilecodec ; import java.io.InputStream; import java.util.Vector; import java.util.zip.GZIPInputStream; import javax.media.jai.ParameterListDescriptorImpl; import javax.media.jai.remote.NegotiableCapability; import javax.media.jai.tilecodec.TileDecoder ; import javax.media.jai.tilecodec.TileDecoderFactory ; import javax.media.jai.tilecodec.TileCodecParameterList ; /** * A factory for creating GZIPTileDecoders. * *

    This class stipulates that the capabilities of the * TileDecoder be specified by implementing the * getDecodingCapability() method. * * @see javax.media.jai.remote.NegotiableCapability */ public class GZIPTileDecoderFactory implements TileDecoderFactory { /** * Creates a GZIPTileDecoder capable of decoding the encoded * data from the given InputStream using the specified * TileCodecParameterList containing the decoding * parameters to be used. * *

    This method can return null if the TileDecoder is not * capable of producing output for the given set of parameters. * For example, if a TileDecoder is only capable of dealing * with a jpeg quality factor of 0.5, and the associated * TileCodecParameterList specifies a quality factor of 0.75, * null should be returned. * *

    It is recommended that the data in the supplied * InputStream not be used as a factor in determining * whether this InputStream can be successfully decoded, * unless the supplied InputStream is known to be rewindable * (i.e. its markSupported() method returns true or it has * additional functionality that allows backward seeking). It is required * that the InputStream contain the same data on * returning from this method as before this method was called. * In other words, the InputStream should only be used as a * discriminator if it can be rewound to its starting position * before returning from this method. Note that wrapping the * incoming InputStream in a PushbackInputStream * and then rewinding the PushbackInputStream before returning * does not rewind the wrapped InputStream. * *

    If the supplied TileCodecParameterList is null, * a default TileCodecParameterList from the * TileCodecDescriptor will be used to create the decoder. * *

    Exceptions thrown by the TileDecoder will be * caught by this method and will not be propagated. * * @param input The InputStream containing the encoded data * to decode. * @param param The parameters to be be used in the decoding process. * @throws IllegalArgumentException if input is null. */ public TileDecoder createDecoder(InputStream input, TileCodecParameterList param) { if(input == null) throw new IllegalArgumentException(JaiI18N.getString("TileDecoder0")); return new GZIPTileDecoder(input, param) ; } /** * Returns the capabilities of this TileDecoder as a * NegotiableCapability. */ public NegotiableCapability getDecodeCapability() { Vector generators = new Vector(); generators.add(GZIPTileDecoderFactory.class); return new NegotiableCapability("tileCodec", "gzip", generators, new ParameterListDescriptorImpl(null, null, null, null, null), false); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/tilecodec/JPEGTileEncoder.java0000644000175000017500000001347010203035544027255 0ustar mathieumathieu/* * $RCSfile: JPEGTileEncoder.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:57 $ * $State: Exp $ */package com.sun.media.jai.tilecodec ; import java.awt.Point; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.awt.image.SampleModel; import java.io.OutputStream; import java.io.ObjectOutputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.text.MessageFormat; import javax.media.jai.JAI ; import javax.media.jai.ParameterListDescriptor ; import javax.media.jai.RasterFactory ; import javax.media.jai.tilecodec.TileCodecDescriptor ; import javax.media.jai.tilecodec.TileCodecParameterList ; import javax.media.jai.tilecodec.TileEncoderImpl ; import com.sun.image.codec.jpeg.JPEGEncodeParam ; import com.sun.image.codec.jpeg.JPEGImageEncoder ; import com.sun.image.codec.jpeg.JPEGCodec ; import com.sun.image.codec.jpeg.JPEGQTable ; import sun.awt.image.codec.JPEGParam ; /** * A concrete implementation of the TileEncoderImpl class * for the jpeg tile codec. */ public class JPEGTileEncoder extends TileEncoderImpl { /* The associated TileCodecDescriptor */ private TileCodecDescriptor tcd = null ; /** * Constructs an JPEGTileEncoder. Concrete implementations * of TileEncoder may throw an * IllegalArgumentException if the * param's getParameterListDescriptor() method * does not return the same descriptor as that from the associated * TileCodecDescriptor's * getParameterListDescriptor method for the "tileEncoder" * registry mode. * *

    If param is null, then the default parameter list for encoding * as defined by the associated TileCodecDescriptor's * getDefaultParameters() method will be used for encoding. * * @param output The OutputStream to write encoded data to. * @param param The object containing the tile encoding parameters. * @throws IllegalArgumentException if param is not the appropriate * Class type. * @throws IllegalArgumentException is output is null. */ public JPEGTileEncoder(OutputStream output, TileCodecParameterList param) { super("jpeg", output, param) ; tcd = TileCodecUtils.getTileCodecDescriptor("tileEncoder", "jpeg"); } /** * Encodes a Raster and writes the output * to the OutputStream associated with this * TileEncoder. * * @param ras the Raster to encode. * @throws IOException if an I/O error occurs while writing to the * OutputStream. * @throws IllegalArgumentException if ras is null. */ public void encode(Raster ras) throws IOException { if(ras == null) throw new IllegalArgumentException( JaiI18N.getString("TileEncoder1")) ; ByteArrayOutputStream baos = new ByteArrayOutputStream() ; SampleModel sm = ras.getSampleModel() ; JPEGEncodeParam j2dEP = convertToJ2DJPEGEncodeParam(paramList, sm) ; ((JPEGParam)j2dEP).setWidth(ras.getWidth()) ; ((JPEGParam)j2dEP).setHeight(ras.getHeight()) ; JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(baos, j2dEP) ; encoder.encode(ras) ; byte[] data = baos.toByteArray() ; ObjectOutputStream oos = new ObjectOutputStream(outputStream) ; oos.writeFloat(paramList.getFloatParameter("quality")); oos.writeBoolean(paramList.getBooleanParameter("qualitySet")); oos.writeObject(TileCodecUtils.serializeSampleModel(sm)); Point location = new Point( ras.getMinX(), ras.getMinY() ) ; oos.writeObject( location ) ; oos.writeObject( data ) ; oos.close() ; } private JPEGEncodeParam convertToJ2DJPEGEncodeParam( TileCodecParameterList paramList, SampleModel sm) { if(sm == null) return null ; int nbands = sm.getNumBands() ; JPEGParam j2dJP = createDefaultJ2DJPEGEncodeParam(nbands) ; int[] hSubSamp = (int[])paramList.getObjectParameter("horizontalSubsampling") ; int[] vSubSamp = (int[])paramList.getObjectParameter("verticalSubsampling") ; int[] qTabSlot = (int[])paramList.getObjectParameter("quantizationTableMapping") ; for(int i=0; iOpImage.mapDestRect(). * * @param dst The destination image to be tested. * @param dstRect The rectangle of interest within dst. */ public static void testOpImage(OpImage dst, Rectangle dstRect) { for (int i = 0; i < dst.getNumSources(); i++) { PlanarImage src = dst.getSourceImage(i); Rectangle srcRect = dst.mapDestRect(dstRect, i); String message = "Source " + (i+1) + ":"; printPixels(message, src, srcRect); } printPixels("Dest:", dst, dstRect); } private static long benchmarkOpImage(OpImage img, int loop) { img.setTileCache(null); int minX = img.getMinTileX(); int maxX = img.getMaxTileX(); int minY = img.getMinTileY(); int maxY = img.getMaxTileY(); long total = 0; for (int i = 0; i < loop; i++) { for (int y = minY; y <= maxY; y++) { for (int x = minX; x <= maxX; x++) { // System.gc(); long start = System.currentTimeMillis(); img.getTile(x, y); long end = System.currentTimeMillis(); // System.gc(); int diff = (int)(end-start); total += diff; } } } return total; } public static long timeOpImage(OpImage img, int loops) { long total = benchmarkOpImage(img,loops); int w = img.getWidth(); int h = img.getHeight(); SampleModel sm = img.getSampleModel(); double time = (total)/1000.0; int width = img.getWidth(); int height = img.getHeight(); System.out.print("\tLoops : " + loops); System.out.print("\tTime : " + time); System.out.println("\tMpixels/sec : " +((double)loops/1000000.0)*((double)width*(double)height/time)); return total; } public static void performDiagnostics(String classname, String args[]) { int dataTypes[] = allDataTypes; boolean verbose = false; int width = 512; int height = 512; int bands = 1; for (int i = 0; i < args.length; i++) { if (args[i].equals("-fast")) { int dt[] = {0}; dataTypes = dt; } else if (args[i].equals("-verbose")) { verbose = true; } } runDiagnostics(classname,dataTypes,width,height,bands,verbose); } public static void runDiagnostics(String classname, int dataTypes[], int width, int height, int bands, boolean verbose) { System.out.println("Performing Diagnostics for " + classname); for (int i = 0; i < dataTypes.length; i++) { OpImageTester oit = new OpImageTester(512,512,1,dataTypes[i]); Class clazz = null; Method createTestMethod = null; System.out.print(" Testing DataBuffer.TYPE_" + dataTypeNames[dataTypes[i]] + " "); System.out.println(" Size : " + width + "x" + height + " by " + bands + " bands"); try { clazz = Class.forName(classname); Class params[] = { Class.forName("com.sun.media.jai.opimage.OpImageTester")}; createTestMethod = clazz.getMethod("createTestImage",params); Object methodArgs[] = {oit}; OpImage o = (OpImage)createTestMethod.invoke(null,methodArgs); long total = benchmarkOpImage(o,10); total = benchmarkOpImage(o,10); int loops = (int)(15000/((double)total/10.0)); timeOpImage(o,loops); } catch (Exception e) { if (verbose) { e.printStackTrace(); } System.err.println("\tException thrown"); } } System.out.println("Finished Diagnostics\n"); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/test/RandomOpImage.java0000644000175000017500000001017210207233360026114 0ustar mathieumathieu/* * $RCSfile: RandomOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-02-24 02:07:44 $ * $State: Exp $ */ package com.sun.media.jai.test; import java.awt.Frame; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.util.Map; import javax.media.jai.ImageLayout; import javax.media.jai.PlanarImage; import javax.media.jai.RasterFactory; import javax.media.jai.SourcelessOpImage; import javax.media.jai.widget.ScrollingImagePanel; /** Defines an OpImage with random pixel values for testing purposes. */ final class RandomOpImage extends SourcelessOpImage { private int maxValue; private int transtype; public RandomOpImage(int minX, int minY, int width, int height, SampleModel sampleModel, Map configuration, ImageLayout layout) { super(layout, configuration, sampleModel, minX, minY, width, height); switch (this.transtype = sampleModel.getTransferType()) { case DataBuffer.TYPE_BYTE: maxValue = 255; break; case DataBuffer.TYPE_USHORT: maxValue = 65535; break; case DataBuffer.TYPE_SHORT: maxValue = Short.MAX_VALUE; break; case DataBuffer.TYPE_INT: maxValue = Integer.MAX_VALUE; break; } /* * Fill in all pixel values so that when other OpImages uses this * one for performance calculation it doesn't take away time. */ for (int y = getMinTileY(); y <= getMaxTileY(); y++) { for (int x = getMinTileX(); x <= getMaxTileX(); x++) { getTile(x, y); } } } public Raster computeTile(int tileX, int tileY) { int orgX = tileXToX(tileX); int orgY = tileYToY(tileY); WritableRaster dst = RasterFactory.createWritableRaster( sampleModel, new Point(orgX, orgY)); Rectangle rect = new Rectangle(orgX, orgY, sampleModel.getWidth(), sampleModel.getHeight()); rect = rect.intersection(getBounds()); int numBands = sampleModel.getNumBands(); int p[] = new int[numBands]; for (int y = rect.y; y < (rect.y + rect.height); y++) { for (int x = rect.x; x < (rect.x + rect.width); x++) { for (int i = 0; i < numBands; i++) { switch ( transtype ) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: // For unsigned data, limint the random number to [0, 1] // and the result to [0, MAX_VALUE]; p[i] = (int)(maxValue * Math.random()); break; default: // For signed data, limint the random number to [-1, 1] // and the result to [MIN_VALUE, MAX_VALUE]; p[i] = (int)((maxValue+1.0F) * (Math.random() - 0.5F) * 2.0F); } } dst.setPixel(x, y, p); } } return dst; } public static void main (String args[]) { ImageLayout layout = new ImageLayout(); layout.setTileWidth(64); layout.setTileHeight(64); layout.setColorModel(OpImageTester.createComponentColorModel()); PlanarImage image = new RandomOpImage(0, 0, 100, 100, RasterFactory.createPixelInterleavedSampleModel( DataBuffer.TYPE_BYTE, 64, 64, 3), null, layout); ScrollingImagePanel panel = new ScrollingImagePanel(image, 120, 120); Frame window = new Frame("JAI RandomOpImage Test"); window.add(panel); window.pack(); window.show(); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/test/RampOpImage.java0000644000175000017500000000347510207233360025603 0ustar mathieumathieu/* * $RCSfile: RampOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-02-24 02:07:44 $ * $State: Exp $ */ package com.sun.media.jai.test; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.Raster; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.util.Map; import javax.media.jai.ImageLayout; import javax.media.jai.RasterFactory; import javax.media.jai.SourcelessOpImage; /** Defines a ramp image for testing purpose. */ final class RampOpImage extends SourcelessOpImage { public RampOpImage(int minX, int minY, int width, int height, SampleModel sampleModel, Map configuration, ImageLayout layout) { super(layout, configuration, sampleModel, minX, minY, width, height); } public Raster computeTile(int tileX, int tileY) { int orgX = tileXToX(tileX); int orgY = tileYToY(tileY); WritableRaster dst = RasterFactory.createWritableRaster( sampleModel, new Point(orgX, orgY)); Rectangle rect = new Rectangle(orgX, orgY, sampleModel.getWidth(), sampleModel.getHeight()); rect = rect.intersection(getBounds()); int numBands = sampleModel.getNumBands(); int p[] = new int[numBands]; for (int y = rect.y; y < (rect.y + rect.height); y++) { for (int x = rect.x; x < (rect.x + rect.width); x++) { int value = Math.max(x & 0xFF, y & 0xFF); for (int i = 0; i < numBands; i++) { p[i] = value; } dst.setPixel(x, y, p); } } return dst; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/0000755000175000017500000000000011633360404023552 5ustar mathieumathieujai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/PNMImageDecoder.java0000644000175000017500000002627110472445724027321 0ustar mathieumathieu/* * $RCSfile: PNMImageDecoder.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.3 $ * $Date: 2006-08-22 00:12:04 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl; import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Transparency; import java.awt.color.ColorSpace; import java.awt.image.ComponentColorModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferInt; import java.awt.image.DataBufferUShort; import java.awt.image.IndexColorModel; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.io.IOException; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.ImageDecoder; import com.sun.media.jai.codec.ImageDecoderImpl; import com.sun.media.jai.codec.ImageDecodeParam; import com.sun.media.jai.codec.SeekableStream; import com.sun.media.jai.codecimpl.util.RasterFactory; import com.sun.media.jai.codecimpl.ImagingListenerProxy; import com.sun.media.jai.codecimpl.util.ImagingException; /** * @since EA2 */ public class PNMImageDecoder extends ImageDecoderImpl { public PNMImageDecoder(SeekableStream input, ImageDecodeParam param) { super(input, param); } public RenderedImage decodeAsRenderedImage(int page) throws IOException { if (page != 0) { throw new IOException(JaiI18N.getString("PNMImageDecoder5")); } try { return new PNMImage(input); } catch(Exception e) { throw CodecUtils.toIOException(e); } } } class PNMImage extends SimpleRenderedImage { private static final int PBM_ASCII = '1'; private static final int PGM_ASCII = '2'; private static final int PPM_ASCII = '3'; private static final int PBM_RAW = '4'; private static final int PGM_RAW = '5'; private static final int PPM_RAW = '6'; private static final int LINE_FEED = 0x0A; private SeekableStream input; private byte[] lineSeparator; /** File variant: PBM/PGM/PPM, ASCII/RAW. */ private int variant; /** Maximum pixel value. */ private int maxValue; /** Raster that is the entire image. */ private Raster theTile; private int numBands; private int dataType; /** * Construct a PNMImage. * * @param input The SeekableStream for the PNM file. */ public PNMImage(SeekableStream input) { theTile = null; this.input = input; String ls = (String)java.security.AccessController.doPrivileged( new sun.security.action.GetPropertyAction("line.separator")); lineSeparator = ls.getBytes(); // Read file header. try { if (this.input.read() != 'P') { // magic number throw new RuntimeException(JaiI18N.getString("PNMImageDecoder0")); } variant = this.input.read(); // file variant if ((variant < PBM_ASCII) || (variant > PPM_RAW)) { throw new RuntimeException(JaiI18N.getString("PNMImageDecoder1")); } width = readInteger(this.input); // width height = readInteger(this.input); // height if (variant == PBM_ASCII || variant == PBM_RAW) { maxValue = 1; } else { maxValue = readInteger(this.input); // maximum value } } catch (IOException e) { String message = JaiI18N.getString("PNMImageDecoder6"); sendExceptionToListener(message, e); // e.printStackTrace(); // throw new RuntimeException(JaiI18N.getString("PNMImageDecoder2")); } // The RAWBITS format can only support byte image data, which means // maxValue should be less than 0x100. In case there's a conflict, // base the maxValue on variant. if (isRaw(variant) && maxValue >= 0x100) { maxValue = 0xFF; } // Reset image layout so there's only one tile. tileWidth = width; tileHeight = height; // Determine number of bands: pixmap (PPM) is 3 bands, // bitmap (PBM) and greymap (PGM) are 1 band. if (variant == PPM_ASCII || variant == PPM_RAW) { this.numBands = 3; } else { this.numBands = 1; } // Determine data type based on maxValue. if (maxValue < 0x100) { this.dataType = DataBuffer.TYPE_BYTE; } else if (maxValue < 0x10000) { this.dataType = DataBuffer.TYPE_USHORT; } else { this.dataType = DataBuffer.TYPE_INT; } // Choose an appropriate SampleModel. if ((variant == PBM_ASCII) || (variant == PBM_RAW)) { // Each pixel takes 1 bit, pack 8 pixels into a byte. sampleModel = new MultiPixelPackedSampleModel( DataBuffer.TYPE_BYTE, width, height, 1); colorModel = ImageCodec.createGrayIndexColorModel(sampleModel, false); } else { int[] bandOffsets = numBands == 1 ? new int[] {0} : new int[] {0, 1, 2}; sampleModel = RasterFactory.createPixelInterleavedSampleModel( dataType, tileWidth, tileHeight, numBands, tileWidth*numBands, bandOffsets); colorModel = ImageCodec.createComponentColorModel(sampleModel); } } /** Returns true if file variant is raw format, false if ASCII. */ private boolean isRaw(int v) { return (v >= PBM_RAW); } /** Reads the next integer. */ private int readInteger(SeekableStream in) throws IOException { int ret = 0; boolean foundDigit = false; int b; while ((b = in.read()) != -1) { char c = (char)b; if (Character.isDigit(c)) { ret = ret * 10 + Character.digit(c, 10); foundDigit = true; } else { if (c == '#') { // skip to the end of comment line int length = lineSeparator.length; while ((b = in.read()) != -1) { boolean eol = false; for (int i = 0; i < length; i++) { if (b == lineSeparator[i]) { eol = true; break; } } if (eol) { break; } } if (b == -1) { break; } } if (foundDigit) { break; } } } return ret; } private Raster computeTile(int tileX, int tileY) { // Create a new tile. Point org = new Point(tileXToX(tileX), tileYToY(tileY)); WritableRaster tile = Raster.createWritableRaster(sampleModel, org); Rectangle tileRect = tile.getBounds(); // There should only be one tile. try { switch (variant) { case PBM_ASCII: case PBM_RAW: // SampleModel for these cases should be MultiPixelPacked. DataBuffer dataBuffer = tile.getDataBuffer(); if (isRaw(variant)) { // Read the entire image. byte[] buf = ((DataBufferByte)dataBuffer).getData(); input.readFully(buf, 0, buf.length); } else { // Read 8 rows at a time byte[] pixels = new byte[8*width]; int offset = 0; for (int row = 0; row < tileHeight; row += 8) { int rows = Math.min(8, tileHeight - row); int len = (rows*width + 7)/8; for (int i = 0; i < rows*width; i++) { pixels[i] = (byte)readInteger(input); } sampleModel.setDataElements(tileRect.x, row, tileRect.width, rows, pixels, dataBuffer); } } break; case PGM_ASCII: case PGM_RAW: case PPM_ASCII: case PPM_RAW: // SampleModel for these cases should be PixelInterleaved. int size = width*height*numBands; switch (dataType) { case DataBuffer.TYPE_BYTE: DataBufferByte bbuf = (DataBufferByte)tile.getDataBuffer(); byte[] byteArray = bbuf.getData(); if (isRaw(variant)) { input.readFully(byteArray); } else { for (int i = 0; i < size; i++) { byteArray[i] = (byte)readInteger(input); } } break; case DataBuffer.TYPE_USHORT: DataBufferUShort sbuf = (DataBufferUShort)tile.getDataBuffer(); short[] shortArray = sbuf.getData(); for (int i = 0; i < size; i++) { shortArray[i] = (short)readInteger(input); } break; case DataBuffer.TYPE_INT: DataBufferInt ibuf = (DataBufferInt)tile.getDataBuffer(); int[] intArray = ibuf.getData(); for (int i = 0; i < size; i++) { intArray[i] = readInteger(input); } break; } break; } // Close the PNM stream and release system resources. input.close(); } catch (IOException e) { String message = JaiI18N.getString("PNMImageDecoder7"); sendExceptionToListener(message, e); // e.printStackTrace(); // throw new RuntimeException(JaiI18N.getString("PNMImageDecoder3")); } return tile; } public synchronized Raster getTile(int tileX, int tileY) { if ((tileX != 0) || (tileY != 0)) { throw new IllegalArgumentException(JaiI18N.getString("PNMImageDecoder4")); } if (theTile == null) { theTile = computeTile(tileX, tileY); } return theTile; } public void dispose() { theTile = null; } private void sendExceptionToListener(String message, Exception e) { ImagingListenerProxy.errorOccurred(message, new ImagingException(message, e), this, false); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/SingleTileRenderedImage.java0000644000175000017500000000300010203035544031055 0ustar mathieumathieu/* * $RCSfile: SingleTileRenderedImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:38 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl; import java.awt.image.Raster; import java.awt.image.ColorModel; import java.awt.image.SampleModel; /** * A simple class that provides RenderedImage functionality * given a Raster and a ColorModel. */ public class SingleTileRenderedImage extends SimpleRenderedImage { Raster ras; /** * Constructs a SingleTileRenderedImage based on a Raster * and a ColorModel. * * @param ras A Raster that will define tile (0, 0) of the image. * @param cm A ColorModel that will serve as the image's * ColorModel. */ public SingleTileRenderedImage(Raster ras, ColorModel colorModel) { this.ras = ras; this.tileGridXOffset = this.minX = ras.getMinX(); this.tileGridYOffset = this.minY = ras.getMinY(); this.tileWidth = this.width = ras.getWidth(); this.tileHeight = this.height = ras.getHeight(); this.sampleModel = ras.getSampleModel(); this.colorModel = colorModel; } /** * Returns the image's Raster as tile (0, 0). */ public Raster getTile(int tileX, int tileY) { if (tileX != 0 || tileY != 0) { throw new IllegalArgumentException(JaiI18N.getString("SingleTileRenderedImage0")); } return ras; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/GIFCodec.java0000644000175000017500000000462410203035544025762 0ustar mathieumathieu/* * $RCSfile: GIFCodec.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:36 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl; import java.awt.image.RenderedImage; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.ImageDecoder; import com.sun.media.jai.codec.ImageDecodeParam; import com.sun.media.jai.codec.ImageEncoder; import com.sun.media.jai.codec.ImageEncodeParam; import com.sun.media.jai.codec.SeekableStream; // import com.sun.media.jai.codec.GIFEncodeParam; /** * @since EA3 */ public final class GIFCodec extends ImageCodec { public GIFCodec() {} public String getFormatName() { return "gif"; } public Class getEncodeParamClass() { return Object.class; } public Class getDecodeParamClass() { return Object.class; } public boolean canEncodeImage(RenderedImage im, ImageEncodeParam param) { return false; } protected ImageEncoder createImageEncoder(OutputStream dst, ImageEncodeParam param) { /* GIFEncodeParam p = null; if (param != null) { p = (GIFEncodeParam)param; } return new GIFImageEncoder(dst, p); */ return null; } protected ImageDecoder createImageDecoder(InputStream src, ImageDecodeParam param) { return new GIFImageDecoder(src, param); } protected ImageDecoder createImageDecoder(File src, ImageDecodeParam param) throws IOException { return new GIFImageDecoder(new FileInputStream(src), null); } protected ImageDecoder createImageDecoder(SeekableStream src, ImageDecodeParam param) { return new GIFImageDecoder(src, param); } public int getNumHeaderBytes() { return 4; } public boolean isFormatRecognized(byte[] header) { return ((header[0] == 'G') && (header[1] == 'I') && (header[2] == 'F') && (header[3] == '8')); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/TIFFImageEncoder.java0000644000175000017500000021745410377166702027436 0ustar mathieumathieu/* * $RCSfile: TIFFImageEncoder.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.3 $ * $Date: 2006-02-22 23:03:30 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; import java.io.RandomAccessFile; import java.awt.Point; import java.awt.Rectangle; import java.awt.color.ColorSpace; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.ComponentSampleModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.IndexColorModel; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.util.ArrayList; import java.util.Iterator; import java.util.SortedSet; import java.util.TreeSet; import java.util.zip.Deflater; import com.sun.media.jai.codec.ImageEncoderImpl; import com.sun.media.jai.codec.ImageEncodeParam; import com.sun.media.jai.codec.JPEGEncodeParam; import com.sun.media.jai.codec.SeekableOutputStream; import com.sun.media.jai.codec.TIFFEncodeParam; import com.sun.media.jai.codec.TIFFField; import com.sun.media.jai.codecimpl.util.RasterFactory; /** * A baseline TIFF writer. The writer outputs TIFF images in either Bilevel, * Greyscale, Palette color or Full Color modes. * * @since EA4 */ public class TIFFImageEncoder extends ImageEncoderImpl { // Image Types private static final int TIFF_UNSUPPORTED = -1; private static final int TIFF_BILEVEL_WHITE_IS_ZERO = 0; private static final int TIFF_BILEVEL_BLACK_IS_ZERO = 1; private static final int TIFF_GRAY = 2; private static final int TIFF_PALETTE = 3; private static final int TIFF_RGB = 4; private static final int TIFF_CMYK = 5; private static final int TIFF_YCBCR = 6; private static final int TIFF_CIELAB = 7; private static final int TIFF_GENERIC = 8; // Compression types private static final int COMP_NONE = TIFFEncodeParam.COMPRESSION_NONE; private static final int COMP_GROUP3_1D = TIFFEncodeParam.COMPRESSION_GROUP3_1D; private static final int COMP_GROUP3_2D = TIFFEncodeParam.COMPRESSION_GROUP3_2D; private static final int COMP_GROUP4 = TIFFEncodeParam.COMPRESSION_GROUP4; private static final int COMP_JPEG_TTN2 = TIFFEncodeParam.COMPRESSION_JPEG_TTN2; private static final int COMP_PACKBITS = TIFFEncodeParam.COMPRESSION_PACKBITS; private static final int COMP_DEFLATE = TIFFEncodeParam.COMPRESSION_DEFLATE; // Incidental tags private static final int TIFF_JPEG_TABLES = 347; private static final int TIFF_YCBCR_SUBSAMPLING = 530; private static final int TIFF_YCBCR_POSITIONING = 531; private static final int TIFF_REF_BLACK_WHITE = 532; // ExtraSamples types private static final int EXTRA_SAMPLE_UNSPECIFIED = 0; private static final int EXTRA_SAMPLE_ASSOCIATED_ALPHA = 1; private static final int EXTRA_SAMPLE_UNASSOCIATED_ALPHA = 2; // Default values private static final int DEFAULT_ROWS_PER_STRIP = 8; // Little endian flag private boolean isLittleEndian = false; private static final char[] intsToChars(int[] intArray) { int arrayLength = intArray.length; char[] charArray = new char[arrayLength]; for(int i = 0; i < arrayLength; i++) { charArray[i] = (char)(intArray[i]&0x0000ffff); } return charArray; } public TIFFImageEncoder(OutputStream output, ImageEncodeParam param) { super(output, param); if (this.param == null) { this.param = new TIFFEncodeParam(); } } /** * Encodes a RenderedImage and writes the output to the * OutputStream associated with this ImageEncoder. */ public void encode(RenderedImage im) throws IOException { // Get the encoding parameters. TIFFEncodeParam encodeParam = (TIFFEncodeParam)param; // Set the byte order flag before any data are written. isLittleEndian = encodeParam.getLittleEndian(); // Write the file header (8 bytes). writeFileHeader(); Iterator iter = encodeParam.getExtraImages(); if(iter != null) { int ifdOffset = 8; RenderedImage nextImage = im; TIFFEncodeParam nextParam = encodeParam; boolean hasNext; do { hasNext = iter.hasNext(); ifdOffset = encode(nextImage, nextParam, ifdOffset, !hasNext); if(hasNext) { Object obj = iter.next(); if(obj instanceof RenderedImage) { nextImage = (RenderedImage)obj; nextParam = encodeParam; } else if(obj instanceof Object[]) { Object[] o = (Object[])obj; nextImage = (RenderedImage)o[0]; nextParam = (TIFFEncodeParam)o[1]; } } } while(hasNext); } else { encode(im, encodeParam, 8, true); } } private int encode(RenderedImage im, TIFFEncodeParam encodeParam, int ifdOffset, boolean isLast) throws IOException { // Cannot store a packed byte image directly so reformat it. if(CodecUtils.isPackedByteImage(im)) { // Get the source ColorModel. ColorModel sourceCM = im.getColorModel(); // Create an equivalent ComponentColorModel. ColorModel destCM = RasterFactory.createComponentColorModel( DataBuffer.TYPE_BYTE, sourceCM.getColorSpace(), sourceCM.hasAlpha(), sourceCM.isAlphaPremultiplied(), sourceCM.getTransparency()); // Create a raster which can contain the entire source. Point origin = new Point(im.getMinX(), im.getMinY()); WritableRaster raster = Raster.createWritableRaster( destCM.createCompatibleSampleModel(im.getWidth(), im.getHeight()), origin); // Copy the source data. raster.setRect(im.getData()); // Replace the source reference with the new image. im = new SingleTileRenderedImage(raster, destCM); } // Currently all images are stored uncompressed. int compression = encodeParam.getCompression(); // Get tiled output preference. boolean isTiled = encodeParam.getWriteTiled(); // Set bounds. int minX = im.getMinX(); int minY = im.getMinY(); int width = im.getWidth(); int height = im.getHeight(); // Get SampleModel. SampleModel sampleModel = im.getSampleModel(); // Retrieve and verify sample size. int sampleSize[] = sampleModel.getSampleSize(); for(int i = 1; i < sampleSize.length; i++) { if(sampleSize[i] != sampleSize[0]) { throw new RuntimeException(JaiI18N.getString("TIFFImageEncoder0")); } } // Check low bit limits. int numBands = sampleModel.getNumBands(); if((sampleSize[0] == 1 || sampleSize[0] == 4) && numBands != 1) { throw new RuntimeException(JaiI18N.getString("TIFFImageEncoder1")); } // Retrieve and verify data type. int dataType = sampleModel.getDataType(); switch(dataType) { case DataBuffer.TYPE_BYTE: if(sampleSize[0] != 1 && sampleSize[0] != 4 && sampleSize[0] != 8) { throw new RuntimeException(JaiI18N.getString("TIFFImageEncoder2")); } break; case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_USHORT: if(sampleSize[0] != 16) { throw new RuntimeException(JaiI18N.getString("TIFFImageEncoder3")); } break; case DataBuffer.TYPE_INT: case DataBuffer.TYPE_FLOAT: if(sampleSize[0] != 32) { throw new RuntimeException(JaiI18N.getString("TIFFImageEncoder4")); } break; default: throw new RuntimeException(JaiI18N.getString("TIFFImageEncoder5")); } boolean dataTypeIsShort = dataType == DataBuffer.TYPE_SHORT || dataType == DataBuffer.TYPE_USHORT; ColorModel colorModel = im.getColorModel(); if (colorModel != null && colorModel instanceof IndexColorModel && dataType != DataBuffer.TYPE_BYTE) { // Don't support (unsigned) short palette-color images. throw new RuntimeException(JaiI18N.getString("TIFFImageEncoder6")); } IndexColorModel icm = null; int sizeOfColormap = 0; int colormap[] = null; // Set image type. int imageType = TIFF_UNSUPPORTED; int numExtraSamples = 0; int extraSampleType = EXTRA_SAMPLE_UNSPECIFIED; if(colorModel instanceof IndexColorModel) { // Bilevel or palette icm = (IndexColorModel)colorModel; int mapSize = icm.getMapSize(); if(sampleSize[0] == 1 && numBands == 1) { // Bilevel image if (mapSize != 2) { throw new IllegalArgumentException( JaiI18N.getString("TIFFImageEncoder7")); } byte r[] = new byte[mapSize]; icm.getReds(r); byte g[] = new byte[mapSize]; icm.getGreens(g); byte b[] = new byte[mapSize]; icm.getBlues(b); if ((r[0] & 0xff) == 0 && (r[1] & 0xff) == 255 && (g[0] & 0xff) == 0 && (g[1] & 0xff) == 255 && (b[0] & 0xff) == 0 && (b[1] & 0xff) == 255) { imageType = TIFF_BILEVEL_BLACK_IS_ZERO; } else if ((r[0] & 0xff) == 255 && (r[1] & 0xff) == 0 && (g[0] & 0xff) == 255 && (g[1] & 0xff) == 0 && (b[0] & 0xff) == 255 && (b[1] & 0xff) == 0) { imageType = TIFF_BILEVEL_WHITE_IS_ZERO; } else { imageType = TIFF_PALETTE; } } else if(numBands == 1) { // Non-bilevel image. // Palette color image. imageType = TIFF_PALETTE; } } else if(colorModel == null) { if(sampleSize[0] == 1 && numBands == 1) { // bilevel imageType = TIFF_BILEVEL_BLACK_IS_ZERO; } else { // generic image imageType = TIFF_GENERIC; if(numBands > 1) { numExtraSamples = numBands - 1; } } } else { // colorModel is non-null but not an IndexColorModel ColorSpace colorSpace = colorModel.getColorSpace(); switch(colorSpace.getType()) { case ColorSpace.TYPE_CMYK: imageType = TIFF_CMYK; break; case ColorSpace.TYPE_GRAY: imageType = TIFF_GRAY; break; case ColorSpace.TYPE_Lab: imageType = TIFF_CIELAB; break; case ColorSpace.TYPE_RGB: if(compression == COMP_JPEG_TTN2 && encodeParam.getJPEGCompressRGBToYCbCr()) { imageType = TIFF_YCBCR; } else { imageType = TIFF_RGB; } break; case ColorSpace.TYPE_YCbCr: imageType = TIFF_YCBCR; break; default: imageType = TIFF_GENERIC; // generic break; } if(imageType == TIFF_GENERIC) { numExtraSamples = numBands - 1; } else if(numBands > 1) { numExtraSamples = numBands - colorSpace.getNumComponents(); } if(numExtraSamples == 1 && colorModel.hasAlpha()) { extraSampleType = colorModel.isAlphaPremultiplied() ? EXTRA_SAMPLE_ASSOCIATED_ALPHA : EXTRA_SAMPLE_UNASSOCIATED_ALPHA; } } if(imageType == TIFF_UNSUPPORTED) { throw new RuntimeException(JaiI18N.getString("TIFFImageEncoder8")); } // Check JPEG compatibility. if(compression == COMP_JPEG_TTN2) { if(imageType == TIFF_PALETTE) { throw new RuntimeException(JaiI18N.getString("TIFFImageEncoder11")); } else if(!(sampleSize[0] == 8 && (imageType == TIFF_GRAY || imageType == TIFF_RGB || imageType == TIFF_YCBCR))) { throw new RuntimeException(JaiI18N.getString("TIFFImageEncoder9")); } } // Check bilevel encoding compatibility. if((imageType != TIFF_BILEVEL_WHITE_IS_ZERO && imageType != TIFF_BILEVEL_BLACK_IS_ZERO) && (compression == COMP_GROUP3_1D || compression == COMP_GROUP3_2D || compression == COMP_GROUP4)) { throw new RuntimeException(JaiI18N.getString("TIFFImageEncoder12")); } int photometricInterpretation = -1; switch (imageType) { case TIFF_BILEVEL_WHITE_IS_ZERO: photometricInterpretation = 0; break; case TIFF_BILEVEL_BLACK_IS_ZERO: photometricInterpretation = 1; break; case TIFF_GRAY: case TIFF_GENERIC: // Since the CS_GRAY colorspace is always of type black_is_zero photometricInterpretation = 1; break; case TIFF_PALETTE: photometricInterpretation = 3; icm = (IndexColorModel)colorModel; sizeOfColormap = icm.getMapSize(); byte r[] = new byte[sizeOfColormap]; icm.getReds(r); byte g[] = new byte[sizeOfColormap]; icm.getGreens(g); byte b[] = new byte[sizeOfColormap]; icm.getBlues(b); int redIndex = 0, greenIndex = sizeOfColormap; int blueIndex = 2 * sizeOfColormap; colormap = new int[sizeOfColormap * 3]; for (int i=0; i 0 ? encodeParam.getTileWidth() : im.getTileWidth(); tileHeight = encodeParam.getTileHeight() > 0 ? encodeParam.getTileHeight() : im.getTileHeight(); } else { tileWidth = width; // XXX Set rows per strip based on memory value if not specified? tileHeight = encodeParam.getTileHeight() > 0 ? encodeParam.getTileHeight() : DEFAULT_ROWS_PER_STRIP; } // Re-tile for JPEG conformance if needed. JPEGEncodeParam jep = null; if(compression == COMP_JPEG_TTN2) { // Get JPEGEncodeParam from encodeParam. jep = encodeParam.getJPEGEncodeParam(); // Determine maximum subsampling. int maxSubH = jep.getHorizontalSubsampling(0); int maxSubV = jep.getVerticalSubsampling(0); for(int i = 1; i < numBands; i++) { int subH = jep.getHorizontalSubsampling(i); if(subH > maxSubH) { maxSubH = subH; } int subV = jep.getVerticalSubsampling(i); if(subV > maxSubV) { maxSubV = subV; } } int factorV = 8*maxSubV; tileHeight = (int)((float)tileHeight/(float)factorV + 0.5F)*factorV; if(tileHeight < factorV) { tileHeight = factorV; } if(isTiled) { int factorH = 8*maxSubH; tileWidth = (int)((float)tileWidth/(float)factorH + 0.5F)*factorH; if(tileWidth < factorH) { tileWidth = factorH; } } } int numTiles; if(isTiled) { // NB: Parentheses are used in this statement for correct rounding. numTiles = ((width + tileWidth - 1)/tileWidth) * ((height + tileHeight - 1)/tileHeight); } else { numTiles = (int)Math.ceil((double)height/(double)tileHeight); } long tileByteCounts[] = new long[numTiles]; long bytesPerRow = (long)Math.ceil((sampleSize[0] / 8.0) * tileWidth * numBands); long bytesPerTile = bytesPerRow * tileHeight; for (int i=0; i 0) { int[] extraSamples = new int[numExtraSamples]; for(int i = 0; i < numExtraSamples; i++) { extraSamples[i] = extraSampleType; } fields.add(new TIFFField(TIFFImageDecoder.TIFF_EXTRA_SAMPLES, TIFFField.TIFF_SHORT, numExtraSamples, intsToChars(extraSamples))); } // Data Sample Format Extension fields. if(dataType != DataBuffer.TYPE_BYTE) { // SampleFormat int[] sampleFormat = new int[numBands]; if(dataType == DataBuffer.TYPE_FLOAT) { sampleFormat[0] = 3; } else if(dataType == DataBuffer.TYPE_USHORT) { sampleFormat[0] = 1; } else { sampleFormat[0] = 2; } for(int b = 1; b < numBands; b++) { sampleFormat[b] = sampleFormat[0]; } fields.add(new TIFFField(TIFFImageDecoder.TIFF_SAMPLE_FORMAT, TIFFField.TIFF_SHORT, numBands, intsToChars(sampleFormat))); // NOTE: We don't bother setting the SMinSampleValue and // SMaxSampleValue fields as these both default to the // extrema of the respective data types. Probably we should // check for the presence of the "extrema" property and // use it if available. } // Bilevel compression variables. boolean inverseFill = encodeParam.getReverseFillOrder(); boolean T4encode2D = encodeParam.getT4Encode2D(); boolean T4PadEOLs = encodeParam.getT4PadEOLs(); TIFFFaxEncoder faxEncoder = null; // Add bilevel compression fields. if((imageType == TIFF_BILEVEL_BLACK_IS_ZERO || imageType == TIFF_BILEVEL_WHITE_IS_ZERO) && (compression == COMP_GROUP3_1D || compression == COMP_GROUP3_2D || compression == COMP_GROUP4)) { // Create the encoder. faxEncoder = new TIFFFaxEncoder(inverseFill); // FillOrder field. fields.add(new TIFFField(TIFFImageDecoder.TIFF_FILL_ORDER, TIFFField.TIFF_SHORT, 1, new char[] {inverseFill ? (char)2 : (char)1})); if(compression == COMP_GROUP3_2D) { // T4Options field. long T4Options = 0x00000000; if(T4encode2D) { T4Options |= 0x00000001; } if(T4PadEOLs) { T4Options |= 0x00000004; } fields.add(new TIFFField(TIFFImageDecoder.TIFF_T4_OPTIONS, TIFFField.TIFF_LONG, 1, new long[] {T4Options})); } else if(compression == COMP_GROUP4) { // T6Options field. fields.add(new TIFFField(TIFFImageDecoder.TIFF_T6_OPTIONS, TIFFField.TIFF_LONG, 1, new long[] {(long)0x00000000})); } } // Initialize some JPEG variables. com.sun.image.codec.jpeg.JPEGEncodeParam jpegEncodeParam = null; com.sun.image.codec.jpeg.JPEGImageEncoder jpegEncoder = null; int jpegColorID = 0; if(compression == COMP_JPEG_TTN2) { // Initialize JPEG color ID. jpegColorID = com.sun.image.codec.jpeg.JPEGDecodeParam.COLOR_ID_UNKNOWN; switch(imageType) { case TIFF_GRAY: case TIFF_PALETTE: jpegColorID = com.sun.image.codec.jpeg.JPEGDecodeParam.COLOR_ID_GRAY; break; case TIFF_RGB: jpegColorID = com.sun.image.codec.jpeg.JPEGDecodeParam.COLOR_ID_RGB; break; case TIFF_YCBCR: jpegColorID = com.sun.image.codec.jpeg.JPEGDecodeParam.COLOR_ID_YCbCr; break; } // Get the JDK encoding parameters. Raster tile00 = im.getTile(im.getMinTileX(), im.getMinTileY()); jpegEncodeParam = com.sun.image.codec.jpeg.JPEGCodec.getDefaultJPEGEncodeParam( tile00, jpegColorID); // Modify per values passed in. JPEGImageEncoder.modifyEncodeParam(jep, jpegEncodeParam, numBands); // JPEGTables field. if(jep.getWriteImageOnly()) { // Write an abbreviated tables-only stream to JPEGTables field. jpegEncodeParam.setImageInfoValid(false); jpegEncodeParam.setTableInfoValid(true); ByteArrayOutputStream tableStream = new ByteArrayOutputStream(); jpegEncoder = com.sun.image.codec.jpeg.JPEGCodec.createJPEGEncoder( tableStream, jpegEncodeParam); jpegEncoder.encode(tile00); byte[] tableData = tableStream.toByteArray(); fields.add(new TIFFField(TIFF_JPEG_TABLES, TIFFField.TIFF_UNDEFINED, tableData.length, tableData)); // Reset encoder so it's recreated below. jpegEncoder = null; } } if(imageType == TIFF_YCBCR) { // YCbCrSubSampling: 2 is the default so we must write 1 as // we do not (yet) do any subsampling. int subsampleH = 1; int subsampleV = 1; // If JPEG, update values. if(compression == COMP_JPEG_TTN2) { // Determine maximum subsampling. subsampleH = jep.getHorizontalSubsampling(0); subsampleV = jep.getVerticalSubsampling(0); for(int i = 1; i < numBands; i++) { int subH = jep.getHorizontalSubsampling(i); if(subH > subsampleH) { subsampleH = subH; } int subV = jep.getVerticalSubsampling(i); if(subV > subsampleV) { subsampleV = subV; } } } fields.add(new TIFFField(TIFF_YCBCR_SUBSAMPLING, TIFFField.TIFF_SHORT, 2, new char[] {(char)subsampleH, (char)subsampleV})); // YCbCr positioning. fields.add(new TIFFField(TIFF_YCBCR_POSITIONING, TIFFField.TIFF_SHORT, 1, new char[] {compression == COMP_JPEG_TTN2 ? (char)1 : (char)2})); // Reference black/white. long[][] refbw; if(compression == COMP_JPEG_TTN2) { refbw = new long[][] { // no headroon/footroom {0, 1}, {255, 1}, {128, 1}, {255, 1}, {128, 1}, {255, 1} }; } else { refbw = new long[][] { // CCIR 601.1 headroom/footroom (presumptive) {15, 1}, {235, 1}, {128, 1}, {240, 1}, {128, 1}, {240, 1} }; } fields.add(new TIFFField(TIFF_REF_BLACK_WHITE, TIFFField.TIFF_RATIONAL, 6, refbw)); } // ---- No more automatically generated fields should be added // after this point. ---- // Add extra fields specified via the encoding parameters. TIFFField[] extraFields = encodeParam.getExtraFields(); if(extraFields != null) { ArrayList extantTags = new ArrayList(fields.size()); Iterator fieldIter = fields.iterator(); while(fieldIter.hasNext()) { TIFFField fld = (TIFFField)fieldIter.next(); extantTags.add(new Integer(fld.getTag())); } int numExtraFields = extraFields.length; for(int i = 0; i < numExtraFields; i++) { TIFFField fld = extraFields[i]; Integer tagValue = new Integer(fld.getTag()); if(!extantTags.contains(tagValue)) { fields.add(fld); extantTags.add(tagValue); } } } // ---- No more fields of any type should be added after this. ---- // Determine the size of the IFD which is written after the header // of the stream or after the data of the previous image in a // multi-page stream. int dirSize = getDirectorySize(fields); // The first data segment is written after the field overflow // following the IFD so initialize the first offset accordingly. tileOffsets[0] = ifdOffset + dirSize; // Branch here depending on whether data are being comrpressed. // If not, then the IFD is written immediately. // If so then there are three possibilities: // A) the OutputStream is a SeekableOutputStream (outCache null); // B) the OutputStream is not a SeekableOutputStream and a file cache // is used (outCache non-null, tempFile non-null); // C) the OutputStream is not a SeekableOutputStream and a memory cache // is used (outCache non-null, tempFile null). OutputStream outCache = null; byte[] compressBuf = null; File tempFile = null; int nextIFDOffset = 0; boolean skipByte = false; Deflater deflater = null; int deflateLevel = Deflater.DEFAULT_COMPRESSION; boolean jpegRGBToYCbCr = false; if(compression == COMP_NONE) { // Determine the number of bytes of padding necessary between // the end of the IFD and the first data segment such that the // alignment of the data conforms to the specification (required // for uncompressed data only). int numBytesPadding = 0; if(sampleSize[0] == 16 && tileOffsets[0] % 2 != 0) { numBytesPadding = 1; tileOffsets[0]++; } else if(sampleSize[0] == 32 && tileOffsets[0] % 4 != 0) { numBytesPadding = (int)(4 - tileOffsets[0] % 4); tileOffsets[0] += numBytesPadding; } // Update the data offsets (which TIFFField stores by reference). for (int i = 1; i < numTiles; i++) { tileOffsets[i] = tileOffsets[i-1] + tileByteCounts[i-1]; } if(!isLast) { // Determine the offset of the next IFD. nextIFDOffset = (int)(tileOffsets[0] + totalBytesOfData); // IFD offsets must be on a word boundary. if(nextIFDOffset % 2 != 0) { nextIFDOffset++; skipByte = true; } } // Write the IFD and field overflow before the image data. writeDirectory(ifdOffset, fields, nextIFDOffset); // Write any padding bytes needed between the end of the IFD // and the start of the actual image data. if(numBytesPadding != 0) { for(int padding = 0; padding < numBytesPadding; padding++) { output.write((byte)0); } } } else { // If compressing, the cannot be written yet as the size of the // data segments is unknown. if((output instanceof SeekableOutputStream)) { // Simply seek to the first data segment position. ((SeekableOutputStream)output).seek(tileOffsets[0]); } else { // Cache the original OutputStream. outCache = output; try { // Attempt to create a temporary file. tempFile = File.createTempFile("jai-SOS-", ".tmp"); tempFile.deleteOnExit(); RandomAccessFile raFile = new RandomAccessFile(tempFile, "rw"); output = new SeekableOutputStream(raFile); // XXX Be sure that this file is deleted no matter how // this method is exited! } catch(Exception e) { tempFile = null; // Allocate memory for the entire image data (!). output = new ByteArrayOutputStream((int)totalBytesOfData); } } int bufSize = 0; switch(compression) { case COMP_GROUP3_1D: // This initial buffer size is based on an alternating 1-0 // pattern generating the most bits when converted to code // words: 9 bits out for each pair of bits in. So the number // of bit pairs is determined, multiplied by 9, converted to // bytes, and a ceil() is taken to account for fill bits at the // end of each line. The "2" addend accounts for the case // of the pattern beginning with black. The buffer is intended // to hold only a single row. bufSize = (int)Math.ceil((((tileWidth + 1)/2)*9 + 2)/8.0); break; case COMP_GROUP3_2D: case COMP_GROUP4: // Calculate the maximum row as the G3-1D size plus the EOL, // multiply this by the number of rows in the tile, and add // 6 EOLs for the RTC (return to control). bufSize = (int)Math.ceil((((tileWidth + 1)/2)*9 + 2)/8.0); bufSize = tileHeight*(bufSize + 2) + 12; break; case COMP_PACKBITS: bufSize = (int)(bytesPerTile + ((bytesPerRow+127)/128)*tileHeight); break; case COMP_JPEG_TTN2: bufSize = 0; // Set color conversion flag. if(imageType == TIFF_YCBCR && colorModel != null && colorModel.getColorSpace().getType() == ColorSpace.TYPE_RGB) { jpegRGBToYCbCr = true; } break; case COMP_DEFLATE: bufSize = (int)bytesPerTile; deflater = new Deflater(encodeParam.getDeflateLevel()); break; default: bufSize = 0; } if(bufSize != 0) { compressBuf = new byte[bufSize]; } } // ---- Writing of actual image data ---- // Buffer for up to tileHeight rows of pixels int[] pixels = null; float[] fpixels = null; // Whether to test for contiguous data. boolean checkContiguous = ((sampleSize[0] == 1 && sampleModel instanceof MultiPixelPackedSampleModel && dataType == DataBuffer.TYPE_BYTE) || (sampleSize[0] == 8 && sampleModel instanceof ComponentSampleModel)); // Also create a buffer to hold tileHeight lines of the // data to be written to the file, so we can use array writes. byte[] bpixels = null; if(compression != COMP_JPEG_TTN2) { if(dataType == DataBuffer.TYPE_BYTE) { bpixels = new byte[tileHeight * tileWidth * numBands]; } else if(dataTypeIsShort) { bpixels = new byte[2 * tileHeight * tileWidth * numBands]; } else if(dataType == DataBuffer.TYPE_INT || dataType == DataBuffer.TYPE_FLOAT) { bpixels = new byte[4 * tileHeight * tileWidth * numBands]; } } // Process tileHeight rows at a time int lastRow = minY + height; int lastCol = minX + width; int tileNum = 0; for (int row = minY; row < lastRow; row += tileHeight) { int rows = isTiled ? tileHeight : Math.min(tileHeight, lastRow - row); int size = rows * tileWidth * numBands; for(int col = minX; col < lastCol; col += tileWidth) { // Grab the pixels Raster src = im.getData(new Rectangle(col, row, tileWidth, rows)); boolean useDataBuffer = false; if(compression != COMP_JPEG_TTN2) { // JPEG access Raster if(checkContiguous) { if(sampleSize[0] == 8) { // 8-bit ComponentSampleModel csm = (ComponentSampleModel)src.getSampleModel(); int[] bankIndices = csm.getBankIndices(); int[] bandOffsets = csm.getBandOffsets(); int pixelStride = csm.getPixelStride(); int lineStride = csm.getScanlineStride(); if(pixelStride != numBands || lineStride != bytesPerRow) { useDataBuffer = false; } else { useDataBuffer = true; for(int i = 0; useDataBuffer && i < numBands; i++) { if(bankIndices[i] != 0 || bandOffsets[i] != i) { useDataBuffer = false; } } } } else { // 1-bit MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)src.getSampleModel(); if(mpp.getNumBands() == 1 && mpp.getDataBitOffset() == 0 && mpp.getPixelBitStride() == 1) { useDataBuffer = true; } } } if(!useDataBuffer) { if(dataType == DataBuffer.TYPE_FLOAT) { fpixels = src.getPixels(col, row, tileWidth, rows, fpixels); } else { pixels = src.getPixels(col, row, tileWidth, rows, pixels); } } } int index; int pixel = 0;; int k = 0; switch(sampleSize[0]) { case 1: if(useDataBuffer) { byte[] btmp = ((DataBufferByte)src.getDataBuffer()).getData(); MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)src.getSampleModel(); int lineStride = mpp.getScanlineStride(); int inOffset = mpp.getOffset(col - src.getSampleModelTranslateX(), row - src.getSampleModelTranslateY()); if(lineStride == (int)bytesPerRow) { System.arraycopy(btmp, inOffset, bpixels, 0, (int)bytesPerRow*rows); } else { int outOffset = 0; for(int j = 0; j < rows; j++) { System.arraycopy(btmp, inOffset, bpixels, outOffset, (int)bytesPerRow); inOffset += lineStride; outOffset += (int)bytesPerRow; } } } else { index = 0; // For each of the rows in a strip for (int i=0; i 0) { pixel = 0; for (int j=0; j> 8); bpixels[ls++] = (byte)(value & 0x00ff); } if(compression == COMP_NONE) { output.write(bpixels, 0, size*2); } else if(compression == COMP_PACKBITS) { int numCompressedBytes = compressPackBits(bpixels, rows, (int)bytesPerRow, compressBuf); tileByteCounts[tileNum++] = numCompressedBytes; output.write(compressBuf, 0, numCompressedBytes); } else if(compression == COMP_DEFLATE) { int numCompressedBytes = deflate(deflater, bpixels, compressBuf); tileByteCounts[tileNum++] = numCompressedBytes; output.write(compressBuf, 0, numCompressedBytes); } break; case 32: if(dataType == DataBuffer.TYPE_INT) { int li = 0; for (int i = 0; i < size; i++) { int value = pixels[i]; bpixels[li++] = (byte)((value & 0xff000000) >> 24); bpixels[li++] = (byte)((value & 0x00ff0000) >> 16); bpixels[li++] = (byte)((value & 0x0000ff00) >> 8); bpixels[li++] = (byte)(value & 0x000000ff); } } else { // DataBuffer.TYPE_FLOAT int lf = 0; for (int i = 0; i < size; i++) { int value = Float.floatToIntBits(fpixels[i]); bpixels[lf++] = (byte)((value & 0xff000000) >> 24); bpixels[lf++] = (byte)((value & 0x00ff0000) >> 16); bpixels[lf++] = (byte)((value & 0x0000ff00) >> 8); bpixels[lf++] = (byte)(value & 0x000000ff); } } if(compression == COMP_NONE) { output.write(bpixels, 0, size*4); } else if(compression == COMP_PACKBITS) { int numCompressedBytes = compressPackBits(bpixels, rows, (int)bytesPerRow, compressBuf); tileByteCounts[tileNum++] = numCompressedBytes; output.write(compressBuf, 0, numCompressedBytes); } else if(compression == COMP_DEFLATE) { int numCompressedBytes = deflate(deflater, bpixels, compressBuf); tileByteCounts[tileNum++] = numCompressedBytes; output.write(compressBuf, 0, numCompressedBytes); } break; } } } if(compression == COMP_NONE) { // Write an extra byte for IFD word alignment if needed. if(skipByte) { output.write((byte)0); } } else { // Recompute tile offsets from the size of the compressed tiles. int totalBytes = 0; for (int i=1; i 4 bytes. int dirSize = 2 + numEntries*12 + 4; // Loop over fields adding the size of all values > 4 bytes. Iterator iter = fields.iterator(); while(iter.hasNext()) { // Get the field. TIFFField field = (TIFFField)iter.next(); // Determine the size of the field value. int valueSize = getValueSize(field); // Add any excess size. if(valueSize > 4) { dirSize += valueSize; } } return dirSize; } private void writeFileHeader() throws IOException { // 8 byte image file header // Byte order used within the file if(isLittleEndian) { // Little Endian output.write('I'); output.write('I'); } else { // Big Endian output.write('M'); output.write('M'); } // Magic value writeUnsignedShort(42); // Offset in bytes of the first IFD. writeLong(8); } private void writeDirectory(int thisIFDOffset, SortedSet fields, int nextIFDOffset) throws IOException { // 2 byte count of number of directory entries (fields) int numEntries = fields.size(); long offsetBeyondIFD = thisIFDOffset + 12 * numEntries + 4 + 2; ArrayList tooBig = new ArrayList(); // Write number of fields in the IFD writeUnsignedShort(numEntries); Iterator iter = fields.iterator(); while(iter.hasNext()) { // 12 byte field entry TIFFField TIFFField field = (TIFFField)iter.next(); // byte 0-1 Tag that identifies a field int tag = field.getTag(); writeUnsignedShort(tag); // byte 2-3 The field type int type = field.getType(); writeUnsignedShort(type); // bytes 4-7 the number of values of the indicated type except // ASCII-valued fields which require the total number of bytes. int count = field.getCount(); int valueSize = getValueSize(field); writeLong(type == TIFFField.TIFF_ASCII ? valueSize : count); // bytes 8 - 11 the value or value offset if (valueSize > 4) { // We need an offset as data won't fit into 4 bytes writeLong(offsetBeyondIFD); offsetBeyondIFD += valueSize; tooBig.add(field); } else { writeValuesAsFourBytes(field); } } // Address of next IFD writeLong(nextIFDOffset); // Write the tag values that did not fit into 4 bytes for (int i = 0; i < tooBig.size(); i++) { writeValues((TIFFField)tooBig.get(i)); } } /** * Determine the number of bytes in the value portion of the field. */ private static final int getValueSize(TIFFField field) { int type = field.getType(); int count = field.getCount(); int valueSize = 0; if(type == TIFFField.TIFF_ASCII) { for(int i = 0; i < count; i++) { byte[] stringBytes = field.getAsString(i).getBytes(); valueSize += stringBytes.length; if(stringBytes[stringBytes.length-1] != (byte)0) { valueSize++; } } } else { valueSize = count * sizeOfType[type]; } return valueSize; } private static final int[] sizeOfType = { 0, // 0 = n/a 1, // 1 = byte 1, // 2 = ascii 2, // 3 = short 4, // 4 = long 8, // 5 = rational 1, // 6 = sbyte 1, // 7 = undefined 2, // 8 = sshort 4, // 9 = slong 8, // 10 = srational 4, // 11 = float 8 // 12 = double }; private void writeValuesAsFourBytes(TIFFField field) throws IOException { int dataType = field.getType(); int count = field.getCount(); switch (dataType) { // 8 bits case TIFFField.TIFF_BYTE: case TIFFField.TIFF_SBYTE: case TIFFField.TIFF_UNDEFINED: byte bytes[] = field.getAsBytes(); for (int i=0; i> 32)); writeLong((int)(longBits & 0xffffffff)); } break; // unsigned rationals case TIFFField.TIFF_RATIONAL: long rationals[][] = field.getAsRationals(); for (int i=0; i>> 8); } else { output.write((s & 0xff00) >>> 8); output.write(s & 0x00ff); } } private void writeLong(long l) throws IOException { if(isLittleEndian) { output.write( ((int)l & 0x000000ff)); output.write( (int)((l & 0x0000ff00) >>> 8)); output.write( (int)((l & 0x00ff0000) >>> 16)); output.write( (int)((l & 0xff000000) >>> 24)); } else { output.write( (int)((l & 0xff000000) >>> 24)); output.write( (int)((l & 0x00ff0000) >>> 16)); output.write( (int)((l & 0x0000ff00) >>> 8)); output.write( ((int)l & 0x000000ff)); } } /** * Returns the current offset in the supplied OutputStream. * This method should only be used if compressing data. */ private long getOffset(OutputStream out) throws IOException { if(out instanceof ByteArrayOutputStream) { return ((ByteArrayOutputStream)out).size(); } else if(out instanceof SeekableOutputStream) { return ((SeekableOutputStream)out).getFilePointer(); } else { // Shouldn't happen. throw new IllegalStateException(); } } /** * Performs PackBits compression on a tile of data. */ private static int compressPackBits(byte[] data, int numRows, int bytesPerRow, byte[] compData) { int inOffset = 0; int outOffset = 0; for(int i = 0; i < numRows; i++) { outOffset = packBits(data, inOffset, bytesPerRow, compData, outOffset); inOffset += bytesPerRow; } return outOffset; } /** * Performs PackBits compression for a single buffer of data. * This should be called for each row of each tile. The returned * value is the offset into the output buffer after compression. */ private static int packBits(byte[] input, int inOffset, int inCount, byte[] output, int outOffset) { int inMax = inOffset + inCount - 1; int inMaxMinus1 = inMax - 1; while(inOffset <= inMax) { int run = 1; byte replicate = input[inOffset]; while(run < 127 && inOffset < inMax && input[inOffset] == input[inOffset+1]) { run++; inOffset++; } if(run > 1) { inOffset++; output[outOffset++] = (byte)(-(run - 1)); output[outOffset++] = replicate; } run = 0; int saveOffset = outOffset; while(run < 128 && ((inOffset < inMax && input[inOffset] != input[inOffset+1]) || (inOffset < inMaxMinus1 && input[inOffset] != input[inOffset+2]))) { run++; output[++outOffset] = input[inOffset++]; } if(run > 0) { output[saveOffset] = (byte)(run - 1); outOffset++; } if(inOffset == inMax) { if(run > 0 && run < 128) { output[saveOffset]++; output[outOffset++] = input[inOffset++]; } else { output[outOffset++] = (byte)0; output[outOffset++] = input[inOffset++]; } } } return outOffset; } private static int deflate(Deflater deflater, byte[] inflated, byte[] deflated) { deflater.setInput(inflated); deflater.finish(); int numCompressedBytes = deflater.deflate(deflated); deflater.reset(); return numCompressedBytes; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/FPXCodec.java0000644000175000017500000000406110203035544026005 0ustar mathieumathieu/* * $RCSfile: FPXCodec.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:36 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl; import java.awt.image.RenderedImage; import java.io.InputStream; import java.io.OutputStream; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.ImageDecoder; import com.sun.media.jai.codec.ImageDecodeParam; import com.sun.media.jai.codec.ImageEncoder; import com.sun.media.jai.codec.ImageEncodeParam; import com.sun.media.jai.codec.SeekableStream; import com.sun.media.jai.codec.FPXDecodeParam; // import com.sun.media.jai.codec.FPXEncodeParam; /** * @since EA3 */ public final class FPXCodec extends ImageCodec { public FPXCodec() {} public String getFormatName() { return "fpx"; } public Class getEncodeParamClass() { return null; } public Class getDecodeParamClass() { return com.sun.media.jai.codec.FPXDecodeParam.class; } public boolean canEncodeImage(RenderedImage im, ImageEncodeParam param) { return false; } protected ImageEncoder createImageEncoder(OutputStream dst, ImageEncodeParam param) { throw new RuntimeException(JaiI18N.getString("FPXCodec0")); } protected ImageDecoder createImageDecoder(SeekableStream src, ImageDecodeParam param) { return new FPXImageDecoder(src, param); } public int getNumHeaderBytes() { return 8; } public boolean isFormatRecognized(byte[] header) { return ((header[0] == (byte)0xd0) && (header[1] == (byte)0xcf) && (header[2] == (byte)0x11) && (header[3] == (byte)0xe0) && (header[4] == (byte)0xa1) && (header[5] == (byte)0xb1) && (header[6] == (byte)0x1a) && (header[7] == (byte)0xe1)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/BMPCodec.java0000644000175000017500000000607410203035544025774 0ustar mathieumathieu/* * $RCSfile: BMPCodec.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:35 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl; import java.awt.image.DataBuffer; import java.awt.image.PixelInterleavedSampleModel; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.ImageDecoder; import com.sun.media.jai.codec.ImageDecodeParam; import com.sun.media.jai.codec.ImageEncoder; import com.sun.media.jai.codec.ImageEncodeParam; import com.sun.media.jai.codec.BMPEncodeParam; import com.sun.media.jai.codec.SeekableStream; /** * @since EA2 */ public final class BMPCodec extends ImageCodec { public BMPCodec() {} public String getFormatName() { return "bmp"; } public Class getEncodeParamClass() { return com.sun.media.jai.codec.BMPEncodeParam.class; } public Class getDecodeParamClass() { return Object.class; } public boolean canEncodeImage(RenderedImage im, ImageEncodeParam param) { SampleModel sampleModel = im.getSampleModel(); int dataType = sampleModel.getTransferType(); if (dataType != DataBuffer.TYPE_BYTE && !CodecUtils.isPackedByteImage(im)) { return false; } if (param != null) { if (!(param instanceof BMPEncodeParam)) { return false; } BMPEncodeParam BMPParam = (BMPEncodeParam)param; int version = BMPParam.getVersion(); if ((version == BMPEncodeParam.VERSION_2) || (version == BMPEncodeParam.VERSION_4)) { return false; } } return true; } protected ImageEncoder createImageEncoder(OutputStream dst, ImageEncodeParam param) { BMPEncodeParam p = null; if (param != null) { p = (BMPEncodeParam)param; } return new BMPImageEncoder(dst, p); } protected ImageDecoder createImageDecoder(InputStream src, ImageDecodeParam param) { return new BMPImageDecoder(src, null); } protected ImageDecoder createImageDecoder(File src, ImageDecodeParam param) throws IOException { return new BMPImageDecoder(new FileInputStream(src), null); } protected ImageDecoder createImageDecoder(SeekableStream src, ImageDecodeParam param) { return new BMPImageDecoder(src, null); } public int getNumHeaderBytes() { return 2; } public boolean isFormatRecognized(byte[] header) { return ((header[0] == 0x42) && (header[1] == 0x4d)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/PNMImageEncoder.java0000644000175000017500000004057010203035544027314 0ustar mathieumathieu/* * $RCSfile: PNMImageEncoder.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:38 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.ComponentSampleModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.IndexColorModel; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.io.IOException; import java.io.OutputStream; import com.sun.media.jai.codec.ImageEncoderImpl; import com.sun.media.jai.codec.ImageEncodeParam; import com.sun.media.jai.codec.PNMEncodeParam; /** * An ImageEncoder for the PNM family of file formats. * *

    The PNM file format includes PBM for monochrome images, PGM for * grey scale images, and PPM for color images. When writing the * source data out, the encoder chooses the appropriate file variant * based on the actual SampleModel of the source image. In case the * source image data is unsuitable for the PNM file format, for * example when source has 4 bands or float data type, the encoder * throws an Error. * *

    The raw file format is used wherever possible, unless the * PNMEncodeParam object supplied to the constructor returns * true from its getRaw() method. * * * @since EA2 */ public class PNMImageEncoder extends ImageEncoderImpl { private static final int PBM_ASCII = '1'; private static final int PGM_ASCII = '2'; private static final int PPM_ASCII = '3'; private static final int PBM_RAW = '4'; private static final int PGM_RAW = '5'; private static final int PPM_RAW = '6'; private static final int SPACE = ' '; private static final String COMMENT = "# written by com.sun.media.jai.codecimpl.PNMImageEncoder"; private byte[] lineSeparator; private int variant; private int maxValue; public PNMImageEncoder(OutputStream output, ImageEncodeParam param) { super(output, param); if (this.param == null) { this.param = new PNMEncodeParam(); } } /** * Encodes a RenderedImage and writes the output to the * OutputStream associated with this ImageEncoder. */ public void encode(RenderedImage im) throws IOException { int minX = im.getMinX(); int minY = im.getMinY(); int width = im.getWidth(); int height = im.getHeight(); int tileHeight = im.getTileHeight(); SampleModel sampleModel = im.getSampleModel(); ColorModel colorModel = im.getColorModel(); String ls = (String)java.security.AccessController.doPrivileged( new sun.security.action.GetPropertyAction("line.separator")); lineSeparator = ls.getBytes(); int dataType = sampleModel.getTransferType(); if ((dataType == DataBuffer.TYPE_FLOAT) || (dataType == DataBuffer.TYPE_DOUBLE)) { throw new RuntimeException(JaiI18N.getString("PNMImageEncoder0")); } // Raw data can only handle bytes, everything greater must be ASCII. int[] sampleSize = sampleModel.getSampleSize(); int numBands = sampleModel.getNumBands(); // Colormap populated for non-bilevel IndexColorModel only. byte[] reds = null; byte[] greens = null; byte[] blues = null; // Flag indicating that PB data should be inverted before writing. boolean isPBMInverted = false; if (numBands == 1) { if (colorModel instanceof IndexColorModel) { IndexColorModel icm = (IndexColorModel)colorModel; int mapSize = icm.getMapSize(); if (mapSize < (1 << sampleSize[0])) { throw new RuntimeException( JaiI18N.getString("PNMImageEncoder1")); } if(sampleSize[0] == 1) { variant = PBM_RAW; // Set PBM inversion flag if 1 maps to a higher color // value than 0: PBM expects white-is-zero so if this // does not obtain then inversion needs to occur. isPBMInverted = (icm.getRed(1) + icm.getGreen(1) + icm.getBlue(1)) > (icm.getRed(0) + icm.getGreen(0) + icm.getBlue(0)); } else { variant = PPM_RAW; reds = new byte[mapSize]; greens = new byte[mapSize]; blues = new byte[mapSize]; icm.getReds(reds); icm.getGreens(greens); icm.getBlues(blues); } } else if (sampleSize[0] == 1) { variant = PBM_RAW; } else if (sampleSize[0] <= 8) { variant = PGM_RAW; } else { variant = PGM_ASCII; } } else if (numBands == 3) { if (sampleSize[0] <= 8 && sampleSize[1] <= 8 && sampleSize[2] <= 8) { // all 3 bands must be <= 8 variant = PPM_RAW; } else { variant = PPM_ASCII; } } else { throw new RuntimeException(JaiI18N.getString("PNMImageEncoder2")); } // Read parameters if (((PNMEncodeParam)param).getRaw()) { if (!isRaw(variant)) { boolean canUseRaw = true; // Make sure sampleSize for all bands no greater than 8. for (int i = 0; i < sampleSize.length; i++) { if (sampleSize[i] > 8) { canUseRaw = false; break; } } if (canUseRaw) { variant += 0x3; } } } else { if (isRaw(variant)) { variant -= 0x3; } } maxValue = (1 << sampleSize[0]) - 1; // Write PNM file. output.write('P'); // magic value output.write(variant); output.write(lineSeparator); output.write(COMMENT.getBytes()); // comment line output.write(lineSeparator); writeInteger(output, width); // width output.write(SPACE); writeInteger(output, height); // height // Writ esample max value for non-binary images if ((variant != PBM_RAW) && (variant != PBM_ASCII)) { output.write(lineSeparator); writeInteger(output, maxValue); } // The spec allows a single character between the // last header value and the start of the raw data. if (variant == PBM_RAW || variant == PGM_RAW || variant == PPM_RAW) { output.write('\n'); } // Set flag for optimal image writing case: row-packed data with // correct band order if applicable. boolean writeOptimal = false; if (variant == PBM_RAW && sampleModel.getTransferType() == DataBuffer.TYPE_BYTE && sampleModel instanceof MultiPixelPackedSampleModel) { MultiPixelPackedSampleModel mppsm = (MultiPixelPackedSampleModel)sampleModel; // Must have left-aligned bytes with unity bit stride. if(mppsm.getDataBitOffset() == 0 && mppsm.getPixelBitStride() == 1) { writeOptimal = true; } } else if ((variant == PGM_RAW || variant == PPM_RAW) && sampleModel instanceof ComponentSampleModel && !(colorModel instanceof IndexColorModel)) { ComponentSampleModel csm = (ComponentSampleModel)sampleModel; // Pixel stride must equal band count. if(csm.getPixelStride() == numBands) { writeOptimal = true; // Band offsets must equal band indices. if(variant == PPM_RAW) { int[] bandOffsets = csm.getBandOffsets(); for(int b = 0; b < numBands; b++) { if(bandOffsets[b] != b) { writeOptimal = false; break; } } } } } // Write using an optimal approach if possible. if(writeOptimal) { int bytesPerRow = variant == PBM_RAW ? (width + 7)/8 : width*sampleModel.getNumBands(); int numYTiles = im.getNumYTiles(); Rectangle imageBounds = new Rectangle(im.getMinX(), im.getMinY(), im.getWidth(), im.getHeight()); Rectangle stripRect = new Rectangle(im.getMinX(), im.getMinTileY() * im.getTileHeight() + im.getTileGridYOffset(), im.getWidth(), im.getTileHeight()); byte[] invertedData = null; if(isPBMInverted) { invertedData = new byte[bytesPerRow]; } // Loop over tiles to minimize cobbling. for(int j = 0; j < numYTiles; j++) { // Clamp the strip to the image bounds. if(j == numYTiles - 1) { stripRect.height = im.getHeight() - stripRect.y; } Rectangle encodedRect = stripRect.intersection(imageBounds); // Get a strip of data. Raster strip = im.getData(encodedRect); // Get the data array. byte[] bdata = ((DataBufferByte)strip.getDataBuffer()).getData(); // Get the scanline stride. int rowStride = variant == PBM_RAW ? ((MultiPixelPackedSampleModel)strip.getSampleModel()).getScanlineStride() : ((ComponentSampleModel)strip.getSampleModel()).getScanlineStride(); if(rowStride == bytesPerRow && !isPBMInverted) { // Write the entire strip at once. output.write(bdata, 0, bdata.length); } else { // Write the strip row-by-row. int offset = 0; for(int i = 0; i < encodedRect.height; i++) { if(isPBMInverted) { for(int k = 0; k < bytesPerRow; k++) { invertedData[k] = (byte)(~(bdata[offset+k]&0xff)); } output.write(invertedData, 0, bytesPerRow); } else { output.write(bdata, offset, bytesPerRow); } offset += rowStride; } } // Increment the strip origin. stripRect.y += tileHeight; } // Write all buffered bytes and return. output.flush(); return; } // Buffer for up to 8 rows of pixels int[] pixels = new int[8*width*numBands]; // Also allocate a buffer to hold the data to be written to the file, // so we can use array writes. byte[] bpixels = reds == null ? new byte[8*width*numBands] : new byte[8*width*3]; // The index of the sample being written, used to // place a line separator after every 16th sample in // ASCII mode. Not used in raw mode. int count = 0; // Process 8 rows at a time so all but the last will have // a multiple of 8 pixels. This simplifies PBM_RAW encoding. int lastRow = minY + height; for (int row = minY; row < lastRow; row += 8) { int rows = Math.min(8, lastRow - row); int size = rows*width*numBands; // Grab the pixels Raster src = im.getData(new Rectangle(minX, row, width, rows)); src.getPixels(minX, row, width, rows, pixels); // Invert bits if necessary. if(isPBMInverted) { for(int k = 0; k < size; k++) { pixels[k] ^= 0x00000001; } } switch (variant) { case PBM_ASCII: case PGM_ASCII: for (int i = 0; i < size; i++) { if ((count++ % 16) == 0) { output.write(lineSeparator); } else { output.write(SPACE); } writeInteger(output, pixels[i]); } output.write(lineSeparator); break; case PPM_ASCII: if (reds == null) { // no need to expand for (int i = 0; i < size; i++) { if ((count++ % 16) == 0) { output.write(lineSeparator); } else { output.write(SPACE); } writeInteger(output, pixels[i]); } } else { for (int i = 0; i < size; i++) { if ((count++ % 16) == 0) { output.write(lineSeparator); } else { output.write(SPACE); } writeInteger(output, (reds[pixels[i]] & 0xFF)); output.write(SPACE); writeInteger(output, (greens[pixels[i]] & 0xFF)); output.write(SPACE); writeInteger(output, (blues[pixels[i]] & 0xFF)); } } output.write(lineSeparator); break; case PBM_RAW: // 8 pixels packed into 1 byte, the leftovers are padded. int kdst = 0; int ksrc = 0; for (int i = 0; i < size/8; i++) { int b = (pixels[ksrc++] << 7) | (pixels[ksrc++] << 6) | (pixels[ksrc++] << 5) | (pixels[ksrc++] << 4) | (pixels[ksrc++] << 3) | (pixels[ksrc++] << 2) | (pixels[ksrc++] << 1) | pixels[ksrc++]; bpixels[kdst++] = (byte)b; } // Leftover pixels, only possible at the end of the file. if (size%8 > 0) { int b = 0; for (int i=0; i= PBM_RAW); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/JPEGCodec.java0000644000175000017500000000472010203035544026077 0ustar mathieumathieu/* * $RCSfile: JPEGCodec.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:36 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl; import java.awt.image.RenderedImage; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.ImageDecoder; import com.sun.media.jai.codec.ImageDecodeParam; import com.sun.media.jai.codec.ImageEncoder; import com.sun.media.jai.codec.ImageEncodeParam; import com.sun.media.jai.codec.JPEGDecodeParam; import com.sun.media.jai.codec.JPEGEncodeParam; import com.sun.media.jai.codec.SeekableStream; /** * @since EA2 */ public final class JPEGCodec extends ImageCodec { public JPEGCodec() {} public String getFormatName() { return "jpeg"; } public Class getEncodeParamClass() { return com.sun.media.jai.codec.JPEGEncodeParam.class; } public Class getDecodeParamClass() { return com.sun.media.jai.codec.JPEGDecodeParam.class; } public boolean canEncodeImage(RenderedImage im, ImageEncodeParam param) { return true; } protected ImageEncoder createImageEncoder(OutputStream dst, ImageEncodeParam param) { JPEGEncodeParam p = null; if (param != null) { p = (JPEGEncodeParam)param; } return new JPEGImageEncoder(dst, p); } protected ImageDecoder createImageDecoder(InputStream src, ImageDecodeParam param) { return new JPEGImageDecoder(src, param); } protected ImageDecoder createImageDecoder(File src, ImageDecodeParam param) throws IOException { return new JPEGImageDecoder(new FileInputStream(src), param); } protected ImageDecoder createImageDecoder(SeekableStream src, ImageDecodeParam param) { return new JPEGImageDecoder(src, param); } public int getNumHeaderBytes() { return 3; } public boolean isFormatRecognized(byte[] header) { return ((header[0] == (byte)0xff) && (header[1] == (byte)0xd8) && (header[2] == (byte)0xff)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/TIFFLZWDecoder.java0000644000175000017500000001244010203035544027025 0ustar mathieumathieu/* * $RCSfile: TIFFLZWDecoder.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:40 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl; /** * A class for performing LZW decoding. * * @since FCS * */ public class TIFFLZWDecoder { byte stringTable[][]; byte data[] = null, uncompData[]; int tableIndex, bitsToGet = 9; int bytePointer, bitPointer; int dstIndex; int w, h; int predictor, samplesPerPixel; int nextData = 0; int nextBits = 0; int andTable[] = { 511, 1023, 2047, 4095 }; public TIFFLZWDecoder(int w, int predictor, int samplesPerPixel) { this.w = w; this.predictor = predictor; this.samplesPerPixel = samplesPerPixel; } /** * Method to decode LZW compressed data. * * @param data The compressed data. * @param uncompData Array to return the uncompressed data in. * @param h The number of rows the compressed data contains. */ public byte[] decode(byte data[], byte uncompData[], int h) { if(data[0] == (byte)0x00 && data[1] == (byte)0x01) { throw new UnsupportedOperationException(JaiI18N.getString("TIFFLZWDecoder0")); } initializeStringTable(); this.data = data; this.h = h; this.uncompData = uncompData; // Initialize pointers bytePointer = 0; bitPointer = 0; dstIndex = 0; nextData = 0; nextBits = 0; int code, oldCode = 0; byte string[]; int uncompDataLength = uncompData.length; while ( ((code = getNextCode()) != 257) && dstIndex < uncompDataLength) { if (code == 256) { initializeStringTable(); code = getNextCode(); if (code == 257) { break; } writeString(stringTable[code]); oldCode = code; } else { if (code < tableIndex) { string = stringTable[code]; writeString(string); addStringToTable(stringTable[oldCode], string[0]); oldCode = code; } else { string = stringTable[oldCode]; string = composeString(string, string[0]); writeString(string); addStringToTable(string); oldCode = code; } } } // Horizontal Differencing Predictor if (predictor == 2) { int count; for (int j = 0; j < h; j++) { count = samplesPerPixel * (j * w + 1); for (int i = samplesPerPixel; i < w * samplesPerPixel; i++) { uncompData[count] += uncompData[count - samplesPerPixel]; count++; } } } return uncompData; } /** * Initialize the string table. */ public void initializeStringTable() { stringTable = new byte[4096][]; for (int i=0; i<256; i++) { stringTable[i] = new byte[1]; stringTable[i][0] = (byte)i; } tableIndex = 258; bitsToGet = 9; } /** * Write out the string just uncompressed. */ public void writeString(byte string[]) { if(dstIndex < uncompData.length) { int maxIndex = Math.min(string.length, uncompData.length - dstIndex); for (int i=0; i < maxIndex; i++) { uncompData[dstIndex++] = string[i]; } } } /** * Add a new string to the string table. */ public void addStringToTable(byte oldString[], byte newString) { int length = oldString.length; byte string[] = new byte[length + 1]; System.arraycopy(oldString, 0, string, 0, length); string[length] = newString; // Add this new String to the table stringTable[tableIndex++] = string; if (tableIndex == 511) { bitsToGet = 10; } else if (tableIndex == 1023) { bitsToGet = 11; } else if (tableIndex == 2047) { bitsToGet = 12; } } /** * Add a new string to the string table. */ public void addStringToTable(byte string[]) { // Add this new String to the table stringTable[tableIndex++] = string; if (tableIndex == 511) { bitsToGet = 10; } else if (tableIndex == 1023) { bitsToGet = 11; } else if (tableIndex == 2047) { bitsToGet = 12; } } /** * Append newString to the end of oldString. */ public byte[] composeString(byte oldString[], byte newString) { int length = oldString.length; byte string[] = new byte[length + 1]; System.arraycopy(oldString, 0, string, 0, length); string[length] = newString; return string; } // Returns the next 9, 10, 11 or 12 bits public int getNextCode() { // Attempt to get the next code. The exception is caught to make // this robust to cases wherein the EndOfInformation code has been // omitted from a strip. Examples of such cases have been observed // in practice. try { nextData = (nextData << 8) | (data[bytePointer++] & 0xff); nextBits += 8; if (nextBits < bitsToGet) { nextData = (nextData << 8) | (data[bytePointer++] & 0xff); nextBits += 8; } int code = (nextData >> (nextBits - bitsToGet)) & andTable[bitsToGet-9]; nextBits -= bitsToGet; return code; } catch(ArrayIndexOutOfBoundsException e) { // Strip not terminated as expected: return EndOfInformation code. return 257; } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/BMPImageEncoder.java0000644000175000017500000006012010203035544027271 0ustar mathieumathieu/* * $RCSfile: BMPImageEncoder.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:35 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl; import java.io.OutputStream; import java.io.IOException; import java.awt.image.Raster; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferShort; import java.awt.image.DataBufferUShort; import java.awt.image.DataBufferInt; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.Rectangle; import java.awt.image.PixelInterleavedSampleModel; import java.awt.image.SinglePixelPackedSampleModel; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.ColorModel; import java.awt.image.DirectColorModel; import java.awt.image.IndexColorModel; import com.sun.media.jai.codec.ImageEncoderImpl; import com.sun.media.jai.codec.ImageEncodeParam; import com.sun.media.jai.codec.BMPEncodeParam; import com.sun.media.jai.codec.SeekableOutputStream; /** * An ImageEncoder for the various versions of the BMP image file format. * * Unless specified otherwise by the BMPDecodeParam object passed to the * constructor, Version 3 will be the default version used. * *

    If the image to be encoded has an IndexColorModel and can be encoded * using upto 8 bits per pixel, the image will be written out as a Palette * color image with an appropriate number of bits per pixel. For example an * image having a 256 color IndexColorModel will be written out as a Palette * image with 8 bits per pixel while one with a 16 color palette will be * written out as a Palette image with 4 bits per pixel. For all other images, * the 24 bit image format will be used. * * * @since EA2 */ public class BMPImageEncoder extends ImageEncoderImpl { private OutputStream output; private int version; private boolean isCompressed, isTopDown; private int w, h; private int compImageSize = 0; /** * An ImageEncoder for the BMP file format. * * @param output The OutputStream to write to. * @param param The BMPEncodeParam object. */ public BMPImageEncoder(OutputStream output, ImageEncodeParam param) { super(output, param); this.output = output; BMPEncodeParam bmpParam; if (param == null) { // Use default valued BMPEncodeParam bmpParam = new BMPEncodeParam(); } else { bmpParam = (BMPEncodeParam)param; } this.version = bmpParam.getVersion(); this.isCompressed = bmpParam.isCompressed(); if(isCompressed && !(output instanceof SeekableOutputStream)){ throw new IllegalArgumentException(JaiI18N.getString("BMPImageEncoder6")); } this.isTopDown = bmpParam.isTopDown(); } /** * Encodes a RenderedImage and writes the output to the * OutputStream associated with this ImageEncoder. */ public void encode(RenderedImage im) throws IOException { // Get image dimensions int minX = im.getMinX(); int minY = im.getMinY(); w = im.getWidth(); h = im.getHeight(); // Default is using 24 bits per pixel. int bitsPerPixel = 24; boolean isPalette = false; int paletteEntries = 0; IndexColorModel icm = null; SampleModel sm = im.getSampleModel(); int numBands = sm.getNumBands(); ColorModel cm = im.getColorModel(); if (numBands != 1 && numBands != 3) { throw new IllegalArgumentException(JaiI18N.getString("BMPImageEncoder1")); } int sampleSize[] = sm.getSampleSize(); if (sampleSize[0] > 8) { throw new RuntimeException(JaiI18N.getString("BMPImageEncoder2")); } for (int i=1; i> 3; } } // actual writing of image data int fileSize = 0; int offset = 0; int headerSize = 0; int imageSize = 0; int xPelsPerMeter = 0; int yPelsPerMeter = 0; int colorsUsed = 0; int colorsImportant = paletteEntries; int padding = 0; // Calculate padding for each scanline int remainder = destScanlineBytes % 4; if (remainder != 0) { padding = 4 - remainder; } switch (version) { case BMPEncodeParam.VERSION_2: offset = 26 + paletteEntries * 3; headerSize = 12; imageSize = (destScanlineBytes + padding) * h; fileSize = imageSize + offset; throw new RuntimeException(JaiI18N.getString("BMPImageEncoder5")); //break; case BMPEncodeParam.VERSION_3: // FileHeader is 14 bytes, BitmapHeader is 40 bytes, // add palette size and that is where the data will begin if (isCompressed && bitsPerPixel == 8) { compression = 1; } else if (isCompressed && bitsPerPixel == 4) { compression = 2; } offset = 54 + paletteEntries * 4; imageSize = (destScanlineBytes + padding) * h; fileSize = imageSize + offset; headerSize = 40; break; case BMPEncodeParam.VERSION_4: headerSize = 108; throw new RuntimeException(JaiI18N.getString("BMPImageEncoder5")); // break; } int redMask = 0, blueMask = 0, greenMask = 0; if (cm instanceof DirectColorModel) { redMask = ((DirectColorModel)cm).getRedMask(); greenMask = ((DirectColorModel)cm).getGreenMask(); blueMask = ((DirectColorModel)cm).getBlueMask(); destScanlineBytes = w; compression = 3; fileSize += 12; offset += 12; } writeFileHeader(fileSize, offset); writeInfoHeader(headerSize, bitsPerPixel); // compression writeDWord(compression); // imageSize writeDWord(imageSize); // xPelsPerMeter writeDWord(xPelsPerMeter); // yPelsPerMeter writeDWord(yPelsPerMeter); // Colors Used writeDWord(colorsUsed); // Colors Important writeDWord(colorsImportant); if (compression == 3) { writeDWord(redMask); writeDWord(greenMask); writeDWord(blueMask); } if (compression == 3) { for (int i = 0; i < h; i++) { int row = minY + i; if (!isTopDown) row = minY + h - i -1; // Get the pixels Rectangle srcRect = new Rectangle(minX, row, w, 1); Raster src = im.getData(srcRect); SampleModel sm1 = src.getSampleModel(); int pos = 0; int startX = srcRect.x - src.getSampleModelTranslateX(); int startY = srcRect.y - src.getSampleModelTranslateY(); if (sm1 instanceof SinglePixelPackedSampleModel) { SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel)sm1; pos = sppsm.getOffset(startX, startY); } switch(dataType) { case DataBuffer.TYPE_SHORT: short[] sdata = ((DataBufferShort)src.getDataBuffer()).getData(); for (int m = 0; m < sdata.length; m++) writeWord(sdata[m]); break; case DataBuffer.TYPE_USHORT: short[] usdata = ((DataBufferUShort)src.getDataBuffer()).getData(); for (int m = 0; m < usdata.length; m++) writeWord(usdata[m]); break; case DataBuffer.TYPE_INT: int[] idata = ((DataBufferInt)src.getDataBuffer()).getData(); for (int m = 0; m < idata.length; m++) writeDWord(idata[m]); break; } } return; } // palette if (isPalette == true) { // write palette switch(version) { // has 3 field entries case BMPEncodeParam.VERSION_2: for (int i=0; i= minY; row -= 8) { // Number of rows being read int rows = Math.min(8, row - minY + 1); // Get the pixels Raster src = im.getData(new Rectangle(minX, row - rows + 1, w, rows)); src.getPixels(minX, row - rows + 1, w, rows, pixels); l = 0; // Last possible position in the pixels array int max = scanlineBytes * rows - 1; for (int i=0; i 0) { pixel = 0; for (int j=0; j 256 colors. int entries = icm.getMapSize(); byte r[] = new byte[entries]; byte g[] = new byte[entries]; byte b[] = new byte[entries]; icm.getReds(r); icm.getGreens(g); icm.getBlues(b); int index; for (int j=0; j= 3 ){ /// Check if there was an existing Absolute Run output.write(0); output.write(absVal); incCompImageSize(2); for(int a=0; a -1){ /// Absolute Encoding for less than 3 /// treated as regular encoding /// Do not include the last element since it will /// be inclued in the next encoding/run for (int b=0;b 1){ /// If there was an existing run output.write(runCount); output.write(runVal); incCompImageSize(2); } else if (absVal < 0){ // First time.. absBuf[++absVal] = runVal; absBuf[++absVal] = nextVal; } else if (absVal < 254){ // 0-254 only absBuf[++absVal] = nextVal; } else { output.write(0); output.write(absVal+1); incCompImageSize(2); for(int a=0; a<=absVal;a++){ output.write(absBuf[a]); incCompImageSize(1); } // padding since 255 elts is not even output.write(0); incCompImageSize(1); absVal = -1; } runVal = nextVal; runCount = 1; } if (j == scanlineBytes-1){ // EOF scanline // Write the run if (absVal == -1){ output.write(runCount); output.write(runVal); incCompImageSize(2); runCount = 1; } else { // write the Absolute Run if(absVal >= 2){ output.write(0); output.write(absVal+1); incCompImageSize(2); for(int a=0; a<=absVal;a++){ output.write(absBuf[a]); incCompImageSize(1); } if (!isEven(absVal+1)){ //Padding output.write(0); incCompImageSize(1); } } else if(absVal > -1){ for (int b=0;b<=absVal;b++){ output.write(1); output.write(absBuf[b]); incCompImageSize(2); } } } /// EOF scanline output.write(0); output.write(0); incCompImageSize(2); } } } private void encodeRLE4(byte[] bipixels, int scanlineBytes) throws IOException { int runCount=2, absVal=-1, j=-1, pixel=0, q=0; byte runVal1=0, runVal2=0, nextVal1=0, nextVal2=0; byte[] absBuf = new byte[256]; runVal1 = bipixels[++j]; runVal2 = bipixels[++j]; while (j < scanlineBytes-2){ nextVal1 = bipixels[++j]; nextVal2 = bipixels[++j]; if (nextVal1 == runVal1 ) { //Check if there was an existing Absolute Run if(absVal >= 4){ output.write(0); output.write(absVal - 1); incCompImageSize(2); // we need to exclude last 2 elts, similarity of // which caused to enter this part of the code for(int a=0; a -1){ output.write(2); pixel = (absBuf[0] << 4) | absBuf[1]; output.write(pixel); incCompImageSize(2); } absVal = -1; if (nextVal2 == runVal2){ // Even runlength runCount+=2; if(runCount == 256){ output.write(runCount-1); pixel = ( runVal1 << 4) | runVal2; output.write(pixel); incCompImageSize(2); runCount =2; if(j< scanlineBytes - 1){ runVal1 = runVal2; runVal2 = bipixels[++j]; } else { output.write(01); int r = runVal2 << 4 | 0; output.write(r); incCompImageSize(2); runCount = -1;/// Only EOF required now } } } else { // odd runlength and the run ends here // runCount wont be > 254 since 256/255 case will // be taken care of in above code. runCount++; pixel = ( runVal1 << 4) | runVal2; output.write(runCount); output.write(pixel); incCompImageSize(2); runCount = 2; runVal1 = nextVal2; // If end of scanline if (j < scanlineBytes -1){ runVal2 = bipixels[++j]; }else { output.write(01); int r = nextVal2 << 4 | 0; output.write(r); incCompImageSize(2); runCount = -1;/// Only EOF required now } } } else{ // Check for existing run if (runCount > 2){ pixel = ( runVal1 << 4) | runVal2; output.write(runCount); output.write(pixel); incCompImageSize(2); } else if (absVal < 0){ // first time absBuf[++absVal] = runVal1; absBuf[++absVal] = runVal2; absBuf[++absVal] = nextVal1; absBuf[++absVal] = nextVal2; } else if (absVal < 253){ // only 255 elements absBuf[++absVal] = nextVal1; absBuf[++absVal] = nextVal2; } else { output.write(0); output.write(absVal+1); incCompImageSize(2); for(int a=0; a= scanlineBytes-2 ) { if (absVal == -1 && runCount >= 2){ if (j == scanlineBytes-2){ if(bipixels[++j] == runVal1){ runCount++; pixel = ( runVal1 << 4) | runVal2; output.write(runCount); output.write(pixel); incCompImageSize(2); } else { pixel = ( runVal1 << 4) | runVal2; output.write(runCount); output.write(pixel); output.write(01); pixel = bipixels[j]<<4 |0; output.write(pixel); int n = bipixels[j]<<4|0; incCompImageSize(4); } } else { output.write(runCount); pixel =( runVal1 << 4) | runVal2 ; output.write(pixel); incCompImageSize(2); } } else if(absVal > -1){ if (j == scanlineBytes-2){ absBuf[++absVal] = bipixels[++j]; } if (absVal >=2){ output.write(0); output.write(absVal+1); incCompImageSize(2); for(int a=0; a> 8); } public void writeDWord(int dword) throws IOException { output.write(dword & 0xff); output.write((dword & 0xff00) >> 8); output.write((dword & 0xff0000) >> 16); output.write((dword & 0xff000000) >> 24); } private void writeSize(int dword, int offset) throws IOException { ((SeekableOutputStream)output).seek(offset); writeDWord(dword); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/GIFImageDecoder.java0000644000175000017500000005067210444643225027271 0ustar mathieumathieu/* * $RCSfile: GIFImageDecoder.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2006-06-17 00:02:28 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl; import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.ImageProducer; import java.awt.image.IndexColorModel; import java.awt.image.PixelInterleavedSampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.HashMap; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.ImageDecodeParam; import com.sun.media.jai.codec.ImageDecoderImpl; import com.sun.media.jai.codec.SeekableStream; import com.sun.media.jai.codecimpl.ImagingListenerProxy; import com.sun.media.jai.codecimpl.util.ImagingException; /** * @since EA3 */ public class GIFImageDecoder extends ImageDecoderImpl { // The global color table. private byte[] globalColorTable = null; // Whether the last page has been encountered. private boolean maxPageFound = false; // The maximum allowable page for reading. private int maxPage; // The previous page read. private int prevPage = -1; // The previous page on which getTile() was invoked in this object. private int prevSyncedPage = -1; // Map of Integer page numbers to RenderedImages. private HashMap images = new HashMap(); /** * Read the overall stream header and return the global color map * or null. */ private static byte[] readHeader(SeekableStream input) throws IOException { byte[] globalColorTable = null; try { // Skip the version string and logical screen dimensions. input.skipBytes(10); int packedFields = input.readUnsignedByte(); boolean globalColorTableFlag = (packedFields & 0x80) != 0; int numGCTEntries = 1 << ((packedFields & 0x7) + 1); int backgroundColorIndex = input.readUnsignedByte(); // Read the aspect ratio but ignore the returned value. input.read(); if (globalColorTableFlag) { globalColorTable = new byte[3*numGCTEntries]; input.readFully(globalColorTable); } else { globalColorTable = null; } } catch (IOException e) { String message = JaiI18N.getString("GIFImageDecoder0"); ImagingListenerProxy.errorOccurred(message, new ImagingException(message, e), GIFImageDecoder.class, false); // throw new IOException(JaiI18N.getString("GIFImageDecoder0")); } return globalColorTable; } public GIFImageDecoder(SeekableStream input, ImageDecodeParam param) { super(input, param); } public GIFImageDecoder(InputStream input, ImageDecodeParam param) { super(input, param); } public int getNumPages() throws IOException { int page = prevPage + 1; while(!maxPageFound) { try { decodeAsRenderedImage(page++); } catch(IOException e) { // Ignore } } return maxPage + 1; } public synchronized RenderedImage decodeAsRenderedImage(int page) throws IOException { // Verify that the index is in range. if (page < 0 || (maxPageFound && page > maxPage)) { throw new IOException(JaiI18N.getString("GIFImageDecoder1")); } // Attempt to get the image from the cache. Integer pageKey = new Integer(page); if(images.containsKey(pageKey)) { return (RenderedImage)images.get(pageKey); } // If the zeroth image, set the global color table. if(prevPage == -1) { try { globalColorTable = readHeader(input); } catch(IOException e) { maxPageFound = true; maxPage = -1; throw e; } } // Force previous data to be read. if(page > 0) { for(int idx = prevSyncedPage + 1; idx < page; idx++) { RenderedImage im = (RenderedImage)images.get(new Integer(idx)); im.getTile(0, 0); prevSyncedPage = idx; } } // Read as many images as possible. RenderedImage image = null; while(prevPage < page) { int index = prevPage + 1; RenderedImage ri = null; try { ri = new GIFImage(input, globalColorTable); images.put(new Integer(index), ri); if(index < page) { ri.getTile(0, 0); prevSyncedPage = index; } prevPage = index; if(index == page) { image = ri; break; } } catch(IOException e) { maxPageFound = true; maxPage = prevPage; String message = JaiI18N.getString("GIFImage3"); ImagingListenerProxy.errorOccurred(message, new ImagingException(message, e), this, false); // throw e; } } return image; } } /** * @since 1.1.1 */ class GIFImage extends SimpleRenderedImage { // Constants used to control interlacing. private static final int[] INTERLACE_INCREMENT = { 8, 8, 4, 2, -1 }; private static final int[] INTERLACE_OFFSET = { 0, 4, 2, 1, -1 }; // The source stream. private SeekableStream input; // The interlacing flag. private boolean interlaceFlag = false; // Variables used by LZW decoding private byte[] block = new byte[255]; private int blockLength = 0; private int bitPos = 0; private int nextByte = 0; private int initCodeSize; private int clearCode; private int eofCode; private int bitsLeft; // 32-bit lookahead buffer private int next32Bits = 0; // True if the end of the data blocks has been found, // and we are simply draining the 32-bit buffer private boolean lastBlockFound = false; // The current interlacing pass, starting with 0. private int interlacePass = 0; // The image's tile. private WritableRaster theTile = null; // Read blocks of 1-255 bytes, stop at a 0-length block private void skipBlocks() throws IOException { while (true) { int length = input.readUnsignedByte(); if (length == 0) { break; } input.skipBytes(length); } } /** * Create a new GIFImage. The input stream must * be positioned at the start of the image, i.e., not at the * start of the overall stream. * * @param input the stream from which to read. * @param globalColorTable the global colormap of null. * * @throws IOException. */ GIFImage(SeekableStream input, byte[] globalColorTable) throws IOException { this.input = input; byte[] localColorTable = null; boolean transparentColorFlag = false; int transparentColorIndex = 0; // Read the image header initializing the local color table, // if any, and the transparent index, if any. try { long startPosition = input.getFilePointer(); while (true) { int blockType = input.readUnsignedByte(); if (blockType == 0x2c) { // Image Descriptor // Skip image top and left position. input.skipBytes(4); width = input.readUnsignedShortLE(); height = input.readUnsignedShortLE(); int idPackedFields = input.readUnsignedByte(); boolean localColorTableFlag = (idPackedFields & 0x80) != 0; interlaceFlag = (idPackedFields & 0x40) != 0; int numLCTEntries = 1 << ((idPackedFields & 0x7) + 1); if (localColorTableFlag) { // Read color table if any localColorTable = new byte[3*numLCTEntries]; input.readFully(localColorTable); } else { localColorTable = null; } // Now positioned at start of LZW-compressed pixels break; } else if (blockType == 0x21) { // Extension block int label = input.readUnsignedByte(); if (label == 0xf9) { // Graphics Control Extension input.read(); // extension length int gcePackedFields = input.readUnsignedByte(); transparentColorFlag = (gcePackedFields & 0x1) != 0; input.skipBytes(2); // delay time transparentColorIndex = input.readUnsignedByte(); input.read(); // terminator } else if (label == 0x1) { // Plain text extension // Skip content. input.skipBytes(13); // Read but do not save content. skipBlocks(); } else if (label == 0xfe) { // Comment extension // Read but do not save content. skipBlocks(); } else if (label == 0xff) { // Application extension // Skip content. input.skipBytes(12); // Read but do not save content. skipBlocks(); } else { // Skip over unknown extension blocks int length = 0; do { length = input.readUnsignedByte(); input.skipBytes(length); } while (length > 0); } } else { throw new IOException(JaiI18N.getString("GIFImage0")+" "+ blockType + "!"); } } } catch (IOException ioe) { throw new IOException(JaiI18N.getString("GIFImage1")); } // Set the image layout from the header information. // Set the image and tile grid origin to (0, 0). minX = minY = tileGridXOffset = tileGridYOffset = 0; // Force the image to have a single tile. tileWidth = width; tileHeight = height; byte[] colorTable; if (localColorTable != null) { colorTable = localColorTable; } else { colorTable = globalColorTable; } // Normalize color table length to 2^1, 2^2, 2^4, or 2^8 int length = colorTable.length/3; int bits; if (length == 2) { bits = 1; } else if (length == 4) { bits = 2; } else if (length == 8 || length == 16) { // Bump from 3 to 4 bits bits = 4; } else { // Bump to 8 bits bits = 8; } int lutLength = 1 << bits; byte[] r = new byte[lutLength]; byte[] g = new byte[lutLength]; byte[] b = new byte[lutLength]; // Entries from length + 1 to lutLength - 1 will be 0 int rgbIndex = 0; for (int i = 0; i < length; i++) { r[i] = colorTable[rgbIndex++]; g[i] = colorTable[rgbIndex++]; b[i] = colorTable[rgbIndex++]; } int[] bitsPerSample = new int[1]; bitsPerSample[0] = bits; sampleModel = new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE, width, height, 1, width, new int[] {0}); if (!transparentColorFlag) { if (ImageCodec.isIndicesForGrayscale(r, g, b)) colorModel = ImageCodec.createComponentColorModel(sampleModel); else colorModel = new IndexColorModel(bits, r.length, r, g, b); } else { colorModel = new IndexColorModel(bits, r.length, r, g, b, transparentColorIndex); } } // BEGIN LZW CODE private void initNext32Bits() { next32Bits = block[0] & 0xff; next32Bits |= (block[1] & 0xff) << 8; next32Bits |= (block[2] & 0xff) << 16; next32Bits |= block[3] << 24; nextByte = 4; } // Load a block (1-255 bytes) at a time, and maintain // a 32-bit lookahead buffer that is filled from the left // and extracted from the right. private int getCode(int codeSize, int codeMask) throws IOException { //if (bitPos + codeSize > 32) { if (bitsLeft <= 0) { return eofCode; // No more data available } int code = (next32Bits >> bitPos) & codeMask; bitPos += codeSize; bitsLeft -= codeSize; // Shift in a byte of new data at a time while (bitPos >= 8 && !lastBlockFound) { next32Bits >>>= 8; bitPos -= 8; // Check if current block is out of bytes if (nextByte >= blockLength) { // Get next block size blockLength = input.readUnsignedByte(); if (blockLength == 0) { lastBlockFound = true; if (bitsLeft < 0) return eofCode; else return code; } else { int left = blockLength; int off = 0; while (left > 0) { int nbytes = input.read(block, off, left); off += nbytes; left -= nbytes; } bitsLeft += blockLength << 3; nextByte = 0; } } next32Bits |= block[nextByte++] << 24; } return code; } private void initializeStringTable(int[] prefix, byte[] suffix, byte[] initial, int[] length) { int numEntries = 1 << initCodeSize; for (int i = 0; i < numEntries; i++) { prefix[i] = -1; suffix[i] = (byte)i; initial[i] = (byte)i; length[i] = 1; } // Fill in the entire table for robustness against // out-of-sequence codes. for (int i = numEntries; i < 4096; i++) { prefix[i] = -1; length[i] = 1; } } private Point outputPixels(byte[] string, int len, Point streamPos, byte[] rowBuf) { if (interlacePass < 0 || interlacePass > 3) { return streamPos; } for (int i = 0; i < len; i++) { if (streamPos.x >= minX) { rowBuf[streamPos.x - minX] = string[i]; } // Process end-of-row ++streamPos.x; if (streamPos.x == width) { theTile.setDataElements(minX, streamPos.y, width, 1, rowBuf); streamPos.x = 0; if (interlaceFlag) { streamPos.y += INTERLACE_INCREMENT[interlacePass]; if (streamPos.y >= height) { ++interlacePass; if (interlacePass > 3) { return streamPos; } streamPos.y = INTERLACE_OFFSET[interlacePass]; } } else { ++streamPos.y; } } } return streamPos; } // END LZW CODE public synchronized Raster getTile(int tileX, int tileY) { // Should be a unique tile. if (tileX != 0 || tileY != 0) { throw new IllegalArgumentException(JaiI18N.getString("GIFImage2")); } // Return the tile if it's already computed. if (theTile != null) { return theTile; } // Initialize the destination image theTile = WritableRaster.createWritableRaster(sampleModel, sampleModel.createDataBuffer(), null); // Position in stream coordinates. Point streamPos = new Point(0, 0); // Allocate a row of memory. byte[] rowBuf = new byte[width]; try { // Read and decode the image data, fill in theTile. this.initCodeSize = input.readUnsignedByte(); // Read first data block this.blockLength = input.readUnsignedByte(); int left = blockLength; int off = 0; while (left > 0) { int nbytes = input.read(block, off, left); left -= nbytes; off += nbytes; } this.bitPos = 0; this.nextByte = 0; this.lastBlockFound = false; this.bitsLeft = this.blockLength << 3; // Init 32-bit buffer initNext32Bits(); this.clearCode = 1 << initCodeSize; this.eofCode = clearCode + 1; int code, oldCode = 0; int[] prefix = new int[4096]; byte[] suffix = new byte[4096]; byte[] initial = new byte[4096]; int[] length = new int[4096]; byte[] string = new byte[4096]; initializeStringTable(prefix, suffix, initial, length); int tableIndex = (1 << initCodeSize) + 2; int codeSize = initCodeSize + 1; int codeMask = (1 << codeSize) - 1; while (true) { code = getCode(codeSize, codeMask); if (code == clearCode) { initializeStringTable(prefix, suffix, initial, length); tableIndex = (1 << initCodeSize) + 2; codeSize = initCodeSize + 1; codeMask = (1 << codeSize) - 1; code = getCode(codeSize, codeMask); if (code == eofCode) { return theTile; } } else if (code == eofCode) { return theTile; } else { int newSuffixIndex; if (code < tableIndex) { newSuffixIndex = code; } else { // code == tableIndex newSuffixIndex = oldCode; } int ti = tableIndex; int oc = oldCode; prefix[ti] = oc; suffix[ti] = initial[newSuffixIndex]; initial[ti] = initial[oc]; length[ti] = length[oc] + 1; ++tableIndex; if ((tableIndex == (1 << codeSize)) && (tableIndex < 4096)) { ++codeSize; codeMask = (1 << codeSize) - 1; } } // Reverse code int c = code; int len = length[c]; for (int i = len - 1; i >= 0; i--) { string[i] = suffix[c]; c = prefix[c]; } outputPixels(string, len, streamPos, rowBuf); oldCode = code; } } catch (IOException e) { String message = JaiI18N.getString("GIFImage3"); ImagingListenerProxy.errorOccurred(message, new ImagingException(message, e), this, false); // throw new RuntimeException(JaiI18N.getString("GIFImage3")); } finally { return theTile; } } public void dispose() { theTile = null; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/TIFFFaxEncoder.java0000644000175000017500000006645210311610741027113 0ustar mathieumathieu/* * $RCSfile: TIFFFaxEncoder.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-09-13 17:52:33 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl; class TIFFFaxEncoder { /** * The CCITT numerical definition of white. */ private static final int WHITE = 0; /** * The CCITT numerical definition of black. */ private static final int BLACK = 1; // --- Begin tables for CCITT compression --- private static byte[] byteTable = new byte[] { 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, // 0 to 15 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 16 to 31 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 32 to 47 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 48 to 63 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 to 79 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80 to 95 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 to 111 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 112 to 127 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128 to 143 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 144 to 159 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160 to 175 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 176 to 191 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 192 to 207 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 208 to 223 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 224 to 239 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 240 to 255 }; /** * Terminating codes for black runs. */ private static int[] termCodesBlack = new int[] { /* 0 0x0000 */ 0x0dc0000a, 0x40000003, 0xc0000002, 0x80000002, /* 4 0x0004 */ 0x60000003, 0x30000004, 0x20000004, 0x18000005, /* 8 0x0008 */ 0x14000006, 0x10000006, 0x08000007, 0x0a000007, /* 12 0x000c */ 0x0e000007, 0x04000008, 0x07000008, 0x0c000009, /* 16 0x0010 */ 0x05c0000a, 0x0600000a, 0x0200000a, 0x0ce0000b, /* 20 0x0014 */ 0x0d00000b, 0x0d80000b, 0x06e0000b, 0x0500000b, /* 24 0x0018 */ 0x02e0000b, 0x0300000b, 0x0ca0000c, 0x0cb0000c, /* 28 0x001c */ 0x0cc0000c, 0x0cd0000c, 0x0680000c, 0x0690000c, /* 32 0x0020 */ 0x06a0000c, 0x06b0000c, 0x0d20000c, 0x0d30000c, /* 36 0x0024 */ 0x0d40000c, 0x0d50000c, 0x0d60000c, 0x0d70000c, /* 40 0x0028 */ 0x06c0000c, 0x06d0000c, 0x0da0000c, 0x0db0000c, /* 44 0x002c */ 0x0540000c, 0x0550000c, 0x0560000c, 0x0570000c, /* 48 0x0030 */ 0x0640000c, 0x0650000c, 0x0520000c, 0x0530000c, /* 52 0x0034 */ 0x0240000c, 0x0370000c, 0x0380000c, 0x0270000c, /* 56 0x0038 */ 0x0280000c, 0x0580000c, 0x0590000c, 0x02b0000c, /* 60 0x003c */ 0x02c0000c, 0x05a0000c, 0x0660000c, 0x0670000c }; /** * Terminating codes for white runs. */ private static int[] termCodesWhite = new int[] { /* 0 0x0000 */ 0x35000008, 0x1c000006, 0x70000004, 0x80000004, /* 4 0x0004 */ 0xb0000004, 0xc0000004, 0xe0000004, 0xf0000004, /* 8 0x0008 */ 0x98000005, 0xa0000005, 0x38000005, 0x40000005, /* 12 0x000c */ 0x20000006, 0x0c000006, 0xd0000006, 0xd4000006, /* 16 0x0010 */ 0xa8000006, 0xac000006, 0x4e000007, 0x18000007, /* 20 0x0014 */ 0x10000007, 0x2e000007, 0x06000007, 0x08000007, /* 24 0x0018 */ 0x50000007, 0x56000007, 0x26000007, 0x48000007, /* 28 0x001c */ 0x30000007, 0x02000008, 0x03000008, 0x1a000008, /* 32 0x0020 */ 0x1b000008, 0x12000008, 0x13000008, 0x14000008, /* 36 0x0024 */ 0x15000008, 0x16000008, 0x17000008, 0x28000008, /* 40 0x0028 */ 0x29000008, 0x2a000008, 0x2b000008, 0x2c000008, /* 44 0x002c */ 0x2d000008, 0x04000008, 0x05000008, 0x0a000008, /* 48 0x0030 */ 0x0b000008, 0x52000008, 0x53000008, 0x54000008, /* 52 0x0034 */ 0x55000008, 0x24000008, 0x25000008, 0x58000008, /* 56 0x0038 */ 0x59000008, 0x5a000008, 0x5b000008, 0x4a000008, /* 60 0x003c */ 0x4b000008, 0x32000008, 0x33000008, 0x34000008 }; /** * Make-up codes for black runs. */ private static int[] makeupCodesBlack = new int[] { /* 0 0x0000 */ 0x00000000, 0x03c0000a, 0x0c80000c, 0x0c90000c, /* 4 0x0004 */ 0x05b0000c, 0x0330000c, 0x0340000c, 0x0350000c, /* 8 0x0008 */ 0x0360000d, 0x0368000d, 0x0250000d, 0x0258000d, /* 12 0x000c */ 0x0260000d, 0x0268000d, 0x0390000d, 0x0398000d, /* 16 0x0010 */ 0x03a0000d, 0x03a8000d, 0x03b0000d, 0x03b8000d, /* 20 0x0014 */ 0x0290000d, 0x0298000d, 0x02a0000d, 0x02a8000d, /* 24 0x0018 */ 0x02d0000d, 0x02d8000d, 0x0320000d, 0x0328000d, /* 28 0x001c */ 0x0100000b, 0x0180000b, 0x01a0000b, 0x0120000c, /* 32 0x0020 */ 0x0130000c, 0x0140000c, 0x0150000c, 0x0160000c, /* 36 0x0024 */ 0x0170000c, 0x01c0000c, 0x01d0000c, 0x01e0000c, /* 40 0x0028 */ 0x01f0000c, 0x00000000, 0x00000000, 0x00000000, /* 44 0x002c */ 0x00000000, 0x00000000, 0x00000000, 0x00000000, /* 48 0x0030 */ 0x00000000, 0x00000000, 0x00000000, 0x00000000, /* 52 0x0034 */ 0x00000000, 0x00000000, 0x00000000, 0x00000000, /* 56 0x0038 */ 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; /** * Make-up codes for white runs. */ private static int[] makeupCodesWhite = new int[] { /* 0 0x0000 */ 0x00000000, 0xd8000005, 0x90000005, 0x5c000006, /* 4 0x0004 */ 0x6e000007, 0x36000008, 0x37000008, 0x64000008, /* 8 0x0008 */ 0x65000008, 0x68000008, 0x67000008, 0x66000009, /* 12 0x000c */ 0x66800009, 0x69000009, 0x69800009, 0x6a000009, /* 16 0x0010 */ 0x6a800009, 0x6b000009, 0x6b800009, 0x6c000009, /* 20 0x0014 */ 0x6c800009, 0x6d000009, 0x6d800009, 0x4c000009, /* 24 0x0018 */ 0x4c800009, 0x4d000009, 0x60000006, 0x4d800009, /* 28 0x001c */ 0x0100000b, 0x0180000b, 0x01a0000b, 0x0120000c, /* 32 0x0020 */ 0x0130000c, 0x0140000c, 0x0150000c, 0x0160000c, /* 36 0x0024 */ 0x0170000c, 0x01c0000c, 0x01d0000c, 0x01e0000c, /* 40 0x0028 */ 0x01f0000c, 0x00000000, 0x00000000, 0x00000000, /* 44 0x002c */ 0x00000000, 0x00000000, 0x00000000, 0x00000000, /* 48 0x0030 */ 0x00000000, 0x00000000, 0x00000000, 0x00000000, /* 52 0x0034 */ 0x00000000, 0x00000000, 0x00000000, 0x00000000, /* 56 0x0038 */ 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; /** * Pass mode table. */ private static int[] passMode = new int[] { 0x10000004 // 0001 }; /** * Vertical mode table. */ private static int[] vertMode = new int[] { 0x06000007, // 0000011 0x0c000006, // 000011 0x60000003, // 011 0x80000001, // 1 0x40000003, // 010 0x08000006, // 000010 0x04000007 // 0000010 }; /** * Horizontal mode table. */ private static int[] horzMode = new int[] { 0x20000003 // 001 }; /** * Black and white terminating code table. */ private static int[][] termCodes = new int[][] {termCodesWhite, termCodesBlack}; /** * Black and white make-up code table. */ private static int[][] makeupCodes = new int[][] {makeupCodesWhite, makeupCodesBlack}; /** * Black and white pass mode table. */ private static int[][] pass = new int[][] {passMode, passMode}; /** * Black and white vertical mode table. */ private static int[][] vert = new int[][] {vertMode, vertMode}; /** * Black and white horizontal mode table. */ private static int[][] horz = new int[][] {horzMode, horzMode}; // --- End tables for CCITT compression --- /** * Whether bits are inserted in reverse order (TIFF FillOrder 2). */ private boolean inverseFill; /** * Output bit buffer. */ private int bits; /** * Number of bits in the output bit buffer. */ private int ndex; /** * Constructs a TIFFFaxEncoder for CCITT bilevel encoding. * * @param inverseFill Whether bits are inserted in reverse order * (TIFF FillOrder 2). */ TIFFFaxEncoder(boolean inverseFill) { this.inverseFill = inverseFill; } /** * Return min of maxOffset or offset of first pixel * different from pixel at bitOffset. */ private int nextState(byte[] data, int base, int bitOffset, int maxOffset) { if(data == null) { return maxOffset; } int next = base + (bitOffset>>>3); // If the offset is beyond the data already then the minimum of the // current offset and maxOffset must be maxOffset. if(next >= data.length) { return maxOffset; } int end = base + (maxOffset>>>3); if(end == data.length) { // Prevents out of bounds exception below end--; } int extra = bitOffset & 0x7; int testbyte; if((data[next] & (0x80 >>> extra)) != 0) { // look for "0" testbyte = ~(data[next]) & (0xff >>> extra); while (next < end) { if (testbyte != 0) { break; } testbyte = ~(data[++next]) & 0xff; } } else { // look for "1" if ((testbyte = (data[next] & (0xff >>> extra))) != 0) { bitOffset = (next-base)*8 + byteTable[testbyte]; return ((bitOffset < maxOffset) ? bitOffset : maxOffset); } while (next < end) { if ((testbyte = data[++next]&0xff) != 0) { // "1" is in current byte bitOffset = (next-base)*8 + byteTable[testbyte]; return ((bitOffset < maxOffset) ? bitOffset : maxOffset); } } } bitOffset = (next-base)*8 + byteTable[testbyte]; return ((bitOffset < maxOffset) ? bitOffset : maxOffset); } /** * Initialize bit buffer machinery. */ private void initBitBuf() { ndex = 0; bits = 0x00000000; } /** * Get code for run and add to compressed bitstream. */ private int add1DBits(byte[] buf, int where, // byte offs int count, // #pixels in run int color) // color of run { int sixtyfours; int mask; int len = where; sixtyfours = count >>> 6; // count / 64; count = count & 0x3f; // count % 64 if (sixtyfours != 0) { for ( ; sixtyfours > 40; sixtyfours -= 40) { mask = makeupCodes[color][40]; bits |= (mask & 0xfff80000) >>> ndex; ndex += (int)(mask & 0x0000ffff); while (ndex > 7) { buf[len++] = (byte)(bits >>> 24); bits <<= 8; ndex -= 8; } } mask = makeupCodes[color][sixtyfours]; bits |= (mask & 0xfff80000) >>> ndex; ndex += (int)(mask & 0x0000ffff); while (ndex > 7) { buf[len++] = (byte)(bits >>> 24); bits <<= 8; ndex -= 8; } } mask = termCodes[color][count]; bits |= (mask & 0xfff80000) >>> ndex; ndex += (int)(mask & 0x0000ffff); while (ndex > 7) { buf[len++] = (byte)(bits >>> 24); bits <<= 8; ndex -= 8; } return(len - where); } /** * Place entry from mode table into compressed bitstream. */ private int add2DBits(byte[] buf, // compressed buffer int where, // byte offset into compressed buffer int[][] mode, // 2-D mode to be encoded int entry) // mode entry (0 unless vertical) { int mask; int len = where; int color = 0; mask = mode[color][entry]; bits |= (mask & 0xfff80000) >>> ndex; ndex += (int)(mask & 0x0000ffff); while (ndex > 7) { buf[len++] = (byte)(bits >>> 24); bits <<= 8; ndex -= 8; } return(len - where); } /** * Add an End-of-Line (EOL == 0x001) to the compressed bitstream * with optional byte alignment. */ private int addEOL(boolean is1DMode,// 1D encoding boolean addFill, // byte aligned EOLs boolean add1, // add1 ? EOL+1 : EOL+0 byte[] buf, // compressed buffer address int where) // current byte offset into buffer { int len = where; // // Add zero-valued fill bits such that the EOL is aligned as // // xxxx 0000 0000 0001 // if(addFill) { // // Simply increment the bit count. No need to feed bits into // the output buffer at this point as there are at most 7 bits // in the bit buffer, at most 7 are added here, and at most // 13 below making the total 7+7+13 = 27 before the bit feed // at the end of this routine. // ndex += ((ndex <= 4) ? 4 - ndex : 12 - ndex); } // // Write EOL into buffer // if(is1DMode) { bits |= 0x00100000 >>> ndex; ndex += 12; } else { bits |= (add1 ? 0x00180000 : 0x00100000) >>> ndex; ndex += 13; } while (ndex > 7) { buf[len++] = (byte)(bits >>> 24); bits <<= 8; ndex -= 8; } return(len - where); } /** * Add an End-of-Facsimile-Block (EOFB == 0x001001) to the compressed * bitstream. */ private int addEOFB(byte[] buf, // compressed buffer int where) // byte offset into compressed buffer { int len = where; // // eofb code // bits |= 0x00100100 >>> ndex; // // eofb code length // ndex += 24; // // flush all pending bits // while(ndex > 0) { buf[len++] = (byte)(bits >>> 24); bits <<= 8; ndex -= 8; } return(len - where); } /** * One-dimensionally encode a row of data using CCITT Huffman compression. * The bit buffer should be initialized as required before invoking this * method and should be flushed after the method returns. The fill order * is always highest-order to lowest-order bit so the calling routine * should handle bit inversion. */ private int encode1D(byte[] data, int rowOffset, int colOffset, int rowLength, byte[] compData, int compOffset) { int lineAddr = rowOffset; int bitIndex = colOffset; int last = bitIndex + rowLength; int outIndex = compOffset; // // Is first pixel black // int testbit = ((data[lineAddr + (bitIndex>>>3)]&0xff) >>> (7-(bitIndex & 0x7))) & 0x1; int currentColor = BLACK; if(testbit != 0) { outIndex += add1DBits(compData, outIndex, 0, WHITE); } else { currentColor = WHITE; } // // Run-length encode line // while(bitIndex < last) { int bitCount = nextState(data, lineAddr, bitIndex, last) - bitIndex; outIndex += add1DBits(compData, outIndex, bitCount, currentColor); bitIndex += bitCount; currentColor ^= 0x00000001; } return outIndex - compOffset; } /** * Encode a row of data using Modified Huffman Compression also known as * CCITT RLE (Run Lenth Encoding). * * @param data The row of data to compress. * @param rowOffset Starting index in data. * @param colOffset Bit offset within first data[rowOffset]. * @param rowLength Number of bits in the row. * @param compData The compressed data. * * @return The number of bytes saved in the compressed data array. */ synchronized int encodeRLE(byte[] data, int rowOffset, int colOffset, int rowLength, byte[] compData) { // // Initialize bit buffer machinery. // initBitBuf(); // // Run-length encode line. // int outIndex = encode1D(data, rowOffset, colOffset, rowLength, compData, 0); // // Flush pending bits // while(ndex > 0) { compData[outIndex++] = (byte)(bits >>> 24); bits <<= 8; ndex -= 8; } // // Flip the bytes if inverse fill was requested. // if(inverseFill) { byte[] flipTable = TIFFFaxDecoder.flipTable; for(int i = 0; i < outIndex; i++) { compData[i] = flipTable[compData[i]&0xff]; } } return outIndex; } /** * Encode a buffer of data using CCITT T.4 Compression also known as * Group 3 facsimile compression. * * @param is1DMode Whether to perform one-dimensional encoding. * @param isEOLAligned Whether EOL bit sequences should be padded. * @param data The row of data to compress. * @param lineStride Byte step between the same sample in different rows. * @param colOffset Bit offset within first data[rowOffset]. * @param width Number of bits in the row. * @param height Number of rows in the buffer. * @param compData The compressed data. * * @return The number of bytes saved in the compressed data array. */ synchronized int encodeT4(boolean is1DMode, boolean isEOLAligned, byte[] data, int lineStride, int colOffset, int width, int height, byte[] compData) { // // ao, a1, a2 are bit indices in the current line // b1 and b2 are bit indices in the reference line (line above) // color is the current color (WHITE or BLACK) // byte[] refData = data; int lineAddr = 0; int outIndex = 0; initBitBuf(); int KParameter = 2; for(int numRows = 0; numRows < height; numRows++) { if(is1DMode || (numRows % KParameter) == 0) { // 1D encoding // Write EOL+1 outIndex += addEOL(is1DMode, isEOLAligned, true, compData, outIndex); // Encode row outIndex += encode1D(data, lineAddr, colOffset, width, compData, outIndex); } else { // 2D encoding. // Write EOL+0 outIndex += addEOL(is1DMode, isEOLAligned, false, compData, outIndex); // Set reference to previous line int refAddr = lineAddr - lineStride; // Encode row int a0 = colOffset; int last = a0 + width; int testbit = ((data[lineAddr + (a0>>>3)]&0xff) >>> (7-(a0 & 0x7))) & 0x1; int a1 = testbit != 0 ? a0 : nextState(data, lineAddr, a0, last); testbit = ((refData[refAddr + (a0>>>3)]&0xff) >>> (7-(a0 & 0x7))) & 0x1; int b1 = testbit != 0 ? a0 : nextState(refData, refAddr, a0, last); // The current color is set to WHITE at line start int color = WHITE; while(true) { int b2 = nextState(refData, refAddr, b1, last); if(b2 < a1) { // pass mode outIndex += add2DBits(compData, outIndex, pass, 0); a0 = b2; } else { int tmp = b1 - a1 + 3; if((tmp <= 6) && (tmp >= 0)) { // vertical mode outIndex += add2DBits(compData, outIndex, vert, tmp); a0 = a1; } else { // horizontal mode int a2 = nextState(data, lineAddr, a1, last); outIndex += add2DBits(compData, outIndex, horz, 0); outIndex += add1DBits(compData, outIndex, a1-a0, color); outIndex += add1DBits(compData, outIndex, a2-a1, color^1); a0 = a2; } } if(a0 >= last) { break; } color = ((data[lineAddr + (a0>>>3)]&0xff) >>> (7-(a0 & 0x7))) & 0x1; a1 = nextState(data, lineAddr, a0, last); b1 = nextState(refData, refAddr, a0, last); testbit = ((refData[refAddr + (b1>>>3)]&0xff) >>> (7-(b1 & 0x7))) & 0x1; if(testbit == color) { b1 = nextState(refData, refAddr, b1, last); } } } // Skip to next line. lineAddr += lineStride; } for(int i = 0; i < 6; i++) { outIndex += addEOL(is1DMode, isEOLAligned, true, compData, outIndex); } // // flush all pending bits // while(ndex > 0) { compData[outIndex++] = (byte)(bits >>> 24); bits <<= 8; ndex -= 8; } // Flip the bytes if inverse fill was requested. if(inverseFill) { for(int i = 0; i < outIndex; i++) { compData[i] = TIFFFaxDecoder.flipTable[compData[i]&0xff]; } } return outIndex; } /** * Encode a buffer of data using CCITT T.6 Compression also known as * Group 4 facsimile compression. * * @param data The row of data to compress. * @param lineStride Byte step between the same sample in different rows. * @param colOffset Bit offset within first data[rowOffset]. * @param width Number of bits in the row. * @param height Number of rows in the buffer. * @param compData The compressed data. * * @return The number of bytes saved in the compressed data array. */ public synchronized int encodeT6(byte[] data, int lineStride, int colOffset, int width, int height, byte[] compData) { // // ao, a1, a2 are bit indices in the current line // b1 and b2 are bit indices in the reference line (line above) // color is the current color (WHITE or BLACK) // byte[] refData = null; int refAddr = 0; int lineAddr = 0; int outIndex = 0; initBitBuf(); // // Iterate over all lines // while(height-- != 0) { int a0 = colOffset; int last = a0 + width; int testbit = ((data[lineAddr + (a0>>>3)]&0xff) >>> (7-(a0 & 0x7))) & 0x1; int a1 = testbit != 0 ? a0 : nextState(data, lineAddr, a0, last); testbit = refData == null ? 0: ((refData[refAddr + (a0>>>3)]&0xff) >>> (7-(a0 & 0x7))) & 0x1; int b1 = testbit != 0 ? a0 : nextState(refData, refAddr, a0, last); // // The current color is set to WHITE at line start // int color = WHITE; while(true) { int b2 = nextState(refData, refAddr, b1, last); if(b2 < a1) { // pass mode outIndex += add2DBits(compData, outIndex, pass, 0); a0 = b2; } else { int tmp = b1 - a1 + 3; if((tmp <= 6) && (tmp >= 0)) { // vertical mode outIndex += add2DBits(compData, outIndex, vert, tmp); a0 = a1; } else { // horizontal mode int a2 = nextState(data, lineAddr, a1, last); outIndex += add2DBits(compData, outIndex, horz, 0); outIndex += add1DBits(compData, outIndex, a1-a0, color); outIndex += add1DBits(compData, outIndex, a2-a1, color^1); a0 = a2; } } if(a0 >= last) { break; } color = ((data[lineAddr + (a0>>>3)]&0xff) >>> (7-(a0 & 0x7))) & 0x1; a1 = nextState(data, lineAddr, a0, last); b1 = nextState(refData, refAddr, a0, last); testbit = refData == null ? 0: ((refData[refAddr + (b1>>>3)]&0xff) >>> (7-(b1 & 0x7))) & 0x1; if(testbit == color) { b1 = nextState(refData, refAddr, b1, last); } } refData = data; refAddr = lineAddr; lineAddr += lineStride; } // End while(height--) // // append eofb // outIndex += addEOFB(compData, outIndex); // Flip the bytes if inverse fill was requested. if(inverseFill) { for(int i = 0; i < outIndex; i++) { compData[i] = TIFFFaxDecoder.flipTable[compData[i]&0xff]; } } return outIndex; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/TIFFImageDecoder.java0000644000175000017500000001131610472445724027411 0ustar mathieumathieu/* * $RCSfile: TIFFImageDecoder.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2006-08-22 00:12:04 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl; import java.awt.Point; import java.awt.Rectangle; import java.awt.Transparency; import java.awt.color.ColorSpace; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferShort; import java.awt.image.DataBufferUShort; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.IndexColorModel; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.PixelInterleavedSampleModel; import java.awt.image.ComponentColorModel; import java.io.File; import java.io.InputStream; import java.io.IOException; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.ImageDecoder; import com.sun.media.jai.codec.ImageDecoderImpl; import com.sun.media.jai.codec.ImageDecodeParam; import com.sun.media.jai.codec.SeekableStream; import com.sun.media.jai.codec.TIFFDecodeParam; import com.sun.media.jai.codec.TIFFDirectory; import com.sun.media.jai.codec.TIFFField; import com.sun.media.jai.codecimpl.util.RasterFactory; /** * A baseline TIFF reader. The reader has some functionality in addition to * the baseline specifications for Bilevel images, for which the group 3 and * group 4 decompression schemes have been implemented. Support for LZW * decompression has also been added. Support for Horizontal differencing * predictor decoding is also included, when used with LZW compression. * However, this support is limited to data with bitsPerSample value of 8. * When reading in RGB images, support for alpha and extraSamples being * present has been added. Support for reading in images with 16 bit samples * has been added. Support for the SampleFormat tag (signed samples as well * as floating-point samples) has also been added. In all other cases, support * is limited to Baseline specifications. * * @since EA3 * */ public class TIFFImageDecoder extends ImageDecoderImpl { // All the TIFF tags that we care about public static final int TIFF_IMAGE_WIDTH = 256; public static final int TIFF_IMAGE_LENGTH = 257; public static final int TIFF_BITS_PER_SAMPLE = 258; public static final int TIFF_COMPRESSION = 259; public static final int TIFF_PHOTOMETRIC_INTERPRETATION = 262; public static final int TIFF_FILL_ORDER = 266; public static final int TIFF_STRIP_OFFSETS = 273; public static final int TIFF_SAMPLES_PER_PIXEL = 277; public static final int TIFF_ROWS_PER_STRIP = 278; public static final int TIFF_STRIP_BYTE_COUNTS = 279; public static final int TIFF_X_RESOLUTION = 282; public static final int TIFF_Y_RESOLUTION = 283; public static final int TIFF_PLANAR_CONFIGURATION = 284; public static final int TIFF_T4_OPTIONS = 292; public static final int TIFF_T6_OPTIONS = 293; public static final int TIFF_RESOLUTION_UNIT = 296; public static final int TIFF_PREDICTOR = 317; public static final int TIFF_COLORMAP = 320; public static final int TIFF_TILE_WIDTH = 322; public static final int TIFF_TILE_LENGTH = 323; public static final int TIFF_TILE_OFFSETS = 324; public static final int TIFF_TILE_BYTE_COUNTS = 325; public static final int TIFF_EXTRA_SAMPLES = 338; public static final int TIFF_SAMPLE_FORMAT = 339; public static final int TIFF_S_MIN_SAMPLE_VALUE = 340; public static final int TIFF_S_MAX_SAMPLE_VALUE = 341; public TIFFImageDecoder(SeekableStream input, ImageDecodeParam param) { super(input, param); } public int getNumPages() throws IOException { try { return TIFFDirectory.getNumDirectories(input); } catch(Exception e) { throw CodecUtils.toIOException(e); } } public RenderedImage decodeAsRenderedImage(int page) throws IOException { if ((page < 0) || (page >= getNumPages())) { throw new IOException(JaiI18N.getString("TIFFImageDecoder0")); } try { return new TIFFImage(input, (TIFFDecodeParam)param, page); } catch(Exception e) { throw CodecUtils.toIOException(e); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/PNMCodec.java0000644000175000017500000001246310203035544026007 0ustar mathieumathieu/* * $RCSfile: PNMCodec.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:38 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.io.BufferedInputStream; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import com.sun.media.jai.codec.ForwardSeekableStream; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.ImageDecoder; import com.sun.media.jai.codec.ImageDecodeParam; import com.sun.media.jai.codec.ImageEncoder; import com.sun.media.jai.codec.ImageEncodeParam; import com.sun.media.jai.codec.PNMEncodeParam; import com.sun.media.jai.codec.SeekableStream; /** * A subclass of ImageCodec that handles the * PNM family of formats (PBM, PGM, PPM). * *

    The PBM format encodes a single-banded, 1-bit image. The PGM * format encodes a single-banded image of any bit depth between 1 and * 32. The PPM format encodes three-banded images of any bit depth * between 1 and 32. All formats have an ASCII and a raw * representation. * * @since EA2 */ public final class PNMCodec extends ImageCodec { /** Constructs an instance of PNMCodec. */ public PNMCodec() {} /** Returns the name of the format handled by this codec. */ public String getFormatName() { return "pnm"; } public Class getEncodeParamClass() { return com.sun.media.jai.codec.PNMEncodeParam.class; } public Class getDecodeParamClass() { return Object.class; } public boolean canEncodeImage(RenderedImage im, ImageEncodeParam param) { SampleModel sampleModel = im.getSampleModel(); int dataType = sampleModel.getTransferType(); if ((dataType == DataBuffer.TYPE_FLOAT) || (dataType == DataBuffer.TYPE_DOUBLE)) { return false; } int numBands = sampleModel.getNumBands(); if (numBands != 1 && numBands != 3) { return false; } return true; } /** * Instantiates a PNMImageEncoder to write to the * given OutputStream. * * @param dst the OutputStream to write to. * @param param an instance of PNMEncodeParam used to * control the encoding process, or null. A * ClassCastException will be thrown if * param is non-null but not an instance of * PNMEncodeParam. */ protected ImageEncoder createImageEncoder(OutputStream dst, ImageEncodeParam param) { PNMEncodeParam p = null; if (param != null) { p = (PNMEncodeParam)param; // May throw a ClassCast exception } return new PNMImageEncoder(dst, p); } /** * Instantiates a PNMImageDecoder to read from the * given InputStream. * *

    By overriding this method, PNMCodec is able to * ensure that a ForwardSeekableStream is used to * wrap the source InputStream instead of the a * general (and more expensive) subclass of * SeekableStream. Since the PNM decoder does not * require the ability to seek backwards in its input, this allows * for greater efficiency. * * @param src the InputStream to read from. * @param param an instance of ImageDecodeParam used to * control the decoding process, or null. * This parameter is ignored by this class. */ protected ImageDecoder createImageDecoder(InputStream src, ImageDecodeParam param) { // Add buffering for efficiency if (!(src instanceof BufferedInputStream)) { src = new BufferedInputStream(src); } return new PNMImageDecoder(new ForwardSeekableStream(src), null); } /** * Instantiates a PNMImageDecoder to read from the * given SeekableStream. * * @param src the SeekableStream to read from. * @param param an instance of ImageDecodeParam used to * control the decoding process, or null. * This parameter is ignored by this class. */ protected ImageDecoder createImageDecoder(SeekableStream src, ImageDecodeParam param) { return new PNMImageDecoder(src, null); } /** * Returns the number of bytes from the beginning of the data required * to recognize it as being in PNM format. */ public int getNumHeaderBytes() { return 2; } /** * Returns true if the header bytes indicate PNM format. * * @param header an array of bytes containing the initial bytes of the * input data. */ public boolean isFormatRecognized(byte[] header) { return ((header[0] == 'P') && (header[1] >= '1') && (header[1] <= '6')); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/TIFFFaxDecoder.java0000644000175000017500000014101310336211611027064 0ustar mathieumathieu/* * $RCSfile: TIFFFaxDecoder.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.3 $ * $Date: 2005-11-14 22:44:48 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl; import java.awt.image.WritableRaster; class TIFFFaxDecoder { private int bitPointer, bytePointer; private byte[] data; private int w, h; private int fillOrder; // Data structures needed to store changing elements for the previous // and the current scanline private int changingElemSize = 0; private int prevChangingElems[]; private int currChangingElems[]; // Element at which to start search in getNextChangingElement private int lastChangingElement = 0; private int compression = 2; // Variables set by T4Options private int uncompressedMode = 0; private int fillBits = 0; private int oneD; static int table1[] = { 0x00, // 0 bits are left in first byte - SHOULD NOT HAPPEN 0x01, // 1 bits are left in first byte 0x03, // 2 bits are left in first byte 0x07, // 3 bits are left in first byte 0x0f, // 4 bits are left in first byte 0x1f, // 5 bits are left in first byte 0x3f, // 6 bits are left in first byte 0x7f, // 7 bits are left in first byte 0xff // 8 bits are left in first byte }; static int table2[] = { 0x00, // 0 0x80, // 1 0xc0, // 2 0xe0, // 3 0xf0, // 4 0xf8, // 5 0xfc, // 6 0xfe, // 7 0xff // 8 }; // Table to be used when fillOrder = 2, for flipping bytes. static byte flipTable[] = { 0, -128, 64, -64, 32, -96, 96, -32, 16, -112, 80, -48, 48, -80, 112, -16, 8, -120, 72, -56, 40, -88, 104, -24, 24, -104, 88, -40, 56, -72, 120, -8, 4, -124, 68, -60, 36, -92, 100, -28, 20, -108, 84, -44, 52, -76, 116, -12, 12, -116, 76, -52, 44, -84, 108, -20, 28, -100, 92, -36, 60, -68, 124, -4, 2, -126, 66, -62, 34, -94, 98, -30, 18, -110, 82, -46, 50, -78, 114, -14, 10, -118, 74, -54, 42, -86, 106, -22, 26, -102, 90, -38, 58, -70, 122, -6, 6, -122, 70, -58, 38, -90, 102, -26, 22, -106, 86, -42, 54, -74, 118, -10, 14, -114, 78, -50, 46, -82, 110, -18, 30, -98, 94, -34, 62, -66, 126, -2, 1, -127, 65, -63, 33, -95, 97, -31, 17, -111, 81, -47, 49, -79, 113, -15, 9, -119, 73, -55, 41, -87, 105, -23, 25, -103, 89, -39, 57, -71, 121, -7, 5, -123, 69, -59, 37, -91, 101, -27, 21, -107, 85, -43, 53, -75, 117, -11, 13, -115, 77, -51, 45, -83, 109, -19, 29, -99, 93, -35, 61, -67, 125, -3, 3, -125, 67, -61, 35, -93, 99, -29, 19, -109, 83, -45, 51, -77, 115, -13, 11, -117, 75, -53, 43, -85, 107, -21, 27, -101, 91, -37, 59, -69, 123, -5, 7, -121, 71, -57, 39, -89, 103, -25, 23, -105, 87, -41, 55, -73, 119, -9, 15, -113, 79, -49, 47, -81, 111, -17, 31, -97, 95, -33, 63, -65, 127, -1, }; // The main 10 bit white runs lookup table static short white[] = { // 0 - 7 6430, 6400, 6400, 6400, 3225, 3225, 3225, 3225, // 8 - 15 944, 944, 944, 944, 976, 976, 976, 976, // 16 - 23 1456, 1456, 1456, 1456, 1488, 1488, 1488, 1488, // 24 - 31 718, 718, 718, 718, 718, 718, 718, 718, // 32 - 39 750, 750, 750, 750, 750, 750, 750, 750, // 40 - 47 1520, 1520, 1520, 1520, 1552, 1552, 1552, 1552, // 48 - 55 428, 428, 428, 428, 428, 428, 428, 428, // 56 - 63 428, 428, 428, 428, 428, 428, 428, 428, // 64 - 71 654, 654, 654, 654, 654, 654, 654, 654, // 72 - 79 1072, 1072, 1072, 1072, 1104, 1104, 1104, 1104, // 80 - 87 1136, 1136, 1136, 1136, 1168, 1168, 1168, 1168, // 88 - 95 1200, 1200, 1200, 1200, 1232, 1232, 1232, 1232, // 96 - 103 622, 622, 622, 622, 622, 622, 622, 622, // 104 - 111 1008, 1008, 1008, 1008, 1040, 1040, 1040, 1040, // 112 - 119 44, 44, 44, 44, 44, 44, 44, 44, // 120 - 127 44, 44, 44, 44, 44, 44, 44, 44, // 128 - 135 396, 396, 396, 396, 396, 396, 396, 396, // 136 - 143 396, 396, 396, 396, 396, 396, 396, 396, // 144 - 151 1712, 1712, 1712, 1712, 1744, 1744, 1744, 1744, // 152 - 159 846, 846, 846, 846, 846, 846, 846, 846, // 160 - 167 1264, 1264, 1264, 1264, 1296, 1296, 1296, 1296, // 168 - 175 1328, 1328, 1328, 1328, 1360, 1360, 1360, 1360, // 176 - 183 1392, 1392, 1392, 1392, 1424, 1424, 1424, 1424, // 184 - 191 686, 686, 686, 686, 686, 686, 686, 686, // 192 - 199 910, 910, 910, 910, 910, 910, 910, 910, // 200 - 207 1968, 1968, 1968, 1968, 2000, 2000, 2000, 2000, // 208 - 215 2032, 2032, 2032, 2032, 16, 16, 16, 16, // 216 - 223 10257, 10257, 10257, 10257, 12305, 12305, 12305, 12305, // 224 - 231 330, 330, 330, 330, 330, 330, 330, 330, // 232 - 239 330, 330, 330, 330, 330, 330, 330, 330, // 240 - 247 330, 330, 330, 330, 330, 330, 330, 330, // 248 - 255 330, 330, 330, 330, 330, 330, 330, 330, // 256 - 263 362, 362, 362, 362, 362, 362, 362, 362, // 264 - 271 362, 362, 362, 362, 362, 362, 362, 362, // 272 - 279 362, 362, 362, 362, 362, 362, 362, 362, // 280 - 287 362, 362, 362, 362, 362, 362, 362, 362, // 288 - 295 878, 878, 878, 878, 878, 878, 878, 878, // 296 - 303 1904, 1904, 1904, 1904, 1936, 1936, 1936, 1936, // 304 - 311 -18413, -18413, -16365, -16365, -14317, -14317, -10221, -10221, // 312 - 319 590, 590, 590, 590, 590, 590, 590, 590, // 320 - 327 782, 782, 782, 782, 782, 782, 782, 782, // 328 - 335 1584, 1584, 1584, 1584, 1616, 1616, 1616, 1616, // 336 - 343 1648, 1648, 1648, 1648, 1680, 1680, 1680, 1680, // 344 - 351 814, 814, 814, 814, 814, 814, 814, 814, // 352 - 359 1776, 1776, 1776, 1776, 1808, 1808, 1808, 1808, // 360 - 367 1840, 1840, 1840, 1840, 1872, 1872, 1872, 1872, // 368 - 375 6157, 6157, 6157, 6157, 6157, 6157, 6157, 6157, // 376 - 383 6157, 6157, 6157, 6157, 6157, 6157, 6157, 6157, // 384 - 391 -12275, -12275, -12275, -12275, -12275, -12275, -12275, -12275, // 392 - 399 -12275, -12275, -12275, -12275, -12275, -12275, -12275, -12275, // 400 - 407 14353, 14353, 14353, 14353, 16401, 16401, 16401, 16401, // 408 - 415 22547, 22547, 24595, 24595, 20497, 20497, 20497, 20497, // 416 - 423 18449, 18449, 18449, 18449, 26643, 26643, 28691, 28691, // 424 - 431 30739, 30739, -32749, -32749, -30701, -30701, -28653, -28653, // 432 - 439 -26605, -26605, -24557, -24557, -22509, -22509, -20461, -20461, // 440 - 447 8207, 8207, 8207, 8207, 8207, 8207, 8207, 8207, // 448 - 455 72, 72, 72, 72, 72, 72, 72, 72, // 456 - 463 72, 72, 72, 72, 72, 72, 72, 72, // 464 - 471 72, 72, 72, 72, 72, 72, 72, 72, // 472 - 479 72, 72, 72, 72, 72, 72, 72, 72, // 480 - 487 72, 72, 72, 72, 72, 72, 72, 72, // 488 - 495 72, 72, 72, 72, 72, 72, 72, 72, // 496 - 503 72, 72, 72, 72, 72, 72, 72, 72, // 504 - 511 72, 72, 72, 72, 72, 72, 72, 72, // 512 - 519 104, 104, 104, 104, 104, 104, 104, 104, // 520 - 527 104, 104, 104, 104, 104, 104, 104, 104, // 528 - 535 104, 104, 104, 104, 104, 104, 104, 104, // 536 - 543 104, 104, 104, 104, 104, 104, 104, 104, // 544 - 551 104, 104, 104, 104, 104, 104, 104, 104, // 552 - 559 104, 104, 104, 104, 104, 104, 104, 104, // 560 - 567 104, 104, 104, 104, 104, 104, 104, 104, // 568 - 575 104, 104, 104, 104, 104, 104, 104, 104, // 576 - 583 4107, 4107, 4107, 4107, 4107, 4107, 4107, 4107, // 584 - 591 4107, 4107, 4107, 4107, 4107, 4107, 4107, 4107, // 592 - 599 4107, 4107, 4107, 4107, 4107, 4107, 4107, 4107, // 600 - 607 4107, 4107, 4107, 4107, 4107, 4107, 4107, 4107, // 608 - 615 266, 266, 266, 266, 266, 266, 266, 266, // 616 - 623 266, 266, 266, 266, 266, 266, 266, 266, // 624 - 631 266, 266, 266, 266, 266, 266, 266, 266, // 632 - 639 266, 266, 266, 266, 266, 266, 266, 266, // 640 - 647 298, 298, 298, 298, 298, 298, 298, 298, // 648 - 655 298, 298, 298, 298, 298, 298, 298, 298, // 656 - 663 298, 298, 298, 298, 298, 298, 298, 298, // 664 - 671 298, 298, 298, 298, 298, 298, 298, 298, // 672 - 679 524, 524, 524, 524, 524, 524, 524, 524, // 680 - 687 524, 524, 524, 524, 524, 524, 524, 524, // 688 - 695 556, 556, 556, 556, 556, 556, 556, 556, // 696 - 703 556, 556, 556, 556, 556, 556, 556, 556, // 704 - 711 136, 136, 136, 136, 136, 136, 136, 136, // 712 - 719 136, 136, 136, 136, 136, 136, 136, 136, // 720 - 727 136, 136, 136, 136, 136, 136, 136, 136, // 728 - 735 136, 136, 136, 136, 136, 136, 136, 136, // 736 - 743 136, 136, 136, 136, 136, 136, 136, 136, // 744 - 751 136, 136, 136, 136, 136, 136, 136, 136, // 752 - 759 136, 136, 136, 136, 136, 136, 136, 136, // 760 - 767 136, 136, 136, 136, 136, 136, 136, 136, // 768 - 775 168, 168, 168, 168, 168, 168, 168, 168, // 776 - 783 168, 168, 168, 168, 168, 168, 168, 168, // 784 - 791 168, 168, 168, 168, 168, 168, 168, 168, // 792 - 799 168, 168, 168, 168, 168, 168, 168, 168, // 800 - 807 168, 168, 168, 168, 168, 168, 168, 168, // 808 - 815 168, 168, 168, 168, 168, 168, 168, 168, // 816 - 823 168, 168, 168, 168, 168, 168, 168, 168, // 824 - 831 168, 168, 168, 168, 168, 168, 168, 168, // 832 - 839 460, 460, 460, 460, 460, 460, 460, 460, // 840 - 847 460, 460, 460, 460, 460, 460, 460, 460, // 848 - 855 492, 492, 492, 492, 492, 492, 492, 492, // 856 - 863 492, 492, 492, 492, 492, 492, 492, 492, // 864 - 871 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, // 872 - 879 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, // 880 - 887 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, // 888 - 895 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, // 896 - 903 200, 200, 200, 200, 200, 200, 200, 200, // 904 - 911 200, 200, 200, 200, 200, 200, 200, 200, // 912 - 919 200, 200, 200, 200, 200, 200, 200, 200, // 920 - 927 200, 200, 200, 200, 200, 200, 200, 200, // 928 - 935 200, 200, 200, 200, 200, 200, 200, 200, // 936 - 943 200, 200, 200, 200, 200, 200, 200, 200, // 944 - 951 200, 200, 200, 200, 200, 200, 200, 200, // 952 - 959 200, 200, 200, 200, 200, 200, 200, 200, // 960 - 967 232, 232, 232, 232, 232, 232, 232, 232, // 968 - 975 232, 232, 232, 232, 232, 232, 232, 232, // 976 - 983 232, 232, 232, 232, 232, 232, 232, 232, // 984 - 991 232, 232, 232, 232, 232, 232, 232, 232, // 992 - 999 232, 232, 232, 232, 232, 232, 232, 232, // 1000 - 1007 232, 232, 232, 232, 232, 232, 232, 232, // 1008 - 1015 232, 232, 232, 232, 232, 232, 232, 232, // 1016 - 1023 232, 232, 232, 232, 232, 232, 232, 232, }; // Additional make up codes for both White and Black runs static short additionalMakeup[] = { 28679, 28679, 31752, (short)32777, (short)33801, (short)34825, (short)35849, (short)36873, (short)29703, (short)29703, (short)30727, (short)30727, (short)37897, (short)38921, (short)39945, (short)40969 }; // Initial black run look up table, uses the first 4 bits of a code static short initBlack[] = { // 0 - 7 3226, 6412, 200, 168, 38, 38, 134, 134, // 8 - 15 100, 100, 100, 100, 68, 68, 68, 68 }; // static short twoBitBlack[] = {292, 260, 226, 226}; // 0 - 3 // Main black run table, using the last 9 bits of possible 13 bit code static short black[] = { // 0 - 7 62, 62, 30, 30, 0, 0, 0, 0, // 8 - 15 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 23 0, 0, 0, 0, 0, 0, 0, 0, // 24 - 31 0, 0, 0, 0, 0, 0, 0, 0, // 32 - 39 3225, 3225, 3225, 3225, 3225, 3225, 3225, 3225, // 40 - 47 3225, 3225, 3225, 3225, 3225, 3225, 3225, 3225, // 48 - 55 3225, 3225, 3225, 3225, 3225, 3225, 3225, 3225, // 56 - 63 3225, 3225, 3225, 3225, 3225, 3225, 3225, 3225, // 64 - 71 588, 588, 588, 588, 588, 588, 588, 588, // 72 - 79 1680, 1680, 20499, 22547, 24595, 26643, 1776, 1776, // 80 - 87 1808, 1808, -24557, -22509, -20461, -18413, 1904, 1904, // 88 - 95 1936, 1936, -16365, -14317, 782, 782, 782, 782, // 96 - 103 814, 814, 814, 814, -12269, -10221, 10257, 10257, // 104 - 111 12305, 12305, 14353, 14353, 16403, 18451, 1712, 1712, // 112 - 119 1744, 1744, 28691, 30739, -32749, -30701, -28653, -26605, // 120 - 127 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, // 128 - 135 424, 424, 424, 424, 424, 424, 424, 424, // 136 - 143 424, 424, 424, 424, 424, 424, 424, 424, // 144 - 151 424, 424, 424, 424, 424, 424, 424, 424, // 152 - 159 424, 424, 424, 424, 424, 424, 424, 424, // 160 - 167 750, 750, 750, 750, 1616, 1616, 1648, 1648, // 168 - 175 1424, 1424, 1456, 1456, 1488, 1488, 1520, 1520, // 176 - 183 1840, 1840, 1872, 1872, 1968, 1968, 8209, 8209, // 184 - 191 524, 524, 524, 524, 524, 524, 524, 524, // 192 - 199 556, 556, 556, 556, 556, 556, 556, 556, // 200 - 207 1552, 1552, 1584, 1584, 2000, 2000, 2032, 2032, // 208 - 215 976, 976, 1008, 1008, 1040, 1040, 1072, 1072, // 216 - 223 1296, 1296, 1328, 1328, 718, 718, 718, 718, // 224 - 231 456, 456, 456, 456, 456, 456, 456, 456, // 232 - 239 456, 456, 456, 456, 456, 456, 456, 456, // 240 - 247 456, 456, 456, 456, 456, 456, 456, 456, // 248 - 255 456, 456, 456, 456, 456, 456, 456, 456, // 256 - 263 326, 326, 326, 326, 326, 326, 326, 326, // 264 - 271 326, 326, 326, 326, 326, 326, 326, 326, // 272 - 279 326, 326, 326, 326, 326, 326, 326, 326, // 280 - 287 326, 326, 326, 326, 326, 326, 326, 326, // 288 - 295 326, 326, 326, 326, 326, 326, 326, 326, // 296 - 303 326, 326, 326, 326, 326, 326, 326, 326, // 304 - 311 326, 326, 326, 326, 326, 326, 326, 326, // 312 - 319 326, 326, 326, 326, 326, 326, 326, 326, // 320 - 327 358, 358, 358, 358, 358, 358, 358, 358, // 328 - 335 358, 358, 358, 358, 358, 358, 358, 358, // 336 - 343 358, 358, 358, 358, 358, 358, 358, 358, // 344 - 351 358, 358, 358, 358, 358, 358, 358, 358, // 352 - 359 358, 358, 358, 358, 358, 358, 358, 358, // 360 - 367 358, 358, 358, 358, 358, 358, 358, 358, // 368 - 375 358, 358, 358, 358, 358, 358, 358, 358, // 376 - 383 358, 358, 358, 358, 358, 358, 358, 358, // 384 - 391 490, 490, 490, 490, 490, 490, 490, 490, // 392 - 399 490, 490, 490, 490, 490, 490, 490, 490, // 400 - 407 4113, 4113, 6161, 6161, 848, 848, 880, 880, // 408 - 415 912, 912, 944, 944, 622, 622, 622, 622, // 416 - 423 654, 654, 654, 654, 1104, 1104, 1136, 1136, // 424 - 431 1168, 1168, 1200, 1200, 1232, 1232, 1264, 1264, // 432 - 439 686, 686, 686, 686, 1360, 1360, 1392, 1392, // 440 - 447 12, 12, 12, 12, 12, 12, 12, 12, // 448 - 455 390, 390, 390, 390, 390, 390, 390, 390, // 456 - 463 390, 390, 390, 390, 390, 390, 390, 390, // 464 - 471 390, 390, 390, 390, 390, 390, 390, 390, // 472 - 479 390, 390, 390, 390, 390, 390, 390, 390, // 480 - 487 390, 390, 390, 390, 390, 390, 390, 390, // 488 - 495 390, 390, 390, 390, 390, 390, 390, 390, // 496 - 503 390, 390, 390, 390, 390, 390, 390, 390, // 504 - 511 390, 390, 390, 390, 390, 390, 390, 390, }; static byte twoDCodes[] = { // 0 - 7 80, 88, 23, 71, 30, 30, 62, 62, // 8 - 15 4, 4, 4, 4, 4, 4, 4, 4, // 16 - 23 11, 11, 11, 11, 11, 11, 11, 11, // 24 - 31 11, 11, 11, 11, 11, 11, 11, 11, // 32 - 39 35, 35, 35, 35, 35, 35, 35, 35, // 40 - 47 35, 35, 35, 35, 35, 35, 35, 35, // 48 - 55 51, 51, 51, 51, 51, 51, 51, 51, // 56 - 63 51, 51, 51, 51, 51, 51, 51, 51, // 64 - 71 41, 41, 41, 41, 41, 41, 41, 41, // 72 - 79 41, 41, 41, 41, 41, 41, 41, 41, // 80 - 87 41, 41, 41, 41, 41, 41, 41, 41, // 88 - 95 41, 41, 41, 41, 41, 41, 41, 41, // 96 - 103 41, 41, 41, 41, 41, 41, 41, 41, // 104 - 111 41, 41, 41, 41, 41, 41, 41, 41, // 112 - 119 41, 41, 41, 41, 41, 41, 41, 41, // 120 - 127 41, 41, 41, 41, 41, 41, 41, 41, }; /** * @param fillOrder The fill order of the compressed data bytes. * @param compData Array containing compressed data. * @param w * @param h */ public TIFFFaxDecoder(int fillOrder, int w, int h) { this.fillOrder = fillOrder; this.w = w; this.h = h; this.bitPointer = 0; this.bytePointer = 0; this.prevChangingElems = new int[w+1]; this.currChangingElems = new int[w+1]; } // One-dimensional decoding methods public void decode1D(byte[] buffer, byte[] compData, int startX, int height) { this.data = compData; int lineOffset = 0; int scanlineStride = (w + 7)/8; bitPointer = 0; bytePointer = 0; for (int i = 0; i < height; i++) { decodeNextScanline(buffer, lineOffset, startX); lineOffset += scanlineStride; } } public void decodeNextScanline(byte[] buffer, int lineOffset, int bitOffset) { int bits = 0, code = 0, isT = 0; int current, entry, twoBits; boolean isWhite = true; int dstEnd = 0; // Initialize starting of the changing elements array changingElemSize = 0; // While scanline not complete while (bitOffset < w) { while (isWhite) { // White run current = nextNBits(10); entry = white[current]; // Get the 3 fields from the entry isT = entry & 0x0001; bits = (entry >>> 1) & 0x0f; if (bits == 12) { // Additional Make up code // Get the next 2 bits twoBits = nextLesserThan8Bits(2); // Consolidate the 2 new bits and last 2 bits into 4 bits current = ((current << 2) & 0x000c) | twoBits; entry = additionalMakeup[current]; bits = (entry >>> 1) & 0x07; // 3 bits 0000 0111 code = (entry >>> 4) & 0x0fff; // 12 bits bitOffset += code; // Skip white run updatePointer(4 - bits); } else if (bits == 0) { // ERROR throw new RuntimeException(JaiI18N.getString("TIFFFaxDecoder0")); } else if (bits == 15) { // EOL //throw new Error(JaiI18N.getString("TIFFFaxDecoder1")); // // Instead of throwing an error, assume that the EOL // was premature. Rewind the pointers to the start // of the EOL and return, thereby stopping decoding // this line. // updatePointer(12); return; } else { // 11 bits - 0000 0111 1111 1111 = 0x07ff code = (entry >>> 5) & 0x07ff; bitOffset += code; updatePointer(10 - bits); if (isT == 0) { isWhite = false; currChangingElems[changingElemSize++] = bitOffset; } } } // Check whether this run completed one width, if so // advance to next byte boundary for compression = 2. if (bitOffset == w) { if (compression == 2) { advancePointer(); } break; } while (isWhite == false) { // Black run current = nextLesserThan8Bits(4); entry = initBlack[current]; // Get the 3 fields from the entry isT = entry & 0x0001; bits = (entry >>> 1) & 0x000f; code = (entry >>> 5) & 0x07ff; if (code == 100) { current = nextNBits(9); entry = black[current]; // Get the 3 fields from the entry isT = entry & 0x0001; bits = (entry >>> 1) & 0x000f; code = (entry >>> 5) & 0x07ff; if (bits == 12) { // Additional makeup codes updatePointer(5); current = nextLesserThan8Bits(4); entry = additionalMakeup[current]; bits = (entry >>> 1) & 0x07; // 3 bits 0000 0111 code = (entry >>> 4) & 0x0fff; // 12 bits setToBlack(buffer, lineOffset, bitOffset, code); bitOffset += code; updatePointer(4 - bits); } else if (bits == 15) { // EOL code //throw new Error(JaiI18N.getString("TIFFFaxDecoder2")); // // Instead of throwing an error, assume that the EOL // was premature. Rewind the pointers to the start // of the EOL and return, thereby stopping decoding // this line. // updatePointer(12); return; } else { setToBlack(buffer, lineOffset, bitOffset, code); bitOffset += code; updatePointer(9 - bits); if (isT == 0) { isWhite = true; currChangingElems[changingElemSize++] = bitOffset; } } } else if (code == 200) { // Is a Terminating code current = nextLesserThan8Bits(2); entry = twoBitBlack[current]; code = (entry >>> 5) & 0x07ff; bits = (entry >>> 1) & 0x0f; setToBlack(buffer, lineOffset, bitOffset, code); bitOffset += code; updatePointer(2 - bits); isWhite = true; currChangingElems[changingElemSize++] = bitOffset; } else { // Is a Terminating code setToBlack(buffer, lineOffset, bitOffset, code); bitOffset += code; updatePointer(4 - bits); isWhite = true; currChangingElems[changingElemSize++] = bitOffset; } } // Check whether this run completed one width if (bitOffset == w) { if (compression == 2) { advancePointer(); } break; } } currChangingElems[changingElemSize++] = bitOffset; } // Two-dimensional decoding methods public void decode2D(byte[] buffer, byte compData[], int startX, int height, long tiffT4Options) { this.data = compData; compression = 3; bitPointer = 0; bytePointer = 0; int scanlineStride = (w + 7)/8; int a0, a1, b1, b2; int[] b = new int[2]; int entry, code, bits, color; boolean isWhite; int currIndex = 0; int temp[]; // fillBits - dealt with this in readEOL // 1D/2D encoding - dealt with this in readEOL // uncompressedMode - haven't dealt with this yet. // TODO: Deal with uncompressedMode, aastha 03/03/1999 oneD = (int)(tiffT4Options & 0x01); uncompressedMode = (int)((tiffT4Options & 0x02) >> 1); fillBits = (int)((tiffT4Options & 0x04) >> 2); // The data must start with an EOL code if (readEOL(true) != 1) { throw new RuntimeException(JaiI18N.getString("TIFFFaxDecoder3")); } int lineOffset = 0; int bitOffset; // Then the 1D encoded scanline data will occur, changing elements // array gets set. decodeNextScanline(buffer, lineOffset, startX); lineOffset += scanlineStride; for (int lines = 1; lines < height; lines++) { // Every line must begin with an EOL followed by a bit which // indicates whether the following scanline is 1D or 2D encoded. if (readEOL(false) == 0) { // 2D encoded scanline follows // Initialize previous scanlines changing elements, and // initialize current scanline's changing elements array temp = prevChangingElems; prevChangingElems = currChangingElems; currChangingElems = temp; currIndex = 0; // a0 has to be set just before the start of this scanline. a0 = -1; isWhite = true; bitOffset = startX; lastChangingElement = 0; while (bitOffset < w) { // Get the next changing element getNextChangingElement(a0, isWhite, b); b1 = b[0]; b2 = b[1]; // Get the next seven bits entry = nextLesserThan8Bits(7); // Run these through the 2DCodes table entry = (int)(twoDCodes[entry] & 0xff); // Get the code and the number of bits used up code = (entry & 0x78) >>> 3; bits = entry & 0x07; if (code == 0) { if (!isWhite) { setToBlack(buffer, lineOffset, bitOffset, b2 - bitOffset); } bitOffset = a0 = b2; // Set pointer to consume the correct number of bits. updatePointer(7 - bits); } else if (code == 1) { // Horizontal updatePointer(7 - bits); // identify the next 2 codes. int number; if (isWhite) { number = decodeWhiteCodeWord(); bitOffset += number; currChangingElems[currIndex++] = bitOffset; number = decodeBlackCodeWord(); setToBlack(buffer, lineOffset, bitOffset, number); bitOffset += number; currChangingElems[currIndex++] = bitOffset; } else { number = decodeBlackCodeWord(); setToBlack(buffer, lineOffset, bitOffset, number); bitOffset += number; currChangingElems[currIndex++] = bitOffset; number = decodeWhiteCodeWord(); bitOffset += number; currChangingElems[currIndex++] = bitOffset; } a0 = bitOffset; } else if (code <= 8) { // Vertical a1 = b1 + (code - 5); currChangingElems[currIndex++] = a1; // We write the current color till a1 - 1 pos, // since a1 is where the next color starts if (!isWhite) { setToBlack(buffer, lineOffset, bitOffset, a1 - bitOffset); } bitOffset = a0 = a1; isWhite = !isWhite; updatePointer(7 - bits); } else { throw new RuntimeException(JaiI18N.getString("TIFFFaxDecoder4")); } } // Add the changing element beyond the current scanline for the // other color too currChangingElems[currIndex++] = bitOffset; changingElemSize = currIndex; } else { // 1D encoded scanline follows decodeNextScanline(buffer, lineOffset, startX); } lineOffset += scanlineStride; } } public synchronized void decodeT6(byte[] buffer, byte[] compData, int startX, int height, long tiffT6Options) { this.data = compData; compression = 4; bitPointer = 0; bytePointer = 0; int scanlineStride = (w + 7)/8; int bufferOffset = 0; int a0, a1, b1, b2; int entry, code, bits; byte color; boolean isWhite; int currIndex; int temp[]; // Return values from getNextChangingElement int[] b = new int[2]; // uncompressedMode - have written some code for this, but this // has not been tested due to lack of test images using this optional // extension. This code is when code == 11. aastha 03/03/1999 uncompressedMode = (int)((tiffT6Options & 0x02) >> 1); // Local cached reference int[] cce = currChangingElems; // Assume invisible preceding row of all white pixels and insert // both black and white changing elements beyond the end of this // imaginary scanline. changingElemSize = 0; cce[changingElemSize++] = w; cce[changingElemSize++] = w; int lineOffset = 0; int bitOffset; for (int lines = 0; lines < height; lines++) { // a0 has to be set just before the start of the scanline. a0 = -1; isWhite = true; // Assign the changing elements of the previous scanline to // prevChangingElems and start putting this new scanline's // changing elements into the currChangingElems. temp = prevChangingElems; prevChangingElems = currChangingElems; cce = currChangingElems = temp; currIndex = 0; // Start decoding the scanline at startX in the raster bitOffset = startX; // Reset search start position for getNextChangingElement lastChangingElement = 0; // Till one whole scanline is decoded while (bitOffset < w) { // Get the next changing element getNextChangingElement(a0, isWhite, b); b1 = b[0]; b2 = b[1]; // Get the next seven bits entry = nextLesserThan8Bits(7); // Run these through the 2DCodes table entry = (int)(twoDCodes[entry] & 0xff); // Get the code and the number of bits used up code = (entry & 0x78) >>> 3; bits = entry & 0x07; if (code == 0) { // Pass // We always assume WhiteIsZero format for fax. if (!isWhite) { if(b2 > w) { b2 = w; } setToBlack(buffer, lineOffset, bitOffset, b2 - bitOffset); } bitOffset = a0 = b2; // Set pointer to only consume the correct number of bits. updatePointer(7 - bits); } else if (code == 1) { // Horizontal // Set pointer to only consume the correct number of bits. updatePointer(7 - bits); // identify the next 2 alternating color codes. int number; if (isWhite) { // Following are white and black runs number = decodeWhiteCodeWord(); bitOffset += number; cce[currIndex++] = bitOffset; number = decodeBlackCodeWord(); if(number > w - bitOffset) { number = w - bitOffset; } setToBlack(buffer, lineOffset, bitOffset, number); bitOffset += number; cce[currIndex++] = bitOffset; } else { // First a black run and then a white run follows number = decodeBlackCodeWord(); if(number > w - bitOffset) { number = w - bitOffset; } setToBlack(buffer, lineOffset, bitOffset, number); bitOffset += number; cce[currIndex++] = bitOffset; number = decodeWhiteCodeWord(); bitOffset += number; cce[currIndex++] = bitOffset; } a0 = bitOffset; } else if (code <= 8) { // Vertical a1 = b1 + (code - 5); cce[currIndex++] = a1; // We write the current color till a1 - 1 pos, // since a1 is where the next color starts if (!isWhite) { if(a1 > w) { a1 = w; } setToBlack(buffer, lineOffset, bitOffset, a1 - bitOffset); } bitOffset = a0 = a1; isWhite = !isWhite; updatePointer(7 - bits); } else if (code == 11) { if (nextLesserThan8Bits(3) != 7) { throw new RuntimeException(JaiI18N.getString("TIFFFaxDecoder5")); } int zeros = 0; boolean exit = false; while (!exit) { while (nextLesserThan8Bits(1) != 1) { zeros++; } if (zeros > 5) { // Exit code // Zeros before exit code zeros = zeros - 6; if (!isWhite && (zeros > 0)) { cce[currIndex++] = bitOffset; } // Zeros before the exit code bitOffset += zeros; if (zeros > 0) { // Some zeros have been written isWhite = true; } // Read in the bit which specifies the color of // the following run if (nextLesserThan8Bits(1) == 0) { if (!isWhite) { cce[currIndex++] = bitOffset; } isWhite = true; } else { if (isWhite) { cce[currIndex++] = bitOffset; } isWhite = false; } exit = true; } if (zeros == 5) { if (!isWhite) { cce[currIndex++] = bitOffset; } bitOffset += zeros; // Last thing written was white isWhite = true; } else { bitOffset += zeros; cce[currIndex++] = bitOffset; setToBlack(buffer, lineOffset, bitOffset, 1); ++bitOffset; // Last thing written was black isWhite = false; } } } else { throw new RuntimeException(JaiI18N.getString("TIFFFaxDecoder5")); } } // Add the changing element beyond the current scanline for the // other color too, if not already added if (currIndex <= w) cce[currIndex++] = bitOffset; // Number of changing elements in this scanline. changingElemSize = currIndex; lineOffset += scanlineStride; } } private void setToBlack(byte[] buffer, int lineOffset, int bitOffset, int numBits) { int bitNum = 8*lineOffset + bitOffset; int lastBit = bitNum + numBits; int byteNum = bitNum >> 3; // Handle bits in first byte int shift = bitNum & 0x7; if (shift > 0) { int maskVal = 1 << (7 - shift); byte val = buffer[byteNum]; while (maskVal > 0 && bitNum < lastBit) { val |= maskVal; maskVal >>= 1; ++bitNum; } buffer[byteNum] = val; } // Fill in 8 bits at a time byteNum = bitNum >> 3; while (bitNum < lastBit - 7) { buffer[byteNum++] = (byte)255; bitNum += 8; } // Fill in remaining bits while (bitNum < lastBit) { byteNum = bitNum >> 3; buffer[byteNum] |= 1 << (7 - (bitNum & 0x7)); ++bitNum; } } // Returns run length private int decodeWhiteCodeWord() { int current, entry, bits, isT, twoBits, code = -1; int runLength = 0; boolean isWhite = true; while (isWhite) { current = nextNBits(10); entry = white[current]; // Get the 3 fields from the entry isT = entry & 0x0001; bits = (entry >>> 1) & 0x0f; if (bits == 12) { // Additional Make up code // Get the next 2 bits twoBits = nextLesserThan8Bits(2); // Consolidate the 2 new bits and last 2 bits into 4 bits current = ((current << 2) & 0x000c) | twoBits; entry = additionalMakeup[current]; bits = (entry >>> 1) & 0x07; // 3 bits 0000 0111 code = (entry >>> 4) & 0x0fff; // 12 bits runLength += code; updatePointer(4 - bits); } else if (bits == 0) { // ERROR throw new RuntimeException(JaiI18N.getString("TIFFFaxDecoder0")); } else if (bits == 15) { // EOL throw new RuntimeException(JaiI18N.getString("TIFFFaxDecoder1")); } else { // 11 bits - 0000 0111 1111 1111 = 0x07ff code = (entry >>> 5) & 0x07ff; runLength += code; updatePointer(10 - bits); if (isT == 0) { isWhite = false; } } } return runLength; } // Returns run length private int decodeBlackCodeWord() { int current, entry, bits, isT, twoBits, code = -1; int runLength = 0; boolean isWhite = false; while (!isWhite) { current = nextLesserThan8Bits(4); entry = initBlack[current]; // Get the 3 fields from the entry isT = entry & 0x0001; bits = (entry >>> 1) & 0x000f; code = (entry >>> 5) & 0x07ff; if (code == 100) { current = nextNBits(9); entry = black[current]; // Get the 3 fields from the entry isT = entry & 0x0001; bits = (entry >>> 1) & 0x000f; code = (entry >>> 5) & 0x07ff; if (bits == 12) { // Additional makeup codes updatePointer(5); current = nextLesserThan8Bits(4); entry = additionalMakeup[current]; bits = (entry >>> 1) & 0x07; // 3 bits 0000 0111 code = (entry >>> 4) & 0x0fff; // 12 bits runLength += code; updatePointer(4 - bits); } else if (bits == 15) { // EOL code throw new RuntimeException(JaiI18N.getString("TIFFFaxDecoder2")); } else { runLength += code; updatePointer(9 - bits); if (isT == 0) { isWhite = true; } } } else if (code == 200) { // Is a Terminating code current = nextLesserThan8Bits(2); entry = twoBitBlack[current]; code = (entry >>> 5) & 0x07ff; runLength += code; bits = (entry >>> 1) & 0x0f; updatePointer(2 - bits); isWhite = true; } else { // Is a Terminating code runLength += code; updatePointer(4 - bits); isWhite = true; } } return runLength; } // Seeks to the next EOL in the compressed bitstream. // Returns 'true' if and only if an EOL is found; if 'false' // is returned it may be inferred that the EOF was reached first. private boolean seekEOL() { // Set maximum and current bit index into the compressed data. int bitIndexMax = data.length*8 - 1; int bitIndex = bytePointer*8 + bitPointer; // Loop while at least 12 bits are available. while(bitIndex <= bitIndexMax - 12) { // Get the next 12 bits. int next12Bits = nextNBits(12); bitIndex += 12; // Loop while the 12 bits are not unity, i.e., while the EOL // has not been reached, and there is at least one bit left. while(next12Bits != 1 && bitIndex < bitIndexMax) { next12Bits = ((next12Bits & 0x000007ff) << 1) | (nextLesserThan8Bits(1) & 0x00000001); bitIndex++; } // If EOL reached, rewind the pointers and return 'true'. if(next12Bits == 1) { updatePointer(12); return true; } } // EOL not found: return 'false'. return false; } private int readEOL(boolean isFirstEOL) { if(oneD == 0) { // Seek to the next EOL. if(!seekEOL()) { throw new RuntimeException(JaiI18N.getString("TIFFFaxDecoder9")); } } if (fillBits == 0) { int next12Bits = nextNBits(12); if (isFirstEOL && next12Bits == 0) { // Might have the case of EOL padding being used even // though it was not flagged in the T4Options field. // This was observed to be the case in TIFFs produced // by a well known vendor who shall remain nameless. if(nextNBits(4) == 1) { // // EOL must be padded: reset the fillBits flag. // fillBits = 1; return 1; } } if(next12Bits != 1) { throw new RuntimeException(JaiI18N.getString("TIFFFaxDecoder6")); } } else if (fillBits == 1) { // First EOL code word xxxx 0000 0000 0001 will occur // As many fill bits will be present as required to make // the EOL code of 12 bits end on a byte boundary. int bitsLeft = 8 - bitPointer; if (nextNBits(bitsLeft) != 0) { throw new RuntimeException(JaiI18N.getString("TIFFFaxDecoder8")); } // If the number of bitsLeft is less than 4, then to have a 12 // bit EOL sequence, two more bytes are certainly going to be // required. The first of them has to be all zeros, so ensure // that. if (bitsLeft < 4) { if (nextNBits(8) != 0) { throw new RuntimeException(JaiI18N.getString("TIFFFaxDecoder8")); } } // // Some encoders under Group 3 Fax compression 1D writes TIFF // files without the fill bits, but say otherwise in T4Options. // Need to check for this here. // int next8 = nextNBits(8); if (isFirstEOL && (next8 & 0xf0) == 0x10) { // // Fill bits are not actually used despite what the flag // says. So switch fillBits off and then rewind so that // only 12 bits have effectively been read. // fillBits = 0; updatePointer(4); } else { // // This is the normal case. // There might be a random number of fill bytes with 0s, so // loop till the EOL of 0000 0001 is found, as long as all // the bytes preceding it are 0's. // while(next8 != 1) { // If not all zeros if (next8 != 0) { throw new RuntimeException(JaiI18N.getString("TIFFFaxDecoder8")); } next8 = nextNBits(8); } } } // If one dimensional encoding mode, then always return 1 if (oneD == 0) { return 1; } else { // Otherwise for 2D encoding mode, // The next one bit signifies 1D/2D encoding of next line. return nextLesserThan8Bits(1); } } private void getNextChangingElement(int a0, boolean isWhite, int[] ret) { // Local copies of instance variables int[] pce = this.prevChangingElems; int ces = this.changingElemSize; // If the previous match was at an odd element, we still // have to search the preceeding element. // int start = lastChangingElement & ~0x1; int start = lastChangingElement > 0 ? lastChangingElement - 1 : 0; if (isWhite) { start &= ~0x1; // Search even numbered elements } else { start |= 0x1; // Search odd numbered elements } int i = start; for (; i < ces; i += 2) { int temp = pce[i]; if (temp > a0) { lastChangingElement = i; ret[0] = temp; break; } } if (i + 1 < ces) { ret[1] = pce[i + 1]; } } private int nextNBits(int bitsToGet) { byte b, next, next2next; int l = data.length - 1; int bp = this.bytePointer; if (fillOrder == 1) { b = data[bp]; if (bp == l) { next = 0x00; next2next = 0x00; } else if ((bp + 1) == l) { next = data[bp + 1]; next2next = 0x00; } else { next = data[bp + 1]; next2next = data[bp + 2]; } } else if (fillOrder == 2) { b = flipTable[data[bp] & 0xff]; if (bp == l) { next = 0x00; next2next = 0x00; } else if ((bp + 1) == l) { next = flipTable[data[bp + 1] & 0xff]; next2next = 0x00; } else { next = flipTable[data[bp + 1] & 0xff]; next2next = flipTable[data[bp + 2] & 0xff]; } } else { throw new RuntimeException(JaiI18N.getString("TIFFFaxDecoder7")); } int bitsLeft = 8 - bitPointer; int bitsFromNextByte = bitsToGet - bitsLeft; int bitsFromNext2NextByte = 0; if (bitsFromNextByte > 8) { bitsFromNext2NextByte = bitsFromNextByte - 8; bitsFromNextByte = 8; } bytePointer++; int i1 = (b & table1[bitsLeft]) << (bitsToGet - bitsLeft); int i2 = (next & table2[bitsFromNextByte]) >>> (8 - bitsFromNextByte); int i3 = 0; if (bitsFromNext2NextByte != 0) { i2 <<= bitsFromNext2NextByte; i3 = (next2next & table2[bitsFromNext2NextByte]) >>> (8 - bitsFromNext2NextByte); i2 |= i3; bytePointer++; bitPointer = bitsFromNext2NextByte; } else { if (bitsFromNextByte == 8) { bitPointer = 0; bytePointer++; } else { bitPointer = bitsFromNextByte; } } int i = i1 | i2; return i; } private int nextLesserThan8Bits(int bitsToGet) { byte b, next; int l = data.length - 1; int bp = this.bytePointer; if (fillOrder == 1) { b = data[bp]; if (bp == l) { next = 0x00; } else { next = data[bp + 1]; } } else if (fillOrder == 2) { b = flipTable[data[bp] & 0xff]; if (bp == l) { next = 0x00; } else { next = flipTable[data[bp + 1] & 0xff]; } } else { throw new RuntimeException(JaiI18N.getString("TIFFFaxDecoder7")); } int bitsLeft = 8 - bitPointer; int bitsFromNextByte = bitsToGet - bitsLeft; int shift = bitsLeft - bitsToGet; int i1, i2; if (shift >= 0) { i1 = (b & table1[bitsLeft]) >>> shift; bitPointer += bitsToGet; if (bitPointer == 8) { bitPointer = 0; bytePointer++; } } else { i1 = (b & table1[bitsLeft]) << (-shift); i2 = (next & table2[bitsFromNextByte]) >>> (8 - bitsFromNextByte); i1 |= i2; bytePointer++; bitPointer = bitsFromNextByte; } return i1; } // Move pointer backwards by given amount of bits private void updatePointer(int bitsToMoveBack) { if(bitsToMoveBack > 8) { bytePointer -= bitsToMoveBack/8; bitsToMoveBack %= 8; } int i = bitPointer - bitsToMoveBack; if (i < 0) { bytePointer--; bitPointer = 8 + i; } else { bitPointer = i; } } // Move to the next byte boundary private boolean advancePointer() { if (bitPointer != 0) { bytePointer++; bitPointer = 0; } return true; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/PNGImageDecoder.java0000644000175000017500000016500410472445724027311 0ustar mathieumathieu/* * $RCSfile: PNGImageDecoder.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.3 $ * $Date: 2006-08-22 00:12:04 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl; import java.awt.Color; import java.awt.Point; import java.awt.RenderingHints; import java.awt.Transparency; import java.awt.color.ColorSpace; import java.awt.color.ICC_ColorSpace; import java.awt.color.ICC_Profile; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.ComponentColorModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferUShort; import java.awt.image.IndexColorModel; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.PixelInterleavedSampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.FileInputStream; import java.io.InputStream; import java.io.IOException; import java.io.SequenceInputStream; import java.text.DateFormat; import java.util.Date; import java.util.Enumeration; import java.util.GregorianCalendar; import java.util.Hashtable; import java.util.TimeZone; import java.util.Vector; import java.util.zip.Inflater; import java.util.zip.InflaterInputStream; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.ImageDecoder; import com.sun.media.jai.codec.ImageDecoderImpl; import com.sun.media.jai.codec.ImageDecodeParam; import com.sun.media.jai.codec.PNGDecodeParam; import com.sun.media.jai.codec.PNGEncodeParam; import com.sun.media.jai.codecimpl.ImagingListenerProxy; import com.sun.media.jai.codecimpl.util.ImagingException; /** * @since EA3 */ public class PNGImageDecoder extends ImageDecoderImpl { public PNGImageDecoder(InputStream input, PNGDecodeParam param) { super(input, param); } public RenderedImage decodeAsRenderedImage(int page) throws IOException { if (page != 0) { throw new IOException(JaiI18N.getString("PNGImageDecoder19")); } try { return new PNGImage(input, (PNGDecodeParam)param); } catch(Exception e) { throw CodecUtils.toIOException(e); } } } class PNGChunk { int length; int type; byte[] data; int crc; String typeString; public PNGChunk(int length, int type, byte[] data, int crc) { this.length = length; this.type = type; this.data = data; this.crc = crc; typeString = new String(); typeString += (char)(type >> 24); typeString += (char)((type >> 16) & 0xff); typeString += (char)((type >> 8) & 0xff); typeString += (char)(type & 0xff); } public int getLength() { return length; } public int getType() { return type; } public String getTypeString() { return typeString; } public byte[] getData() { return data; } public byte getByte(int offset) { return data[offset]; } public int getInt1(int offset) { return data[offset] & 0xff; } public int getInt2(int offset) { return ((data[offset] & 0xff) << 8) | (data[offset + 1] & 0xff); } public int getInt4(int offset) { return ((data[offset] & 0xff) << 24) | ((data[offset + 1] & 0xff) << 16) | ((data[offset + 2] & 0xff) << 8) | (data[offset + 3] & 0xff); } public String getString4(int offset) { String s = new String(); s += (char)data[offset]; s += (char)data[offset + 1]; s += (char)data[offset + 2]; s += (char)data[offset + 3]; return s; } public boolean isType(String typeName) { return typeString.equals(typeName); } } /** * TO DO: * * zTXt chunks * */ class PNGImage extends SimpleRenderedImage { public static final int PNG_COLOR_GRAY = 0; public static final int PNG_COLOR_RGB = 2; public static final int PNG_COLOR_PALETTE = 3; public static final int PNG_COLOR_GRAY_ALPHA = 4; public static final int PNG_COLOR_RGB_ALPHA = 6; private static final String[] colorTypeNames = { "Grayscale", "Error", "Truecolor", "Index", "Grayscale with alpha", "Error", "Truecolor with alpha" }; public static final int PNG_FILTER_NONE = 0; public static final int PNG_FILTER_SUB = 1; public static final int PNG_FILTER_UP = 2; public static final int PNG_FILTER_AVERAGE = 3; public static final int PNG_FILTER_PAETH = 4; private static final int RED_OFFSET = 2; private static final int GREEN_OFFSET = 1; private static final int BLUE_OFFSET = 0; private int[][] bandOffsets = { null, { 0 }, // G { 0, 1 }, // GA in GA order { 0, 1, 2 }, // RGB in RGB order { 0, 1, 2, 3 } // RGBA in RGBA order }; private int bitDepth; private int colorType; private int compressionMethod; private int filterMethod; private int interlaceMethod; private int paletteEntries; private byte[] redPalette; private byte[] greenPalette; private byte[] bluePalette; private byte[] alphaPalette; private int bkgdRed; private int bkgdGreen; private int bkgdBlue; private int grayTransparentAlpha; private int redTransparentAlpha; private int greenTransparentAlpha; private int blueTransparentAlpha; private int maxOpacity; private int[] significantBits = null; private boolean hasBackground = false; // Parameter information // If true, the user wants destination alpha where applicable. private boolean suppressAlpha = false; // If true, perform palette lookup internally private boolean expandPalette = false; // If true, output < 8 bit gray images in 8 bit components format private boolean output8BitGray = false; // Create an alpha channel in the destination color model. private boolean outputHasAlphaPalette = false; // Perform gamma correction on the image private boolean performGammaCorrection = false; // Expand GA to GGGA for compatbility with Java2D private boolean expandGrayAlpha = false; // Produce an instance of PNGEncodeParam private boolean generateEncodeParam = false; // PNGDecodeParam controlling decode process private PNGDecodeParam decodeParam = null; // PNGEncodeParam to store file details in private PNGEncodeParam encodeParam = null; private boolean emitProperties = true; private float fileGamma = 45455/100000.0F; private float userExponent = 1.0F; private float displayExponent = 2.2F; private float[] chromaticity = null; private int sRGBRenderingIntent = -1; // ICCP parameters private ICC_Profile iccProfile = null; private String iccProfileName = null; // Post-processing step implied by above parameters private int postProcess = POST_NONE; // Possible post-processing steps // Do nothing private static final int POST_NONE = 0; // Gamma correct only private static final int POST_GAMMA = 1; // Push gray values through grayLut to expand to 8 bits private static final int POST_GRAY_LUT = 2; // Push gray values through grayLut to expand to 8 bits, add alpha private static final int POST_GRAY_LUT_ADD_TRANS = 3; // Push palette value through R,G,B lookup tables private static final int POST_PALETTE_TO_RGB = 4; // Push palette value through R,G,B,A lookup tables private static final int POST_PALETTE_TO_RGBA = 5; // Add transparency to a given gray value (w/ optional gamma) private static final int POST_ADD_GRAY_TRANS = 6; // Add transparency to a given RGB value (w/ optional gamma) private static final int POST_ADD_RGB_TRANS = 7; // Remove the alpha channel from a gray image (w/ optional gamma) private static final int POST_REMOVE_GRAY_TRANS = 8; // Remove the alpha channel from an RGB image (w/optional gamma) private static final int POST_REMOVE_RGB_TRANS = 9; // Mask to add expansion of GA -> GGGA private static final int POST_EXP_MASK = 16; // Expand gray to G/G/G private static final int POST_GRAY_ALPHA_EXP = POST_NONE | POST_EXP_MASK; // Expand gray to G/G/G through a gamma lut private static final int POST_GAMMA_EXP = POST_GAMMA | POST_EXP_MASK; // Push gray values through grayLut to expand to 8 bits, expand, add alpha private static final int POST_GRAY_LUT_ADD_TRANS_EXP = POST_GRAY_LUT_ADD_TRANS | POST_EXP_MASK; // Add transparency to a given gray value, expand private static final int POST_ADD_GRAY_TRANS_EXP = POST_ADD_GRAY_TRANS | POST_EXP_MASK; private Vector streamVec = new Vector(); private DataInputStream dataStream; private int bytesPerPixel; // number of bytes per input pixel private int inputBands; private int outputBands; // Number of private chunks private int chunkIndex = 0; private Vector textKeys = new Vector(); private Vector textStrings = new Vector(); private Vector ztextKeys = new Vector(); private Vector ztextStrings = new Vector(); private WritableRaster theTile; private int[] gammaLut = null; private void initGammaLut(int bits) { double exp = (double)userExponent/(fileGamma*displayExponent); int numSamples = 1 << bits; int maxOutSample = (bits == 16) ? 65535 : 255; gammaLut = new int[numSamples]; for (int i = 0; i < numSamples; i++) { double gbright = (double)i/(numSamples - 1); double gamma = Math.pow(gbright, exp); int igamma = (int)(gamma*maxOutSample + 0.5); if (igamma > maxOutSample) { igamma = maxOutSample; } gammaLut[i] = igamma; } } private final byte[][] expandBits = { null, { (byte)0x00, (byte)0xff }, { (byte)0x00, (byte)0x55, (byte)0xaa, (byte)0xff }, null, { (byte)0x00, (byte)0x11, (byte)0x22, (byte)0x33, (byte)0x44, (byte)0x55, (byte)0x66, (byte)0x77, (byte)0x88, (byte)0x99, (byte)0xaa, (byte)0xbb, (byte)0xcc, (byte)0xdd, (byte)0xee, (byte)0xff } }; private int[] grayLut = null; private void initGrayLut(int bits) { int len = 1 << bits; grayLut = new int[len]; if (performGammaCorrection) { for (int i = 0; i < len; i++) { grayLut[i] = gammaLut[i]; } } else { for (int i = 0; i < len; i++) { grayLut[i] = expandBits[bits][i]; } } } public PNGImage(InputStream stream, PNGDecodeParam decodeParam) throws IOException { if (!stream.markSupported()) { stream = new BufferedInputStream(stream); } DataInputStream distream = new DataInputStream(stream); if (decodeParam == null) { decodeParam = new PNGDecodeParam(); } this.decodeParam = decodeParam; // Get parameter values this.suppressAlpha = decodeParam.getSuppressAlpha(); this.expandPalette = decodeParam.getExpandPalette(); this.output8BitGray = decodeParam.getOutput8BitGray(); this.expandGrayAlpha = decodeParam.getExpandGrayAlpha(); if (decodeParam.getPerformGammaCorrection()) { this.userExponent = decodeParam.getUserExponent(); this.displayExponent = decodeParam.getDisplayExponent(); performGammaCorrection = true; output8BitGray = true; } this.generateEncodeParam = decodeParam.getGenerateEncodeParam(); if (emitProperties) { properties.put("file_type", "PNG v. 1.0"); } try { long magic = distream.readLong(); if (magic != 0x89504e470d0a1a0aL) { String msg = JaiI18N.getString("PNGImageDecoder0"); throw new RuntimeException(msg); } } catch (Exception e) { String message = JaiI18N.getString("PNGImageDecoder1"); ImagingListenerProxy.errorOccurred(message, new ImagingException(message, e), this, false); /* e.printStackTrace(); String msg = JaiI18N.getString("PNGImageDecoder1"); throw new RuntimeException(msg); */ } do { try { PNGChunk chunk; String chunkType = getChunkType(distream); if (chunkType.equals("IHDR")) { chunk = readChunk(distream); parse_IHDR_chunk(chunk); } else if (chunkType.equals("PLTE")) { chunk = readChunk(distream); parse_PLTE_chunk(chunk); } else if (chunkType.equals("IDAT")) { chunk = readChunk(distream); streamVec.add(new ByteArrayInputStream(chunk.getData())); } else if (chunkType.equals("IEND")) { chunk = readChunk(distream); parse_IEND_chunk(chunk); break; // fall through to the bottom } else if (chunkType.equals("bKGD")) { chunk = readChunk(distream); parse_bKGD_chunk(chunk); } else if (chunkType.equals("cHRM")) { chunk = readChunk(distream); parse_cHRM_chunk(chunk); } else if (chunkType.equals("gAMA")) { chunk = readChunk(distream); parse_gAMA_chunk(chunk); } else if (chunkType.equals("hIST")) { chunk = readChunk(distream); parse_hIST_chunk(chunk); } else if (chunkType.equals("iCCP")) { chunk = readChunk(distream); parse_iCCP_chunk(chunk); } else if (chunkType.equals("pHYs")) { chunk = readChunk(distream); parse_pHYs_chunk(chunk); } else if (chunkType.equals("sBIT")) { chunk = readChunk(distream); parse_sBIT_chunk(chunk); } else if (chunkType.equals("sRGB")) { chunk = readChunk(distream); parse_sRGB_chunk(chunk); } else if (chunkType.equals("tEXt")) { chunk = readChunk(distream); parse_tEXt_chunk(chunk); } else if (chunkType.equals("tIME")) { chunk = readChunk(distream); parse_tIME_chunk(chunk); } else if (chunkType.equals("tRNS")) { chunk = readChunk(distream); parse_tRNS_chunk(chunk); } else if (chunkType.equals("zTXt")) { chunk = readChunk(distream); parse_zTXt_chunk(chunk); } else { chunk = readChunk(distream); // Output the chunk data in raw form String type = chunk.getTypeString(); byte[] data = chunk.getData(); if (encodeParam != null) { encodeParam.addPrivateChunk(type, data); } if (emitProperties) { String key = "chunk_" + chunkIndex++ + ":" + type; properties.put(key.toLowerCase(), data); } } } catch (Exception e) { String message = JaiI18N.getString("PNGImageDecoder2"); ImagingListenerProxy.errorOccurred(message, new ImagingException(message, e), this, false); /* e.printStackTrace(); String msg = JaiI18N.getString("PNGImageDecoder2"); throw new RuntimeException(msg); */ } } while (true); // Final post-processing if (significantBits == null) { significantBits = new int[inputBands]; for (int i = 0; i < inputBands; i++) { significantBits[i] = bitDepth; } if (emitProperties) { properties.put("significant_bits", significantBits); } } } private static String getChunkType(DataInputStream distream) { try { distream.mark(8); int length = distream.readInt(); int type = distream.readInt(); distream.reset(); String typeString = new String(); typeString += (char)(type >> 24); typeString += (char)((type >> 16) & 0xff); typeString += (char)((type >> 8) & 0xff); typeString += (char)(type & 0xff); return typeString; } catch (Exception e) { ImagingListenerProxy.errorOccurred(JaiI18N.getString("PNGImageDecoder20"), e, PNGImageDecoder.class, false); // e.printStackTrace(); return null; } } private static PNGChunk readChunk(DataInputStream distream) { try { int length = distream.readInt(); int type = distream.readInt(); byte[] data = new byte[length]; distream.readFully(data); int crc = distream.readInt(); return new PNGChunk(length, type, data, crc); } catch (Exception e) { ImagingListenerProxy.errorOccurred(JaiI18N.getString("PNGImageDecoder21"), e, PNGImageDecoder.class, false); // e.printStackTrace(); return null; } } private void parse_IHDR_chunk(PNGChunk chunk) { tileWidth = width = chunk.getInt4(0); tileHeight = height = chunk.getInt4(4); bitDepth = chunk.getInt1(8); if ((bitDepth != 1) && (bitDepth != 2) && (bitDepth != 4) && (bitDepth != 8) && (bitDepth != 16)) { // Error -- bad bit depth throw new RuntimeException(JaiI18N.getString("PNGImageDecoder3")); } maxOpacity = (1 << bitDepth) - 1; colorType = chunk.getInt1(9); if ((colorType != PNG_COLOR_GRAY) && (colorType != PNG_COLOR_RGB) && (colorType != PNG_COLOR_PALETTE) && (colorType != PNG_COLOR_GRAY_ALPHA) && (colorType != PNG_COLOR_RGB_ALPHA)) { System.out.println(JaiI18N.getString("PNGImageDecoder4")); } if ((colorType == PNG_COLOR_RGB) && (bitDepth < 8)) { // Error -- RGB images must have 8 or 16 bits throw new RuntimeException(JaiI18N.getString("PNGImageDecoder5")); } if ((colorType == PNG_COLOR_PALETTE) && (bitDepth == 16)) { // Error -- palette images must have < 16 bits throw new RuntimeException(JaiI18N.getString("PNGImageDecoder6")); } if ((colorType == PNG_COLOR_GRAY_ALPHA) && (bitDepth < 8)) { // Error -- gray/alpha images must have >= 8 bits throw new RuntimeException(JaiI18N.getString("PNGImageDecoder7")); } if ((colorType == PNG_COLOR_RGB_ALPHA) && (bitDepth < 8)) { // Error -- RGB/alpha images must have >= 8 bits throw new RuntimeException(JaiI18N.getString("PNGImageDecoder8")); } if (emitProperties) { properties.put("color_type", colorTypeNames[colorType]); } if (generateEncodeParam) { if (colorType == PNG_COLOR_PALETTE) { encodeParam = new PNGEncodeParam.Palette(); } else if (colorType == PNG_COLOR_GRAY || colorType == PNG_COLOR_GRAY_ALPHA) { encodeParam = new PNGEncodeParam.Gray(); } else { encodeParam = new PNGEncodeParam.RGB(); } decodeParam.setEncodeParam(encodeParam); } if (encodeParam != null) { encodeParam.setBitDepth(bitDepth); } if (emitProperties) { properties.put("bit_depth", new Integer(bitDepth)); } if (performGammaCorrection) { // Assume file gamma is 1/2.2 unless we get a gAMA chunk float gamma = (1.0F/2.2F)*(displayExponent/userExponent); if (encodeParam != null) { encodeParam.setGamma(gamma); } if (emitProperties) { properties.put("gamma", new Float(gamma)); } } compressionMethod = chunk.getInt1(10); if (compressionMethod != 0) { // Error -- only know about compression method 0 throw new RuntimeException(JaiI18N.getString("PNGImageDecoder9")); } filterMethod = chunk.getInt1(11); if (filterMethod != 0) { // Error -- only know about filter method 0 throw new RuntimeException(JaiI18N.getString("PNGImageDecoder10")); } interlaceMethod = chunk.getInt1(12); if (interlaceMethod == 0) { if (encodeParam != null) { encodeParam.setInterlacing(false); } if (emitProperties) { properties.put("interlace_method", "None"); } } else if (interlaceMethod == 1) { if (encodeParam != null) { encodeParam.setInterlacing(true); } if (emitProperties) { properties.put("interlace_method", "Adam7"); } } else { // Error -- only know about Adam7 interlacing throw new RuntimeException(JaiI18N.getString("PNGImageDecoder11")); } bytesPerPixel = (bitDepth == 16) ? 2 : 1; switch (colorType) { case PNG_COLOR_GRAY: inputBands = 1; outputBands = 1; if (output8BitGray && (bitDepth < 8)) { postProcess = POST_GRAY_LUT; } else if (performGammaCorrection) { postProcess = POST_GAMMA; } else { postProcess = POST_NONE; } break; case PNG_COLOR_RGB: inputBands = 3; bytesPerPixel *= 3; outputBands = 3; if (performGammaCorrection) { postProcess = POST_GAMMA; } else { postProcess = POST_NONE; } break; case PNG_COLOR_PALETTE: inputBands = 1; bytesPerPixel = 1; outputBands = expandPalette ? 3 : 1; if (expandPalette) { postProcess = POST_PALETTE_TO_RGB; } else { postProcess = POST_NONE; } break; case PNG_COLOR_GRAY_ALPHA: inputBands = 2; bytesPerPixel *= 2; if (suppressAlpha) { outputBands = 1; postProcess = POST_REMOVE_GRAY_TRANS; } else { if (performGammaCorrection) { postProcess = POST_GAMMA; } else { postProcess = POST_NONE; } if (expandGrayAlpha) { postProcess |= POST_EXP_MASK; outputBands = 4; } else { outputBands = 2; } } break; case PNG_COLOR_RGB_ALPHA: inputBands = 4; bytesPerPixel *= 4; outputBands = (!suppressAlpha) ? 4 : 3; if (suppressAlpha) { postProcess = POST_REMOVE_RGB_TRANS; } else if (performGammaCorrection) { postProcess = POST_GAMMA; } else { postProcess = POST_NONE; } break; } } private void parse_IEND_chunk(PNGChunk chunk) throws Exception { // Store text strings int textLen = textKeys.size(); String[] textArray = new String[2*textLen]; for (int i = 0; i < textLen; i++) { String key = (String)textKeys.elementAt(i); String val = (String)textStrings.elementAt(i); textArray[2*i] = key; textArray[2*i + 1] = val; if (emitProperties) { String uniqueKey = "text_" + i + ":" + key; properties.put(uniqueKey.toLowerCase(), val); } } if (encodeParam != null) { encodeParam.setText(textArray); } // Store compressed text strings int ztextLen = ztextKeys.size(); String[] ztextArray = new String[2*ztextLen]; for (int i = 0; i < ztextLen; i++) { String key = (String)ztextKeys.elementAt(i); String val = (String)ztextStrings.elementAt(i); ztextArray[2*i] = key; ztextArray[2*i + 1] = val; if (emitProperties) { String uniqueKey = "ztext_" + i + ":" + key; properties.put(uniqueKey.toLowerCase(), val); } } if (encodeParam != null) { encodeParam.setCompressedText(ztextArray); } // detect sRGB & iCCP conflict if (sRGBRenderingIntent != -1 && iccProfile != null) { // resolve by dropping ICC Profile iccProfile = null; } // add iccProfile to encodeParam if (encodeParam != null && iccProfile != null) { encodeParam.setICCProfileData(iccProfile.getData()); encodeParam.setICCProfileName(iccProfileName); } // Parse prior IDAT chunks InputStream seqStream = new SequenceInputStream(streamVec.elements()); InputStream infStream = new InflaterInputStream(seqStream, new Inflater()); dataStream = new DataInputStream(infStream); // Create an empty WritableRaster int depth = bitDepth; if ((colorType == PNG_COLOR_GRAY) && (bitDepth < 8) && output8BitGray) { depth = 8; } if ((colorType == PNG_COLOR_PALETTE) && expandPalette) { depth = 8; } int bytesPerRow = (outputBands*width*depth + 7)/8; int scanlineStride = (depth == 16) ? (bytesPerRow/2) : bytesPerRow; theTile = createRaster(width, height, outputBands, scanlineStride, depth); if (performGammaCorrection && (gammaLut == null)) { initGammaLut(bitDepth); } if ((postProcess == POST_GRAY_LUT) || (postProcess == POST_GRAY_LUT_ADD_TRANS) || (postProcess == POST_GRAY_LUT_ADD_TRANS_EXP)) { initGrayLut(bitDepth); } decodeImage(interlaceMethod == 1); sampleModel = theTile.getSampleModel(); if ((colorType == PNG_COLOR_PALETTE) && !expandPalette) { if (outputHasAlphaPalette) { colorModel = new IndexColorModel(bitDepth, paletteEntries, redPalette, greenPalette, bluePalette, alphaPalette); } else { colorModel = new IndexColorModel(bitDepth, paletteEntries, redPalette, greenPalette, bluePalette); } } else if ((colorType == PNG_COLOR_GRAY) && (bitDepth < 8) && !output8BitGray) { byte[] palette = expandBits[bitDepth]; colorModel = new IndexColorModel(bitDepth, palette.length, palette, palette, palette); } else { colorModel = ImageCodec.createComponentColorModel(sampleModel, iccProfile == null ? null : new ICC_ColorSpace(iccProfile)); } } private void parse_PLTE_chunk(PNGChunk chunk) { paletteEntries = chunk.getLength()/3; redPalette = new byte[paletteEntries]; greenPalette = new byte[paletteEntries]; bluePalette = new byte[paletteEntries]; int pltIndex = 0; // gAMA chunk must precede PLTE chunk if (performGammaCorrection) { if (gammaLut == null) { initGammaLut(bitDepth == 16 ? 16 : 8); } for (int i = 0; i < paletteEntries; i++) { byte r = chunk.getByte(pltIndex++); byte g = chunk.getByte(pltIndex++); byte b = chunk.getByte(pltIndex++); redPalette[i] = (byte)gammaLut[r & 0xff]; greenPalette[i] = (byte)gammaLut[g & 0xff]; bluePalette[i] = (byte)gammaLut[b & 0xff]; } } else { for (int i = 0; i < paletteEntries; i++) { redPalette[i] = chunk.getByte(pltIndex++); greenPalette[i] = chunk.getByte(pltIndex++); bluePalette[i] = chunk.getByte(pltIndex++); } } } private void parse_bKGD_chunk(PNGChunk chunk) { hasBackground = true; switch (colorType) { case PNG_COLOR_PALETTE: int bkgdIndex = chunk.getByte(0) & 0xff; bkgdRed = redPalette[bkgdIndex] & 0xff; bkgdGreen = greenPalette[bkgdIndex] & 0xff; bkgdBlue = bluePalette[bkgdIndex] & 0xff; if (encodeParam != null) { ((PNGEncodeParam.Palette)encodeParam). setBackgroundPaletteIndex(bkgdIndex); } break; case PNG_COLOR_GRAY: case PNG_COLOR_GRAY_ALPHA: int bkgdGray = chunk.getInt2(0); bkgdRed = bkgdGreen = bkgdBlue = bkgdGray; if (encodeParam != null) { ((PNGEncodeParam.Gray)encodeParam). setBackgroundGray(bkgdGray); } break; case PNG_COLOR_RGB: case PNG_COLOR_RGB_ALPHA: // Fix 4625294: In the case of bitDepth = 8, // when the background color values is larger // than 128, and the encoder copies the byte into a short // without masking, the decoded background values may be // out of 8 bit range. So mask them here to avoid the // exception thrown by the constructor of Color. // So mask to make it safe even when the values exceeds // the range. int mask = (1 << bitDepth) - 1; bkgdRed = chunk.getInt2(0) & mask; bkgdGreen = chunk.getInt2(2) & mask; bkgdBlue = chunk.getInt2(4) & mask; int[] bkgdRGB = new int[3]; bkgdRGB[0] = bkgdRed; bkgdRGB[1] = bkgdGreen; bkgdRGB[2] = bkgdBlue; if (encodeParam != null) { ((PNGEncodeParam.RGB)encodeParam). setBackgroundRGB(bkgdRGB); } break; } int r = 0, g = 0, b = 0; if (bitDepth < 8) { r = expandBits[bitDepth][bkgdRed]; g = expandBits[bitDepth][bkgdGreen]; b = expandBits[bitDepth][bkgdBlue]; } else if (bitDepth == 8) { r = bkgdRed; g = bkgdGreen; b = bkgdBlue; } else if (bitDepth == 16) { r = bkgdRed >> 8; g = bkgdGreen >> 8; b = bkgdBlue >> 8; } if (emitProperties) { properties.put("background_color", new Color(r, g, b)); } } private void parse_cHRM_chunk(PNGChunk chunk) { // If an sRGB chunk exists, ignore cHRM chunks if (sRGBRenderingIntent != -1) { return; } chromaticity = new float[8]; chromaticity[0] = chunk.getInt4(0)/100000.0F; chromaticity[1] = chunk.getInt4(4)/100000.0F; chromaticity[2] = chunk.getInt4(8)/100000.0F; chromaticity[3] = chunk.getInt4(12)/100000.0F; chromaticity[4] = chunk.getInt4(16)/100000.0F; chromaticity[5] = chunk.getInt4(20)/100000.0F; chromaticity[6] = chunk.getInt4(24)/100000.0F; chromaticity[7] = chunk.getInt4(28)/100000.0F; if (encodeParam != null) { encodeParam.setChromaticity(chromaticity); } if (emitProperties) { properties.put("white_point_x", new Float(chromaticity[0])); properties.put("white_point_y", new Float(chromaticity[1])); properties.put("red_x", new Float(chromaticity[2])); properties.put("red_y", new Float(chromaticity[3])); properties.put("green_x", new Float(chromaticity[4])); properties.put("green_y", new Float(chromaticity[5])); properties.put("blue_x", new Float(chromaticity[6])); properties.put("blue_y", new Float(chromaticity[7])); } } private void parse_gAMA_chunk(PNGChunk chunk) { // If an sRGB chunk exists, ignore gAMA chunks if (sRGBRenderingIntent != -1) { return; } fileGamma = chunk.getInt4(0)/100000.0F; float exp = performGammaCorrection ? displayExponent/userExponent : 1.0F; if (encodeParam != null) { encodeParam.setGamma(fileGamma*exp); } if (emitProperties) { properties.put("gamma", new Float(fileGamma*exp)); } } private void parse_hIST_chunk(PNGChunk chunk) { if (redPalette == null) { throw new RuntimeException(JaiI18N.getString("PNGImageDecoder18")); } int length = redPalette.length; int[] hist = new int[length]; for (int i = 0; i < length; i++) { hist[i] = chunk.getInt2(2*i); } if (encodeParam != null) { encodeParam.setPaletteHistogram(hist); } } private void parse_iCCP_chunk(PNGChunk chunk) { byte b; byte[] data = new byte[80]; int pos = 0; while (pos < 79 && (b = chunk.getByte(pos)) != 0) { data[pos++] = b; } data[pos] = 0; String name = new String(data); byte compMethod = chunk.getByte(pos++); InflaterInputStream infls = new InflaterInputStream( new ByteArrayInputStream( chunk.getData(), pos, chunk.getLength() - pos ) ); try { iccProfile = ICC_Profile.getInstance(infls); iccProfileName = name; } catch (IOException e) { iccProfile = null; iccProfileName = null; } } private void parse_pHYs_chunk(PNGChunk chunk) { int xPixelsPerUnit = chunk.getInt4(0); int yPixelsPerUnit = chunk.getInt4(4); int unitSpecifier = chunk.getInt1(8); if (encodeParam != null) { encodeParam.setPhysicalDimension(xPixelsPerUnit, yPixelsPerUnit, unitSpecifier); } if (emitProperties) { properties.put("x_pixels_per_unit", new Integer(xPixelsPerUnit)); properties.put("y_pixels_per_unit", new Integer(yPixelsPerUnit)); properties.put("pixel_aspect_ratio", new Float((float)xPixelsPerUnit/yPixelsPerUnit)); if (unitSpecifier == 1) { properties.put("pixel_units", "Meters"); } else if (unitSpecifier != 0) { // Error -- unit specifier must be 0 or 1 throw new RuntimeException(JaiI18N.getString("PNGImageDecoder12")); } } } private void parse_sBIT_chunk(PNGChunk chunk) { if (colorType == PNG_COLOR_PALETTE) { significantBits = new int[3]; } else { significantBits = new int[inputBands]; } for (int i = 0; i < significantBits.length; i++) { int bits = (int)chunk.getByte(i); int depth = (colorType == PNG_COLOR_PALETTE) ? 8 : bitDepth; if (bits <= 0 || bits > depth) { // Error -- significant bits must be between 0 and // image bit depth. throw new RuntimeException(JaiI18N.getString("PNGImageDecoder13")); } significantBits[i] = bits; } if (encodeParam != null) { encodeParam.setSignificantBits(significantBits); } if (emitProperties) { properties.put("significant_bits", significantBits); } } private void parse_sRGB_chunk(PNGChunk chunk) { sRGBRenderingIntent = chunk.getByte(0); // The presence of an sRGB chunk implies particular // settings for gamma and chroma. fileGamma = 45455/100000.0F; chromaticity = new float[8]; chromaticity[0] = 31270/10000.0F; chromaticity[1] = 32900/10000.0F; chromaticity[2] = 64000/10000.0F; chromaticity[3] = 33000/10000.0F; chromaticity[4] = 30000/10000.0F; chromaticity[5] = 60000/10000.0F; chromaticity[6] = 15000/10000.0F; chromaticity[7] = 6000/10000.0F; if (performGammaCorrection) { // File gamma is 1/2.2 float gamma = fileGamma*(displayExponent/userExponent); if (encodeParam != null) { encodeParam.setGamma(gamma); encodeParam.setChromaticity(chromaticity); } if (emitProperties) { properties.put("gamma", new Float(gamma)); properties.put("white_point_x", new Float(chromaticity[0])); properties.put("white_point_y", new Float(chromaticity[1])); properties.put("red_x", new Float(chromaticity[2])); properties.put("red_y", new Float(chromaticity[3])); properties.put("green_x", new Float(chromaticity[4])); properties.put("green_y", new Float(chromaticity[5])); properties.put("blue_x", new Float(chromaticity[6])); properties.put("blue_y", new Float(chromaticity[7])); } } } private void parse_tEXt_chunk(PNGChunk chunk) { String key = new String(); String value = new String(); byte b; int textIndex = 0; while ((b = chunk.getByte(textIndex++)) != 0) { key += (char)b; } for (int i = textIndex; i < chunk.getLength(); i++) { value += (char)chunk.getByte(i); } textKeys.add(key); textStrings.add(value); } private void parse_tIME_chunk(PNGChunk chunk) { int year = chunk.getInt2(0); int month = chunk.getInt1(2) - 1; int day = chunk.getInt1(3); int hour = chunk.getInt1(4); int minute = chunk.getInt1(5); int second = chunk.getInt1(6); TimeZone gmt = TimeZone.getTimeZone("GMT"); GregorianCalendar cal = new GregorianCalendar(gmt); cal.set(year, month, day, hour, minute, second); Date date = cal.getTime(); if (encodeParam != null) { encodeParam.setModificationTime(date); } if (emitProperties) { properties.put("timestamp", date); } } private void parse_tRNS_chunk(PNGChunk chunk) { if (colorType == PNG_COLOR_PALETTE) { int entries = chunk.getLength(); if (entries > paletteEntries) { // Error -- mustn't have more alpha than RGB palette entries throw new RuntimeException(JaiI18N.getString("PNGImageDecoder14")); } // Load beginning of palette from the chunk alphaPalette = new byte[paletteEntries]; for (int i = 0; i < entries; i++) { alphaPalette[i] = chunk.getByte(i); } // Fill rest of palette with 255 for (int i = entries; i < paletteEntries; i++) { alphaPalette[i] = (byte)255; } if (!suppressAlpha) { if (expandPalette) { postProcess = POST_PALETTE_TO_RGBA; outputBands = 4; } else { outputHasAlphaPalette = true; } } } else if (colorType == PNG_COLOR_GRAY) { grayTransparentAlpha = chunk.getInt2(0); if (!suppressAlpha) { if (bitDepth < 8) { output8BitGray = true; maxOpacity = 255; postProcess = POST_GRAY_LUT_ADD_TRANS; } else { postProcess = POST_ADD_GRAY_TRANS; } if (expandGrayAlpha) { outputBands = 4; postProcess |= POST_EXP_MASK; } else { outputBands = 2; } if (encodeParam != null) { ((PNGEncodeParam.Gray)encodeParam). setTransparentGray(grayTransparentAlpha); } } } else if (colorType == PNG_COLOR_RGB) { redTransparentAlpha = chunk.getInt2(0); greenTransparentAlpha = chunk.getInt2(2); blueTransparentAlpha = chunk.getInt2(4); if (!suppressAlpha) { outputBands = 4; postProcess = POST_ADD_RGB_TRANS; if (encodeParam != null) { int[] rgbTrans = new int[3]; rgbTrans[0] = redTransparentAlpha; rgbTrans[1] = greenTransparentAlpha; rgbTrans[2] = blueTransparentAlpha; ((PNGEncodeParam.RGB)encodeParam). setTransparentRGB(rgbTrans); } } } else if (colorType == PNG_COLOR_GRAY_ALPHA || colorType == PNG_COLOR_RGB_ALPHA) { // Error -- GA or RGBA image can't have a tRNS chunk. throw new RuntimeException(JaiI18N.getString("PNGImageDecoder15")); } } private void parse_zTXt_chunk(PNGChunk chunk) { String key = new String(); String value = new String(); byte b; int textIndex = 0; while ((b = chunk.getByte(textIndex++)) != 0) { key += (char)b; } int method = chunk.getByte(textIndex++); try { int length = chunk.getLength() - textIndex; byte[] data = chunk.getData(); InputStream cis = new ByteArrayInputStream(data, textIndex, length); InputStream iis = new InflaterInputStream(cis); int c; while ((c = iis.read()) != -1) { value += (char)c; } ztextKeys.add(key); ztextStrings.add(value); } catch (Exception e) { ImagingListenerProxy.errorOccurred(JaiI18N.getString("PNGImageDecoder21"), e, this, false); // e.printStackTrace(); } } private WritableRaster createRaster(int width, int height, int bands, int scanlineStride, int bitDepth) { DataBuffer dataBuffer; WritableRaster ras = null; Point origin = new Point(0, 0); if ((bitDepth < 8) && (bands == 1)) { dataBuffer = new DataBufferByte(height*scanlineStride); ras = Raster.createPackedRaster(dataBuffer, width, height, bitDepth, origin); } else if (bitDepth <= 8) { dataBuffer = new DataBufferByte(height*scanlineStride); ras = Raster.createInterleavedRaster(dataBuffer, width, height, scanlineStride, bands, bandOffsets[bands], origin); } else { dataBuffer = new DataBufferUShort(height*scanlineStride); ras = Raster.createInterleavedRaster(dataBuffer, width, height, scanlineStride, bands, bandOffsets[bands], origin); } return ras; } // Data filtering methods private static void decodeSubFilter(byte[] curr, int count, int bpp) { for (int i = bpp; i < count; i++) { int val; val = curr[i] & 0xff; val += curr[i - bpp] & 0xff; curr[i] = (byte)val; } } private static void decodeUpFilter(byte[] curr, byte[] prev, int count) { for (int i = 0; i < count; i++) { int raw = curr[i] & 0xff; int prior = prev[i] & 0xff; curr[i] = (byte)(raw + prior); } } private static void decodeAverageFilter(byte[] curr, byte[] prev, int count, int bpp) { int raw, priorPixel, priorRow; for (int i = 0; i < bpp; i++) { raw = curr[i] & 0xff; priorRow = prev[i] & 0xff; curr[i] = (byte)(raw + priorRow/2); } for (int i = bpp; i < count; i++) { raw = curr[i] & 0xff; priorPixel = curr[i - bpp] & 0xff; priorRow = prev[i] & 0xff; curr[i] = (byte)(raw + (priorPixel + priorRow)/2); } } private static int paethPredictor(int a, int b, int c) { int p = a + b - c; int pa = Math.abs(p - a); int pb = Math.abs(p - b); int pc = Math.abs(p - c); if ((pa <= pb) && (pa <= pc)) { return a; } else if (pb <= pc) { return b; } else { return c; } } private static void decodePaethFilter(byte[] curr, byte[] prev, int count, int bpp) { int raw, priorPixel, priorRow, priorRowPixel; for (int i = 0; i < bpp; i++) { raw = curr[i] & 0xff; priorRow = prev[i] & 0xff; curr[i] = (byte)(raw + priorRow); } for (int i = bpp; i < count; i++) { raw = curr[i] & 0xff; priorPixel = curr[i - bpp] & 0xff; priorRow = prev[i] & 0xff; priorRowPixel = prev[i - bpp] & 0xff; curr[i] = (byte)(raw + paethPredictor(priorPixel, priorRow, priorRowPixel)); } } private void processPixels(int process, Raster src, WritableRaster dst, int xOffset, int step, int y, int width) { int srcX, dstX; // Create an array suitable for holding one pixel int[] ps = src.getPixel(0, 0, (int[])null); int[] pd = dst.getPixel(0, 0, (int[])null); dstX = xOffset; switch (process) { case POST_NONE: for (srcX = 0; srcX < width; srcX++) { src.getPixel(srcX, 0, ps); dst.setPixel(dstX, y, ps); dstX += step; } break; case POST_GAMMA: for (srcX = 0; srcX < width; srcX++) { src.getPixel(srcX, 0, ps); for (int i = 0; i < inputBands; i++) { int x = ps[i]; ps[i] = gammaLut[x]; } dst.setPixel(dstX, y, ps); dstX += step; } break; case POST_GRAY_LUT: for (srcX = 0; srcX < width; srcX++) { src.getPixel(srcX, 0, ps); pd[0] = grayLut[ps[0]]; dst.setPixel(dstX, y, pd); dstX += step; } break; case POST_GRAY_LUT_ADD_TRANS: for (srcX = 0; srcX < width; srcX++) { src.getPixel(srcX, 0, ps); int val = ps[0]; pd[0] = grayLut[val]; if (val == grayTransparentAlpha) { pd[1] = 0; } else { pd[1] = maxOpacity; } dst.setPixel(dstX, y, pd); dstX += step; } break; case POST_PALETTE_TO_RGB: for (srcX = 0; srcX < width; srcX++) { src.getPixel(srcX, 0, ps); int val = ps[0]; pd[0] = redPalette[val]; pd[1] = greenPalette[val]; pd[2] = bluePalette[val]; dst.setPixel(dstX, y, pd); dstX += step; } break; case POST_PALETTE_TO_RGBA: for (srcX = 0; srcX < width; srcX++) { src.getPixel(srcX, 0, ps); int val = ps[0]; pd[0] = redPalette[val]; pd[1] = greenPalette[val]; pd[2] = bluePalette[val]; pd[3] = alphaPalette[val]; dst.setPixel(dstX, y, pd); dstX += step; } break; case POST_ADD_GRAY_TRANS: for (srcX = 0; srcX < width; srcX++) { src.getPixel(srcX, 0, ps); int val = ps[0]; if (performGammaCorrection) { val = gammaLut[val]; } pd[0] = val; if (val == grayTransparentAlpha) { pd[1] = 0; } else { pd[1] = maxOpacity; } dst.setPixel(dstX, y, pd); dstX += step; } break; case POST_ADD_RGB_TRANS: for (srcX = 0; srcX < width; srcX++) { src.getPixel(srcX, 0, ps); int r = ps[0]; int g = ps[1]; int b = ps[2]; if (performGammaCorrection) { pd[0] = gammaLut[r]; pd[1] = gammaLut[g]; pd[2] = gammaLut[b]; } else { pd[0] = r; pd[1] = g; pd[2] = b; } if ((r == redTransparentAlpha) && (g == greenTransparentAlpha) && (b == blueTransparentAlpha)) { pd[3] = 0; } else { pd[3] = maxOpacity; } dst.setPixel(dstX, y, pd); dstX += step; } break; case POST_REMOVE_GRAY_TRANS: for (srcX = 0; srcX < width; srcX++) { src.getPixel(srcX, 0, ps); int g = ps[0]; if (performGammaCorrection) { pd[0] = gammaLut[g]; } else { pd[0] = g; } dst.setPixel(dstX, y, pd); dstX += step; } break; case POST_REMOVE_RGB_TRANS: for (srcX = 0; srcX < width; srcX++) { src.getPixel(srcX, 0, ps); int r = ps[0]; int g = ps[1]; int b = ps[2]; if (performGammaCorrection) { pd[0] = gammaLut[r]; pd[1] = gammaLut[g]; pd[2] = gammaLut[b]; } else { pd[0] = r; pd[1] = g; pd[2] = b; } dst.setPixel(dstX, y, pd); dstX += step; } break; case POST_GAMMA_EXP: for (srcX = 0; srcX < width; srcX++) { src.getPixel(srcX, 0, ps); int val = ps[0]; int alpha = ps[1]; int gamma = gammaLut[val]; pd[0] = gamma; pd[1] = gamma; pd[2] = gamma; pd[3] = alpha; dst.setPixel(dstX, y, pd); dstX += step; } break; case POST_GRAY_ALPHA_EXP: for (srcX = 0; srcX < width; srcX++) { src.getPixel(srcX, 0, ps); int val = ps[0]; int alpha = ps[1]; pd[0] = val; pd[1] = val; pd[2] = val; pd[3] = alpha; dst.setPixel(dstX, y, pd); dstX += step; } break; case POST_ADD_GRAY_TRANS_EXP: for (srcX = 0; srcX < width; srcX++) { src.getPixel(srcX, 0, ps); int val = ps[0]; if (performGammaCorrection) { val = gammaLut[val]; } pd[0] = val; pd[1] = val; pd[2] = val; if (val == grayTransparentAlpha) { pd[3] = 0; } else { pd[3] = maxOpacity; } dst.setPixel(dstX, y, pd); dstX += step; } break; case POST_GRAY_LUT_ADD_TRANS_EXP: for (srcX = 0; srcX < width; srcX++) { src.getPixel(srcX, 0, ps); int val = ps[0]; int val2 = grayLut[val]; pd[0] = val2; pd[1] = val2; pd[2] = val2; if (val == grayTransparentAlpha) { pd[3] = 0; } else { pd[3] = maxOpacity; } dst.setPixel(dstX, y, pd); dstX += step; } break; } } /** * Reads in an image of a given size and returns it as a * WritableRaster. */ private void decodePass(WritableRaster imRas, int xOffset, int yOffset, int xStep, int yStep, int passWidth, int passHeight) { if ((passWidth == 0) || (passHeight == 0)) { return; } int bytesPerRow = (inputBands*passWidth*bitDepth + 7)/8; int eltsPerRow = (bitDepth == 16) ? bytesPerRow/2 : bytesPerRow; byte[] curr = new byte[bytesPerRow]; byte[] prior = new byte[bytesPerRow]; // Create a 1-row tall Raster to hold the data WritableRaster passRow = createRaster(passWidth, 1, inputBands, eltsPerRow, bitDepth); DataBuffer dataBuffer = passRow.getDataBuffer(); int type = dataBuffer.getDataType(); byte[] byteData = null; short[] shortData = null; if (type == DataBuffer.TYPE_BYTE) { byteData = ((DataBufferByte)dataBuffer).getData(); } else { shortData = ((DataBufferUShort)dataBuffer).getData(); } // Decode the (sub)image row-by-row int srcY, dstY; for (srcY = 0, dstY = yOffset; srcY < passHeight; srcY++, dstY += yStep) { // Read the filter type byte and a row of data int filter = 0; try { filter = dataStream.read(); dataStream.readFully(curr, 0, bytesPerRow); } catch (Exception e) { ImagingListenerProxy.errorOccurred(JaiI18N.getString("PNGImageDecoder2"), e, this, false); // e.printStackTrace(); } switch (filter) { case PNG_FILTER_NONE: break; case PNG_FILTER_SUB: decodeSubFilter(curr, bytesPerRow, bytesPerPixel); break; case PNG_FILTER_UP: decodeUpFilter(curr, prior, bytesPerRow); break; case PNG_FILTER_AVERAGE: decodeAverageFilter(curr, prior, bytesPerRow, bytesPerPixel); break; case PNG_FILTER_PAETH: decodePaethFilter(curr, prior, bytesPerRow, bytesPerPixel); break; default: // Error -- uknown filter type throw new RuntimeException(JaiI18N.getString("PNGImageDecoder16")); } // Copy data into passRow byte by byte if (bitDepth < 16) { System.arraycopy(curr, 0, byteData, 0, bytesPerRow); } else { int idx = 0; for (int j = 0; j < eltsPerRow; j++) { shortData[j] = (short)((curr[idx] << 8) | (curr[idx + 1] & 0xff)); idx += 2; } } processPixels(postProcess, passRow, imRas, xOffset, xStep, dstY, passWidth); // Swap curr and prior byte[] tmp = prior; prior = curr; curr = tmp; } } private void decodeImage(boolean useInterlacing) { if (!useInterlacing) { decodePass(theTile, 0, 0, 1, 1, width, height); } else { decodePass(theTile, 0, 0, 8, 8, (width + 7)/8, (height + 7)/8); decodePass(theTile, 4, 0, 8, 8, (width + 3)/8, (height + 7)/8); decodePass(theTile, 0, 4, 4, 8, (width + 3)/4, (height + 3)/8); decodePass(theTile, 2, 0, 4, 4, (width + 1)/4, (height + 3)/4); decodePass(theTile, 0, 2, 2, 4, (width + 1)/2, (height + 1)/4); decodePass(theTile, 1, 0, 2, 2, width/2, (height + 1)/2); decodePass(theTile, 0, 1, 1, 2, width, height/2); } } // RenderedImage stuff public Raster getTile(int tileX, int tileY) { if (tileX != 0 || tileY != 0) { // Error -- bad tile requested throw new IllegalArgumentException(JaiI18N.getString("PNGImageDecoder17")); } return theTile; } public void dispose() { theTile = null; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/JPEGImageEncoder.java0000644000175000017500000002612010336211611027400 0ustar mathieumathieu/* * $RCSfile: JPEGImageEncoder.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.5 $ * $Date: 2005-11-14 22:44:48 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl; import java.awt.Point; import java.awt.RenderingHints; import java.awt.color.ColorSpace; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.DirectColorModel; import java.awt.image.IndexColorModel; import java.awt.image.PackedColorModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.io.IOException; import java.io.OutputStream; import com.sun.media.jai.codec.ImageEncoderImpl; import com.sun.media.jai.codec.ImageEncodeParam; import com.sun.media.jai.codec.JPEGEncodeParam; // // Need these classes since we are currently using the // Java2D JpegEncoder for our Jpeg Implementation. // import com.sun.image.codec.jpeg.JPEGQTable; import com.sun.image.codec.jpeg.JPEGDecodeParam; import com.sun.media.jai.codecimpl.ImagingListenerProxy; import com.sun.media.jai.codecimpl.util.ImagingException; /** * An ImageEncoder for the JPEG (JFIF) file format. * * The common cases of single band grayscale and three or four band RGB images * are handled so as to minimize the amount of information required of the * programmer. See the comments pertaining to the constructor and the * writeToStream() method for more detailed information. * * @since EA2 */ public class JPEGImageEncoder extends ImageEncoderImpl { private JPEGEncodeParam jaiEP = null; public JPEGImageEncoder(OutputStream output, ImageEncodeParam param) { super(output, param); if (param != null) { jaiEP = (JPEGEncodeParam)param; } } // // Go through the settable encoding parameters and see // if any of them have been set. If so, transfer then to the // com.sun.image.codec.jpeg.JPEGEncodeParam object. // static void modifyEncodeParam(JPEGEncodeParam jaiEP, com.sun.image.codec.jpeg.JPEGEncodeParam j2dEP, int nbands) { int val; int[] qTab; for(int i=0; ideflated into inflated using the * Inflater constructed during class instantiation. */ private final void inflate(byte[] deflated, byte[] inflated) { inflater.setInput(deflated); try { inflater.inflate(inflated); } catch(DataFormatException dfe) { String message = JaiI18N.getString("TIFFImage17"); ImagingListenerProxy.errorOccurred(message, new ImagingException(message, dfe), this, false); // throw new RuntimeException(JaiI18N.getString("TIFFImage17")+": "+ // dfe.getMessage()); } inflater.reset(); } /** * Creates a pixel-interleaved SampleModel. This is a hack * to work around a cast exception when using JAI with float data. */ private final static SampleModel createPixelInterleavedSampleModel(int dataType, int tileWidth, int tileHeight, int pixelStride, int scanlineStride, int bandOffsets[]) { SampleModel sampleModel = null; if(dataType == DataBuffer.TYPE_FLOAT) { // This is a hack to make this work with JAI which in some // cases downcasts the DataBuffer to a type-specific class. // In the case of float data this current means the JAI class // javax.media.jai.DataBufferFloat. try { Class rfClass = Class.forName("javax.media.jai.RasterFactory"); Class[] paramTypes = new Class[] {int.class, int.class, int.class, int.class, int.class, int[].class}; Method rfMthd = rfClass.getMethod("createPixelInterleavedSampleModel", paramTypes); Object[] params = new Object[] {new Integer(dataType), new Integer(tileWidth), new Integer(tileHeight), new Integer(pixelStride), new Integer(scanlineStride), bandOffsets}; sampleModel = (SampleModel)rfMthd.invoke(null, params); } catch(Exception e) { // Deliberately ignore the Exception. } } // Create a SampleModel for non-float data or, in the case of // float data, if it is still null. This latter case should occur // if and only if the decoder is being used without JAI. if(dataType != DataBuffer.TYPE_FLOAT || sampleModel == null) { sampleModel = RasterFactory.createPixelInterleavedSampleModel(dataType, tileWidth, tileHeight, pixelStride, scanlineStride, bandOffsets); } return sampleModel; } /** * Return as a long[] the value of a TIFF_LONG or TIFF_SHORT field. */ private final long[] getFieldAsLongs(TIFFField field) { long[] value = null; if(field.getType() == TIFFField.TIFF_SHORT) { char[] charValue = field.getAsChars(); value = new long[charValue.length]; for(int i = 0; i < charValue.length; i++) { value[i] = charValue[i] & 0xffff; } } else if(field.getType() == TIFFField.TIFF_LONG) { value = field.getAsLongs(); } else { throw new RuntimeException(); } return value; } /* * Check whether the specified tag exists in the specified * TIFFDirectory. If not, throw an error message. Otherwise * return the TIFFField. */ private TIFFField getField(TIFFDirectory dir, int tagID, String tagName) { TIFFField field = dir.getField(tagID); if (field == null) { MessageFormat mf = new MessageFormat(JaiI18N.getString("TIFFImage5")); mf.setLocale(Locale.getDefault()); throw new RuntimeException(mf.format(new Object[] {tagName})); } else { return field; } } /** * Constructs a TIFFImage that acquires its data from a given * SeekableStream and reads from a particular IFD of the stream. * The index of the first IFD is 0. * * @param stream the SeekableStream to read from. * @param param an instance of TIFFDecodeParam, or null. * @param directory the index of the IFD to read from. */ public TIFFImage(SeekableStream stream, TIFFDecodeParam param, int directory) throws IOException { this.stream = stream; if (param == null) { param = new TIFFDecodeParam(); } decodePaletteAsShorts = param.getDecodePaletteAsShorts(); // Read the specified directory. TIFFDirectory dir = param.getIFDOffset() == null ? new TIFFDirectory(stream, directory) : new TIFFDirectory(stream, param.getIFDOffset().longValue(), directory); // Set a property "tiff_directory". properties.put("tiff_directory", dir); // Get the number of samples per pixel TIFFField sfield = dir.getField(TIFFImageDecoder.TIFF_SAMPLES_PER_PIXEL); int samplesPerPixel = sfield == null ? 1 : (int)sfield.getAsLong(0); // Read the TIFF_PLANAR_CONFIGURATION field TIFFField planarConfigurationField = dir.getField(TIFFImageDecoder.TIFF_PLANAR_CONFIGURATION); char[] planarConfiguration = planarConfigurationField == null ? new char[] {1} : planarConfigurationField.getAsChars(); // Support planar format (band sequential) only for 1 sample/pixel. if (planarConfiguration[0] != 1 && samplesPerPixel != 1) { throw new RuntimeException(JaiI18N.getString("TIFFImage0")); } // Read the TIFF_BITS_PER_SAMPLE field TIFFField bitsField = dir.getField(TIFFImageDecoder.TIFF_BITS_PER_SAMPLE); char[] bitsPerSample = null; if(bitsField != null) { bitsPerSample = bitsField.getAsChars(); } else { bitsPerSample = new char[] {1}; // Ensure that all samples have the same bit depth. for (int i = 1; i < bitsPerSample.length; i++) { if (bitsPerSample[i] != bitsPerSample[0]) { throw new RuntimeException( JaiI18N.getString("TIFFImage1")); } } } sampleSize = (int)bitsPerSample[0]; // Read the TIFF_SAMPLE_FORMAT tag to see whether the data might be // signed or floating point TIFFField sampleFormatField = dir.getField(TIFFImageDecoder.TIFF_SAMPLE_FORMAT); char[] sampleFormat = null; if (sampleFormatField != null) { sampleFormat = sampleFormatField.getAsChars(); // Check that all the samples have the same format for (int l=1; l height) { // 2^32 - 1 (effectively infinity, entire image is 1 strip) // or RowsPerStrip > ImageLength so clamp as having a tile // larger than the image is pointless. tileHeight = height; } else { tileHeight = (int)l; } } TIFFField tileOffsetsField = getField(dir, TIFFImageDecoder.TIFF_STRIP_OFFSETS, "Strip Offsets"); tileOffsets = getFieldAsLongs(tileOffsetsField); TIFFField tileByteCountsField = dir.getField(TIFFImageDecoder.TIFF_STRIP_BYTE_COUNTS); if(tileByteCountsField == null) { // Attempt to infer the number of bytes in each strip. int totalBytes = ((sampleSize+7)/8)*numBands*width*height; int bytesPerStrip = ((sampleSize+7)/8)*numBands*width*tileHeight; int cumulativeBytes = 0; tileByteCounts = new long[tileOffsets.length]; for(int i = 0; i < tileOffsets.length; i++) { tileByteCounts[i] = Math.min(totalBytes - cumulativeBytes, bytesPerStrip); cumulativeBytes += bytesPerStrip; } if(compression != COMP_NONE) { // Replace the stream with one that will not throw // an EOFException when it runs past the end. this.stream = new NoEOFStream(stream); } } else { tileByteCounts = getFieldAsLongs(tileByteCountsField); } // Uncompressed image provided in a single tile: clamp to max bytes. int maxBytes = width*height*numBands*((sampleSize + 7)/8); if(tileByteCounts.length == 1 && compression == COMP_NONE && tileByteCounts[0] > maxBytes) { tileByteCounts[0] = maxBytes; } } // Calculate number of tiles and the tileSize in bytes tilesX = (width + tileWidth - 1)/tileWidth; tilesY = (height + tileHeight - 1)/tileHeight; tileSize = tileWidth * tileHeight * numBands; // Check whether big endian or little endian format is used. isBigEndian = dir.isBigEndian(); TIFFField fillOrderField = dir.getField(TIFFImageDecoder.TIFF_FILL_ORDER); if (fillOrderField != null) { fillOrder = fillOrderField.getAsInt(0); } else { // Default Fill Order fillOrder = 1; } switch(compression) { case COMP_NONE: case COMP_PACKBITS: // Do nothing. break; case COMP_DEFLATE: inflater = new Inflater(); break; case COMP_FAX_G3_1D: case COMP_FAX_G3_2D: case COMP_FAX_G4_2D: if(sampleSize != 1) { throw new RuntimeException(JaiI18N.getString("TIFFImage7")); } // Fax T.4 compression options if (compression == 3) { TIFFField t4OptionsField = dir.getField(TIFFImageDecoder.TIFF_T4_OPTIONS); if (t4OptionsField != null) { tiffT4Options = t4OptionsField.getAsLong(0); } else { // Use default value tiffT4Options = 0; } } // Fax T.6 compression options if (compression == 4) { TIFFField t6OptionsField = dir.getField(TIFFImageDecoder.TIFF_T6_OPTIONS); if (t6OptionsField != null) { tiffT6Options = t6OptionsField.getAsLong(0); } else { // Use default value tiffT6Options = 0; } } // Fax encoding, need to create the Fax decoder. decoder = new TIFFFaxDecoder(fillOrder, tileWidth, tileHeight); break; case COMP_LZW: // LZW compression used, need to create the LZW decoder. TIFFField predictorField = dir.getField(TIFFImageDecoder.TIFF_PREDICTOR); if (predictorField == null) { predictor = 1; } else { predictor = predictorField.getAsInt(0); if (predictor != 1 && predictor != 2) { throw new RuntimeException(JaiI18N.getString("TIFFImage8")); } if (predictor == 2 && sampleSize != 8) { throw new RuntimeException(sampleSize + JaiI18N.getString("TIFFImage9")); } } lzwDecoder = new TIFFLZWDecoder(tileWidth, predictor, samplesPerPixel); break; case COMP_JPEG_OLD: throw new RuntimeException(JaiI18N.getString("TIFFImage15")); case COMP_JPEG_TTN2: if(!(sampleSize == 8 && ((imageType == TYPE_GRAY && samplesPerPixel == 1) || (imageType == TYPE_PALETTE && samplesPerPixel == 1) || (imageType == TYPE_RGB && samplesPerPixel == 3)))) { throw new RuntimeException(JaiI18N.getString("TIFFImage16")); } // Create decodeParam from JPEGTables field if present. if(dir.isTagPresent(TIFF_JPEG_TABLES)) { TIFFField jpegTableField = dir.getField(TIFF_JPEG_TABLES); byte[] jpegTable = jpegTableField.getAsBytes(); ByteArrayInputStream tableStream = new ByteArrayInputStream(jpegTable); JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(tableStream); decoder.decodeAsRaster(); decodeParam = decoder.getJPEGDecodeParam(); } break; default: throw new RuntimeException(JaiI18N.getString("TIFFImage10")); } switch(imageType) { case TYPE_BILEVEL: case TYPE_GRAY_4BIT: sampleModel = new MultiPixelPackedSampleModel(dataType, tileWidth, tileHeight, sampleSize); if(imageType == TYPE_BILEVEL) { byte[] map = new byte[] {(byte)(isWhiteZero ? 255 : 0), (byte)(isWhiteZero ? 0 : 255)}; colorModel = new IndexColorModel(1, 2, map, map, map); } else { colorModel = ImageCodec.createGrayIndexColorModel(sampleModel, !isWhiteZero); } break; case TYPE_GRAY: case TYPE_GRAY_ALPHA: case TYPE_RGB: case TYPE_RGB_ALPHA: case TYPE_CMYK: // Create a pixel interleaved SampleModel with decreasing // band offsets. int[] RGBOffsets = new int[numBands]; if(compression == COMP_JPEG_TTN2) { for (int i=0; i= tilesX) || (tileY < 0) || (tileY >= tilesY)) { throw new IllegalArgumentException(JaiI18N.getString("TIFFImage12")); } // The tile to return. WritableRaster tile = null; // Synchronize the rest of the method in case other TIFFImage // instances using the same stream were created by the same // TIFFImageDecoder. This fixes 4690773. synchronized(this.stream) { // Get the data array out of the DataBuffer byte bdata[] = null; short sdata[] = null; int idata[] = null; float fdata[] = null; DataBuffer buffer = sampleModel.createDataBuffer(); int dataType = sampleModel.getDataType(); if (dataType == DataBuffer.TYPE_BYTE) { bdata = ((DataBufferByte)buffer).getData(); } else if (dataType == DataBuffer.TYPE_USHORT) { sdata = ((DataBufferUShort)buffer).getData(); } else if (dataType == DataBuffer.TYPE_SHORT) { sdata = ((DataBufferShort)buffer).getData(); } else if (dataType == DataBuffer.TYPE_INT) { idata = ((DataBufferInt)buffer).getData(); } else if (dataType == DataBuffer.TYPE_FLOAT) { if(buffer instanceof DataBufferFloat) { fdata = ((DataBufferFloat)buffer).getData(); } else { // This is a hack to make this work with JAI which in some // cases downcasts the DataBuffer to a type-specific class. // In the case of float data this current means the JAI class // javax.media.jai.DataBufferFloat. try { Method getDataMethod = buffer.getClass().getMethod("getData", null); fdata = (float[])getDataMethod.invoke(buffer, null); } catch(Exception e) { String message = JaiI18N.getString("TIFFImage18"); ImagingListenerProxy.errorOccurred(message, new ImagingException(message, e), this, false); // throw new RuntimeException(JaiI18N.getString("TIFFImage18")); } } } tile = (WritableRaster)RasterFactory.createWritableRaster(sampleModel, buffer, new Point(tileXToX(tileX), tileYToY(tileY))); // Save original file pointer position and seek to tile data location. long save_offset = 0; try { save_offset = stream.getFilePointer(); stream.seek(tileOffsets[tileY*tilesX + tileX]); } catch (IOException ioe) { String message = JaiI18N.getString("TIFFImage13"); ImagingListenerProxy.errorOccurred(message, new ImagingException(message, ioe), this, false); // throw new RuntimeException(JaiI18N.getString("TIFFImage13")); } // Number of bytes in this tile (strip) after compression. int byteCount = (int)tileByteCounts[tileY*tilesX + tileX]; // Find out the number of bytes in the current tile. If the image is // tiled this may include pixels which are outside of the image bounds // if the image width and height are not multiples of the tile width // and height respectively. Rectangle tileRect = new Rectangle(tileXToX(tileX), tileYToY(tileY), tileWidth, tileHeight); Rectangle newRect = isTiled ? tileRect : tileRect.intersection(getBounds()); int unitsInThisTile = newRect.width * newRect.height * numBands; // Allocate read buffer if needed. byte data[] = compression != COMP_NONE || imageType == TYPE_PALETTE ? new byte[byteCount] : null; // Read the data, uncompressing as needed. There are four cases: // bilevel, palette-RGB, 4-bit grayscale, and everything else. if(imageType == TYPE_BILEVEL) { // bilevel try { if (compression == COMP_PACKBITS) { stream.readFully(data, 0, byteCount); // Since the decompressed data will still be packed // 8 pixels into 1 byte, calculate bytesInThisTile int bytesInThisTile; if ((newRect.width % 8) == 0) { bytesInThisTile = (newRect.width/8) * newRect.height; } else { bytesInThisTile = (newRect.width/8 + 1) * newRect.height; } decodePackbits(data, bytesInThisTile, bdata); } else if (compression == COMP_LZW) { stream.readFully(data, 0, byteCount); lzwDecoder.decode(data, bdata, newRect.height); } else if (compression == COMP_FAX_G3_1D) { stream.readFully(data, 0, byteCount); decoder.decode1D(bdata, data, 0, newRect.height); } else if (compression == COMP_FAX_G3_2D) { stream.readFully(data, 0, byteCount); decoder.decode2D(bdata, data, 0, newRect.height, tiffT4Options); } else if (compression == COMP_FAX_G4_2D) { stream.readFully(data, 0, byteCount); decoder.decodeT6(bdata, data, 0, newRect.height, tiffT6Options); } else if (compression == COMP_DEFLATE) { stream.readFully(data, 0, byteCount); inflate(data, bdata); } else if (compression == COMP_NONE) { stream.readFully(bdata, 0, byteCount); } stream.seek(save_offset); } catch (IOException ioe) { String message = JaiI18N.getString("TIFFImage13"); ImagingListenerProxy.errorOccurred(message, new ImagingException(message, ioe), this, false); // throw new RuntimeException(JaiI18N.getString("TIFFImage13")); } } else if(imageType == TYPE_PALETTE) { // palette-RGB if (sampleSize == 16) { if (decodePaletteAsShorts) { short tempData[]= null; // At this point the data is 1 banded and will // become 3 banded only after we've done the palette // lookup, since unitsInThisTile was calculated with // 3 bands, we need to divide this by 3. int unitsBeforeLookup = unitsInThisTile / 3; // Since unitsBeforeLookup is the number of shorts, // but we do our decompression in terms of bytes, we // need to multiply it by 2 in order to figure out // how many bytes we'll get after decompression. int entries = unitsBeforeLookup * 2; // Read the data, if compressed, decode it, reset the pointer try { if (compression == COMP_PACKBITS) { stream.readFully(data, 0, byteCount); byte byteArray[] = new byte[entries]; decodePackbits(data, entries, byteArray); tempData = new short[unitsBeforeLookup]; interpretBytesAsShorts(byteArray, tempData, unitsBeforeLookup); } else if (compression == COMP_LZW) { // Read in all the compressed data for this tile stream.readFully(data, 0, byteCount); byte byteArray[] = new byte[entries]; lzwDecoder.decode(data, byteArray, newRect.height); tempData = new short[unitsBeforeLookup]; interpretBytesAsShorts(byteArray, tempData, unitsBeforeLookup); } else if (compression == COMP_DEFLATE) { stream.readFully(data, 0, byteCount); byte byteArray[] = new byte[entries]; inflate(data, byteArray); tempData = new short[unitsBeforeLookup]; interpretBytesAsShorts(byteArray, tempData, unitsBeforeLookup); } else if (compression == COMP_NONE) { // byteCount tells us how many bytes are there // in this tile, but we need to read in shorts, // which will take half the space, so while // allocating we divide byteCount by 2. tempData = new short[byteCount/2]; readShorts(byteCount/2, tempData); } stream.seek(save_offset); } catch (IOException ioe) { String message = JaiI18N.getString("TIFFImage13"); ImagingListenerProxy.errorOccurred(message, new ImagingException(message, ioe), this, false); // throw new RuntimeException( // JaiI18N.getString("TIFFImage13")); } if (dataType == DataBuffer.TYPE_USHORT) { // Expand the palette image into an rgb image with ushort // data type. int cmapValue; int count = 0, lookup, len = colormap.length/3; int len2 = len * 2; for (int i=0; i> 4); data[dstCount++] = (byte)(tempData[srcCount++] & 0x0f); } if (padding == 1) { data[dstCount++] = (byte)((tempData[srcCount++] & 0xf0) >> 4); } } int len = colormap.length/3; int len2 = len*2; int cmapValue, lookup; int count = 0; for (int i=0; i= 0 && b <= 127) { // literal run packet for (int i=0; i<(b + 1); i++) { dst[dstCount++] = data[srcCount++]; } } else if (b <= -1 && b >= -127) { // 2 byte encoded run packet repeat = data[srcCount++]; for (int i=0; i<(-b + 1); i++) { dst[dstCount++] = repeat; } } else { // no-op packet. Do nothing srcCount++; } } } catch (java.lang.ArrayIndexOutOfBoundsException ae) { String message = JaiI18N.getString("TIFFImage14"); ImagingListenerProxy.errorOccurred(message, new ImagingException(message, ae), this, false); // throw new RuntimeException(JaiI18N.getString("TIFFImage14")); } return dst; } // Need a createColorModel(). // Create ComponentColorModel for TYPE_RGB images private ComponentColorModel createAlphaComponentColorModel( int dataType, int numBands, boolean isAlphaPremultiplied, int transparency) { ComponentColorModel ccm = null; int RGBBits[] = null; ColorSpace cs = null; switch(numBands) { case 2: // gray+alpha cs = ColorSpace.getInstance(ColorSpace.CS_GRAY); break; case 4: // RGB+alpha cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); break; default: throw new IllegalArgumentException(); } if(dataType == DataBuffer.TYPE_FLOAT) { ccm = new FloatDoubleColorModel(cs, true, isAlphaPremultiplied, transparency, dataType); } else { // all other types int componentSize = 0; switch(dataType) { case DataBuffer.TYPE_BYTE: componentSize = 8; break; case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: componentSize = 16; break; case DataBuffer.TYPE_INT: componentSize = 32; break; default: throw new IllegalArgumentException(); } RGBBits = new int[numBands]; for(int i = 0; i < numBands; i++) { RGBBits[i] = componentSize; } ccm = new ComponentColorModel(cs, RGBBits, true, isAlphaPremultiplied, transparency, dataType); } return ccm; } } /** * Wrapper class for a SeekableStream but which does not throw * an EOFException from readFully() when the end * of stream is encountered. */ // NB This is a hack to fix bug 4823200 "Make TIFF decoder robust to (comp) // images with no strip/tile byte counts field" but there does not seem to // be any other way to work around this without extensive code changes. class NoEOFStream extends SeekableStream { private SeekableStream stream; NoEOFStream(SeekableStream ss) { if(ss == null) { throw new IllegalArgumentException(); } this.stream = ss; } public int read() throws IOException { int b = stream.read(); return b < 0 ? 0 : b; } public int read(byte[] b, int off, int len) throws IOException { int count = stream.read(b, off, len); return count < 0 ? len : count; } public long getFilePointer() throws IOException { return stream.getFilePointer(); } public void seek(long pos) throws IOException { stream.seek(pos); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/com.sun.media.jai.codecimpl.properties0000644000175000017500000001714710417241013033033 0ustar mathieumathieu# # $RCSfile: com.sun.media.jai.codecimpl.properties,v $ # # Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. # # Use is subject to license terms. # # $Revision: 1.3 $ # $Date: 2006-04-12 18:08:11 $ # $State: Exp $ # BMPImageDecoder0=Invalid magic value for BMP file BMPImageDecoder1=Invalid compression specified in BMP file. BMPImageDecoder2=Not implemented yet. BMPImageDecoder3=Invalid compression specified for BMP file. BMPImageDecoder4=BMP version 5 not implemented yet. BMPImageDecoder5=IOException while reading the BMP file headers. BMPImageDecoder6=Error while reading the BMP file. BMPImageDecoder7=Illegal tile requested from a BMPImage. BMPImageDecoder8=Illegal page requested from a BMP file. BMPImageDecoder9=IOException while resetting and skipping bytes from InputStream. BMPImageEncoder0=Image to be written has ushort/short/int/float/double data type, unsuitable for BMP file format. BMPImageEncoder1=Only images with either 1 or 3 bands can be written out as BMP files. BMPImageEncoder2=BMP file format cannot support data with a bitdepth greater than 8. BMPImageEncoder3=All samples must have the same size. BMPImageEncoder4=IOException occurs when read data. BMPImageEncoder5=Encoding of BMP files in any format other than Version 3 is not implemented yet. BMPImageEncoder6=If Image is to be compressed, then the OutputStream parameter must be a SeekableOutputStream. FPXCodec0=FPX encoding not supported yet. FPXImageDecoder0=Illegal page requested from an FPX file. GIFImage0=Unexpected block type GIFImage1=Error reading GIF image header. GIFImage2=Illegal tile requested from a GIFImage. GIFImage3=Error reading GIF image data. GIFImageDecoder0=Error reading GIF stream header. GIFImageDecoder1=Illegal page requested from a GIF file. JPEGImageDecoder0=Illegal page requested from a JPEG file. JPEGImageDecoder1=Unable to process image stream, incorrect format. JPEGImageDecoder2=Unable to process image stream, I/O error. JPEGImageDecoder3=Cannot decode a 2-banded image with a PackedColorModel. JPEGImageDecoder4=Illegal tile requested from a JPEG image. JPEGImageEncoder0=Only 1, or 3-band byte data may be written. JPEGImageEncoder1=ColorSpace must be TYPE_RGB for numBands > 1 JPEGImageEncoder2=IOException occurs when encode the image. PNGCodec0=PNG encoding not supported yet. PNGImageDecoder0=PNG magic number not found. PNGImageDecoder1=Error reading PNG header. PNGImageDecoder2=I/O error reading PNG file. PNGImageDecoder3=Illegal bit depth for a PNG image. PNGImageDecoder4=Bad color type for a PNG image. PNGImageDecoder5=An RGB PNG image can't have a bit depth less than 8. PNGImageDecoder6=A palette-color PNG image can't have a bit depth of 16. PNGImageDecoder7=A PNG Gray+Alpha image can't have a bit depth less than 8. PNGImageDecoder8=A PNG RGB+Alpha image can't have a bit depth less than 8. PNGImageDecoder9=Unsupported PNG compression method (not 0). PNGImageDecoder10=Unsupported PNG filter method (not 0). PNGImageDecoder11=Unsupported PNG interlace method (not 0 or 1). PNGImageDecoder12=Unknown PNG pHYs unit specifier (not 0 or 1). PNGImageDecoder13=Illegal PNG sBit value (< 0 or > bit depth). PNGImageDecoder14=Too many PNG alpha palette entries. PNGImageDecoder15=PNG image already has alpha, can't have tRNS chunk. PNGImageDecoder16=Unknown PNG filter type (not 0-4). PNGImageDecoder17=Illegal tile requested from a PNG image. PNGImageDecoder18=PNG can't have hIST chunk without a PLTE chunk. PNGImageDecoder19=Illegal page requested from a PNG file. PNGImageDecoder20=IOException occurs when get the chunk type. PNGImageDecoder21=IOException occurs when get a chunk. PNMImageDecoder0=Invalid magic value for PBM/PGM/PPM file. PNMImageDecoder1=Unrecognized file variant. PNMImageDecoder2=IOException occured while reading PNM file header. PNMImageDecoder3=IOException occured while processing PNM file. PNMImageDecoder4=Illegal tile requested from a PNMImage. PNMImageDecoder5=Illegal page requested from a PNM file. PNMImageDecoder6=IOException occurs when read the image header. PNMImageDecoder7=IOException occurs when read the image data. PNMImageEncoder0=Source image has float/double data type, unsuitable for PNM file format. PNMImageEncoder1=Image has an IndexColorModel whose map size is to small for the data type obtained from SampleModel. PNMImageEncoder2=Source image has unsuitable number of bands for PNM file format. SimpleRenderedImage0=The specified region, if not null, must intersect the image bounds. SingleTileRenderedImage0=Illegal tile requested from a SingleTileRenderedImage. TIFFFaxDecoder0=Invalid code encountered. TIFFFaxDecoder1=EOL code word encountered in White run. TIFFFaxDecoder2=EOL code word encountered in Black run. TIFFFaxDecoder3=First scanline must be 1D encoded. TIFFFaxDecoder4=Invalid code encountered while decoding 2D group 3 compressed data. TIFFFaxDecoder5=Invalid code encountered while decoding 2D group 4 compressed data. TIFFFaxDecoder6=Scanline must begin with EOL code word. TIFFFaxDecoder7=TIFF_FILL_ORDER tag must be either 1 or 2. TIFFFaxDecoder8=All fill bits preceding EOL code must be 0. TIFFFaxDecoder9=End of data reached before next EOL encountered. TIFFImage0=Planar (band-sequential) format TIFF is not supported. TIFFImage1=All samples must have the same bit depth. TIFFImage2=All samples must have the same data format. TIFFImage3=Unsupported combination of bit depth and sample format. TIFFImage4=Unsupported combination of photometric interpretation, samples per pixel, and bit depth. TIFFImage5="{0}", a required field, is not present in the TIFF file. TIFFImage7=Unsupported compression type for non-bilevel data. TIFFImage8=Illegal value for Predictor in TIFF file. TIFFImage9=-bit samples are not supported for Horizontal differencing Predictor. TIFFImage10=Unsupported compression type. TIFFImage11=Colormap must be present for a Palette Color image. TIFFImage12=Illegal tile requested from a TIFFImage. TIFFImage13=IOException occured while reading TIFF image data. TIFFImage14=Unable to decode Packbits compressed data - not enough data. TIFFImage15=Decoding of old style JPEG-in-TIFF data is not supported. TIFFImage16=JPEG-in-TIFF decoding supported only for 8-bit samples and either 1 (grayscale or palette-color) or 3 (RGB or YCbCr) samples per pixel. TIFFImage17=Error inflating data TIFFImage18=Error extracting data array from floating point DataBuffer. TIFFImageDecoder0=Illegal page requested from a TIFF file. TIFFImageEncoder0=All samples must have the same bit depth. TIFFImageEncoder1=1- and 4-bit data supported for single band images only. TIFFImageEncoder2=Byte buffers require 1-, 4-, or 8-bit data. TIFFImageEncoder3=Short or unsigned short buffers require 16-bit data. TIFFImageEncoder4=Int or float buffers require 32-bit data. TIFFImageEncoder5=Unsupported output data type. TIFFImageEncoder6=TIFF encoder does not support (unsigned) short palette images. TIFFImageEncoder7=Invalid image - An image with sampleSize of 1 bit must have IndexColorModel with mapsize of 2. TIFFImageEncoder8=Image type not supported for output. TIFFImageEncoder9=JPEG-in-TIFF encoding supported only for 8-bit samples and either 1 (grayscale) or 3 (RGB or YCbCr) samples per pixel. TIFFImageEncoder10=Unsupported TIFFField type. TIFFImageEncoder11=JPEG-in-TIFF encoding is not supported for palette-color images. TIFFImageEncoder12=Bilevel encodings are supported for bilevel images only. TIFFLZWDecoder0=TIFF 5.0-style LZW codes are not supported. WBMPImageDecoder0=Illegal page requested from a WBMP file. WBMPImageEncoder0=WBMP encoder does not support FLOAT or DOUBLE buffers. WBMPImageEncoder1=WBMP encoder can write only 1-band images. WBMPImageEncoder2=WBMP encoder can write only bilevel (1 bit per pixel) images. jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/CodecUtils.java0000644000175000017500000000521010472445724026462 0ustar mathieumathieu/* * $RCSfile: CodecUtils.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2006-08-22 00:12:04 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.SinglePixelPackedSampleModel; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * A class for utility functions for codecs. */ class CodecUtils { /** * The initCause() method of IOException * which is available from J2SE 1.4 onward. */ static Method ioExceptionInitCause; static { try { Class c = Class.forName("java.io.IOException"); ioExceptionInitCause = c.getMethod("initCause", new Class[] {java.lang.Throwable.class}); } catch(Exception e) { ioExceptionInitCause = null; } } /** * Returns true if and only if im * has a SinglePixelPackedSampleModel with a * sample size of at most 8 bits for each of its bands. * * @param src The RenderedImage to test. * @return Whether the image is byte-packed. */ static final boolean isPackedByteImage(RenderedImage im) { SampleModel imageSampleModel = im.getSampleModel(); if(imageSampleModel instanceof SinglePixelPackedSampleModel) { for(int i = 0; i < imageSampleModel.getNumBands(); i++) { if(imageSampleModel.getSampleSize(i) > 8) { return false; } } return true; } return false; } /** * Converts the parameter exception to an IOException. */ static final IOException toIOException(Exception cause) { IOException ioe; if(cause != null) { if(cause instanceof IOException) { ioe = (IOException)cause; } else if(ioExceptionInitCause != null) { ioe = new IOException(cause.getMessage()); try { ioExceptionInitCause.invoke(ioe, new Object[] {cause}); } catch(Exception e2) { // Ignore it ... } } else { ioe = new IOException(cause.getClass().getName()+": "+ cause.getMessage()); } } else { ioe = new IOException(); } return ioe; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/PNGImageEncoder.java0000644000175000017500000010312110203035544027276 0ustar mathieumathieu/* * $RCSfile: PNGImageEncoder.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:37 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl; import java.awt.image.IndexColorModel; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.io.ByteArrayOutputStream; import java.io.DataOutput; import java.io.DataOutputStream; import java.io.FileOutputStream; import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.TimeZone; import java.util.zip.Deflater; import java.util.zip.DeflaterOutputStream; import com.sun.media.jai.codec.FileSeekableStream; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.ImageDecoder; import com.sun.media.jai.codec.ImageEncoder; import com.sun.media.jai.codec.ImageEncoderImpl; import com.sun.media.jai.codec.PNGDecodeParam; import com.sun.media.jai.codec.PNGEncodeParam; import com.sun.media.jai.codec.SeekableStream; class CRC { private static int[] crcTable = new int[256]; static { // Initialize CRC table for (int n = 0; n < 256; n++) { int c = n; for (int k = 0; k < 8; k++) { if ((c & 1) == 1) { c = 0xedb88320 ^ (c >>> 1); } else { c >>>= 1; } crcTable[n] = c; } } } public static int updateCRC(int crc, byte[] data, int off, int len) { int c = crc; for (int n = 0; n < len; n++) { c = crcTable[(c ^ data[off + n]) & 0xff] ^ (c >>> 8); } return c; } } class ChunkStream extends OutputStream implements DataOutput { private String type; private ByteArrayOutputStream baos; private DataOutputStream dos; public ChunkStream(String type) throws IOException { this.type = type; this.baos = new ByteArrayOutputStream(); this.dos = new DataOutputStream(baos); } public void write(byte[] b) throws IOException { dos.write(b); } public void write(byte[] b, int off, int len) throws IOException { dos.write(b, off, len); } public void write(int b) throws IOException { dos.write(b); } public void writeBoolean(boolean v) throws IOException { dos.writeBoolean(v); } public void writeByte(int v) throws IOException { dos.writeByte(v); } public void writeBytes(String s) throws IOException { dos.writeBytes(s); } public void writeChar(int v) throws IOException { dos.writeChar(v); } public void writeChars(String s) throws IOException { dos.writeChars(s); } public void writeDouble(double v) throws IOException { dos.writeDouble(v); } public void writeFloat(float v) throws IOException { dos.writeFloat(v); } public void writeInt(int v) throws IOException { dos.writeInt(v); } public void writeLong(long v) throws IOException { dos.writeLong(v); } public void writeShort(int v) throws IOException { dos.writeShort(v); } public void writeUTF(String str) throws IOException { dos.writeUTF(str); } public void writeToStream(DataOutputStream output) throws IOException { byte[] typeSignature = new byte[4]; typeSignature[0] = (byte)type.charAt(0); typeSignature[1] = (byte)type.charAt(1); typeSignature[2] = (byte)type.charAt(2); typeSignature[3] = (byte)type.charAt(3); dos.flush(); baos.flush(); byte[] data = baos.toByteArray(); int len = data.length; output.writeInt(len); output.write(typeSignature); output.write(data, 0, len); int crc = 0xffffffff; crc = CRC.updateCRC(crc, typeSignature, 0, 4); crc = CRC.updateCRC(crc, data, 0, len); output.writeInt(crc ^ 0xffffffff); } } class IDATOutputStream extends FilterOutputStream { private static final byte[] typeSignature = {(byte)'I', (byte)'D', (byte)'A', (byte)'T'}; private int bytesWritten = 0; private int segmentLength; byte[] buffer; public IDATOutputStream(OutputStream output, int segmentLength) { super(output); this.segmentLength = segmentLength; this.buffer = new byte[segmentLength]; } public void close() throws IOException { flush(); } private void writeInt(int x) throws IOException { out.write(x >> 24); out.write((x >> 16) & 0xff); out.write((x >> 8) & 0xff); out.write(x & 0xff); } public void flush() throws IOException { // Length writeInt(bytesWritten); // 'IDAT' signature out.write(typeSignature); // Data out.write(buffer, 0, bytesWritten); int crc = 0xffffffff; crc = CRC.updateCRC(crc, typeSignature, 0, 4); crc = CRC.updateCRC(crc, buffer, 0, bytesWritten); // CRC writeInt(crc ^ 0xffffffff); // Reset buffer bytesWritten = 0; } public void write(byte[] b) throws IOException { this.write(b, 0, b.length); } public void write(byte[] b, int off, int len) throws IOException { while (len > 0) { int bytes = Math.min(segmentLength - bytesWritten, len); System.arraycopy(b, off, buffer, bytesWritten, bytes); off += bytes; len -= bytes; bytesWritten += bytes; if (bytesWritten == segmentLength) { flush(); } } } public void write(int b) throws IOException { buffer[bytesWritten++] = (byte)b; if (bytesWritten == segmentLength) { flush(); } } } /** * An ImageEncoder for the PNG file format. * * @since EA4 */ public class PNGImageEncoder extends ImageEncoderImpl { private static final int PNG_COLOR_GRAY = 0; private static final int PNG_COLOR_RGB = 2; private static final int PNG_COLOR_PALETTE = 3; private static final int PNG_COLOR_GRAY_ALPHA = 4; private static final int PNG_COLOR_RGB_ALPHA = 6; private static final byte[] magic = { (byte)137, (byte) 80, (byte) 78, (byte) 71, (byte) 13, (byte) 10, (byte) 26, (byte) 10 }; private static int filterPrintableLatin1(byte[] data) { int len = 0; int prev = 0; for (int i = 0; i < data.length; i++) { int d = data[i] & 0xFF; if (prev == 32 && d == 32) continue; if ((d > 32 && d <=126) || (d >= 161 && d <=255)) data[len++] = (byte)d; prev = d; } return len; } private PNGEncodeParam param; private RenderedImage image; private int width; private int height; private int bitDepth; private int bitShift; private int numBands; private int colorType; private int bpp; // bytes per pixel, rounded up private boolean skipAlpha = false; private boolean compressGray = false; private boolean interlace; private byte[] redPalette = null; private byte[] greenPalette = null; private byte[] bluePalette = null; private byte[] alphaPalette = null; private DataOutputStream dataOutput; public PNGImageEncoder(OutputStream output, PNGEncodeParam param) { super(output, param); if (param != null) { this.param = (PNGEncodeParam)param; } this.dataOutput = new DataOutputStream(output); } private void writeMagic() throws IOException { dataOutput.write(magic); } private void writeIHDR() throws IOException { ChunkStream cs = new ChunkStream("IHDR"); cs.writeInt(width); cs.writeInt(height); cs.writeByte((byte)bitDepth); cs.writeByte((byte)colorType); cs.writeByte((byte)0); cs.writeByte((byte)0); cs.writeByte(interlace ? (byte)1 : (byte)0); cs.writeToStream(dataOutput); } private byte[] prevRow = null; private byte[] currRow = null; private byte[][] filteredRows = null; private static int clamp(int val, int maxValue) { return (val > maxValue) ? maxValue : val; } private void encodePass(OutputStream os, Raster ras, int xOffset, int yOffset, int xSkip, int ySkip) throws IOException { int minX = ras.getMinX(); int minY = ras.getMinY(); int width = ras.getWidth(); int height = ras.getHeight(); xOffset *= numBands; xSkip *= numBands; int samplesPerByte = 8/bitDepth; int numSamples = width*numBands; int[] samples = new int[numSamples]; int pixels = (numSamples - xOffset + xSkip - 1)/xSkip; int bytesPerRow = pixels*numBands; if (bitDepth < 8) { bytesPerRow = (bytesPerRow + samplesPerByte - 1)/samplesPerByte; } else if (bitDepth == 16) { bytesPerRow *= 2; } if (bytesPerRow == 0) { return; } currRow = new byte[bytesPerRow + bpp]; prevRow = new byte[bytesPerRow + bpp]; filteredRows = new byte[5][bytesPerRow + bpp]; int maxValue = (1 << bitDepth) - 1; for (int row = minY + yOffset; row < minY + height; row += ySkip) { ras.getPixels(minX, row, width, 1, samples); if (compressGray) { int shift = 8 - bitDepth; for (int i = 0; i < width; i++) { samples[i] >>= shift; } } int count = bpp; // leave first 'bpp' bytes zero int pos = 0; int tmp = 0; switch (bitDepth) { case 1: case 2: case 4: // Image can only have a single band int mask = samplesPerByte - 1; for (int s = xOffset; s < numSamples; s += xSkip) { int val = clamp(samples[s] >> bitShift, maxValue); tmp = (tmp << bitDepth) | val; if ((pos++ & mask) == mask) { currRow[count++] = (byte)tmp; tmp = 0; } } // Left shift the last byte if ((pos & mask) != 0) { // Fix 4655018: PNGImageEncoder doesn't correctly write some // bilevel images. // modify "pos" to "pos & mask" in the sentence below. tmp <<= (8/bitDepth - (pos & mask) )*bitDepth; currRow[count++] = (byte)tmp; } break; case 8: for (int s = xOffset; s < numSamples; s += xSkip) { for (int b = 0; b < numBands; b++) { currRow[count++] = (byte)clamp(samples[s + b] >> bitShift, maxValue); } } break; case 16: for (int s = xOffset; s < numSamples; s += xSkip) { for (int b = 0; b < numBands; b++) { int val = clamp(samples[s + b] >> bitShift, maxValue); currRow[count++] = (byte)(val >> 8); currRow[count++] = (byte)(val & 0xff); } } break; } // Perform filtering int filterType = param.filterRow(currRow, prevRow, filteredRows, bytesPerRow, bpp); os.write(filterType); os.write(filteredRows[filterType], bpp, bytesPerRow); // Swap current and previous rows byte[] swap = currRow; currRow = prevRow; prevRow = swap; } } private void writeIDAT() throws IOException { IDATOutputStream ios = new IDATOutputStream(dataOutput, 8192); DeflaterOutputStream dos = new DeflaterOutputStream(ios, new Deflater(9)); // Future work - don't convert entire image to a Raster Raster ras = image.getData(); if (skipAlpha) { int numBands = ras.getNumBands() - 1; int[] bandList = new int[numBands]; for (int i = 0; i < numBands; i++) { bandList[i] = i; } ras = ras.createChild(0, 0, ras.getWidth(), ras.getHeight(), 0, 0, bandList); } if (interlace) { // Interlacing pass 1 encodePass(dos, ras, 0, 0, 8, 8); // Interlacing pass 2 encodePass(dos, ras, 4, 0, 8, 8); // Interlacing pass 3 encodePass(dos, ras, 0, 4, 4, 8); // Interlacing pass 4 encodePass(dos, ras, 2, 0, 4, 4); // Interlacing pass 5 encodePass(dos, ras, 0, 2, 2, 4); // Interlacing pass 6 encodePass(dos, ras, 1, 0, 2, 2); // Interlacing pass 7 encodePass(dos, ras, 0, 1, 1, 2); } else { encodePass(dos, ras, 0, 0, 1, 1); } dos.finish(); ios.flush(); } private void writeIEND() throws IOException { ChunkStream cs = new ChunkStream("IEND"); cs.writeToStream(dataOutput); } private static final float[] srgbChroma = { 0.31270F, 0.329F, 0.64F, 0.33F, 0.3F, 0.6F, 0.15F, 0.06F }; private void writeCHRM() throws IOException { if (param.isChromaticitySet() || param.isSRGBIntentSet()) { ChunkStream cs = new ChunkStream("cHRM"); float[] chroma; if (!param.isSRGBIntentSet()) { chroma = param.getChromaticity(); } else { chroma = srgbChroma; // SRGB chromaticities } for (int i = 0; i < 8; i++) { cs.writeInt((int)(chroma[i]*100000)); } cs.writeToStream(dataOutput); } } private void writeGAMA() throws IOException { if (param.isGammaSet() || param.isSRGBIntentSet()) { ChunkStream cs = new ChunkStream("gAMA"); float gamma; if (!param.isSRGBIntentSet()) { gamma = param.getGamma(); } else { gamma = 1.0F/2.2F; // SRGB gamma } cs.writeInt((int)(gamma*100000)); cs.writeToStream(dataOutput); } } private void writeICCP() throws IOException { if (param.isICCProfileDataSet()) { ChunkStream cs = new ChunkStream("iCCP"); String name = param.getICCProfileName(); if (name == null || name.length() < 1) { name = "JAI-Placed Profile"; } else { name = name.trim(); if (name.length() > 79) { name = name.substring(0, 79); } } // PNG actually only allows printable Latin 1 // characters (33-126 and 161-255) and spaces (32), //but no leading, trailing, or consecutive spaces. byte[] ICCProfileName = name.getBytes("ISO-8859-1"); int length = filterPrintableLatin1(ICCProfileName); // load the actual profile as bytes byte[] ICCProfileData = param.getICCProfileData(); ByteArrayOutputStream iccDflStream = new ByteArrayOutputStream(ICCProfileData.length); // compress (deflate) the profile DeflaterOutputStream dfl = new DeflaterOutputStream(iccDflStream); dfl.write(ICCProfileData); dfl.finish(); // write name cs.write(ICCProfileName, 0, length); // write null delimiter cs.writeByte(0); // write compression type (always 0) cs.writeByte(0); // write ICC data cs.write(iccDflStream.toByteArray()); dfl.close(); cs.writeToStream(dataOutput); } } private void writeSBIT() throws IOException { if (param.isSignificantBitsSet()) { ChunkStream cs = new ChunkStream("sBIT"); int[] significantBits = param.getSignificantBits(); int len = significantBits.length; for (int i = 0; i < len; i++) { cs.writeByte(significantBits[i]); } cs.writeToStream(dataOutput); } } private void writeSRGB() throws IOException { if (param.isSRGBIntentSet()) { ChunkStream cs = new ChunkStream("sRGB"); int intent = param.getSRGBIntent(); cs.write(intent); cs.writeToStream(dataOutput); } } private void writePLTE() throws IOException { if (redPalette == null) { return; } ChunkStream cs = new ChunkStream("PLTE"); for (int i = 0; i < redPalette.length; i++) { cs.writeByte(redPalette[i]); cs.writeByte(greenPalette[i]); cs.writeByte(bluePalette[i]); } cs.writeToStream(dataOutput); } private void writeBKGD() throws IOException { if (param.isBackgroundSet()) { ChunkStream cs = new ChunkStream("bKGD"); switch (colorType) { case PNG_COLOR_GRAY: case PNG_COLOR_GRAY_ALPHA: int gray = ((PNGEncodeParam.Gray)param).getBackgroundGray(); cs.writeShort(gray); break; case PNG_COLOR_PALETTE: int index = ((PNGEncodeParam.Palette)param).getBackgroundPaletteIndex(); cs.writeByte(index); break; case PNG_COLOR_RGB: case PNG_COLOR_RGB_ALPHA: int[] rgb = ((PNGEncodeParam.RGB)param).getBackgroundRGB(); cs.writeShort(rgb[0]); cs.writeShort(rgb[1]); cs.writeShort(rgb[2]); break; } cs.writeToStream(dataOutput); } } private void writeHIST() throws IOException { if (param.isPaletteHistogramSet()) { ChunkStream cs = new ChunkStream("hIST"); int[] hist = param.getPaletteHistogram(); for (int i = 0; i < hist.length; i++) { cs.writeShort(hist[i]); } cs.writeToStream(dataOutput); } } private void writeTRNS() throws IOException { if (param.isTransparencySet() && (colorType != PNG_COLOR_GRAY_ALPHA) && (colorType != PNG_COLOR_RGB_ALPHA)) { ChunkStream cs = new ChunkStream("tRNS"); if (param instanceof PNGEncodeParam.Palette) { byte[] t = ((PNGEncodeParam.Palette)param).getPaletteTransparency(); for (int i = 0; i < t.length; i++) { cs.writeByte(t[i]); } } else if (param instanceof PNGEncodeParam.Gray) { int t = ((PNGEncodeParam.Gray)param).getTransparentGray(); cs.writeShort(t); } else if (param instanceof PNGEncodeParam.RGB) { int[] t = ((PNGEncodeParam.RGB)param).getTransparentRGB(); cs.writeShort(t[0]); cs.writeShort(t[1]); cs.writeShort(t[2]); } cs.writeToStream(dataOutput); } else if (colorType == PNG_COLOR_PALETTE) { int lastEntry = Math.min(255, alphaPalette.length - 1); int nonOpaque; for (nonOpaque = lastEntry; nonOpaque >= 0; nonOpaque--) { if (alphaPalette[nonOpaque] != (byte)255) { break; } } if (nonOpaque >= 0) { ChunkStream cs = new ChunkStream("tRNS"); for (int i = 0; i <= nonOpaque; i++) { cs.writeByte(alphaPalette[i]); } cs.writeToStream(dataOutput); } } } private void writePHYS() throws IOException { if (param.isPhysicalDimensionSet()) { ChunkStream cs = new ChunkStream("pHYs"); int[] dims = param.getPhysicalDimension(); cs.writeInt(dims[0]); cs.writeInt(dims[1]); cs.writeByte((byte)dims[2]); cs.writeToStream(dataOutput); } } private void writeSPLT() throws IOException { if (param.isSuggestedPaletteSet()) { ChunkStream cs = new ChunkStream("sPLT"); System.out.println("sPLT not supported yet."); cs.writeToStream(dataOutput); } } private void writeTIME() throws IOException { if (param.isModificationTimeSet()) { ChunkStream cs = new ChunkStream("tIME"); Date date = param.getModificationTime(); TimeZone gmt = TimeZone.getTimeZone("GMT"); GregorianCalendar cal = new GregorianCalendar(gmt); cal.setTime(date); int year = cal.get(Calendar.YEAR); int month = cal.get(Calendar.MONTH); int day = cal.get(Calendar.DAY_OF_MONTH); int hour = cal.get(Calendar.HOUR_OF_DAY); int minute = cal.get(Calendar.MINUTE); int second = cal.get(Calendar.SECOND); cs.writeShort(year); cs.writeByte(month + 1); cs.writeByte(day); cs.writeByte(hour); cs.writeByte(minute); cs.writeByte(second); cs.writeToStream(dataOutput); } } private void writeTEXT() throws IOException { if (param.isTextSet()) { String[] text = param.getText(); for (int i = 0; i < text.length/2; i++) { text[i << 1] = text[i <<1].trim(); byte[] keyword = text[2*i].getBytes("ISO-8859-1"); int len = filterPrintableLatin1(keyword); ChunkStream cs = new ChunkStream("tEXt"); cs.write(keyword, 0, Math.min(len, 79)); text[(i << 1) + 1] = text[(i << 1) + 1].trim(); byte[] value = text[(i << 1) + 1].getBytes(); len = filterPrintableLatin1(value); cs.write(0); cs.write(value, 0, len); cs.writeToStream(dataOutput); } } } private void writeZTXT() throws IOException { if (param.isCompressedTextSet()) { String[] text = param.getCompressedText(); for (int i = 0; i < text.length/2; i++) { text[i << 1] = text[i <<1].trim(); byte[] keyword = text[2*i].getBytes(); int len = filterPrintableLatin1(keyword); ChunkStream cs = new ChunkStream("zTXt"); cs.write(keyword, 0, Math.min(len, 79)); text[(i << 1) + 1] = text[(i << 1) + 1].trim(); byte[] value = text[2*i + 1].getBytes(); len = filterPrintableLatin1(value); cs.write(0); cs.write(0); DeflaterOutputStream dos = new DeflaterOutputStream(cs); dos.write(value, 0, len); dos.finish(); cs.writeToStream(dataOutput); } } } private void writePrivateChunks() throws IOException { int numChunks = param.getNumPrivateChunks(); for (int i = 0; i < numChunks; i++) { String type = param.getPrivateChunkType(i); char char3 = type.charAt(3); byte[] data = param.getPrivateChunkData(i); ChunkStream cs = new ChunkStream(type); cs.write(data); cs.writeToStream(dataOutput); } } /** * Analyzes a set of palettes and determines if it can be expressed * as a standard set of gray values, with zero or one values being * fully transparent and the rest being fully opaque. If it * is possible to express the data thusly, the method returns * a suitable instance of PNGEncodeParam.Gray; otherwise it * returns null. */ private PNGEncodeParam.Gray createGrayParam(byte[] redPalette, byte[] greenPalette, byte[] bluePalette, byte[] alphaPalette) { PNGEncodeParam.Gray param = new PNGEncodeParam.Gray(); int numTransparent = 0; int grayFactor = 255/((1 << bitDepth) - 1); int entries = 1 << bitDepth; for (int i = 0; i < entries; i++) { byte red = redPalette[i]; if ((red != i*grayFactor) || (red != greenPalette[i]) || (red != bluePalette[i])) { return null; } // All alphas must be 255 except at most 1 can be 0 byte alpha = alphaPalette[i]; if (alpha == (byte)0) { param.setTransparentGray(i); ++numTransparent; if (numTransparent > 1) { return null; } } else if (alpha != (byte)255) { return null; } } return param; } public void encode(RenderedImage im) throws IOException { this.image = im; this.width = image.getWidth(); this.height = image.getHeight(); SampleModel sampleModel = image.getSampleModel(); int[] sampleSize = sampleModel.getSampleSize(); // Set bitDepth to a sentinel value this.bitDepth = -1; this.bitShift = 0; // Allow user to override the bit depth of gray images if (param instanceof PNGEncodeParam.Gray) { PNGEncodeParam.Gray paramg = (PNGEncodeParam.Gray)param; if (paramg.isBitDepthSet()) { this.bitDepth = paramg.getBitDepth(); } if (paramg.isBitShiftSet()) { this.bitShift = paramg.getBitShift(); } } // Get bit depth from image if not set in param if (this.bitDepth == -1) { // Get bit depth from channel 0 of the image this.bitDepth = sampleSize[0]; // Ensure all channels have the same bit depth for (int i = 1; i < sampleSize.length; i++) { if (sampleSize[i] != bitDepth) { throw new RuntimeException(); } } // Round bit depth up to a power of 2 if (bitDepth > 2 && bitDepth < 4) { bitDepth = 4; } else if (bitDepth > 4 && bitDepth < 8) { bitDepth = 8; } else if (bitDepth > 8 && bitDepth < 16) { bitDepth = 16; } else if (bitDepth > 16) { throw new RuntimeException(); } } this.numBands = sampleModel.getNumBands(); this.bpp = numBands*((bitDepth == 16) ? 2 : 1); ColorModel colorModel = image.getColorModel(); if (colorModel instanceof IndexColorModel) { if (bitDepth < 1 || bitDepth > 8) { throw new RuntimeException(); } if (sampleModel.getNumBands() != 1) { throw new RuntimeException(); } IndexColorModel icm = (IndexColorModel)colorModel; int size = icm.getMapSize(); redPalette = new byte[size]; greenPalette = new byte[size]; bluePalette = new byte[size]; alphaPalette = new byte[size]; icm.getReds(redPalette); icm.getGreens(greenPalette); icm.getBlues(bluePalette); icm.getAlphas(alphaPalette); this.bpp = 1; if (param == null) { param = createGrayParam(redPalette, greenPalette, bluePalette, alphaPalette); } // If param is still null, it can't be expressed as gray if (param == null) { param = new PNGEncodeParam.Palette(); } if (param instanceof PNGEncodeParam.Palette) { // If palette not set in param, create one from the ColorModel. PNGEncodeParam.Palette parami = (PNGEncodeParam.Palette)param; if (parami.isPaletteSet()) { int[] palette = parami.getPalette(); size = palette.length/3; int index = 0; for (int i = 0; i < size; i++) { redPalette[i] = (byte)palette[index++]; greenPalette[i] = (byte)palette[index++]; bluePalette[i] = (byte)palette[index++]; alphaPalette[i] = (byte)255; } } this.colorType = PNG_COLOR_PALETTE; } else if (param instanceof PNGEncodeParam.Gray) { redPalette = greenPalette = bluePalette = alphaPalette = null; this.colorType = PNG_COLOR_GRAY; } else { throw new RuntimeException(); } } else if (numBands == 1) { if (param == null) { param = new PNGEncodeParam.Gray(); } this.colorType = PNG_COLOR_GRAY; } else if (numBands == 2) { if (param == null) { param = new PNGEncodeParam.Gray(); } if (param.isTransparencySet()) { skipAlpha = true; numBands = 1; if ((sampleSize[0] == 8) && (bitDepth < 8)) { compressGray = true; } bpp = (bitDepth == 16) ? 2 : 1; this.colorType = PNG_COLOR_GRAY; } else { if (this.bitDepth < 8) { this.bitDepth = 8; } this.colorType = PNG_COLOR_GRAY_ALPHA; } } else if (numBands == 3) { if (param == null) { param = new PNGEncodeParam.RGB(); } this.colorType = PNG_COLOR_RGB; } else if (numBands == 4) { if (param == null) { param = new PNGEncodeParam.RGB(); } if (param.isTransparencySet()) { skipAlpha = true; numBands = 3; bpp = (bitDepth == 16) ? 6 : 3; this.colorType = PNG_COLOR_RGB; } else { this.colorType = PNG_COLOR_RGB_ALPHA; } } interlace = param.getInterlacing(); writeMagic(); writeIHDR(); writeCHRM(); writeGAMA(); writeICCP(); writeSBIT(); writeSRGB(); writePLTE(); writeHIST(); writeTRNS(); writeBKGD(); writePHYS(); writeSPLT(); writeTIME(); writeTEXT(); writeZTXT(); writePrivateChunks(); writeIDAT(); writeIEND(); dataOutput.flush(); //Fix: 4636212. This sentence closes the provided stream. // Thus, the call for flush() in EncodeRIF will fail on // SeekableOutputStream. Also, the flush method in // SeekableOutputStream is improved. //dataOutput.close(); } // public static void main(String[] args) { // try { // SeekableStream stream = new FileSeekableStream(args[0]); // String[] names = ImageCodec.getDecoderNames(stream); // ImageDecoder dec = // ImageCodec.createImageDecoder(names[0], stream, null); // RenderedImage im = dec.decodeAsRenderedImage(); // OutputStream output = new FileOutputStream(args[1]); // PNGEncodeParam param = null; // Object o = im.getProperty("encode_param"); // if ((o != null) && (o instanceof PNGEncodeParam)) { // param = (PNGEncodeParam)o; // } else { // param = PNGEncodeParam.getDefaultEncodeParam(im); // } // if (param instanceof PNGEncodeParam.RGB) { // int[] rgb = { 50, 100, 150 }; // ((PNGEncodeParam.RGB)param).setBackgroundRGB(rgb); // } // param.setChromaticity(0.32270F, 0.319F, // 0.65F, 0.32F, // 0.31F, 0.58F, // 0.16F, 0.04F); // param.setGamma(3.5F); // int[] sbits = { 8, 8, 8 }; // param.setSignificantBits(sbits); // param.setSRGBIntent(0); // String[] text = new String[4]; // text[0] = "Title"; // text[1] = "PNG Test Image"; // text[2] = "Author"; // text[3] = "Daniel Rice"; // param.setText(text); // String[] ztext = new String[2]; // ztext[0] = "Description"; // ztext[1] = "A really incredibly long-winded description of extremely little if any substance whatsoever."; // param.setCompressedText(ztext); // ImageEncoder enc = new PNGImageEncoder(output, param); // enc.encode(im); // } catch (Exception e) { // e.printStackTrace(); // System.exit(1); // } // } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/WBMPCodec.java0000644000175000017500000002304210350070606026116 0ustar mathieumathieu/* * $RCSfile: WBMPCodec.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-12-14 19:24:54 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl; import java.awt.Point; import java.awt.image.BufferedImage; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.IndexColorModel; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.io.BufferedInputStream; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import com.sun.media.jai.codec.ForwardSeekableStream; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.ImageDecoder; import com.sun.media.jai.codec.ImageDecoderImpl; import com.sun.media.jai.codec.ImageDecodeParam; import com.sun.media.jai.codec.ImageEncoder; import com.sun.media.jai.codec.ImageEncoderImpl; import com.sun.media.jai.codec.ImageEncodeParam; import com.sun.media.jai.codec.SeekableStream; /** * A subclass of ImageCodec that handles the WBMP format. */ public final class WBMPCodec extends ImageCodec { public WBMPCodec() {} public String getFormatName() { return "wbmp"; } public Class getEncodeParamClass() { return Object.class; } public Class getDecodeParamClass() { return Object.class; } public boolean canEncodeImage(RenderedImage im, ImageEncodeParam param) { SampleModel sampleModel = im.getSampleModel(); int dataType = sampleModel.getTransferType(); if (dataType == DataBuffer.TYPE_FLOAT || dataType == DataBuffer.TYPE_DOUBLE || sampleModel.getNumBands() != 1 || sampleModel.getSampleSize(0) != 1) { return false; } return true; } protected ImageEncoder createImageEncoder(OutputStream dst, ImageEncodeParam param) { return new WBMPImageEncoder(dst, null); } protected ImageDecoder createImageDecoder(InputStream src, ImageDecodeParam param) { // Add buffering for efficiency if (!(src instanceof BufferedInputStream)) { src = new BufferedInputStream(src); } return new WBMPImageDecoder(new ForwardSeekableStream(src), null); } protected ImageDecoder createImageDecoder(SeekableStream src, ImageDecodeParam param) { return new WBMPImageDecoder(src, null); } public int getNumHeaderBytes() { return 3; } public boolean isFormatRecognized(byte[] header) { // WBMP has no magic bytes at the beginning so simply check // the first three bytes for known constraints. return ((header[0] == (byte)0) && // TypeField == 0 header[1] == 0 && // FixHeaderField == 0xxx00000; not support ext header ((header[2] & 0x8f) != 0 || (header[2] & 0x7f) != 0)); // First width byte //XXX: header[2] & 0x8f) != 0 for the bug in Sony Ericsson encoder. } } final class WBMPImageEncoder extends ImageEncoderImpl { // Get the number of bits required to represent an int. private static int getNumBits(int intValue) { int numBits = 32; int mask = 0x80000000; while(mask != 0 && (intValue & mask) == 0) { numBits--; mask >>>= 1; } return numBits; } // Convert an int value to WBMP multi-byte format. private static byte[] intToMultiByte(int intValue) { int numBitsLeft = getNumBits(intValue); byte[] multiBytes = new byte[(numBitsLeft + 6)/7]; int maxIndex = multiBytes.length - 1; for(int b = 0; b <= maxIndex; b++) { multiBytes[b] = (byte)((intValue >>> ((maxIndex - b)*7))&0x7f); if(b != maxIndex) { multiBytes[b] |= (byte)0x80; } } return multiBytes; } public WBMPImageEncoder(OutputStream output, ImageEncodeParam param) { super(output, param); } public void encode(RenderedImage im) throws IOException { // Get the SampleModel. SampleModel sm = im.getSampleModel(); // Check the data type, band count, and sample size. int dataType = sm.getTransferType(); if (dataType == DataBuffer.TYPE_FLOAT || dataType == DataBuffer.TYPE_DOUBLE) { throw new IllegalArgumentException(JaiI18N.getString("WBMPImageEncoder0")); } else if (sm.getNumBands() != 1) { throw new IllegalArgumentException(JaiI18N.getString("WBMPImageEncoder1")); } else if (sm.getSampleSize(0) != 1) { throw new IllegalArgumentException(JaiI18N.getString("WBMPImageEncoder2")); } // Save image dimensions. int width = im.getWidth(); int height = im.getHeight(); // Write WBMP header. output.write(0); // TypeField output.write(0); // FixHeaderField output.write(intToMultiByte(width)); // width output.write(intToMultiByte(height)); // height Raster tile = null; // If the data are not formatted nominally then reformat. if(sm.getDataType() != DataBuffer.TYPE_BYTE || !(sm instanceof MultiPixelPackedSampleModel) || ((MultiPixelPackedSampleModel)sm).getDataBitOffset() != 0) { MultiPixelPackedSampleModel mppsm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, width, height, 1, (width + 7)/8, 0); WritableRaster raster = Raster.createWritableRaster(mppsm, new Point(im.getMinX(), im.getMinY())); raster.setRect(im.getData()); tile = raster; } else if(im.getNumXTiles() == 1 && im.getNumYTiles() == 1) { tile = im.getTile(im.getMinTileX(), im.getMinTileY()); } else { tile = im.getData(); } // Check whether the image is white-is-zero. boolean isWhiteZero = false; if(im.getColorModel() instanceof IndexColorModel) { IndexColorModel icm = (IndexColorModel)im.getColorModel(); isWhiteZero = (icm.getRed(0) + icm.getGreen(0) + icm.getBlue(0)) > (icm.getRed(1) + icm.getGreen(1) + icm.getBlue(1)); } // Get the line stride, bytes per row, and data array. int lineStride = ((MultiPixelPackedSampleModel)sm).getScanlineStride(); int bytesPerRow = (width + 7)/8; byte[] bdata = ((DataBufferByte)tile.getDataBuffer()).getData(); // Write the data. if(!isWhiteZero && lineStride == bytesPerRow) { // Write the entire image. output.write(bdata, 0, height*bytesPerRow); } else { // Write the image row-by-row. int offset = 0; if(!isWhiteZero) { // Black-is-zero for(int row = 0; row < height; row++) { output.write(bdata, offset, bytesPerRow); offset += lineStride; } } else { // White-is-zero: need to invert data. byte[] inverted = new byte[bytesPerRow]; for(int row = 0; row < height; row++) { for(int col = 0; col < bytesPerRow; col++) { inverted[col] = (byte)(~(bdata[col+offset])); } output.write(inverted, 0, bytesPerRow); offset += lineStride; } } } } } final class WBMPImageDecoder extends ImageDecoderImpl { public WBMPImageDecoder(SeekableStream input, ImageDecodeParam param) { super(input, param); } public RenderedImage decodeAsRenderedImage(int page) throws IOException { if (page != 0) { throw new IOException(JaiI18N.getString(JaiI18N.getString("WBMPImageDecoder0"))); } input.read(); // TypeField input.read(); // FixHeaderField // Image width int value = input.read(); int width = value & 0x7f; while((value & 0x80) == 0x80) { width <<= 7; value = input.read(); width |= (value & 0x7f); } // Image height value = input.read(); int height = value & 0x7f; while((value & 0x80) == 0x80) { height <<= 7; value = input.read(); height |= (value & 0x7f); } // Create byte-packed bilevel image width an IndexColorModel BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY); // Get the image tile. WritableRaster tile = bi.getWritableTile(0, 0); // Get the SampleModel. MultiPixelPackedSampleModel sm = (MultiPixelPackedSampleModel)bi.getSampleModel(); // Read the data. input.readFully(((DataBufferByte)tile.getDataBuffer()).getData(), 0, height*sm.getScanlineStride()); return bi; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/SimpleRenderedImage.java0000644000175000017500000004717710203035544030276 0ustar mathieumathieu/* * $RCSfile: SimpleRenderedImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:38 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.awt.image.RenderedImage; import java.awt.image.ColorModel; import java.awt.image.SampleModel; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import java.util.Vector; import com.sun.media.jai.codecimpl.util.RasterFactory; /** * A simple class implemented the RenderedImage * interface. Only the getTile() method needs to be * implemented by subclasses. The instance variables must also be * filled in properly. * *

    Normally in JAI PlanarImage is used for this * purpose, but in the interest of making * com.sun.media.jai.codec and * com.sun.media.jai.codecimpl be as modular as possible the * use of PlanarImage has been avoided. */ public abstract class SimpleRenderedImage implements RenderedImage { /** The X coordinate of the image's upper-left pixel. */ protected int minX; /** The Y coordinate of the image's upper-left pixel. */ protected int minY; /** The image's width in pixels. */ protected int width; /** The image's height in pixels. */ protected int height; /** The width of a tile. */ protected int tileWidth; /** The height of a tile. */ protected int tileHeight; /** The X coordinate of the upper-left pixel of tile (0, 0). */ protected int tileGridXOffset = 0; /** The Y coordinate of the upper-left pixel of tile (0, 0). */ protected int tileGridYOffset = 0; /** The image's SampleModel. */ protected SampleModel sampleModel = null; /** The image's ColorModel. */ protected ColorModel colorModel = null; /** The image's sources, stored in a Vector. */ protected Vector sources = new Vector(); /** A Hashtable containing the image properties. */ protected Hashtable properties = new Hashtable(); public SimpleRenderedImage() {} /** Returns the X coordinate of the leftmost column of the image. */ public int getMinX() { return minX; } /** * Returns the X coordinate of the column immediatetely to the * right of the rightmost column of the image. getMaxX() is * implemented in terms of getMinX() and getWidth() and so does * not need to be implemented by subclasses. */ public final int getMaxX() { return getMinX() + getWidth(); } /** Returns the X coordinate of the uppermost row of the image. */ public int getMinY() { return minY; } /** * Returns the Y coordinate of the row immediately below the * bottom row of the image. getMaxY() is implemented in terms of * getMinY() and getHeight() and so does not need to be * implemented by subclasses. */ public final int getMaxY() { return getMinY() + getHeight(); } /** Returns the width of the image. */ public int getWidth() { return width; } /** Returns the height of the image. */ public int getHeight() { return height; } /** Returns a Rectangle indicating the image bounds. */ public Rectangle getBounds() { return new Rectangle(getMinX(), getMinY(), getWidth(), getHeight()); } /** Returns the width of a tile. */ public int getTileWidth() { return tileWidth; } /** Returns the height of a tile. */ public int getTileHeight() { return tileHeight; } /** * Returns the X coordinate of the upper-left pixel of tile (0, 0). */ public int getTileGridXOffset() { return tileGridXOffset; } /** * Returns the Y coordinate of the upper-left pixel of tile (0, 0). */ public int getTileGridYOffset() { return tileGridYOffset; } /** * Returns the horizontal index of the leftmost column of tiles. * getMinTileX() is implemented in terms of getMinX() * and so does not need to be implemented by subclasses. */ public int getMinTileX() { return XToTileX(getMinX()); } /** * Returns the horizontal index of the rightmost column of tiles. * getMaxTileX() is implemented in terms of getMaxX() * and so does not need to be implemented by subclasses. */ public int getMaxTileX() { return XToTileX(getMaxX() - 1); } /** * Returns the number of tiles along the tile grid in the * horizontal direction. getNumXTiles() is implemented in terms * of getMinTileX() and getMaxTileX() and so does not need to be * implemented by subclasses. */ public int getNumXTiles() { return getMaxTileX() - getMinTileX() + 1; } /** * Returns the vertical index of the uppermost row of tiles. getMinTileY() * is implemented in terms of getMinY() and so does not need to be * implemented by subclasses. */ public int getMinTileY() { return YToTileY(getMinY()); } /** * Returns the vertical index of the bottom row of tiles. getMaxTileY() * is implemented in terms of getMaxY() and so does not need to * be implemented by subclasses. */ public int getMaxTileY() { return YToTileY(getMaxY() - 1); } /** * Returns the number of tiles along the tile grid in the vertical * direction. getNumYTiles() is implemented in terms * of getMinTileY() and getMaxTileY() and so does not need to be * implemented by subclasses. */ public int getNumYTiles() { return getMaxTileY() - getMinTileY() + 1; } /** Returns the SampleModel of the image. */ public SampleModel getSampleModel() { return sampleModel; } /** Returns the ColorModel of the image. */ public ColorModel getColorModel() { return colorModel; } /** * 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) { name = name.toLowerCase(); Object value = properties.get(name); return value != null ? value : java.awt.Image.UndefinedProperty; } /** * Returns a list of the properties recognized by this image. If * no properties are available, null will be * returned. * * @return an array of Strings representing valid * property names. */ public String[] getPropertyNames() { String[] names = null; if(properties.size() > 0) { names = new String[properties.size()]; int index = 0; Enumeration e = properties.keys(); while (e.hasMoreElements()) { String name = (String)e.nextElement(); names[index++] = name; } } return names; } /** * Returns an array of Strings recognized as names by * this property source that begin with the supplied prefix. If * no property names match, null will be returned. * The comparison is done in a case-independent manner. * *

    The default implementation calls * getPropertyNames() and searches the list of names * for matches. * * @return an array of Strings giving the valid * property names. */ public String[] getPropertyNames(String prefix) { String propertyNames[] = getPropertyNames(); if (propertyNames == null) { return null; } prefix = prefix.toLowerCase(); Vector names = new Vector(); for (int i = 0; i < propertyNames.length; i++) { if (propertyNames[i].startsWith(prefix)) { names.addElement(propertyNames[i]); } } if (names.size() == 0) { return null; } // Copy the strings from the Vector over to a String array. String prefixNames[] = new String[names.size()]; int count = 0; for (Iterator it = names.iterator(); it.hasNext(); ) { prefixNames[count++] = (String)it.next(); } return prefixNames; } // Utility methods. /** * Converts a pixel's X coordinate into a horizontal tile index * relative to a given tile grid layout specified by its X offset * and tile width. */ public static int XToTileX(int x, int tileGridXOffset, int tileWidth) { x -= tileGridXOffset; if (x < 0) { x += 1 - tileWidth; // Force round to -infinity } return x/tileWidth; } /** * Converts a pixel's Y coordinate into a vertical tile index * relative to a given tile grid layout specified by its Y offset * and tile height. */ public static int YToTileY(int y, int tileGridYOffset, int tileHeight) { y -= tileGridYOffset; if (y < 0) { y += 1 - tileHeight; // Force round to -infinity } return y/tileHeight; } /** * Converts a pixel's X coordinate into a horizontal tile index. * This is a convenience method. No attempt is made to detect * out-of-range coordinates. * * @param x the X coordinate of a pixel. * @return the X index of the tile containing the pixel. */ public int XToTileX(int x) { return XToTileX(x, getTileGridXOffset(), getTileWidth()); } /** * Converts a pixel's Y coordinate into a vertical tile index. * This is a convenience method. No attempt is made to detect * out-of-range coordinates. * * @param y the Y coordinate of a pixel. * @return the Y index of the tile containing the pixel. */ public int YToTileY(int y) { return YToTileY(y, getTileGridYOffset(), getTileHeight()); } /** * Converts a horizontal tile index into the X coordinate of its * upper left pixel relative to a given tile grid layout specified * by its X offset and tile width. */ public static int tileXToX(int tx, int tileGridXOffset, int tileWidth) { return tx*tileWidth + tileGridXOffset; } /** * Converts a vertical tile index into the Y coordinate of * its upper left pixel relative to a given tile grid layout * specified by its Y offset and tile height. */ public static int tileYToY(int ty, int tileGridYOffset, int tileHeight) { return ty*tileHeight + tileGridYOffset; } /** * Converts a horizontal tile index into the X coordinate of its * upper left pixel. This is a convenience method. No attempt is made * to detect out-of-range indices. * * @param tx the horizontal index of a tile. * @return the X coordinate of the tile's upper left pixel. */ public int tileXToX(int tx) { return tx*tileWidth + tileGridXOffset; } /** * Converts a vertical tile index into the Y coordinate of its * upper left pixel. This is a convenience method. No attempt is made * to detect out-of-range indices. * * @param ty the vertical index of a tile. * @return the Y coordinate of the tile's upper left pixel. */ public int tileYToY(int ty) { return ty*tileHeight + tileGridYOffset; } public Vector getSources() { return null; } /** * Returns the entire image in a single Raster. For images with * multiple tiles this will require making a copy. * *

    The returned Raster is semantically a copy. This means * that updates to the source image will not be reflected in the * returned Raster. For non-writable (immutable) source images, * the returned value may be a reference to the image's internal * data. The returned Raster should be considered non-writable; * any attempt to alter its pixel data (such as by casting it to * WritableRaster or obtaining and modifying its DataBuffer) may * result in undefined behavior. The copyData method should be * used if the returned Raster is to be modified. * * @return a Raster containing a copy of this image's data. */ public Raster getData() { Rectangle rect = new Rectangle(getMinX(), getMinY(), getWidth(), getHeight()); return getData(rect); } /** * Returns an arbitrary rectangular region of the RenderedImage * in a Raster. The rectangle of interest will be clipped against * the image bounds. * *

    The returned Raster is semantically a copy. This means * that updates to the source image will not be reflected in the * returned Raster. For non-writable (immutable) source images, * the returned value may be a reference to the image's internal * data. The returned Raster should be considered non-writable; * any attempt to alter its pixel data (such as by casting it to * WritableRaster or obtaining and modifying its DataBuffer) may * result in undefined behavior. The copyData method should be * used if the returned Raster is to be modified. * * @param bounds the region of the RenderedImage to be returned. */ public Raster getData(Rectangle bounds) { // Get the image bounds. Rectangle imageBounds = getBounds(); // Check for parameter validity. if(bounds == null) { bounds = imageBounds; } else if(!bounds.intersects(imageBounds)) { throw new IllegalArgumentException(JaiI18N.getString("SimpleRenderedImage0")); } // Determine tile limits for the prescribed bounds. int startX = XToTileX(bounds.x); int startY = YToTileY(bounds.y); int endX = XToTileX(bounds.x + bounds.width - 1); int endY = YToTileY(bounds.y + bounds.height - 1); // If the bounds are contained in a single tile, return a child // of that tile's Raster. if ((startX == endX) && (startY == endY)) { Raster tile = getTile(startX, startY); return tile.createChild(bounds.x, bounds.y, bounds.width, bounds.height, bounds.x, bounds.y, null); } else { // Recalculate the tile limits if the data bounds are not a // subset of the image bounds. if(!imageBounds.contains(bounds)) { Rectangle xsect = bounds.intersection(imageBounds); startX = XToTileX(xsect.x); startY = YToTileY(xsect.y); endX = XToTileX(xsect.x + xsect.width - 1); endY = YToTileY(xsect.y + xsect.height - 1); } // Create a WritableRaster of the desired size SampleModel sm = sampleModel.createCompatibleSampleModel(bounds.width, bounds.height); // Translate it WritableRaster dest = RasterFactory.createWritableRaster(sm, bounds.getLocation()); // Loop over the tiles in the intersection. for (int j = startY; j <= endY; j++) { for (int i = startX; i <= endX; i++) { // Retrieve the tile. Raster tile = getTile(i, j); // Create a child of the tile for the intersection of // the tile bounds and the bounds of the requested area. Rectangle tileRect = tile.getBounds(); Rectangle intersectRect = bounds.intersection(tile.getBounds()); Raster liveRaster = tile.createChild(intersectRect.x, intersectRect.y, intersectRect.width, intersectRect.height, intersectRect.x, intersectRect.y, null); // Copy the data from the child. dest.setRect(liveRaster); } } return dest; } } /** * Copies an arbitrary rectangular region of the RenderedImage * into a caller-supplied WritableRaster. The region to be * computed is determined by clipping the bounds of the supplied * WritableRaster against the bounds of the image. The supplied * WritableRaster must have a SampleModel that is compatible with * that of the image. * *

    If the raster argument is null, the entire image will * be copied into a newly-created WritableRaster with a SampleModel * that is compatible with that of the image. * * @param dest a WritableRaster to hold the returned portion of * the image. * @return a reference to the supplied WritableRaster, or to a * new WritableRaster if the supplied one was null. */ public WritableRaster copyData(WritableRaster dest) { // Get the image bounds. Rectangle imageBounds = getBounds(); Rectangle bounds; if (dest == null) { // Create a WritableRaster for the entire image. bounds = imageBounds; Point p = new Point(minX, minY); SampleModel sm = sampleModel.createCompatibleSampleModel(width, height); dest = RasterFactory.createWritableRaster(sm, p); } else { bounds = dest.getBounds(); } // Determine tile limits for the intersection of the prescribed // bounds with the image bounds. Rectangle xsect = imageBounds.contains(bounds) ? bounds : bounds.intersection(imageBounds); int startX = XToTileX(xsect.x); int startY = YToTileY(xsect.y); int endX = XToTileX(xsect.x + xsect.width - 1); int endY = YToTileY(xsect.y + xsect.height - 1); // Loop over the tiles in the intersection. for (int j = startY; j <= endY; j++) { for (int i = startX; i <= endX; i++) { // Retrieve the tile. Raster tile = getTile(i, j); // Create a child of the tile for the intersection of // the tile bounds and the bounds of the requested area. Rectangle tileRect = tile.getBounds(); Rectangle intersectRect = bounds.intersection(tile.getBounds()); Raster liveRaster = tile.createChild(intersectRect.x, intersectRect.y, intersectRect.width, intersectRect.height, intersectRect.x, intersectRect.y, null); // Copy the data from the child. dest.setRect(liveRaster); } } return dest; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/JPEGImageDecoder.java0000644000175000017500000001322310472445724027405 0ustar mathieumathieu/* * $RCSfile: JPEGImageDecoder.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.3 $ * $Date: 2006-08-22 00:12:04 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl; import java.awt.Graphics2D; import java.awt.Point; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.awt.image.ComponentSampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.io.FilterInputStream; import java.io.InputStream; import java.io.IOException; import com.sun.media.jai.codec.ImageDecoderImpl; import com.sun.media.jai.codec.ImageDecodeParam; import com.sun.media.jai.codec.JPEGDecodeParam; import com.sun.media.jai.codecimpl.ImagingListenerProxy; import com.sun.media.jai.codecimpl.util.ImagingException; import com.sun.image.codec.jpeg.ImageFormatException; /** * @since EA2 */ public class JPEGImageDecoder extends ImageDecoderImpl { public JPEGImageDecoder(InputStream input, ImageDecodeParam param) { super(input, param); } public RenderedImage decodeAsRenderedImage(int page) throws IOException { if (page != 0) { throw new IOException(JaiI18N.getString("JPEGImageDecoder0")); } try { return new JPEGImage(input, param); } catch(Exception e) { throw CodecUtils.toIOException(e); } } } /** * FilterInputStream subclass which does not support mark/reset. * Used to work around a failure of com.sun.image.codec.jpeg.JPEGImageDecoder * in which decodeAsBufferedImage() blocks in reset() if a corrupted JPEG * image is encountered. */ class NoMarkStream extends FilterInputStream { NoMarkStream(InputStream in) { super(in); } /** * Disallow mark/reset. */ public boolean markSupported() { return false; } /** * Disallow close() from closing the stream passed in. */ public final void close() throws IOException { // Deliberately do nothing. } } class JPEGImage extends SimpleRenderedImage { /** * Mutex for the entire class to circumvent thread unsafety of * com.sun.image.codec.jpeg.JPEGImageDecoder implementation. */ private static final Object LOCK = new Object(); private Raster theTile = null; /** * Construct a JPEGmage. * * @param stream The JPEG InputStream. * @param param The decoding parameters. */ public JPEGImage(InputStream stream, ImageDecodeParam param) { // If the supplied InputStream supports mark/reset wrap it so // it does not. if(stream.markSupported()) { stream = new NoMarkStream(stream); } // Lock the entire class to work around lack of thread safety // in com.sun.image.codec.jpeg.JPEGImageDecoder implementation. BufferedImage image = null; synchronized(LOCK) { com.sun.image.codec.jpeg.JPEGImageDecoder decoder = com.sun.image.codec.jpeg.JPEGCodec.createJPEGDecoder(stream); try { // decodeAsBufferedImage performs default color conversions image = decoder.decodeAsBufferedImage(); } catch (ImageFormatException e) { String message = JaiI18N.getString("JPEGImageDecoder1"); sendExceptionToListener(message, (Exception)e); // throw new RuntimeException(JaiI18N.getString("JPEGImageDecoder1")); } catch (IOException e) { String message = JaiI18N.getString("JPEGImageDecoder1"); sendExceptionToListener(message, (Exception)e); // throw new RuntimeException(JaiI18N.getString("JPEGImageDecoder2")); } } minX = 0; minY = 0; tileWidth = width = image.getWidth(); tileHeight = height = image.getHeight(); // Force image to have a ComponentSampleModel if it does not have one // and the ImageDecodeParam is either null or is a JPEGDecodeParam // with 'decodeToCSM' set to 'true'. if ((param == null || (param instanceof JPEGDecodeParam && ((JPEGDecodeParam)param).getDecodeToCSM())) && !(image.getSampleModel() instanceof ComponentSampleModel)) { int type = -1; int numBands = image.getSampleModel().getNumBands(); if (numBands == 1) { type = BufferedImage.TYPE_BYTE_GRAY; } else if (numBands == 3) { type = BufferedImage.TYPE_3BYTE_BGR; } else if (numBands == 4) { type = BufferedImage.TYPE_4BYTE_ABGR; } else { throw new RuntimeException(JaiI18N.getString("JPEGImageDecoder3")); } BufferedImage bi = new BufferedImage(width, height, type); bi.getWritableTile(0, 0).setRect(image.getWritableTile(0, 0)); bi.releaseWritableTile(0, 0); image = bi; } sampleModel = image.getSampleModel(); colorModel = image.getColorModel(); theTile = image.getWritableTile(0, 0); } public synchronized Raster getTile(int tileX, int tileY) { if (tileX != 0 || tileY != 0) { throw new IllegalArgumentException(JaiI18N.getString("JPEGImageDecoder4")); } return theTile; } public void dispose() { theTile = null; } private void sendExceptionToListener(String message, Exception e) { ImagingListenerProxy.errorOccurred(message, new ImagingException(message, e), this, false); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/ImagingListenerProxy.java0000644000175000017500000000653110203035544030541 0ustar mathieumathieu/* * $RCSfile: ImagingListenerProxy.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:36 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import com.sun.media.jai.codecimpl.util.ImagingException; public class ImagingListenerProxy { public static synchronized boolean errorOccurred(String message, Throwable thrown, Object where, boolean isRetryable) throws RuntimeException { Method errorOccurred = null; Object listener = null; try { Class jaiClass = Class.forName("javax.media.jai.JAI"); if (jaiClass == null) return defaultImpl(message, thrown, where, isRetryable); Method jaiInstance = jaiClass.getMethod("getDefaultInstance", null); Method getListener = jaiClass.getMethod("getImagingListener", null); Object jai = jaiInstance.invoke(null, null); if (jai == null) return defaultImpl(message, thrown, where, isRetryable); listener = getListener.invoke(jai, null); Class listenerClass = listener.getClass(); errorOccurred = listenerClass.getMethod("errorOccurred", new Class[]{String.class, Throwable.class, Object.class, boolean.class}); } catch(Throwable e) { return defaultImpl(message, thrown, where, isRetryable); } try { Boolean result = (Boolean)errorOccurred.invoke(listener, new Object[] {message, thrown, where, new Boolean(isRetryable)}); return result.booleanValue(); } catch(InvocationTargetException e) { Throwable te = ((InvocationTargetException)e).getTargetException(); throw new ImagingException(te); } catch(Throwable e) { return defaultImpl(message, thrown, where, isRetryable); } } private static synchronized boolean defaultImpl(String message, Throwable thrown, Object where, boolean isRetryable) throws RuntimeException { // Silent the RuntimeException occuring in any OperationRegistry // and rethrown all the other RuntimeExceptions. if (thrown instanceof RuntimeException) throw (RuntimeException)thrown; System.err.println("Error: " + message); System.err.println("Occurs in: " + ((where instanceof Class) ? ((Class)where).getName() : where.getClass().getName())); thrown.printStackTrace(System.err); return false; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/BMPImageDecoder.java0000644000175000017500000010507610472445724027306 0ustar mathieumathieu/* * $RCSfile: BMPImageDecoder.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.4 $ * $Date: 2006-08-22 00:12:03 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl; import java.awt.Point; import java.awt.RenderingHints; import java.awt.Transparency; import java.awt.color.ColorSpace; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferInt; import java.awt.image.DataBufferUShort; import java.awt.image.DirectColorModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.IndexColorModel; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.SinglePixelPackedSampleModel; import java.io.IOException; import java.io.BufferedInputStream; import java.io.InputStream; import java.util.Hashtable; import java.util.Enumeration; import com.sun.media.jai.codec.BMPEncodeParam; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.ImageDecoder; import com.sun.media.jai.codec.ImageDecoderImpl; import com.sun.media.jai.codec.ImageDecodeParam; import com.sun.media.jai.codecimpl.ImagingListenerProxy; import com.sun.media.jai.codecimpl.util.ImagingException; import com.sun.media.jai.codecimpl.util.RasterFactory; /** * @since EA2 */ public class BMPImageDecoder extends ImageDecoderImpl { public BMPImageDecoder(InputStream input, ImageDecodeParam param) { super(input, param); } public RenderedImage decodeAsRenderedImage(int page) throws IOException { if (page != 0) { throw new IOException(JaiI18N.getString("BMPImageDecoder8")); } try { return new BMPImage(input); } catch(Exception e) { throw CodecUtils.toIOException(e); } } } class BMPImage extends SimpleRenderedImage { // BMP variables private BufferedInputStream inputStream; private long bitmapFileSize; private long bitmapOffset; private long compression; private long imageSize; private byte palette[]; private int imageType; private int numBands; private boolean isBottomUp; private int bitsPerPixel; private int redMask, greenMask, blueMask, alphaMask; // BMP Image types private static final int VERSION_2_1_BIT = 0; private static final int VERSION_2_4_BIT = 1; private static final int VERSION_2_8_BIT = 2; private static final int VERSION_2_24_BIT = 3; private static final int VERSION_3_1_BIT = 4; private static final int VERSION_3_4_BIT = 5; private static final int VERSION_3_8_BIT = 6; private static final int VERSION_3_24_BIT = 7; private static final int VERSION_3_NT_16_BIT = 8; private static final int VERSION_3_NT_32_BIT = 9; private static final int VERSION_4_1_BIT = 10; private static final int VERSION_4_4_BIT = 11; private static final int VERSION_4_8_BIT = 12; private static final int VERSION_4_16_BIT = 13; private static final int VERSION_4_24_BIT = 14; private static final int VERSION_4_32_BIT = 15; // Color space types private static final int LCS_CALIBRATED_RGB = 0; private static final int LCS_sRGB = 1; private static final int LCS_CMYK = 2; // Compression Types private static final int BI_RGB = 0; private static final int BI_RLE8 = 1; private static final int BI_RLE4 = 2; private static final int BI_BITFIELDS = 3; private WritableRaster theTile = null; /** * Constructor for BMPImage * * @param stream */ public BMPImage(InputStream stream) { if (stream instanceof BufferedInputStream) { inputStream = (BufferedInputStream)stream; } else { inputStream = new BufferedInputStream(stream); } try { inputStream.mark(Integer.MAX_VALUE); // Start File Header if (!(readUnsignedByte(inputStream) == 'B' && readUnsignedByte(inputStream) == 'M')) { throw new RuntimeException(JaiI18N.getString("BMPImageDecoder0")); } // Read file size bitmapFileSize = readDWord(inputStream); // Read the two reserved fields readWord(inputStream); readWord(inputStream); // Offset to the bitmap from the beginning bitmapOffset = readDWord(inputStream); // End File Header // Start BitmapCoreHeader long size = readDWord(inputStream); if (size == 12) { width = readWord(inputStream); height = readWord(inputStream); } else { width = readLong(inputStream); height = readLong(inputStream); } int planes = readWord(inputStream); bitsPerPixel = readWord(inputStream); properties.put("color_planes", new Integer(planes)); properties.put("bits_per_pixel", new Integer(bitsPerPixel)); // As BMP always has 3 rgb bands, except for Version 5, // which is bgra numBands = 3; if (size == 12) { // Windows 2.x and OS/2 1.x properties.put("bmp_version", "BMP v. 2.x"); // Classify the image type if (bitsPerPixel == 1) { imageType = VERSION_2_1_BIT; } else if (bitsPerPixel == 4) { imageType = VERSION_2_4_BIT; } else if (bitsPerPixel == 8) { imageType = VERSION_2_8_BIT; } else if (bitsPerPixel == 24) { imageType = VERSION_2_24_BIT; } // Read in the palette int numberOfEntries = (int)((bitmapOffset-14-size) / 3); int sizeOfPalette = numberOfEntries*3; palette = new byte[sizeOfPalette]; inputStream.read(palette, 0, sizeOfPalette); properties.put("palette", palette); } else { compression = readDWord(inputStream); imageSize = readDWord(inputStream); long xPelsPerMeter = readLong(inputStream); long yPelsPerMeter = readLong(inputStream); long colorsUsed = readDWord(inputStream); long colorsImportant = readDWord(inputStream); switch((int)compression) { case BI_RGB: properties.put("compression", "BI_RGB"); break; case BI_RLE8: properties.put("compression", "BI_RLE8"); break; case BI_RLE4: properties.put("compression", "BI_RLE4"); break; case BI_BITFIELDS: properties.put("compression", "BI_BITFIELDS"); break; } properties.put("x_pixels_per_meter", new Long(xPelsPerMeter)); properties.put("y_pixels_per_meter", new Long(yPelsPerMeter)); properties.put("colors_used", new Long(colorsUsed)); properties.put("colors_important", new Long(colorsImportant)); if (size == 40) { // Windows 3.x and Windows NT switch((int)compression) { case BI_RGB: // No compression case BI_RLE8: // 8-bit RLE compression case BI_RLE4: // 4-bit RLE compression // Read in the palette int numberOfEntries = (int)((bitmapOffset-14-size) / 4); int sizeOfPalette = numberOfEntries*4; palette = new byte[sizeOfPalette]; inputStream.read(palette, 0, sizeOfPalette); properties.put("palette", palette); if (bitsPerPixel == 1) { imageType = VERSION_3_1_BIT; } else if (bitsPerPixel == 4) { imageType = VERSION_3_4_BIT; } else if (bitsPerPixel == 8) { imageType = VERSION_3_8_BIT; } else if (bitsPerPixel == 24) { imageType = VERSION_3_24_BIT; } else if (bitsPerPixel == 16) { imageType = VERSION_3_NT_16_BIT; redMask = 0x7C00; greenMask = 0x3E0; blueMask = 0x1F; properties.put("red_mask", new Integer(redMask)); properties.put("green_mask", new Integer(greenMask)); properties.put("blue_mask", new Integer(blueMask)); } else if (bitsPerPixel == 32) { imageType = VERSION_3_NT_32_BIT; redMask = 0x00FF0000; greenMask = 0x0000FF00; blueMask = 0x000000FF; properties.put("red_mask", new Integer(redMask)); properties.put("green_mask", new Integer(greenMask)); properties.put("blue_mask", new Integer(blueMask)); } properties.put("bmp_version", "BMP v. 3.x"); break; case BI_BITFIELDS: if (bitsPerPixel == 16) { imageType = VERSION_3_NT_16_BIT; } else if (bitsPerPixel == 32) { imageType = VERSION_3_NT_32_BIT; } // BitsField encoding redMask = (int)readDWord(inputStream); greenMask = (int)readDWord(inputStream); blueMask = (int)readDWord(inputStream); properties.put("red_mask", new Integer(redMask)); properties.put("green_mask", new Integer(greenMask)); properties.put("blue_mask", new Integer(blueMask)); if (colorsUsed != 0) { // there is a palette sizeOfPalette = (int)colorsUsed*4; palette = new byte[sizeOfPalette]; inputStream.read(palette, 0, sizeOfPalette); properties.put("palette", palette); } properties.put("bmp_version", "BMP v. 3.x NT"); break; default: throw new RuntimeException(JaiI18N.getString("BMPImageDecoder1")); } } else if (size == 108) { // Windows 4.x BMP properties.put("bmp_version", "BMP v. 4.x"); // rgb masks, valid only if comp is BI_BITFIELDS redMask = (int)readDWord(inputStream); greenMask = (int)readDWord(inputStream); blueMask = (int)readDWord(inputStream); // Only supported for 32bpp BI_RGB argb alphaMask = (int)readDWord(inputStream); long csType = readDWord(inputStream); int redX = readLong(inputStream); int redY = readLong(inputStream); int redZ = readLong(inputStream); int greenX = readLong(inputStream); int greenY = readLong(inputStream); int greenZ = readLong(inputStream); int blueX = readLong(inputStream); int blueY = readLong(inputStream); int blueZ = readLong(inputStream); long gammaRed = readDWord(inputStream); long gammaGreen = readDWord(inputStream); long gammaBlue = readDWord(inputStream); // Read in the palette int numberOfEntries = (int)((bitmapOffset-14-size) / 4); int sizeOfPalette = numberOfEntries*4; palette = new byte[sizeOfPalette]; inputStream.read(palette, 0, sizeOfPalette); if (palette != null || palette.length != 0) { properties.put("palette", palette); } switch((int)csType) { case LCS_CALIBRATED_RGB: // All the new fields are valid only for this case properties.put("color_space", "LCS_CALIBRATED_RGB"); properties.put("redX", new Integer(redX)); properties.put("redY", new Integer(redY)); properties.put("redZ", new Integer(redZ)); properties.put("greenX", new Integer(greenX)); properties.put("greenY", new Integer(greenY)); properties.put("greenZ", new Integer(greenZ)); properties.put("blueX", new Integer(blueX)); properties.put("blueY", new Integer(blueY)); properties.put("blueZ", new Integer(blueZ)); properties.put("gamma_red", new Long(gammaRed)); properties.put("gamma_green", new Long(gammaGreen)); properties.put("gamma_blue", new Long(gammaBlue)); // break; throw new RuntimeException(JaiI18N.getString("BMPImageDecoder2")); case LCS_sRGB: // Default Windows color space properties.put("color_space", "LCS_sRGB"); break; case LCS_CMYK: properties.put("color_space", "LCS_CMYK"); // break; throw new RuntimeException(JaiI18N.getString("BMPImageDecoder2")); } if (bitsPerPixel == 1) { imageType = VERSION_4_1_BIT; } else if (bitsPerPixel == 4) { imageType = VERSION_4_4_BIT; } else if (bitsPerPixel == 8) { imageType = VERSION_4_8_BIT; } else if (bitsPerPixel == 16) { imageType = VERSION_4_16_BIT; if ((int)compression == BI_RGB) { redMask = 0x7C00; greenMask = 0x3E0; blueMask = 0x1F; } } else if (bitsPerPixel == 24) { imageType = VERSION_4_24_BIT; } else if (bitsPerPixel == 32) { imageType = VERSION_4_32_BIT; if ((int)compression == BI_RGB) { redMask = 0x00FF0000; greenMask = 0x0000FF00; blueMask = 0x000000FF; } } properties.put("red_mask", new Integer(redMask)); properties.put("green_mask", new Integer(greenMask)); properties.put("blue_mask", new Integer(blueMask)); properties.put("alpha_mask", new Integer(alphaMask)); } else { properties.put("bmp_version", "BMP v. 5.x"); throw new RuntimeException(JaiI18N.getString("BMPImageDecoder4")); } } } catch (IOException ioe) { String message = JaiI18N.getString("BMPImageDecoder5"); ImagingListenerProxy.errorOccurred(message, new ImagingException(message, ioe), this, false); // throw new RuntimeException(JaiI18N.getString("BMPImageDecoder5")); } if (height > 0) { // bottom up image isBottomUp = true; } else { // top down image isBottomUp = false; height = Math.abs(height); } // Reset Image Layout so there's only one tile. tileWidth = width; tileHeight = height; // When number of bitsPerPixel is <= 8, we use IndexColorModel. if (bitsPerPixel == 1 || bitsPerPixel == 4 || bitsPerPixel == 8) { numBands = 1; if (bitsPerPixel == 8) { sampleModel = RasterFactory.createPixelInterleavedSampleModel( DataBuffer.TYPE_BYTE, width, height, numBands); } else { // 1 and 4 bit pixels can be stored in a packed format. sampleModel = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, width, height, bitsPerPixel); } // Create IndexColorModel from the palette. byte r[], g[], b[]; int size; if (imageType == VERSION_2_1_BIT || imageType == VERSION_2_4_BIT || imageType == VERSION_2_8_BIT) { size = palette.length/3; if (size > 256) { size = 256; } int off; r = new byte[size]; g = new byte[size]; b = new byte[size]; for (int i=0; i 256) { size = 256; } int off; r = new byte[size]; g = new byte[size]; b = new byte[size]; for (int i=0; i= 0; i--) { index = i * width; lineEnd = l + width; while(l != lineEnd) { val[l++] = inverted[index++]; } } } // This array will be used to call setPixels as the decompression // had unpacked the 4bit pixels each into an int. return val; } private int[] decodeRLE4(int imSize, int padding, int values[]) { int val[] = new int[width * height]; int count = 0, l = 0; int value; boolean flag = false; while (count != imSize) { value = values[count++]; if (value == 0) { // Absolute mode switch(values[count++]) { case 0: // End-of-scanline marker break; case 1: // End-of-RLE marker flag = true; break; case 2: // delta or vector marker int xoff = values[count++]; int yoff = values[count]; // Move to the position xoff, yoff down l += xoff + yoff*width; break; default: int end = values[count-1]; for (int i=0; i> 4 : (values[count++] & 0x0f); } // When end is odd, the above for loop does not // increment count, so do it now. if (!isEven(end)) { count++; } // Whenever end pixels can fit into odd number of bytes, // an extra padding byte will be present, so skip that. if ( !isEven((int)Math.ceil(end/2)) ) { count++; } break; } } else { // Encoded mode int alternate[] = { (values[count] & 0xf0) >> 4, values[count] & 0x0f }; for (int i=0; i 16) { return false; } // Number of bands must be between 1 and 4 int numBands = sampleModel.getNumBands(); if (numBands < 1 || numBands > 4) { return false; } // Palette images must be 1-banded, depth 8 or less ColorModel colorModel = im.getColorModel(); if (colorModel instanceof IndexColorModel) { if (numBands != 1 || bitDepth > 8) { return false; } } // If a param object is supplied, check that it is of the right type if (param != null) { if (param instanceof PNGEncodeParam) { if (colorModel instanceof IndexColorModel) { if (!(param instanceof PNGEncodeParam.Palette) ) { return false; } } else if (numBands < 3) { if (!(param instanceof PNGEncodeParam.Gray) ) { return false; } } else { if (!(param instanceof PNGEncodeParam.RGB) ) { return false; } } } else { return false; } } return true; } protected ImageEncoder createImageEncoder(OutputStream dst, ImageEncodeParam param) { PNGEncodeParam p = null; if (param != null) { p = (PNGEncodeParam)param; } return new PNGImageEncoder(dst, p); } protected ImageDecoder createImageDecoder(InputStream src, ImageDecodeParam param) { PNGDecodeParam p = null; if (param != null) { p = (PNGDecodeParam)param; } return new PNGImageDecoder(src, p); } protected ImageDecoder createImageDecoder(File src, ImageDecodeParam param) throws IOException { PNGDecodeParam p = null; if (param != null) { p = (PNGDecodeParam)param; } return new PNGImageDecoder(new FileInputStream(src), p); } protected ImageDecoder createImageDecoder(SeekableStream src, ImageDecodeParam param) { PNGDecodeParam p = null; if (param != null) { p = (PNGDecodeParam)param; } return new PNGImageDecoder(src, p); } public int getNumHeaderBytes() { return 8; } public boolean isFormatRecognized(byte[] header) { return ((header[0] == (byte)0x89) && (header[1] == (byte)0x50) && (header[2] == (byte)0x4e) && (header[3] == (byte)0x47) && (header[4] == (byte)0x0d) && (header[5] == (byte)0x0a) && (header[6] == (byte)0x1a) && (header[7] == (byte)0x0a)); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/JaiI18N.java0000644000175000017500000000076710203035544025526 0ustar mathieumathieu/* * $RCSfile: JaiI18N.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:37 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl; import com.sun.media.jai.codecimpl.util.PropertyUtil; class JaiI18N { static String packageName = "com.sun.media.jai.codecimpl"; public static String getString(String key) { return PropertyUtil.getString(packageName, key); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/fpx/0000755000175000017500000000000011633360404024347 5ustar mathieumathieujai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/fpx/PropertySet.java0000644000175000017500000002141010203035544027504 0ustar mathieumathieu/* * $RCSfile: PropertySet.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:41 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl.fpx; import java.awt.RenderingHints; import java.io.IOException; import java.util.Date; import java.util.Hashtable; import com.sun.media.jai.codec.SeekableStream; import com.sun.media.jai.codecimpl.ImagingListenerProxy; import com.sun.media.jai.codecimpl.util.ImagingException; class Property { private int type; private int offset; public Property(int type, int offset) { this.type = type; this.offset = offset; } public int getType() { return type; } public int getOffset() { return offset; } } class PropertySet { private static final int TYPE_VT_EMPTY = -1; private static final int TYPE_VT_NULL = -1; private static final int TYPE_VT_I2 = 2; private static final int TYPE_VT_I4 = 3; private static final int TYPE_VT_R4 = -1; private static final int TYPE_VT_R8 = -1; private static final int TYPE_VT_CY = -1; private static final int TYPE_VT_DATE = -1; private static final int TYPE_VT_BSTR = -1; private static final int TYPE_VT_ERROR = -1; private static final int TYPE_VT_BOOL = -1; private static final int TYPE_VT_VARIANT = -1; private static final int TYPE_VT_UI1 = -1; private static final int TYPE_VT_UI2 = -1; private static final int TYPE_VT_UI4 = 19; private static final int TYPE_VT_I8 = -1; private static final int TYPE_VT_UI8 = -1; private static final int TYPE_VT_LPSTR = 30; private static final int TYPE_VT_LPWSTR = 31; private static final int TYPE_VT_FILETIME = 64; private static final int TYPE_VT_BLOB = 65; private static final int TYPE_VT_STREAM = -1; private static final int TYPE_VT_STORAGE = -1; private static final int TYPE_VT_STREAMED_OBJECT = -1; private static final int TYPE_VT_STORED_OBJECT = -1; private static final int TYPE_VT_BLOB_OBJECT = -1; private static final int TYPE_VT_CF = 71; private static final int TYPE_VT_CLSID = 72; private static final int TYPE_VT_VECTOR = 4096; SeekableStream stream; Hashtable properties = new Hashtable(); public PropertySet(SeekableStream stream) throws IOException { this.stream = stream; stream.seek(44); int sectionOffset = stream.readIntLE(); stream.seek(sectionOffset); int sectionSize = stream.readIntLE(); int sectionCount = stream.readIntLE(); for (int i = 0; i < sectionCount; i++) { stream.seek(sectionOffset + 8*i + 8); int pid = stream.readIntLE(); int offset = stream.readIntLE(); stream.seek(sectionOffset + offset); int type = stream.readIntLE(); Property p = new Property(type, sectionOffset + offset + 4); properties.put(new Integer(pid), p); } } public boolean hasProperty(int id) { Property p = (Property)properties.get(new Integer(id)); return (p != null); } public int getI4(int id) { Property p = (Property)properties.get(new Integer(id)); try { int offset = p.getOffset(); stream.seek(offset); return stream.readIntLE(); } catch (IOException e) { ImagingListenerProxy.errorOccurred(JaiI18N.getString("PropertySet1"), e, this, false); // e.printStackTrace(); } return -1; } public int getUI1(int id) { Property p = (Property)properties.get(new Integer(id)); try { int offset = p.getOffset(); stream.seek(offset); return stream.readUnsignedByte(); } catch (IOException e) { ImagingListenerProxy.errorOccurred(JaiI18N.getString("PropertySet1"), e, this, false); // e.printStackTrace(); } return -1; } public int getUI2(int id) { Property p = (Property)properties.get(new Integer(id)); try { int offset = p.getOffset(); stream.seek(offset); return stream.readUnsignedShortLE(); } catch (IOException e) { ImagingListenerProxy.errorOccurred(JaiI18N.getString("PropertySet2"), e, this, false); // e.printStackTrace(); } return -1; } public long getUI4(int id) { Property p = (Property)properties.get(new Integer(id)); try { int offset = p.getOffset(); stream.seek(offset); return stream.readUnsignedIntLE(); } catch (IOException e) { ImagingListenerProxy.errorOccurred(JaiI18N.getString("PropertySet4"), e, this, false); // e.printStackTrace(); } return -1; } public long getUI4(int id, long defaultValue) { Property p = (Property)properties.get(new Integer(id)); if (p == null) { return defaultValue; } try { int offset = p.getOffset(); stream.seek(offset); return stream.readUnsignedIntLE(); } catch (IOException e) { ImagingListenerProxy.errorOccurred(JaiI18N.getString("PropertySet4"), e, this, false); // e.printStackTrace(); } return -1; } public String getLPSTR(int id) { Property p = (Property)properties.get(new Integer(id)); if (p == null) { return null; } try { int offset = p.getOffset(); stream.seek(offset); int length = stream.readIntLE(); StringBuffer sb = new StringBuffer(length); for (int i = 0; i < length; i++) { sb.append((char)stream.read()); } return sb.toString(); } catch (IOException e) { ImagingListenerProxy.errorOccurred(JaiI18N.getString("PropertySet5"), e, this, false); // e.printStackTrace(); return null; } } public String getLPWSTR(int id) { Property p = (Property)properties.get(new Integer(id)); try { int offset = p.getOffset(); stream.seek(offset); int length = stream.readIntLE(); StringBuffer sb = new StringBuffer(length); for (int i = 0; i < length; i++) { sb.append(stream.readCharLE()); } return sb.toString(); } catch (IOException e) { ImagingListenerProxy.errorOccurred(JaiI18N.getString("PropertySet5"), e, this, false); // e.printStackTrace(); return null; } } public float getR4(int id) { Property p = (Property)properties.get(new Integer(id)); try { int offset = p.getOffset(); stream.seek(offset); return stream.readFloatLE(); } catch (IOException e) { ImagingListenerProxy.errorOccurred(JaiI18N.getString("PropertySet6"), e, this, false); // e.printStackTrace(); return -1.0F; } } public Date getDate(int id) { throw new RuntimeException(JaiI18N.getString("PropertySet0")); } public Date getFiletime(int id) { throw new RuntimeException(JaiI18N.getString("PropertySet0")); } public byte[] getBlob(int id) { Property p = (Property)properties.get(new Integer(id)); try { int offset = p.getOffset(); stream.seek(offset); int length = stream.readIntLE(); byte[] buf = new byte[length]; stream.seek(offset + 4); stream.readFully(buf); return buf; } catch (IOException e) { ImagingListenerProxy.errorOccurred(JaiI18N.getString("PropertySet7"), e, this, false); // e.printStackTrace(); return null; } } public int[] getUI1Vector(int id) { throw new RuntimeException(JaiI18N.getString("PropertySet0")); } public int[] getUI2Vector(int id) { throw new RuntimeException(JaiI18N.getString("PropertySet0")); } public long[] getUI4Vector(int id) { throw new RuntimeException(JaiI18N.getString("PropertySet0")); } public float[] getR4Vector(int id) { throw new RuntimeException(JaiI18N.getString("PropertySet0")); } public String[] getLPWSTRVector(int id) { throw new RuntimeException(JaiI18N.getString("PropertySet0")); } } ././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootjai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/fpx/com.sun.media.jai.codecimpl.fpx.propertiesjai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/fpx/com.sun.media.jai.codecimpl.fpx.pro0000644000175000017500000000137610203035544033030 0ustar mathieumathieu# # $RCSfile: com.sun.media.jai.codecimpl.fpx.properties,v $ # # Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. # # Use is subject to license terms. # # $Revision: 1.1 $ # $Date: 2005-02-11 04:55:41 $ # $State: Exp $ # FPXImage0=IOException occurs when get a tile. FPXImage1=IOException occurs when get the summery information. FPXImage2=IOException occurs when get the image information. PropertySet0=Not implemented. PropertySet1=IOException occurs when get I4. PropertySet2=IOException occurs when get UI1. PropertySet3=IOException occurs when get UI2. PropertySet4=IOException occurs when get UI4. PropertySet5=IOException occurs when get LPSTR/LPWSTR. PropertySet6=IOException occurs when get R4. PropertySet7=IOException occurs when get blob. jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/fpx/StructuredStorage.java0000644000175000017500000005125310203035544030705 0ustar mathieumathieu/* * $RCSfile: StructuredStorage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:41 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl.fpx; import java.awt.RenderingHints; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.IOException; import java.io.RandomAccessFile; import java.util.StringTokenizer; import com.sun.media.jai.codec.ByteArraySeekableStream; import com.sun.media.jai.codec.FileSeekableStream; import com.sun.media.jai.codec.SeekableStream; import com.sun.media.jai.codec.SegmentedSeekableStream; // // NOTE -- all 'long' variables are really at most 32 bits, // corresponding to Microsoft 'ULONG' variables. // // Temporary (?) assumptions: // // All streams, including the ministream, are shorter than 2GB (size < 2GB) // // There are < 2^31 directory entries (#streams < 2^31) // class SSDirectoryEntry { int index; String name; long size; long startSector; long SIDLeftSibling; long SIDRightSibling; long SIDChild; public SSDirectoryEntry(int index, String name, long size, long startSector, long SIDLeftSibling, long SIDRightSibling, long SIDChild) { this.name = name; this.index = index; this.size = size; this.startSector = startSector; this.SIDLeftSibling = SIDLeftSibling; this.SIDRightSibling = SIDRightSibling; this.SIDChild = SIDChild; // System.out.println("Got a directory entry named " + name + // " (index " + index + ")"); // System.out.println("Start sector = " + startSector); } public String getName() { return name; } public long getSize() { return size; } public long getStartSector() { return startSector; } public long getSIDLeftSibling() { return SIDLeftSibling; } public long getSIDRightSibling() { return SIDRightSibling; } public long getSIDChild() { return SIDChild; } } public class StructuredStorage { // Chain terminator private static final long FAT_ENDOFCHAIN = 0xFFFFFFFEL; // Free sector private static final long FAT_FREESECT = 0xFFFFFFFFL; SeekableStream file; // Header fields private int sectorShift; // ULONG -- must be between 1 and 31 private int miniSectorShift; private long csectFat; private long sectDirStart; private long miniSectorCutoff; private long sectMiniFatStart; private long csectMiniFat; private long sectDifStart; private long csectDif; private long[] sectFat; // FAT, MiniFAT, and ministream in unrolled format // private long[] FAT; // ULONG -- only 2G entries max private long[] MINIFAT; // ULONG -- only 2G entries max private SSDirectoryEntry[] DIR; private SeekableStream miniStream; private SeekableStream FATStream; // The index of the current directory long cwdIndex = -1L; public StructuredStorage(SeekableStream file) throws IOException { this.file = file; // Read fields from the header getHeader(); // Read the FAT getFat(); // Read the MiniFAT getMiniFat(); // Read the directory getDirectory(); // Read the MiniStream getMiniStream(); } private void getHeader() throws IOException { file.seek(0x1e); this.sectorShift = file.readUnsignedShortLE(); // System.out.println("sectorShift = " + sectorShift); file.seek(0x20); this.miniSectorShift = file.readUnsignedShortLE(); // System.out.println("miniSectorShift = " + miniSectorShift); file.seek(0x2c); this.csectFat = file.readUnsignedIntLE(); // System.out.println("csectFat = " + csectFat); file.seek(0x30); this.sectDirStart = file.readUnsignedIntLE(); // System.out.println("sectDirStart = " + sectDirStart); file.seek(0x38); this.miniSectorCutoff = file.readUnsignedIntLE(); // System.out.println("miniSectorCutoff = " + miniSectorCutoff); file.seek(0x3c); this.sectMiniFatStart = file.readUnsignedIntLE(); // System.out.println("sectMiniFatStart = " + sectMiniFatStart); file.seek(0x40); this.csectMiniFat = file.readUnsignedIntLE(); // System.out.println("csectMiniFat = " + csectMiniFat); file.seek(0x44); this.sectDifStart = file.readUnsignedIntLE(); // System.out.println("sectDifStart = " + sectDifStart); file.seek(0x48); this.csectDif = file.readUnsignedIntLE(); // System.out.println("csectDif = " + csectDif); this.sectFat = new long[109]; file.seek(0x4c); for (int i = 0; i < 109; i++) { this.sectFat[i] = file.readUnsignedIntLE(); } } private void getFat() throws IOException { int size = getSectorSize(); int sectsPerFat = size/4; int fatsPerDif = size/4 - 1; // int index = 0; // this.FAT = // new long[(int)((csectFat + csectDif*fatsPerDif)*sectsPerFat)]; /* System.out.println("FAT has " + ((int)((csectFat + csectDif*fatsPerDif)*sectsPerFat)) + " entries."); System.out.println("csectFat = " + csectFat); System.out.println("csectDif = " + csectDif); System.out.println("fatsPerDif = " + fatsPerDif); System.out.println("sectsPerFat = " + sectsPerFat); */ int numFATSectors = (int)(csectFat + csectDif*fatsPerDif); long[] FATSectors = new long[numFATSectors]; int count = 0; for (int i = 0; i < 109; i++) { long sector = sectFat[i]; if (sector == FAT_FREESECT) { break; } FATSectors[count++] = getOffsetOfSector(sectFat[i]); // readFatSector(sector, index); // index += sectsPerFat; } if (csectDif > 0) { long dif = sectDifStart; byte[] difBuf = new byte[size]; for (int i = 0; i < csectDif; i++) { readSector(dif, difBuf, 0); for (int j = 0; j < fatsPerDif; j++) { int sec = FPXUtils.getIntLE(difBuf, 4*j); FATSectors[count++] = getOffsetOfSector(sec); // readFatSector(sec, index); // index += sectsPerFat; } dif = FPXUtils.getIntLE(difBuf, size - 4); } } FATStream = new SegmentedSeekableStream(file, FATSectors, size, numFATSectors*size, true); } private void getMiniFat() throws IOException { int size = getSectorSize(); int sectsPerFat = size/4; int index = 0; this.MINIFAT = new long[(int)(csectMiniFat*sectsPerFat)]; long sector = sectMiniFatStart; // System.out.println("minifat start sector = " + sector); byte[] buf = new byte[size]; while (sector != FAT_ENDOFCHAIN) { // System.out.println("minifat sector = " + sector); readSector(sector, buf, 0); for (int j = 0; j < sectsPerFat; j++) { MINIFAT[index++] = FPXUtils.getIntLE(buf, 4*j); } sector = getFATSector(sector); } } private void getDirectory() throws IOException { int size = getSectorSize(); long sector = sectDirStart; // Count the length of the directory in sectors int numDirectorySectors = 0; while (sector != FAT_ENDOFCHAIN) { sector = getFATSector(sector); ++numDirectorySectors; } int directoryEntries = 4*numDirectorySectors; this.DIR = new SSDirectoryEntry[directoryEntries]; sector = sectDirStart; byte[] buf = new byte[size]; int index = 0; while (sector != FAT_ENDOFCHAIN) { readSector(sector, buf, 0); int offset = 0; for (int i = 0; i < 4; i++) { // 4 dirents per sector // We divide the length by 2 for now even though // the spec says not to... int length = FPXUtils.getShortLE(buf, offset + 0x40); // System.out.println("\n\nDirent name length = " + length); /* FPXUtils.dumpBuffer(buf, offset, 128, 0); for (int j = 0; j < 32; j++) { int c = FPXUtils.getShortLE(buf, offset + 2*j); System.out.println("name[" + (2*j) + "] = " + c + " '" + (char)c + "'"); } */ String name = FPXUtils.getString(buf, offset + 0x00, length); long SIDLeftSibling = FPXUtils.getUnsignedIntLE(buf, offset + 0x44); long SIDRightSibling = FPXUtils.getUnsignedIntLE(buf, offset + 0x48); long SIDChild = FPXUtils.getUnsignedIntLE(buf, offset + 0x4c); long startSector = FPXUtils.getUnsignedIntLE(buf, offset + 0x74); long streamSize = FPXUtils.getUnsignedIntLE(buf, offset + 0x78); DIR[index] = new SSDirectoryEntry(index, name, streamSize, startSector, SIDLeftSibling, SIDRightSibling, SIDChild); ++index; offset += 128; } sector = getFATSector(sector); } } private void getMiniStream() throws IOException { int length = getLength(0L); int sectorSize = getSectorSize(); int sectors = (int)((length + sectorSize - 1)/sectorSize); long[] segmentPositions = new long[sectors]; long sector = getStartSector(0); // int offset = 0; for (int i = 0; i < sectors - 1; i++) { segmentPositions[i] = getOffsetOfSector(sector); sector = getFATSector(sector); if(sector == FAT_ENDOFCHAIN) break; } segmentPositions[sectors - 1] = getOffsetOfSector(sector); miniStream = new SegmentedSeekableStream(file, segmentPositions, sectorSize, length, true); } /* private void readFatSector(long sector, int index) throws IOException { int sectsPerFat = getSectorSize()/4; long offset = getOffsetOfSector(sector); file.seek(offset); for (int i = 0; i < sectsPerFat; i++) { FAT[index] = file.readUnsignedIntLE(); // System.out.println("FAT[" + index + "] = " + FAT[index]); index++; } } */ private int getSectorSize() { return 1 << sectorShift; } private long getOffsetOfSector(long sector) { return sector*getSectorSize() + 512; } private int getMiniSectorSize() { return 1 << miniSectorShift; } private long getOffsetOfMiniSector(long sector) { return sector*getMiniSectorSize(); } private void readMiniSector(long sector, byte[] buf, int offset, int length) throws IOException { miniStream.seek(getOffsetOfMiniSector(sector)); miniStream.read(buf, offset, length); } private void readMiniSector(long sector, byte[] buf, int offset) throws IOException { readMiniSector(sector, buf, offset, getMiniSectorSize()); } private void readSector(long sector, byte[] buf, int offset, int length) throws IOException { file.seek(getOffsetOfSector(sector)); file.read(buf, offset, length); } private void readSector(long sector, byte[] buf, int offset) throws IOException { readSector(sector, buf, offset, getSectorSize()); } private SSDirectoryEntry getDirectoryEntry(long index) { // Assume #streams < 2^31 return DIR[(int)index]; } private long getStartSector(long index) { // Assume #streams < 2^31 return DIR[(int)index].getStartSector(); } private int getLength(long index) { // Assume #streams < 2^31 // Assume size < 2GB return (int)DIR[(int)index].getSize(); } private long getFATSector(long sector) throws IOException { FATStream.seek(4*sector); return FATStream.readUnsignedIntLE(); // return FAT[(int)sector]; } private long getMiniFATSector(long sector) { return MINIFAT[(int)sector]; } private int getCurrentIndex() { return -1; } private int getIndex(String name, int index) { return -1; } private long searchDirectory(String name, long index) { if (index == FAT_FREESECT) { return -1L; } SSDirectoryEntry dirent = getDirectoryEntry(index); /* System.out.println("Comparing " + name + " (" + name.length() + ") against " + dirent.getName() + " (" + (dirent.getName()).length() + ") index " + index); */ if (name.equals(dirent.getName())) { // System.out.println("Matched!"); return index; } else { long lindex = searchDirectory(name, dirent.getSIDLeftSibling()); if (lindex != -1L) { return lindex; } long rindex = searchDirectory(name, dirent.getSIDRightSibling()); if (rindex != -1L) { return rindex; } } return -1L; } // Public methods public void changeDirectoryToRoot() { cwdIndex = getDirectoryEntry(0L).getSIDChild(); } public boolean changeDirectory(String name) { long index = searchDirectory(name, cwdIndex); if (index != -1L) { cwdIndex = getDirectoryEntry(index).getSIDChild(); // System.out.println("changeDirectory: setting cwdIndex to " + // cwdIndex); return true; } else { return false; } } /* public SSDirectory[] getDirectoryEntries() { } */ private long getStreamIndex(String name) { // Move down the directory hierarchy long index = cwdIndex; // System.out.println("start index = " + index); StringTokenizer st = new StringTokenizer(name, "/"); boolean firstTime = true; while (st.hasMoreTokens()) { String tok = st.nextToken(); // System.out.println("Token = " + tok); if (!firstTime) { index = getDirectoryEntry(index).getSIDChild(); } else { firstTime = false; } index = searchDirectory(tok, index); // System.out.println("index = " + index); } return index; } public byte[] getStreamAsBytes(String name) throws IOException { long index = getStreamIndex(name); if (index == -1L) { return null; } // Cast index to int (streams < 2^31) and cast stream size to an // int (size < 2GB) int length = getLength(index); byte[] buf = new byte[length]; if (length > miniSectorCutoff) { int sectorSize = getSectorSize(); int sectors = (int)((length + sectorSize - 1)/sectorSize); long sector = getStartSector(index); int offset = 0; for (int i = 0; i < sectors - 1; i++) { readSector(sector, buf, offset, sectorSize); offset += sectorSize; sector = getFATSector(sector); // System.out.println("next sector = " + sector); if(sector == FAT_ENDOFCHAIN) break; } readSector(sector, buf, offset, length - offset); } else { int sectorSize = getMiniSectorSize(); int sectors = (int)((length + sectorSize - 1)/sectorSize); long sector = getStartSector(index); // Assume ministream size < 2GB int offset = 0; for (int i = 0; i < sectors - 1; i++) { long miniSectorOffset = getOffsetOfMiniSector(sector); readMiniSector(sector, buf, offset, sectorSize); offset += sectorSize; sector = getMiniFATSector(sector); } readMiniSector(sector, buf, offset, length - offset); } return buf; } public SeekableStream getStream(String name) throws IOException { long index = getStreamIndex(name); if (index == -1L) { return null; } // Cast index to int (streams < 2^31) and cast stream size to an // int (size < 2GB) int length = getLength(index); long[] segmentPositions; int sectorSize, sectors; if (length > miniSectorCutoff) { sectorSize = getSectorSize(); sectors = (int)((length + sectorSize - 1)/sectorSize); segmentPositions = new long[sectors]; long sector = getStartSector(index); for (int i = 0; i < sectors - 1; i++) { segmentPositions[i] = getOffsetOfSector(sector); sector = getFATSector(sector); if(sector == FAT_ENDOFCHAIN) break; } segmentPositions[sectors - 1] = getOffsetOfSector(sector); return new SegmentedSeekableStream(file, segmentPositions, sectorSize, length, true); } else { sectorSize = getMiniSectorSize(); sectors = (int)((length + sectorSize - 1)/sectorSize); segmentPositions = new long[sectors]; long sector = getStartSector(index); for (int i = 0; i < sectors - 1; i++) { segmentPositions[i] = getOffsetOfMiniSector(sector); sector = getMiniFATSector(sector); } segmentPositions[sectors - 1] = getOffsetOfMiniSector(sector); return new SegmentedSeekableStream(miniStream, segmentPositions, sectorSize, length, true); } } public static void main(String[] args) { try { RandomAccessFile f = new RandomAccessFile(args[0], "r"); SeekableStream sis = new FileSeekableStream(f); StructuredStorage ss = new StructuredStorage(sis); ss.changeDirectoryToRoot(); byte[] s = ss.getStreamAsBytes("SummaryInformation"); PropertySet ps = new PropertySet(new ByteArraySeekableStream(s)); // Get the thumbnail property byte[] thumb = ps.getBlob(17); // Emit it as a BMP file System.out.print("BM"); int fs = (thumb.length - 8) + 14 + 40; System.out.print((char)(fs & 0xff)); System.out.print((char)((fs >> 8) & 0xff)); System.out.print((char)((fs >> 16) & 0xff)); System.out.print((char)((fs >> 24) & 0xff)); System.out.print((char)0); System.out.print((char)0); System.out.print((char)0); System.out.print((char)0); System.out.print('6'); System.out.print((char)0); System.out.print((char)0); System.out.print((char)0); for (int i = 8; i < thumb.length; i++) { System.out.print((char)(thumb[i] & 0xff)); } /* ss.changeDirectory("Data Object Store 000001"); SeekableStream imageContents = ss.getStream("Image Contents"); */ } catch (Exception e) { e.printStackTrace(); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/fpx/FPXImage.java0000644000175000017500000007446010240717543026630 0ustar mathieumathieu/* * $RCSfile: FPXImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-05-12 18:24:31 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl.fpx; import java.awt.Point; import java.awt.RenderingHints; import java.awt.Transparency; import java.awt.color.ColorSpace; import java.awt.image.ColorModel; import java.awt.image.ComponentColorModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.Raster; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.IOException; import java.util.Enumeration; import java.util.Hashtable; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.FPXDecodeParam; import com.sun.media.jai.codec.SeekableStream; import com.sun.media.jai.codecimpl.SimpleRenderedImage; import com.sun.media.jai.codecimpl.util.RasterFactory; import com.sun.image.codec.jpeg.JPEGCodec; import com.sun.image.codec.jpeg.JPEGDecodeParam; import com.sun.image.codec.jpeg.JPEGEncodeParam; import com.sun.image.codec.jpeg.JPEGImageDecoder; import com.sun.media.jai.codecimpl.ImagingListenerProxy; import com.sun.media.jai.codecimpl.util.ImagingException; public class FPXImage extends SimpleRenderedImage { private static final int SUBIMAGE_COLOR_SPACE_COLORLESS = 0; private static final int SUBIMAGE_COLOR_SPACE_MONOCHROME = 0; private static final int SUBIMAGE_COLOR_SPACE_PHOTOYCC = 0; private static final int SUBIMAGE_COLOR_SPACE_NIFRGB = 0; private static final String[] COLORSPACE_NAME = { "Colorless", "Monochrome", "PhotoYCC", "NIF RGB" }; StructuredStorage storage; int numResolutions; int highestResWidth; int highestResHeight; float defaultDisplayHeight; float defaultDisplayWidth; int displayHeightWidthUnits; boolean[] subimageValid; int[] subimageWidth; int[] subimageHeight; int[][] subimageColor; // subimageNumericalFormat int[] decimationMethod; float[] decimationPrefilterWidth; // subimage ICC profile int highestResolution = -1; int maxJPEGTableIndex; byte[][] JPEGTable; // Values from "Subimage 0000 Header" stream int numChannels; int tileHeaderTableOffset; int tileHeaderEntryLength; // int[] tileOffset; // int[] tileSize; // int[] compressionType; // int[] compressionSubtype; // The "Subimage 0000 Header" stream SeekableStream subimageHeaderStream; // The "Subimage 0000 Data" stream SeekableStream subimageDataStream; int resolution; // Derived values int tilesAcross; int[] bandOffsets = { 0, 1, 2 }; private static final int[] RGBBits8 = { 8, 8, 8 }; private static final ComponentColorModel colorModelRGB8 = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB), RGBBits8, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE); public FPXImage(SeekableStream stream, FPXDecodeParam param) throws IOException { this.storage = new StructuredStorage(stream); readImageContents(); if (param == null) { param = new FPXDecodeParam(); } this.resolution = param.getResolution(); readResolution(); bandOffsets = new int[numChannels]; for (int i = 0; i < numChannels; i++) { bandOffsets[i] = i; } this.minX = 0; this.minY = 0; this.sampleModel = RasterFactory.createPixelInterleavedSampleModel( DataBuffer.TYPE_BYTE, tileWidth, tileHeight, numChannels, numChannels*tileWidth, bandOffsets); this.colorModel = ImageCodec.createComponentColorModel(sampleModel); } private void readImageContents() throws IOException { storage.changeDirectoryToRoot(); storage.changeDirectory("Data Object Store 000001"); SeekableStream imageContents = storage.getStream("Image Contents"); PropertySet icps = new PropertySet(imageContents); this.numResolutions = (int)icps.getUI4(0x01000000); this.highestResWidth = (int)icps.getUI4(0x01000002); this.highestResHeight = (int)icps.getUI4(0x01000003); // this.defaultDisplayHeight = icps.getR4(0x01000004); // this.defaultDisplayWidth = icps.getR4(0x01000005); this.displayHeightWidthUnits = (int)icps.getUI4(0x01000006, 0L); /* System.out.println("\nImage Contents Property Set:\n"); System.out.println(" numResolutions = " + numResolutions); System.out.println(" highestResWidth = " + highestResWidth); System.out.println(" highestResHeight = " + highestResHeight); System.out.println(); */ this.subimageValid = new boolean[numResolutions]; this.subimageWidth = new int[numResolutions]; this.subimageHeight = new int[numResolutions]; this.subimageColor = new int[numResolutions][]; // subimageNumericalFormat this.decimationMethod = new int[numResolutions]; this.decimationPrefilterWidth = new float[numResolutions]; // subimage ICC profile for (int i = 0; i < numResolutions; i++) { int index = i << 16; if (!icps.hasProperty(0x02000000 | index)) { break; } this.highestResolution = i; subimageValid[i] = true; subimageWidth[i] = (int)icps.getUI4(0x02000000 | index); subimageHeight[i] = (int)icps.getUI4(0x02000001 | index); byte[] subimageColorBlob = icps.getBlob(0x02000002 | index); decimationMethod[i] = icps.getI4(0x02000004 | index); // decimationPrefilterWidth[i] = icps.getR4(0x02000005 | index); int numSubImages = FPXUtils.getIntLE(subimageColorBlob, 0); int numChannels = FPXUtils.getIntLE(subimageColorBlob, 4); // System.out.println(" subimageWidth[" + i + "] = " + // subimageWidth[i]); // System.out.println(" subimageHeight[" + i + "] = " + // subimageHeight[i]); // System.out.println(" subimageColor[" + i + "] = "); // System.out.println(" numSubimages = " + numSubImages); subimageColor[i] = new int[numChannels]; for (int c = 0; c < numChannels; c++) { int color = FPXUtils.getIntLE(subimageColorBlob, 8 + 4*c); // // Mask off the most significant bit as the FlashPix // specification states that: // // "If the most significant bit of the color space subfield // is set, then the image channel definitions are that of the // color space, but the image is not calibrated. [...] the // setting of the uncalibrated bit shall not imply any // different processing path." // subimageColor[i][c] = (color & 0x7fffffff); // System.out.println(" channel " + c + " color space " + // (color >> 16) + // " (" + COLORSPACE_NAME[color >> 16] +")"); // System.out.println(" channel " + c + " color type " + // (color & 0x7fff)); // if ((color & 0x8000) != 0) { // System.out.println(" channel " + c + " has premultiplied opacity"); // } } // System.out.println(" decimationMethod[" + i + "] = " + // decimationMethod[i]); // System.out.println(); } this.maxJPEGTableIndex = (int)icps.getUI4(0x03000002, -1L); // System.out.println("maxJPEGTableIndex = " + maxJPEGTableIndex); this.JPEGTable = new byte[maxJPEGTableIndex + 1][]; for (int i = 0; i <= maxJPEGTableIndex; i++) { int index = i << 16; if (icps.hasProperty(0x03000001 | index)) { // System.out.println("Found a table at index " + i); this.JPEGTable[i] = icps.getBlob(0x03000001 | index); } else { // System.out.println("No table at index " + i); this.JPEGTable[i] = null; } } } private void readResolution() throws IOException { if (resolution == -1) { resolution = this.highestResolution; } // System.out.println("Reading resolution " + resolution); storage.changeDirectoryToRoot(); storage.changeDirectory("Data Object Store 000001"); storage.changeDirectory("Resolution 000" + resolution); // FIX this.subimageHeaderStream = storage.getStream("Subimage 0000 Header"); subimageHeaderStream.skip(28); int headerLength = subimageHeaderStream.readIntLE(); this.width = subimageHeaderStream.readIntLE(); this.height = subimageHeaderStream.readIntLE(); int numTiles = subimageHeaderStream.readIntLE(); this.tileWidth = subimageHeaderStream.readIntLE(); this.tileHeight = subimageHeaderStream.readIntLE(); this.numChannels = subimageHeaderStream.readIntLE(); this.tileHeaderTableOffset = subimageHeaderStream.readIntLE() + 28; this.tileHeaderEntryLength = subimageHeaderStream.readIntLE(); // System.out.println("\nResolution 000" + resolution + "\n"); // System.out.println("Subimage 0000 Header:\n"); // System.out.println(" headerLength = " + headerLength); // System.out.println(" width = " + width); // System.out.println(" height = " + height); // System.out.println(" numTiles = " + numTiles); // System.out.println(" tileWidth = " + tileWidth); // System.out.println(" tileHeight = " + tileHeight); // System.out.println(" numChannels = " + numChannels); // System.out.println(" tileHeaderTableOffset = " + // tileHeaderTableOffset); // System.out.println(" tileHeaderEntryLength = " + // tileHeaderEntryLength); this.subimageDataStream = storage.getStream("Subimage 0000 Data"); // Compute derived values tilesAcross = (width + tileWidth - 1)/tileWidth; } private int getTileOffset(int tileIndex) throws IOException { // return tileOffset[tileIndex]; subimageHeaderStream.seek(tileHeaderTableOffset + 16*tileIndex); return subimageHeaderStream.readIntLE() + 28; } private int getTileSize(int tileIndex) throws IOException { // return tileSize[tileIndex]; subimageHeaderStream.seek(tileHeaderTableOffset + 16*tileIndex + 4); return subimageHeaderStream.readIntLE(); } private int getCompressionType(int tileIndex) throws IOException { // return compressionType[tileIndex]; subimageHeaderStream.seek(tileHeaderTableOffset + 16*tileIndex + 8); return subimageHeaderStream.readIntLE(); } private int getCompressionSubtype(int tileIndex) throws IOException { // return compressionSubtype[tileIndex]; subimageHeaderStream.seek(tileHeaderTableOffset + 16*tileIndex + 12); return subimageHeaderStream.readIntLE(); } private static final byte[] PhotoYCCToRGBLUT = { (byte)0, (byte)1, (byte)1, (byte)2, (byte)2, (byte)3, (byte)4, (byte)5, (byte)6, (byte)7, (byte)8, (byte)9, (byte)10, (byte)11, (byte)12, (byte)13, (byte)14, (byte)15, (byte)16, (byte)17, (byte)18, (byte)19, (byte)20, (byte)22, (byte)23, (byte)24, (byte)25, (byte)26, (byte)28, (byte)29, (byte)30, (byte)31, (byte)33, (byte)34, (byte)35, (byte)36, (byte)38, (byte)39, (byte)40, (byte)41, (byte)43, (byte)44, (byte)45, (byte)47, (byte)48, (byte)49, (byte)51, (byte)52, (byte)53, (byte)55, (byte)56, (byte)57, (byte)59, (byte)60, (byte)61, (byte)63, (byte)64, (byte)65, (byte)67, (byte)68, (byte)70, (byte)71, (byte)72, (byte)74, (byte)75, (byte)76, (byte)78, (byte)79, (byte)81, (byte)82, (byte)83, (byte)85, (byte)86, (byte)88, (byte)89, (byte)91, (byte)92, (byte)93, (byte)95, (byte)96, (byte)98, (byte)99, (byte)101, (byte)102, (byte)103, (byte)105, (byte)106, (byte)108, (byte)109, (byte)111, (byte)112, (byte)113, (byte)115, (byte)116, (byte)118, (byte)119, (byte)121, (byte)122, (byte)123, (byte)125, (byte)126, (byte)128, (byte)129, (byte)130, (byte)132, (byte)133, (byte)134, (byte)136, (byte)137, (byte)138, (byte)140, (byte)141, (byte)142, (byte)144, (byte)145, (byte)146, (byte)148, (byte)149, (byte)150, (byte)152, (byte)153, (byte)154, (byte)155, (byte)157, (byte)158, (byte)159, (byte)160, (byte)162, (byte)163, (byte)164, (byte)165, (byte)166, (byte)168, (byte)169, (byte)170, (byte)171, (byte)172, (byte)174, (byte)175, (byte)176, (byte)177, (byte)178, (byte)179, (byte)180, (byte)182, (byte)183, (byte)184, (byte)185, (byte)186, (byte)187, (byte)188, (byte)189, (byte)190, (byte)191, (byte)192, (byte)194, (byte)195, (byte)196, (byte)197, (byte)198, (byte)199, (byte)200, (byte)201, (byte)202, (byte)203, (byte)204, (byte)204, (byte)205, (byte)206, (byte)207, (byte)208, (byte)209, (byte)210, (byte)211, (byte)212, (byte)213, (byte)213, (byte)214, (byte)215, (byte)216, (byte)217, (byte)217, (byte)218, (byte)219, (byte)220, (byte)221, (byte)221, (byte)222, (byte)223, (byte)223, (byte)224, (byte)225, (byte)225, (byte)226, (byte)227, (byte)227, (byte)228, (byte)229, (byte)229, (byte)230, (byte)230, (byte)231, (byte)231, (byte)232, (byte)233, (byte)233, (byte)234, (byte)234, (byte)235, (byte)235, (byte)236, (byte)236, (byte)236, (byte)237, (byte)237, (byte)238, (byte)238, (byte)238, (byte)239, (byte)239, (byte)240, (byte)240, (byte)240, (byte)241, (byte)241, (byte)241, (byte)242, (byte)242, (byte)242, (byte)242, (byte)243, (byte)243, (byte)243, (byte)244, (byte)244, (byte)244, (byte)244, (byte)245, (byte)245, (byte)245, (byte)245, (byte)245, (byte)246, (byte)246, (byte)246, (byte)246, (byte)246, (byte)247, (byte)247, (byte)247, (byte)247, (byte)247, (byte)247, (byte)248, (byte)248, (byte)248, (byte)248, (byte)248, (byte)248, (byte)249, (byte)249, (byte)249, (byte)249, (byte)249, (byte)249, (byte)249, (byte)249, (byte)249, (byte)250, (byte)250, (byte)250, (byte)250, (byte)250, (byte)250, (byte)250, (byte)250, (byte)250, (byte)250, (byte)251, (byte)251, (byte)251, (byte)251, (byte)251, (byte)251, (byte)251, (byte)251, (byte)251, (byte)251, (byte)251, (byte)251, (byte)251, (byte)251, (byte)252, (byte)252, (byte)252, (byte)252, (byte)252, (byte)252, (byte)252, (byte)252, (byte)252, (byte)252, (byte)252, (byte)252, (byte)252, (byte)252, (byte)252, (byte)252, (byte)252, (byte)253, (byte)253, (byte)253, (byte)253, (byte)253, (byte)253, (byte)253, (byte)253, (byte)253, (byte)253, (byte)253, (byte)253, (byte)253, (byte)253, (byte)253, (byte)253, (byte)253, (byte)253, (byte)254, (byte)254, (byte)254, (byte)254, (byte)254, (byte)254, (byte)254, (byte)254, (byte)254, (byte)254, (byte)254, (byte)254, (byte)254, (byte)254, (byte)254, (byte)255, (byte)255, (byte)255, (byte)255, (byte)255, (byte)255, (byte)255, (byte)255, (byte)255, (byte)255, (byte)255, (byte)255, (byte)255, (byte)255, (byte)255, (byte)255, (byte)255, (byte)255, (byte)255 }; private final byte PhotoYCCToNIFRed(float scaledY, float Cb, float Cr) { float red = scaledY + 1.8215F*Cr - 249.55F; if (red < 0.0F) { return (byte)0; } else if (red > 360.0F) { return (byte)255; } else { byte r = PhotoYCCToRGBLUT[(int)red]; return r; } } private final byte PhotoYCCToNIFGreen(float scaledY, float Cb, float Cr) { float green = scaledY - .43031F*Cb - .9271F*Cr + 194.14F; if (green < 0.0F) { return (byte)0; } else if (green > 360.0F) { return (byte)255; } else { byte g = PhotoYCCToRGBLUT[(int)green]; return g; } } private final byte PhotoYCCToNIFBlue(float scaledY, float Cb, float Cr) { float blue = scaledY + 2.2179F*Cb - 345.99F; if (blue < 0.0F) { return (byte)0; } else if (blue > 360.0F) { return (byte)255; } else { byte b = PhotoYCCToRGBLUT[(int)blue]; return b; } } private final byte YCCToNIFRed(float Y, float Cb, float Cr) { float red = Y + 1.402F*Cr - (255.0F*.701F); if (red < 0.0F) { return (byte)0; } else if (red > 255.0F) { return (byte)255; } else { return (byte)red; } } private final byte YCCToNIFGreen(float Y, float Cb, float Cr) { float green = Y - .34414F*Cb - .71414F*Cr + (255.0F*.52914F); if (green < 0.0F) { return (byte)0; } else if (green > 255.0F) { return (byte)255; } else { return (byte)green; } } private final byte YCCToNIFBlue(float Y, float Cb, float Cr) { float blue = Y + 1.772F*Cb - (255.0F*.886F); if (blue < 0.0F) { return (byte)0; } else if (blue > 255.0F) { return (byte)255; } else { return (byte)blue; } } private Raster getUncompressedTile(int tileX, int tileY) throws IOException { int tx = tileXToX(tileX); int ty = tileYToY(tileY); Raster ras = RasterFactory.createInterleavedRaster(DataBuffer.TYPE_BYTE, tileWidth, tileHeight, numChannels*tileWidth, numChannels, bandOffsets, new Point(tx, ty)); // System.out.println("Uncompressed tile."); DataBufferByte dataBuffer = (DataBufferByte)ras.getDataBuffer(); byte[] data = dataBuffer.getData(); int tileIndex = tileY*tilesAcross + tileX; subimageDataStream.seek(getTileOffset(tileIndex)); subimageDataStream.readFully(data, 0, numChannels*tileWidth*tileHeight); // Color convert if subimage is in PhotoYCC format if (subimageColor[resolution][0] >> 16 == 2) { int size = tileWidth*tileHeight; for (int i = 0; i < size; i++) { float Y = data[3*i] & 0xff; float Cb = data[3*i + 1] & 0xff; float Cr = data[3*i + 2] & 0xff; float scaledY = Y*1.3584F; byte red = PhotoYCCToNIFRed(scaledY, Cb, Cr); byte green = PhotoYCCToNIFGreen(scaledY, Cb, Cr); byte blue = PhotoYCCToNIFBlue(scaledY, Cb, Cr); data[3*i] = red; data[3*i + 1] = green; data[3*i + 2] = blue; } } return ras; } private Raster getSingleColorCompressedTile(int tileX, int tileY) throws IOException { // System.out.println("Single color compressed tile."); int tx = tileXToX(tileX); int ty = tileYToY(tileY); Raster ras = RasterFactory.createInterleavedRaster(DataBuffer.TYPE_BYTE, tileWidth, tileHeight, numChannels*tileWidth, numChannels, bandOffsets, new Point(tx, ty)); int subimageColorType = subimageColor[resolution][0] >> 16; DataBufferByte dataBuffer = (DataBufferByte)ras.getDataBuffer(); byte[] data = dataBuffer.getData(); int tileIndex = tileY*tilesAcross + tileX; int color = getCompressionSubtype(tileIndex); byte c0 = (byte)((color >> 0) & 0xff); byte c1 = (byte)((color >> 8) & 0xff); byte c2 = (byte)((color >> 16) & 0xff); byte alpha = (byte)((color >> 24) & 0xff); byte red, green, blue; // Color convert if subimage is in PhotoYCC format if (subimageColor[resolution][0] >> 16 == 2) { float Y = c0 & 0xff; float Cb = c1 & 0xff; float Cr = c2 & 0xff; float scaledY = Y*1.3584F; red = PhotoYCCToNIFRed(scaledY, Cb, Cr); green = PhotoYCCToNIFGreen(scaledY, Cb, Cr); blue = PhotoYCCToNIFBlue(scaledY, Cb, Cr); } else { red = c0; green = c1; blue = c2; } int index = 0; int pixels = tileWidth*tileHeight; if (numChannels == 1) { } else if (numChannels == 2) { } else if (numChannels == 3) { for (int i = 0; i < pixels; i++) { data[index + 0] = red; data[index + 1] = green; data[index + 2] = blue; index += 3; } } else if (numChannels == 4) { for (int i = 0; i < pixels; i++) { data[index + 0] = red; data[index + 1] = green; data[index + 2] = blue; data[index + 3] = alpha; index += 4; } } return ras; } private Raster getJPEGCompressedTile(int tileX, int tileY) throws IOException { // System.out.println("JPEG compressed tile."); int tileIndex = tileY*tilesAcross + tileX; int tx = tileXToX(tileX); int ty = tileYToY(tileY); int subtype = getCompressionSubtype(tileIndex); int interleave = (subtype >> 0) & 0xff; int chroma = (subtype >> 8) & 0xff; int conversion = (subtype >> 16) & 0xff; int table = (subtype >> 24) & 0xff; JPEGImageDecoder dec; JPEGDecodeParam param = null; if (table != 0) { InputStream tableStream = new ByteArrayInputStream(JPEGTable[table]); dec = JPEGCodec.createJPEGDecoder(tableStream); Raster junk = dec.decodeAsRaster(); param = dec.getJPEGDecodeParam(); } subimageDataStream.seek(getTileOffset(tileIndex)); if (param != null) { dec = JPEGCodec.createJPEGDecoder(subimageDataStream, param); } else { dec = JPEGCodec.createJPEGDecoder(subimageDataStream); } Raster ras = dec.decodeAsRaster().createTranslatedChild(tx, ty); DataBufferByte dataBuffer = (DataBufferByte)ras.getDataBuffer(); byte[] data = dataBuffer.getData(); int subimageColorType = subimageColor[resolution][0] >> 16; int size = tileWidth*tileHeight; if ((conversion == 0) && (subimageColorType == 2)) { // System.out.println("Converting PhotoYCC to NIFRGB"); int offset = 0; for (int i = 0; i < size; i++) { float Y = data[offset] & 0xff; float Cb = data[offset + 1] & 0xff; float Cr = data[offset + 2] & 0xff; float scaledY = Y*1.3584F; byte red = PhotoYCCToNIFRed(scaledY, Cb, Cr); byte green = PhotoYCCToNIFGreen(scaledY, Cb, Cr); byte blue = PhotoYCCToNIFBlue(scaledY, Cb, Cr); data[offset] = red; data[offset + 1] = green; data[offset + 2] = blue; offset += numChannels; } } else if ((conversion == 1) && (subimageColorType == 3)) { // System.out.println("Converting YCC to NIFRGB"); int offset = 0; for (int i = 0; i < size; i++) { float Y = data[offset] & 0xff; float Cb = data[offset + 1] & 0xff; float Cr = data[offset + 2] & 0xff; byte red = YCCToNIFRed(Y, Cb, Cr); byte green = YCCToNIFGreen(Y, Cb, Cr); byte blue = YCCToNIFBlue(Y, Cb, Cr); data[offset] = red; data[offset + 1] = green; data[offset + 2] = blue; offset += numChannels; } } // Perform special inversion step when output space is // NIF RGB (subimageColorType == 3) with premultiplied opacity // (numChannels == 4). if ((conversion == 1) && (subimageColorType == 3) && (numChannels == 4)) { // System.out.println("Flipping NIFRGB"); int offset = 0; for (int i = 0; i < size; i++) { data[offset + 0] = (byte)(255 - data[offset + 0]); data[offset + 1] = (byte)(255 - data[offset + 1]); data[offset + 2] = (byte)(255 - data[offset + 2]); offset += 4; } } return ras; } public synchronized Raster getTile(int tileX, int tileY) { int tileIndex = tileY*tilesAcross + tileX; try { int ctype = getCompressionType(tileIndex); if (ctype == 0) { return getUncompressedTile(tileX, tileY); } else if (ctype == 1) { return getSingleColorCompressedTile(tileX, tileY); } else if (ctype == 2) { return getJPEGCompressedTile(tileX, tileY); } return null; } catch (IOException e) { ImagingListenerProxy.errorOccurred(JaiI18N.getString("FPXImage0"), e, ImageCodec.class, false); // e.printStackTrace(); return null; } } Hashtable properties = null; private void addLPSTRProperty(String name, PropertySet ps, int id) { String s = ps.getLPSTR(id); if (s != null) { properties.put(name.toLowerCase(), s); } } private void addLPWSTRProperty(String name, PropertySet ps, int id) { String s = ps.getLPWSTR(id); if (s != null) { properties.put(name.toLowerCase(), s); } } private void addUI4Property(String name, PropertySet ps, int id) { if (ps.hasProperty(id)) { long i = ps.getUI4(id); properties.put(name.toLowerCase(), new Integer((int)i)); } } private void getSummaryInformation() { SeekableStream summaryInformation = null; PropertySet sips = null; try { storage.changeDirectoryToRoot(); summaryInformation = storage.getStream("SummaryInformation"); sips = new PropertySet(summaryInformation); } catch (IOException e) { ImagingListenerProxy.errorOccurred(JaiI18N.getString("FPXImage1"), e, ImageCodec.class, false); // e.printStackTrace(); return; } addLPSTRProperty("title", sips, 0x000000002); addLPSTRProperty("subject", sips, 0x000000003); addLPSTRProperty("author", sips, 0x000000004); addLPSTRProperty("keywords", sips, 0x000000005); addLPSTRProperty("comments", sips, 0x000000006); addLPSTRProperty("template", sips, 0x000000007); addLPSTRProperty("last saved by", sips, 0x000000008); addLPSTRProperty("revision number", sips, 0x000000009); } private void getImageInfo() { SeekableStream imageInfo = null; PropertySet iips = null; try { storage.changeDirectoryToRoot(); imageInfo = storage.getStream("Image Info"); if (imageInfo == null) { return; } iips = new PropertySet(imageInfo); } catch (IOException e) { ImagingListenerProxy.errorOccurred(JaiI18N.getString("FPXImage2"), e, ImageCodec.class, false); // e.printStackTrace(); return; } addUI4Property("file source", iips, 0x21000000); addUI4Property("scene type", iips, 0x21000001); // creation path vector addLPWSTRProperty("software name/manufacturer/release", iips, 0x21000003); addLPWSTRProperty("user defined id", iips, 0x21000004); addLPWSTRProperty("copyright message", iips, 0x22000000); addLPWSTRProperty("legal broker for the original image", iips, 0x22000001); addLPWSTRProperty("legal broker for the digital image", iips, 0x22000002); addLPWSTRProperty("authorship", iips, 0x22000003); addLPWSTRProperty("intellectual property notes", iips, 0x22000004); } private synchronized void getProperties() { if (properties != null) { return; } properties = new Hashtable(); getSummaryInformation(); getImageInfo(); // Ad hoc properties properties.put("max_resolution", new Integer(highestResolution)); } public String[] getPropertyNames() { getProperties(); int len = properties.size(); String[] names = new String[len]; Enumeration enumeration = properties.keys(); int count = 0; while (enumeration.hasMoreElements()) { names[count++] = (String)enumeration.nextElement(); } return names; } public Object getProperty(String name) { getProperties(); return properties.get(name.toLowerCase()); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/fpx/JaiI18N.java0000644000175000017500000000077710203035544026324 0ustar mathieumathieu/* * $RCSfile: JaiI18N.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:41 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl.fpx; import com.sun.media.jai.codecimpl.util.PropertyUtil; class JaiI18N { static String packageName = "com.sun.media.jai.codecimpl.fpx"; public static String getString(String key) { return PropertyUtil.getString(packageName, key); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/fpx/FPXUtils.java0000644000175000017500000001077310203035544026674 0ustar mathieumathieu/* * $RCSfile: FPXUtils.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:41 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl.fpx; import java.io.IOException; import java.text.DecimalFormat; import com.sun.media.jai.codec.SeekableStream; public class FPXUtils { // /** Reads a 2-byte little-endian value. */ // public static final int readInt2(SeekableStream file, int offset) // throws IOException { // file.seek(offset); // return file.readShortLE(); // } // /** Reads a 4-byte little-endian value. */ // public static final int readInt4(SeekableStream file, int offset) // throws IOException { // file.seek(offset); // return file.readIntLE(); // } // /** Reads a 4-byte little-endian IEEE float. */ // public static final float readFloat(SeekableStream file, int offset) // throws IOException { // file.seek(offset); // return file.readFloatLE(); // } // /** Reads an 8-byte little-endian IEEE double. */ // public static final double readDouble(SeekableStream file, // int offset) // throws IOException { // file.seek(offset); // return file.readDoubleLE(); // } public static final short getShortLE(byte[] data, int offset) { int b0 = data[offset] & 0xff; int b1 = data[offset + 1] & 0xff; return (short)((b1 << 8) | b0); } public static final int getUnsignedShortLE(byte[] data, int offset) { int b0 = data[offset] & 0xff; int b1 = data[offset + 1] & 0xff; return (b1 << 8) | b0; } public static final int getIntLE(byte[] data, int offset) { int b0 = data[offset] & 0xff; int b1 = data[offset + 1] & 0xff; int b2 = data[offset + 2] & 0xff; int b3 = data[offset + 3] & 0xff; return (b3 << 24) | (b2 << 16) | (b1 << 8) | b0; } public static final long getUnsignedIntLE(byte[] data, int offset) { long b0 = data[offset] & 0xff; long b1 = data[offset + 1] & 0xff; long b2 = data[offset + 2] & 0xff; long b3 = data[offset + 3] & 0xff; return (b3 << 24) | (b2 << 16) | (b1 << 8) | b0; } public static final String getString(byte[] data, int offset, int length) { if (length == 0) { return ""; } else { length = length/2 - 1; // workaround for Kodak bug } StringBuffer b = new StringBuffer(length); for (int i = 0; i < length; i++) { int c = getUnsignedShortLE(data, offset); b.append((char)c); offset += 2; } return b.toString(); } private static void printDecimal(int i) { DecimalFormat d = new DecimalFormat("00000"); System.out.print(d.format(i)); } private static void printHex(byte b) { int i = b & 0xff; int hi = i/16; int lo = i % 16; if (hi < 10) { System.out.print((char)('0' + hi)); } else { System.out.print((char)('a' + hi - 10)); } if (lo < 10) { System.out.print((char)('0' + lo)); } else { System.out.print((char)('a' + lo - 10)); } } private static void printChar(byte b) { char c = (char)(b & 0xff); if (c >= '!' && c <= '~') { System.out.print(' '); System.out.print(c); } else if (c == 0) { System.out.print("^@"); } else if (c < ' ') { System.out.print('^'); System.out.print((char)('A' + c - 1)); } else if (c == ' ') { System.out.print("__"); } else { System.out.print("??"); } } public static void dumpBuffer(byte[] buf, int offset, int length, int printOffset) { int lines = length/8; for (int j = 0; j < lines; j++) { printDecimal(printOffset); System.out.print(": "); for (int i = 0; i < 8; i++) { printHex(buf[offset + i]); System.out.print(" "); } for (int i = 0; i < 8; i++) { printChar(buf[offset + i]); System.out.print(" "); } offset += 8; printOffset += 8; System.out.println(); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/FPXImageDecoder.java0000644000175000017500000000230310472445724027312 0ustar mathieumathieu/* * $RCSfile: FPXImageDecoder.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2006-08-22 00:12:04 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl; import java.awt.image.RenderedImage; import java.io.InputStream; import java.io.IOException; import com.sun.media.jai.codec.FPXDecodeParam; import com.sun.media.jai.codec.ImageDecoder; import com.sun.media.jai.codec.ImageDecodeParam; import com.sun.media.jai.codec.ImageDecoderImpl; import com.sun.media.jai.codec.SeekableStream; import com.sun.media.jai.codecimpl.fpx.FPXImage; /** * @since EA3 */ public class FPXImageDecoder extends ImageDecoderImpl { public FPXImageDecoder(SeekableStream input, ImageDecodeParam param) { super(input, param); } public RenderedImage decodeAsRenderedImage(int page) throws IOException { if (page != 0) { throw new IOException(JaiI18N.getString("FPXImageDecoder0")); } try { return new FPXImage(input, (FPXDecodeParam)param); } catch(Exception e) { throw CodecUtils.toIOException(e); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/codecimpl/TIFFCodec.java0000644000175000017500000000421510203035544026101 0ustar mathieumathieu/* * $RCSfile: TIFFCodec.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:39 $ * $State: Exp $ */ package com.sun.media.jai.codecimpl; import java.awt.image.RenderedImage; import java.io.File; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.ImageDecoder; import com.sun.media.jai.codec.ImageDecodeParam; import com.sun.media.jai.codec.ImageEncoder; import com.sun.media.jai.codec.ImageEncodeParam; import com.sun.media.jai.codec.FileSeekableStream; import com.sun.media.jai.codec.SeekableStream; import com.sun.media.jai.codec.TIFFEncodeParam; /** * @since EA3 */ public final class TIFFCodec extends ImageCodec { public TIFFCodec() {} public String getFormatName() { return "tiff"; } public Class getEncodeParamClass() { return com.sun.media.jai.codec.TIFFEncodeParam.class; } public Class getDecodeParamClass() { return com.sun.media.jai.codec.TIFFDecodeParam.class; } public boolean canEncodeImage(RenderedImage im, ImageEncodeParam param) { return true; } protected ImageEncoder createImageEncoder(OutputStream dst, ImageEncodeParam param) { return new TIFFImageEncoder(dst, param); } protected ImageDecoder createImageDecoder(SeekableStream src, ImageDecodeParam param) { return new TIFFImageDecoder(src, param); } public int getNumHeaderBytes() { return 4; } public boolean isFormatRecognized(byte[] header) { if ((header[0] == 0x49) && (header[1] == 0x49) && (header[2] == 0x2a) && (header[3] == 0x00)) { return true; } if ((header[0] == 0x4d) && (header[1] == 0x4d) && (header[2] == 0x00) && (header[3] == 0x2a)) { return true; } return false; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/0000755000175000017500000000000011633360406022404 5ustar mathieumathieujai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/RenderingHintsProxy.java0000644000175000017500000002675510203035544027245 0ustar mathieumathieu/* * $RCSfile: RenderingHintsProxy.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:53 $ * $State: Exp $ */ package com.sun.media.jai.rmi; import java.awt.RenderingHints; import java.io.IOException; import java.io.NotSerializableException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.ref.SoftReference; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import java.util.Set; import java.util.Vector; import javax.media.jai.JAI; import javax.media.jai.OperationRegistry; /** * This class is a serializable proxy for a RenderingHints object from which * the RenderingHints object may be reconstituted. * * * @since EA3 */ public class RenderingHintsProxy implements Serializable { /** The RenderingHints object. */ private transient RenderingHints hints; /** * The classes wherein all possible relevant public static * RenderingHints.Key objects are defined. Classes which contain * declarations of such keys should be added to this array. */ private static final Class[] KEY_CLASSES = {RenderingHints.class, JAI.class}; /** * Instances of keys which should not be serialized. Objects which * represent such keys should be added to this array. Presumably such * objects would be static and final members of one of the classes * in the KEY_CLASSES array. */ private static final Object[] SUPPRESSED_KEYS = {JAI.KEY_OPERATION_REGISTRY, JAI.KEY_TILE_CACHE, JAI.KEY_RETRY_INTERVAL, JAI.KEY_NUM_RETRIES, JAI.KEY_NEGOTIATION_PREFERENCES}; /** A SoftReference to a Vector of keys which are to be suppressed. */ private static SoftReference suppressedKeyReference = null; /** * A SoftReference to a Hashtable containing serializable versions of * all public static fields in the classes in the KEY_CLASSES array. */ private static SoftReference hintTableReference = null; /** * Constructs a RenderingHintsProxy from a * RenderingHints object. * * @param source The RenderingHints object to be serialized. */ public RenderingHintsProxy(RenderingHints source) { hints = source; } /** An inner class representing either a hint key or a hint value. */ private static class HintElement implements Serializable { /** The class represents a serializable object. */ private static final int TYPE_OBJECT = 1; /** The class represents an element by class and field name. */ private static final int TYPE_FIELD = 2; /** The type of the element representation. */ private int type; /** The element itself. */ private Object obj; /** The name of the class of the element (type == TYPE_FIELD only). */ private String className; /** The name of the field of the element (type == TYPE_FIELD only). */ private String fieldName; /** Constructs a HintElement representing a serializable object. */ public HintElement(Object obj) throws NotSerializableException { if(!(obj instanceof Serializable)) { throw new NotSerializableException(); } type = TYPE_OBJECT; this.obj = obj; } /** Constructs a HintElement representing a public static field. */ public HintElement(Class cls, Field fld) { type = TYPE_FIELD; this.className = cls.getName(); this.fieldName = fld.getName(); } /** Retrieves the element value. Returns null on error. */ public Object getObject() { Object elt = null; if(type == TYPE_OBJECT) { elt = obj; } else if(type == TYPE_FIELD) { try { Class cls = Class.forName(className); Field fld = cls.getField(fieldName); elt = fld.get(null); } catch(Exception e) { // Do nothing. } } return elt; } } /** * Retrieves the associated RenderingHints object. * @return The (perhaps reconstructed) RenderingHints * object. */ public RenderingHints getRenderingHints() { return hints; } /** Returns a Vector of keys which should not be serialized or null. */ private static synchronized Vector getSuppressedKeys() { Vector suppressedKeys = null; if(SUPPRESSED_KEYS != null) { // Initialize the Vector to the SoftReference's referent or null. suppressedKeys = suppressedKeyReference != null ? (Vector)suppressedKeyReference.get() : null; if(suppressedKeys == null) { // Cache the number of suppressed keys. int numSuppressedKeys = SUPPRESSED_KEYS.length; // Load the Vector with the suppressed key objects. suppressedKeys = new Vector(numSuppressedKeys); for(int i = 0; i < numSuppressedKeys; i++) { suppressedKeys.add(SUPPRESSED_KEYS[i]); } // Cache the Vector of suppressed keys. suppressedKeyReference = new SoftReference(suppressedKeys); } } return suppressedKeys; } /** * Returns a Hashtable wherein the keys are instances of * RenderingHints.Key and the values are HintElements. */ private static synchronized Hashtable getHintTable() { // Initialize the table to the SoftReference's referent or null. Hashtable table = hintTableReference != null ? (Hashtable)hintTableReference.get() : null; if(table == null) { // Allocate a table for the field values. table = new Hashtable(); for(int i = 0; i < KEY_CLASSES.length; i++) { // Cache the class. Class cls = KEY_CLASSES[i]; // Retrieve the fields for this class. Field[] fields = cls.getFields(); // Load the table with the values of all // fields with public and static modifiers. for(int j = 0; j < fields.length; j++) { Field fld = fields[j]; int modifiers = fld.getModifiers(); if(Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers)) { try { Object fieldValue = fld.get(null); table.put(fieldValue, new HintElement(cls, fld)); } catch(Exception e) { // Ignore exception. } } } } // Cache the table. hintTableReference = new SoftReference(table); } return table; } /* * The RenderingHints are serialized by creating a Hashtable of key/value * pairs wherein the keys are instances of the inner class HintElement and * the values are also instances of HintElement or of seriailizable * classes. */ /** * Serialize the RenderingHintsProxy. */ private void writeObject(ObjectOutputStream out) throws IOException { // -- Create a serializable form of the RenderingHints object. -- // Create an empty Hashtable. Hashtable table = new Hashtable(); // If there are hints, add them to the table. if(hints != null && !hints.isEmpty()) { // Get a view of the hints' keys. Set keySet = hints.keySet(); // Proceed if the key set is non-empty. if(!keySet.isEmpty()) { // Get an iterator for the key set. Iterator keyIterator = keySet.iterator(); // Get the cached hint table. Hashtable hintTable = getHintTable(); // Get the suppressed key Vector. Vector suppressedKeys = getSuppressedKeys(); // Loop over the keys. while(keyIterator.hasNext()) { // Get the next key. Object key = keyIterator.next(); // Skip this element if the key is suppressed. if(suppressedKeys != null && suppressedKeys.indexOf(key) != -1) { continue; } // Get the field of the key. HintElement keyElement = (HintElement)hintTable.get(key); // If the key was not in the table it is not a public // static field in one of the KEY_CLASSES so skip it. if(keyElement == null) { continue; } // Get the next value. Object value = hints.get(key); // Pack the key/value pair in a Hashtable entry. HintElement valueElement = null; try { // Try to create a HintElement from the value directly. valueElement = new HintElement(value); } catch(NotSerializableException nse) { // The value is not serializable so try to get a // HintElement from the table in case the value is // a public static field in one of the KEY_CLASSES. valueElement = (HintElement)hintTable.get(value); } // If the value element is valid add it and its key. if(valueElement != null) { table.put(keyElement, valueElement); } } } } // Write serialized form to the stream. out.writeObject(table); } /** * Deserialize the RenderingHintsProxy. */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { // Read serialized form from the stream. Hashtable table = (Hashtable)in.readObject(); // Create an empty RenderingHints object. hints = new RenderingHints(null); // If the table is empty just return. if(table.isEmpty()) { return; } // -- Restore the transient RenderingHints object. -- // Get an enumeration of the table keys. Enumeration keys = table.keys(); // Loop over the table keys. while(keys.hasMoreElements()) { // Get the next key element. HintElement keyElement = (HintElement)keys.nextElement(); // Get the key object corresponding to this key element. Object key = keyElement.getObject(); // Get the value element. HintElement valueElement = (HintElement)table.get(keyElement); // Get the value object corresponding to this value element. Object value = valueElement.getObject(); // Add an entry to the hints. hints.put(key, value); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/HashtableState.java0000644000175000017500000000777310203035544026153 0ustar mathieumathieu/* * $RCSfile: HashtableState.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:50 $ * $State: Exp $ */ package com.sun.media.jai.rmi; import java.awt.RenderingHints; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import java.util.Set; import javax.media.jai.JAI; import javax.media.jai.remote.SerializerFactory; import javax.media.jai.remote.SerializableState; /** * This class is a serializable proxy for a Hashtable object. *
    (entries which are neither Serializable nor supported by * SerializerFactory are omitted); * * * @since 1.1 */ public class HashtableState extends SerializableStateImpl { /** * Returns the classes supported by this SerializableState. */ public static Class[] getSupportedClasses() { return new Class[] {Hashtable.class}; } /** * Constructs a HashtableState from a * Hashtable object. * * @param c The Class of the object to be serialized. * @param o The Hashtable object to be serialized. * @param h The RenderingHints for this serialization. */ public HashtableState(Class c, Object o, RenderingHints h) { super(c, o, h); } /** * Serialize the HashtableState. */ private void writeObject(ObjectOutputStream out) throws IOException { // -- Create a serializable form of the Hashtable object. -- Hashtable table = (Hashtable)theObject; Hashtable serializableTable = new Hashtable(); // If there are hints, add them to the table. if (table != null && !table.isEmpty()) { // Get a view of the hints' keys. Set keySet = table.keySet(); // Proceed if the key set is non-empty. if (!keySet.isEmpty()) { // Get an iterator for the key set. Iterator keyIterator = keySet.iterator(); // Loop over the keys. while (keyIterator.hasNext()) { // Get the next key. Object key = keyIterator.next(); Object serializableKey = getSerializableForm(key); if (serializableKey == null) continue; // Get the next value. Object value = table.get(key); Object serializableValue = getSerializableForm(value); if (serializableValue == null) continue; serializableTable.put(serializableKey, serializableValue); } } } // Write serialized form to the stream. out.writeObject(serializableTable); } /** * Deserialize the HashtableState. */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { // Read serialized form from the stream. Hashtable serializableTable = (Hashtable)in.readObject(); // Create an empty Hashtable object. Hashtable table = new Hashtable(); theObject = table; // If the table is empty just return. if (serializableTable.isEmpty()) { return; } // Get an enumeration of the table keys. Enumeration keys = serializableTable.keys(); // Loop over the table keys. while (keys.hasMoreElements()) { // Get the next key element. Object serializableKey = keys.nextElement(); Object key = getDeserializedFrom(serializableKey); // Get the value element. Object serializableValue = serializableTable.get(serializableKey); Object value = getDeserializedFrom(serializableValue); // Add an entry to the table. table.put(key, value); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/SerializerImpl.java0000644000175000017500000001765010203035544026205 0ustar mathieumathieu/* * $RCSfile: SerializerImpl.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:55 $ * $State: Exp $ */ package com.sun.media.jai.rmi; import java.awt.RenderingHints; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import javax.media.jai.remote.RemoteImagingException; import javax.media.jai.remote.SerializableState; import javax.media.jai.remote.Serializer; import javax.media.jai.remote.SerializerFactory; import javax.media.jai.util.ImagingException; import javax.media.jai.util.ImagingListener; import com.sun.media.jai.util.ImageUtil; /** * Framework class for automatically creating Serializers * for SerializableStateImpl subclasses. Each subclass of * SerializableStateImpl should add a statement like *

     *       registerSerializers(MySerializableState.class);
     * 
    * to the no-argument version of registerSerializers(). * This latter method is invoked by the static initializer of * SerializerFactory. * * @since 1.1 */ public final class SerializerImpl implements Serializer { private Class theClass; private boolean areSubclassesPermitted; private Constructor ctor; /** * Registers all known Serializers with the * SerializerFactory. */ public static final void registerSerializers() { registerSerializers(ColorModelState.class); registerSerializers(DataBufferState.class); registerSerializers(HashSetState.class); registerSerializers(HashtableState.class); registerSerializers(RasterState.class); registerSerializers(RenderedImageState.class); registerSerializers(RenderContextState.class); registerSerializers(RenderingHintsState.class); registerSerializers(RenderingKeyState.class); registerSerializers(SampleModelState.class); registerSerializers(VectorState.class); registerSerializers(ShapeState.class); } private static void registerSerializers(Class ssi) { if(ssi == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if(!SerializableStateImpl.class.isAssignableFrom(ssi)) { throw new IllegalArgumentException(JaiI18N.getString("SerializerImpl0")); } ImagingListener listener = ImageUtil.getImagingListener((RenderingHints)null); Class[] classes = null; try { Method m1 = ssi.getMethod("getSupportedClasses", null); classes = (Class[])m1.invoke(null, null); } catch(java.lang.NoSuchMethodException e) { String message = JaiI18N.getString("SerializerImpl1"); listener.errorOccurred(message, new RemoteImagingException(message, e), SerializerImpl.class, false); } catch (java.lang.IllegalAccessException e) { String message = JaiI18N.getString("SerializerImpl1"); listener.errorOccurred(message, new RemoteImagingException(message, e), SerializerImpl.class, false); } catch (java.lang.reflect.InvocationTargetException e) { String message = JaiI18N.getString("SerializerImpl1"); listener.errorOccurred(message, new RemoteImagingException(message, e), SerializerImpl.class, false); } boolean supportsSubclasses = false; try { Method m2 = ssi.getMethod("permitsSubclasses", null); Boolean b = (Boolean)m2.invoke(null, null); supportsSubclasses = b.booleanValue(); } catch(java.lang.NoSuchMethodException e) { String message = JaiI18N.getString("SerializerImpl4"); listener.errorOccurred(message, new RemoteImagingException(message, e), SerializerImpl.class, false); } catch (java.lang.IllegalAccessException e) { String message = JaiI18N.getString("SerializerImpl4"); listener.errorOccurred(message, new RemoteImagingException(message, e), SerializerImpl.class, false); } catch (java.lang.reflect.InvocationTargetException e) { String message = JaiI18N.getString("SerializerImpl4"); listener.errorOccurred(message, new RemoteImagingException(message, e), SerializerImpl.class, false); } int numClasses = classes.length; for(int i = 0; i < numClasses; i++) { Serializer s = new SerializerImpl(ssi, classes[i], supportsSubclasses); SerializerFactory.registerSerializer(s); } } /** * Constructs a SerializerImpl. The parameter c * is saved by reference. The parameter c is used to * determine the standard SerializableStateImpl constructor * which is saved by reference. The supplied parameters are not checked * as this class should never be instantiated except from within * registerSerializers(Class). */ protected SerializerImpl(Class ssi, // SerializableStateImpl subclass Class c, boolean areSubclassesPermitted) { theClass = c; this.areSubclassesPermitted = areSubclassesPermitted; try { Class[] paramTypes = new Class[] {Class.class, Object.class, RenderingHints.class}; ctor = ssi.getConstructor(paramTypes); } catch(java.lang.NoSuchMethodException e) { String message = theClass.getName()+": "+ JaiI18N.getString("SerializerImpl2"); sendExceptionToListener(message, new RemoteImagingException(message, e)); } } /** * Creates a SerializableState using the * SerializableStateImpl subclass constructor obtained * by reflection. */ public SerializableState getState(Object o, RenderingHints h) { Object state = null; try { state = ctor.newInstance(new Object[] {theClass, o, h}); } catch(InstantiationException e) { String message = theClass.getName()+": "+ JaiI18N.getString("SerializerImpl3"); sendExceptionToListener(message, new RemoteImagingException(message, e)); } catch (IllegalAccessException e) { String message = theClass.getName()+": "+ JaiI18N.getString("SerializerImpl3"); sendExceptionToListener(message, new RemoteImagingException(message, e)); } catch (java.lang.reflect.InvocationTargetException e) { String message = theClass.getName()+": "+ JaiI18N.getString("SerializerImpl3"); sendExceptionToListener(message, new RemoteImagingException(message, e)); } return (SerializableState)state; } public Class getSupportedClass() { return theClass; } public boolean permitsSubclasses() { return areSubclassesPermitted; } private void sendExceptionToListener(String message, Exception e) { ImagingListener listener = ImageUtil.getImagingListener((RenderingHints)null); listener.errorOccurred(message, new ImagingException(message, e), this, false); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/ShapeState.java0000644000175000017500000003165210203035544025311 0ustar mathieumathieu/* * $RCSfile: ShapeState.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:55 $ * $State: Exp $ */ package com.sun.media.jai.rmi; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.geom.Area; import java.awt.geom.Arc2D; import java.awt.geom.CubicCurve2D; import java.awt.geom.Ellipse2D; import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; import java.awt.geom.QuadCurve2D; import java.awt.geom.Rectangle2D; import java.awt.geom.RoundRectangle2D; import java.awt.geom.AffineTransform; import java.awt.geom.PathIterator; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import javax.media.jai.remote.SerializableState; import javax.media.jai.remote.SerializerFactory; /** * This class is a serializable proxy for a Shape. * * * @since 1.1 */ public class ShapeState extends SerializableStateImpl { private final static int SHAPE_UNKNOWN = 0; private final static int SHAPE_AREA = 1; private final static int SHAPE_ARC_DOUBLE = 2; private final static int SHAPE_ARC_FLOAT = 3; private final static int SHAPE_CUBICCURVE_DOUBLE= 4; private final static int SHAPE_CUBICCURVE_FLOAT = 5; private final static int SHAPE_ELLIPSE_DOUBLE = 6; private final static int SHAPE_ELLIPSE_FLOAT = 7; private final static int SHAPE_GENERALPATH = 8; private final static int SHAPE_LINE_DOUBLE = 9; private final static int SHAPE_LINE_FLOAT = 10; private final static int SHAPE_QUADCURVE_DOUBLE = 11; private final static int SHAPE_QUADCURVE_FLOAT = 12; private final static int SHAPE_ROUNDRECTANGLE_DOUBLE = 13; private final static int SHAPE_ROUNDRECTANGLE_FLOAT = 14; private final static int SHAPE_RECTANGLE_DOUBLE = 15; private final static int SHAPE_RECTANGLE_FLOAT = 16; public static Class[] getSupportedClasses() { return new Class[] {Shape.class}; } /** * Constructs a ShapeState from a * Shape. * * @param c The class of the object to be serialized. * @param o The Shape to be serialized. * @param h The RenderingHints (ignored). */ public ShapeState(Class c, Object o, RenderingHints h) { super(c, o, h); } /** * Serialize the ShapeState. * * @param out The ObjectOutputStream. */ private void writeObject(ObjectOutputStream out) throws IOException { boolean serializable = false; Object object = theObject; // if the specific Shape is Serializable, then write itself; // if the specific Shape has a proxy itself, use that proxy; // for the regular Shapes, such as Arc, Ellipse and etc, write // the parameters; // for an Area, write the path and recover the Area; // for the instance of GeneralPath or unknown Shape, write the path; // if (object instanceof Serializable) serializable = true; // deal with Serializable Shape such as Polygon and Rectangle or // the Shape has its own proxy. // out.writeBoolean(serializable); if (serializable) { out.writeObject(object); return; } Object dataArray = null; Object otherData = null; int type = SHAPE_UNKNOWN; // deal with the regular Shapes if (theObject instanceof Area) type = SHAPE_AREA; else if (theObject instanceof Arc2D.Double) { Arc2D.Double ad = (Arc2D.Double)theObject; dataArray = new double[]{ad.x, ad.y, ad.width, ad.height, ad.start, ad.extent}; type = SHAPE_ARC_DOUBLE; otherData = new Integer(ad.getArcType()); } else if (theObject instanceof Arc2D.Float) { Arc2D.Float af = (Arc2D.Float)theObject; dataArray = new float[]{af.x, af.y, af.width, af.height, af.start, af.extent}; type = SHAPE_ARC_FLOAT; otherData = new Integer(af.getArcType()); } else if (theObject instanceof CubicCurve2D.Double) { CubicCurve2D.Double cd = (CubicCurve2D.Double)theObject; dataArray = new double[]{cd.x1, cd.y1, cd.ctrlx1, cd.ctrly1, cd.ctrlx2, cd.ctrly2, cd.x2, cd.y2}; type = SHAPE_CUBICCURVE_DOUBLE; } else if (theObject instanceof CubicCurve2D.Float) { CubicCurve2D.Float cf = (CubicCurve2D.Float)theObject; dataArray = new float[]{cf.x1, cf.y1, cf.ctrlx1, cf.ctrly1, cf.ctrlx2, cf.ctrly2, cf.x2, cf.y2}; type = SHAPE_CUBICCURVE_FLOAT; } else if (theObject instanceof Ellipse2D.Double) { Ellipse2D.Double ed = (Ellipse2D.Double)theObject; dataArray = new double[]{ed.x, ed.y, ed.width, ed.height}; type = SHAPE_ELLIPSE_DOUBLE; } else if (theObject instanceof Ellipse2D.Float) { Ellipse2D.Float ef = (Ellipse2D.Float)theObject; dataArray = new float[]{ef.x, ef.y, ef.width, ef.height}; type = SHAPE_ELLIPSE_FLOAT; } else if (theObject instanceof GeneralPath) type = SHAPE_GENERALPATH; else if (theObject instanceof Line2D.Double) { Line2D.Double ld = (Line2D.Double)theObject; dataArray = new double[]{ld.x1, ld.y1, ld.x2, ld.y2}; type = SHAPE_LINE_DOUBLE; } else if (theObject instanceof Line2D.Float) { Line2D.Float lf = (Line2D.Float)theObject; dataArray = new float[]{lf.x1, lf.y1, lf.x2, lf.y2}; type = SHAPE_LINE_FLOAT; } else if (theObject instanceof QuadCurve2D.Double) { QuadCurve2D.Double qd = (QuadCurve2D.Double)theObject; dataArray = new double[]{qd.x1, qd.y1, qd.ctrlx, qd.ctrly, qd.x2, qd.y2}; type = SHAPE_QUADCURVE_DOUBLE; } else if (theObject instanceof QuadCurve2D.Float) { QuadCurve2D.Float qf = (QuadCurve2D.Float)theObject; dataArray = new float[]{qf.x1, qf.y1, qf.ctrlx, qf.ctrly, qf.x2, qf.y2}; type = SHAPE_QUADCURVE_FLOAT; } else if (theObject instanceof RoundRectangle2D.Double) { RoundRectangle2D.Double rrd = (RoundRectangle2D.Double)theObject; dataArray = new double[]{rrd.x, rrd.y, rrd.width, rrd.height, rrd.arcwidth, rrd.archeight}; type = SHAPE_ROUNDRECTANGLE_DOUBLE; } else if (theObject instanceof RoundRectangle2D.Float) { RoundRectangle2D.Float rrf = (RoundRectangle2D.Float)theObject; dataArray = new float[]{rrf.x, rrf.y, rrf.width, rrf.height, rrf.arcwidth, rrf.archeight}; type = SHAPE_ROUNDRECTANGLE_FLOAT; } else if (theObject instanceof Rectangle2D.Double) { Rectangle2D.Double rd = (Rectangle2D.Double)theObject; dataArray = new double[]{rd.x, rd.y, rd.width, rd.height}; type = SHAPE_RECTANGLE_DOUBLE; } else if (theObject instanceof Rectangle2D.Float) { Rectangle2D.Float rf = (Rectangle2D.Float)theObject; dataArray = new float[]{rf.x, rf.y, rf.width, rf.height}; type = SHAPE_RECTANGLE_FLOAT; } // write Shape belonging to : GenrealPath, Area and unknown classes out.writeInt(type); if (dataArray != null) { out.writeObject(dataArray); if (otherData != null) out.writeObject(otherData); return; } PathIterator pathIterator = ((Shape)theObject).getPathIterator(null); // obtain and write the winding rule int rule = pathIterator.getWindingRule(); out.writeInt(rule); float[] coordinates = new float[6]; // iteratively write isDone, segment type and segment coordinates boolean isDone = pathIterator.isDone(); while (!isDone){ int segmentType = pathIterator.currentSegment(coordinates); out.writeBoolean(isDone); out.writeInt(segmentType); for (int i = 0; i < 6; i++) out.writeFloat(coordinates[i]) ; pathIterator.next(); isDone = pathIterator.isDone(); } out.writeBoolean(isDone); } /** * Deserialize the ShapeState. * * @param out The ObjectInputStream. */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { boolean serializable = in.readBoolean(); // if Serializable or has a specific proxy if (serializable) { theObject = in.readObject(); return; } // read the type of the wrapped Shape int type = in.readInt(); //for regular shapes, read the parameters and recover it switch (type) { case SHAPE_ARC_DOUBLE: { double[] data = (double[])in.readObject(); int arcType = ((Integer)in.readObject()).intValue(); theObject = new Arc2D.Double(data[0], data[1], data[2], data[3], data[4], data[5], arcType); return; } case SHAPE_ARC_FLOAT: { float[] data = (float[])in.readObject(); int arcType = ((Integer)in.readObject()).intValue(); theObject = new Arc2D.Float(data[0], data[1], data[2], data[3], data[4], data[5], arcType); return; } case SHAPE_CUBICCURVE_DOUBLE: { double[] data = (double[])in.readObject(); theObject = new CubicCurve2D.Double(data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]); return; } case SHAPE_CUBICCURVE_FLOAT: { float[] data = (float[])in.readObject(); theObject = new CubicCurve2D.Float(data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]); return; } case SHAPE_ELLIPSE_DOUBLE: { double[] data = (double[])in.readObject(); theObject = new Ellipse2D.Double(data[0], data[1], data[2], data[3]); return; } case SHAPE_ELLIPSE_FLOAT: { float[] data = (float[])in.readObject(); theObject = new Ellipse2D.Float(data[0], data[1], data[2], data[3]); return; } case SHAPE_LINE_DOUBLE: { double[] data = (double[])in.readObject(); theObject = new Line2D.Double(data[0], data[1], data[2], data[3]); return; } case SHAPE_LINE_FLOAT: { float[] data = (float[])in.readObject(); theObject = new Line2D.Float(data[0], data[1], data[2], data[3]); return; } case SHAPE_QUADCURVE_DOUBLE: { double[] data = (double[])in.readObject(); theObject = new QuadCurve2D.Double(data[0], data[1], data[2], data[3], data[4], data[5]); return; } case SHAPE_QUADCURVE_FLOAT: { float[] data = (float[])in.readObject(); theObject = new QuadCurve2D.Float(data[0], data[1], data[2], data[3], data[4], data[5]); return; } case SHAPE_ROUNDRECTANGLE_DOUBLE: { double[] data = (double[])in.readObject(); theObject = new RoundRectangle2D.Double(data[0], data[1], data[2], data[3], data[4], data[5]); return; } case SHAPE_ROUNDRECTANGLE_FLOAT: { float[] data = (float[])in.readObject(); theObject = new RoundRectangle2D.Float(data[0], data[1], data[2], data[3], data[4], data[5]); return; } case SHAPE_RECTANGLE_DOUBLE: { double[] data = (double[])in.readObject(); theObject = new Rectangle2D.Double(data[0], data[1], data[2], data[3]); return; } case SHAPE_RECTANGLE_FLOAT: { float[] data = (float[])in.readObject(); theObject = new Rectangle2D.Float(data[0], data[1], data[2], data[3]); return; } } //read the winding rule int rule = in.readInt(); GeneralPath path = new GeneralPath(rule); float[] coordinates = new float[6]; //read the path while (!in.readBoolean()) { int segmentType = in.readInt(); for (int i = 0; i < 6; i++) coordinates[i] = in.readFloat(); switch (segmentType) { case PathIterator.SEG_MOVETO: path.moveTo(coordinates[0], coordinates[1]); break; case PathIterator.SEG_LINETO: path.lineTo(coordinates[0], coordinates[1]); break; case PathIterator.SEG_QUADTO: path.quadTo(coordinates[0], coordinates[1], coordinates[2], coordinates[3]); break; case PathIterator.SEG_CUBICTO: path.curveTo(coordinates[0], coordinates[1], coordinates[2], coordinates[3], coordinates[4], coordinates[5]); break; case PathIterator.SEG_CLOSE: path.closePath(); break; default: break; } } //recover Area instance switch (type) { case SHAPE_AREA: theObject = new Area(path); break; default: theObject = path; break; } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/InterfaceProxy.java0000644000175000017500000000120510203035544026201 0ustar mathieumathieu/* * $RCSfile: InterfaceProxy.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:51 $ * $State: Exp $ */ package com.sun.media.jai.rmi; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; /** * A class which acts as a proxy for an object which is serialized as an * amalgam of Serializers for the various interfaces that * it implements. * * @since 1.1 */ public final class InterfaceProxy extends Proxy { public InterfaceProxy(InvocationHandler h) { super(h); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/JAIRMICRIF.java0000644000175000017500000006420010203035544024662 0ustar mathieumathieu/* * $RCSfile: JAIRMICRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:51 $ * $State: Exp $ */package com.sun.media.jai.rmi; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.Rectangle2D; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderContext; import java.awt.image.renderable.RenderableImage; import java.rmi.RemoteException; import java.rmi.Naming; import java.net.InetAddress; import java.util.Iterator; import java.util.Vector; import javax.media.jai.JAI; import javax.media.jai.OperationRegistry; import javax.media.jai.OperationNode; import javax.media.jai.PropertyChangeEventJAI; import javax.media.jai.RenderedOp; import javax.media.jai.remote.RemoteCRIF; import javax.media.jai.remote.JAIRMIDescriptor; import javax.media.jai.remote.NegotiableCapabilitySet; import javax.media.jai.remote.PlanarImageServerProxy; import javax.media.jai.remote.RemoteImagingException; import javax.media.jai.remote.RemoteRenderableOp; import javax.media.jai.remote.RemoteRenderedImage; import javax.media.jai.remote.RemoteRenderedOp; import javax.media.jai.remote.SerializableState; import javax.media.jai.remote.SerializerFactory; import javax.media.jai.remote.SerializableRenderedImage; import javax.media.jai.tilecodec.TileDecoderFactory; import javax.media.jai.util.ImagingListener; import com.sun.media.jai.util.ImageUtil; /** * An implementation of the RemoteRIF interface for the * "jairmi" remote imaging protocol. */ public class JAIRMICRIF implements RemoteCRIF { /** * No arg constructor. */ public JAIRMICRIF() { } /** * 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 serverName A String specifying the name of the * server to perform the remote operation on. * @param operationName The String specifying the name of the * operation to be performed remotely. * @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. */ public RenderContext mapRenderContext(String serverName, String operationName, int i, RenderContext renderContext, ParameterBlock paramBlock, RenderableImage image) throws RemoteImagingException { // Create the RenderableOp on the server and it's sources on the // appropriate machines. RemoteRenderableOp rrop = (RemoteRenderableOp)image; RenderableRMIServerProxy rmisp = createProxy(rrop); SerializableState rcs = SerializerFactory.getState(renderContext, null); // Perform the mapRenderContext on the server. try { SerializableState rcpOut = rmisp.getImageServer(serverName).mapRenderContext( i, rmisp.getRMIID(), operationName, rcs); } catch (RemoteException re) { String message = JaiI18N.getString("JAIRMICRIF5"); sendExceptionToListener(renderContext, message, re); // throw new RemoteImagingException(ImageUtil.getStackTraceString(re)); } return (RenderContext)rcs.getObject(); } /** * 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 serverName A String specifying the name of the * server to perform the remote operation on. * @param operationName The String specifying the name of the * operation to be performed remotely. * @param paramBlock A ParameterBlock containing the * operation's sources and parameters. * @return a Rectangle2D specifying the rendering-independent * bounding box of the output. */ public Rectangle2D getBounds2D(String serverName, String operationName, ParameterBlock paramBlock) throws RemoteImagingException { SerializableState bounds = null; // Create a RemoteRenderableOp that represents the operation. RemoteRenderableOp original = new RemoteRenderableOp("jairmi", serverName, operationName, paramBlock); // Create the RenderableOp on the server alongwith creating the // sources on appropriate machines. RenderableRMIServerProxy rmisp = createProxy(original); try { bounds = rmisp.getImageServer(serverName).getBounds2D(rmisp.getRMIID(), operationName); } catch (RemoteException e) { String message = JaiI18N.getString("JAIRMICRIF6"); sendExceptionToListener(null, message, e); // throw new RemoteImagingException(ImageUtil.getStackTraceString(e)); } return (Rectangle2D.Float)bounds.getObject(); } /** * 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. */ public Object getProperty(String serverName, String operationName, ParameterBlock paramBlock, String name) throws RemoteImagingException { ParameterBlock pb = null; if(paramBlock == null){ pb = new ParameterBlock(); } else { pb = (ParameterBlock)paramBlock.clone(); } // Create a RemoteRenderableOp that represents the operation. RemoteRenderableOp original = new RemoteRenderableOp("jairmi", serverName, operationName, paramBlock); // Create the RenderableOp on the server alongwith creating the // sources on appropriate machines. RenderableRMIServerProxy rmisp = createProxy(original); try { return rmisp.getProperty(name); } catch(Exception e) { String message = JaiI18N.getString("JAIRMICRIF7"); sendExceptionToListener(null, message, new RemoteImagingException(message, e)); // throw new RemoteImagingException(ImageUtil.getStackTraceString(e)); } return null; } /** * Returns a list of names recognized by getProperty. */ public String[] getPropertyNames(String serverName, String operationName) throws RemoteImagingException { ImageServer remoteImage = getImageServer(serverName); try { return remoteImage.getPropertyNames(operationName); } catch (RemoteException e){ // Should we be catching Exception or RemoteException String message = JaiI18N.getString("JAIRMICRIF8"); sendExceptionToListener(null, message, new RemoteImagingException(message, e)); // throw new RemoteImagingException(ImageUtil.getStackTraceString(e)); } return null; } private ImageServer getImageServer(String serverName){ if (serverName == null) { try { serverName = InetAddress.getLocalHost().getHostAddress(); } catch(java.net.UnknownHostException e) { String message = JaiI18N.getString("RMIServerProxy11"); sendExceptionToListener(null, message, new RemoteImagingException(message, e)); // throw new RuntimeException(e.getMessage()); } } // Derive the service name. String serviceName = new String("rmi://"+serverName+"/"+ JAIRMIDescriptor.IMAGE_SERVER_BIND_NAME); // Look up the remote object. try { return (ImageServer)Naming.lookup(serviceName); } catch(java.rmi.NotBoundException e) { String message = JaiI18N.getString("RMIServerProxy12"); sendExceptionToListener(null, message, new RemoteImagingException(message, e)); // throw new RemoteImagingException(ImageUtil.getStackTraceString(e)); } catch (java.net.MalformedURLException e) { String message = JaiI18N.getString("RMIServerProxy12"); sendExceptionToListener(null, message, new RemoteImagingException(message, e)); } catch (java.rmi.RemoteException e) { String message = JaiI18N.getString("RMIServerProxy12"); sendExceptionToListener(null, message, new RemoteImagingException(message, e)); } return null; } /** * 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. */ public boolean isDynamic(String serverName, String operationName) throws RemoteImagingException { ImageServer remoteImage = getImageServer(serverName); try { return remoteImage.isDynamic(operationName); } catch (RemoteException e){ String message = JaiI18N.getString("JAIRMICRIF9"); sendExceptionToListener(null, message, new RemoteImagingException(message, e)); // throw new RemoteImagingException(ImageUtil.getStackTraceString(e)); } return true; } /** * Creates a RemoteRenderedImage representing the results * of an imaging operation (or chain of operations) for a given * ParameterBlock and RenderingHints. The * RemoteRIF 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 * RemoteRIF (representing the server) is not capable of * producing output for the given set of source images and parameters. * For example, if a server (represented by a RemoteRIF) 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 RemoteRenderedImage 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 RemoteRenderedImage may have such * a property. * * @param serverName A String specifying the name of the * server to perform the remote operation on. * @param operationName The String specifying the name of the * operation to be performed remotely. * @param paramBlock A ParameterBlock containing sources * and parameters for the * RemoteRenderedImage to be created. * @param hints A RenderingHints object containing * hints. * @return A RemoteRenderedImage containing the desired * output. */ public RemoteRenderedImage create(String serverName, String operationName, ParameterBlock paramBlock, RenderingHints hints) throws RemoteImagingException { // Create the RenderedOp on the server RMIServerProxy rmisp = new RMIServerProxy(serverName, operationName, paramBlock, hints); // Check whether there is a RIF on the server that can satisfy this // rendering request. boolean cbr = rmisp.canBeRendered(); if (!cbr){ return null; } else { return rmisp; } } /** * Creates a RemoteRenderedImage representing the results * of an imaging operation, whose given old rendering is updated * according to the given PropertyChangeEventJAI. This * factory method should be used to create a new rendering updated * according to the changes reported by the given * PropertyChangeEventJAI. The RemoteRIF * can query the supplied PlanarImageServerProxy for * references to the server name, operation name, parameter block, * and rendering hints. The RemoteRIF 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 * RemoteRIF (representing the server) is not capable of * producing output for the given set of source images and parameters. * For example, if a server (represented by a RemoteRIF) 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 RemoteRenderedImage 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 RemoteRenderedImage may have such * a property. * * @param oldRendering The old rendering of the imaging operation. * @param event An event that specifies the changes made to the * imaging operation. * @return A RemoteRenderedImage containing the desired * output. */ public RemoteRenderedImage create(PlanarImageServerProxy oldRendering, OperationNode node, PropertyChangeEventJAI event) throws RemoteImagingException { if (!(node instanceof RemoteRenderedOp)) return null; String propName = event.getPropertyName(); RMIServerProxy rmisp; if (propName.equals("servername")) { rmisp = new RMIServerProxy(oldRendering, node, (String)event.getNewValue()); } else if (propName.equals("operationregistry") || propName.equals("protocolname") || propName.equals("protocolandservername")) { return create(((RemoteRenderedOp)node).getServerName(), node.getOperationName(), node.getParameterBlock(), node.getRenderingHints()); } else { rmisp = new RMIServerProxy(oldRendering, node, event); } return rmisp; } /** * Creates a rendering, given a RenderContext and a * ParameterBlock containing the operation's sources * and parameters. The output is a RemoteRenderedImage * 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 serverName A String specifying the name of the * server to perform the remote operation on. * @param operationName The String specifying the name of the * operation to be performed remotely. * @param renderContext The RenderContext specifying the * rendering context. * @param paramBlock A ParameterBlock containing the * operation's sources and parameters. */ public RemoteRenderedImage create(String serverName, String operationName, RenderContext renderContext, ParameterBlock paramBlock) throws RemoteImagingException { // Create the RenderableOp on the server. RMIServerProxy rmisp = new RMIServerProxy(serverName, operationName, paramBlock, renderContext, true); // Cause a rendering to take place, so we can return a RenderedImage // since we can't return a RenderableOp. Long renderingID = rmisp.getRenderingID(); // Return an RMIServerProxy that is a link to the rendering on the // server. return new RMIServerProxy((serverName+"::"+renderingID), paramBlock, operationName, renderContext.getRenderingHints()); } /** * Returns the set of capabilities supported by the client object. */ public NegotiableCapabilitySet getClientCapabilities() { OperationRegistry registry = JAI.getDefaultInstance().getOperationRegistry(); String modeName = "tileDecoder"; String[] descriptorNames = registry.getDescriptorNames(modeName); TileDecoderFactory tdf = null; // Only non-preference NegotiableCapability objects can be added // to this NCS, which is ok, since these represent the capabilities // of the client and thus are not preferences as much as they are // hard capabilities (i.e. non-preferences) NegotiableCapabilitySet capabilities = new NegotiableCapabilitySet(false); // Note that tileEncoder capabilities cannot be added since there is // no way to differentiate between a encoding and a decoding capability // within an NCS, and since the client should only be expected to // decode, this should be ok. Iterator it; for (int i=0; iRenderableRMIServerProxy that represents * a remote Renderable operation. This method examines the operation's * sources recursively and creates them as needed. */ private RenderableRMIServerProxy createProxy(RemoteRenderableOp rop) { ParameterBlock oldPB = rop.getParameterBlock(); ParameterBlock newPB = (ParameterBlock)oldPB.clone(); Vector sources = oldPB.getSources(); newPB.removeSources(); // Create a new RenderableOp on the server ImageServer remoteImage = getImageServer(rop.getServerName()); ImagingListener listener = ImageUtil.getImagingListener(rop.getRenderingHints()); Long opID = new Long(0L); try { opID = remoteImage.getRemoteID(); remoteImage.createRenderableOp(opID, rop.getOperationName(), newPB); } catch (RemoteException e){ String message = JaiI18N.getString("RMIServerProxy8"); listener.errorOccurred(message, new RemoteImagingException(message, e), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(e)); } // Now look at the sources of the RenderableOp if (sources != null) { for (int i = 0; i < sources.size(); i++) { Object source = sources.elementAt(i); if (source instanceof RemoteRenderedOp) { // If source is a RenderedOp on a remote machine, create // it on the remote machine. RMIServerProxy rmisp = (RMIServerProxy)(((RemoteRenderedOp)source).getRendering()); try { if (rmisp.getServerName().equalsIgnoreCase( rop.getServerName())) { // Both the RenderableOp and this source are on // the same server. remoteImage.setRenderedSource(opID, rmisp.getRMIID(), i); newPB.setSource(rmisp, i); } else { // The RenderableOp and this source are on // different servers. remoteImage.setRenderedSource( opID, rmisp.getRMIID(), rmisp.getServerName(), rmisp.getOperationName(), i); newPB.setSource(rmisp, i); } } catch (RemoteException e) { String message = JaiI18N.getString("RMIServerProxy6"); listener.errorOccurred(message, new RemoteImagingException(message, e), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(e)); } } else if (source instanceof RenderedOp) { // If the source is a local RenderedOp, then the only way // to access it from a remote machine is to render it and // create a SerializableRenderedImage wrapping the // rendering, which is set as the source of the remote op. RenderedImage ri = ((RenderedOp)source).getRendering(); try { SerializableRenderedImage sri = new SerializableRenderedImage(ri); remoteImage.setRenderedSource(opID, sri, i); newPB.setSource(sri, i); } catch (RemoteException e) { String message = JaiI18N.getString("RMIServerProxy6"); listener.errorOccurred(message, new RemoteImagingException(message, e), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(e)); } } else if (source instanceof RenderedImage) { // If the source is a local RenderedImage, then the only // way to access it from a remote machine is by wrapping // it in SerializableRenderedImage and then setting the // SRI as the source. RenderedImage ri = (RenderedImage)source; try { SerializableRenderedImage sri = new SerializableRenderedImage(ri); remoteImage.setRenderedSource(opID, sri, i); newPB.setSource(sri, i); } catch (RemoteException e) { String message = JaiI18N.getString("RMIServerProxy6"); listener.errorOccurred(message, new RemoteImagingException(message, e), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(e)); } } else if (source instanceof RemoteRenderableOp) { // If the source is a RenderableOp on a remote machine, // cause the RenderableOp to be created on the remote // machine. RenderableRMIServerProxy rrmisp = createProxy((RemoteRenderableOp)source); try { // If the source RenderableOp is on the same server // as the RenderableOp that represents the operation if (rrmisp.getServerName().equalsIgnoreCase( rop.getServerName())) { remoteImage.setRenderableSource(opID, rrmisp.getRMIID(), i); newPB.setSource(rrmisp, i); } else { // If the source RenderableOp is on a different // server than the RenderableOp that represents // the operation remoteImage.setRenderableRMIServerProxyAsSource( opID, rrmisp.getRMIID(), rrmisp.getServerName(), rrmisp.getOperationName(), i); newPB.setSource(rrmisp, i); } } catch (RemoteException e) { String message = JaiI18N.getString("RMIServerProxy6"); listener.errorOccurred(message, new RemoteImagingException(message, e), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(e)); } } else if (source instanceof RenderableImage) { // If the source is a local RenderableImage, the only way // to access it from a remote machine is to wrap it in // a SerializableRenderableImage and then set the SRI as // the source on the remote operation. RenderableImage ri = (RenderableImage)source; try { SerializableRenderableImage sri = new SerializableRenderableImage(ri); remoteImage.setRenderableSource(opID, sri, i); newPB.setSource(sri, i); } catch (RemoteException e) { String message = JaiI18N.getString("RMIServerProxy6"); listener.errorOccurred(message, new RemoteImagingException(message, e), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(e)); } } } } // Create a new RMIServerProxy to access the RenderableOp // created at the beginning of this method. RenderableRMIServerProxy finalRmisp = new RenderableRMIServerProxy(rop.getServerName(), rop.getOperationName(), newPB, opID); return finalRmisp; } private void sendExceptionToListener(RenderContext renderContext, String message, Exception e) { ImagingListener listener = ImageUtil.getImagingListener(renderContext); listener.errorOccurred(message, new RemoteImagingException(message, e), this, false); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/InterfaceState.java0000644000175000017500000001235410203035544026147 0ustar mathieumathieu/* * $RCSfile: InterfaceState.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:51 $ * $State: Exp $ */ package com.sun.media.jai.rmi; import java.awt.RenderingHints; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Hashtable; import javax.media.jai.JAI; import javax.media.jai.remote.SerializableState; import javax.media.jai.remote.Serializer; import javax.media.jai.remote.SerializerFactory; /** * Class enabling serialization of an object which implements multiple * interfacs supported by SerializerFactory. * * @since 1.1 */ public class InterfaceState implements SerializableState { // The object before serialization, the Proxy afterwards. private transient Object theObject; private transient Serializer[] theSerializers; // Not deserialized. private transient RenderingHints hints; // Not deserialized. public InterfaceState(Object o, Serializer[] serializers, RenderingHints h) { if(o == null || serializers == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } theObject = o; theSerializers = serializers; hints = h == null ? null : (RenderingHints)h.clone(); } public Object getObject() { return theObject; } public Class getObjectClass() { return theObject.getClass(); } /** * Serialize the InterfaceState. * * @param out The ObjectOutputStream. */ private void writeObject(ObjectOutputStream out) throws IOException { int numSerializers = theSerializers.length; out.writeInt(numSerializers); for(int i = 0; i < numSerializers; i++) { Serializer s = theSerializers[i]; out.writeObject(s.getSupportedClass()); out.writeObject(s.getState(theObject, hints)); } } /** * Deserialize the InterfaceState. * * @param out The ObjectInputStream. */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { int numInterfaces = in.readInt(); Class[] interfaces = new Class[numInterfaces]; SerializableState[] implementations = new SerializableState[numInterfaces]; for(int i = 0; i < numInterfaces; i++) { interfaces[i] = (Class)in.readObject(); implementations[i] = (SerializableState)in.readObject(); } InvocationHandler handler = new InterfaceHandler(interfaces, implementations); theObject = Proxy.newProxyInstance(JAI.class.getClassLoader(), interfaces, handler); } } class InterfaceHandler implements InvocationHandler { private Hashtable interfaceMap; public InterfaceHandler(Class[] interfaces, SerializableState[] implementations) { if(interfaces == null || implementations == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } else if(interfaces.length != implementations.length) { throw new IllegalArgumentException(JaiI18N.getString("InterfaceHandler0")); } int numInterfaces = interfaces.length; interfaceMap = new Hashtable(numInterfaces); for(int i = 0; i < numInterfaces; i++) { Class iface = interfaces[i]; SerializableState state = implementations[i]; if(!iface.isAssignableFrom(state.getObjectClass())) { throw new RuntimeException(JaiI18N.getString("InterfaceHandler1")); } Object impl = state.getObject(); interfaceMap.put(iface, impl); } } public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException, InvocationTargetException { Class key = method.getDeclaringClass(); if(!interfaceMap.containsKey(key)) { Class[] classes = (Class[])interfaceMap.keySet().toArray(new Class[0]); for(int i = 0; i < classes.length; i++) { Class aClass = classes[i]; if(key.isAssignableFrom(aClass)) { interfaceMap.put(key, interfaceMap.get(aClass)); break; } } if(!interfaceMap.containsKey(key)) { throw new RuntimeException(key.getName()+ JaiI18N.getString("InterfaceHandler2")); } } Object result= null; try { Object impl = interfaceMap.get(key); result = method.invoke(impl, args); } catch(IllegalAccessException e) { throw new RuntimeException(method.getName()+ JaiI18N.getString("InterfaceHandler3")); } return result; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/ImageServer.java0000644000175000017500000005121110203035544025452 0ustar mathieumathieu/* * $RCSfile: ImageServer.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:51 $ * $State: Exp $ */ package com.sun.media.jai.rmi; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderContext; import java.rmi.Remote; import java.rmi.RemoteException; import java.util.List; import java.util.Vector; import javax.media.jai.PropertyChangeEventJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.remote.NegotiableCapabilitySet; import javax.media.jai.remote.SerializableState; /** * An interface for server-side imaging. This interface attempts to * mimic the RenderedImage interface as much as possible. However, there * are several unavoidable differences: * *

      *
    • Additional setRenderedSource() and setRenderableSource methods * are provided to inform the server as to the source of image data for * this image. Sources may be set * either from a RenderedImage that is copied over to the server, or * from a graph of RenderedOp objects indicating an abstract * imaging chain to be instantiated using the server's * OperationRegistry. * *
    • All methods throw RemoteException. This is a requirement of * any Remote interface. * *
    • The getTile() method does not return a reference to a `live' * tile; instead it returns a client-side copy of the server image's * tile. The difference is moot since the server image is immutable. *
    * * To instantiate a ImageServer, do the following: * *
     * ImageServer im;
     * im = java.rmi.Naming.lookup("//host:1099/javax.media.jai.RemoteImageServer");
     * 
    * *

    The hostname and port will of course depend on the local setup. * The host must be running an rmiregistry process and have a * RemoteImageServer listening at the desired port. * *

    This call will result in the creation of a server-side * JAIRMIImageServer object and a client-side stub object. * The client stub serializes its method arguments and transfers * them to the server over a socket; the server serializes it return * values and returns them in the same manner. * *

    This process implies that all arguments and return values must * be serializable. In the case of a RenderedImage source, * serializability is not guaranteed and must be considered on a * class-by-class basis. For RenderedOps, which are basically * simple nodes connected by ParameterBlocks, serializability will be * determined by the serializabiility of the ultimate * (non-RenderedOp) sources of the DAG and the serializability * of any ad-hoc Object parameters held in the ParameterBlocks. * *

    The return values of the getData(), copyData(), and getTile() * methods are various kinds of Rasters; at present, Java2D does not * define serialization on Rasters. We will either need to add this * feature to Java2D or else coerce the server-side Rasters into a * serializable subclass form. In any case, we will want to * implement lossless (and possibly lossy) compression as part of * the serialization process wherever possible. * * @see java.rmi.Remote * @see java.rmi.RemoteException * @see java.awt.image.RenderedImage * * */ public interface ImageServer extends Remote { /** * Returns the identifier of the remote image. This method should be * called to return an identifier before any other methods are invoked. * The same ID must be used in all subsequent references to the remote * image. */ Long getRemoteID() throws RemoteException; /** * Disposes of any resouces allocated to the client object with * the specified ID. */ void dispose(Long id) throws RemoteException; /** * Increments the reference count for this id, i.e. increments the * number of RMIServerProxy objects that currently reference this id. */ void incrementRefCount(Long id) throws RemoteException; /// Methods Common To Rendered as well as Renderable modes. /** * 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 id An ID for the source which must be unique across all clients. * @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(Long id, String name) throws RemoteException; /** * Returns a list of names recognized by getProperty(String). * * @return an array of Strings representing proeprty names. */ String [] getPropertyNames(Long id) throws RemoteException; /** * Returns a list of names recognized by getProperty(). * * @return an array of Strings representing property names. */ String[] getPropertyNames(String opName) throws RemoteException; /// Rendered Mode Methods /** Returns the ColorModel associated with this image. */ SerializableState getColorModel(Long id) throws RemoteException; /** Returns the SampleModel associated with this image. */ SerializableState getSampleModel(Long id) throws RemoteException; /** Returns the width of the image on the ImageServer. */ int getWidth(Long id) throws RemoteException; /** Returns the height of the image on the ImageServer. */ int getHeight(Long id) throws RemoteException; /** * Returns the minimum X coordinate of the image on the ImageServer. */ int getMinX(Long id) throws RemoteException; /** * Returns the minimum Y coordinate of the image on the ImageServer. */ int getMinY(Long id) throws RemoteException; /** Returns the number of tiles across the image. */ int getNumXTiles(Long id) throws RemoteException; /** Returns the number of tiles down the image. */ int getNumYTiles(Long id) throws RemoteException; /** * Returns the index of the minimum tile in the X direction of the image. */ int getMinTileX(Long id) throws RemoteException; /** * Returns the index of the minimum tile in the Y direction of the image. */ int getMinTileY(Long id) throws RemoteException; /** Returns the width of a tile in pixels. */ int getTileWidth(Long id) throws RemoteException; /** Returns the height of a tile in pixels. */ int getTileHeight(Long id) throws RemoteException; /** Returns the X offset of the tile grid relative to the origin. */ int getTileGridXOffset(Long id) throws RemoteException; /** Returns the Y offset of the tile grid relative to the origin. */ int getTileGridYOffset(Long id) throws RemoteException; /** * Returns tile (x, y). Note that x and y are indices into the * tile array, not pixel locations. Unlike in the true RenderedImage * interface, the Raster that is returned should be considered a copy. * * @param id An ID for the source which must be unique across all clients. * @param x the x index of the requested tile in the tile array * @param y the y index of the requested tile in the tile array * @return a copy of the tile as a Raster. */ SerializableState getTile(Long id, int x, int y) throws RemoteException; /** * Compresses tile (x, y) and returns the compressed tile's contents * as a byte array. Note that x and y are indices into the * tile array, not pixel locations. * * @param id An ID for the source which must be unique across all clients. * @param x the x index of the requested tile in the tile array * @param y the y index of the requested tile in the tile array * @return a byte array containing the compressed tile contents. */ byte[] getCompressedTile(Long id, int x, int y) throws RemoteException; /** * Returns the entire image as a single Raster. * * @return a SerializableState containing a copy of this image's data. */ SerializableState getData(Long id) throws RemoteException; /** * Returns an arbitrary rectangular region of the RenderedImage * in a Raster. The rectangle of interest will be clipped against * the image bounds. * * @param id An ID for the source which must be unique across all clients. * @param bounds the region of the RenderedImage to be returned. * @return a SerializableState containing a copy of the desired data. */ SerializableState getData(Long id, Rectangle bounds) throws RemoteException; /** * Returns the same result as getData(Rectangle) would for the * same rectangular region. */ SerializableState copyData(Long id, Rectangle bounds) throws RemoteException; /** * Creates a RenderedOp on the server side with a parameter block * empty of sources. The sources are set by separate calls depending * upon the type and serializabilty of the source. */ void createRenderedOp(Long id, String opName, ParameterBlock pb, SerializableState hints) throws RemoteException; /** * Calls for Rendering of the Op and returns true if the RenderedOp * could be rendered else false */ boolean getRendering(Long id) throws RemoteException; /** * Retrieve a node from the hashtable. */ RenderedOp getNode(Long id) throws RemoteException; /** * Sets the source of the image as a RenderedImage on the server side */ void setRenderedSource(Long id, RenderedImage source, int index) throws RemoteException; /** * Sets the source of the image as a RenderedOp on the server side */ void setRenderedSource(Long id, RenderedOp source, int index) throws RemoteException; /** * Sets the source of the image which is on the same * server */ void setRenderedSource(Long id, Long sourceId, int index) throws RemoteException; /** * Sets the source of the image which is on a different * server */ void setRenderedSource(Long id, Long sourceId, String serverName, String opName, int index) throws RemoteException; /// Renderable mode methods /** * Gets the minimum X coordinate of the rendering-independent image * stored against the given ID. * * @return the minimum X coordinate of the rendering-independent image * data. */ float getRenderableMinX(Long id) throws RemoteException; /** * Gets the minimum Y coordinate of the rendering-independent image * stored against the given ID. * * @return the minimum X coordinate of the rendering-independent image * data. */ float getRenderableMinY(Long id) throws RemoteException; /** * Gets the width (in user coordinate space) of the * RenderableImage stored against the given ID. * * @return the width of the renderable image in user coordinates. */ float getRenderableWidth(Long id) throws RemoteException; /** * Gets the height (in user coordinate space) of the * RenderableImage stored against the given ID. * * @return the height of the renderable image in user coordinates. */ float getRenderableHeight(Long id) throws RemoteException; /** * 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(Long id, int w, int h, SerializableState hintsState) throws RemoteException; /** * 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(Long id) throws RemoteException; /** * 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(Long id, SerializableState renderContextState) throws RemoteException; /** * Creates a RenderableOp on the server side with a parameter block * empty of sources. The sources are set by separate calls depending * upon the type and serializabilty of the source. */ void createRenderableOp(Long id, String opName, ParameterBlock pb) throws RemoteException; /** * Calls for rendering of a RenderableOp with the given SerializableState * which should be a RenderContextState. */ Long getRendering(Long id, SerializableState rcs) throws RemoteException; /** * Sets the source of the image which is on the same * server */ void setRenderableSource(Long id, Long sourceId, int index) throws RemoteException; /** * Sets the source of the image which is on a different * server */ void setRenderableSource(Long id, Long sourceId, String serverName, String opName, int index) throws RemoteException; /** * Sets the source of the operation refered to by the supplied * id to the RenderableRMIServerProxy * that exists on the supplied serverName under the * supplied sourceId. */ void setRenderableRMIServerProxyAsSource(Long id, Long sourceId, String serverName, String opName, int index) throws RemoteException; /** * Sets the source of the image as a RenderableOp on the server side. */ void setRenderableSource(Long id, RenderableOp source, int index) throws RemoteException; /** * Sets the source of the image as a RenderableImage on the server side. */ void setRenderableSource(Long id, SerializableRenderableImage source, int index) throws RemoteException; /** * Sets the source of the image as a RenderedImage on the server side */ void setRenderableSource(Long id, RenderedImage source, int index) throws RemoteException; /** * Maps the RenderContext for the remote Image */ SerializableState mapRenderContext(int id, Long nodeId, String operationName, SerializableState rcs) throws RemoteException; /** * Gets the Bounds2D of the specified Remote Image */ SerializableState getBounds2D(Long nodeId, String operationName) throws RemoteException; /** * Returns true if successive renderings with the same * arguments may produce different results for this opName * * @return false indicating that the rendering is static. */ public boolean isDynamic(String opName) throws RemoteException; /** * Returns true if successive renderings with the same * arguments may produce different results for this opName * * @return false indicating that the rendering is static. */ public boolean isDynamic(Long id) throws RemoteException; /** * Gets the operation names supported on the Server */ String[] getServerSupportedOperationNames() throws RemoteException; /** * Gets the OperationDescriptors of the operations * supported on this server. */ List getOperationDescriptors() throws RemoteException; /** * Calculates the region over which two distinct renderings * of an operation may be expected to differ. * *

    The class of the returned object will vary as a function of * the nature of the operation. For rendered and renderable two- * dimensional images this should be an instance of a class which * implements java.awt.Shape. * * @return The region over which the data of two renderings of this * operation may be expected to be invalid or null * if there is no common region of validity. */ SerializableState getInvalidRegion(Long id, ParameterBlock oldParamBlock, SerializableState oldHints, ParameterBlock newParamBlock, SerializableState newHints) throws RemoteException; /** * Returns a conservative estimate of the destination region that * can potentially be affected by the pixels of a rectangle of a * given source. * * @param id A Long identifying the node for whom * the destination region needs to be calculated . * @param sourceRect The Rectangle in source coordinates. * @param sourceIndex The index of the source image. * * @return A Rectangle indicating the potentially * affected destination region, or null if * the region is unknown. */ Rectangle mapSourceRect(Long id, Rectangle sourceRect, int sourceIndex) throws RemoteException; /** * Returns a conservative estimate of the region of a specified * source that is required in order to compute the pixels of a * given destination rectangle. * * @param id A Long identifying the node for whom * the source region needs to be calculated . * @param destRect The Rectangle in destination coordinates. * @param sourceIndex The index of the source image. * * @return A Rectangle indicating the required source region. */ Rectangle mapDestRect(Long id, Rectangle destRect, int sourceIndex) throws RemoteException; /** * A method that handles a change in some critical parameter. */ Long handleEvent(Long renderedOpID, String propName, Object oldValue, Object newValue) throws RemoteException; /** * A method that handles a change in one of it's source's rendering, * i.e. a change that would be signalled by RenderingChangeEvent. */ Long handleEvent(Long renderedOpID, int srcIndex, SerializableState srcInvalidRegion, Object oldRendering) throws RemoteException; /** * Returns the server's capabilities as a * NegotiableCapabilitySet. Currently the only capabilities * that are returned are those of TileCodecs. */ NegotiableCapabilitySet getServerCapabilities() throws RemoteException; /** * Informs the server of the negotiated values that are the result of * a successful negotiation. * * @param id An ID for the node which must be unique across all clients. * @param negotiatedValues The result of the negotiation. */ void setServerNegotiatedValues(Long id, NegotiableCapabilitySet negotiatedValues) throws RemoteException; } jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/ColorModelState.java0000644000175000017500000002774010203035544026313 0ustar mathieumathieu/* * $RCSfile: ColorModelState.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:49 $ * $State: Exp $ */ package com.sun.media.jai.rmi; import java.awt.RenderingHints; import java.awt.color.ColorSpace; import java.awt.color.ICC_ColorSpace; import java.awt.image.ColorModel; import java.awt.image.ComponentColorModel; import java.awt.image.DataBuffer; import java.awt.image.DirectColorModel; import java.awt.image.IndexColorModel; import java.awt.image.SampleModel; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import javax.media.jai.FloatDoubleColorModel; /** * This class is a serializable proxy for a ColorModel from which the * ColorModel may be reconstituted. * * * @since 1.1 */ public class ColorModelState extends SerializableStateImpl { /** Flag indicating that the ColorSpace is unknown. */ private static final int COLORSPACE_OTHERS = 0; /** * Flag indicating that the ColorSpace is one of those of which an * instance may be obtained using ColorSpace.getInstance() with one * of the pre-defined constants ColorSpace.CS_*. */ private static final int COLORSPACE_PREDEFINED = 1; /** Flag indicating that the ColorSpace is an ICC_ColorSpace. */ private static final int COLORSPACE_ICC = 2; /** Flag indicating that the ColorModel is null. */ private static final int COLORMODEL_NULL = 0; /** Flag indicating that the ColorModel is a FloatDoubleColorModel. */ private static final int COLORMODEL_FLOAT_DOUBLE_COMPONENT = 1; /** Flag indicating that the ColorModel is a ComponentColorModel. */ private static final int COLORMODEL_COMPONENT = 2; /** Flag indicating that the ColorModel is a IndexColorModel. */ private static final int COLORMODEL_INDEX = 3; /** Flag indicating that the ColorModel is a DirectColorModel. */ private static final int COLORMODEL_DIRECT = 4; /** The ColorModel. */ private transient ColorModel colorModel = null; /** * Returns an array of length one containing the pre-defined * ColorSpace.CS_* colorspace which equals the parameter ColorSpace * or null if it does not equal any of the pre-defined ColorSpaces. */ private static int[] getPredefinedColorSpace(ColorSpace cs) { // Initialize an array of the pre-defined ColorSpaces. int[] colorSpaces = new int[] {ColorSpace.CS_CIEXYZ, ColorSpace.CS_GRAY, ColorSpace.CS_LINEAR_RGB, ColorSpace.CS_PYCC, ColorSpace.CS_sRGB}; // Return the pre-defined index if the parameter is one of these. for(int i = 0; i < colorSpaces.length; i++) { try { if(cs.equals(ColorSpace.getInstance(colorSpaces[i]))) { return new int[] {colorSpaces[i]}; } } catch (Throwable e) { // profile not found ; resilent. } } // Try to find a similar ColorSpace. int numComponents = cs.getNumComponents(); int type = cs.getType(); if(numComponents == 1 && type == ColorSpace.TYPE_GRAY) { return new int[] {ColorSpace.CS_GRAY}; } else if(numComponents == 3) { if(type == ColorSpace.TYPE_RGB) { return new int[] {ColorSpace.CS_sRGB}; } else if(type == ColorSpace.TYPE_XYZ) { return new int[] {ColorSpace.CS_CIEXYZ}; } } // Unknown type - too bad! return null; } /** * Serialize the parameter ColorSpace object. */ private static boolean serializeColorSpace(ColorSpace cs, ObjectOutputStream out) throws IOException { int[] colorSpaceType = getPredefinedColorSpace(cs); boolean isICCColorSpace = (cs instanceof ICC_ColorSpace); if (colorSpaceType == null) { out.writeInt(COLORSPACE_OTHERS); Object object = cs; boolean flag = false; try { Class cls = cs.getClass(); Method getInstance = cls.getMethod("getInstance", null); if (Modifier.isPublic(cls.getModifiers())) { flag = true; object = cls.getName(); } } catch (Exception e) { } finally { out.writeBoolean(flag); out.writeObject(object); } } else { out.writeInt(COLORSPACE_PREDEFINED); out.writeInt(colorSpaceType[0]); } return true; } /** * Derialize the parameter ColorSpace object. */ private static ColorSpace deserializeColorSpace(ObjectInputStream in) throws IOException, ClassNotFoundException { ColorSpace cs = null; int colorSpaceType = in.readInt(); if (colorSpaceType == COLORSPACE_OTHERS) { if (in.readBoolean()) { String name = (String)in.readObject(); try { Class cls = Class.forName(name); Method getInstance = cls.getMethod("getInstance", null); cs = (ColorSpace)getInstance.invoke(null, null); } catch (Exception e) { e.printStackTrace(); } } else { cs = (ColorSpace)in.readObject(); } } else if(colorSpaceType == COLORSPACE_PREDEFINED) { cs = ColorSpace.getInstance(in.readInt()); } return cs; } public static Class[] getSupportedClasses() { return new Class[] { ComponentColorModel.class, FloatDoubleColorModel.class, IndexColorModel.class, DirectColorModel.class, com.sun.media.jai.codecimpl.util.FloatDoubleColorModel.class }; } /** * Constructs a ColorModelState from a * ColorModel. * * @param source The ColorModel to be serialized. * @param o The SampleModel to be serialized. * @param h The RenderingHints (ignored). */ public ColorModelState(Class c, Object o, RenderingHints h) { super(c, o, h); } /** * Serialize the ColorModelState. * * @param out The ObjectOutputStream. */ private void writeObject(ObjectOutputStream out) throws IOException { ColorModel colorModel = (ColorModel)theObject; // Write serialized form to the stream. if(colorModel == null) { out.writeInt(COLORMODEL_NULL); } else if(colorModel instanceof ComponentColorModel) { ComponentColorModel cm = (ComponentColorModel)colorModel; int type = COLORMODEL_COMPONENT; if(colorModel instanceof FloatDoubleColorModel) { type = COLORMODEL_FLOAT_DOUBLE_COMPONENT; } out.writeInt(type); serializeColorSpace(cm.getColorSpace(), out); // ignore return if(type == COLORMODEL_COMPONENT) { out.writeObject(cm.getComponentSize()); } out.writeBoolean(cm.hasAlpha()); out.writeBoolean(cm.isAlphaPremultiplied()); out.writeInt(cm.getTransparency()); // Create a SampleModel to get the transferType. This is // absurd but is the only apparent way to retrieve this value. SampleModel sm = cm.createCompatibleSampleModel(1, 1); out.writeInt(sm.getTransferType()); } else if(colorModel instanceof IndexColorModel) { IndexColorModel cm = (IndexColorModel)colorModel; out.writeInt(COLORMODEL_INDEX); int size = cm.getMapSize(); int[] cmap = new int[size]; cm.getRGBs(cmap); out.writeInt(cm.getPixelSize()); out.writeInt(size); out.writeObject(cmap); out.writeBoolean(cm.hasAlpha()); out.writeInt(cm.getTransparentPixel()); // Create a SampleModel to get the transferType. This is // absurd but is the only apparent way to retrieve this value. SampleModel sm = cm.createCompatibleSampleModel(1, 1); out.writeInt(sm.getTransferType()); } else if(colorModel instanceof DirectColorModel) { DirectColorModel cm = (DirectColorModel)colorModel; out.writeInt(COLORMODEL_DIRECT); boolean csSerialized = serializeColorSpace(cm.getColorSpace(), out); if(!csSerialized) { out.writeBoolean(cm.hasAlpha()); } out.writeInt(cm.getPixelSize()); out.writeInt(cm.getRedMask()); out.writeInt(cm.getGreenMask()); out.writeInt(cm.getBlueMask()); if(csSerialized || cm.hasAlpha()) { out.writeInt(cm.getAlphaMask()); } if(csSerialized) { out.writeBoolean(cm.isAlphaPremultiplied()); // Create a SampleModel to get the transferType. This is // absurd but is the only apparent way to retrieve this // value. SampleModel sm = cm.createCompatibleSampleModel(1, 1); out.writeInt(sm.getTransferType()); } } else { throw new RuntimeException(JaiI18N.getString("ColorModelState0")); } } /** * Deserialize the ColorModelState. * * @param out The ObjectInputStream. */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { ColorModel colorModel = null; // Read serialized form from the stream. ColorSpace cs = null; // Switch on first int which is a flag indicating the class. switch((int)in.readInt()) { case COLORMODEL_NULL: colorModel = null; break; case COLORMODEL_FLOAT_DOUBLE_COMPONENT: if((cs = deserializeColorSpace(in)) == null) { colorModel = null; return; } colorModel = new FloatDoubleColorModel(cs, in.readBoolean(), in.readBoolean(), in.readInt(), in.readInt()); break; case COLORMODEL_COMPONENT: if((cs = deserializeColorSpace(in)) == null) { colorModel = null; return; } colorModel = new ComponentColorModel(cs, (int[])in.readObject(), in.readBoolean(), in.readBoolean(), in.readInt(), in.readInt()); break; case COLORMODEL_INDEX: colorModel = new IndexColorModel(in.readInt(), in.readInt(), (int[])in.readObject(), 0, in.readBoolean(), in.readInt(), in.readInt()); break; case COLORMODEL_DIRECT: if((cs = deserializeColorSpace(in)) != null) { colorModel = new DirectColorModel(cs, in.readInt(), in.readInt(), in.readInt(), in.readInt(), in.readInt(), in.readBoolean(), in.readInt()); } else if(in.readBoolean()) { colorModel = new DirectColorModel(in.readInt(), in.readInt(), in.readInt(), in.readInt(), in.readInt()); } else { colorModel = new DirectColorModel(in.readInt(), in.readInt(), in.readInt(), in.readInt()); } break; default: // NB: Should never get here. throw new RuntimeException(JaiI18N.getString("ColorModelState1")); } theObject = colorModel; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/RenderableRMIServerProxy.java0000644000175000017500000003452610203035544030117 0ustar mathieumathieu/* * $RCSfile: RenderableRMIServerProxy.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:53 $ * $State: Exp $ */package com.sun.media.jai.rmi; import java.awt.Image; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import java.awt.image.renderable.RenderContext; import java.net.InetAddress; import java.rmi.Naming; import java.rmi.RemoteException; import java.util.Vector; import javax.media.jai.remote.JAIRMIDescriptor; import javax.media.jai.remote.RemoteImagingException; import javax.media.jai.remote.SerializerFactory; import javax.media.jai.remote.SerializableState; import javax.media.jai.util.ImagingListener; import com.sun.media.jai.util.ImageUtil; /** * A class that represents and allows access to a * RenderableOp on a remote machine. */ public class RenderableRMIServerProxy implements RenderableImage { /** The name of the server where the RenderableOp exists. */ private String serverName; /** The name of the operation that is represented by this class. */ private String operationName; /** The ParameterBlock for the operation. */ private ParameterBlock paramBlock; /** A reference to the ImageServer object */ private ImageServer imageServer; /** The ID that refers to the RenderableOp on the server. */ public Long id; // The class of the serializable representation of a NULL property. private static final Class NULL_PROPERTY_CLASS = com.sun.media.jai.rmi.JAIRMIImageServer.NULL_PROPERTY.getClass(); // Cache the imaging listener private ImagingListener listener; /** * Creates a RenderableRMIServerProxy to access the * RenderableOp on the server identified by the * supplied opID. */ public RenderableRMIServerProxy(String serverName, String operationName, ParameterBlock paramBlock, Long opID) { this.serverName = serverName; this.operationName = operationName; this.paramBlock = paramBlock; imageServer = getImageServer(serverName); this.id = opID; listener = ImageUtil.getImagingListener((RenderingHints)null); } /** * 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 null; } /** * 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) throws RemoteImagingException { try { Object property = imageServer.getProperty(id, name); if (NULL_PROPERTY_CLASS.isInstance(property)) { property = Image.UndefinedProperty; } return property; } catch (RemoteException re) { String message = JaiI18N.getString("JAIRMICRIF7"); listener.errorOccurred(message, new RemoteImagingException(message, re), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(re)); } return null; } /** * Returns a list of names recognized by getProperty. * @return a list of property names. */ public String[] getPropertyNames() throws RemoteImagingException { try { return imageServer.getPropertyNames(id); } catch (RemoteException re) { String message = JaiI18N.getString("JAIRMICRIF8"); listener.errorOccurred(message, new RemoteImagingException(message, re), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(re)); } return null; } /** * 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. */ public boolean isDynamic() throws RemoteImagingException { try { return imageServer.isDynamic(id); } catch (RemoteException re) { String message = JaiI18N.getString("JAIRMICRIF9"); listener.errorOccurred(message, new RemoteImagingException(message, re), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(re)); } return true; } /** * 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() throws RemoteImagingException { try { return imageServer.getRenderableWidth(id); } catch (RemoteException re) { String message = JaiI18N.getString("RenderableRMIServerProxy0"); listener.errorOccurred(message, new RemoteImagingException(message, re), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(re)); } return 0; } /** * 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() throws RemoteImagingException { try { return imageServer.getRenderableHeight(id); } catch (RemoteException re) { String message = JaiI18N.getString("RenderableRMIServerProxy0"); listener.errorOccurred(message, new RemoteImagingException(message, re), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(re)); } return 0; } /** * Gets the minimum X coordinate of the rendering-independent image data. * @return the minimum X coordinate of the rendering-independent image * data. */ public float getMinX() throws RemoteImagingException { try { return imageServer.getRenderableMinX(id); } catch (RemoteException re) { String message = JaiI18N.getString("RenderableRMIServerProxy1"); listener.errorOccurred(message, new RemoteImagingException(message, re), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(re)); } return 0; } /** * Gets the minimum Y coordinate of the rendering-independent image data. * @return the minimum Y coordinate of the rendering-independent image * data. */ public float getMinY() throws RemoteImagingException { try { return imageServer.getRenderableMinY(id); } catch (RemoteException re) { String message = JaiI18N.getString("RenderableRMIServerProxy1"); listener.errorOccurred(message, new RemoteImagingException(message, re), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(re)); } return 0; } /** * Returns the ID that refers to the RenderableOp on the * server. */ public Long getRMIID() { return id; } /** * Returns the name of the server on which the RenderableOp exists. */ public String getServerName() { return serverName; } /** * Returns the operation name. */ public String getOperationName() { return operationName; } /** * 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) throws RemoteImagingException { SerializableState ss = SerializerFactory.getState(hints, null); try { return imageServer.createScaledRendering(id, w, h, ss); } catch (RemoteException re) { String message = JaiI18N.getString("RMIServerProxy10"); listener.errorOccurred(message, new RemoteImagingException(message, re), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(re)); } return null; } /** * Returns 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. */ public RenderedImage createDefaultRendering() throws RemoteImagingException { try { return imageServer.createDefaultRendering(id); } catch (RemoteException re) { String message = JaiI18N.getString("RMIServerProxy10"); listener.errorOccurred(message, new RemoteImagingException(message, re), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(re)); } return null; } /** * 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. */ public RenderedImage createRendering(RenderContext renderContext) throws RemoteImagingException { SerializableState ss = SerializerFactory.getState(renderContext, null); try { return imageServer.createRendering(id, ss); } catch (RemoteException re) { String message = JaiI18N.getString("RMIServerProxy10"); listener.errorOccurred(message, new RemoteImagingException(message, re), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(re)); } return null; } /** * Construct an ImageServer on the indicated server. * *

    The name of the server must be supplied in the form *

         * host:port
         * 
    * where the port number is optional and may be supplied only if * the host name is supplied. If this parameter is null the default * is to search for the ImageServer service on the local host at the * default rmiregistry port (1099). * *

    The result is cached in the instance variable "remoteImage". * * @param serverName The name of the server in the format described. */ protected synchronized ImageServer getImageServer(String serverName) { if (imageServer == null) { if(serverName == null) { try { serverName = InetAddress.getLocalHost().getHostAddress(); } catch(Exception e) { String message = JaiI18N.getString("RMIServerProxy11"); listener.errorOccurred(message, new RemoteImagingException(message, e), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(e)); } } // Derive the service name. String serviceName = new String("rmi://"+serverName+"/"+ JAIRMIDescriptor.IMAGE_SERVER_BIND_NAME); // Look up the remote object. imageServer = null; try { imageServer = (ImageServer)Naming.lookup(serviceName); } catch(Exception e) { String message = JaiI18N.getString("RMIServerProxy12"); listener.errorOccurred(message, new RemoteImagingException(message, e), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(e)); } } return imageServer; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/SampleModelState.java0000644000175000017500000002170510203035544026451 0ustar mathieumathieu/* * $RCSfile: SampleModelState.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:54 $ * $State: Exp $ */ package com.sun.media.jai.rmi; import java.awt.RenderingHints; import java.awt.image.BandedSampleModel; import java.awt.image.ComponentSampleModel; import java.awt.image.DataBuffer; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.PixelInterleavedSampleModel; import java.awt.image.SampleModel; import java.awt.image.SinglePixelPackedSampleModel; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import javax.media.jai.ComponentSampleModelJAI; import javax.media.jai.RasterFactory; /** * This class is a serializable proxy for a SampleModel from which the * SampleModel may be reconstituted. * * * @since 1.1 */ public class SampleModelState extends SerializableStateImpl { /** Flag indicating a BandedSampleModel. */ private static final int TYPE_BANDED = 1; /** Flag indicating a PixelInterleavedSampleModel. */ private static final int TYPE_PIXEL_INTERLEAVED = 2; /** Flag indicating a SinglePixelPackedSampleModel. */ private static final int TYPE_SINGLE_PIXEL_PACKED = 3; /** Flag indicating a MultiPixelPackedSampleModel. */ private static final int TYPE_MULTI_PIXEL_PACKED = 4; /** Flag indicating a ComponentSampleModelJAI. */ private static final int TYPE_COMPONENT_JAI = 5; /** Flag indicating a generic ComponentSampleModel. */ private static final int TYPE_COMPONENT = 6; public static Class[] getSupportedClasses() { return new Class[] { BandedSampleModel.class, PixelInterleavedSampleModel.class, ComponentSampleModel.class, MultiPixelPackedSampleModel.class, SinglePixelPackedSampleModel.class, ComponentSampleModelJAI.class, com.sun.media.jai.codecimpl.util.ComponentSampleModelJAI.class }; } /* parameter banded ileaved packed1 packedN --------- ------ ------- ------- ------- dataType * * * * width * * * * height * * * * numBands * bankIndices * bandOffsets * * pixelStride * scanlineStride * * * bitMasks * numberOfBits * dataBitOffset * */ /** * Constructs a SampleModelState from a * SampleModel. * * @param c The SampleModel subclass. * @param o The SampleModel to be serialized. * @param h The RenderingHints (ignored). */ public SampleModelState(Class c, Object o, RenderingHints h) { super(c, o, h); } /** * Serialize the SampleModelState. * * @param out The ObjectOutputStream. */ private void writeObject(ObjectOutputStream out) throws IOException { SampleModel sampleModel = (SampleModel)theObject; if(sampleModel instanceof ComponentSampleModel) { ComponentSampleModel sm = (ComponentSampleModel)sampleModel; int sampleModelType = TYPE_COMPONENT; int transferType = sm.getTransferType(); if(sampleModel instanceof PixelInterleavedSampleModel) { sampleModelType = TYPE_PIXEL_INTERLEAVED; } else if(sampleModel instanceof BandedSampleModel) { sampleModelType = TYPE_BANDED; } else if(sampleModel instanceof ComponentSampleModelJAI || transferType == DataBuffer.TYPE_FLOAT || transferType == DataBuffer.TYPE_DOUBLE) { sampleModelType = TYPE_COMPONENT_JAI; } out.writeInt(sampleModelType); out.writeInt(transferType); out.writeInt(sm.getWidth()); out.writeInt(sm.getHeight()); if(sampleModelType != TYPE_BANDED) { out.writeInt(sm.getPixelStride()); } out.writeInt(sm.getScanlineStride()); if(sampleModelType != TYPE_PIXEL_INTERLEAVED) { out.writeObject(sm.getBankIndices()); } out.writeObject(sm.getBandOffsets()); } else if(sampleModel instanceof SinglePixelPackedSampleModel) { SinglePixelPackedSampleModel sm = (SinglePixelPackedSampleModel)sampleModel; out.writeInt(TYPE_SINGLE_PIXEL_PACKED); out.writeInt(sm.getTransferType()); out.writeInt(sm.getWidth()); out.writeInt(sm.getHeight()); out.writeInt(sm.getScanlineStride()); out.writeObject(sm.getBitMasks()); } else if(sampleModel instanceof MultiPixelPackedSampleModel) { MultiPixelPackedSampleModel sm = (MultiPixelPackedSampleModel)sampleModel; out.writeInt(TYPE_MULTI_PIXEL_PACKED); out.writeInt(sm.getTransferType()); out.writeInt(sm.getWidth()); out.writeInt(sm.getHeight()); out.writeInt(sm.getPixelBitStride()); out.writeInt(sm.getScanlineStride()); out.writeInt(sm.getDataBitOffset()); } else { throw new RuntimeException(JaiI18N.getString("SampleModelState0")); } } /** * Deserialize the SampleModelState. * * @param out The ObjectInputStream. */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { SampleModel sampleModel = null; int sampleModelType = (int)in.readInt(); switch(sampleModelType) { case TYPE_PIXEL_INTERLEAVED: sampleModel = RasterFactory.createPixelInterleavedSampleModel(in.readInt(), in.readInt(), in.readInt(), in.readInt(), in.readInt(), (int[])in.readObject()); break; case TYPE_BANDED: sampleModel = RasterFactory.createBandedSampleModel(in.readInt(), in.readInt(), in.readInt(), in.readInt(), (int[])in.readObject(), (int[])in.readObject()); break; case TYPE_COMPONENT_JAI: sampleModel = new ComponentSampleModelJAI(in.readInt(), in.readInt(), in.readInt(), in.readInt(), in.readInt(), (int[])in.readObject(), (int[])in.readObject()); break; case TYPE_COMPONENT: sampleModel = new ComponentSampleModel(in.readInt(), in.readInt(), in.readInt(), in.readInt(), in.readInt(), (int[])in.readObject(), (int[])in.readObject()); break; case TYPE_SINGLE_PIXEL_PACKED: sampleModel = new SinglePixelPackedSampleModel(in.readInt(), in.readInt(), in.readInt(), in.readInt(), (int[])in.readObject()); break; case TYPE_MULTI_PIXEL_PACKED: sampleModel = new MultiPixelPackedSampleModel(in.readInt(), in.readInt(), in.readInt(), in.readInt(), in.readInt(), in.readInt()); break; default: throw new RuntimeException(JaiI18N.getString("SampleModelState0")); } theObject = sampleModel; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/RenderingHintsState.java0000644000175000017500000002751110203035544027173 0ustar mathieumathieu/* * $RCSfile: RenderingHintsState.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:54 $ * $State: Exp $ */ package com.sun.media.jai.rmi; import java.awt.RenderingHints; import java.io.IOException; import java.io.NotSerializableException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.ref.SoftReference; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import java.util.Set; import java.util.Vector; import javax.media.jai.JAI; import javax.media.jai.remote.SerializableState; import javax.media.jai.remote.SerializerFactory; /** * This class is a serializable proxy for a RenderingHints object from which * the RenderingHints object may be reconstituted. * * * @since 1.1 */ public class RenderingHintsState extends SerializableStateImpl { /** * Returns the classes supported by this SerializableState. */ public static Class[] getSupportedClasses() { return new Class[] {RenderingHints.class}; } /** * The classes wherein all possible relevant public static * RenderingHints.Key objects are defined. Classes which contain * declarations of such keys should be added to this array. */ private static final Class[] KEY_CLASSES = {RenderingHints.class, JAI.class}; /** * Instances of keys which should not be serialized. Objects which * represent such keys should be added to this array. Presumably such * objects would be static and final members of one of the classes * in the KEY_CLASSES array. */ private static final Object[] SUPPRESSED_KEYS = {JAI.KEY_OPERATION_REGISTRY, JAI.KEY_TILE_CACHE, JAI.KEY_RETRY_INTERVAL, JAI.KEY_NUM_RETRIES, JAI.KEY_NEGOTIATION_PREFERENCES}; /** A SoftReference to a Vector of keys which are to be suppressed. */ private static SoftReference suppressedKeyReference = null; /** * A SoftReference to a Hashtable containing serializable versions of * all public static fields in the classes in the KEY_CLASSES array. */ private static SoftReference hintTableReference = null; /** * Constructs a RenderingHintsState from a * RenderingHints object. * * @param source The RenderingHints object to be serialized. */ public RenderingHintsState(Class c, Object o, RenderingHints h) { super(c, o, h); } /** An inner class representing either a hint key or a hint value. For a * hint key, the name of the class which contains the declaration of this * key and the field name of this declaration are recorded. For a value, if * it is serializable, the object is recorded. Otherwise, if it is * predefined, the class name and the field name are recorded. */ static class HintElement implements Serializable { /** The class represents a serializable object. */ private static final int TYPE_OBJECT = 1; /** The class represents an element by class and field name. */ private static final int TYPE_FIELD = 2; /** The type of the element representation. */ private int type; /** The element itself. */ private Object obj; /** The name of the class of the element (type == TYPE_FIELD only). */ private String className; /** The name of the field of the element (type == TYPE_FIELD only). */ private String fieldName; /** Constructs a HintElement representing a serializable object. */ public HintElement(Object obj) throws NotSerializableException { if (!(obj instanceof Serializable)) { throw new NotSerializableException(); } type = TYPE_OBJECT; this.obj = obj; } /** Constructs a HintElement representing a public static field. */ public HintElement(Class cls, Field fld) { type = TYPE_FIELD; this.className = cls.getName(); this.fieldName = fld.getName(); } /** Retrieves the element value. Returns null on error. */ public Object getObject() { Object elt = null; if (type == TYPE_OBJECT) { elt = obj; } else if (type == TYPE_FIELD) { try { Class cls = Class.forName(className); Field fld = cls.getField(fieldName); elt = fld.get(null); } catch (Exception e) { // Do nothing. } } return elt; } } /** Returns a Vector of keys which should not be serialized or null. */ private static synchronized Vector getSuppressedKeys() { Vector suppressedKeys = null; if (SUPPRESSED_KEYS != null) { // Initialize the Vector to the SoftReference's referent or null. suppressedKeys = suppressedKeyReference != null ? (Vector)suppressedKeyReference.get() : null; if (suppressedKeys == null) { // Cache the number of suppressed keys. int numSuppressedKeys = SUPPRESSED_KEYS.length; // Load the Vector with the suppressed key objects. suppressedKeys = new Vector(numSuppressedKeys); for (int i = 0; i < numSuppressedKeys; i++) { suppressedKeys.add(SUPPRESSED_KEYS[i]); } // Cache the Vector of suppressed keys. suppressedKeyReference = new SoftReference(suppressedKeys); } } return suppressedKeys; } /** * Returns a Hashtable wherein the keys are instances of * RenderingHints.Key and the values are HintElements. */ static synchronized Hashtable getHintTable() { // Initialize the table to the SoftReference's referent or null. Hashtable table = hintTableReference != null ? (Hashtable)hintTableReference.get() : null; if (table == null) { // Allocate a table for the field values. table = new Hashtable(); for (int i = 0; i < KEY_CLASSES.length; i++) { // Cache the class. Class cls = KEY_CLASSES[i]; // Retrieve the fields for this class. Field[] fields = cls.getFields(); // Load the table with the values of all // fields with public and static modifiers. for (int j = 0; j < fields.length; j++) { Field fld = fields[j]; int modifiers = fld.getModifiers(); if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers)) { try { Object fieldValue = fld.get(null); table.put(fieldValue, new HintElement(cls, fld)); } catch (Exception e) { // Ignore exception. } } } } // Cache the table. hintTableReference = new SoftReference(table); } return table; } /* * The RenderingHints are serialized by creating a Hashtable of key/value * pairs wherein the keys are instances of the inner class HintElement and * the values are also instances of HintElement or of seriailizable * classes. */ /** * Serialize the RenderingHintsState. */ private void writeObject(ObjectOutputStream out) throws IOException { // -- Create a serializable form of the RenderingHints object. -- RenderingHints hints = (RenderingHints)theObject; // Create an empty Hashtable. Hashtable table = new Hashtable(); // If there are hints, add them to the table. if (hints != null && !hints.isEmpty()) { // Get a view of the hints' keys. Set keySet = hints.keySet(); // Proceed if the key set is non-empty. if (!keySet.isEmpty()) { // Get an iterator for the key set. Iterator keyIterator = keySet.iterator(); // Get the cached hint table. Hashtable hintTable = getHintTable(); // Get the suppressed key Vector. Vector suppressedKeys = getSuppressedKeys(); // Loop over the keys. while (keyIterator.hasNext()) { // Get the next key. Object key = keyIterator.next(); // Skip this element if the key is suppressed. if (suppressedKeys != null && suppressedKeys.indexOf(key) != -1) { continue; } // Get the field of the key. Object keyElement = SerializerFactory.getState(key, null); // If the key was not in the table it is not a public // static field in one of the KEY_CLASSES so skip it. if (keyElement == null) { continue; } // Get the next value. Object value = hints.get(key); // Pack the key/value pair in a Hashtable entry. HintElement valueElement = null; try { // Try to create a HintElement from the value directly. valueElement = new HintElement(value); } catch (NotSerializableException nse) { // The value is not serializable so try to get a // HintElement from the table in case the value is // a public static field in one of the KEY_CLASSES. valueElement = (HintElement)hintTable.get(value); } // If the value element is valid add it and its key. if (valueElement != null) { table.put(keyElement, valueElement); } } } } // Write serialized form to the stream. out.writeObject(table); } /** * Deserialize the RenderingHintsState. */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { // Read serialized form from the stream. Hashtable table = (Hashtable)in.readObject(); // Create an empty RenderingHints object. RenderingHints hints = new RenderingHints(null); theObject = hints; // If the table is empty just return. if (table.isEmpty()) { return; } // -- Restore the transient RenderingHints object. -- // Get an enumeration of the table keys. Enumeration keys = table.keys(); // Loop over the table keys. while (keys.hasMoreElements()) { // Get the next key element. SerializableState keyElement = (SerializableState)keys.nextElement(); // Get the key object corresponding to this key element. Object key = keyElement.getObject(); // Get the value element. HintElement valueElement = (HintElement)table.get(keyElement); // Get the value object corresponding to this value element. Object value = valueElement.getObject(); // Add an entry to the hints. hints.put(key, value); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/RenderedImageState.java0000644000175000017500000000663510203035544026747 0ustar mathieumathieu/* * $RCSfile: RenderedImageState.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:53 $ * $State: Exp $ */ package com.sun.media.jai.rmi; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.WritableRenderedImage; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import javax.media.jai.JAI; import javax.media.jai.OperationRegistry; import javax.media.jai.TiledImage; import javax.media.jai.remote.SerializableRenderedImage; import javax.media.jai.tilecodec.TileCodecParameterList; /** * A SerializableState wrapper for a RenderedImage * or WritableRenderedImage. This class simply uses a * SerializableRenderedImage to do the work. * * @since 1.1 */ public final class RenderedImageState extends SerializableStateImpl { private boolean isWritable; private transient boolean useDeepCopy; private transient OperationRegistry registry; private transient String formatName; private transient TileCodecParameterList encodingParam; private transient TileCodecParameterList decodingParam; public static Class[] getSupportedClasses() { return new Class[] { RenderedImage.class, WritableRenderedImage.class}; } public RenderedImageState(Class c, Object o, RenderingHints h) { super(c, o, h); isWritable = o instanceof WritableRenderedImage; if(h != null) { Object value = h.get(JAI.KEY_SERIALIZE_DEEP_COPY); if(value != null) { useDeepCopy = ((Boolean)value).booleanValue(); } else { useDeepCopy = false; } value = h.get(JAI.KEY_OPERATION_REGISTRY); if(value != null) { registry = (OperationRegistry)value; } value = h.get(JAI.KEY_TILE_CODEC_FORMAT); if(value != null) { formatName = (String)value; } value = h.get(JAI.KEY_TILE_ENCODING_PARAM); if(value != null) { encodingParam = (TileCodecParameterList)value; } value = h.get(JAI.KEY_TILE_DECODING_PARAM); if(value != null) { decodingParam = (TileCodecParameterList)value; } } } private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); SerializableRenderedImage sri; if (formatName == null || encodingParam == null || decodingParam == null) { sri = new SerializableRenderedImage((RenderedImage)theObject, useDeepCopy); } else { sri = new SerializableRenderedImage((RenderedImage)theObject, useDeepCopy, registry, formatName, encodingParam, decodingParam); } out.writeObject(sri); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); theObject = in.readObject(); if(isWritable) { theObject = new TiledImage((RenderedImage)theObject, true); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/SerializableStateImpl.java0000644000175000017500000001011410203035544027467 0ustar mathieumathieu/* * $RCSfile: SerializableStateImpl.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:54 $ * $State: Exp $ */ package com.sun.media.jai.rmi; import java.awt.RenderingHints; import java.io.Serializable; import java.lang.reflect.Method; import javax.media.jai.remote.SerializableState; import javax.media.jai.remote.SerializerFactory; /** * Framework class for adding Serializers based on * SerializableState implementations which support one or * more classes or interfaces. * *

    Extending classes MUST: *

      *
    1. be public;
    2. *
    3. provide a single public constructor with exactly the same signature as * the protected constructor of this class;
    4. *
    5. provide a static override of getSupportedClasses();
    6. *
    7. implement the (de)serialization methods writeObject() * and readObject(); and
    8. *
    9. add the class to SerializerImpl.registerSerializers() as *
       *       registerSerializers(MySerializableState.class);
       * 
    10. *
    * * @since 1.1 */ public abstract class SerializableStateImpl implements SerializableState { protected Class theClass; protected transient Object theObject; /** * Returns the classes supported by this SerializableState. * Subclasses MUST override this method with their own STATIC method. */ public static Class[] getSupportedClasses() { throw new RuntimeException(JaiI18N.getString("SerializableStateImpl0")); } /** * Whether the SerializableStateImpl permits its Serializer to * serialize subclasses of the supported class(es). * Subclasses SHOULD override this method to return "true" with their * own STATIC method IF AND ONLY IF they support subclass serialization. */ public static boolean permitsSubclasses() { return false; } /** * Constructor. All subclasses MUST have exactly ONE constructor with * the SAME signature as this constructor. */ protected SerializableStateImpl(Class c, Object o, RenderingHints h) { if (c == null || o == null) { throw new IllegalArgumentException(JaiI18N.getString("SerializableStateImpl1")); } else { boolean isInterface = c.isInterface(); if (isInterface && !c.isInstance(o)) { throw new IllegalArgumentException(JaiI18N.getString("SerializableStateImpl2")); } else if (!isInterface) { boolean permitsSubclasses = false; try { Method m = this.getClass().getMethod("permitsSubclasses", null); permitsSubclasses = ((Boolean)m.invoke(null, null)).booleanValue(); } catch (Exception e){ throw new IllegalArgumentException(JaiI18N.getString("SerializableStateImpl5")); } if (!permitsSubclasses && !c.equals(o.getClass())) { throw new IllegalArgumentException(JaiI18N.getString("SerializableStateImpl3")); } else if (permitsSubclasses && !c.isAssignableFrom(o.getClass())) { throw new IllegalArgumentException(JaiI18N.getString("SerializableStateImpl4")); } } } theClass = c; theObject = o; } public Class getObjectClass() { return theClass; } public Object getObject() { return theObject; } protected Object getSerializableForm(Object object) { if (object instanceof Serializable) return object; if (object != null) try { object = SerializerFactory.getState(object, null); } catch (Exception e) { object = null; } return object; } protected Object getDeserializedFrom(Object object) { if (object instanceof SerializableState) object = ((SerializableState)object).getObject(); return object; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/DataBufferProxy.java0000644000175000017500000001072310203035544026311 0ustar mathieumathieu/* * $RCSfile: DataBufferProxy.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:49 $ * $State: Exp $ */ package com.sun.media.jai.rmi; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferInt; import java.awt.image.DataBufferShort; import java.awt.image.DataBufferUShort; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import com.sun.media.jai.util.DataBufferUtils; /** * This class is a serializable proxy for a DataBuffer from which the * DataBuffer may be reconstituted. * * * @since EA3 */ public class DataBufferProxy implements Serializable { /** The DataBuffer. */ private transient DataBuffer dataBuffer; /** * Constructs a DataBufferProxy from a * DataBuffer. * * @param source The DataBuffer to be serialized. */ public DataBufferProxy(DataBuffer source) { dataBuffer = source; } /** * Retrieves the associated DataBuffer. * @return The (perhaps reconstructed) DataBuffer. */ public DataBuffer getDataBuffer() { return dataBuffer; } // XXX Note that there is potential for some form of data compression in // the readObject() and writeObject() methods. /** * Serialize the DataBufferProxy. * * @param out The ObjectOutputStream. */ private void writeObject(ObjectOutputStream out) throws IOException { // Write serialized form to the stream. int dataType = dataBuffer.getDataType(); out.writeInt(dataType); out.writeObject(dataBuffer.getOffsets()); out.writeInt(dataBuffer.getSize()); Object dataArray = null; switch (dataType) { case DataBuffer.TYPE_BYTE: dataArray = ((DataBufferByte)dataBuffer).getBankData(); break; case DataBuffer.TYPE_SHORT: dataArray = ((DataBufferShort)dataBuffer).getBankData(); break; case DataBuffer.TYPE_USHORT: dataArray = ((DataBufferUShort)dataBuffer).getBankData(); break; case DataBuffer.TYPE_INT: dataArray = ((DataBufferInt)dataBuffer).getBankData(); break; case DataBuffer.TYPE_FLOAT: dataArray = DataBufferUtils.getBankDataFloat(dataBuffer); break; case DataBuffer.TYPE_DOUBLE: dataArray = DataBufferUtils.getBankDataDouble(dataBuffer); break; default: throw new RuntimeException(JaiI18N.getString("DataBufferProxy0")); } out.writeObject(dataArray); } /** * Deserialize the DataBufferProxy. * * @param out The ObjectInputStream. */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { // Read serialized form from the stream. int dataType = -1; int[] offsets = null; int size = -1; Object dataArray = null; dataType = in.readInt(); offsets = (int[])in.readObject(); size = in.readInt(); dataArray = in.readObject(); // Restore the transient DataBuffer. switch (dataType) { case DataBuffer.TYPE_BYTE: dataBuffer = new DataBufferByte((byte[][])dataArray, size, offsets); break; case DataBuffer.TYPE_SHORT: dataBuffer = new DataBufferShort((short[][])dataArray, size, offsets); break; case DataBuffer.TYPE_USHORT: dataBuffer = new DataBufferUShort((short[][])dataArray, size, offsets); break; case DataBuffer.TYPE_INT: dataBuffer = new DataBufferInt((int[][])dataArray, size, offsets); break; case DataBuffer.TYPE_FLOAT: dataBuffer = DataBufferUtils.createDataBufferFloat((float[][])dataArray, size, offsets); break; case DataBuffer.TYPE_DOUBLE: dataBuffer = DataBufferUtils.createDataBufferDouble((double[][])dataArray, size, offsets); break; default: throw new RuntimeException(JaiI18N.getString("DataBufferProxy0")); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/com.sun.media.jai.rmi.properties0000644000175000017500000001226010203035544030505 0ustar mathieumathieu# # $RCSfile: com.sun.media.jai.rmi.properties,v $ # # Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. # # Use is subject to license terms. # # $Revision: 1.1 $ # $Date: 2005-02-11 04:56:55 $ # $State: Exp $ # Generic0=The method parameter(s) may not be null. ColorModelProxy0=Unknown ColorModel class. ColorModelProxy1=Unknown ColorModel type. ColorModelState0=Unknown ColorModel class. ColorModelState1=Unknown ColorModel type. DataBufferProxy0=Unsupported data type. DataBufferState0=Unsupported data type. InterfaceHandler0=The number of implementations must equal the number of interfaces. InterfaceHandler1=Implementations must be compatible with specified interfaces. InterfaceHandler2= has no compatible implementation. InterfaceHandler3= cannot be evaluated by proxy. JAIRMICRIF0=serverName argument cannot be null. JAIRMICRIF1=operationName argument cannot be null. JAIRMICRIF2=renderContext argument cannot be null. JAIRMICRIF3=paramBlock argument cannot be null. JAIRMICRIF4=image argument cannot be null. JAIRMICRIF5=RemoteException occurs in mapRenderContext() JAIRMICRIF6=RemoteException occurs in getBounds() JAIRMICRIF7=RemoteException occurs in getProperty() JAIRMICRIF8=RemoteException occurs in getPropertyNames() JAIRMICRIF9=RemoteException occurs in isDynamic() JAIRMIImageServer0=No suitable TileEncoder could be found to encode the tile before transmission. JAIRMIImageServer1=Cannot encode according to the specified tile codec scheme which does not include SampleModel or location info in the encoded stream. JAIRMIImageServer2=Cannot transmit compressed tile, there is no negotiated result for the "tileCodec" category. JAIRMIImageServer3=No appropriate ContextualRenderedImageFactory exists for the specified operationName. RemoteImage1=Incorrect server name string. RenderingKeyState0=This RenderingHints.Key is not predefined and cannot be serialized. RenderableRMIServerProxy0=RemoteException occurs in getting the image width/height. RenderableRMIServerProxy1=RemoteException occurs in getting the image position. RMIImageImpl0=Server construction error: RMIImageImpl1=Server: Error: RMIImageImpl2=Unable to retrieve requested client object from the cache. RMIImageImpl3=Server: using host/port RMIImageImpl4=Registering image server as RMIImageImpl5=Server: Bound RemoteImageServer into the registry. RMIServerProxy0=No suitable TileDecoder could be found to encode the tile before transmission. RMIServerProxy1=Cannot decode according to the specified tile codec scheme which does not include SampleModel or location info in the encoded stream. RMIServerProxy2=Error encountered while disposing the corresponding node on the server. RMIServerProxy3=The old rendering provided is not an instance of RMIServerProxy. RMIServerProxy4=The property name of the supplied PropertyChangeEventJAI is not supported. RMIServerProxy5=RemoteException occurs when creating the node. RMIServerProxy6=RemoteException occurs when setting the sources. RMIServerProxy7=RemoteException occurs when handling the event. RMIServerProxy8=RemoteException occurs when creating renderable node. RMIServerProxy9=RemoteException occurs when incrementing the reference count. RMIServerProxy10=RemoteException occurs when rendering the node. RMIServerProxy11=Cannot get the server host address. RMIServerProxy12=Cannot look up the remote object. RMIServerProxy13=Cannot get the remote id. RMIServerProxy14=RemoteException occurs when getting the image layout. RMIServerProxy15=RemoteException occurs when getting a tile. RMIServerProxy16=RemoteException occurs when getting the properties. RMIServerProxy17=RemoteException occurs when getting the property names. RMIServerProxy18=RemoteException occurs when mapping the source/dest rectangle. RMIServerProxy19=RemoteException occurs when setting the server negotiation values. SampleModelProxy0=Unknown SampleModel type. SampleModelState0=Unsupported SampleModel type. SerializableRenderableImage0=Incorrect object received as server request. SerializableRenderableImage1=The source RenderableImage is null. SerializableRenderableImage2=The format name is null. SerializableStateImpl0=SerializableStateImpl subclass must override getSupportedClasses(). SerializableStateImpl1=The supplied Class or Object value is null. SerializableStateImpl2=The supplied Class does not implement the supported interface. SerializableStateImpl3=The supplied Class does not equal the supported class. SerializableStateImpl4=The supplied Class is not assignable to the supported superclass. SerializableStateImpl5=Unable to retrieve permitsSubclasses() by reflection. SerializerImpl0=Supplied Class is not a subclass of SerializableStateImpl. SerializerImpl1=Unable to retrieve getSupportedClasses() by reflection. SerializerImpl2=Unable to retrieve SerializableStateImpl subclass constructor by reflection. SerializerImpl3=Unable to construct SerializableState by reflection. SerializerImpl4=Unable to retrieve permitsSubclasses() by reflection. UseTileCodec0=The format name in the encoding parameter should be the same as the provided one. UseTileCodec1=The format name in the decoding parameter should be the same as the provided one. UseTileCodec2=The encoder or decoder factory is not registered for the provided format. jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/HashSetState.java0000644000175000017500000000613710203035544025610 0ustar mathieumathieu/* * $RCSfile: HashSetState.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:50 $ * $State: Exp $ */ package com.sun.media.jai.rmi; import java.awt.RenderingHints; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.HashSet; import java.util.Iterator; import javax.media.jai.JAI; import javax.media.jai.remote.SerializerFactory; import javax.media.jai.remote.SerializableState; /** * This class is a serializable proxy for a HashSet object. *
    (entries which are neither Serializable nor supported by * SerializerFactory are omitted); * * * @since 1.1 */ public class HashSetState extends SerializableStateImpl { /** * Returns the classes supported by this SerializableState. */ public static Class[] getSupportedClasses() { return new Class[] {HashSet.class}; } /** * Constructs a HashSetState from a * HashSet object. * * @param c The Class of the object to be serialized. * @param o The HashSet object to be serialized. * @param h The RebderingHint for this serialization. */ public HashSetState(Class c, Object o, RenderingHints h) { super(c, o, h); } /** * Serialize the HashSetState. */ private void writeObject(ObjectOutputStream out) throws IOException { // -- Create a serializable form of the HashSet object. -- HashSet set = (HashSet)theObject; HashSet serializableSet = new HashSet(); // If there are hints, add them to the set. if (set != null && !set.isEmpty()) { // Get an iterator for the set. Iterator iterator = set.iterator(); // Loop over the set. while (iterator.hasNext()) { Object object = iterator.next(); Object serializableObject = getSerializableForm(object); serializableSet.add(serializableObject); } } // Write serialized form to the stream. out.writeObject(serializableSet); } /** * Deserialize the HashSetState. */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { // Read serialized form from the stream. HashSet serializableSet = (HashSet)in.readObject(); // Create an empty HashSet object. HashSet set = new HashSet(); // If the set is empty just return. if (serializableSet.isEmpty()) { theObject = set; return; } Iterator iterator = serializableSet.iterator(); // Loop over the set keys. while (iterator.hasNext()) { // Get the next key element. Object serializableObject = iterator.next(); Object object = getDeserializedFrom(serializableObject); // Add an entry to the set. set.add(object); } theObject = set; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/RMIImageImpl.java0000644000175000017500000004360410203035544025464 0ustar mathieumathieu/* * $RCSfile: RMIImageImpl.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:52 $ * $State: Exp $ */ package com.sun.media.jai.rmi; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.Rectangle2D; import java.awt.image.ColorModel; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.RenderContext; import java.io.Serializable; import java.net.InetAddress; import java.rmi.Naming; import java.rmi.Remote; import java.rmi.RemoteException; import java.rmi.RMISecurityManager; import java.rmi.server.UnicastRemoteObject; import java.util.Hashtable; import java.util.Vector; import javax.media.jai.PlanarImage; import javax.media.jai.PropertySource; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.remote.SerializableRenderedImage; import javax.media.jai.remote.RemoteImagingException; import javax.media.jai.util.ImagingException; import javax.media.jai.util.ImagingListener; import com.sun.media.jai.util.ImageUtil; /* A singleton class representing the serializable version of a null property. This required because java.awt.Image.UndefinedProperty is not serializable. */ class NullPropertyTag implements Serializable { NullPropertyTag() {} } /** * The server-side implementation of the RMIImage interface. A * RMIImageImpl has a RenderedImage source, acquired via one of three * setSource() methods. The first takes a RenderedImage directly as * its parameter; this image is simply copied over the network using * the normal RMI mechanisms. Note that not every image can be * transferred in this way -- for example, attempting to pass an * OpImage that uses native code or that depends on the availability * of a class not resident on the server as a parameter will cause an * exception to be thrown. * *

    The second and third ways of setting sources make use of the * RenderedOp and RenderableOp classes to send a high-level * description of an image chain based on operation names. This * chain will be copied over to the server using RMI, where it will be * expanded into an OpImage chain using the server's registry. This * is the preferred method since it requires less data transfer and * offers a better chance of success. It may still fail if the * sources or parameters of any operation in the chain are not * serializable. * *

    RMI requires all remote methods to declare `throws * RemoteException' in their signatures. It is up to the client to * deal with errors. A simple implementation of error handling may be * found in the RemoteRenderedImage class. * *

    This class contains a main() method that should be run on the * server after starting the RMI registry. The registry will then * construct new instances of RMIImageImpl on demand. * * @see RMIImage * @see RemoteImage * @see RenderedOp * * @since EA3 * */ public class RMIImageImpl implements RMIImage { /** Tag to represent a null property. */ public static final Object NULL_PROPERTY = new NullPropertyTag(); /** Identifier counter for the remote images. */ private static long idCounter = 0; /** * The RenderedImage sources hashed by an ID string which must be unique * across all possible clients of this object. */ private static Hashtable sources = null; /** * The PropertySources hashed by an ID string which must be unique * across all possible clients of this object. */ private static Hashtable propertySources = null; /** * Adds a RenderedImage source to the Hashtable of sources. * * @param id A unique ID for the source. * @param source The source RenderedImage. * @param ps The PropertySource. */ private static synchronized void addSource(Long id, RenderedImage source, PropertySource ps) { // Create the Hashtables "just in time". if(sources == null) { sources = new Hashtable(); propertySources = new Hashtable(); } // Add the source and PropertySource. sources.put(id, source); propertySources.put(id, ps); } /** * Retrieve a PlanarImage source from the Hashtable of sources. * * @param id The unique ID of the source. * @return The source. */ private static PlanarImage getSource(Long id) throws RemoteException { Object obj = null; if(sources == null || (obj = sources.get(id)) == null) { throw new RemoteException(JaiI18N.getString("RMIImageImpl2")); } return (PlanarImage)obj; } /** * Retrieve a PropertySource from the Hashtable of PropertySources. * * @param id The unique ID of the source. * @return The PropertySource. */ private static PropertySource getPropertySource(Long id) throws RemoteException { Object obj = null; if(propertySources == null || (obj = propertySources.get(id)) == null) { throw new RemoteException(JaiI18N.getString("RMIImageImpl2")); } return (PropertySource)obj; } /** * Constructs a RMIImageImpl with a source to be specified * later. */ public RMIImageImpl() throws RemoteException { super(); try { UnicastRemoteObject.exportObject(this); } catch(RemoteException e) { ImagingListener listener = ImageUtil.getImagingListener((RenderingHints)null); String message = JaiI18N.getString("RMIImageImpl0"); listener.errorOccurred(message, new RemoteImagingException(message, e), this, false); /* e.printStackTrace(); throw new RuntimeException(JaiI18N.getString("RMIImageImpl0") + e.getMessage()); */ } } /** * Returns the identifier of the remote image. This method should be * called to return an identifier before any other methods are invoked. * The same ID must be used in all subsequent references to the remote * image. */ public synchronized Long getRemoteID() throws RemoteException { return new Long(++idCounter); } /** * Sets the source of the image on the server side. This source * should ideally be a lightweight reference to an image available * locally on the server or over a further network link (for * example, an IIPOpImage that contains a URL but not actual image * data). * *

    Although it is legal to use any RenderedImage, one should be * aware that a deep copy might be made and transmitted to the server. * * @param id An ID for the source which must be unique across all clients. * @param source a RenderedImage source. */ public void setSource(Long id, RenderedImage source) throws RemoteException { PlanarImage pi = PlanarImage.wrapRenderedImage(source); addSource(id, pi, pi); } /** * Sets the source to a RenderedOp (i.e., an imaging DAG). * This DAG will be copied over to the server where it will be * transformed into an OpImage chain using the server's local * OperationRegistry and available RenderedImageFactory objects. * * @param id An ID for the source which must be unique across all clients. * @param source a RenderedOp source. */ public void setSource(Long id, RenderedOp source) throws RemoteException { addSource(id, source.getRendering(), source); } /** * Sets the source to a RenderableOp defined by a renderable imaging * DAG and a rendering context. The entire RenderableImage * DAG will be copied over to the server. */ public void setSource(Long id, RenderableOp source, RenderContextProxy renderContextProxy) throws RemoteException { RenderContext renderContext = renderContextProxy.getRenderContext(); RenderedImage r = source.createRendering(renderContext); PlanarImage pi = PlanarImage.wrapRenderedImage(r); addSource(id, pi, pi); } /** * Disposes of any resouces allocated to the client object with * the specified ID. */ public void dispose(Long id) throws RemoteException { if(sources != null) { sources.remove(id); propertySources.remove(id); } } /** Gets a property from the property set of this image. If the property is undefined the constant NULL_PROPERTY is returned. */ public Object getProperty(Long id, String name) throws RemoteException { PropertySource ps = getPropertySource(id); Object property = ps.getProperty(name); if(property == null || property.equals(java.awt.Image.UndefinedProperty)) { property = NULL_PROPERTY; } return property; } /** * Returns a list of names recognized by getProperty(). * * @return an array of Strings representing proeprty names. */ public String[] getPropertyNames(Long id) throws RemoteException { PropertySource ps = getPropertySource(id); return ps.getPropertyNames(); } /** Returns the minimum X coordinate of the RMIImage. */ public int getMinX(Long id) throws RemoteException { return getSource(id).getMinX(); } /** Returns the smallest X coordinate to the right of the RMIImage. */ public int getMaxX(Long id) throws RemoteException { return getSource(id).getMaxX(); } /** Returns the minimum Y coordinate of the RMIImage. */ public int getMinY(Long id) throws RemoteException { return getSource(id).getMinY(); } /** Returns the smallest Y coordinate below the RMIImage. */ public int getMaxY(Long id) throws RemoteException { return getSource(id).getMaxY(); } /** Returns the width of the RMIImage. */ public int getWidth(Long id) throws RemoteException { return getSource(id).getWidth(); } /** Returns the height of the RMIImage. */ public int getHeight(Long id) throws RemoteException { return getSource(id).getHeight(); } /** Returns the width of a tile in pixels. */ public int getTileWidth(Long id) throws RemoteException { return getSource(id).getTileWidth(); } /** Returns the height of a tile in pixels. */ public int getTileHeight(Long id) throws RemoteException { return getSource(id).getTileHeight(); } /** * Returns the X coordinate of the upper-left pixel of tile (0, 0). */ public int getTileGridXOffset(Long id) throws RemoteException { return getSource(id).getTileGridXOffset(); } /** * Returns the Y coordinate of the upper-left pixel of tile (0, 0). */ public int getTileGridYOffset(Long id) throws RemoteException { return getSource(id).getTileGridYOffset(); } /** Returns the index of the leftmost column of tiles. */ public int getMinTileX(Long id) throws RemoteException { return getSource(id).getMinTileX(); } /** * Returns the number of tiles along the tile grid in the horizontal * direction. */ public int getNumXTiles(Long id) throws RemoteException { return getSource(id).getNumXTiles(); } /** Returns the index of the uppermost row of tiles. */ public int getMinTileY(Long id) throws RemoteException { return getSource(id).getMinTileY(); } /** * Returns the number of tiles along the tile grid in the vertical * direction. */ public int getNumYTiles(Long id) throws RemoteException { return getSource(id).getNumYTiles(); } /** Returns the index of the rightmost column of tiles. */ public int getMaxTileX(Long id) throws RemoteException { return getSource(id).getMaxTileX(); } /** Returns the index of the bottom row of tiles. */ public int getMaxTileY(Long id) throws RemoteException { return getSource(id).getMaxTileY(); } /** Returns the SampleModel associated with this image. */ public SampleModelProxy getSampleModel(Long id) throws RemoteException { return new SampleModelProxy(getSource(id).getSampleModel()); } /** Returns the ColorModel associated with this image. */ public ColorModelProxy getColorModel(Long id) throws RemoteException { return new ColorModelProxy(getSource(id).getColorModel()); } /** * Returns a vector of RenderedImages that are the sources of * image data for this RMIImage. Note that this method * will often return an empty vector. */ public Vector getSources(Long id) throws RemoteException { Vector sourceVector = getSource(id).getSources(); int size = sourceVector.size(); boolean isCloned = false; for(int i = 0; i < size; i++) { RenderedImage img = (RenderedImage)sourceVector.get(i); if(!(img instanceof Serializable)) { if(!isCloned) { sourceVector = (Vector)sourceVector.clone(); } sourceVector.set(i, new SerializableRenderedImage(img, false)); } } return sourceVector; } /** Returns a Rectangle indicating the image bounds. */ public Rectangle getBounds(Long id) throws RemoteException { return getSource(id).getBounds(); } /** * Returns tile (x, y). Note that x and y are indices into the * tile array, not pixel locations. Unlike in the true RenderedImage * interface, the Raster that is returned should be considered a copy. * * @param id An ID for the source which must be unique across all clients. * @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 as a Raster. */ public RasterProxy getTile(Long id, int tileX, int tileY) throws RemoteException { return new RasterProxy(getSource(id).getTile(tileX, tileY)); } /** * Returns the entire image as a single Raster. * * @return a Raster containing a copy of this image's data. */ public RasterProxy getData(Long id) throws RemoteException { return new RasterProxy(getSource(id).getData()); } /** * Returns an arbitrary rectangular region of the RenderedImage * in a Raster. The rectangle of interest will be clipped against * the image bounds. * * @param id An ID for the source which must be unique across all clients. * @param rect the region of the RenderedImage to be returned. * @return a Raster containing a copy of the desired data. */ public RasterProxy getData(Long id, Rectangle bounds) throws RemoteException { RasterProxy rp = null; if(bounds == null) { rp = getData(id); } else { bounds = bounds.intersection(getBounds(id)); rp = new RasterProxy(getSource(id).getData(bounds)); } return rp; } /** * Returns the same result as getData(Rectangle) would for the * same rectangular region. */ public RasterProxy copyData(Long id, Rectangle bounds) throws RemoteException { return getData(id, bounds); } /** * Starts a server on a given port. The RMI registry must be running * on the server host. * *

    The usage of this class is * *

         * java -Djava.rmi.server.codebase=file:$JAI/lib/jai.jar \
         * -Djava.rmi.server.useCodebaseOnly=false \
         * -Djava.security.policy=\
         * file:`pwd`/policy com.sun.media.jai.rmi.RMIImageImpl \
         * [-host hostName] [-port portNumber]
         * 
    * * The default host is the local host and the default port is 1099. * * @param args the port number as a command-line argument. */ public static void main(String [] args) { // Set the security manager. if(System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager()); } // Set the host name and port number. String host = null; int port = 1099; // default port is 1099 for(int i = 0; i < args.length; i++) { if(args[i].equalsIgnoreCase("-host")) { host = args[++i]; } else if(args[i].equalsIgnoreCase("-port")) { port = Integer.parseInt(args[++i]); } } // Default to the local host if the host was not specified. if(host == null) { try { host = InetAddress.getLocalHost().getHostAddress(); } catch(java.net.UnknownHostException e) { System.err.println(JaiI18N.getString("RMIImageImpl1") + e.getMessage()); e.printStackTrace(); } } System.out.println(JaiI18N.getString("RMIImageImpl3")+" "+ host+":"+port); try { RMIImageImpl im = new RMIImageImpl(); String serverName = new String("rmi://" + host + ":" + port + "/" + RMIImage.RMI_IMAGE_SERVER_NAME); System.out.println(JaiI18N.getString("RMIImageImpl4")+" \""+ serverName+"\"."); Naming.rebind(serverName, im); System.out.println(JaiI18N.getString("RMIImageImpl5")); } catch (Exception e) { System.err.println(JaiI18N.getString("RMIImageImpl0") + e.getMessage()); e.printStackTrace(); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/ColorModelProxy.java0000644000175000017500000002732410203035544026352 0ustar mathieumathieu/* * $RCSfile: ColorModelProxy.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:49 $ * $State: Exp $ */ package com.sun.media.jai.rmi; import java.awt.color.ColorSpace; import java.awt.color.ICC_ColorSpace; import java.awt.color.ICC_Profile; import java.awt.image.ColorModel; import java.awt.image.ComponentColorModel; import java.awt.image.DataBuffer; import java.awt.image.DirectColorModel; import java.awt.image.IndexColorModel; import java.awt.image.SampleModel; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import javax.media.jai.FloatDoubleColorModel; /** * This class is a serializable proxy for a ColorModel from which the * ColorModel may be reconstituted. * * * @since EA3 */ public class ColorModelProxy implements Serializable { /** Flag indicating that the ColorSpace is unknown. */ private static final int COLORSPACE_UNKNOWN = 0; /** * Flag indicating that the ColorSpace is one of those of which an * instance may be obtained using ColorSpace.getInstance() with one * of the pre-defined constants ColorSpace.CS_*. */ private static final int COLORSPACE_PREDEFINED = 1; /** Flag indicating that the ColorSpace is an ICC_ColorSpace. */ private static final int COLORSPACE_ICC = 2; /** Flag indicating that the ColorModel is null. */ private static final int COLORMODEL_NULL = 0; /** Flag indicating that the ColorModel is a FloatDoubleColorModel. */ private static final int COLORMODEL_FLOAT_DOUBLE_COMPONENT = 1; /** Flag indicating that the ColorModel is a ComponentColorModel. */ private static final int COLORMODEL_COMPONENT = 2; /** Flag indicating that the ColorModel is a IndexColorModel. */ private static final int COLORMODEL_INDEX = 3; /** Flag indicating that the ColorModel is a DirectColorModel. */ private static final int COLORMODEL_DIRECT = 4; /** The ColorModel. */ private transient ColorModel colorModel = null; /** * Returns an array of length one containing the pre-defined * ColorSpace.CS_* colorspace which equals the parameter ColorSpace * or null if it does not equal any of the pre-defined ColorSpaces. */ private static int[] getPredefinedColorSpace(ColorSpace cs) { // Initialize an array of the pre-defined ColorSpaces. int[] colorSpaces = new int[] {ColorSpace.CS_CIEXYZ, ColorSpace.CS_GRAY, ColorSpace.CS_LINEAR_RGB, ColorSpace.CS_PYCC, ColorSpace.CS_sRGB}; // Return the pre-defined index if the parameter is one of these. for(int i = 0; i < colorSpaces.length; i++) { try { if(cs.equals(ColorSpace.getInstance(colorSpaces[i]))) { return new int[] {colorSpaces[i]}; } } catch (Throwable e) { // Profile not found ; resilent. } } // Try to find a similar ColorSpace. int numComponents = cs.getNumComponents(); int type = cs.getType(); if(numComponents == 1 && type == ColorSpace.TYPE_GRAY) { return new int[] {ColorSpace.CS_GRAY}; } else if(numComponents == 3) { if(type == ColorSpace.TYPE_RGB) { return new int[] {ColorSpace.CS_sRGB}; } else if(type == ColorSpace.TYPE_XYZ) { return new int[] {ColorSpace.CS_CIEXYZ}; } } // Unknown type - too bad! return null; } /** * Serialize the parameter ColorSpace object. */ private static boolean serializeColorSpace(ColorSpace cs, ObjectOutputStream out) throws IOException { int[] colorSpaceType = null; if(!(cs instanceof ICC_ColorSpace) && (colorSpaceType = getPredefinedColorSpace(cs)) == null) { out.writeInt(COLORSPACE_UNKNOWN); out.writeInt(cs.getNumComponents()); return false; } if(cs instanceof ICC_ColorSpace) { out.writeInt(COLORSPACE_ICC); ((ICC_ColorSpace)cs).getProfile().write(out); } else { out.writeInt(COLORSPACE_PREDEFINED); out.writeInt(colorSpaceType[0]); } return true; } /** * Derialize the parameter ColorSpace object. */ private static ColorSpace deserializeColorSpace(ObjectInputStream in) throws IOException { ColorSpace cs = null; int colorSpaceType = in.readInt(); if(colorSpaceType == COLORSPACE_ICC) { cs = new ICC_ColorSpace(ICC_Profile.getInstance(in)); } else if(colorSpaceType == COLORSPACE_PREDEFINED) { cs = ColorSpace.getInstance(in.readInt()); } else if(colorSpaceType == COLORSPACE_UNKNOWN) { // Create probable ColorSpaces for 1- and 3-band cases. switch(in.readInt()) { case 1: // gray scale cs = ColorSpace.getInstance(ColorSpace.CS_GRAY); break; case 3: // sRGB cs = ColorSpace.getInstance(ColorSpace.CS_sRGB); break; default: // Unknown cs = null; // to be explicit ... } } return cs; } /** * Constructs a ColorModelProxy from a * ColorModel. * * @param source The ColorModel to be serialized. */ public ColorModelProxy(ColorModel source) { colorModel = source; } /** * Retrieves the associated ColorModel. * @return The (perhaps reconstructed) ColorModel. */ public ColorModel getColorModel() { return colorModel; } /** * Serialize the ColorModelProxy. * * @param out The ObjectOutputStream. */ private void writeObject(ObjectOutputStream out) throws IOException { // Write serialized form to the stream. if(colorModel == null) { out.writeInt(COLORMODEL_NULL); } else if(colorModel instanceof ComponentColorModel) { ComponentColorModel cm = (ComponentColorModel)colorModel; int type = COLORMODEL_COMPONENT; if(colorModel instanceof FloatDoubleColorModel) { type = COLORMODEL_FLOAT_DOUBLE_COMPONENT; } out.writeInt(type); serializeColorSpace(cm.getColorSpace(), out); // ignore return if(type == COLORMODEL_COMPONENT) { out.writeObject(cm.getComponentSize()); } out.writeBoolean(cm.hasAlpha()); out.writeBoolean(cm.isAlphaPremultiplied()); out.writeInt(cm.getTransparency()); // Create a SampleModel to get the transferType. This is // absurd but is the only apparent way to retrieve this value. SampleModel sm = cm.createCompatibleSampleModel(1, 1); out.writeInt(sm.getTransferType()); } else if(colorModel instanceof IndexColorModel) { IndexColorModel cm = (IndexColorModel)colorModel; out.writeInt(COLORMODEL_INDEX); int size = cm.getMapSize(); int[] cmap = new int[size]; cm.getRGBs(cmap); out.writeInt(cm.getPixelSize()); out.writeInt(size); out.writeObject(cmap); out.writeBoolean(cm.hasAlpha()); out.writeInt(cm.getTransparentPixel()); // Create a SampleModel to get the transferType. This is // absurd but is the only apparent way to retrieve this value. SampleModel sm = cm.createCompatibleSampleModel(1, 1); out.writeInt(sm.getTransferType()); } else if(colorModel instanceof DirectColorModel) { DirectColorModel cm = (DirectColorModel)colorModel; out.writeInt(COLORMODEL_DIRECT); boolean csSerialized = serializeColorSpace(cm.getColorSpace(), out); if(!csSerialized) { out.writeBoolean(cm.hasAlpha()); } out.writeInt(cm.getPixelSize()); out.writeInt(cm.getRedMask()); out.writeInt(cm.getGreenMask()); out.writeInt(cm.getBlueMask()); if(csSerialized || cm.hasAlpha()) { out.writeInt(cm.getAlphaMask()); } if(csSerialized) { out.writeBoolean(cm.isAlphaPremultiplied()); // Create a SampleModel to get the transferType. This is // absurd but is the only apparent way to retrieve this // value. SampleModel sm = cm.createCompatibleSampleModel(1, 1); out.writeInt(sm.getTransferType()); } } else { throw new RuntimeException(JaiI18N.getString("ColorModelProxy0")); } } /** * Deserialize the ColorModelProxy. * * @param out The ObjectInputStream. */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { // Read serialized form from the stream. ColorSpace cs = null; // Switch on first int which is a flag indicating the class. switch((int)in.readInt()) { case COLORMODEL_NULL: colorModel = null; break; case COLORMODEL_FLOAT_DOUBLE_COMPONENT: if((cs = deserializeColorSpace(in)) == null) { colorModel = null; return; } colorModel = new FloatDoubleColorModel(cs, in.readBoolean(), in.readBoolean(), in.readInt(), in.readInt()); break; case COLORMODEL_COMPONENT: if((cs = deserializeColorSpace(in)) == null) { colorModel = null; return; } colorModel = new ComponentColorModel(cs, (int[])in.readObject(), in.readBoolean(), in.readBoolean(), in.readInt(), in.readInt()); break; case COLORMODEL_INDEX: colorModel = new IndexColorModel(in.readInt(), in.readInt(), (int[])in.readObject(), 0, in.readBoolean(), in.readInt(), in.readInt()); break; case COLORMODEL_DIRECT: if((cs = deserializeColorSpace(in)) != null) { colorModel = new DirectColorModel(cs, in.readInt(), in.readInt(), in.readInt(), in.readInt(), in.readInt(), in.readBoolean(), in.readInt()); } else if(in.readBoolean()) { colorModel = new DirectColorModel(in.readInt(), in.readInt(), in.readInt(), in.readInt(), in.readInt()); } else { colorModel = new DirectColorModel(in.readInt(), in.readInt(), in.readInt(), in.readInt()); } break; default: // NB: Should never get here. throw new RuntimeException(JaiI18N.getString("ColorModelProxy1")); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/RenderContextState.java0000644000175000017500000000557310203035544027040 0ustar mathieumathieu/* * $RCSfile: RenderContextState.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:53 $ * $State: Exp $ */ package com.sun.media.jai.rmi; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.image.renderable.RenderContext; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import javax.media.jai.ROIShape; import javax.media.jai.remote.SerializableState; import javax.media.jai.remote.SerializerFactory; /** * This class is a serializable proxy for a RenderContext from which the * RenderContext may be reconstituted. * * * @since 1.1 */ public class RenderContextState extends SerializableStateImpl { public static Class[] getSupportedClasses() { return new Class[] {RenderContext.class}; } /** * Constructs a RenderContextState from a * RenderContext. * * @param c The class of the object to be serialized. * @param o The RenderContext to be serialized. * @param h The RenderingHints (ignored). */ public RenderContextState(Class c, Object o, RenderingHints h) { super(c, o, h); } /** * Serialize the RenderContextState. * * @param out The ObjectOutputStream. */ private void writeObject(ObjectOutputStream out) throws IOException { RenderContext renderContext = (RenderContext)theObject; // Extract the affine transform. AffineTransform usr2dev = renderContext.getTransform(); // Extract the hints. RenderingHints hints = renderContext.getRenderingHints(); // Extract the AOI Shape aoi = renderContext.getAreaOfInterest(); // Write serialized form to the stream. out.writeObject(usr2dev); out.writeObject(SerializerFactory.getState(aoi)); out.writeObject(SerializerFactory.getState(hints, null)); } /** * Deserialize the RenderContextState. * * @param out The ObjectInputStream. */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { RenderContext renderContext = null; // Read serialized form from the stream. AffineTransform usr2dev = (AffineTransform)in.readObject(); SerializableState aoi = (SerializableState)in.readObject(); Shape shape = (Shape)aoi.getObject(); SerializableState rhs = (SerializableState)in.readObject(); RenderingHints hints = (RenderingHints)rhs.getObject(); // Restore the transient RenderContext. renderContext = new RenderContext(usr2dev, shape, hints); theObject = renderContext; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/RenderContextProxy.java0000644000175000017500000000662610203035544027101 0ustar mathieumathieu/* * $RCSfile: RenderContextProxy.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:53 $ * $State: Exp $ */ package com.sun.media.jai.rmi; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.image.renderable.RenderContext; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import javax.media.jai.ROIShape; /** * This class is a serializable proxy for a RenderContext from which the * RenderContext may be reconstituted. * * * @since EA3 */ public class RenderContextProxy implements Serializable { /** The RenderContext. */ private transient RenderContext renderContext; /** * Constructs a DatabufferProxy from a * Databuffer. * * @param source The Databuffer to be serialized. */ public RenderContextProxy(RenderContext source) { renderContext = source; } /** * Retrieves the associated Databuffer. * @return The (perhaps reconstructed) Databuffer. */ public RenderContext getRenderContext() { return renderContext; } /** * Serialize the RenderContextProxy. * * @param out The ObjectOutputStream. */ private void writeObject(ObjectOutputStream out) throws IOException { boolean isNull = renderContext == null; out.writeBoolean(isNull); if (isNull) return; // Extract the affine transform. AffineTransform usr2dev = renderContext.getTransform(); // Create serializable form of the hints. RenderingHintsProxy rhp = new RenderingHintsProxy(renderContext.getRenderingHints()); Shape aoi = renderContext.getAreaOfInterest(); // Write serialized form to the stream. out.writeObject(usr2dev); out.writeBoolean(aoi != null); if(aoi != null) { if(aoi instanceof Serializable){ out.writeObject(aoi); }else { out.writeObject(new ROIShape(aoi)); } } out.writeObject(rhp); } /** * Deserialize the RenderContextProxy. * * @param out The ObjectInputStream. */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { if (in.readBoolean()) { renderContext = null; return; } // Read serialized form from the stream. AffineTransform usr2dev = (AffineTransform)in.readObject(); Shape shape = null; Object aoi = in.readBoolean() ? (Object)in.readObject() : null; RenderingHintsProxy rhp = (RenderingHintsProxy)in.readObject(); RenderingHints hints = rhp.getRenderingHints(); // Restore the transient RenderContext. if (aoi != null){ if (aoi instanceof ROIShape){ shape = ((ROIShape)aoi).getAsShape(); }else { shape = (Shape)aoi; } } if(aoi == null && hints.isEmpty()) { renderContext = new RenderContext(usr2dev); } else if(aoi == null) { renderContext = new RenderContext(usr2dev, hints); } else if(hints.isEmpty()) { renderContext = new RenderContext(usr2dev, shape); } else { renderContext = new RenderContext(usr2dev, shape, hints); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/SampleModelProxy.java0000644000175000017500000002103410203035544026505 0ustar mathieumathieu/* * $RCSfile: SampleModelProxy.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:54 $ * $State: Exp $ */ package com.sun.media.jai.rmi; import java.awt.image.BandedSampleModel; import java.awt.image.ComponentSampleModel; import java.awt.image.DataBuffer; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.PixelInterleavedSampleModel; import java.awt.image.SampleModel; import java.awt.image.SinglePixelPackedSampleModel; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import javax.media.jai.ComponentSampleModelJAI; import javax.media.jai.RasterFactory; /** * This class is a serializable proxy for a SampleModel from which the * SampleModel may be reconstituted. * * * @since EA3 */ public class SampleModelProxy implements Serializable { /** Flag indicating a BandedSampleModel. */ private static final int TYPE_BANDED = 1; /** Flag indicating a PixelInterleavedSampleModel. */ private static final int TYPE_PIXEL_INTERLEAVED = 2; /** Flag indicating a SinglePixelPackedSampleModel. */ private static final int TYPE_SINGLE_PIXEL_PACKED = 3; /** Flag indicating a MultiPixelPackedSampleModel. */ private static final int TYPE_MULTI_PIXEL_PACKED = 4; /** Flag indicating a ComponentSampleModelJAI. */ private static final int TYPE_COMPONENT_JAI = 5; /** Flag indicating a generic ComponentSampleModel. */ private static final int TYPE_COMPONENT = 6; /** The SampleModel. */ private transient SampleModel sampleModel; /* parameter banded ileaved packed1 packedN --------- ------ ------- ------- ------- dataType * * * * width * * * * height * * * * numBands * bankIndices * bandOffsets * * pixelStride * scanlineStride * * * bitMasks * numberOfBits * dataBitOffset * */ /** * Constructs a SampleModelProxy from a * SampleModel. * * @param source The SampleModel to be serialized. */ public SampleModelProxy(SampleModel source) { sampleModel = source; } /** * Retrieves the associated SampleModel. * @return The (perhaps reconstructed) SampleModel. */ public SampleModel getSampleModel() { return sampleModel; } /** * Serialize the SampleModelProxy. * * @param out The ObjectOutputStream. */ private void writeObject(ObjectOutputStream out) throws IOException { if(sampleModel instanceof ComponentSampleModel) { ComponentSampleModel sm = (ComponentSampleModel)sampleModel; int sampleModelType = TYPE_COMPONENT; int transferType = sm.getTransferType(); if(sampleModel instanceof PixelInterleavedSampleModel) { sampleModelType = TYPE_PIXEL_INTERLEAVED; } else if(sampleModel instanceof BandedSampleModel) { sampleModelType = TYPE_BANDED; } else if(sampleModel instanceof ComponentSampleModelJAI || transferType == DataBuffer.TYPE_FLOAT || transferType == DataBuffer.TYPE_DOUBLE) { sampleModelType = TYPE_COMPONENT_JAI; } out.writeInt(sampleModelType); out.writeInt(transferType); out.writeInt(sm.getWidth()); out.writeInt(sm.getHeight()); if(sampleModelType != TYPE_BANDED) { out.writeInt(sm.getPixelStride()); } out.writeInt(sm.getScanlineStride()); if(sampleModelType != TYPE_PIXEL_INTERLEAVED) { out.writeObject(sm.getBankIndices()); } out.writeObject(sm.getBandOffsets()); } else if(sampleModel instanceof SinglePixelPackedSampleModel) { SinglePixelPackedSampleModel sm = (SinglePixelPackedSampleModel)sampleModel; out.writeInt(TYPE_SINGLE_PIXEL_PACKED); out.writeInt(sm.getTransferType()); out.writeInt(sm.getWidth()); out.writeInt(sm.getHeight()); out.writeInt(sm.getScanlineStride()); out.writeObject(sm.getBitMasks()); } else if(sampleModel instanceof MultiPixelPackedSampleModel) { MultiPixelPackedSampleModel sm = (MultiPixelPackedSampleModel)sampleModel; out.writeInt(TYPE_MULTI_PIXEL_PACKED); out.writeInt(sm.getTransferType()); out.writeInt(sm.getWidth()); out.writeInt(sm.getHeight()); out.writeInt(sm.getPixelBitStride()); out.writeInt(sm.getScanlineStride()); out.writeInt(sm.getDataBitOffset()); } else { throw new RuntimeException(JaiI18N.getString("SampleModelProxy0")); } } /** * Deserialize the SampleModelProxy. * * @param out The ObjectInputStream. */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { int sampleModelType = (int)in.readInt(); switch(sampleModelType) { case TYPE_PIXEL_INTERLEAVED: sampleModel = RasterFactory.createPixelInterleavedSampleModel(in.readInt(), in.readInt(), in.readInt(), in.readInt(), in.readInt(), (int[])in.readObject()); break; case TYPE_BANDED: sampleModel = RasterFactory.createBandedSampleModel(in.readInt(), in.readInt(), in.readInt(), in.readInt(), (int[])in.readObject(), (int[])in.readObject()); break; case TYPE_COMPONENT_JAI: sampleModel = new ComponentSampleModelJAI(in.readInt(), in.readInt(), in.readInt(), in.readInt(), in.readInt(), (int[])in.readObject(), (int[])in.readObject()); break; case TYPE_COMPONENT: sampleModel = new ComponentSampleModel(in.readInt(), in.readInt(), in.readInt(), in.readInt(), in.readInt(), (int[])in.readObject(), (int[])in.readObject()); break; case TYPE_SINGLE_PIXEL_PACKED: sampleModel = new SinglePixelPackedSampleModel(in.readInt(), in.readInt(), in.readInt(), in.readInt(), (int[])in.readObject()); break; case TYPE_MULTI_PIXEL_PACKED: sampleModel = new MultiPixelPackedSampleModel(in.readInt(), in.readInt(), in.readInt(), in.readInt(), in.readInt(), in.readInt()); break; default: throw new RuntimeException(JaiI18N.getString("SampleModelProxy0")); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/DataBufferState.java0000644000175000017500000001447610203035544026261 0ustar mathieumathieu/* * $RCSfile: DataBufferState.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:50 $ * $State: Exp $ */ package com.sun.media.jai.rmi; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferInt; import java.awt.image.DataBufferShort; import java.awt.image.DataBufferUShort; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import com.sun.media.jai.util.DataBufferUtils; /** * This class is a serializable proxy for a DataBuffer from which the * DataBuffer may be reconstituted. * * * @since 1.1 */ public class DataBufferState extends SerializableStateImpl { /** DataBufferFloat and DataBufferDouble core classes or null. */ private static Class[] J2DDataBufferClasses = null; /** The DataBuffer. */ private transient DataBuffer dataBuffer; // Initialize J2DDataBufferClasses. static { try { Class dbfClass = Class.forName("java.awt.image.DataBufferFloat"); Class dbdClass = Class.forName("java.awt.image.DataBufferDouble"); J2DDataBufferClasses = new Class[] {dbfClass, dbdClass}; } catch(ClassNotFoundException e) { // Ignore the exception. } } public static Class[] getSupportedClasses() { Class[] supportedClasses = null; if(J2DDataBufferClasses != null) { // Java 2 1.4.0 and higher. supportedClasses = new Class[] { DataBufferByte.class, DataBufferShort.class, DataBufferUShort.class, DataBufferInt.class, J2DDataBufferClasses[0], J2DDataBufferClasses[1], javax.media.jai.DataBufferFloat.class, javax.media.jai.DataBufferDouble.class, com.sun.media.jai.codecimpl.util.DataBufferFloat.class, com.sun.media.jai.codecimpl.util.DataBufferDouble.class }; } else { // Java 2 pre-1.4.0. supportedClasses = new Class[] { DataBufferByte.class, DataBufferShort.class, DataBufferUShort.class, DataBufferInt.class, javax.media.jai.DataBufferFloat.class, javax.media.jai.DataBufferDouble.class, com.sun.media.jai.codecimpl.util.DataBufferFloat.class, com.sun.media.jai.codecimpl.util.DataBufferDouble.class }; } return supportedClasses; } /** * Constructs a DataBufferState from a * DataBuffer. * * @param source The DataBuffer to be serialized. * @param o The SampleModel to be serialized. * @param h The RenderingHints (ignored). */ public DataBufferState(Class c, Object o, RenderingHints h) { super(c, o, h); } // XXX Note that there is potential for some form of data compression in // the readObject() and writeObject() methods. /** * Serialize the DataBufferState. * * @param out The ObjectOutputStream. */ private void writeObject(ObjectOutputStream out) throws IOException { DataBuffer dataBuffer = (DataBuffer)theObject; // Write serialized form to the stream. int dataType = dataBuffer.getDataType(); out.writeInt(dataType); out.writeObject(dataBuffer.getOffsets()); out.writeInt(dataBuffer.getSize()); Object dataArray = null; switch (dataType) { case DataBuffer.TYPE_BYTE: dataArray = ((DataBufferByte)dataBuffer).getBankData(); break; case DataBuffer.TYPE_SHORT: dataArray = ((DataBufferShort)dataBuffer).getBankData(); break; case DataBuffer.TYPE_USHORT: dataArray = ((DataBufferUShort)dataBuffer).getBankData(); break; case DataBuffer.TYPE_INT: dataArray = ((DataBufferInt)dataBuffer).getBankData(); break; case DataBuffer.TYPE_FLOAT: dataArray = DataBufferUtils.getBankDataFloat(dataBuffer); break; case DataBuffer.TYPE_DOUBLE: dataArray = DataBufferUtils.getBankDataDouble(dataBuffer); break; default: throw new RuntimeException(JaiI18N.getString("DataBufferState0")); } out.writeObject(dataArray); } /** * Deserialize the DataBufferState. * * @param out The ObjectInputStream. */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { DataBuffer dataBuffer = null; // Read serialized form from the stream. int dataType = -1; int[] offsets = null; int size = -1; Object dataArray = null; dataType = in.readInt(); offsets = (int[])in.readObject(); size = in.readInt(); dataArray = in.readObject(); // Restore the transient DataBuffer. switch (dataType) { case DataBuffer.TYPE_BYTE: dataBuffer = new DataBufferByte((byte[][])dataArray, size, offsets); break; case DataBuffer.TYPE_SHORT: dataBuffer = new DataBufferShort((short[][])dataArray, size, offsets); break; case DataBuffer.TYPE_USHORT: dataBuffer = new DataBufferUShort((short[][])dataArray, size, offsets); break; case DataBuffer.TYPE_INT: dataBuffer = new DataBufferInt((int[][])dataArray, size, offsets); break; case DataBuffer.TYPE_FLOAT: dataBuffer = DataBufferUtils.createDataBufferFloat((float[][])dataArray, size, offsets); break; case DataBuffer.TYPE_DOUBLE: dataBuffer = DataBufferUtils.createDataBufferDouble((double[][])dataArray, size, offsets); break; default: throw new RuntimeException(JaiI18N.getString("DataBufferState0")); } theObject = dataBuffer; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/RasterProxy.java0000644000175000017500000000720310203035544025545 0ustar mathieumathieu/* * $RCSfile: RasterProxy.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:52 $ * $State: Exp $ */ package com.sun.media.jai.rmi; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.BandedSampleModel; import java.awt.image.DataBuffer; import java.awt.image.ComponentSampleModel; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.PixelInterleavedSampleModel; import java.awt.image.Raster; import java.awt.image.SampleModel; import java.awt.image.SinglePixelPackedSampleModel; import java.awt.image.WritableRaster; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import javax.media.jai.RasterFactory; import javax.media.jai.remote.SerializableState; import javax.media.jai.remote.SerializerFactory; /** * This class is a serializable proxy for a Raster from which the * Raster may be reconstituted. * * * @since EA3 */ public class RasterProxy implements Serializable { /** The Raster. */ private transient Raster raster; /** * Constructs a RasterProxy from a * Raster. * * @param source The Raster to be serialized. */ public RasterProxy(Raster source) { raster = source; } /** * Retrieves the associated Raster. * @return The (perhaps reconstructed) Raster. */ public Raster getRaster() { return raster; } /** * Serialize the RasterProxy. * * @param out The ObjectOutputStream. */ private void writeObject(ObjectOutputStream out) throws IOException { Raster r; if (raster.getParent() != null) { // Use the child ratser to create another Raster // containing data for the requested bounds but which does // not share the SampleModel and DataBuffer of the parent. // Fix : 4631478 r = raster.createCompatibleWritableRaster(raster.getBounds()); ((WritableRaster)r).setRect(raster); } else { r = raster; } out.writeInt(r.getWidth()); out.writeInt(r.getHeight()); out.writeObject(SerializerFactory.getState(r.getSampleModel(), null)); out.writeObject(SerializerFactory.getState(r.getDataBuffer(), null)); out.writeObject(new Point(r.getMinX(), r.getMinY())); } /** * Deserialize the RasterProxy. * * @param out The ObjectInputStream. */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { int width; int height; SerializableState sampleModelState = null; SerializableState dataBufferState = null; Point location = null; width = in.readInt(); height = in.readInt(); sampleModelState = (SerializableState)in.readObject(); dataBufferState = (SerializableState)in.readObject(); location = (Point)in.readObject(); // Restore the SampleModel from its serialized form. SampleModel sampleModel = (SampleModel)sampleModelState.getObject(); if(sampleModel == null) { raster = null; return; } // Restore the DataBuffer from its serialized form. DataBuffer dataBuffer = (DataBuffer)dataBufferState.getObject(); // Reconstruct the Raster. raster = Raster.createRaster(sampleModel, dataBuffer, location); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/JAIRMIUtil.java0000644000175000017500000002016110203035544025052 0ustar mathieumathieu/* * $RCSfile: JAIRMIUtil.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:51 $ * $State: Exp $ */package com.sun.media.jai.rmi; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.io.Serializable; import java.util.Vector; import java.util.Hashtable; import javax.media.jai.RenderedOp; import javax.media.jai.PlanarImage; import javax.media.jai.remote.RemoteRenderedOp; import javax.media.jai.remote.SerializableRenderedImage; /** * A class containing utility methods used for the implementation of * the "jairmi" protocol. */ public final class JAIRMIUtil { /** * Replaces any element in the source Vector which is an ID * with the server-side node that is associated with the * given ID. */ public static Vector replaceIdWithSources(Vector srcs, Hashtable nodes, String opName, RenderingHints hints) { Vector replacedSrcs = new Vector(); Object obj; for (int i=0; iRMIServerProxy with * the id of the server-side node that is represented by the * RMIServerProxy. */ public static Vector replaceSourcesWithId(Vector srcs, String serverName) { Vector replacedSrcs = new Vector(); Object obj; for (int i=0; iRasterState from a * Raster. * * @param c The Raster subclass. * @param o The Raster object to be serialized. * @param h The RenderingHints (ignored). */ public RasterState(Class c, Object o, RenderingHints h) { super(c, o, h); } /** * Serialize the RasterState. * * @param out The ObjectOutputStream. */ private void writeObject(ObjectOutputStream out) throws IOException { Raster raster = (Raster)theObject; Raster r; if (raster.getParent() != null) { // Use the child ratser to create another Raster // containing data for the requested bounds but which does // not share the SampleModel and DataBuffer of the parent. // Fix : 4631478 r = raster.createCompatibleWritableRaster(raster.getBounds()); ((WritableRaster)r).setRect(raster); } else { r = raster; } out.writeInt(r.getWidth()); out.writeInt(r.getHeight()); out.writeObject(SerializerFactory.getState(r.getSampleModel(), null)); out.writeObject(SerializerFactory.getState(r.getDataBuffer(), null)); out.writeObject(new Point(r.getMinX(), r.getMinY())); } /** * Deserialize the RasterState. * * @param out The ObjectInputStream. */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { int width; int height; SerializableState sampleModelState = null; SerializableState dataBufferState = null; Point location = null; width = in.readInt(); height = in.readInt(); sampleModelState = (SerializableState)in.readObject(); dataBufferState = (SerializableState)in.readObject(); location = (Point)in.readObject(); // Restore the SampleModel from its serialized form. SampleModel sampleModel = (SampleModel)sampleModelState.getObject(); if (sampleModel == null) { theObject = null; return; } // Restore the DataBuffer from its serialized form. DataBuffer dataBuffer = (DataBuffer)dataBufferState.getObject(); // Reconstruct the Raster. theObject = Raster.createRaster(sampleModel, dataBuffer, location); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/RMIServerProxy.java0000644000175000017500000010660610203035544026132 0ustar mathieumathieu/* * $RCSfile: RMIServerProxy.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:52 $ * $State: Exp $ */package com.sun.media.jai.rmi; import java.awt.Image; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.renderable.RenderContext; import java.awt.image.renderable.ParameterBlock; import java.io.ByteArrayInputStream; import java.io.Serializable; import java.net.InetAddress; import java.rmi.Naming; import java.rmi.RemoteException; import java.util.Collection; import java.util.List; import java.util.Iterator; import java.util.Vector; import javax.media.jai.ImageLayout; import javax.media.jai.JAI; import javax.media.jai.remote.NegotiableCapability; import javax.media.jai.OperationRegistry; import javax.media.jai.OperationDescriptor; import javax.media.jai.OperationNode; import javax.media.jai.ParameterListDescriptor; import javax.media.jai.PlanarImage; import javax.media.jai.PropertyChangeEventJAI; import javax.media.jai.PropertySourceChangeEvent; import javax.media.jai.RenderingChangeEvent; import javax.media.jai.RenderedOp; import javax.media.jai.RenderableOp; import javax.media.jai.TileCache; import javax.media.jai.remote.JAIRMIDescriptor; import javax.media.jai.remote.NegotiableCapabilitySet; import javax.media.jai.remote.RemoteImagingException; import javax.media.jai.remote.RemoteRenderedOp; import javax.media.jai.remote.PlanarImageServerProxy; import javax.media.jai.remote.SerializerFactory; import javax.media.jai.remote.SerializableState; import javax.media.jai.remote.SerializableRenderedImage; import javax.media.jai.tilecodec.TileCodecDescriptor; import javax.media.jai.tilecodec.TileCodecParameterList; import javax.media.jai.tilecodec.TileDecoder; import javax.media.jai.tilecodec.TileDecoderFactory; import javax.media.jai.tilecodec.TileEncoderFactory; import javax.media.jai.util.ImagingException; import javax.media.jai.util.ImagingListener; import com.sun.media.jai.util.ImageUtil; public class RMIServerProxy extends PlanarImageServerProxy { /** The server object our data will come from. */ private ImageServer remoteImage = null; /** The RMI ID of this object. */ private Long id; /** * The ID associated with the Rendering of the corresponding * RenderableOp. */ private Long renderingID = null; // Boolean to indicate whether PlanarImageServerProxy set the // negotiation preferences when super(...) was called. private boolean preferencesSet; // NegotiableCapabilitySet that stores the negotiation preferences // that were set by PlanarImageServerProxy when super(...) was called. private NegotiableCapabilitySet negPref; // The class of the serializable representation of a NULL property. private static final Class NULL_PROPERTY_CLASS = com.sun.media.jai.rmi.JAIRMIImageServer.NULL_PROPERTY.getClass(); // Cache the listener private ImagingListener listener; /** * Construct an RMIServerProxy. This constructor should only be used * when the source is a RenderedOp on a different server. */ public RMIServerProxy(String serverName, String opName, RenderingHints hints) { super(serverName, "jairmi", opName, null, hints); // Look for a separator indicating the remote image chaining hack // in which case the serverName argument contains host[:port]::id // where id is the RMI ID of the image on the indicated server. int index = serverName.indexOf("::"); boolean remoteChaining = index != -1; if(!remoteChaining) { // Don't throw the IllegalArgumentException if it's the hack. throw new IllegalArgumentException(JaiI18N.getString("RemoteImage1")); } if(remoteChaining) { // Extract the RMI ID from the servername string and replace // the original serverName string with one of the usual type. id = Long.valueOf(serverName.substring(index+2)); serverName = serverName.substring(0, index); super.serverName = serverName; } listener = ImageUtil.getImagingListener(hints); remoteImage = getImageServer(serverName); if (preferencesSet) { super.setNegotiationPreferences(negPref); } try { // Increment the reference count for this id on the server remoteImage.incrementRefCount(id); } catch (RemoteException re) { System.err.println(JaiI18N.getString("RMIServerProxy2")); } } /** * Construct an RMIServerProxy. This constructor should only be used * when the source is a RenderedOp on a different server and the * ParameterBlock for the source itself is available. */ public RMIServerProxy(String serverName, ParameterBlock pb, String opName, RenderingHints hints){ super(serverName, "jairmi", opName, pb, hints); // Look for a separator indicating the remote image chaining hack // in which case the serverName argument contains host[:port]::id // where id is the RMI ID of the image on the indicated server. int index = serverName.indexOf("::"); boolean remoteChaining = index != -1; if(!remoteChaining) { // Don't throw the IllegalArgumentException if it's the hack. throw new IllegalArgumentException(JaiI18N.getString("RemoteImage1")); } if(remoteChaining) { // Extract the RMI ID from the servername string and replace // the original serverName string with one of the usual type. id = Long.valueOf(serverName.substring(index+2)); serverName = serverName.substring(0, index); super.serverName = serverName; } listener = ImageUtil.getImagingListener(hints); remoteImage = getImageServer(serverName); if (preferencesSet) { super.setNegotiationPreferences(negPref); } try { // Increment the reference count for this id on the server remoteImage.incrementRefCount(id); } catch (RemoteException re) { System.err.println(JaiI18N.getString("RMIServerProxy2")); } } /** * Constructs an RMIServerProxy. This constructor creates nodes on * the server corresponding to a RemoteRenderedOp on the client. */ public RMIServerProxy(String serverName, String operationName, ParameterBlock paramBlock, RenderingHints hints) { super(serverName, "jairmi", operationName, paramBlock, hints); listener = ImageUtil.getImagingListener(hints); // Construct the remote RMI image. remoteImage = getImageServer(serverName); // Get the RMI ID for this object. getRMIID(); // If PlanarImageServerProxy had set the preferences during the // call to the super constructor, then honor that now. if (preferencesSet) super.setNegotiationPreferences(negPref); // Create a RenderedOp on the server for this operation. ParameterBlock newPB = (ParameterBlock)paramBlock.clone(); newPB.removeSources(); // Check to see whether any of the parameters are images JAIRMIUtil.checkClientParameters(newPB, serverName); try { SerializableState rhs = SerializerFactory.getState(hints, null); remoteImage.createRenderedOp(id, operationName, newPB, rhs); } catch (RemoteException e) { String message = JaiI18N.getString("RMIServerProxy5"); listener.errorOccurred(message, new RemoteImagingException(message, e), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(e)); } RenderedImage source; int size = getNumSources(); for (int i=0; i < size; i++) { source = getSource(i); if (source instanceof RMIServerProxy) { try { RMIServerProxy rop = (RMIServerProxy)source; if (rop.serverName.equalsIgnoreCase(this.serverName)){ // Send the id of the source remoteImage.setRenderedSource(id, rop.getRMIID(), i); } else { remoteImage.setRenderedSource(id, rop.getRMIID(), rop.serverName, rop.operationName, i); } } catch (RemoteException e) { String message = JaiI18N.getString("RMIServerProxy6"); listener.errorOccurred(message, new RemoteImagingException(e), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(e)); } } else if (source instanceof RenderedOp) { /// XXX This should not happen, since by the time a // RMIServerProxy is created, all its sources should already // have been rendered. In any case, the following deals // correctly with the situation if it should arise. RenderedOp rop = (RenderedOp)source; RenderedImage rendering = rop.getRendering(); if (!(rendering instanceof Serializable)) rendering = new SerializableRenderedImage(rendering); try { remoteImage.setRenderedSource(id, rendering, i); } catch(RemoteException e) { String message = JaiI18N.getString("RMIServerProxy6"); listener.errorOccurred(message, new RemoteImagingException(message, e), this, false); /* throw new RemoteImagingException( ImageUtil.getStackTraceString(e)); */ } } else if (source instanceof RenderedImage) { try { if (source instanceof Serializable) { remoteImage.setRenderedSource(id, source, i); } else { remoteImage.setRenderedSource( id, new SerializableRenderedImage(source), i); } } catch(RemoteException e) { String message = JaiI18N.getString("RMIServerProxy6"); listener.errorOccurred(message, new RemoteImagingException(message, e), this, false); /* throw new RemoteImagingException( ImageUtil.getStackTraceString(e)); */ } } } try { // Increment the reference count for this id on the server remoteImage.incrementRefCount(id); } catch (RemoteException re) { System.err.println(JaiI18N.getString("RMIServerProxy2")); } } /** * Creates a RMIServerProxy which is the new rendering * produced when the serverName is updated. */ public RMIServerProxy(PlanarImageServerProxy oldRendering, OperationNode node, String newServerName) { // Simply create a new RMIServerProxy that creates a new node // on the new server. this(newServerName, node.getOperationName(), node.getParameterBlock(), node.getRenderingHints()); } /** * Creates a RMIServerProxy which is the new rendering * produced by updating the given old rendering by the changes * specified by the given PropertyChangeEventJAI. */ public RMIServerProxy(PlanarImageServerProxy oldRendering, OperationNode node, PropertyChangeEventJAI event) { super (((RemoteRenderedOp)node).getServerName(), "jairmi", node.getOperationName(), node.getParameterBlock(), node.getRenderingHints()); listener = ImageUtil.getImagingListener(node.getRenderingHints()); remoteImage = getImageServer(serverName); RMIServerProxy oldRMISP = null; if (oldRendering instanceof RMIServerProxy) { oldRMISP = (RMIServerProxy)oldRendering; } else { System.err.println(JaiI18N.getString("RMIServerProxy3")); } Long opID = oldRMISP.getRMIID(); String propName = event.getPropertyName(); if (event instanceof RenderingChangeEvent) { // Event is a RenderingChangeEvent RenderingChangeEvent rce = (RenderingChangeEvent)event; // Get index of source which changed. int idx = ((RenderedOp)node).getSources().indexOf(rce.getSource()); PlanarImage oldSrcRendering = (PlanarImage)event.getOldValue(); Object oldSrc = null; String serverNodeDesc = null; if (oldSrcRendering instanceof RMIServerProxy) { RMIServerProxy oldSrcRMISP = (RMIServerProxy)oldSrcRendering; if (oldSrcRMISP.getServerName().equalsIgnoreCase( this.serverName) == false) { serverNodeDesc = oldSrcRMISP.getServerName() + "::" + oldSrcRMISP.getRMIID(); } else { serverNodeDesc = oldSrcRMISP.getRMIID().toString(); } oldSrc = serverNodeDesc; } else if (oldSrcRendering instanceof Serializable) { oldSrc = oldSrcRendering; } else { oldSrc = new SerializableRenderedImage(oldSrcRendering); } Object srcInvalidRegion = rce.getInvalidRegion(); SerializableState shapeState = SerializerFactory.getState((Shape)srcInvalidRegion, null); Long oldRenderingID = null; try { oldRenderingID = remoteImage.handleEvent(opID, idx, shapeState, oldSrc); } catch (RemoteException re) { String message = JaiI18N.getString("RMIServerProxy7"); listener.errorOccurred(message, new RemoteImagingException(message, re), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(re)); } oldRMISP.id = oldRenderingID; this.id = opID; } else { // Changes to operationName, operationRegistry, protocolName // and protocolAndServerName should never be sent to this // constructor and thus don't need to be handled here. // Changes to serverName should be sent only to the previous // constructor and thus do not need to be handled here. Object oldValue = null, newValue = null; if (propName.equals("operationname")) { oldValue = event.getOldValue(); newValue = event.getNewValue(); } else if (propName.equals("parameterblock")) { ParameterBlock oldPB = (ParameterBlock)event.getOldValue(); Vector oldSrcs = oldPB.getSources(); oldPB.removeSources(); ParameterBlock newPB = (ParameterBlock)event.getNewValue(); Vector newSrcs = newPB.getSources(); newPB.removeSources(); // XXX Check serverName is correct thing to pass JAIRMIUtil.checkClientParameters(oldPB, serverName); JAIRMIUtil.checkClientParameters(newPB, serverName); oldPB.setSources(JAIRMIUtil.replaceSourcesWithId(oldSrcs, serverName)); newPB.setSources(JAIRMIUtil.replaceSourcesWithId(newSrcs, serverName)); oldValue = oldPB; newValue = newPB; } else if (propName.equals("sources")) { Vector oldSrcs = (Vector)event.getOldValue(); Vector newSrcs = (Vector)event.getNewValue(); oldValue = JAIRMIUtil.replaceSourcesWithId(oldSrcs, serverName); newValue = JAIRMIUtil.replaceSourcesWithId(newSrcs, serverName); } else if (propName.equals("parameters")) { Vector oldParameters = (Vector)event.getOldValue(); Vector newParameters = (Vector)event.getNewValue(); // XXX Check serverName is correct thing to pass JAIRMIUtil.checkClientParameters(oldParameters, serverName); JAIRMIUtil.checkClientParameters(newParameters, serverName); oldValue = oldParameters; newValue = newParameters; } else if (propName.equals("renderinghints")) { RenderingHints oldRH = (RenderingHints)event.getOldValue(); RenderingHints newRH = (RenderingHints)event.getNewValue(); oldValue = SerializerFactory.getState(oldRH, null); newValue = SerializerFactory.getState(newRH, null); } else { throw new RemoteImagingException( JaiI18N.getString("RMIServerProxy4")); } Long oldRenderingID = null; try { oldRenderingID = remoteImage.handleEvent(opID, propName, oldValue, newValue); // Increment the reference count for this id on the server remoteImage.incrementRefCount(oldRenderingID); } catch (RemoteException re) { String message = JaiI18N.getString("RMIServerProxy7"); listener.errorOccurred(message, new RemoteImagingException(message, re), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(re)); } oldRMISP.id = oldRenderingID; this.id = opID; } // If PlanarImageServerProxy had set the preferences during the // call to the super constructor, then honor that now. if (preferencesSet) super.setNegotiationPreferences(negPref); } /** * Create an RMIServerProxy to access an already created operation * (as specified by the supplied id) on the server. */ public RMIServerProxy(String serverName, String operationName, ParameterBlock pb, RenderingHints hints, Long id) { super (serverName, "jairmi", operationName, pb, hints); listener = ImageUtil.getImagingListener(hints); //Construct the the remote ImageServer remoteImage = getImageServer(serverName); this.id = id; } /** * the RMIServerProxy for the Renderable Layer */ public RMIServerProxy(String serverName, String operationName, ParameterBlock paramBlock, RenderContext rc, boolean isRender) { super(serverName, "jairmi", operationName, paramBlock, null); listener = ImageUtil.getImagingListener(rc.getRenderingHints()); //Construct the the remote ImageServer remoteImage = getImageServer(serverName); // get the Remote ID getRMIID(); if (preferencesSet) super.setNegotiationPreferences(negPref); // Create a RenderableOp on the server for this operation. ParameterBlock newPB = (ParameterBlock)paramBlock.clone(); newPB.removeSources(); // XXX Since checking to see whether any of the parameters are images // causes problems with the "renderable" operator (the RenderedOp // downsampler chain needs to be sent to the server as a RenderedOp, // and checkClientParameters would make it a RenderedImage), we do // not do checkClientParameters here (in renderable). This currently // works because there are no renderable operations which have images // as parameters. aastha 09/26/01 try { remoteImage.createRenderableOp(id, operationName, newPB); } catch(RemoteException e) { String message = JaiI18N.getString("RMIServerProxy8"); listener.errorOccurred(message, new RemoteImagingException(message, e), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(e)); } Object source; int size = getNumSources(); for (int i=0; i < size; i++) { Vector sources = paramBlock.getSources(); source = sources.elementAt(i); if (source instanceof RMIServerProxy) { try { RMIServerProxy rop = (RMIServerProxy)source; // Send the id of the source if ((rop.serverName).equals(this.serverName)){ remoteImage.setRenderableSource(id, rop.getRMIID(), i); } else { remoteImage.setRenderableSource(id, rop.getRMIID(), rop.serverName, rop.operationName, i); } } catch (RemoteException e) { String message = JaiI18N.getString("RMIServerProxy6"); listener.errorOccurred(message, new RemoteImagingException(message, e), this, false); /* throw new RemoteImagingException( ImageUtil.getStackTraceString(e)); */ } } else if (source instanceof RenderableOp) { try { remoteImage.setRenderableSource(id, (RenderableOp)source, i); } catch(RemoteException e) { String message = JaiI18N.getString("RMIServerProxy6"); listener.errorOccurred(message, new RemoteImagingException(message, e), this, false); /* throw new RemoteImagingException( ImageUtil.getStackTraceString(e)); */ } } else if (source instanceof RenderedImage) { try { remoteImage.setRenderableSource( id, new SerializableRenderedImage((RenderedImage)source), i); } catch(RemoteException e) { String message = JaiI18N.getString("RMIServerProxy6"); listener.errorOccurred(message, new RemoteImagingException(message, e), this, false); /* throw new RemoteImagingException( ImageUtil.getStackTraceString(e)); */ } } } try { // Increment the reference count for this id on the server remoteImage.incrementRefCount(id); } catch (RemoteException e) { String message = JaiI18N.getString("RMIServerProxy9"); listener.errorOccurred(message, new RemoteImagingException(message, e), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(e)); } // If this was a call for Rendering of this RenderableOp // then render it and store the associated id in // renderingID and then RMICRIF will return a new RMISP with reference // to that rendering ID // This will not be executed at the time of calls to getBounds2D and // mapRenderContext if (isRender){ try { renderingID = remoteImage.getRendering(id, SerializerFactory.getState(rc, null)); // Increment the reference count for this id on the server remoteImage.incrementRefCount(renderingID); } catch (RemoteException e) { String message = JaiI18N.getString("RMIServerProxy10"); listener.errorOccurred(message, new RemoteImagingException(message, e), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(e)); } } } /** * Construct an ImageServer on the indicated server. * *

    The name of the server must be supplied in the form *

         * host:port
         * 
    * where the port number is optional and may be supplied only if * the host name is supplied. If this parameter is null the default * is to search for the ImageServer service on the local host at the * default rmiregistry port (1099). * *

    The result is cached in the instance variable "remoteImage". * * @param serverName The name of the server in the format described. */ protected synchronized ImageServer getImageServer(String serverName) { if (remoteImage == null) { if(serverName == null) { try { serverName = InetAddress.getLocalHost().getHostAddress(); } catch(Exception e) { String message = JaiI18N.getString("RMIServerProxy11"); listener.errorOccurred(message, new RemoteImagingException(message, e), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(e)); } } // Derive the service name. String serviceName = new String("rmi://"+serverName+"/"+ JAIRMIDescriptor.IMAGE_SERVER_BIND_NAME); // Look up the remote object. remoteImage = null; try { remoteImage = (ImageServer)Naming.lookup(serviceName); } catch(Exception e) { String message = JaiI18N.getString("RMIServerProxy12"); listener.errorOccurred(message, new RemoteImagingException(message, e), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(e)); } } return remoteImage; } /** * Get the unique ID to be used to refer to this object on the server. * The result is cached in the instance variable "id". */ public synchronized Long getRMIID() { if (id != null) { return id; } try { id = remoteImage.getRemoteID(); return id; } catch(Exception e) { String message = JaiI18N.getString("RMIServerProxy13"); listener.errorOccurred(message, new RemoteImagingException(message, e), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(e)); } return id; } public Long getRenderingID(){ return renderingID; } public boolean canBeRendered(){ boolean cbr = true; //XXX: please verify getImageServer(serverName); try { cbr = remoteImage.getRendering(getRMIID()); } catch (RemoteException re){ String message = JaiI18N.getString("RMIServerProxy10"); listener.errorOccurred(message, new RemoteImagingException(message, re), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(re)); } return cbr; } /* * Disposes of any resources allocated for remote operation. */ protected void finalize() { try { remoteImage.dispose(id); } catch(Exception e) { // Ignore the Exception. } super.dispose(); } /** * Gets the image layout variables from the server and creates * an ImageLayout object initialized with these values. * * @throws RemoteImagingException if a RemoteException is thrown * during the RMI communication. */ public ImageLayout getImageLayout() throws RemoteImagingException { ImageLayout layout = new ImageLayout(); try { layout.setMinX(remoteImage.getMinX(id)); layout.setMinY(remoteImage.getMinY(id)); layout.setWidth(remoteImage.getWidth(id)); layout.setHeight(remoteImage.getHeight(id)); layout.setTileWidth(remoteImage.getTileWidth(id)); layout.setTileHeight(remoteImage.getTileHeight(id)); layout.setTileGridXOffset(remoteImage.getTileGridXOffset(id)); layout.setTileGridYOffset(remoteImage.getTileGridYOffset(id)); SerializableState smState = remoteImage.getSampleModel(id); layout.setSampleModel((SampleModel)(smState.getObject())); SerializableState cmState = remoteImage.getColorModel(id); layout.setColorModel((ColorModel)(cmState.getObject())); return layout; } catch (RemoteException re) { String message = JaiI18N.getString("RMIServerProxy14"); listener.errorOccurred(message, new RemoteImagingException(message, re), this, false); return null; // throw new RemoteImagingException(ImageUtil.getStackTraceString(re)); } } /** * Gets the requested tile from the server, which does the processing * to produce the desired tile. * * @throws a RemoteImagingException if a RemoteException is thrown * during the RMI communication. */ public Raster computeTile(int tileX, int tileY) throws RemoteImagingException { // Return null if the requested tile is outside this image's boundary. if (tileX < getMinTileX() || tileX > getMaxTileX() || tileY < getMinTileY() || tileY > getMaxTileY()) { return null; } // Since "tileCodec" is the only category that we care about or honor // currently in the remote communication. NegotiableCapability codecCap = getNegotiatedValue("tileCodec"); TileDecoderFactory tdf = null; TileCodecParameterList tcpl = null; if (codecCap != null) { String category = codecCap.getCategory(); String capabilityName = codecCap.getCapabilityName(); List generators = codecCap.getGenerators(); Class factory; for (Iterator i=generators.iterator(); i.hasNext(); ) { factory = (Class)i.next(); if (tdf == null && TileDecoderFactory.class.isAssignableFrom(factory)) { try { tdf = (TileDecoderFactory)factory.newInstance(); } catch (InstantiationException ie) { throw new RemoteImagingException(ImageUtil.getStackTraceString(ie)); } catch (IllegalAccessException iae) { throw new RemoteImagingException(ImageUtil.getStackTraceString(iae)); } } } if (tdf == null) { throw new RemoteImagingException( JaiI18N.getString("RMIServerProxy0")); } TileCodecDescriptor tcd = (TileCodecDescriptor)registry.getDescriptor("tileDecoder", capabilityName); if (tcd.includesSampleModelInfo() == false || tcd.includesLocationInfo() == false) { throw new RemoteImagingException( JaiI18N.getString("RMIServerProxy1")); } ParameterListDescriptor pld = tcd.getParameterListDescriptor("tileDecoder"); tcpl = new TileCodecParameterList(capabilityName, new String[] {"tileDecoder"}, pld); // Set parameters on TileCodecParameterList only if there are any // parameters defined. if (pld != null) { String paramNames[] = pld.getParamNames(); String currParam; Object currValue; if (paramNames != null) { for (int i=0; igetRemoteProperty * method. Network errors encountered should be signalled by * throwing a RemoteImagingException. * * @throws RemoteImagingException if an error condition during remote * image processing occurs */ public String[] getRemotePropertyNames() throws RemoteImagingException { try { return remoteImage.getPropertyNames(id); } catch (RemoteException re) { String message = JaiI18N.getString("RMIServerProxy17"); listener.errorOccurred(message, new RemoteImagingException(message, re), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(re)); } return null; } /** * Returns a conservative estimate of the destination region that * can potentially be affected by the pixels of a rectangle of a * given source. This can be implemented by either asking the server * to compute the destination region, or by having the client compute * the destination region. Network errors encountered should be * signalled by throwing a RemoteImagingException. * * @param sourceRect The Rectangle in source coordinates. * @param sourceIndex The index of the source image. * * @return A Rectangle indicating the potentially * affected destination region, or null if * the region is unknown. * * @throws IllegalArgumentException If the source index is * negative or greater than that of the last source. * @throws IllegalArgumentException If sourceRect is * null. */ public Rectangle mapSourceRect(Rectangle sourceRect, int sourceIndex) throws RemoteImagingException { Rectangle dstRect = null; try { dstRect = remoteImage.mapSourceRect(id, sourceRect, sourceIndex); } catch (RemoteException re) { String message = JaiI18N.getString("RMIServerProxy18"); listener.errorOccurred(message, new RemoteImagingException(message, re), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(re)); } return dstRect; } /** * Returns a conservative estimate of the region of a specified * source that is required in order to compute the pixels of a * given destination rectangle. Either the server or the client can * compute the source region to implement this method. Network errors * encountered should be signalled by throwing a * RemoteImagingException. * * @param destRect The Rectangle in destination coordinates. * @param sourceIndex The index of the source image. * * @return A Rectangle indicating the required source region. * * @throws IllegalArgumentException If the source index is * negative or greater than that of the last source. * @throws IllegalArgumentException If destRect is * null. */ public Rectangle mapDestRect(Rectangle destRect, int sourceIndex) throws RemoteImagingException { Rectangle srcRect = null; try { srcRect = remoteImage.mapDestRect(id, destRect, sourceIndex); } catch (RemoteException re) { String message = JaiI18N.getString("RMIServerProxy18"); listener.errorOccurred(message, new RemoteImagingException(message, re), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(re)); } return srcRect; } public void setNegotiationPreferences(NegotiableCapabilitySet preferences) { if (remoteImage == null) { this.negPref = preferences; preferencesSet = true; } else { super.setNegotiationPreferences(preferences); } } /** * Informs the server of the negotiated values that are the result of * a successful negotiation. * * @param negotiatedValues The result of the negotiation. */ public synchronized void setServerNegotiatedValues(NegotiableCapabilitySet negotiatedValues) throws RemoteImagingException { try { remoteImage.setServerNegotiatedValues(id, negotiatedValues); } catch (RemoteException re) { String message = JaiI18N.getString("RMIServerProxy19"); listener.errorOccurred(message, new RemoteImagingException(message, re), this, false); // throw new RemoteImagingException(ImageUtil.getStackTraceString(re)); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/RenderingKeyState.java0000644000175000017500000000430310203035544026630 0ustar mathieumathieu/* * $RCSfile: RenderingKeyState.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:54 $ * $State: Exp $ */ package com.sun.media.jai.rmi; import java.awt.RenderingHints; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Hashtable; import javax.media.jai.remote.SerializerFactory; /** * This class is a serializable proxy for the predefined * RenderingHints.Key objects. * For a hint key, the name of the class which contains the declaration * of this key and the field name of this declaration are recorded. * * * @since 1.1 */ public class RenderingKeyState extends SerializableStateImpl { /** * Returns the classes supported by this SerializableState. */ public static Class[] getSupportedClasses() { return new Class[] {RenderingHints.Key.class}; } /** Support subclasses as Raster is a factory class. */ public static boolean permitsSubclasses() { return true; } private transient RenderingHintsState.HintElement predefinedKey; /** * Constructs a RenderingKeyState from a * RenderingHints.Key object. * * @param c The Class of the object to be serialized. * @param o The object to be serialized. * @param h The RenderingHints used in serialization. */ public RenderingKeyState(Class c, Object o, RenderingHints h) { super(c, o, h); Hashtable predefinedObjects = RenderingHintsState.getHintTable(); predefinedKey = (RenderingHintsState.HintElement)predefinedObjects.get(o); if (predefinedKey == null) throw new RuntimeException(JaiI18N.getString("RenderingKeyState0")); } private void writeObject(ObjectOutputStream out) throws IOException { out.writeObject(predefinedKey); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { predefinedKey = (RenderingHintsState.HintElement)in.readObject(); theObject = predefinedKey.getObject(); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/JaiI18N.java0000644000175000017500000000074110203035544024346 0ustar mathieumathieu/* * $RCSfile: JaiI18N.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:52 $ * $State: Exp $ */ package com.sun.media.jai.rmi; import com.sun.media.jai.util.PropertyUtil; class JaiI18N { static String packageName = "com.sun.media.jai.rmi"; public static String getString(String key) { return PropertyUtil.getString(packageName, key); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/JAIRMIImageServer.java0000644000175000017500000014470410203035544026360 0ustar mathieumathieu/* * $RCSfile: JAIRMIImageServer.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:51 $ * $State: Exp $ */package com.sun.media.jai.rmi; import java.awt.Dimension; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.geom.Rectangle2D; import java.awt.image.ColorModel; import java.awt.image.SampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.image.renderable.ContextualRenderedImageFactory; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderContext; import java.awt.image.renderable.RenderableImage; import java.io.ByteArrayOutputStream; import java.io.Serializable; import java.net.InetAddress; import java.rmi.Naming; import java.rmi.Remote; import java.rmi.RemoteException; import java.rmi.RMISecurityManager; import java.rmi.server.UnicastRemoteObject; import java.util.Collection; import java.util.Hashtable; import java.util.List; import java.util.Iterator; import java.util.Vector; import javax.media.jai.CollectionOp; import javax.media.jai.CollectionImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptor; import javax.media.jai.OperationRegistry; import javax.media.jai.OpImage; import javax.media.jai.ParameterListDescriptor; import javax.media.jai.PlanarImage; import javax.media.jai.PropertyChangeEventJAI; import javax.media.jai.PropertySource; import javax.media.jai.RenderingChangeEvent; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.CRIFRegistry; import javax.media.jai.remote.JAIRMIDescriptor; import javax.media.jai.remote.RemoteImagingException; import javax.media.jai.remote.RemoteRenderedOp; import javax.media.jai.remote.NegotiableCapability; import javax.media.jai.remote.NegotiableCapabilitySet; import javax.media.jai.remote.SerializableRenderedImage; import javax.media.jai.remote.SerializableState; import javax.media.jai.remote.SerializerFactory; import javax.media.jai.tilecodec.TileCodecDescriptor; import javax.media.jai.tilecodec.TileCodecParameterList; import javax.media.jai.tilecodec.TileDecoderFactory; import javax.media.jai.tilecodec.TileEncoder; import javax.media.jai.tilecodec.TileEncoderFactory; import javax.media.jai.util.ImagingListener; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.util.Service; import com.sun.media.jai.remote.JAIServerConfigurationSpi; /** * The server-side implementation of the ImageServer interface. A * JAIRMIImageServer has a RenderedImage source, acquired via one of three * setSource() methods. The first takes a RenderedImage directly as * its parameter; this image is simply copied over the network using * the normal RMI mechanisms. Note that not every image can be * transferred in this way -- for example, attempting to pass an * OpImage that uses native code or that depends on the availability * of a class not resident on the server as a parameter will cause an * exception to be thrown. * *

    The second and third ways of setting sources make use of the * RenderedOp and RenderableOp classes to send a high-level * description of an image chain based on operation names. This * chain will be copied over to the server using RMI, where it will be * expanded into an OpImage chain using the server's registry. This * is the preferred method since it requires less data transfer and * offers a better chance of success. It may still fail if the * sources or parameters of any operation in the chain are not * serializable. * *

    RMI requires all remote methods to declare `throws * RemoteException' in their signatures. It is up to the client to * deal with errors. A simple implementation of error handling may be * found in the RemoteRenderedImage class. * *

    This class contains a main() method that should be run on the * server after starting the RMI registry. The registry will then * construct new instances of JAIRMIImageServer on demand. * * @see ImageServer * @see RenderedOp * * @since 1.1 */ public class JAIRMIImageServer extends UnicastRemoteObject implements ImageServer { private boolean DEBUG = true; /** Tag to represent a null property. */ public static final Object NULL_PROPERTY = RMIImageImpl.NULL_PROPERTY; /** Identifier counter for the remote images. */ private static long idCounter = 0; /** * The RenderedImage nodes hashed by an ID string which must be unique * across all possible clients of this object. */ private static Hashtable nodes = new Hashtable(); /** * Hashtable to store the negotiated values for each id. */ private static Hashtable negotiated = new Hashtable(); /** * Hashtable to store the number of references existing to a * particular id on this server. */ private static Hashtable refCount = new Hashtable(); /** * Retrieve a PlanarImage source from the Hashtable of sources. * * @param id The unique ID of the source. * @return The source. */ private static PlanarImage getSource(Long id) throws RemoteException { Object obj = null; if(nodes == null || (obj = nodes.get(id)) == null) { throw new RemoteException(JaiI18N.getString("RMIImageImpl2")); } return (PlanarImage)obj; } /** * Retrieve a PropertySource from the Hashtable of PropertySources. * * @param id The unique ID of the source. * @return The PropertySource. */ private static PropertySource getPropertySource(Long id) throws RemoteException { Object obj = nodes.get(id); return (PropertySource)obj; } /** * Constructs a JAIRMIImageServer with a source to be specified * later. */ public JAIRMIImageServer(int serverport) throws RemoteException { super(serverport); } /** * Returns the identifier of the remote image. This method should be * called to return an identifier before any other methods are invoked. * The same ID must be used in all subsequent references to the remote * image. */ public synchronized Long getRemoteID() throws RemoteException { return new Long(++idCounter); } /** * Disposes of any resouces allocated to the client object with * the specified ID. */ public synchronized void dispose(Long id) throws RemoteException { int count = ((Integer)refCount.get(id)).intValue(); if (count == 1) { // If this was the last reference, remove all Objects // associated with this id in various Hashtables. if (nodes != null) { nodes.remove(id); negotiated.remove(id); } refCount.remove(id); } else { // Decrement count of references to this id. count--; if (count == 0) { refCount.remove(id); } refCount.put(id, new Integer(count)); } } /** * Increments the reference count for this id, i.e. increments the * number of RMIServerProxy objects that currently reference this id. */ public void incrementRefCount(Long id) throws RemoteException { Integer iCount = (Integer)refCount.get(id); int count = 0; if (iCount != null) { count = iCount.intValue(); } count++; refCount.put(id, new Integer(count)); } /** Gets a property from the property set of this image. If the * property is undefined the constant NULL_PROPERTY is returned. */ public Object getProperty(Long id, String name) throws RemoteException { PropertySource ps = getPropertySource(id); Object property = ps.getProperty(name); if (property == null || property.equals(java.awt.Image.UndefinedProperty)) { property = NULL_PROPERTY; } return property; } /** * Returns a list of names recognized by getProperty(). * * @return an array of Strings representing property names. */ public String[] getPropertyNames(Long id) throws RemoteException { PropertySource ps = getPropertySource(id); return ps.getPropertyNames(); } /** * Returns a list of names recognized by getProperty(). * * @return an array of Strings representing property names. */ public String[] getPropertyNames(String opName) throws RemoteException { return (CRIFRegistry.get(null, opName)).getPropertyNames(); } /** Returns the minimum X coordinate of the ImageServer. */ public int getMinX(Long id) throws RemoteException { return getSource(id).getMinX(); } /** Returns the smallest X coordinate to the right of the ImageServer. */ public int getMaxX(Long id) throws RemoteException { return getSource(id).getMaxX(); } /** Returns the minimum Y coordinate of the ImageServer. */ public int getMinY(Long id) throws RemoteException { return getSource(id).getMinY(); } /** Returns the smallest Y coordinate below the ImageServer. */ public int getMaxY(Long id) throws RemoteException { return getSource(id).getMaxY(); } /** Returns the width of the ImageServer. */ public int getWidth(Long id) throws RemoteException { return getSource(id).getWidth(); } /** Returns the height of the ImageServer. */ public int getHeight(Long id) throws RemoteException { return getSource(id).getHeight(); } /** Returns the width of a tile in pixels. */ public int getTileWidth(Long id) throws RemoteException { return getSource(id).getTileWidth(); } /** Returns the height of a tile in pixels. */ public int getTileHeight(Long id) throws RemoteException { return getSource(id).getTileHeight(); } /** * Returns the X coordinate of the upper-left pixel of tile (0, 0). */ public int getTileGridXOffset(Long id) throws RemoteException { return getSource(id).getTileGridXOffset(); } /** * Returns the Y coordinate of the upper-left pixel of tile (0, 0). */ public int getTileGridYOffset(Long id) throws RemoteException { return getSource(id).getTileGridYOffset(); } /** Returns the index of the leftmost column of tiles. */ public int getMinTileX(Long id) throws RemoteException { return getSource(id).getMinTileX(); } /** * Returns the number of tiles along the tile grid in the horizontal * direction. */ public int getNumXTiles(Long id) throws RemoteException { return getSource(id).getNumXTiles(); } /** Returns the index of the uppermost row of tiles. */ public int getMinTileY(Long id) throws RemoteException { return getSource(id).getMinTileY(); } /** * Returns the number of tiles along the tile grid in the vertical * direction. */ public int getNumYTiles(Long id) throws RemoteException { return getSource(id).getNumYTiles(); } /** Returns the index of the rightmost column of tiles. */ public int getMaxTileX(Long id) throws RemoteException { return getSource(id).getMaxTileX(); } /** Returns the index of the bottom row of tiles. */ public int getMaxTileY(Long id) throws RemoteException { return getSource(id).getMaxTileY(); } /** Returns the SampleModel associated with this image. */ public SerializableState getSampleModel(Long id) throws RemoteException { return SerializerFactory.getState(getSource(id).getSampleModel(), null); } /** Returns the ColorModel associated with this image. */ public SerializableState getColorModel(Long id) throws RemoteException { return SerializerFactory.getState(getSource(id).getColorModel(), null); } /** Returns a Rectangle indicating the image bounds. */ public Rectangle getBounds(Long id) throws RemoteException { return getSource(id).getBounds(); } /** * Returns tile (x, y). Note that x and y are indices into the * tile array, not pixel locations. Unlike in the true RenderedImage * interface, the Raster that is returned should be considered a copy. * * @param id An ID for the source which must be unique across all clients. * @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 as a Raster. */ public SerializableState getTile(Long id, int tileX, int tileY) throws RemoteException { Raster r = getSource(id).getTile(tileX, tileY); return SerializerFactory.getState(r, null); } /** * Compresses tile (x, y) and returns the compressed tile's contents * as a byte array. Note that x and y are indices into the * tile array, not pixel locations. Unlike in the true RenderedImage * interface, the Raster that is returned should be considered a copy. * * @param id An ID for the source which must be unique across all clients. * @param x the x index of the requested tile in the tile array * @param y the y index of the requested tile in the tile array * @return a byte array containing the compressed tile contents. */ public byte[] getCompressedTile(Long id, int x, int y) throws RemoteException { TileCodecParameterList tcpl = null; TileEncoderFactory tef = null; NegotiableCapability codecCap = null; if (negotiated != null) { codecCap = ((NegotiableCapabilitySet)negotiated.get(id)). getNegotiatedValue("tileCodec"); } if (codecCap != null) { String category = codecCap.getCategory(); String capabilityName = codecCap.getCapabilityName(); List generators = codecCap.getGenerators(); Class factory; for (Iterator i=generators.iterator(); i.hasNext(); ) { factory = (Class)i.next(); if (tef == null && TileEncoderFactory.class.isAssignableFrom(factory)) { try { tef = (TileEncoderFactory)factory.newInstance(); } catch (InstantiationException ie) { throw new RuntimeException(ie.getMessage()); } catch (IllegalAccessException iae) { throw new RuntimeException(iae.getMessage()); } } } if (tef == null) { throw new RuntimeException( JaiI18N.getString("JAIRMIImageServer0")); } TileCodecDescriptor tcd = (TileCodecDescriptor)JAI.getDefaultInstance(). getOperationRegistry().getDescriptor("tileEncoder", capabilityName); if (tcd.includesSampleModelInfo() == false || tcd.includesLocationInfo() == false) { throw new RuntimeException( JaiI18N.getString("JAIRMIImageServer1")); } ParameterListDescriptor pld = tcd.getParameterListDescriptor("tileEncoder"); tcpl = new TileCodecParameterList(capabilityName, new String[] {"tileEncoder"}, pld); if (pld != null) { String paramNames[] = pld.getParamNames(); String currParam; Object currValue; if (paramNames != null) { for (int i=0; iRenderableImage stored against the given ID. * * @return the width of the renderable image in user coordinates. */ public float getRenderableWidth(Long id) throws RemoteException { RenderableImage ri = (RenderableImage)nodes.get(id); return ri.getWidth(); } /** * Gets the height (in user coordinate space) of the * RenderableImage stored against the given ID. * * @return the height of the renderable image in user coordinates. */ public float getRenderableHeight(Long id) throws RemoteException { RenderableImage ri = (RenderableImage)nodes.get(id); return ri.getHeight(); } /** * 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(Long id, int w, int h, SerializableState hintsState) throws RemoteException { RenderableImage ri = (RenderableImage)nodes.get(id); RenderingHints hints = (RenderingHints)hintsState.getObject(); RenderedImage rendering = ri.createScaledRendering(w, h, hints); if (rendering instanceof Serializable) { return rendering; } else { return new SerializableRenderedImage(rendering); } } /** * 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. */ public RenderedImage createDefaultRendering(Long id) throws RemoteException { RenderableImage ri = (RenderableImage)nodes.get(id); RenderedImage rendering = ri.createDefaultRendering(); if (rendering instanceof Serializable) { return rendering; } else { return new SerializableRenderedImage(rendering); } } /** * 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. */ public RenderedImage createRendering(Long id, SerializableState renderContextState) throws RemoteException { RenderableImage ri = (RenderableImage)nodes.get(id); RenderContext renderContext = (RenderContext)renderContextState.getObject(); RenderedImage rendering = ri.createRendering(renderContext); if (rendering instanceof Serializable) { return rendering; } else { return new SerializableRenderedImage(rendering); } } /** * Creates a RenderableOp on the server side with a parameter block * empty of sources. The sources are set by separate calls depending * upon the type and serializabilty of the source. */ public synchronized void createRenderableOp(Long id, String opName, ParameterBlock pb) throws RemoteException { // XXX Since RMIServerProxy does not do a checkClientParameters, this // side obviously does not do the corresponding // checkServerParameters. Look at RMIServerProxy's renderable // constructor for reasoning. aastha, 09/26/01 RenderableOp node = new RenderableOp(opName, pb); nodes.put(id, node); } /** * Calls for rendering of a RenderableOp with the given SerializableState */ public synchronized Long getRendering(Long id, SerializableState rcs) throws RemoteException { RenderableOp op = (RenderableOp)nodes.get(id); PlanarImage pi = PlanarImage.wrapRenderedImage(op.createRendering( (RenderContext)rcs.getObject())); Long renderingID = getRemoteID(); nodes.put(renderingID, pi); // Put the op's negotiated result values for its rendering too. setServerNegotiatedValues(renderingID, (NegotiableCapabilitySet) negotiated.get(id)); return renderingID; } /** * Sets the source of the image which is on the same * server */ public synchronized void setRenderableSource(Long id, Long sourceId, int index) throws RemoteException { RenderableOp node = (RenderableOp)nodes.get(id); Object obj = nodes.get(sourceId); if (obj instanceof RenderableOp){ node.setSource((RenderableOp)obj, index); } else if (obj instanceof RenderedImage) { node.setSource(PlanarImage.wrapRenderedImage((RenderedImage)obj), index); } } /** * Sets the source of the image which is on a different * server */ public synchronized void setRenderableSource(Long id, Long sourceId, String serverName, String opName, int index) throws RemoteException { RenderableOp node = (RenderableOp)nodes.get(id); node.setSource(new RMIServerProxy((serverName+"::"+sourceId), opName, null), index); } /** * Sets the source of the image which is on a different * server */ public synchronized void setRenderableRMIServerProxyAsSource( Long id, Long sourceId, String serverName, String opName, int index) throws RemoteException { RenderableOp node = (RenderableOp)nodes.get(id); node.setSource(new RenderableRMIServerProxy(serverName, opName, null, sourceId), index); } /** * when source is set to a RenderableOp and isnt supposed to be * rendered yet. like at the time of getBounds2D * * Sets the source of the image as a RenderableOp on the server side * */ public synchronized void setRenderableSource(Long id, RenderableOp source, int index) throws RemoteException { RenderableOp op = (RenderableOp)nodes.get(id); op.setSource(source, index); } /** * Sets the source of the image as a RenderableImage on the server side */ public synchronized void setRenderableSource(Long id, SerializableRenderableImage s, int index) throws RemoteException { RenderableOp op = (RenderableOp)nodes.get(id); op.setSource(s, index); } /** * Sets the source of the image as a RenderedImage on the server side */ public synchronized void setRenderableSource(Long id, RenderedImage source, int index) throws RemoteException { PlanarImage pi = PlanarImage.wrapRenderedImage(source); RenderableOp op = (RenderableOp)nodes.get(id); op.setSource(pi, index); } /** * Maps the RenderContext for the remote Image */ public SerializableState mapRenderContext(int id, Long nodeId, String operationName, SerializableState rcs) throws RemoteException { // Retrieve the RenderableOp for the rendering of which // the mapRenderContext call is being made. RenderableOp rop = (RenderableOp)nodes.get(nodeId); //Find the CRIF for the respective operation ContextualRenderedImageFactory crif = CRIFRegistry.get(rop.getRegistry(), operationName); if (crif == null) { throw new RuntimeException( JaiI18N.getString("JAIRMIImageServer3")); } RenderContext rc = crif.mapRenderContext(id, (RenderContext)rcs.getObject(), (ParameterBlock)rop.getParameterBlock(), rop); return SerializerFactory.getState(rc, null); } /** * Gets the Bounds2D of the specified Remote Image */ public SerializableState getBounds2D(Long nodeId, String operationName) throws RemoteException { // Retrieve the RenderableOp for whose RIF // the mapRenderContext call is being made. RenderableOp rop = (RenderableOp)nodes.get(nodeId); //Find the CRIF for the respective operation ContextualRenderedImageFactory crif = CRIFRegistry.get(rop.getRegistry(), operationName); if (crif == null) { throw new RuntimeException( JaiI18N.getString("JAIRMIImageServer3")); } Rectangle2D r2D = crif.getBounds2D((ParameterBlock)rop.getParameterBlock()); return SerializerFactory.getState(r2D, null); } /** * Returns true if successive renderings with the same * arguments may produce different results for this opName * * @return false indicating that the rendering is static. */ public boolean isDynamic(String opName) throws RemoteException { return (CRIFRegistry.get(null, opName)).isDynamic(); } /** * Returns true if successive renderings with the same * arguments may produce different results for the node represented * by the given id. */ public boolean isDynamic(Long id) throws RemoteException { RenderableImage node = (RenderableImage)nodes.get(id); return node.isDynamic(); } /** * Gets the operation names supported on the Server */ public String[] getServerSupportedOperationNames() throws RemoteException { return JAI.getDefaultInstance().getOperationRegistry(). getDescriptorNames(OperationDescriptor.class); } /** * Gets the OperationDescriptors of the operations * supported on this server. */ public List getOperationDescriptors() throws RemoteException { return JAI.getDefaultInstance().getOperationRegistry(). getDescriptors(OperationDescriptor.class); } /** * Calculates the region over which two distinct renderings * of an operation may be expected to differ. * *

    The class of the returned object will vary as a function of * the nature of the operation. For rendered and renderable two- * dimensional images this should be an instance of a class which * implements java.awt.Shape. * * @return The region over which the data of two renderings of this * operation may be expected to be invalid or null * if there is no common region of validity. */ public synchronized SerializableState getInvalidRegion( Long id, ParameterBlock oldParamBlock, SerializableState oldRHints, ParameterBlock newParamBlock, SerializableState newRHints) throws RemoteException { RenderingHints oldHints = (RenderingHints)oldRHints.getObject(); RenderingHints newHints = (RenderingHints)newRHints.getObject(); RenderedOp op = (RenderedOp)nodes.get(id); OperationDescriptor od = (OperationDescriptor) JAI.getDefaultInstance().getOperationRegistry(). getDescriptor("rendered", op.getOperationName()); boolean samePBs = false; if (oldParamBlock == newParamBlock) samePBs = true; Vector oldSources = oldParamBlock.getSources(); oldParamBlock.removeSources(); Vector oldReplacedSources = JAIRMIUtil.replaceIdWithSources(oldSources, nodes, op.getOperationName(), op.getRenderingHints()); oldParamBlock.setSources(oldReplacedSources); if (samePBs) { newParamBlock = oldParamBlock; } else { Vector newSources = newParamBlock.getSources(); newParamBlock.removeSources(); Vector newReplacedSources = JAIRMIUtil.replaceIdWithSources(newSources, nodes, op.getOperationName(), op.getRenderingHints()); newParamBlock.setSources(newReplacedSources); } Object invalidRegion = od.getInvalidRegion("rendered", oldParamBlock, oldHints, newParamBlock, newHints, op); SerializableState shapeState = SerializerFactory.getState((Shape)invalidRegion, null); return shapeState; } /** * Returns a conservative estimate of the destination region that * can potentially be affected by the pixels of a rectangle of a * given source. * * @param id A Long identifying the node for whom * the destination region needs to be calculated . * @param sourceRect The Rectangle in source coordinates. * @param sourceIndex The index of the source image. * * @return A Rectangle indicating the potentially * affected destination region, or null if * the region is unknown. */ public Rectangle mapSourceRect(Long id, Rectangle sourceRect, int sourceIndex) throws RemoteException { RenderedOp op = (RenderedOp)nodes.get(id); OpImage rendering = (OpImage)(op.getRendering()); return rendering.mapSourceRect(sourceRect, sourceIndex); } /** * Returns a conservative estimate of the region of a specified * source that is required in order to compute the pixels of a * given destination rectangle. * * @param id A Long identifying the node for whom * the source region needs to be calculated . * @param destRect The Rectangle in destination coordinates. * @param sourceIndex The index of the source image. * * @return A Rectangle indicating the required source region. */ public Rectangle mapDestRect(Long id, Rectangle destRect, int sourceIndex) throws RemoteException { RenderedOp op = (RenderedOp)nodes.get(id); OpImage rendering = (OpImage)(op.getRendering()); return rendering.mapDestRect(destRect, sourceIndex); } /** * A method that handles the given event. */ public synchronized Long handleEvent(Long renderedOpID, String propName, Object oldValue, Object newValue) throws RemoteException { RenderedOp op = (RenderedOp)nodes.get(renderedOpID); PlanarImage rendering = op.getRendering(); // Get a new unique ID Long id = getRemoteID(); // Cache the old rendering against the new id nodes.put(id, rendering); // Put the op's negotiated result values for its rendering too. setServerNegotiatedValues(id, (NegotiableCapabilitySet) negotiated.get(renderedOpID)); // A PropertyChangeEventJAI with name "operationregistry", // "protocolname", "protocolandservername" or "servername" should // never be received here, since it is handled entirely on the // client side, so we don't handle those here. if (propName.equals("operationname")) { op.setOperationName((String)newValue); } else if (propName.equals("parameterblock")) { ParameterBlock newPB = (ParameterBlock)newValue; Vector newSrcs = newPB.getSources(); newPB.removeSources(); JAIRMIUtil.checkServerParameters(newPB, nodes); Vector replacedSources = JAIRMIUtil.replaceIdWithSources(newSrcs, nodes, op.getOperationName(), op.getRenderingHints()); newPB.setSources(replacedSources); op.setParameterBlock(newPB); // Remove the newly created sinks of the srcs in the newPB Vector newSources = newPB.getSources(); if(newSources != null && newSources.size() > 0) { Iterator it = newSources.iterator(); while(it.hasNext()) { Object src = it.next(); if(src instanceof PlanarImage) { ((PlanarImage)src).removeSinks(); } else if(src instanceof CollectionImage) { ((CollectionImage)src).removeSinks(); } } } } else if (propName.equals("sources")) { Vector replacedSources = JAIRMIUtil.replaceIdWithSources((Vector)newValue, nodes, op.getOperationName(), op.getRenderingHints()); op.setSources(replacedSources); // Remove the newly created sinks for the replacedSources if(replacedSources != null && replacedSources.size() > 0) { Iterator it = replacedSources.iterator(); while(it.hasNext()) { Object src = it.next(); if(src instanceof PlanarImage) { ((PlanarImage)src).removeSinks(); } else if(src instanceof CollectionImage) { ((CollectionImage)src).removeSinks(); } } } } else if (propName.equals("parameters")) { Vector parameters = (Vector)newValue; JAIRMIUtil.checkServerParameters(parameters, nodes); op.setParameters(parameters); } else if (propName.equals("renderinghints")) { SerializableState newState = (SerializableState)newValue; op.setRenderingHints((RenderingHints)newState.getObject()); } return id; } /** * A method that handles a change in one of it's source's rendering, * i.e. a change that would be signalled by RenderingChangeEvent. */ public synchronized Long handleEvent(Long renderedOpID, int srcIndex, SerializableState srcInvalidRegion, Object oldRendering) throws RemoteException { RenderedOp op = (RenderedOp)nodes.get(renderedOpID); PlanarImage rendering = op.getRendering(); // Get a new unique ID Long id = getRemoteID(); // Cache the old rendering against the new id nodes.put(id, rendering); // Put the op's negotiated result values for its rendering too. setServerNegotiatedValues(id, (NegotiableCapabilitySet) negotiated.get(renderedOpID)); PlanarImage oldSrcRendering = null, newSrcRendering = null; String serverNodeDesc = null; Object src = null; if (oldRendering instanceof String) { serverNodeDesc = (String)oldRendering; int index = serverNodeDesc.indexOf("::"); boolean diffServer = index != -1; if (diffServer) { // Create an RMIServerProxy to access the node on a // different server oldSrcRendering = new RMIServerProxy(serverNodeDesc, op.getOperationName(), op.getRenderingHints()); } else { src = nodes.get(Long.valueOf(serverNodeDesc)); if (src instanceof RenderedOp) { oldSrcRendering = ((RenderedOp)src).getRendering(); } else { oldSrcRendering = PlanarImage.wrapRenderedImage((RenderedImage)src); } } } else { oldSrcRendering = PlanarImage.wrapRenderedImage((RenderedImage)oldRendering); } Object srcObj = op.getSource(srcIndex); if (srcObj instanceof RenderedOp) { newSrcRendering = ((RenderedOp)srcObj).getRendering(); } else if (srcObj instanceof RenderedImage) { newSrcRendering = PlanarImage.wrapRenderedImage((RenderedImage)srcObj); } Shape invalidRegion = (Shape)srcInvalidRegion.getObject(); RenderingChangeEvent rcEvent = new RenderingChangeEvent((RenderedOp)op.getSource(srcIndex), oldSrcRendering, newSrcRendering, invalidRegion); op.propertyChange(rcEvent); return id; } /** * Returns the server's capabilities. Currently the only capabilities * that are reported are those dealing with TileCodecs. */ public synchronized NegotiableCapabilitySet getServerCapabilities() { OperationRegistry registry = JAI.getDefaultInstance().getOperationRegistry(); // Note that only the tileEncoder capabilities are returned from // this method since there is no way to distinguish between NC's // for the encoder and the decoder. String modeName = "tileEncoder"; String[] descriptorNames = registry.getDescriptorNames(modeName); TileEncoderFactory tef = null; // Only non-preference NC's can be added. NegotiableCapabilitySet capabilities = new NegotiableCapabilitySet(false); Iterator it; for (int i=0; i The usage of this class is * *

         * java -Djava.rmi.server.codebase=file:$JAI/lib/jai.jar \
         * -Djava.rmi.server.useCodebaseOnly=false \
         * -Djava.security.policy=\
         * file:`pwd`/policy com.sun.media.jai.rmi.JAIRMIImageServer \
         * [-host hostName] [-port portNumber]
         * 
    * * The default host is the local host and the default port is 1099. * * @param args the port number as a command-line argument. */ public static void main(String [] args) { // Set the security manager. if(System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager()); } // Load all JAIServerConfigurationSpi implementations on the CLASSPATH Iterator spiIter = Service.providers(JAIServerConfigurationSpi.class); JAI jai = JAI.getDefaultInstance(); while (spiIter.hasNext()) { JAIServerConfigurationSpi serverSpi = (JAIServerConfigurationSpi)spiIter.next(); serverSpi.updateServer(jai); } // Set the host name and port number. String host = null; int rmiRegistryPort = 1099; // default port is 1099 int serverport = 0; if (args.length != 0) { String value; for (int i=0; i The server name or server IP address"); System.out.println("\t-port The port that rmiregistry is running on"); System.out.println("\t-rmiRegistryPort Same as -port option"); System.out.println("\t-serverPort The port that the server should listen on, for connections from clients"); System.out.println("\t-cacheMemCapacity The memory capacity in bytes."); System.out.println("\t-cacheMemThreshold The memory threshold, which is the fractional amount of cache memory to retain during tile removal"); System.out.println("\t-disableDefaultCache Disable use of default tile cache. Tiles are not stored."); System.out.println("\t-schedulerParallelism The degree of parallelism of the default TileScheduler"); System.out.println("\t-schedulerPrefetchParallelism The degree of parallelism of the default TileScheduler for tile prefetching"); System.out.println("\t-schedulerPriority The priority of tile scheduling for the default TileScheduler"); System.out.println("\t-schedulerPrefetchPriority The priority of tile prefetch scheduling for the default TileScheduler"); System.out.println("\t-defaultTileSize x The default tile dimensions in the form x"); System.out.println("\t-defaultRenderingSize x The default size to render a RenderableImage to, in the form x"); System.out.println("\t-serializeDeepCopy Whether a deep copy of the image data should be used when serializing images"); System.out.println("\t-tileCodecFormat The default format to be used for tile serialization via TileCodecs"); System.out.println("\t-retryInterval The retry interval value to be used for dealing with network errors during remote imaging"); System.out.println("\t-numRetries The number of retries to be used for dealing with network errors during remote imaging"); } else if (args[i].equalsIgnoreCase("-host")) { host = args[++i]; } else if (args[i].equalsIgnoreCase("-port") || args[i].equalsIgnoreCase("-rmiRegistryPort")) { rmiRegistryPort = Integer.parseInt(args[++i]); } else if (args[i].equalsIgnoreCase("-serverport")) { serverport = Integer.parseInt(args[++i]); } else if (args[i].equalsIgnoreCase("-cacheMemCapacity")) { jai.getTileCache().setMemoryCapacity( Long.parseLong(args[++i])); } else if (args[i].equalsIgnoreCase("-cacheMemThreshold")) { jai.getTileCache().setMemoryThreshold( Float.parseFloat(args[++i])); } else if (args[i].equalsIgnoreCase("-disableDefaultCache")) { jai.disableDefaultTileCache(); } else if (args[i].equalsIgnoreCase("-schedulerParallelism")) { jai.getTileScheduler().setParallelism( Integer.parseInt(args[++i])); } else if (args[i].equalsIgnoreCase("-schedulerPrefetchParallelism")) { jai.getTileScheduler().setPrefetchParallelism( Integer.parseInt(args[++i])); } else if (args[i].equalsIgnoreCase("-schedulerPriority")) { jai.getTileScheduler().setPriority( Integer.parseInt(args[++i])); } else if (args[i].equalsIgnoreCase("-schedulerPrefetchPriority")) { jai.getTileScheduler().setPrefetchPriority( Integer.parseInt(args[++i])); } else if (args[i].equalsIgnoreCase("-defaultTileSize")) { value = args[++i].toLowerCase(); int xpos = value.indexOf("x"); int xSize = Integer.parseInt(value.substring(0, xpos)); int ySize = Integer.parseInt(value.substring(xpos+1)); jai.setDefaultTileSize(new Dimension(xSize, ySize)); } else if (args[i].equalsIgnoreCase("-defaultRenderingSize")) { value = args[++i].toLowerCase(); int xpos = value.indexOf("x"); int xSize = Integer.parseInt(value.substring(0, xpos)); int ySize = Integer.parseInt(value.substring(xpos+1)); jai.setDefaultRenderingSize(new Dimension(xSize, ySize)); } else if (args[i].equalsIgnoreCase("-serializeDeepCopy")) { jai.setRenderingHint(JAI.KEY_SERIALIZE_DEEP_COPY, Boolean.valueOf(args[++i])); } else if (args[i].equalsIgnoreCase("-tileCodecFormat")) { jai.setRenderingHint(JAI.KEY_TILE_CODEC_FORMAT, args[++i]); } else if (args[i].equalsIgnoreCase("-retryInterval")) { jai.setRenderingHint(JAI.KEY_RETRY_INTERVAL, Integer.valueOf(args[++i])); } else if (args[i].equalsIgnoreCase("-numRetries")) { jai.setRenderingHint(JAI.KEY_NUM_RETRIES, Integer.valueOf(args[++i])); } } } // Default to the local host if the host was not specified. if(host == null) { try { host = InetAddress.getLocalHost().getHostAddress(); } catch(java.net.UnknownHostException e) { String message = JaiI18N.getString("RMIImageImpl1"); sendExceptionToListener(message, new RemoteImagingException(message, e)); /* System.err.println(JaiI18N.getString("RMIImageImpl1") + e.getMessage()); e.printStackTrace(); */ } } System.out.println(JaiI18N.getString("RMIImageImpl3")+" "+ host + ":" + rmiRegistryPort); try { JAIRMIImageServer im = new JAIRMIImageServer(serverport); String serverName = new String("rmi://" + host + ":" + rmiRegistryPort + "/" + JAIRMIDescriptor.IMAGE_SERVER_BIND_NAME); System.out.println(JaiI18N.getString("RMIImageImpl4")+" \""+ serverName+"\"."); Naming.rebind(serverName, im); System.out.println(JaiI18N.getString("RMIImageImpl5")); } catch (Exception e) { String message = JaiI18N.getString("RMIImageImpl1"); sendExceptionToListener(message, new RemoteImagingException(message, e)); /* System.err.println(JaiI18N.getString("RMIImageImpl0") + e.getMessage()); e.printStackTrace(); */ } } private static void sendExceptionToListener(String message, Exception e) { ImagingListener listener = ImageUtil.getImagingListener((RenderingHints)null); listener.errorOccurred(message, new RemoteImagingException(message, e), JAIRMIImageServer.class, false); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/VectorState.java0000644000175000017500000000574410203035544025516 0ustar mathieumathieu/* * $RCSfile: VectorState.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:55 $ * $State: Exp $ */ package com.sun.media.jai.rmi; import java.awt.RenderingHints; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Vector; import java.util.Iterator; import javax.media.jai.remote.SerializerFactory; import javax.media.jai.remote.SerializableState; /** * This class is a serializable proxy for a Vector object. *
    (entries which are neither Serializable nor supported by * SerializerFactory are omitted); * * * @since 1.1 */ public class VectorState extends SerializableStateImpl { /** * Returns the classes supported by this SerializableState. */ public static Class[] getSupportedClasses() { return new Class[] {Vector.class}; } /** * Constructs a VectorState from a * Vector object. * * @param c The Class of the object to be serialized. * @param o The Vector object to be serialized. * @param h The RebderingHint for this serialization. */ public VectorState(Class c, Object o, RenderingHints h) { super(c, o, h); } /** * Serialize the VectorState. */ private void writeObject(ObjectOutputStream out) throws IOException { // -- Create a serializable form of the Vector object. -- Vector vector = (Vector)theObject; Vector serializableVector = new Vector(); Iterator iterator = vector.iterator(); // If there are hints, add them to the vector. while (iterator.hasNext()){ Object object = iterator.next(); Object serializableObject = getSerializableForm(object); serializableVector.add(serializableObject); } // Write serialized form to the stream. out.writeObject(serializableVector); } /** * Deserialize the VectorState. */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { // Read serialized form from the stream. Vector serializableVector = (Vector)in.readObject(); // Create an empty Vector object. Vector vector = new Vector(); theObject = vector; // If the vector is empty just return. if (serializableVector.isEmpty()) { return; } // Get an enumeration of the vector keys. Iterator iterator = serializableVector.iterator(); // Loop over the vector keys. while (iterator.hasNext()) { // Get the next key element. Object serializableObject = iterator.next(); Object object = getDeserializedFrom(serializableObject); // Add an entry to the vector. vector.add(object); } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/SerializableRenderableImage.java0000644000175000017500000010334510203035544030604 0ustar mathieumathieu/* * $RCSfile: SerializableRenderableImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:54 $ * $State: Exp $ */package com.sun.media.jai.rmi; /* XXX See if the SerializableRenderedImage can be sent by requests instead of deep copy. */ import java.awt.Image; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderableImage; import java.awt.image.renderable.RenderContext; import java.io.InputStream; import java.io.InterruptedIOException; import java.io.IOException; import java.io.NotSerializableException; import java.io.OutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.net.InetAddress; import java.net.Socket; import java.net.SocketException; import java.net.ServerSocket; import java.net.UnknownHostException; import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; import javax.media.jai.OperationRegistry; import javax.media.jai.remote.SerializableState; import javax.media.jai.remote.SerializerFactory; import javax.media.jai.remote.SerializableRenderedImage; import javax.media.jai.tilecodec.TileCodecParameterList; import javax.media.jai.tilecodec.TileDecoder; import javax.media.jai.tilecodec.TileEncoder; import javax.media.jai.tilecodec.TileDecoderFactory; import javax.media.jai.tilecodec.TileEncoderFactory; import javax.media.jai.util.CaselessStringKey; /** * A serializable wrapper class for classes which implement the * RenderableImage interface. * *

    A SerializableRenderableImage provides a means to * serialize a RenderableImage. Transient fields are handled * using Serializers registered with the * SerializerFactory. Since no data is associated with a * RenderableImage, SerializableRenderableImage * does not provide any renderable image data. The only way to access image * data from a SerializableRenderableImage is by calling any * one of the createDefaultRendering, * createRendering, or createScaledRendering * methods. The resultant RenderedImage is created on the remote * host and provided via deep copy of the image data. If the request is * made on the local host, the image data are provided by forwarding * the request to the wrapped RenderableImage. Note that a single * SerializableRenderableImage object should be able to service * multiple remote hosts. * *

    An example of the usage of this class is as follows: * *

     * import java.io.IOException;
     * import java.io.ObjectInputStream;
     * import java.io.ObjectOutputStream;
     * import java.io.Serializable;
     *
     * public class SomeSerializableClass implements Serializable {
     *     protected transient RenderableImage image;
     *
     *     // Fields omitted.
     *
     *     public SomeSerializableClass(RenderableImage image) {
     *         this.image = image;
     *     }
     *
     *     // Methods omitted.
     *
     *     // Serialization method.
     *     private void writeObject(ObjectOutputStream out) throws IOException {
     *         out.defaultWriteObject();
     *         out.writeObject(new SerializableRenderableImage(image));
     *     }
     *
     *     // Deserialization method.
     *     private void readObject(ObjectInputStream in)
     *         throws IOException, ClassNotFoundException {
     *         in.defaultReadObject();
     *         image = (RenderableImage)in.readObject();
     *     }
     * }
     * 
    * * @see java.awt.image.renderable.RenderableImage * @see javax.media.jai.RenderableOp */ public final class SerializableRenderableImage implements RenderableImage, Serializable { /** Value to indicate the server socket timeout period (milliseconds). */ private static final int SERVER_TIMEOUT = 60000; // XXX 1 minute? /** Message indicating that a client will not connect again. */ private static final String CLOSE_MESSAGE = "CLOSE"; /** Flag indicating whether this is a data server. */ private transient boolean isServer; /** The RenderableImage source of this object (server only). */ private transient RenderableImage source; /** The X coordinate of the image's upper-left pixel. */ private float minX; /** The Y coordinate of the image's upper-left pixel. */ private float minY; /** The image's width in pixels. */ private float width; /** The image's height in pixels. */ private float height; /** The image's sources, stored in a Vector. */ private transient Vector sources = null; /** A Hashtable containing the image properties. */ private transient Hashtable properties = null; /** */ private boolean isDynamic; /** The Internet Protocol (IP) address of the instantiating host. */ private InetAddress host; /** The port on which the data server is listening. */ private int port; /** Flag indicating that the server is available for connections. */ private transient boolean serverOpen = false; /** The server socket for image data transfer (server only). */ private transient ServerSocket serverSocket = null; /** The thread in which the data server is running (server only). */ private transient Thread serverThread; /** * A table of counts of remote references to instances of this class * (server only). * *

    This table consists of entries with the keys being instances of * SerializableRenderableImage and the values being * Integers the int value of which represents the number * of remote SerializableRenderableImage objects which could * potentially request a socket connection with the associated key. This * table is necessary to prevent the garbage collector of the interpreter * in which the server SerializableRenderableImage object is * instantiated from finalizing the object - and thereby closing its * server socket - when that object could still receive socket connection * requests from its remote clients. The reference to the object in the * static class variable ensures that the object will not be prematurely * finalized. */ private static transient Hashtable remoteReferenceCount; /** Indicate that tilecodec is used in the transfering or not */ private boolean useTileCodec = false; /** * The OperationRegistry to be used to find the * TileEncoderFactory and TileDecoderFactory */ private OperationRegistry registry = null; /** The name of the TileCodec format. */ private String formatName = null; /** Cache the encoding/decoding parameters */ private TileCodecParameterList encodingParam = null; private TileCodecParameterList decodingParam = null; /** * Increment the remote reference count of the argument. * *

    If the argument is not already in the remote reference table, * add it to the table with a count value of unity. If it exists in * table, increment its count value. * * @parameter o The object the count value of which is to be incremented. */ private static synchronized void incrementRemoteReferenceCount(Object o) { if (remoteReferenceCount == null) { remoteReferenceCount = new Hashtable(); remoteReferenceCount.put(o, new Integer(1)); } else { Integer count = (Integer)remoteReferenceCount.get(o); if (count == null) { remoteReferenceCount.put(o, new Integer(1)); } else { remoteReferenceCount.put(o, new Integer(count.intValue()+1)); } } } /** * Decrement the remote reference count of the argument. * *

    If the count value of the argument exists in the table its count * value is decremented unless the count value is unity in which case the * entry is removed from the table. * * @parameter o The object the count value of which is to be decremented. */ private static synchronized void decrementRemoteReferenceCount(Object o) { if (remoteReferenceCount != null) { Integer count = (Integer)remoteReferenceCount.get(o); if (count != null) { if (count.intValue() == 1) { remoteReferenceCount.remove(o); } else { remoteReferenceCount.put(o, new Integer(count.intValue()-1)); } } } } /** * The default constructor. */ SerializableRenderableImage() {} /** * Constructs a SerializableRenderableImage wrapper for a * RenderableImage source. The image data of the rendering * will be serialized via a single deep copy. Tile encoding and * decoding may be effected via a TileEncoder and * TileDecoder specified by format name. * * @param source The RenderableImage source. * @param registry The OperationRegistry to use in * creating the TileEncoder. The * TileDecoder will of necessity be * created using the default OperationRegistry * as the specified OperationRegistry is not * serialized. If null the default registry * will be used. * @param formatName The name of the format used to encode the data. * If null simple tile serialization will * be performed either directly or by use of a "raw" * TileCodec. * @param encodingParam The parameters to be used for data encoding. If * null the default encoding * TileCodecParameterList for this * format will be used. Ignored if * formatName is null. * @param decodingParam The parameters to be used for data decoding. If * null a complementary * TileCodecParameterList will be * derived from encodingParam. Ignored * if formatName is null. * * @exception IllegalArgumentException if source * or formatName is null. * @exception IllegalArgumentException if encodingParam and * decodingParam do not have the same format name as the * supplied formatName. */ public SerializableRenderableImage(RenderableImage source, OperationRegistry registry, String formatName, TileCodecParameterList encodingParam, TileCodecParameterList decodingParam) { this(source); this.registry = registry; this.formatName = formatName; this.encodingParam = encodingParam; this.decodingParam = decodingParam; if (formatName == null) { throw new IllegalArgumentException( JaiI18N.getString("SerializableRenderableImage2")); } if (!formatName.equals(encodingParam.getFormatName())) { throw new IllegalArgumentException( JaiI18N.getString("UseTileCodec0")); } if (!formatName.equals(decodingParam.getFormatName())) { throw new IllegalArgumentException( JaiI18N.getString("UseTileCodec1")); } TileEncoderFactory tileEncoderFactory = (TileEncoderFactory)registry.getFactory("tileEncoder", formatName); TileDecoderFactory tileDecoderFactory = (TileDecoderFactory)registry.getFactory("tileDecoder", formatName); if (tileEncoderFactory == null || tileDecoderFactory == null) throw new RuntimeException(JaiI18N.getString("UseTileCodec2")); useTileCodec = true; } /** * Constructs a SerializableRenderableImage wrapper for a * RenderableImage source. Image data of the rendering of * the RenderableImage will be serialized via a single deep * copy. No TileCodec will be used, i.e., data will be * transmitted using the serialization protocol for Rasters. * * @param source The RenderableImage source. * @exception IllegalArgumentException if source * or formatName is null. */ public SerializableRenderableImage(RenderableImage source) { if (source == null) throw new IllegalArgumentException( JaiI18N.getString("SerializableRenderableImage1")); // Set server flag. isServer = true; // Cache the parameter. this.source = source; // Initialize RenderableImage fields. minX = source.getMinX(); minY = source.getMinY(); width = source.getWidth(); height = source.getHeight(); isDynamic = source.isDynamic(); sources = new Vector(); sources.add(source); properties = new Hashtable(); String[] propertyNames = source.getPropertyNames(); String propertyName; if (propertyNames != null) { for (int i = 0; i < propertyNames.length; i++) { propertyName = propertyNames[i]; properties.put(new CaselessStringKey(propertyName), source.getProperty(propertyName)); } } // Initialize the host field. try { host = InetAddress.getLocalHost(); } catch (UnknownHostException e) { throw new RuntimeException(e.getMessage()); } // Unset the server availability flag. serverOpen = false; } /** * Private implementation of tile server. */ private class RenderingServer implements Runnable { /** * Provide Rasters to clients on request. * *

    This method is called by the data server thread when a deep copy * of the source image Raster is not being used. A socket connection is * set up at a well known address to which clients may connect. After a * client connects it transmits a Rectangle object which is read by * this method. The Raster corresponding to this Rectangle is then * retrieved from the source image and transmitted back over the * socket connection. * *

    The server loop will continue until this object is garbage * collected. */ public void run() { // Loop while the server availability flag is set. while (serverOpen) { // Wait for a client connection request. Socket socket = null; try { socket = serverSocket.accept(); socket.setSoLinger(true,1); } catch (InterruptedIOException e) { // accept() timeout: restart loop to check // availability flag. continue; } catch (Exception e) { throw new RuntimeException(e.getMessage()); } // Get the socket input and output streams and wrap object // input and output streams around them, respectively. InputStream in = null; OutputStream out = null; ObjectInputStream objectIn = null; ObjectOutputStream objectOut = null; try { in = socket.getInputStream(); out = socket.getOutputStream(); objectIn = new ObjectInputStream(in); objectOut = new ObjectOutputStream(out); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } // Read the Object from the object stream. Object obj = null; try { obj = objectIn.readObject(); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } RenderedImage ri = null; SerializableRenderedImage sri; // Switch according to object class; ignore unsupported types. if (obj instanceof String) { String str = (String)obj; if (str.equals(CLOSE_MESSAGE)) { // Decrement the remote reference count. decrementRemoteReferenceCount(this); } else { if (str.equals("createDefaultRendering")) { ri = source.createDefaultRendering(); } else if (str.equals("createRendering")) { // Read the Object from the object stream. obj = null; try { obj = objectIn.readObject(); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } SerializableState ss = (SerializableState)obj; RenderContext rc = (RenderContext)ss.getObject(); ri = source.createRendering(rc); } else if (str.equals("createScaledRendering")) { // Read the Object from the object stream. obj = null; try { obj = objectIn.readObject(); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } int w = ((Integer)obj).intValue(); try { obj = objectIn.readObject(); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } int h = ((Integer)obj).intValue(); try { obj = objectIn.readObject(); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } SerializableState ss = (SerializableState)obj; RenderingHints rh = (RenderingHints)ss.getObject(); ri = source.createScaledRendering(w, h, rh); } if (useTileCodec) { try { sri = new SerializableRenderedImage(ri, true, registry, formatName, encodingParam, decodingParam); } catch (java.io.NotSerializableException nse) { throw new RuntimeException(nse.getMessage()); } } else { sri = new SerializableRenderedImage(ri, true); } try { objectOut.writeObject(sri); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } } } else { throw new RuntimeException( JaiI18N.getString("SerializableRenderableImage0")); } // XXX Concerning serialization of properties, perhaps the // best approach would be to serialize all the properties up // front if a deep copy were being made but otherwise to wait // until the first property request was received before // transmitting any property values. When the first request // was made, all property values would be transmitted and then // cached. Up front serialization might in both cases include // transmitting all names. If property serialization were // deferred, then a new message branch would be added here // to retrieve the properties which could be obtained as // a PropertySourceImpl. If properties are also served up // then this inner class should be renamed "DataServer". // Close the various streams and the socket itself. try { objectOut.close(); objectIn.close(); out.close(); in.close(); socket.close(); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } } } } // --- Begin implementation of java.awt.image.RenderableImage. --- /** * Returns a RenderedImage which is the result of * calling createDefaultRendering on the wrapped * RenderableImage. */ public RenderedImage createDefaultRendering() { if (isServer) { return source.createDefaultRendering(); } // Connect to the data server. Socket socket = connectToServer(); // Get the socket input and output streams and wrap object // input and output streams around them, respectively. OutputStream out = null; ObjectOutputStream objectOut = null; InputStream in = null; ObjectInputStream objectIn = null; try { out = socket.getOutputStream(); objectOut = new ObjectOutputStream(out); in = socket.getInputStream(); objectIn = new ObjectInputStream(in); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } // Write the name of the method to the object output stream. try { objectOut.writeObject("createDefaultRendering"); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } // Read serialized form of the RenderedImage from object output stream. Object object = null; try { object = objectIn.readObject(); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } RenderedImage ri; if (object instanceof SerializableRenderedImage) { ri = (RenderedImage)object; } else { ri = null; } // Close the various streams and the socket. try { out.close(); objectOut.close(); in.close(); objectIn.close(); socket.close(); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } return ri; } public RenderedImage createRendering(RenderContext renderContext) { if (isServer) { return source.createRendering(renderContext); } // Connect to the data server. Socket socket = connectToServer(); // Get the socket input and output streams and wrap object // input and output streams around them, respectively. OutputStream out = null; ObjectOutputStream objectOut = null; InputStream in = null; ObjectInputStream objectIn = null; try { out = socket.getOutputStream(); objectOut = new ObjectOutputStream(out); in = socket.getInputStream(); objectIn = new ObjectInputStream(in); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } // Write the name of the method and the RenderContext to the // object output stream. try { objectOut.writeObject("createRendering"); objectOut.writeObject(SerializerFactory.getState(renderContext, null)); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } // Read serialized form of the RenderedImage from object output stream. Object object = null; try { object = objectIn.readObject(); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } RenderedImage ri = (RenderedImage)object; // Close the various streams and the socket. try { out.close(); objectOut.close(); in.close(); objectIn.close(); socket.close(); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } return ri; } public RenderedImage createScaledRendering(int w, int h, RenderingHints hints) { if (isServer) { return source.createScaledRendering(w, h, hints); } // Connect to the data server. Socket socket = connectToServer(); // Get the socket input and output streams and wrap object // input and output streams around them, respectively. OutputStream out = null; ObjectOutputStream objectOut = null; InputStream in = null; ObjectInputStream objectIn = null; try { out = socket.getOutputStream(); objectOut = new ObjectOutputStream(out); in = socket.getInputStream(); objectIn = new ObjectInputStream(in); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } // Write the name of the method and the necessary method argument // to the object output stream. try { objectOut.writeObject("createScaledRendering"); objectOut.writeObject(new Integer(w)); objectOut.writeObject(new Integer(h)); objectOut.writeObject(SerializerFactory.getState(hints, null)); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } // Read serialized form of the RenderedImage from object output stream. Object object = null; try { object = objectIn.readObject(); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } RenderedImage ri = (RenderedImage)object; // Close the various streams and the socket. try { out.close(); objectOut.close(); in.close(); objectIn.close(); socket.close(); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } return ri; } public float getHeight() { return height; } public float getMinX() { return minX; } public float getMinY() { return minY; } // XXX Should getProperty() request property values over a socket // connection also? public Object getProperty(String name) { Object property = properties.get(new CaselessStringKey(name)); return property == null ? Image.UndefinedProperty : property; } public String[] getPropertyNames() { String[] names = null; if (!properties.isEmpty()) { names = new String[properties.size()]; Enumeration keys = properties.keys(); int index = 0; CaselessStringKey key; while (keys.hasMoreElements()) { key = (CaselessStringKey)keys.nextElement(); names[index++] = key.getName(); } } return names; } /** * If this SerializableRenderableImage has not been * serialized, this method returns a Vector containing * only the RenderableImage passed to the constructor; if * this image has been deserialized, it returns null. */ public Vector getSources() { return sources; } /** * */ public boolean isDynamic() { return isDynamic; } public float getWidth() { return width; } // --- End implementation of java.awt.image.RenderableImage. --- /** * Create a server socket and start the server in a separate thread. * *

    Note that this method should be called only the first time this * object is serialized and only if a deep copy is not being used. If * a deep copy is used there is no need to serve clients data on demand. * However if data service is being provided, there is no need to create * multiple threads for the single object as a single server thread * should be able to service multiple remote objects. */ private synchronized void openServer() throws IOException, SocketException { if (!serverOpen) { // Create a ServerSocket. serverSocket = new ServerSocket(0); // Set the ServerSocket accept() method timeout period. serverSocket.setSoTimeout(SERVER_TIMEOUT); // Initialize the port field. port = serverSocket.getLocalPort(); // Set the server availability flag. serverOpen = true; // Spawn a child thread and return the parent thread to the caller. serverThread = new Thread(new RenderingServer()); serverThread.start(); // Increment the remote reference count. incrementRemoteReferenceCount(this); } } /** * Transmit a message to the data server to indicate that the client * will no longer request socket connections. */ private void closeClient() { // Connect to the data server. Socket socket = connectToServer(); // Get the socket output stream and wrap an object // output stream around it. OutputStream out = null; ObjectOutputStream objectOut = null; try { out = socket.getOutputStream(); objectOut = new ObjectOutputStream(out); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } // Write CLOSE_MESSAGE to the object output stream. try { objectOut.writeObject(CLOSE_MESSAGE); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } // Close the streams and the socket. try { out.close(); objectOut.close(); socket.close(); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } } /** * Obtain a connection to the data server socket. This is used only if a * deep copy of the image Raster has not been made. */ private Socket connectToServer() { // Open a connection to the data server. Socket socket = null; try { socket = new Socket(host, port); socket.setSoLinger(true,1); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } return socket; } /** * If a deep copy is not being used, unset the data server availability * flag and wait for the server thread to rejoin the current thread. */ protected void finalize() throws Throwable { dispose(); // Forward to the parent class. super.finalize(); } /** * Provides a hint that an image will no longer be accessed from a * reference in user space. The results are equivalent to those * that occur when the program loses its last reference to this * image, the garbage collector discovers this, and finalize is * called. This can be used as a hint in situations where waiting * for garbage collection would be overly conservative, e.g., there * are a large number of socket connections which may be opened to * transmit tile data. * *

    SerializableRenderableImage defines this method to * behave as follows: *

      *
    • if the image is acting as a server, i.e., has never been * serialized and may be providing data to serialized * versions of itself, it makes itself unavailable to further * client requests and closes its socket;
    • *
    • if the image is acting as a client, i.e., has been serialized * and may be requesting data from a remote, pre-serialization version * of itself, it sends a message to its remote self indicating that it * will no longer be making requests.
    • *
    * *

    The results of referencing an image after a call to * dispose() are undefined. */ public void dispose() { // Rejoin the server thread if using a socket-based server. if (isServer) { if (serverOpen) { // Unset availability flag so server loop exits. serverOpen = false; // Wait for the server (child) thread to die. try { serverThread.join(2*SERVER_TIMEOUT); } catch (Exception e) { // Ignore the Exception. } // Close the server socket. try { serverSocket.close(); } catch (Exception e) { // Ignore the Exception. } } } else { // client // Transmit a message to the server to indicate the child's exit. closeClient(); } } /** * Custom serialization method. In addition to all non-transient fields, * the SampleModel, source vector, and properties table are serialized. * If a deep copy of the source image Raster is being used this is also * serialized. */ private void writeObject(ObjectOutputStream out) throws IOException { // Start the data server. try { openServer(); } catch (Exception e1) { if (e1 instanceof SocketException) { // setSoTimeout() failed. if (serverSocket != null) { // XXX Facultative try { serverSocket.close(); } catch (IOException e2) { // Ignore the exception. } } } // Since server socket creation failed, use a deep copy. serverOpen = false; // XXX Facultative } // Write non-static and non-transient fields. out.defaultWriteObject(); // Remove non-serializable elements from table of properties. Hashtable propertyTable = properties; boolean propertiesCloned = false; Enumeration keys = propertyTable.keys(); while(keys.hasMoreElements()) { Object key = keys.nextElement(); if (!(properties.get(key) instanceof Serializable)) { if (!propertiesCloned) { propertyTable = (Hashtable)properties.clone(); propertiesCloned = true; } propertyTable.remove(key); } } // Write the properties table. out.writeObject(propertyTable); } /** * Custom deserialization method. In addition to all non-transient fields, * the SampleModel, source vector, and properties table are deserialized. * If a deep copy of the source image Raster is being used this is also * deserialized. */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { isServer = false; source = null; serverOpen = false; serverSocket = null; serverThread = null; // Read non-static and non-transient fields. in.defaultReadObject(); // Read the properties table. properties = (Hashtable)in.readObject(); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/rmi/RMIImage.java0000644000175000017500000002251610203035544024641 0ustar mathieumathieu/* * $RCSfile: RMIImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:56:52 $ * $State: Exp $ */ package com.sun.media.jai.rmi; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.awt.image.renderable.RenderContext; import java.rmi.Remote; import java.rmi.RemoteException; import java.util.Vector; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; /** * An interface for server-side imaging. This interface attempts to * mimic the RenderedImage interface as much as possible. However, there * are several unavoidable differences: * *

      *
    • Additional setSource() methods are provided to inform the server * as to the source of image data for this image. Sources may be set * either from a RenderedImage that is copied over to the server, or * from a graph of RenderedOp objects indicating an abstract * imaging chain to be instantiated using the server's * OperationRegistry. * *
    • All methods throw RemoteException. This is a requirement of * any Remote interface. * *
    • The getTile() method does not return a reference to a `live' * tile; instead it returns a client-side copy of the server image's * tile. The difference is moot since the server image is immutable. *
    * * To instantiate a RMIImage, do the following: * *
     * RMIImage im;
     * im = java.rmi.Naming.lookup("//host:1099/javax.media.jai.RemoteImageServer");
     * 
    * *

    The hostname and port will of course depend on the local setup. * The host must be running an rmiregistry process and have a * RemoteImageServer listening at the desired port. * *

    This call will result in the creation of a server-side * RMIImageImpl object and a client-side stub object. * The client stub serializes its method arguments and transfers * them to the server over a socket; the server serializes it return * values and returns them in the same manner. * *

    This process implies that all arguments and return values must * be serializable. In the case of a RenderedImage source, * serializability is not guaranteed and must be considered on a * class-by-class basis. For RenderedOps, which are basically * simple nodes connected by ParameterBlocks, serializability will be * determined by the serializabiility of the ultimate * (non-RenderedOp) sources of the DAG and the serializability * of any ad-hoc Object parameters held in the ParameterBlocks. * *

    The return values of the getData(), copyData(), and getTile() * methods are various kinds of Rasters; at present, Java2D does not * define serialization on Rasters. We will either need to add this * feature to Java2D or else coerce the server-side Rasters into a * serializable subclass form. In any case, we will want to * implement lossless (and possibly lossy) compression as part of * the serialization process wherever possible. * * @see java.rmi.Remote * @see java.rmi.RemoteException * @see java.awt.image.RenderedImage * @see RemoteImage * * @since EA3 * */ public interface RMIImage extends Remote { /** * The name to which the remote image server should be bound. */ public static final String RMI_IMAGE_SERVER_NAME = "RemoteImageServer"; /** * Returns the identifier of the remote image. This method should be * called to return an identifier before any other methods are invoked. * The same ID must be used in all subsequent references to the remote * image. */ Long getRemoteID() throws RemoteException; /** * Sets the source of the image on the server side. This source * should ideally be a lightweight reference to an image available * locally on the server or over a further network link (for * example, an IIPOpImage that contains a URL but not actual image * data). * *

    Although it is legal to use any RenderedImage, one should be * aware that a deep copy might be made and transmitted to the server. * * @param id An ID for the source which must be unique across all clients. * @param source a RenderedImage source. */ void setSource(Long id, RenderedImage source) throws RemoteException; /** * Sets the source to a RenderedOp (i.e., an imaging DAG). * This DAG will be copied over to the server where it will be * transformed into an OpImage chain using the server's local * OperationRegistry and available RenderedImageFactory objects. * * @param id An ID for the source which must be unique across all clients. * @param source a RenderedOp source. */ void setSource(Long id, RenderedOp source) throws RemoteException; /** * Sets the source to a RenderableOp defined by a renderable imaging * DAG and a rendering context. The entire RenderableImage * DAG will be copied over to the server. */ void setSource(Long id, RenderableOp source, RenderContextProxy renderContextProxy) throws RemoteException; /** * Disposes of any resouces allocated to the client object with * the specified ID. */ void dispose(Long id) throws RemoteException; /** * Returns a vector of RenderedImages that are the sources of * image data for this RMIImage. Note that this method * will often return an empty vector. */ Vector getSources(Long id) throws RemoteException; /** * 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 id An ID for the source which must be unique across all clients. * @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(Long id, String name) throws RemoteException; /** * Returns a list of names recognized by getProperty(String). * * @return an array of Strings representing proeprty names. */ String [] getPropertyNames(Long id) throws RemoteException; /** Returns the ColorModel associated with this image. */ ColorModelProxy getColorModel(Long id) throws RemoteException; /** Returns the SampleModel associated with this image. */ SampleModelProxy getSampleModel(Long id) throws RemoteException; /** Returns the width of the RMIImage. */ int getWidth(Long id) throws RemoteException; /** Returns the height of the RMIImage. */ int getHeight(Long id) throws RemoteException; /** * Returns the minimum X coordinate of the RMIImage. */ int getMinX(Long id) throws RemoteException; /** * Returns the minimum Y coordinate of the RMIImage. */ int getMinY(Long id) throws RemoteException; /** Returns the number of tiles across the image. */ int getNumXTiles(Long id) throws RemoteException; /** Returns the number of tiles down the image. */ int getNumYTiles(Long id) throws RemoteException; /** * Returns the index of the minimum tile in the X direction of the image. */ int getMinTileX(Long id) throws RemoteException; /** * Returns the index of the minimum tile in the Y direction of the image. */ int getMinTileY(Long id) throws RemoteException; /** Returns the width of a tile in pixels. */ int getTileWidth(Long id) throws RemoteException; /** Returns the height of a tile in pixels. */ int getTileHeight(Long id) throws RemoteException; /** Returns the X offset of the tile grid relative to the origin. */ int getTileGridXOffset(Long id) throws RemoteException; /** Returns the Y offset of the tile grid relative to the origin. */ int getTileGridYOffset(Long id) throws RemoteException; /** * Returns tile (x, y). Note that x and y are indices into the * tile array, not pixel locations. Unlike in the true RenderedImage * interface, the Raster that is returned should be considered a copy. * * @param id An ID for the source which must be unique across all clients. * @param x the x index of the requested tile in the tile array * @param y the y index of the requested tile in the tile array * @return a copy of the tile as a Raster. */ RasterProxy getTile(Long id, int x, int y) throws RemoteException; /** * Returns the entire image as a single Raster. * * @return a RasterProxy containing a copy of this image's data. */ RasterProxy getData(Long id) throws RemoteException; /** * Returns an arbitrary rectangular region of the RenderedImage * in a Raster. The rectangle of interest will be clipped against * the image bounds. * * @param id An ID for the source which must be unique across all clients. * @param bounds the region of the RenderedImage to be returned. * @return a RasterProxy containing a copy of the desired data. */ RasterProxy getData(Long id, Rectangle bounds) throws RemoteException; /** * Returns the same result as getData(Rectangle) would for the * same rectangular region. */ RasterProxy copyData(Long id, Rectangle bounds) throws RemoteException; } jai-core-1.1.4/src/share/classes/com/sun/media/jai/iterator/0000755000175000017500000000000011633360406023446 5ustar mathieumathieujai-core-1.1.4/src/share/classes/com/sun/media/jai/iterator/WritableRookIterFallback.java0000644000175000017500000000333410203035544031156 0ustar mathieumathieu/* * $RCSfile: WritableRookIterFallback.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:46 $ * $State: Exp $ */ package com.sun.media.jai.iterator; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.WritableRenderedImage; import javax.media.jai.iterator.WritableRookIter; /** * @since EA2 */ public class WritableRookIterFallback extends RookIterFallback implements WritableRookIter { public WritableRookIterFallback(WritableRenderedImage im, Rectangle bounds) { super(im, bounds); } public void setSample(int s) { sampleModel.setSample(localX, localY, b, s, dataBuffer); } public void setSample(int b, int s) { sampleModel.setSample(localX, localY, b, s, dataBuffer); } public void setSample(float s) { sampleModel.setSample(localX, localY, b, s, dataBuffer); } public void setSample(int b, float s) { sampleModel.setSample(localX, localY, b, s, dataBuffer); } public void setSample(double s) { sampleModel.setSample(localX, localY, b, s, dataBuffer); } public void setSample(int b, double s) { sampleModel.setSample(localX, localY, b, s, dataBuffer); } public void setPixel(int[] iArray) { sampleModel.setPixel(localX, localY, iArray, dataBuffer); } public void setPixel(float[] fArray) { sampleModel.setPixel(localX, localY, fArray, dataBuffer); } public void setPixel(double[] dArray) { sampleModel.setPixel(localX, localY, dArray, dataBuffer); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/iterator/WritableRandomIterCSMByte.java0000644000175000017500000000206010203035544031226 0ustar mathieumathieu/* * $RCSfile: WritableRandomIterCSMByte.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:44 $ * $State: Exp $ */ package com.sun.media.jai.iterator; import java.awt.Rectangle; import java.awt.image.WritableRenderedImage; import javax.media.jai.iterator.WritableRandomIter; /** * @since EA2 */ public final class WritableRandomIterCSMByte extends RandomIterCSMByte implements WritableRandomIter { public WritableRandomIterCSMByte(WritableRenderedImage im, Rectangle bounds) { super(im, bounds); } public void setSample(int x, int y, int b, int val) { } public void setSample(int x, int y, int b, float val) { } public void setSample(int x, int y, int b, double val) { } public void setPixel(int x, int y, int[] iArray) { } public void setPixel(int x, int y, float[] fArray) { } public void setPixel(int x, int y, double[] dArray) { } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/iterator/WritableRectIterFallback.java0000644000175000017500000000343410203035544031142 0ustar mathieumathieu/* * $RCSfile: WritableRectIterFallback.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:46 $ * $State: Exp $ */ package com.sun.media.jai.iterator; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.WritableRenderedImage; import javax.media.jai.iterator.WritableRectIter; /** * @since EA2 */ public class WritableRectIterFallback extends RectIterFallback implements WritableRectIter { protected WritableRenderedImage wim; public WritableRectIterFallback(WritableRenderedImage im, Rectangle bounds) { super(im, bounds); this.wim = im; } public void setSample(int s) { sampleModel.setSample(localX, localY, b, s, dataBuffer); } public void setSample(int b, int s) { sampleModel.setSample(localX, localY, b, s, dataBuffer); } public void setSample(float s) { sampleModel.setSample(localX, localY, b, s, dataBuffer); } public void setSample(int b, float s) { sampleModel.setSample(localX, localY, b, s, dataBuffer); } public void setSample(double s) { sampleModel.setSample(localX, localY, b, s, dataBuffer); } public void setSample(int b, double s) { sampleModel.setSample(localX, localY, b, s, dataBuffer); } public void setPixel(int[] iArray) { sampleModel.setPixel(localX, localY, iArray, dataBuffer); } public void setPixel(float[] fArray) { sampleModel.setPixel(localX, localY, fArray, dataBuffer); } public void setPixel(double[] dArray) { sampleModel.setPixel(localX, localY, dArray, dataBuffer); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/iterator/com.sun.media.jai.iterator.properties0000644000175000017500000000067310203035544032616 0ustar mathieumathieu# # $RCSfile: com.sun.media.jai.iterator.properties,v $ # # Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. # # Use is subject to license terms. # # $Revision: 1.1 $ # $Date: 2005-02-11 04:55:46 $ # $State: Exp $ # RectIterFallback0=jumpPixels jumped outside of the iterator bounding boxs. RectIterFallback1=jumpLines jumped outside of the iterator bounding box. WrapperRI0=Not implemented yet. WrapperWRI0=Not implemented yet. jai-core-1.1.4/src/share/classes/com/sun/media/jai/iterator/WritableRandomIterCSMInt.java0000644000175000017500000000205310203035544031057 0ustar mathieumathieu/* * $RCSfile: WritableRandomIterCSMInt.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:44 $ * $State: Exp $ */ package com.sun.media.jai.iterator; import java.awt.Rectangle; import java.awt.image.WritableRenderedImage; import javax.media.jai.iterator.WritableRandomIter; /** * @since EA2 */ public final class WritableRandomIterCSMInt extends RandomIterCSMInt implements WritableRandomIter { public WritableRandomIterCSMInt(WritableRenderedImage im, Rectangle bounds) { super(im, bounds); } public void setSample(int x, int y, int b, int val) { } public void setSample(int x, int y, int b, float val) { } public void setSample(int x, int y, int b, double val) { } public void setPixel(int x, int y, int[] iArray) { } public void setPixel(int x, int y, float[] fArray) { } public void setPixel(int x, int y, double[]dfArray) { } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/iterator/RectIterFallback.java0000644000175000017500000002301710203035544027447 0ustar mathieumathieu/* * $RCSfile: RectIterFallback.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:43 $ * $State: Exp $ */ package com.sun.media.jai.iterator; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import javax.media.jai.PlanarImage; import javax.media.jai.iterator.RectIter; /** * @since EA2 */ public class RectIterFallback implements RectIter { /** The source image. */ protected RenderedImage im; /** The iterator bouning rectangle. */ protected Rectangle bounds; /** The SampleModel for the tiles of the source image. */ protected SampleModel sampleModel; /** The number of bands of the source image. */ protected int numBands; /** The width of tiles in the source image. */ protected int tileWidth; /** The height of tiles in the source image. */ protected int tileHeight; /** The X offset of the source image tile grid. */ protected int tileGridXOffset; /** The Y offset of the source image tile grid. */ protected int tileGridYOffset; /** The tile index of the leftmost column in the iterator's bounds. */ protected int startTileX; /** The tile index of the topmost row in the iterator's bounds. */ protected int startTileY; /** The (inclusive) smallest X coordinate of the current tile. */ protected int tileXStart; /** The (inclusive) largest X coordinate of the current tile. */ protected int tileXEnd; /** The (inclusive) smallest Y coordinate of the current tile. */ protected int tileYStart; /** The (inclusive) largest Y coordinate of the current tile. */ protected int tileYEnd; /** The next leftwards tile or image boundary. */ protected int prevXBoundary; /** The next rightwards tile or image boundary. */ protected int nextXBoundary; /** The next upwards tile or image boundary. */ protected int prevYBoundary; /** The next downwards tile or image boundary. */ protected int nextYBoundary; /** The X index of the current tile. */ protected int tileX; /** The Y index of the current tile. */ protected int tileY; /** The (inclusive) largest X coordinate of the bounding rectangle. */ protected int lastX; /** The (inclusive) largest Y coordinate of the bounding rectangle. */ protected int lastY; /** The current X pixel position. */ protected int x; /** The current Y pixel position. */ protected int y; /** The current X pixel position minus the sample model translation . */ protected int localX; /** The current Y pixel position minus the sample model translation . */ protected int localY; /** The X translation factor of the SampleModel of the current tile. */ protected int sampleModelTranslateX = 0; /** The X translation factor of the SampleModel of the current tile. */ protected int sampleModelTranslateY = 0; /** The current band offset. */ protected int b; /** The DataBuffer of the current tile. */ protected DataBuffer dataBuffer = null; public RectIterFallback(RenderedImage im, Rectangle bounds) { this.im = im; this.bounds = bounds; this.sampleModel = im.getSampleModel(); this.numBands = sampleModel.getNumBands(); this.tileGridXOffset = im.getTileGridXOffset(); this.tileGridYOffset = im.getTileGridYOffset(); this.tileWidth = im.getTileWidth(); this.tileHeight = im.getTileHeight(); this.startTileX = PlanarImage.XToTileX(bounds.x, tileGridXOffset, tileWidth); this.startTileY = PlanarImage.YToTileY(bounds.y, tileGridYOffset, tileHeight); this.tileX = startTileX; this.tileY = startTileY; this.lastX = bounds.x + bounds.width - 1; this.lastY = bounds.y + bounds.height - 1; localX = x = bounds.x; localY = y = bounds.y; b = 0; setTileXBounds(); setTileYBounds(); setDataBuffer(); } protected final void setTileXBounds() { tileXStart = tileX*tileWidth + tileGridXOffset; tileXEnd = tileXStart + tileWidth - 1; prevXBoundary = Math.max(tileXStart, bounds.x); nextXBoundary = Math.min(tileXEnd, lastX); } protected final void setTileYBounds() { tileYStart = tileY*tileHeight + tileGridYOffset; tileYEnd = tileYStart + tileHeight - 1; prevYBoundary = Math.max(tileYStart, bounds.y); nextYBoundary = Math.min(tileYEnd, lastY); } protected void setDataBuffer() { Raster tile = im.getTile(tileX, tileY); this.dataBuffer = tile.getDataBuffer(); int newSampleModelTranslateX = tile.getSampleModelTranslateX(); int newSampleModelTranslateY = tile.getSampleModelTranslateY(); localX += sampleModelTranslateX - newSampleModelTranslateX; localY += sampleModelTranslateY - newSampleModelTranslateY; this.sampleModelTranslateX = newSampleModelTranslateX; this.sampleModelTranslateY = newSampleModelTranslateY; } public void startLines() { y = bounds.y; localY = y - sampleModelTranslateY; tileY = startTileY; setTileYBounds(); setDataBuffer(); } public void nextLine() { ++y; ++localY; } public void jumpLines(int num) { int jumpY = y + num; if (jumpY < bounds.y || jumpY > lastY) { // Jumped outside the image. throw new IndexOutOfBoundsException(JaiI18N.getString("RectIterFallback1")); } y = jumpY; localY += num; if (y < prevYBoundary || y > nextYBoundary) { this.tileY = PlanarImage.YToTileY(y, tileGridYOffset, tileHeight); setTileYBounds(); setDataBuffer(); } } public boolean finishedLines() { if (y > nextYBoundary) { if (y > lastY) { return true; } else { ++tileY; tileYStart += tileHeight; tileYEnd += tileHeight; prevYBoundary = Math.max(tileYStart, bounds.y); nextYBoundary = Math.min(tileYEnd, lastY); setDataBuffer(); return false; } } else { return false; } } public boolean nextLineDone() { nextLine(); return finishedLines(); } public void startPixels() { x = bounds.x; localX = x - sampleModelTranslateX; tileX = startTileX; setTileXBounds(); setDataBuffer(); } public void nextPixel() { ++x; ++localX; } public void jumpPixels(int num) { int jumpX = x + num; if (jumpX < bounds.x || jumpX > lastX) { // Jumped outside the image. throw new IndexOutOfBoundsException(JaiI18N.getString("RectIterFallback0")); } x = jumpX; localX += num; if (x < prevXBoundary || x > nextXBoundary) { this.tileX = PlanarImage.XToTileX(x, tileGridXOffset, tileWidth); setTileXBounds(); setDataBuffer(); } } public boolean finishedPixels() { if (x > nextXBoundary) { if (x > lastX) { return true; } else { ++tileX; tileXStart += tileWidth; tileXEnd += tileWidth; prevXBoundary = Math.max(tileXStart, bounds.x); nextXBoundary = Math.min(tileXEnd, lastX); setDataBuffer(); return false; } } else { return false; } } public boolean nextPixelDone() { nextPixel(); return finishedPixels(); } public void startBands() { b = 0; } public void nextBand() { ++b; } public boolean nextBandDone() { nextBand(); return finishedBands(); } public boolean finishedBands() { return b >= numBands; } public int getSample() { return sampleModel.getSample(localX, localY, b, dataBuffer); } public int getSample(int b) { return sampleModel.getSample(localX, localY, b, dataBuffer); } public float getSampleFloat() { return sampleModel.getSampleFloat(localX, localY, b, dataBuffer); } public float getSampleFloat(int b) { return sampleModel.getSampleFloat(localX, localY, b, dataBuffer); } public double getSampleDouble() { return sampleModel.getSampleDouble(localX, localY, b, dataBuffer); } public double getSampleDouble(int b) { return sampleModel.getSampleDouble(localX, localY, b, dataBuffer); } public int[] getPixel(int[] iArray) { return sampleModel.getPixel(localX, localY, iArray, dataBuffer); } public float[] getPixel(float[] fArray) { return sampleModel.getPixel(localX, localY, fArray, dataBuffer); } public double[] getPixel(double[] dArray) { return sampleModel.getPixel(localX, localY, dArray, dataBuffer); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/iterator/RandomIterCSM.java0000644000175000017500000000565610203035544026726 0ustar mathieumathieu/* * $RCSfile: RandomIterCSM.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:42 $ * $State: Exp $ */ package com.sun.media.jai.iterator; import java.awt.Rectangle; import java.awt.image.ComponentSampleModel; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import javax.media.jai.PlanarImage; import javax.media.jai.iterator.RandomIter; /** * @since EA2 */ public abstract class RandomIterCSM extends RandomIterFallback { protected ComponentSampleModel sampleModel; protected int pixelStride; protected int scanlineStride; protected int[] bandOffsets; protected int numBands; public RandomIterCSM(RenderedImage im, Rectangle bounds) { super(im, bounds); this.sampleModel = (ComponentSampleModel)im.getSampleModel(); this.numBands = sampleModel.getNumBands(); this.pixelStride = sampleModel.getPixelStride(); this.scanlineStride = sampleModel.getScanlineStride(); } protected void dataBufferChanged() {} /** * Sets dataBuffer to the correct buffer for the pixel * (x, y) = (xLocal + boundsRect.x, yLocal + boundsRect.y). * * @param xLocal the X coordinate in the local coordinate system. * @param yLocal the Y coordinate in the local coordinate system. */ protected final void makeCurrent(int xLocal, int yLocal) { int xIDNew = xTiles[xLocal]; int yIDNew = yTiles[yLocal]; if ((xIDNew != xID) || (yIDNew != yID) || (dataBuffer == null)) { xID = xIDNew; yID = yIDNew; Raster tile = im.getTile(xID, yID); this.dataBuffer = tile.getDataBuffer(); dataBufferChanged(); this.bandOffsets = dataBuffer.getOffsets(); } } public float getSampleFloat(int x, int y, int b) { return (float)getSample(x, y, b); } public double getSampleDouble(int x, int y, int b) { return (double)getSample(x, y, b); } public int[] getPixel(int x, int y, int[] iArray) { if (iArray == null) { iArray = new int[numBands]; } for (int b = 0; b < numBands; b++) { iArray[b] = getSample(x, y, b); } return iArray; } public float[] getPixel(int x, int y, float[] fArray) { if (fArray == null) { fArray = new float[numBands]; } for (int b = 0; b < numBands; b++) { fArray[b] = getSampleFloat(x, y, b); } return fArray; } public double[] getPixel(int x, int y, double[] dArray) { if (dArray == null) { dArray = new double[numBands]; } for (int b = 0; b < numBands; b++) { dArray[b] = getSampleDouble(x, y, b); } return dArray; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/iterator/WritableRandomIterFallback.java0000644000175000017500000000654610203035544031474 0ustar mathieumathieu/* * $RCSfile: WritableRandomIterFallback.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:45 $ * $State: Exp $ */ package com.sun.media.jai.iterator; import java.awt.Rectangle; import java.awt.image.Raster; import java.awt.image.WritableRenderedImage; import javax.media.jai.iterator.WritableRandomIter; /** * @since EA2 */ public final class WritableRandomIterFallback extends RandomIterFallback implements WritableRandomIter { WritableRenderedImage wim; public WritableRandomIterFallback(WritableRenderedImage im, Rectangle bounds) { super(im, bounds); this.wim = im; } private void makeCurrentWritable(int xLocal, int yLocal) { int xIDNew = xTiles[xLocal]; int yIDNew = yTiles[yLocal]; if ((xIDNew != xID) || (yIDNew != yID) || (dataBuffer == null)) { if (dataBuffer != null) { wim.releaseWritableTile(xID, yID); } xID = xIDNew; yID = yIDNew; Raster tile = wim.getWritableTile(xID, yID); this.dataBuffer = tile.getDataBuffer(); this.sampleModelTranslateX = tile.getSampleModelTranslateX(); this.sampleModelTranslateY = tile.getSampleModelTranslateY(); } } public void setSample(int x, int y, int b, int s) { makeCurrentWritable(x - boundsX, y - boundsY); sampleModel.setSample(x - sampleModelTranslateX, y - sampleModelTranslateY, b, s, dataBuffer); } public void setSample(int x, int y, int b, float s) { makeCurrentWritable(x - boundsX, y - boundsY); sampleModel.setSample(x - sampleModelTranslateX, y - sampleModelTranslateY, b, s, dataBuffer); } public void setSample(int x, int y, int b, double s) { makeCurrentWritable(x - boundsX, y - boundsY); sampleModel.setSample(x - sampleModelTranslateX, y - sampleModelTranslateY, b, s, dataBuffer); } public void setPixel(int x, int y, int[] iArray) { makeCurrentWritable(x - boundsX, y - boundsY); sampleModel.setPixel(x - sampleModelTranslateX, y - sampleModelTranslateY, iArray, dataBuffer); } public void setPixel(int x, int y, float[] fArray) { makeCurrentWritable(x - boundsX, y - boundsY); sampleModel.setPixel(x - sampleModelTranslateX, y - sampleModelTranslateY, fArray, dataBuffer); } public void setPixel(int x, int y, double[] dArray) { makeCurrentWritable(x - boundsX, y - boundsY); sampleModel.setPixel(x - sampleModelTranslateX, y - sampleModelTranslateY, dArray, dataBuffer); } public void done() { if (dataBuffer != null) { wim.releaseWritableTile(xID, yID); } dataBuffer = null; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/iterator/RandomIterCSMUShort.java0000644000175000017500000000117510203035544030063 0ustar mathieumathieu/* * $RCSfile: RandomIterCSMUShort.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:43 $ * $State: Exp $ */ package com.sun.media.jai.iterator; import java.awt.Rectangle; import java.awt.image.RenderedImage; import javax.media.jai.iterator.RandomIter; /** * @since EA2 */ public class RandomIterCSMUShort extends RandomIterCSM { public RandomIterCSMUShort(RenderedImage im, Rectangle bounds) { super(im, bounds); } public final int getSample(int x, int y, int b) { return 0; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/iterator/WritableRectIterCSMByte.java0000644000175000017500000000331310203035544030705 0ustar mathieumathieu/* * $RCSfile: WritableRectIterCSMByte.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:45 $ * $State: Exp $ */ package com.sun.media.jai.iterator; import java.awt.Rectangle; import java.awt.image.DataBufferByte; import java.awt.image.RenderedImage; import javax.media.jai.iterator.WritableRectIter; /** */ public class WritableRectIterCSMByte extends RectIterCSMByte implements WritableRectIter { public WritableRectIterCSMByte(RenderedImage im, Rectangle bounds) { super(im, bounds); } public void setSample(int s) { bank[offset + bandOffset] = (byte)s; } public void setSample(int b, int s) { bankData[b][offset + bandOffsets[b]] = (byte)s; } public void setSample(float s) { bank[offset + bandOffset] = (byte)s; } public void setSample(int b, float s) { bankData[b][offset + bandOffsets[b]] = (byte)s; } public void setSample(double s) { bank[offset + bandOffset] = (byte)s; } public void setSample(int b, double s) { bankData[b][offset + bandOffsets[b]] = (byte)s; } public void setPixel(int[] iArray) { for (int b = 0; b < numBands; b++) { bankData[b][offset + bandOffsets[b]] = (byte)iArray[b]; } } public void setPixel(float[] fArray) { for (int b = 0; b < numBands; b++) { bankData[b][offset + bandOffsets[b]] = (byte)fArray[b]; } } public void setPixel(double[] dArray) { for (int b = 0; b < numBands; b++) { bankData[b][offset + bandOffsets[b]] = (byte)dArray[b]; } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/iterator/RandomIterCSMShort.java0000644000175000017500000000117210203035544027733 0ustar mathieumathieu/* * $RCSfile: RandomIterCSMShort.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:42 $ * $State: Exp $ */ package com.sun.media.jai.iterator; import java.awt.Rectangle; import java.awt.image.RenderedImage; import javax.media.jai.iterator.RandomIter; /** * @since EA2 */ public class RandomIterCSMShort extends RandomIterCSM { public RandomIterCSMShort(RenderedImage im, Rectangle bounds) { super(im, bounds); } public final int getSample(int x, int y, int b) { return 0; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/iterator/WrapperWRI.java0000644000175000017500000000323210203035544026305 0ustar mathieumathieu/* * $RCSfile: WrapperWRI.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:44 $ * $State: Exp $ */ package com.sun.media.jai.iterator; import java.awt.Point; import java.awt.image.Raster; import java.awt.image.TileObserver; import java.awt.image.WritableRaster; import java.awt.image.WritableRenderedImage; /** * @since EA2 */ public class WrapperWRI extends WrapperRI implements WritableRenderedImage { WritableRaster wras; public WrapperWRI(WritableRaster wras) { super(wras); this.wras = wras; } public void addTileObserver(TileObserver to) { throw new RuntimeException(JaiI18N.getString("WrapperWRI0")); } public void removeTileObserver(TileObserver to) { throw new RuntimeException(JaiI18N.getString("WrapperWRI0")); } public WritableRaster getWritableTile(int tileX, int tileY) { if ((tileX != 0) || (tileY != 0)) { throw new IllegalArgumentException(); } return wras; } public void releaseWritableTile(int tileX, int tileY) { if ((tileX != 0) || (tileY != 0)) { throw new IllegalArgumentException(); } } public boolean isTileWritable(int tileX, int tileY) { return true; } public Point[] getWritableTileIndices() { Point[] p = new Point[1]; p[0] = new Point(0, 0); return p; } public boolean hasTileWriters() { return true; } public void setData(Raster r) { throw new RuntimeException(JaiI18N.getString("WrapperWRI0")); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/iterator/RandomIterCSMDouble.java0000644000175000017500000000154710203035544030054 0ustar mathieumathieu/* * $RCSfile: RandomIterCSMDouble.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:42 $ * $State: Exp $ */ package com.sun.media.jai.iterator; import java.awt.Rectangle; import java.awt.image.RenderedImage; import javax.media.jai.iterator.RandomIter; /** * @since EA2 */ public class RandomIterCSMDouble extends RandomIterCSM { public RandomIterCSMDouble(RenderedImage im, Rectangle bounds) { super(im, bounds); } public final int getSample(int x, int y, int b) { return (int)getSampleDouble(x, y, b); } public final float getSampleFloat(int x, int y, int b) { return (float)getSampleDouble(x, y, b); } public final double getSampleDouble(int x, int y, int b) { return 0.0; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/iterator/RookIterFallback.java0000644000175000017500000002627210203035544027472 0ustar mathieumathieu/* * $RCSfile: RookIterFallback.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:43 $ * $State: Exp $ */ package com.sun.media.jai.iterator; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import javax.media.jai.PlanarImage; import javax.media.jai.iterator.RookIter; /** * @since EA2 */ public class RookIterFallback implements RookIter { /** The source image. */ protected RenderedImage im; /** The iterator bounding rectangle. */ protected Rectangle bounds; /** The SampleModel for the tiles of the source image. */ protected SampleModel sampleModel; /** The number of bands of the source image. */ protected int numBands; /** The width of tiles in the source image. */ protected int tileWidth; /** The height of tiles in the source image. */ protected int tileHeight; /** The X offset of the source image tile grid. */ protected int tileGridXOffset; /** The Y offset of the source image tile grid. */ protected int tileGridYOffset; /** The tile index of the leftmost column in the iterator's bounds. */ protected int startTileX; /** The tile index of the rightmost column in the iterator's bounds. */ protected int endTileX; /** The tile index of the topmost row in the iterator's bounds. */ protected int startTileY; /** The tile index of the bottommost row in the iterator's bounds. */ protected int endTileY; /** The (inclusive) smallest X coordinate of the current tile. */ protected int tileXStart; /** The (inclusive) largest X coordinate of the current tile. */ protected int tileXEnd; /** The (inclusive) smallest Y coordinate of the current tile. */ protected int tileYStart; /** The (inclusive) largest Y coordinate of the current tile. */ protected int tileYEnd; /** The next leftwards tile or image boundary. */ protected int prevXBoundary; /** The next rightwards tile or image boundary. */ protected int nextXBoundary; /** The next upwards tile or image boundary. */ protected int prevYBoundary; /** The next downwards tile or image boundary. */ protected int nextYBoundary; /** The X index of the current tile. */ protected int tileX; /** The Y index of the current tile. */ protected int tileY; /** The (inclusive) smallest X coordinate of the bounding rectangle. */ protected int firstX; /** The (inclusive) smallest Y coordinate of the bounding rectangle. */ protected int firstY; /** The (inclusive) largest X coordinate of the bounding rectangle. */ protected int lastX; /** The (inclusive) largest Y coordinate of the bounding rectangle. */ protected int lastY; /** The current X pixel position. */ protected int x; /** The current Y pixel position. */ protected int y; /** The current X pixel position relative to the tile start. */ protected int localX; /** The current Y pixel position relative to the tile start. */ protected int localY; /** The current band offset. */ protected int b; /** The DataBuffer of the current tile. */ protected DataBuffer dataBuffer = null; public RookIterFallback(RenderedImage im, Rectangle bounds) { this.im = im; this.bounds = bounds; this.sampleModel = im.getSampleModel(); this.numBands = sampleModel.getNumBands(); this.tileGridXOffset = im.getTileGridXOffset(); this.tileGridYOffset = im.getTileGridYOffset(); this.tileWidth = im.getTileWidth(); this.tileHeight = im.getTileHeight(); this.startTileX = PlanarImage.XToTileX(bounds.x, tileGridXOffset, tileWidth); this.endTileX = PlanarImage.XToTileX(bounds.x + bounds.width - 1, tileGridXOffset, tileWidth); this.startTileY = PlanarImage.YToTileY(bounds.y, tileGridYOffset, tileHeight); this.endTileY = PlanarImage.YToTileY(bounds.y + bounds.height - 1, tileGridYOffset, tileHeight); this.tileX = startTileX; this.tileY = startTileY; this.firstX = bounds.x; this.firstY = bounds.y; this.lastX = bounds.x + bounds.width - 1; this.lastY = bounds.y + bounds.height - 1; x = bounds.x; y = bounds.y; b = 0; setTileXBounds(); setTileYBounds(); setDataBuffer(); } private final void setTileXBounds() { tileXStart = tileX*tileWidth + tileGridXOffset; tileXEnd = tileXStart + tileWidth - 1; localX = x - tileXStart; prevXBoundary = Math.max(tileXStart, firstX); nextXBoundary = Math.min(tileXEnd, lastX); } private final void setTileYBounds() { tileYStart = tileY*tileHeight + tileGridYOffset; tileYEnd = tileYStart + tileHeight - 1; localY = y - tileYStart; prevYBoundary = Math.max(tileYStart, firstY); nextYBoundary = Math.min(tileYEnd, lastY); } private final void setDataBuffer() { this.dataBuffer = im.getTile(tileX, tileY).getDataBuffer(); } public void startLines() { y = firstY; localY = y - tileYStart; tileY = startTileY; setTileYBounds(); setDataBuffer(); } public void endLines() { y = lastY; localY = y - tileYStart; tileY = endTileY; setTileYBounds(); setDataBuffer(); } public void nextLine() { ++y; ++localY; } public void prevLine() { --y; --localY; } public void jumpLines(int num) { y += num; localY += num; if (y < tileYStart || y > tileYEnd) { this.tileY = PlanarImage.YToTileY(y, tileGridYOffset, tileHeight); setTileYBounds(); setDataBuffer(); } } public boolean finishedLines() { if (y > nextYBoundary) { if (y > lastY) { return true; } else { ++tileY; tileYStart += tileHeight; tileYEnd += tileHeight; localY -= tileHeight; prevYBoundary = Math.max(tileYStart, firstY); nextYBoundary = Math.min(tileYEnd, lastY); setDataBuffer(); return false; } } else { return false; } } public boolean finishedLinesTop() { if (y < prevYBoundary) { if (y < firstY) { return true; } else { --tileY; tileYStart -= tileHeight; tileYEnd -= tileHeight; localY += tileHeight; prevYBoundary = Math.max(tileYStart, firstY); nextYBoundary = Math.min(tileYEnd, lastY); setDataBuffer(); return false; } } else { return false; } } public boolean nextLineDone() { nextLine(); return finishedLines(); } public boolean prevLineDone() { prevLine(); return finishedLinesTop(); } public void startPixels() { x = firstX; localX = x - tileXStart; tileX = startTileX; setTileXBounds(); setDataBuffer(); } public void endPixels() { x = lastX; tileX = endTileX; setTileXBounds(); setDataBuffer(); } public void nextPixel() { ++x; ++localX; } public void prevPixel() { --x; --localX; } public void jumpPixels(int num) { x += num; localX += num; if (x < tileXStart || x > tileXEnd) { this.tileX = PlanarImage.XToTileX(x, tileGridXOffset, tileWidth); setTileXBounds(); setDataBuffer(); } } public boolean finishedPixels() { if (x > nextXBoundary) { if (x > lastX) { return true; } else { ++tileX; tileXStart += tileWidth; tileXEnd += tileWidth; localX -= tileWidth; prevXBoundary = Math.max(tileXStart, firstX); nextXBoundary = Math.min(tileXEnd, lastX); setDataBuffer(); return false; } } else { return false; } } public boolean finishedPixelsLeft() { if (x < prevXBoundary) { if (x < firstX) { return true; } else { --tileX; tileXStart -= tileWidth; tileXEnd -= tileWidth; localX += tileWidth; prevXBoundary = Math.max(tileXStart, firstX); nextXBoundary = Math.min(tileXEnd, lastX); setDataBuffer(); return false; } } else { return false; } } public boolean nextPixelDone() { nextPixel(); return finishedPixels(); } public boolean prevPixelDone() { prevPixel(); return finishedPixelsLeft(); } public void startBands() { b = 0; } public void endBands() { b = numBands - 1; } public void prevBand() { --b; } public void nextBand() { ++b; } public boolean prevBandDone() { return (--b) < 0; } public boolean nextBandDone() { return (++b) >= numBands; } public boolean finishedBands() { return b >= numBands; } public int getSample() { return sampleModel.getSample(localX, localY, b, dataBuffer); } public int getSample(int b) { return sampleModel.getSample(localX, localY, b, dataBuffer); } public float getSampleFloat() { return sampleModel.getSampleFloat(localX, localY, b, dataBuffer); } public float getSampleFloat(int b) { return sampleModel.getSampleFloat(localX, localY, b, dataBuffer); } public double getSampleDouble() { return sampleModel.getSampleDouble(localX, localY, b, dataBuffer); } public double getSampleDouble(int b) { return sampleModel.getSampleDouble(localX, localY, b, dataBuffer); } public int[] getPixel(int[] iArray) { return sampleModel.getPixel(localX, localY, iArray, dataBuffer); } public float[] getPixel(float[] fArray) { return sampleModel.getPixel(localX, localY, fArray, dataBuffer); } public double[] getPixel(double[] dArray) { return sampleModel.getPixel(localX, localY, dArray, dataBuffer); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/iterator/WritableRectIterCSMFloat.java0000644000175000017500000000323510203035544031052 0ustar mathieumathieu/* * $RCSfile: WritableRectIterCSMFloat.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:45 $ * $State: Exp $ */ package com.sun.media.jai.iterator; import java.awt.Rectangle; import java.awt.image.RenderedImage; import javax.media.jai.iterator.WritableRectIter; /** */ public class WritableRectIterCSMFloat extends RectIterCSMFloat implements WritableRectIter { public WritableRectIterCSMFloat(RenderedImage im, Rectangle bounds) { super(im, bounds); } public void setSample(int s) { bank[offset + bandOffset] = (float)s; } public void setSample(int b, int s) { bankData[b][offset + bandOffsets[b]] = (float)s; } public void setSample(float s) { bank[offset + bandOffset] = s; } public void setSample(int b, float s) { bankData[b][offset + bandOffsets[b]] = s; } public void setSample(double s) { bank[offset + bandOffset] = (float)s; } public void setSample(int b, double s) { bankData[b][offset + bandOffsets[b]] = (float)s; } public void setPixel(int[] iArray) { for (int b = 0; b < numBands; b++) { bankData[b][offset + bandOffsets[b]] = (float)iArray[b]; } } public void setPixel(float[] fArray) { for (int b = 0; b < numBands; b++) { bankData[b][offset + bandOffsets[b]] = fArray[b]; } } public void setPixel(double[] dArray) { for (int b = 0; b < numBands; b++) { bankData[b][offset + bandOffsets[b]] = (float)dArray[b]; } } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/iterator/RandomIterCSMFloat.java0000644000175000017500000000400010203035544027672 0ustar mathieumathieu/* * $RCSfile: RandomIterCSMFloat.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:42 $ * $State: Exp $ */ package com.sun.media.jai.iterator; import java.awt.Rectangle; import java.awt.image.RenderedImage; import com.sun.media.jai.util.DataBufferUtils; /** * @since EA2 */ public class RandomIterCSMFloat extends RandomIterCSM { float[][] bankData; public RandomIterCSMFloat(RenderedImage im, Rectangle bounds) { super(im, bounds); } protected final void dataBufferChanged() { this.bankData = DataBufferUtils.getBankDataFloat(dataBuffer); } public final int getSample(int x, int y, int b) { makeCurrent(x - boundsX, y - boundsX); return (int)bankData[b][(x - sampleModelTranslateX)*pixelStride + (y - sampleModelTranslateY)*scanlineStride + bandOffsets[b]]; } public final float getSampleFloat(int x, int y, int b) { makeCurrent(x - boundsX, y - boundsX); return bankData[b][(x - sampleModelTranslateX)*pixelStride + (y - sampleModelTranslateY)*scanlineStride + bandOffsets[b]]; } public final double getSampleDouble(int x, int y, int b) { makeCurrent(x - boundsX, y - boundsX); return (double)bankData[b][(x - sampleModelTranslateX)*pixelStride + (y - sampleModelTranslateY)*scanlineStride + bandOffsets[b]]; } public float[] getPixel(int x, int y, float[] fArray) { if (fArray == null) { fArray = new float[numBands]; } int offset = (x - sampleModelTranslateX)*pixelStride + (y - sampleModelTranslateY)*scanlineStride; for (int b = 0; b < numBands; b++) { fArray[b] = bankData[b][offset + bandOffsets[b]]; } return fArray; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/iterator/RandomIterCSMByte.java0000644000175000017500000000406710203035544027545 0ustar mathieumathieu/* * $RCSfile: RandomIterCSMByte.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:42 $ * $State: Exp $ */ package com.sun.media.jai.iterator; import java.awt.Rectangle; import java.awt.image.DataBufferByte; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; /** * @since EA2 */ public class RandomIterCSMByte extends RandomIterCSM { byte[][] bankData; public RandomIterCSMByte(RenderedImage im, Rectangle bounds) { super(im, bounds); } protected final void dataBufferChanged() { this.bankData = ((DataBufferByte)dataBuffer).getBankData(); } public final int getSample(int x, int y, int b) { makeCurrent(x - boundsX, y - boundsY); return bankData[b][(x - sampleModelTranslateX)*pixelStride + (y - sampleModelTranslateY)*scanlineStride + bandOffsets[b]] & 0xff; } public final float getSampleFloat(int x, int y, int b) { makeCurrent(x - boundsX, y - boundsX); return (float)(bankData[b][(x - sampleModelTranslateX)*pixelStride + (y - sampleModelTranslateY)*scanlineStride + bandOffsets[b]] & 0xff); } public final double getSampleDouble(int x, int y, int b) { makeCurrent(x - boundsX, y - boundsX); return (double)(bankData[b][(x - sampleModelTranslateX)*pixelStride + (y - sampleModelTranslateY)*scanlineStride + bandOffsets[b]] & 0xff); } public int[] getPixel(int x, int y, int[] iArray) { if (iArray == null) { iArray = new int[numBands]; } int offset = (x - sampleModelTranslateX)*pixelStride + (y - sampleModelTranslateY)*scanlineStride; for (int b = 0; b < numBands; b++) { iArray[b] = bankData[b][offset + bandOffsets[b]] & 0xff; } return iArray; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/iterator/RectIterCSM.java0000644000175000017500000001070210203035544026367 0ustar mathieumathieu/* * $RCSfile: RectIterCSM.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:43 $ * $State: Exp $ */ package com.sun.media.jai.iterator; import java.awt.Rectangle; import java.awt.image.ComponentSampleModel; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import javax.media.jai.PlanarImage; import javax.media.jai.iterator.RectIter; /** */ public abstract class RectIterCSM extends RectIterFallback { protected int[] bankIndices; protected int scanlineStride; protected int pixelStride; protected int[] bandOffsets; protected int[] DBOffsets; protected int offset; protected int bandOffset; public RectIterCSM(RenderedImage im, Rectangle bounds) { super(im, bounds); ComponentSampleModel csm = (ComponentSampleModel)sampleModel; this.scanlineStride = csm.getScanlineStride(); this.pixelStride = csm.getPixelStride(); this.bankIndices = csm.getBankIndices(); int[] bo = csm.getBandOffsets(); this.bandOffsets = new int[numBands + 1]; for (int i = 0; i < numBands; i++) { bandOffsets[i] = bo[i]; } bandOffsets[numBands] = 0; this.DBOffsets = new int[numBands]; this.offset = (y - sampleModelTranslateY)*scanlineStride + (x - sampleModelTranslateX)*pixelStride; this.bandOffset = bandOffsets[0]; } protected void dataBufferChanged() {} protected void adjustBandOffsets() { int[] newDBOffsets = dataBuffer.getOffsets(); for (int i = 0; i < numBands; i++) { int bankNum = bankIndices[i]; bandOffsets[i] += newDBOffsets[bankNum] - DBOffsets[bankNum]; } this.DBOffsets = newDBOffsets; } protected void setDataBuffer() { Raster tile = im.getTile(tileX, tileY); this.dataBuffer = tile.getDataBuffer(); dataBufferChanged(); int newSampleModelTranslateX = tile.getSampleModelTranslateX(); int newSampleModelTranslateY = tile.getSampleModelTranslateY(); int deltaX = sampleModelTranslateX - newSampleModelTranslateX; int deltaY = sampleModelTranslateY - newSampleModelTranslateY; offset += deltaY*scanlineStride + deltaX*pixelStride; this.sampleModelTranslateX = newSampleModelTranslateX; this.sampleModelTranslateY = newSampleModelTranslateY; } public void startLines() { offset += (bounds.y - y)*scanlineStride; y = bounds.y; tileY = startTileY; setTileYBounds(); setDataBuffer(); } public void nextLine() { ++y; offset += scanlineStride; } public void jumpLines(int num) { int jumpY = y + num; if (jumpY < bounds.y || jumpY > lastY) { // Jumped outside the image. throw new IndexOutOfBoundsException(JaiI18N.getString("RectIterFallback1")); } y = jumpY; offset += num*scanlineStride; if (y < prevYBoundary || y > nextYBoundary) { this.tileY = PlanarImage.YToTileY(y, tileGridYOffset, tileHeight); setTileYBounds(); setDataBuffer(); } } public void startPixels() { offset += (bounds.x - x)*pixelStride; x = bounds.x; tileX = startTileX; setTileXBounds(); setDataBuffer(); } public void nextPixel() { ++x; offset += pixelStride; } public void jumpPixels(int num) { int jumpX = x + num; if (jumpX < bounds.x || jumpX > lastX) { // Jumped outside the image. throw new IndexOutOfBoundsException(JaiI18N.getString("RectIterFallback0")); } x = jumpX; offset += num*pixelStride; if (x < prevXBoundary || x > nextXBoundary) { this.tileX = PlanarImage.XToTileX(x, tileGridXOffset, tileWidth); setTileXBounds(); setDataBuffer(); } } public void startBands() { b = 0; bandOffset = bandOffsets[0]; } public void nextBand() { ++b; bandOffset = bandOffsets[b]; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/iterator/WritableRandomIterCSMDouble.java0000644000175000017500000000207210203035544031540 0ustar mathieumathieu/* * $RCSfile: WritableRandomIterCSMDouble.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:44 $ * $State: Exp $ */ package com.sun.media.jai.iterator; import java.awt.Rectangle; import java.awt.image.WritableRenderedImage; import javax.media.jai.iterator.WritableRandomIter; /** * @since EA2 */ public final class WritableRandomIterCSMDouble extends RandomIterCSMDouble implements WritableRandomIter { public WritableRandomIterCSMDouble(WritableRenderedImage im, Rectangle bounds) { super(im, bounds); } public void setSample(int x, int y, int b, int val) { } public void setSample(int x, int y, int b, float val) { } public void setSample(int x, int y, int b, double val) { } public void setPixel(int x, int y, int[] iArray) { } public void setPixel(int x, int y, float[] fArray) { } public void setPixel(int x, int y, double[] dArray) { } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/iterator/RandomIterFallback.java0000644000175000017500000001273210203035544027774 0ustar mathieumathieu/* * $RCSfile: RandomIterFallback.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:43 $ * $State: Exp $ */ package com.sun.media.jai.iterator; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import javax.media.jai.PlanarImage; import javax.media.jai.iterator.RandomIter; /** * @since EA2 */ public class RandomIterFallback implements RandomIter { protected RenderedImage im; protected Rectangle boundsRect; protected SampleModel sampleModel; protected int xID; protected int yID; protected int sampleModelTranslateX; protected int sampleModelTranslateY; protected DataBuffer dataBuffer = null; protected int boundsX; protected int boundsY; protected int[] xTiles; protected int[] yTiles; public RandomIterFallback(RenderedImage im, Rectangle bounds) { this.im = im; Rectangle imBounds = new Rectangle(im.getMinX(), im.getMinY(), im.getWidth(), im.getHeight()); this.boundsRect = imBounds.intersection(bounds); this.sampleModel = im.getSampleModel(); int x = boundsRect.x; int y = boundsRect.y; int width = boundsRect.width; int height = boundsRect.height; this.boundsX = boundsRect.x; this.boundsY = boundsRect.y; this.xTiles = new int[width]; this.yTiles = new int[height]; int tileWidth = im.getTileWidth(); int tileGridXOffset = im.getTileGridXOffset(); int minTileX = PlanarImage.XToTileX(x, tileGridXOffset, tileWidth); int offsetX = x - PlanarImage.tileXToX(minTileX, tileGridXOffset, tileWidth); int tileX = minTileX; for (int i = 0; i < width; i++) { xTiles[i] = tileX; ++offsetX; if (offsetX == tileWidth) { ++tileX; offsetX = 0; } } int tileHeight = im.getTileHeight(); int tileGridYOffset = im.getTileGridYOffset(); int minTileY = PlanarImage.YToTileY(y, tileGridYOffset, tileHeight); int offsetY = y - PlanarImage.tileYToY(minTileY, tileGridYOffset, tileHeight); int tileY = minTileY; for (int i = 0; i < height; i++) { yTiles[i] = tileY; ++offsetY; if (offsetY == tileHeight) { ++tileY; offsetY = 0; } } } /** * Sets dataBuffer to the correct buffer for the pixel * (x, y) = (xLocal + boundsRect.x, yLocal + boundsRect.y). * * @param xLocal the X coordinate in the local coordinate system. * @param yLocal the Y coordinate in the local coordinate system. */ private void makeCurrent(int xLocal, int yLocal) { int xIDNew = xTiles[xLocal]; int yIDNew = yTiles[yLocal]; if ((xIDNew != xID) || (yIDNew != yID) || (dataBuffer == null)) { xID = xIDNew; yID = yIDNew; Raster tile = im.getTile(xID, yID); this.dataBuffer = tile.getDataBuffer(); this.sampleModelTranslateX = tile.getSampleModelTranslateX(); this.sampleModelTranslateY = tile.getSampleModelTranslateY(); } } public int getSample(int x, int y, int b) { makeCurrent(x - boundsX, y - boundsY); return sampleModel.getSample(x - sampleModelTranslateX, y - sampleModelTranslateY, b, dataBuffer); } public float getSampleFloat(int x, int y, int b) { makeCurrent(x - boundsX, y - boundsY); return sampleModel.getSampleFloat(x - sampleModelTranslateX, y - sampleModelTranslateY, b, dataBuffer); } public double getSampleDouble(int x, int y, int b) { makeCurrent(x - boundsX, y - boundsY); return sampleModel.getSampleDouble(x - sampleModelTranslateX, y - sampleModelTranslateY, b, dataBuffer); } public int[] getPixel(int x, int y, int[] iArray) { makeCurrent(x - boundsX, y - boundsY); return sampleModel.getPixel(x - sampleModelTranslateX, y - sampleModelTranslateY, iArray, dataBuffer); } public float[] getPixel(int x, int y, float[] fArray) { makeCurrent(x - boundsX, y - boundsY); return sampleModel.getPixel(x - sampleModelTranslateX, y - sampleModelTranslateY, fArray, dataBuffer); } public double[] getPixel(int x, int y, double[] dArray) { makeCurrent(x - boundsX, y - boundsY); return sampleModel.getPixel(x - sampleModelTranslateX, y - sampleModelTranslateY, dArray, dataBuffer); } public void done() { xTiles = null; yTiles = null; dataBuffer = null; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/iterator/RandomIterCSMInt.java0000644000175000017500000000116510203035544027370 0ustar mathieumathieu/* * $RCSfile: RandomIterCSMInt.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:42 $ * $State: Exp $ */ package com.sun.media.jai.iterator; import java.awt.Rectangle; import java.awt.image.RenderedImage; import javax.media.jai.iterator.RandomIter; /** * @since EA2 */ public class RandomIterCSMInt extends RandomIterCSM { public RandomIterCSMInt(RenderedImage im, Rectangle bounds) { super(im, bounds); } public final int getSample(int x, int y, int b) { return 0; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/iterator/JaiI18N.java0000644000175000017500000000075310203035544025413 0ustar mathieumathieu/* * $RCSfile: JaiI18N.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:42 $ * $State: Exp $ */ package com.sun.media.jai.iterator; import com.sun.media.jai.util.PropertyUtil; class JaiI18N { static String packageName = "com.sun.media.jai.iterator"; public static String getString(String key) { return PropertyUtil.getString(packageName, key); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/iterator/RectIterCSMFloat.java0000644000175000017500000000511210203035544027354 0ustar mathieumathieu/* * $RCSfile: RectIterCSMFloat.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:43 $ * $State: Exp $ */ package com.sun.media.jai.iterator; import java.awt.Rectangle; import java.awt.image.RenderedImage; import com.sun.media.jai.util.DataBufferUtils; /** */ public class RectIterCSMFloat extends RectIterCSM { float[][] bankData; float[] bank; public RectIterCSMFloat(RenderedImage im, Rectangle bounds) { super(im, bounds); this.bankData = new float[numBands + 1][]; dataBufferChanged(); } protected final void dataBufferChanged() { if (bankData == null) { return; } float[][] bd = DataBufferUtils.getBankDataFloat(dataBuffer); for (int i = 0; i < numBands; i++) { bankData[i] = bd[bankIndices[i]]; } bank = bankData[b]; adjustBandOffsets(); } public void startBands() { super.startBands(); bank = bankData[0]; } public void nextBand() { super.nextBand(); bank = bankData[b]; } public final int getSample() { return (int)bank[offset + bandOffset]; } public final int getSample(int b) { return (int)bankData[b][offset + bandOffsets[b]]; } public final float getSampleFloat() { return bank[offset + bandOffset]; } public final float getSampleFloat(int b) { return bankData[b][offset + bandOffsets[b]]; } public final double getSampleDouble() { return (double)bank[offset + bandOffset]; } public final double getSampleDouble(int b) { return (double)bankData[b][offset + bandOffsets[b]]; } public int[] getPixel(int[] iArray) { if (iArray == null) { iArray = new int[numBands]; } for (int b = 0; b < numBands; b++) { iArray[b] = (int)bankData[b][offset + bandOffsets[b]]; } return iArray; } public float[] getPixel(float[] fArray) { if (fArray == null) { fArray = new float[numBands]; } for (int b = 0; b < numBands; b++) { fArray[b] = bankData[b][offset + bandOffsets[b]]; } return fArray; } public double[] getPixel(double[] dArray) { if (dArray == null) { dArray = new double[numBands]; } for (int b = 0; b < numBands; b++) { dArray[b] = (double)bankData[b][offset + bandOffsets[b]]; } return dArray; } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/iterator/WritableRandomIterCSMUShort.java0000644000175000017500000000207210203035544031552 0ustar mathieumathieu/* * $RCSfile: WritableRandomIterCSMUShort.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:45 $ * $State: Exp $ */ package com.sun.media.jai.iterator; import java.awt.Rectangle; import java.awt.image.WritableRenderedImage; import javax.media.jai.iterator.WritableRandomIter; /** * @since EA2 */ public final class WritableRandomIterCSMUShort extends RandomIterCSMUShort implements WritableRandomIter { public WritableRandomIterCSMUShort(WritableRenderedImage im, Rectangle bounds) { super(im, bounds); } public void setSample(int x, int y, int b, int val) { } public void setSample(int x, int y, int b, float val) { } public void setSample(int x, int y, int b, double val) { } public void setPixel(int x, int y, int[] iArray) { } public void setPixel(int x, int y, float[] fArray) { } public void setPixel(int x, int y, double[] dArray) { } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/iterator/WritableRandomIterCSMFloat.java0000644000175000017500000000206510203035544031375 0ustar mathieumathieu/* * $RCSfile: WritableRandomIterCSMFloat.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:44 $ * $State: Exp $ */ package com.sun.media.jai.iterator; import java.awt.Rectangle; import java.awt.image.WritableRenderedImage; import javax.media.jai.iterator.WritableRandomIter; /** * @since EA2 */ public final class WritableRandomIterCSMFloat extends RandomIterCSMFloat implements WritableRandomIter { public WritableRandomIterCSMFloat(WritableRenderedImage im, Rectangle bounds) { super(im, bounds); } public void setSample(int x, int y, int b, int val) { } public void setSample(int x, int y, int b, float val) { } public void setSample(int x, int y, int b, double val) { } public void setPixel(int x, int y, int[] iArray) { } public void setPixel(int x, int y, float[] fArray) { } public void setPixel(int x, int y, double[] dArray) { } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/iterator/WritableRandomIterCSMShort.java0000644000175000017500000000206510203035544031427 0ustar mathieumathieu/* * $RCSfile: WritableRandomIterCSMShort.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:45 $ * $State: Exp $ */ package com.sun.media.jai.iterator; import java.awt.Rectangle; import java.awt.image.WritableRenderedImage; import javax.media.jai.iterator.WritableRandomIter; /** * @since EA2 */ public final class WritableRandomIterCSMShort extends RandomIterCSMShort implements WritableRandomIter { public WritableRandomIterCSMShort(WritableRenderedImage im, Rectangle bounds) { super(im, bounds); } public void setSample(int x, int y, int b, int val) { } public void setSample(int x, int y, int b, float val) { } public void setSample(int x, int y, int b, double val) { } public void setPixel(int x, int y, int[] iArray) { } public void setPixel(int x, int y, float[] fArray) { } public void setPixel(int x, int y, double[] dArray) { } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/iterator/WrapperRI.java0000644000175000017500000000426010203035544026160 0ustar mathieumathieu/* * $RCSfile: WrapperRI.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:44 $ * $State: Exp $ */ package com.sun.media.jai.iterator; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.util.Vector; /** * @since EA2 */ public class WrapperRI implements RenderedImage { Raster ras; public WrapperRI(Raster ras) { this.ras = ras; } public Vector getSources() { return null; } public Object getProperty(String name) { return null; } public String[] getPropertyNames() { return null; } public ColorModel getColorModel() { return null; } public SampleModel getSampleModel() { return ras.getSampleModel(); } public int getWidth() { return ras.getWidth(); } public int getHeight() { return ras.getHeight(); } public int getMinX() { return ras.getMinX(); } public int getMinY() { return ras.getMinY(); } public int getNumXTiles() { return 1; } public int getNumYTiles() { return 1; } public int getMinTileX() { return 0; } public int getMinTileY() { return 0; } public int getTileWidth() { return ras.getWidth(); } public int getTileHeight() { return ras.getHeight(); } public int getTileGridXOffset() { return ras.getMinX(); } public int getTileGridYOffset() { return ras.getMinY(); } public Raster getTile(int tileX, int tileY) { return ras; } public Raster getData() { throw new RuntimeException(JaiI18N.getString("WrapperRI0")); } public Raster getData(Rectangle rect) { throw new RuntimeException(JaiI18N.getString("WrapperRI0")); } public WritableRaster copyData(WritableRaster raster) { throw new RuntimeException(JaiI18N.getString("WrapperRI0")); } } jai-core-1.1.4/src/share/classes/com/sun/media/jai/iterator/RectIterCSMByte.java0000644000175000017500000000521110203035544027212 0ustar mathieumathieu/* * $RCSfile: RectIterCSMByte.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:55:43 $ * $State: Exp $ */ package com.sun.media.jai.iterator; import java.awt.Rectangle; import java.awt.image.DataBufferByte; import java.awt.image.RenderedImage; /** */ public class RectIterCSMByte extends RectIterCSM { byte[][] bankData; byte[] bank; public RectIterCSMByte(RenderedImage im, Rectangle bounds) { super(im, bounds); this.bankData = new byte[numBands + 1][]; dataBufferChanged(); } protected final void dataBufferChanged() { if (bankData == null) { return; } byte[][] bd = ((DataBufferByte)dataBuffer).getBankData(); for (int i = 0; i < numBands; i++) { bankData[i] = bd[bankIndices[i]]; } bank = bankData[b]; adjustBandOffsets(); } public void startBands() { super.startBands(); bank = bankData[0]; } public void nextBand() { super.nextBand(); bank = bankData[b]; } public final int getSample() { return bank[offset + bandOffset] & 0xff; } public final int getSample(int b) { return bankData[b][offset + bandOffsets[b]] & 0xff; } public final float getSampleFloat() { return (float)(bank[offset + bandOffset] & 0xff); } public final float getSampleFloat(int b) { return (float)(bankData[b][offset + bandOffsets[b]] & 0xff); } public final double getSampleDouble() { return (double)(bank[offset + bandOffset] & 0xff); } public final double getSampleDouble(int b) { return (double)(bankData[b][offset + bandOffsets[b]] & 0xff); } public int[] getPixel(int[] iArray) { if (iArray == null) { iArray = new int[numBands]; } for (int b = 0; b < numBands; b++) { iArray[b] = bankData[b][offset + bandOffsets[b]] & 0xff; } return iArray; } public float[] getPixel(float[] fArray) { if (fArray == null) { fArray = new float[numBands]; } for (int b = 0; b < numBands; b++) { fArray[b] = (float)(bankData[b][offset + bandOffsets[b]] & 0xff); } return fArray; } public double[] getPixel(double[] dArray) { if (dArray == null) { dArray = new double[numBands]; } for (int b = 0; b < numBands; b++) { dArray[b] = (double)(bankData[b][offset + bandOffsets[b]] & 0xff); } return dArray; } } jai-core-1.1.4/src/share/classes/javax/0000755000175000017500000000000011633360402017515 5ustar mathieumathieujai-core-1.1.4/src/share/classes/javax/media/0000755000175000017500000000000011633360402020574 5ustar mathieumathieujai-core-1.1.4/src/share/classes/javax/media/jai/0000755000175000017500000000000011633360404021341 5ustar mathieumathieujai-core-1.1.4/src/share/classes/javax/media/jai/PropertySourceImpl.java0000644000175000017500000003524310203035544026036 0ustar mathieumathieu/* * $RCSfile: PropertySourceImpl.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:17 $ * $State: Exp $ */ package javax.media.jai; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Collections; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import com.sun.media.jai.util.PropertyUtil; import javax.media.jai.util.CaselessStringKey; /** * A utility implementation of the PropertySource interface. * Properties are managed by three internal structures: one which maps * property names to values, a second which maps property names to * PropertySources, and a third which tracks which entries * in the name-value mapping derived their respective values from a * PropertySource in the name-PropertySource * mapping. The case of property names is retained for subsequent * retrieval but is ignored when the names are used as keys. * * @see CaselessStringKey * @see PropertySource * @see WritablePropertySource * @see WritablePropertySourceImpl * * @since JAI 1.1 */ // NB A class of this name existed in JAI 1.0.2 but that class was renamed // to what is now PropertyEnvironment. public class PropertySourceImpl implements PropertySource, Serializable { /** * Mapping of CaselessStringKeys to values. * If this object is serialized, only those entries of which * the value is serializable will be retained. */ protected transient Map properties; /** * Mapping of CaselessStringKeys to * PropertySources. * If this object is serialized, only those entries of which * the value is serializable will be retained. */ protected transient Map propertySources; /** * CaselessStringKeys corresponding to the keys of entries * in properties which derived their respective * values from a PropertySource in * propertySources. */ protected Set cachedPropertyNames; /** * Constructs a PropertySourceImpl instance with * no properties set. */ protected PropertySourceImpl() { properties = new Hashtable(); propertySources = new Hashtable(); cachedPropertyNames = Collections.synchronizedSet(new HashSet()); } /** * Constructs a PropertySourceImpl instance which * will derive properties from one or both of the supplied parameters. * The propertyMap and propertySource parameters * will be used to initialize the name-value and * name-PropertySource mappings, respectively. * Entries in the propertyMap object will be assumed * to be properties if the key is a String or a * CaselessStringKey. The propertySource * object will be queried for the names of properties that it emits * but requests for associated values will not be made at this time * so as to to defer any calculation that such requests might provoke. * The case of property names will be retained but will be ignored * insofar as the name is used as a key to the property value. * * @param propertyMap A Map from which to copy properties * which have keys which are either Strings or * CaselessStringKeys. * @param propertySource A PropertySource from which to * derive properties. * * @exception IllegalArgumentException if propertyMap * and propertySource are both null * and this constructor is not being invoked from within a * subclass constructor. When invoked from a subclass * constructor both parameters may be null. */ public PropertySourceImpl(Map propertyMap, PropertySource propertySource) { this(); // If both parameters are null throw an exception if this constructor // is not invoked from within a subclass constructor. if(propertyMap == null && propertySource == null) { boolean throwException = false; try { Class rootClass = Class.forName("javax.media.jai.PropertySourceImpl"); throwException = this.getClass().equals(rootClass); } catch(Exception e) { // Ignore it. } if(throwException) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } } if(propertyMap != null) { Iterator keys = propertyMap.keySet().iterator(); while(keys.hasNext()) { Object key = keys.next(); if(key instanceof String) { properties.put(new CaselessStringKey((String)key), propertyMap.get(key)); } else if(key instanceof CaselessStringKey) { properties.put((CaselessStringKey)key, propertyMap.get(key)); } } } if(propertySource != null) { String[] names = propertySource.getPropertyNames(); if(names != null) { int length = names.length; for(int i = 0; i < length; i++) { propertySources.put(new CaselessStringKey(names[i]), propertySource); } } } } /** * Returns an array of Strings recognized as names by * this property source. The case of the property names is retained. * If no properties are available, null will be returned. * * @return an array of Strings giving the valid * property names or null. */ public String[] getPropertyNames() { synchronized(properties) { if(properties.size() + propertySources.size() == 0) { return null; } // Create a set from the property name-value mapping. Set propertyNames = Collections.synchronizedSet(new HashSet(properties.keySet())); // Add all names not already in the set. propertyNames.addAll(propertySources.keySet()); // Copy names to an array. int length = propertyNames.size(); String[] names = new String[length]; Iterator elements = propertyNames.iterator(); int index = 0; while(elements.hasNext() && index < length) { // redundant test names[index++] = ((CaselessStringKey)elements.next()).getName(); } return names; } } /** * Returns an array of Strings recognized as names by * this property source that begin with the supplied prefix. If * no property names match, null will be returned. * The comparison is done in a case-independent manner. * * @return an array of Strings giving the valid * property names. * * @exception IllegalArgumentException if prefix * is null. */ public String[] getPropertyNames(String prefix) { return PropertyUtil.getPropertyNames(getPropertyNames(), prefix); } /** * Returns the class expected to be returned by a request for * the property with the specified name. If this information * is unavailable, null will be returned. * This method queries only the name-value mapping so as to avoid * requesting a property value from a PropertySource * to which the name might refer via the name-PropertySource * mapping. If it is known from getPropertyNames() that * the property is emitted by this PropertySource but this * method returns null, then getProperty() * will have to be invoked and the Class obtained from * the property value itself. * * @param propertyName the name of the property, as a String. * * @return The Class expected to be returneded by a * request for the value of this property or null. * * @exception IllegalArgumentException if propertyName * is null. */ public Class getPropertyClass(String propertyName) { if(propertyName == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } synchronized(properties) { Class propertyClass = null; Object value = properties.get(new CaselessStringKey(propertyName)); if(value != null) { propertyClass = value.getClass(); } return propertyClass; } } /** * Returns the value of a property. If the property name is not * recognized, java.awt.Image.UndefinedProperty will * be returned. * *

    If the requested name is found in the name-value mapping, * the corresponding value will be returned. Otherwise the * name-PropertySource mapping will be queried and the * value will be derived from the found PropertySource, * if any. If the value is derived from a PropertySource, * a record will be kept of this and this property will be referred to * as a "cached property". * * @param propertyName the name of the property, as a String. * * @return the value of the property, as an * Object, or the value * java.awt.Image.UndefinedProperty. * * @exception IllegalArgumentException if propertyName * is null. */ public Object getProperty(String propertyName) { if(propertyName == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } synchronized(properties) { CaselessStringKey key = new CaselessStringKey(propertyName); // Try to retrieve from value mapping. Object value = properties.get(key); if(value == null) { // Try to retrieve from PropertySource mapping. PropertySource propertySource = (PropertySource)propertySources.get(key); if(propertySource != null) { value = propertySource.getProperty(propertyName); if(value != java.awt.Image.UndefinedProperty) { // Cache the value and flag it as such. properties.put(key, value); cachedPropertyNames.add(key); } } else { // No PropertySource: undefined property. value = java.awt.Image.UndefinedProperty; } } return value; } } /** * Copies into a Map all properties currently available * via this PropertySource. All property values are * copied by reference rather than by being cloned. The keys in the * Map will be Strings with the original * property name case intact. Property values derived from the * name-value mapping will take precedence. The names of properties * whose values are derived via the name-PropertySource * mapping will be recorded as "cached properties". * * @return A Map of all properties or null if * none are defined. */ public Map getProperties() { if(properties.size() + propertySources.size() == 0) { return null; } synchronized(properties) { Hashtable props = null; String[] propertyNames = getPropertyNames(); if(propertyNames != null) { int length = propertyNames.length; props = new Hashtable(properties.size()); for(int i = 0; i < length; i++) { String name = propertyNames[i]; Object value = getProperty(name); props.put(name, value); } } return props; } } /** * Serialize a Map which contains serializable keys. */ private static void writeMap(ObjectOutputStream out, Map map) throws IOException{ // Create an empty Hashtable. Hashtable table = new Hashtable(); // Copy serializable properties to local table. Iterator keys = map.keySet().iterator(); while(keys.hasNext()) { Object key = keys.next(); Object value = map.get(key); if(value instanceof Serializable) { table.put(key, value); } } // Write serialized form to the stream. out.writeObject(table); } /** * Serialize the PropertySourceImpl. */ private void writeObject(ObjectOutputStream out) throws IOException { // Write serializable fields. out.defaultWriteObject(); synchronized(properties) { // Write serializable forms of name-value and // name-PropertySource maps. writeMap(out, properties); writeMap(out, propertySources); } } /** * Deserialize the PropertySourceImpl. */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { // Read serializable fields. in.defaultReadObject(); // Read serializable forms of name-value and name-PropertySource maps. properties = (Map)in.readObject(); propertySources = (Map)in.readObject(); // Clean up cached names list: delete names not in deserialized // name-value map. Iterator names = cachedPropertyNames.iterator(); Set propertyNames = properties.keySet(); while(names.hasNext()) { if(!propertyNames.contains(names.next())) { names.remove(); // remove name from cachedPropertyNames. } } } } jai-core-1.1.4/src/share/classes/javax/media/jai/TiledImageGraphics.java0000644000175000017500000012154010203035544025670 0ustar mathieumathieu/* * $RCSfile: TiledImageGraphics.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:23 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Color; import java.awt.Composite; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.Image; import java.awt.Paint; import java.awt.PaintContext; import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.RenderingHints.Key; import java.awt.Shape; import java.awt.Stroke; import java.awt.font.FontRenderContext; import java.awt.font.GlyphVector; import java.awt.font.TextLayout; import java.awt.geom.AffineTransform; import java.awt.geom.Area; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.image.BufferedImageOp; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.ImageObserver; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.awt.image.renderable.RenderableImage; import java.lang.reflect.Method; import java.text.AttributedCharacterIterator; import java.util.Hashtable; import java.util.Map; import javax.media.jai.util.ImagingException; import javax.media.jai.util.ImagingListener; import com.sun.media.jai.util.JDKWorkarounds; /** * A concrete (i.e., non-abstract) class implementing all the methods * of Graphics2D (and thus of Graphics) with * a TiledImage as the implicit drawing canvas. * The actual implementation will use Java2D to do most of the work * by packaging up the image tiles in a form that Java2D can * understand. * *

    Since the public methods of this class all derive from * Graphics2D, they are not commented individually. * *

    The ColorModel for the canvas will be that of the * associated TiledImage unless that ColorModel * is null. If the TiledImage ColorModel is null, * an attempt will first be made to deduce the ColorModel from * the SampleModel of the TiledImage using the * createColorModel() method of PlanarImage. * If the ColorModel is still null, the default RGB * ColorModel returned by the getRGBdefault() * method of ColorModel will be used if the * TiledImage has a compatible SampleModel. * If no acceptable ColorModel can be derived an * UnsupportedOperationException will be thrown. * * @see java.awt.Graphics * @see java.awt.Graphics2D * @see java.awt.image.ColorModel * @see java.awt.image.SampleModel * @see TiledImage * */ class TiledImageGraphics extends Graphics2D { // Constants private static final Class GRAPHICS2D_CLASS = Graphics2D.class; private static final int PAINT_MODE = 1; private static final int XOR_MODE = 2; // TiledImageGraphics state private TiledImage tiledImage; Hashtable properties; private RenderingHints renderingHints; // Cached variables available from the source TiledImage private int tileWidth; private int tileHeight; private int tileXMinimum; private int tileXMaximum; private int tileYMinimum; private int tileYMaximum; private ColorModel colorModel; // Graphics state information (from java.awt.Graphics) private Point origin; private Shape clip; private Color color; private Font font; private int paintMode = PAINT_MODE; private Color XORColor; // Graphics state information (from java.awt.Graphics2D) private Color background; private Composite composite; private Paint paint; private Stroke stroke; private AffineTransform transform; // ---------- Methods specific to TiledImageGraphics ---------- /** * Determine the bounding box of the points represented by the supplied * arrays of X and Y coordinates. * * @param xPoints An array of x points. * @param yPoints An array of y points. * @param nPoints The total number of points. */ private static final Rectangle getBoundingBox(int[] xPoints, int[] yPoints, int nPoints) { if(nPoints <= 0) return null; int minX; int maxX; int minY; int maxY; minX = maxX = xPoints[0]; minY = maxY = yPoints[0]; for(int i = 1; i < nPoints; i++) { minX = Math.min(minX, xPoints[i]); maxX = Math.max(maxX, xPoints[i]); minY = Math.min(minY, yPoints[i]); maxY = Math.max(maxY, yPoints[i]); } return new Rectangle(minX, minY, maxX - minX + 1, maxY - minY + 1); } /** * Construct a TiledImageGraphics object that draws onto a * particular TiledImage. The TiledImage * parameter must be of integral data type or an * UnsupportedOperationException will be thrown. Likewise, * if no appropriate ColorModel can be derived an * UnsupportedOperationException will be thrown. * * @param im The TiledImage which will serve as the graphics * canvas. * @throws UnsupportedOperationException if no appropriate * ColorModel can be derived. */ public TiledImageGraphics(TiledImage im) { // Check for non-integral data type. int dataType = im.getSampleModel().getTransferType(); if(dataType != DataBuffer.TYPE_BYTE && dataType != DataBuffer.TYPE_SHORT && dataType != DataBuffer.TYPE_USHORT && dataType != DataBuffer.TYPE_INT) { throw new UnsupportedOperationException(JaiI18N.getString("TiledImageGraphics0")); } // Cache the TiledImage. tiledImage = im; // Cache the tile dimensions and index extrema. tileWidth = im.getTileWidth(); tileHeight = im.getTileHeight(); tileXMinimum = im.getMinTileX(); tileXMaximum = im.getMaxTileX(); tileYMinimum = im.getMinTileY(); tileYMaximum = im.getMaxTileY(); // Attempt to derive an appropriate ColorModel. colorModel = getColorModel(tiledImage); // Obtain a Graphics2D object from which to derive state Graphics2D g = getBogusGraphics2D(false); // -- java.awt.Graphics state -- origin = new Point(0, 0); setClip(tiledImage.getBounds()); setColor(g.getColor()); setFont(g.getFont()); setPaintMode(); // -- java.awt.Graphics2D state -- setBackground(g.getBackground()); setComposite(g.getComposite()); setStroke(g.getStroke()); setTransform(g.getTransform()); // Dispose of the Graphics2D g.dispose(); // -- TiledImageGraphics state -- // Cache the Hashtable of properties. properties = tiledImage.getProperties(); // Create RenderingHints from the properties. renderingHints = new RenderingHints(properties); } /** * Copy the graphics state of the current object to a * Graphics2D object. * * @param g2d The target Graphics2D object. */ private void copyState(Graphics2D g2d) { // java.awt.Graphics state g2d.translate(origin.x, origin.y); setClip(getClip()); g2d.setColor(getColor()); if(paintMode == PAINT_MODE) { g2d.setPaintMode(); } else if(XORColor != null) { g2d.setXORMode(XORColor); } g2d.setFont(getFont()); // java.awt.Graphics2D state g2d.setBackground(getBackground()); g2d.setComposite(getComposite()); if(paint != null) g2d.setPaint(getPaint()); g2d.setRenderingHints(renderingHints); g2d.setStroke(getStroke()); g2d.setTransform(getTransform()); } /** * Creates a bogus Graphics2D object to be used to retrieve * information dependent on system aspects which are image-independent. * *

    The dispose() method of the Graphics2D * object returned should be called to free the associated resources as\ * soon as possible. * * @param shouldCopyState Whether the state of the returned * Graphics2D should be initialized to that of the * current TiledImageGraphics object. * * @return A Graphics2D object. */ private Graphics2D getBogusGraphics2D(boolean shouldCopyState) { Raster r = tiledImage.getTile(tileXMinimum, tileYMinimum); WritableRaster wr = r.createCompatibleWritableRaster(1, 1); BufferedImage bi = new BufferedImage(colorModel, wr, colorModel.isAlphaPremultiplied(), properties); Graphics2D bogusG2D = bi.createGraphics(); if(shouldCopyState) { copyState(bogusG2D); } return bogusG2D; } /** * Derive an approriate ColorModel for use with the * underlying BufferedImage canvas. If an appropriate * ColorModel cannot be derived an * UnsupportedOperationException will be thrown. * * @return An appropriate ColorModel. * @throws UnsupportedOperationException if no appropriate * ColorModel can be derived. */ private static ColorModel getColorModel(TiledImage ti) { // Derive an appropriate ColorModel for the BufferedImage: // first try to use that of the TiledImage. ColorModel colorModel = ti.getColorModel(); if(colorModel == null) { // Try to guess an appropriate ColorModel. if(colorModel == null) { // First try to deduce one from the TiledImage SampleModel. SampleModel sm = ti.getSampleModel(); colorModel = PlanarImage.createColorModel(sm); if(colorModel == null) { // ColorModel still null: try RGB default model. ColorModel cm = ColorModel.getRGBdefault(); if(JDKWorkarounds.areCompatibleDataModels(sm, cm)) { colorModel = cm; } } // ColorModel is still null: throw an exception. if(colorModel == null) { throw new UnsupportedOperationException(JaiI18N.getString("TiledImageGraphics1")); } } } return colorModel; } /** * Effect a graphics operation on the TiledImage by * creating a BufferedImage for each tile in the affected * region and using the corresponding Graphics2D to * perform the equivalent operation on the tile. * * @param x The x coordinate of the upper left corner. * @param y The y coordinate of the upper left corner. * @param width The width of the region. * @param height The height of the region. * @param argTypes An array of the Classes of the arguments * of the specified operation. * @param args The arguments of the operation as an array of * Objects. */ private boolean doGraphicsOp(int x, int y, int width, int height, String name, Class[] argTypes, Object[] args) { boolean returnValue = false; // Determine the Method object associated with the Graphics2D method // having the indicated name. The search begins with the Graphics2D // class and continues to its superclasses until the Method is found. Method method = null; try { method = GRAPHICS2D_CLASS.getMethod(name, argTypes); } catch(Exception e) { String message = JaiI18N.getString("TiledImageGraphics2") + name; sendExceptionToListener(message, new ImagingException(e)); // throw new RuntimeException(e.getMessage()); } // Transform requested area to obtain actual bounds. Rectangle bounds = new Rectangle(x, y, width, height); bounds = getTransform().createTransformedShape(bounds).getBounds(); // Determine the range of tile indexes int minTileX = tiledImage.XToTileX(bounds.x); if(minTileX < tileXMinimum) minTileX = tileXMinimum; int minTileY = tiledImage.YToTileY(bounds.y); if(minTileY < tileYMinimum) minTileY = tileYMinimum; int maxTileX = tiledImage.XToTileX(bounds.x + bounds.width - 1); if(maxTileX > tileXMaximum) maxTileX = tileXMaximum; int maxTileY = tiledImage.YToTileY(bounds.y + bounds.height - 1); if(maxTileY > tileYMaximum) maxTileY = tileYMaximum; // Loop over the tiles for(int tileY = minTileY; tileY <= maxTileY; tileY++) { int tileMinY = tiledImage.tileYToY(tileY); for(int tileX = minTileX; tileX <= maxTileX; tileX++) { int tileMinX = tiledImage.tileXToX(tileX); // Get the WritableRaster of the current tile WritableRaster wr = tiledImage.getWritableTile(tileX, tileY); wr = wr.createWritableTranslatedChild(0, 0); // Create an equivalent BufferedImage BufferedImage bi = new BufferedImage(colorModel, wr, colorModel.isAlphaPremultiplied(), properties); // Create the associated Graphics2D Graphics2D g2d = bi.createGraphics(); // Initialize the Graphics2D state copyState(g2d); // Bias the tile origin so that the global coordinates // map correctly onto the tile. try { Point2D origin2D = g2d.getTransform().transform(new Point2D.Double(), null); Point pt = new Point((int)origin2D.getX() - tileMinX, (int)origin2D.getY() - tileMinY); Point2D pt2D = g2d.getTransform().inverseTransform(pt, null); g2d.translate(pt2D.getX(), pt2D.getY()); } catch(Exception e) { String message = JaiI18N.getString("TiledImageGraphics3"); sendExceptionToListener(message, new ImagingException(e)); // throw new RuntimeException(e.getMessage()); } // Perform the graphics operation try { Object retVal = method.invoke(g2d, args); if(retVal != null && retVal.getClass() == boolean.class) { returnValue = ((Boolean)retVal).booleanValue(); } } catch(Exception e) { String message = JaiI18N.getString("TiledImageGraphics3") + " " + name; sendExceptionToListener(message, new ImagingException(e)); // throw new RuntimeException(e.getMessage()); } // Dispose of the Graphics2D g2d.dispose(); // Notify the TiledImage that writing to the tile is complete tiledImage.releaseWritableTile(tileX, tileY); } } return returnValue; } /** * Effect a graphics operation on the TiledImage by * creating a BufferedImage for each tile in the affected * region and using the corresponding Graphics2D to * perform the equivalent operation on the tile. * * @param s The encompassing Shape. * @param argTypes An array of the Classes of the arguments * of the specified operation. * @param args The arguments of the operation as an array of * Objects. */ private boolean doGraphicsOp(Shape s, String name, Class[] argTypes, Object[] args) { Rectangle r = s.getBounds(); return doGraphicsOp(r.x, r.y, r.width, r.height, name, argTypes, args); } // ---------- Methods from java.awt.Graphics ---------- public Graphics create() { // Construct the TiledImageGraphics object from the current TiledImage. TiledImageGraphics tig = new TiledImageGraphics(tiledImage); // Copy the state of the current TiledImageGraphics object. copyState(tig); return tig; } // public Graphics create(int x, int y, int width, int height) // -- implemented in Graphics superclass public Color getColor() { return color; } public void setColor(Color c) { color = c; } public void setPaintMode() { paintMode = PAINT_MODE; } public void setXORMode(Color c1) { paintMode = XOR_MODE; XORColor = c1; } public Font getFont() { return font; } public void setFont(Font font) { this.font = font; } public FontMetrics getFontMetrics(Font f) { Graphics2D g2d = getBogusGraphics2D(true); FontMetrics fontMetrics = g2d.getFontMetrics(f); g2d.dispose(); return fontMetrics; } public Rectangle getClipBounds() { return clip.getBounds(); } public void clipRect(int x, int y, int width, int height) { clip(new Rectangle(x, y, width, height)); } public void setClip(int x, int y, int width, int height) { setClip(new Rectangle(x, y, width, height)); } public Shape getClip() { return clip; } public void setClip(Shape clip) { this.clip = clip; } public void copyArea(int x, int y, int width, int height, int dx, int dy) { Rectangle rect = getBoundingBox(new int[] {x, x+dx, x+width-1, x+width-1+dx}, new int[] {y, y+dy, y+height-1, y+height-1+dy}, 4); doGraphicsOp(rect, "copyArea", new Class[] {int.class, int.class, int.class, int.class, int.class, int.class}, new Object[] {new Integer(x), new Integer(y), new Integer(width), new Integer(height), new Integer(dx), new Integer(dy)}); } public void drawLine(int x1, int y1, int x2, int y2) { Rectangle rect = getBoundingBox(new int[] {x1, x2}, new int[] {y1, y2}, 2); doGraphicsOp(rect, "drawLine", new Class[] {int.class, int.class, int.class, int.class}, new Object[] {new Integer(x1), new Integer(y1), new Integer(x2), new Integer(y2)}); } public void fillRect(int x, int y, int width, int height) { doGraphicsOp(x, y, width, height, "fillRect", new Class[] {int.class, int.class, int.class, int.class}, new Object[] {new Integer(x), new Integer(y), new Integer(width), new Integer(height)}); } // public void drawRect(int x, int y, int width, int height) // -- implemented in Graphics superclass public void clearRect(int x, int y, int width, int height) { doGraphicsOp(x, y, width, height, "clearRect", new Class[] {int.class, int.class, int.class, int.class}, new Object[] {new Integer(x), new Integer(y), new Integer(width), new Integer(height)}); } public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { doGraphicsOp(x-arcWidth, y-arcHeight, width+2*arcWidth, height+2*arcHeight, "drawRoundRect", new Class[] {int.class, int.class, int.class, int.class, int.class, int.class}, new Object[] {new Integer(x), new Integer(y), new Integer(width), new Integer(height), new Integer(arcWidth), new Integer(arcHeight)}); } public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { doGraphicsOp(x-arcWidth, y-arcHeight, width+2*arcWidth, height+2*arcHeight, "fillRoundRect", new Class[] {int.class, int.class, int.class, int.class, int.class, int.class}, new Object[] {new Integer(x), new Integer(y), new Integer(width), new Integer(height), new Integer(arcWidth), new Integer(arcHeight)}); } // draw3DRect() is implemented in the Graphics superclass but is // overridden in Graphics2D and so must be implemented here. public void draw3DRect(int x, int y, int width, int height, boolean raised) { doGraphicsOp(x, y, width, height, "draw3DRect", new Class[] {int.class, int.class, int.class, int.class, boolean.class}, new Object[] {new Integer(x), new Integer(y), new Integer(width), new Integer(height), new Boolean(raised)}); } // fill3DRect() is implemented in the Graphics superclass but is // overridden in Graphics2D and so must be implemented here. public void fill3DRect(int x, int y, int width, int height, boolean raised) { doGraphicsOp(x, y, width, height, "fill3DRect", new Class[] {int.class, int.class, int.class, int.class, boolean.class}, new Object[] {new Integer(x), new Integer(y), new Integer(width), new Integer(height), new Boolean(raised)}); } public void drawOval(int x, int y, int width, int height) { doGraphicsOp(x, y, width, height, "drawOval", new Class[] {int.class, int.class, int.class, int.class}, new Object[] {new Integer(x), new Integer(y), new Integer(width), new Integer(height)}); } public void fillOval(int x, int y, int width, int height) { doGraphicsOp(x, y, width, height, "fillOval", new Class[] {int.class, int.class, int.class, int.class}, new Object[] {new Integer(x), new Integer(y), new Integer(width), new Integer(height)}); } public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) { doGraphicsOp(x, y, width, height, "drawArc", new Class[] {int.class, int.class, int.class, int.class, int.class, int.class}, new Object[] {new Integer(x), new Integer(y), new Integer(width), new Integer(height), new Integer(startAngle), new Integer(arcAngle)}); } public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) { doGraphicsOp(x, y, width, height, "fillArc", new Class[] {int.class, int.class, int.class, int.class, int.class, int.class}, new Object[] {new Integer(x), new Integer(y), new Integer(width), new Integer(height), new Integer(startAngle), new Integer(arcAngle)}); } public void drawPolyline(int xPoints[], int yPoints[], int nPoints) { Class intArrayClass = xPoints.getClass(); Rectangle box = getBoundingBox(xPoints, yPoints, nPoints); if(box == null) return; doGraphicsOp(box, "drawPolyline", new Class[] {intArrayClass, intArrayClass, int.class}, new Object[] {xPoints, yPoints, new Integer(nPoints)}); } public void drawPolygon(int xPoints[], int yPoints[], int nPoints) { Class intArrayClass = xPoints.getClass(); Rectangle box = getBoundingBox(xPoints, yPoints, nPoints); if(box == null) return; doGraphicsOp(box, "drawPolygon", new Class[] {intArrayClass, intArrayClass, int.class}, new Object[] {xPoints, yPoints, new Integer(nPoints)}); } // public void drawPolygon(Polygon) -- implemented in Graphics superclass public void fillPolygon(int xPoints[], int yPoints[], int nPoints) { Class intArrayClass = xPoints.getClass(); Rectangle box = getBoundingBox(xPoints, yPoints, nPoints); if(box == null) return; doGraphicsOp(box, "fillPolygon", new Class[] {intArrayClass, intArrayClass, int.class}, new Object[] {xPoints, yPoints, new Integer(nPoints)}); } // public void fillPolygon(Polygon) -- implemented in Graphics superclass public void drawString(String str, int x, int y) { Rectangle2D r2d = getFontMetrics(getFont() ).getStringBounds(str, this); r2d.setRect(x, y - r2d.getHeight() + 1, r2d.getWidth(), r2d.getHeight()); doGraphicsOp(r2d, "drawString", new Class[] {java.lang.String.class, int.class, int.class}, new Object[] {str, new Integer(x), new Integer(y)}); } // public void drawChars -- implemented in Graphics superclass // public void drawBytes -- implemented in Graphics superclass public boolean drawImage(Image img, int x, int y, ImageObserver observer) { return doGraphicsOp(x, y, img.getWidth(observer), img.getHeight(observer), "drawImage", new Class[] {java.awt.Image.class, int.class, int.class, java.awt.image.ImageObserver.class}, new Object[] {img, new Integer(x), new Integer(y), observer}); } public void drawRenderedImage(RenderedImage im, AffineTransform transform) { Rectangle2D.Double srcRect = new Rectangle2D.Double(im.getMinX(), im.getMinY(), im.getWidth(), im.getHeight()); Rectangle2D dstRect = transform.createTransformedShape(srcRect).getBounds2D(); doGraphicsOp(dstRect, "drawRenderedImage", new Class[] {java.awt.image.RenderedImage.class, java.awt.geom.AffineTransform.class}, new Object[] {im, transform}); } public void drawRenderableImage(RenderableImage img, AffineTransform xform) { Rectangle2D.Double srcRect = new Rectangle2D.Double(img.getMinX(), img.getMinY(), img.getWidth(), img.getHeight()); Rectangle2D dstRect = xform.createTransformedShape(srcRect).getBounds2D(); doGraphicsOp(dstRect, "drawRenderableImage", new Class[] {java.awt.image.renderable.RenderableImage.class, java.awt.geom.AffineTransform.class}, new Object[] {img, xform}); } public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) { return doGraphicsOp(x, y, width, height, "drawImage", new Class[] {java.awt.Image.class, int.class, int.class, int.class, int.class, java.awt.image.ImageObserver.class}, new Object[] {img, new Integer(x), new Integer(y), new Integer(width), new Integer(height), observer}); } public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) { return doGraphicsOp(x, y, img.getWidth(observer), img.getHeight(observer), "drawImage", new Class[] {java.awt.Image.class, int.class, int.class, java.awt.Color.class, java.awt.image.ImageObserver.class}, new Object[] {img, new Integer(x), new Integer(y), bgcolor, observer}); } public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) { return doGraphicsOp(x, y, width, height, "drawImage", new Class[] {java.awt.Image.class, int.class, int.class, int.class, int.class, java.awt.Color.class, java.awt.image.ImageObserver.class}, new Object[] {img, new Integer(x), new Integer(y), new Integer(width), new Integer(height), bgcolor, observer}); } public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) { return doGraphicsOp(dx1, dy1, dx2-dx1+1, dy2-dy1+1, "drawImage", new Class[] {java.awt.Image.class, int.class, int.class, int.class, int.class, int.class, int.class, int.class, int.class, java.awt.image.ImageObserver.class}, new Object[] {img, new Integer(dx1), new Integer(dy1), new Integer(dx2), new Integer(dy2), new Integer(sx1), new Integer(sy1), new Integer(sx2), new Integer(sy2), observer}); } public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer) { return doGraphicsOp(dx1, dy1, dx2-dx1+1, dy2-dy1+1, "drawImage", new Class[] {java.awt.Image.class, int.class, int.class, int.class, int.class, int.class, int.class, int.class, int.class, java.awt.Color.class, java.awt.image.ImageObserver.class}, new Object[] {img, new Integer(dx1), new Integer(dy1), new Integer(dx2), new Integer(dy2), new Integer(sx1), new Integer(sy1), new Integer(sx2), new Integer(sy2), bgcolor, observer}); } public void dispose() { // No resources need to be released. } // public void finalize -- implemented in Graphics superclass // public String toString -- implemented in Graphics superclass // ---------- Methods from java.awt.Graphics2D ---------- public void addRenderingHints(Map hints) { renderingHints.putAll(hints); } public void draw(Shape s) { doGraphicsOp(s.getBounds(), "draw", new Class[] {java.awt.Shape.class}, new Object[] {s}); } public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) { Rectangle2D.Double srcRect = new Rectangle2D.Double(0, 0, img.getWidth(obs), img.getHeight(obs)); Rectangle2D dstRect = transform.createTransformedShape(srcRect).getBounds2D(); return doGraphicsOp(dstRect, "drawImage", new Class[] {java.awt.Image.class, java.awt.geom.AffineTransform.class, java.awt.image.ImageObserver.class}, new Object[] {img, xform, obs}); } public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) { doGraphicsOp(op.getBounds2D(img), "drawImage", new Class[] {java.awt.image.BufferedImage.class, java.awt.image.BufferedImageOp.class, int.class, int.class}, new Object[] {img, op, new Integer(x), new Integer(y)}); } public void drawString(String s, float x, float y) { Rectangle2D r2d = getFontMetrics(getFont() ).getStringBounds(s, this); r2d.setRect(x, y - r2d.getHeight() + 1, r2d.getWidth(), r2d.getHeight()); doGraphicsOp(r2d, "drawString", new Class[] {java.lang.String.class, float.class, float.class}, new Object[] {s, new Float(x), new Float(y)}); } public void drawString(AttributedCharacterIterator iterator, int x, int y) { Rectangle2D r2d = getFontMetrics(getFont() ).getStringBounds(iterator, iterator.getBeginIndex(), iterator.getEndIndex(), this); r2d.setRect(x, y - r2d.getHeight() + 1, r2d.getWidth(), r2d.getHeight()); doGraphicsOp(r2d, "drawString", new Class[] {java.text.AttributedCharacterIterator.class, int.class, int.class}, new Object[] {iterator, new Integer(x), new Integer(y)}); } public void drawString(AttributedCharacterIterator iterator, float x, float y) { Rectangle2D r2d = getFontMetrics(getFont() ).getStringBounds(iterator, iterator.getBeginIndex(), iterator.getEndIndex(), this); r2d.setRect(x, y - r2d.getHeight() + 1, r2d.getWidth(), r2d.getHeight()); doGraphicsOp(r2d, "drawString", new Class[] {java.text.AttributedCharacterIterator.class, float.class, float.class}, new Object[] {iterator, new Float(x), new Float(y)}); } public void drawGlyphVector(GlyphVector g, float x, float y) { doGraphicsOp(g.getVisualBounds(), "drawGlyphVector", new Class[] {java.awt.font.GlyphVector.class, float.class, float.class}, new Object[] {g, new Float(x), new Float(y)}); } public void fill(Shape s) { doGraphicsOp(s.getBounds(), "fill", new Class[] {java.awt.Shape.class}, new Object[] {s}); } public boolean hit(Rectangle rect, Shape s, boolean onStroke) { Graphics2D g2d = getBogusGraphics2D(true); boolean hitTarget = g2d.hit(rect, s, onStroke); g2d.dispose(); return hitTarget; } public GraphicsConfiguration getDeviceConfiguration() { Graphics2D g2d = getBogusGraphics2D(true); GraphicsConfiguration gConf = g2d.getDeviceConfiguration(); g2d.dispose(); return gConf; } public void setComposite(Composite comp) { composite = comp; } public void setPaint(Paint paint) { this.paint = paint; } public void setStroke(Stroke s) { stroke = s; } public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue) { renderingHints.put(hintKey, hintValue); } public Object getRenderingHint(RenderingHints.Key hintKey) { return renderingHints.get(hintKey); } public void setRenderingHints(Map hints) { renderingHints.putAll(hints); } public RenderingHints getRenderingHints() { return renderingHints; } public void translate(int x, int y) { origin = new Point(x, y); transform.translate((double)x, (double)y); } public void translate(double x, double y) { transform.translate(x, y); } public void rotate(double theta) { transform.rotate(theta); } public void rotate(double theta, double x, double y) { transform.rotate(theta, x, y); } public void scale(double sx, double sy) { transform.scale(sx, sy); } public void shear(double shx, double shy) { transform.shear(shx, shy); } public void transform(AffineTransform Tx) { transform.concatenate(Tx); } public void setTransform(AffineTransform Tx) { transform = Tx; } public AffineTransform getTransform() { return transform; } public Paint getPaint() { return paint; } public Composite getComposite() { return composite; } public void setBackground(Color color) { background = color; } public Color getBackground() { return background; } public Stroke getStroke() { return stroke; } public void clip(Shape s) { if(clip == null) { clip = s; } else { Area clipArea = (clip instanceof Area ? (Area)clip : new Area(clip)); clipArea.intersect(s instanceof Area ? (Area)s : new Area(s)); clip = clipArea; } } public FontRenderContext getFontRenderContext() { Graphics2D g2d = getBogusGraphics2D(true); FontRenderContext fontRenderContext = g2d.getFontRenderContext(); g2d.dispose(); return fontRenderContext; } void sendExceptionToListener(String message, Exception e) { ImagingListener listener = null; if (renderingHints != null) listener = (ImagingListener)renderingHints.get(JAI.KEY_IMAGING_LISTENER); if (listener == null) listener = JAI.getDefaultInstance().getImagingListener(); listener.errorOccurred(message, e, this, false); } } jai-core-1.1.4/src/share/classes/javax/media/jai/OpImage.java0000644000175000017500000024761110203035544023534 0ustar mathieumathieu/* * $RCSfile: OpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:12 $ * $State: Exp $ */ package javax.media.jai; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.util.JDKWorkarounds; import java.awt.Dimension; import java.awt.Point; import java.awt.Rectangle; import java.awt.geom.Point2D; import java.awt.image.ColorModel; // 3-22-00 used in deprecated methods only import java.awt.image.IndexColorModel; // 3-22-00 used in deprecated mthds only import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; // 3-22-00 used in deprecated methods only import java.awt.image.WritableRaster; import java.util.ArrayList; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.Vector; import javax.media.jai.util.CaselessStringKey; /** * This is the base class for all image operations. It provides a home * for information and functionalities common to all the op-image classes, * and implements various utility methods that may be useful to a specific * operation. Image operations may be divided into different categories * based on their characteristics. A subclass, extending * OpImage, represents a category and implements methods * unique and common to those operations. Each individual operator * should extend the subclass that represents the specific * category that operator belongs to. * *

    The layout variables of an OpImage are inherited from the * PlanarImage superclass. The layout should be set when the * OpImage is constructed. Each subclass must set * the appropriate layout variables and supply them via the * ImageLayout argument at construction time. This class * simply modifies these settings as described in the OpImage * constructor comments before forwarding the layout to the * PlanarImage constructor. If a subclass needs to modify * any of the layout settings subsequent to invoking its superclass * constructors it should use the setImageLayout() method * defined in PlanarImage in preference to setting the * layout variables directly. * *

    A RenderedImage's pixel data type and number of bands * are defined by its SampleModel, while the * ColorModel translates the pixel data into color/alpha * components in the specific ColorSpace that is associated * with the ColorModel. * *

    By default, the operators provided by Java Advanced Imaging (JAI) * operate on the image's pixel data only. That is, the computations * are performed on the data described by the image's * SampleModel. No color translation is performed prior * to the actual computation by the operator, regardless of the type of * the ColorModel an image has. If a user intends to have * an operation performed on the color data, he must perform the * color translation explicitly prior to invoking the operation. * *

    There are those operators that specifically deal with the * color/alpha data of an image. Such an operator must state its * behavior in its OperationDescriptor explicitly and * explain its intended usage of the image's color/alpha component data. * In such cases, the image's ColorModel as well as the * associated ColorSpace should be considered. * *

    However there are certain operations, the results of which are * incorrect when the source has colormapped imagery, i.e. the source * has an IndexColorModel, and the computations are * performed on the image's non color transformed pixel data. In JAI, * such operations are those that are implemented as subclasses of * {@link AreaOpImage}, {@link GeometricOpImage}, and the "format" * operation. These operations set the * {@link JAI#KEY_REPLACE_INDEX_COLOR_MODEL} RenderingHint * to true, thus ensuring that the operations are performed correctly * on the colormapped imagery, not treating the indices into the color * map as pixel data. * *

    The tile cache and scheduler are handled by this class. JAI * provides a default implementation for TileCache and * TileScheduler. However, they may be overriden by * each application. An OpImage may share a common cache * with other OpImages, or it may have a private cache of * its own. To override an existing cache, use the * setTileCache method; an input argument of null * indicates that this image should not have a tile cache. * *

    The getTile method may be used to request a tile * of the image. The default implementation of this method in this * class first checks whether the requested tile is in the tile cache, * and if not, uses the default TileScheduler to schedule * the tile for computation. Once the tile has been computed, it is * added to the cache and returned as a Raster. * *

    The JAI tile scheduler assumes that when a request is made to * schedule a tile for computation via the scheduleTile * method, that tile is not currently in the cache. To avoid a cycle, * it calls OpImage.computeTile for the actual tile * computation. * *

    The default implementation of the computeTile method * in this class first creates a new Raster to represent * the requested tile, then calls one of the two computeRect * methods to compute the actual pixel values and store the result in * the DataBuffer of the Raster. * *

    Two variants of the computeRect method exist. * *

    The first (with input arguments Raster[], * WritableRaster, and Rectangle) is used when * the OpImage is constructed with the * cobbleSources argument set to true. * This indicates that the source data must be cobbled into a single * Raster and that all the necessary source data are provided * in order to compute the rectangular region of the destination image. * The source Raster array contains one entry for each * source image. * *

    The second (with input arguments PlanarImage[], * WritableRaster, and Rectangle) is used when * the OpImage is constructed with the * cobbleSources argument set to false. * This indicates that the source data are not cobbled into a single * Raster; instead an array of PlanarImages, * one for each source, supply the source data and each image is * responsible for performing its own data accesses. This variant is * generally useful if iterators are to be used for the underlying * implementation of accessing the image data. * *

    The two computeRect methods are not abstract because * normally only one needs to be implemented by the subclass depending on * the cobbleSources value. The default implementation of * these two methods in this class throws a RuntimeException. * *

    Every operator who follows the above default implementation must * supply an overridden version of at least one of the * computeRect method variants, and specify which one is * to be called via the cobbleSources argument of the * constructor, or an exception will be thrown at run time. * *

    If a subclass overrides getTile not to call * computeTile, does not use the JAI implementation of * TileScheduler, overrides computeTile not to * call computeRect, or does not follow the above default * implementation in any way, then it may need to handle issues such as * tile caching, multi-threading, and etc. by itself and may not need to * override some of the methods described above. In some cases, some of * the methods or variables are even irrelevant. However, subclasses * should be careful when not following the default path for computing * a tile. Most importantly, when a subclass overrides * getTile, it should also override computeTile. * *

    To request multiple tiles at a time, it is preferable to * call the getTiles method with a complete list of the * requested tiles' indices, than to call getTile once * per tile. The implementation of getTiles in this class * is optimized using multi-threading so that multiple tiles are * computed simultaneously. * * @see PlanarImage * @see AreaOpImage * @see GeometricOpImage * @see PointOpImage * @see StatisticsOpImage * @see SourcelessOpImage * */ public abstract class OpImage extends PlanarImage { /** * A constant indicating that an operation is likely to * spend its time mainly performing computation. */ public static final int OP_COMPUTE_BOUND = 1; /** * A constant indicating that an operation is likely to * spend its time mainly performing local I/O. */ public static final int OP_IO_BOUND = 2; /** * A constant indicating that an operation is likely to * spend its time mainly performing network I/O. */ public static final int OP_NETWORK_BOUND = 3; /** * A constant equal to what would be returned by * ImageLayout.getValidMask() if all fields were set. */ private static final int LAYOUT_MASK_ALL = ImageLayout.MIN_X_MASK | ImageLayout.MIN_Y_MASK | ImageLayout.WIDTH_MASK | ImageLayout.HEIGHT_MASK | ImageLayout.TILE_GRID_X_OFFSET_MASK | ImageLayout.TILE_GRID_Y_OFFSET_MASK | ImageLayout.TILE_WIDTH_MASK | ImageLayout.TILE_HEIGHT_MASK | ImageLayout.SAMPLE_MODEL_MASK | ImageLayout.COLOR_MODEL_MASK; /** * The cache object used to cache this image's tiles. It may refer * to a common cache shared by many OpImages or a private * cache for this image only. If it is null, it * indicates that this image does not have a tile cache. */ protected transient TileCache cache; /** * Metric used to produce an ordered list of tiles. This determines * which tiles are removed from the cache first if a memory control * operation is required. * * @since JAI 1.1 */ protected Object tileCacheMetric; /** * The scheduler to be used to schedule tile computation. */ private transient TileScheduler scheduler = JAI.getDefaultInstance().getTileScheduler(); /** * Variable indicating whether the TileScheduler is the Sun implementation. */ private boolean isSunTileScheduler = false; /** * Indicates which one of the two computeRect variants * should be called by the computeTile method. If it * is true, computeRect expects * contiguous sources. */ protected boolean cobbleSources; /** * Whether dispose() has been invoked. */ private boolean isDisposed = false; /** * Flag indicating that tile recycling is enabled for tiles which * may be referenced outside the API. */ private boolean isCachedTileRecyclingEnabled = false; /** * A TileRecycler for use in createTile(). * May be null. This field is set by the configuration * map passed to {@link #OpImage(Vector,ImageLayout,Map,boolean}. * * @since JAI 1.1.2 */ protected TileRecycler tileRecycler; /** The default RasterAccessor format tags. */ // XXX This variable should be removed if we stop using RasterAccessor. private RasterFormatTag[] formatTags = null; /** * Creates a new ImageLayout or forwards the argument * layout with or without modifications. * *

    If the layout parameter is non-null * and all its fields are set then it is cloned and returned. * *

    If the layout parameter is non-null * but some of its fields are not set and there is at least one source * available, then all fields of the layout which are not set are * copied from the corresponding attributes of the first source except * possibly the ColorModel. The ColorModel * is copied if and only if the destination SampleModel is * non-null and the ColorModel and * SampleModel are compatible. * * The image's tile dimensions will be set by the first applicable * means in the following priority-ordered list. Note that each tile * dimension, the tileWidth and the * tileHeight, is considered independently : *

      *
    1. Tile dimension set in the ImageLayout (either by * the user or the operator itself);
    2. *
    3. Tile dimension of source, if source is non-null. * The tile dimension will be clamped to the minimum of that of the * source tile dimension and the image's corresponding dimension;
    4. *
    5. Non-null default tile size returned by * JAI.getDefaultTileSize(), if the corresponding * image dimension is at least double the default tile size;
    6. *
    7. The dimensions of the image itself;
    8. *
    * *

    If the layout parameter is null and * there is at least one source available, a new layout is created from * the first source and returned directly. */ private static ImageLayout layoutHelper(ImageLayout layout, Vector sources, Map config) { // Initialize to a reference to the layout passed in. ImageLayout il = layout; // Check the source Vector elements for nulls. if(sources != null) { checkSourceVector(sources, true); } // Check the class of the first source to avoid an exception here; a // class cast exception will be thrown by the PlanarImage constructor. RenderedImage im = sources != null && sources.size() > 0 && sources.firstElement() instanceof RenderedImage ? (RenderedImage)sources.firstElement() : null; // Update some or all of the layout if a source is available. if(im != null) { // Create a new layout with the source as fallback. // The ColorModel field is intentionally NOT set. if(layout == null) { // Copy entirety of source image layout. il = layout = new ImageLayout(im); // Invalidate the ColorModel setting. il.unsetValid(ImageLayout.COLOR_MODEL_MASK); } else { // Set all fields except ColorModel. il = new ImageLayout(layout.getMinX(im), layout.getMinY(im), layout.getWidth(im), layout.getHeight(im), layout.getTileGridXOffset(im), layout.getTileGridYOffset(im), layout.getTileWidth(im), layout.getTileHeight(im), layout.getSampleModel(im), null); } // At this point "layout" and "il" are non-null with "layout" // representing the ImageLayout originally passed in. "il" does // not yet have its ColorModel field set. // Set the ColorModel. if(layout.isValid(ImageLayout.COLOR_MODEL_MASK) && layout.getColorModel(null) == null) { // User wants to force a null ColorModel. il.setColorModel(null); } else if(il.getSampleModel(null) != null) { // Target SampleModel is available. // Get SampleModel from "il"; guaranteed to be non-null. SampleModel sm = il.getSampleModel(null); // Get ColorModel from "layout". ColorModel cmLayout = layout.getColorModel(null); // First attempt to set the ColorModel to that specified // in the original layout, if any. if(cmLayout != null) { // ColorModel is set in original layout. if(JDKWorkarounds.areCompatibleDataModels(sm, cmLayout)) { // ColorModel is compatible with target SampleModel. il.setColorModel(cmLayout); } else if(layout.getSampleModel(null) == null) { // SampleModel not set in original layout so // ColorModel must be incompatible with source // SampleModel: create a compatible SampleModel. // Set the ColorModel to that desired. il.setColorModel(cmLayout); // Derive a new SampleModel from the desired ColorModel SampleModel derivedSM = cmLayout.createCompatibleSampleModel( il.getTileWidth(null), il.getTileHeight(null)); // Set the SampleModel to that derived from the CM. il.setSampleModel(derivedSM); } } // If ColorModel not set from ImageLayout, attempt to set // using a ColorModelFactory and if that fails, attempt to // use the ColorModel of the source. if(!il.isValid(ImageLayout.COLOR_MODEL_MASK) && !setColorModelFromFactory(sm, sources, config, il)) { // Get ColorModel from "im", i.e., the source. ColorModel cmSource = im.getColorModel(); if(cmSource != null && JDKWorkarounds.areCompatibleDataModels(sm, cmSource)) { // Set to source ColorModel. if (cmSource != null && cmSource instanceof IndexColorModel && config != null && config.containsKey( JAI.KEY_REPLACE_INDEX_COLOR_MODEL) && ((Boolean)config.get(JAI.KEY_REPLACE_INDEX_COLOR_MODEL)).booleanValue()) { ColorModel newCM = PlanarImage.getDefaultColorModel( sm.getDataType(), cmSource.getNumComponents()); SampleModel newSM; if (newCM != null) { newSM = newCM.createCompatibleSampleModel( il.getTileWidth(null), il.getTileHeight(null)); } else { newSM = RasterFactory.createPixelInterleavedSampleModel( sm.getDataType(), il.getTileWidth(null), il.getTileHeight(null), cmSource.getNumComponents()); } il.setSampleModel(newSM); if (newCM != null) il.setColorModel(newCM); } else { il.setColorModel(cmSource); } } } } else if(il.getSampleModel(null) == null) { // null SampleModel // Set to whatever is available. il.setColorModel(layout.getColorModel(im)); } // end of block if(im != null) } else if(il != null) { // Can only get here if im == null && layout != null. // Make sure that il is a clone of layout. il = (ImageLayout)layout.clone(); // If the ColorModel is set but the SampleModel is not, // derive a SampleModel from the ColorModel. if(il.getColorModel(null) != null && il.getSampleModel(null) == null) { // Set SampleModel dimensions. int smWidth = il.getTileWidth(null); if(smWidth == 0) { smWidth = 512; } int smHeight = il.getTileHeight(null); if(smHeight == 0) { smHeight = 512; } // Derive a new SampleModel from the desired ColorModel SampleModel derivedSM = il.getColorModel(null).createCompatibleSampleModel(smWidth, smHeight); // Set the SampleModel to that derived from the CM. il.setSampleModel(derivedSM); } } // end of block if(il != null) // If no ColorModel is set, then first attempt to set a ColorModel // using the ColorModelFactory; otherwise set a default ColorModel // if either the configuration mapping is null, it does not contain // a mapping of the key KEY_DEFAULT_COLOR_MODEL_ENABLED, or this key // is mapped to Boolean.TRUE. if(il != null && !il.isValid(ImageLayout.COLOR_MODEL_MASK) && il.getSampleModel(null) != null && !setColorModelFromFactory(il.getSampleModel(null), sources, config, il)) { ColorModel cm = null; SampleModel srcSM = il.getSampleModel(null); // If it is not a byte image, then probably all the above did not // manage to set a ColorModel and ImageUtil.getCompatibleColorModel // will be used to get the ColorModel. However we want to ensure // that the destination ColorModel is expanded if the ColorModel // of the source is an IndexColorModel and we've been asked to // do IndexColorModel expansion if (im != null && im.getColorModel() != null && im.getColorModel() instanceof IndexColorModel && config != null && config.containsKey(JAI.KEY_REPLACE_INDEX_COLOR_MODEL) && ((Boolean)config.get(JAI.KEY_REPLACE_INDEX_COLOR_MODEL)).booleanValue()) { IndexColorModel icm = (IndexColorModel)im.getColorModel(); // We need to change the ColorModel to a non IndexColorModel // so that operations such as geometric can take place // correctly. If the ColorModel is changed the SampleModel // needs to be changed too, so that they are compatible cm = PlanarImage.getDefaultColorModel(srcSM.getDataType(), icm.getNumComponents()); SampleModel newSM; if (cm != null) { newSM = cm.createCompatibleSampleModel(srcSM.getWidth(), srcSM.getHeight()); } else { newSM = RasterFactory.createPixelInterleavedSampleModel( srcSM.getDataType(), srcSM.getWidth(), srcSM.getHeight(), icm.getNumComponents()); } il.setSampleModel(newSM); } else { cm = ImageUtil.getCompatibleColorModel(il.getSampleModel(null), config); } // Set ColorModel only if method succeeded. if(cm != null) il.setColorModel(cm); } // If the tile dimensions were not set in "layout" and are either // not set in "il" or would yield an untiled image then reset them to // the global tile size default if each image dimension is at least // double the respective default tile dimension. if(layout != null && il != null && !layout.isValid(ImageLayout.TILE_WIDTH_MASK| ImageLayout.TILE_HEIGHT_MASK)) { Dimension defaultTileSize = JAI.getDefaultTileSize(); if(defaultTileSize != null) { if(!layout.isValid(ImageLayout.TILE_WIDTH_MASK)) { if(il.getTileWidth(null) <= 0) { il.setTileWidth(defaultTileSize.width); } else { // Calculate number of tiles along X. int numX = XToTileX(il.getMinX(null) + il.getWidth(null) - 1, il.getTileGridXOffset(null), il.getTileWidth(null)) - XToTileX(il.getMinX(null), il.getTileGridXOffset(null), il.getTileWidth(null)) + 1; // Reset if single-tiled and image is big enough in X. if(numX <= 1 && il.getWidth(null) >= 2*defaultTileSize.width) { il.setTileWidth(defaultTileSize.width); } } } if(!layout.isValid(ImageLayout.TILE_HEIGHT_MASK)) { if(il.getTileHeight(null) <= 0) { il.setTileHeight(defaultTileSize.height); } else { // Calculate number of tiles along Y. int numY = YToTileY(il.getMinY(null) + il.getHeight(null) - 1, il.getTileGridYOffset(null), il.getTileHeight(null)) - YToTileY(il.getMinY(null), il.getTileGridYOffset(null), il.getTileHeight(null)) + 1; // Reset if single-tiled and image is big enough in Y. if(numY <= 1 && il.getHeight(null) >= 2*defaultTileSize.height) { il.setTileHeight(defaultTileSize.height); } } } } } // Independently clamp each tile dimension to the respective image // dimension, if the tile dimensions are not set in any supplied // ImageLayout, and the tile and image dimensions are both set in // the ImageLayout to be returned. // Tile width if ((layout == null || !layout.isValid(ImageLayout.TILE_WIDTH_MASK)) && il.isValid(ImageLayout.TILE_WIDTH_MASK | ImageLayout.WIDTH_MASK) && il.getTileWidth(null) > il.getWidth(null)) { il.setTileWidth(il.getWidth(null)); } // Tile height if ((layout == null || !layout.isValid(ImageLayout.TILE_HEIGHT_MASK)) && il.isValid(ImageLayout.TILE_HEIGHT_MASK | ImageLayout.HEIGHT_MASK) && il.getTileHeight(null) > il.getHeight(null)) { il.setTileHeight(il.getHeight(null)); } return il; } /** * Set the ColorModel in layout if * config is non-null and contains a mapping * for JAI.KEY_COLOR_MODEL_FACTORY. If the * ColorModelFactory returns a non-null * ColorModel which is compatible with * sampleModel it is used to set the ColorModel * in layout. * * @param sampleModel The SampleModel to which the * ColorModel to be created must correspond; * may not be null. * @param sources A List of RenderedImages; * may be null. * @param config A configuration mapping; may be * null. * @param layout The image layout; will be updated with the * ColorModelcreated by the * ColorModelFactory * * @return Whether the ColorModel in layout * has been set. * @exception IllegalArgumentException if sampleModel * is null. */ private static boolean setColorModelFromFactory(SampleModel sampleModel, Vector sources, Map config, ImageLayout layout) { boolean isColorModelSet = false; if(config != null && config.containsKey(JAI.KEY_COLOR_MODEL_FACTORY)) { ColorModelFactory cmf = (ColorModelFactory)config.get(JAI.KEY_COLOR_MODEL_FACTORY); ColorModel cm = cmf.createColorModel(sampleModel, sources, config); if(cm != null && JDKWorkarounds.areCompatibleDataModels(sampleModel, cm)) { layout.setColorModel(cm); isColorModelSet = true; } } return isColorModelSet; } // XXX Note: ColorModel.isCompatibleRaster() is mentioned below but it // should be ColorModel.isCompatibleSampleModel(). This has a bug // however (4326636) which is worked around within JAI. The other method // is mentioned as it does NOT have a bug. /** * Constructor. * *

    The image's layout is encapsulated in the layout * argument. The variables of the image layout which are not set in * the layout parameter are copied from the first source * if sources are available. In the case of the ColorModel, * the copy is performed if and only if the ColorModel is * compatible with the destination SampleModel and is not * set by another higher priority mechanism as described presently.

    * *

    Assuming that there is at least one source, the image's * ColorModel will be set by the first applicable means * in the following priority-ordered list: *

      *
    1. null ColorModel from * ImageLayout;
    2. *
    3. Non-null ColorModel from * ImageLayout if compatible with * SampleModel in ImageLayout or if * SampleModel in ImageLayout is * null;
    4. *
    5. Value returned by ColorModelFactory set via the * JAI.KEY_COLOR_MODEL_FACTORY configuration variable if * compatible with SampleModel;
    6. *
    7. An instance of a non-IndexColorModel (or * null if no compatible non-IndexColorModel * could be generated), if the source has an IndexColorModel * and JAI.KEY_REPLACE_INDEX_COLOR_MODEL * is Boolean.TRUE;
    8. *
    9. ColorModel of first source if compatible with * SampleModel;
    10. *
    11. Value returned by default method specified by the * JAI.KEY_DEFAULT_COLOR_MODEL_METHOD configuration variable * if JAI.KEY_DEFAULT_COLOR_MODEL_ENABLED is * Boolean.TRUE.
    12. *
    * If it is not possible to set the ColorModel by any of * these means it will remain null.

    * * The image's tile dimensions will be set by the first applicable * means in the following priority-ordered list. Note that each tile * dimension, the tileWidth and the * tileHeight, is considered independently : *
      *
    1. Tile dimension set in the ImageLayout (either by * the user or the operator itself);
    2. *
    3. Tile dimension of source, if source is non-null. * The tile dimension will be clamped to the minimum of that of the * source tile dimension and the image's corresponding dimension;
    4. *
    5. Non-null default tile size returned by * JAI.getDefaultTileSize(), if the corresponding * image dimension is at least double the default tile size;
    6. *
    7. The dimensions of the image itself;
    8. *
    * *

    The sources contains a list of immediate sources * of this image. Elements in the list may not be null. * If this image has no sources this argument should be null. * This parameter is forwarded unmodified to the PlanarImage * constructor. * *

    The configuration contains a mapping of configuration * variables and image properties. Entries which have keys of type * RenderingHints.Key are taken to be configuration variables. * Entries with a key which is either a String or a * CaselessStringKey are interpreted as image properties. * This parameter is forwarded unmodified to the PlanarImage * constructor. * *

    This image class recognizes the configuration variables referenced * by the following keys: * *

      *
    • JAI.KEY_TILE_CACHE: specifies the * TileCache in which to store the image tiles; * if this key is not supplied no tile caching will be performed. *
    • JAI.KEY_TILE_CACHE_METRIC: establishes an * ordering of tiles stored in the tile cache. This ordering * is used to determine which tiles will be removed first, if * a condition causes tiles to be removed from the cache. *
    • JAI.KEY_TILE_SCHEDULER: specifies the * TileScheduler to use to schedule tile computation; * if this key is not supplied the default scheduler will be used. *
    • JAI.KEY_COLOR_MODEL_FACTORY: specifies a * ColorModelFactory to be used to generate the * ColorModel of the image. If such a callback is * provided it will be invoked if and only if either no * ImageLayout hint is given, or an ImageLayout * hint is given but contains a non-null * ColorModel which is incompatible with the image * SampleModel. In other words, such a callback provides * the second priority mechanism for setting the ColorModel * of the image.
    • *
    • JAI.KEY_DEFAULT_COLOR_MODEL_ENABLED: specifies whether * a default ColorModel will be derived * if none is specified and one cannot be inherited from the first source; * if this key is not supplied a default ColorModel will be * computed if necessary. *
    • JAI.KEY_DEFAULT_COLOR_MODEL_METHOD: specifies the * method to be used to compute the default ColorModel; * if this key is not supplied and a default ColorModel is * required, PlanarImage.createColorModel() will be used to * compute it. *
    • JAI.KEY_TILE_FACTORY: specifies a * {@link TileFactory} to be used to generate the tiles of the * image via {@link TileFactory#createTile(SampleModel,Point)}. If * no such configuration variable is given, a new Raster * will be created for each image tile. This behavior may be * overridden by subclasses which have alternate means of saving * memory, for example as in the case of point operations which * may overwrite a source image not referenced by user code. Note * that the corresponding instance variable is actually set by * the superclass constructor.
    • *
    • JAI.KEY_TILE_RECYCLER: specifies a * {@link TileRecycler} to be used to recycle the tiles of the * image when the dispose() method is invoked. If * such a configuration variable is set, the image has a * non-null TileCache, and tile recycling * is enabled, then invoking dispose() will cause each * of the tiles of this image currently in the cache to be passed to * the configured TileRecycler
    • via * {@link TileRecycler#recycleTile(Raster)}. *
    • JAI.KEY_CACHED_TILE_RECYCLING_ENABLED: specifies a * Boolean value which indicates whether {#dispose()} * should pass to tileRecycler.recycleTile() any image * tiles remaining in the cache.
    • *
    * *

    The cobbleSources indicates which one of the two * variants of the computeRect method should be called. * If a subclass does not follow the default tile computation scheme, * then this argument may be irrelevant. * * @param layout The layout of this image. * @param sources The immediate sources of this image. * @param configuration Configurable attributes of the image including * configuration variables indexed by * RenderingHints.Keys and image properties indexed * by Strings or CaselessStringKeys. * This parameter may be null. * @param cobbleSources Indicates which variant of the * computeRect method should be called. * * @throws IllegalArgumentException If sources * is non-null and any object in * sources is null. * @throws RuntimeException If default ColorModel setting * is enabled via a hint in the configuration Map * and the supplied Method does not conform to the * requirements stated in the JAI class for the * hint key KEY_DEFAULT_COLOR_MODEL_METHOD. * * @since JAI 1.1 */ public OpImage(Vector sources, ImageLayout layout, Map configuration, boolean cobbleSources) { super(layoutHelper(layout, sources, configuration), sources, configuration); if(configuration != null) { // Get the cache from the configuration map. Object cacheConfig = configuration.get(JAI.KEY_TILE_CACHE); // Ensure that it is a TileCache instance with positive capacity. if(cacheConfig != null && cacheConfig instanceof TileCache && ((TileCache)cacheConfig).getMemoryCapacity() > 0) { cache = (TileCache)cacheConfig; } // Get the scheduler from the configuration map. Object schedulerConfig = configuration.get(JAI.KEY_TILE_SCHEDULER); // Ensure that it is a TileScheduler instance. if(schedulerConfig != null && schedulerConfig instanceof TileScheduler) { scheduler = (TileScheduler)schedulerConfig; } try { // Test whether the TileScheduler is the default type. Class sunScheduler = Class.forName("com.sun.media.jai.util.SunTileScheduler"); isSunTileScheduler = sunScheduler.isInstance(scheduler); } catch(Exception e) { // Deliberately ignore any Exceptions. } // Get the tile metric (cost or priority, for example) tileCacheMetric = configuration.get(JAI.KEY_TILE_CACHE_METRIC); // Set up cached tile recycling flag. Object recyclingEnabledValue = configuration.get(JAI.KEY_CACHED_TILE_RECYCLING_ENABLED); if(recyclingEnabledValue instanceof Boolean) { isCachedTileRecyclingEnabled = ((Boolean)recyclingEnabledValue).booleanValue(); } // Set up the TileRecycler. Object recyclerValue = configuration.get(JAI.KEY_TILE_RECYCLER); if(recyclerValue instanceof TileRecycler) { tileRecycler = (TileRecycler)recyclerValue; } } this.cobbleSources = cobbleSources; } /** * A TileComputationListener to pass to the * TileScheduler to intercept method calls such that the * computed tiles are added to the TileCache of the image. */ private class TCL implements TileComputationListener { OpImage opImage; private TCL(OpImage opImage) { this.opImage = opImage; } public void tileComputed(Object eventSource, TileRequest[] requests, PlanarImage image, int tileX, int tileY, Raster tile) { if(image == opImage) { // Cache the tile. addTileToCache(tileX, tileY, tile); } } public void tileCancelled(Object eventSource, TileRequest[] requests, PlanarImage image, int tileX, int tileY) { // Do nothing. } public void tileComputationFailure(Object eventSource, TileRequest[] requests, PlanarImage image, int tileX, int tileY, Throwable situation) { // Do nothing. } } /** * Stores a RenderedImage in a Vector. * * @param image The image to be stored in the Vector. * * @return A Vector containing the image. * * @throws IllegalArgumentException if image is * null. * * @since JAI 1.1 */ protected static Vector vectorize(RenderedImage image) { if(image == null) { throw new IllegalArgumentException(JaiI18N.getString("OpImage3")); } Vector v = new Vector(1); v.addElement(image); return v; } /** * Stores two RenderedImages in a Vector. * * @param image1 The first image to be stored in the Vector. * @param image2 The second image to be stored in the Vector. * * @return A Vector containing the images. * * @throws IllegalArgumentException if image1 or * image2 is null. * * @since JAI 1.1 */ protected static Vector vectorize(RenderedImage image1, RenderedImage image2) { if(image1 == null || image2 == null) { throw new IllegalArgumentException(JaiI18N.getString("OpImage3")); } Vector v = new Vector(2); v.addElement(image1); v.addElement(image2); return v; } /** * Stores three RenderedImages in a Vector. * * @param image1 The first image to be stored in the Vector. * @param image2 The second image to be stored in the Vector. * @param image3 The third image to be stored in the Vector. * * @return A Vector containing the images. * * @throws IllegalArgumentException if image1 or * image2 or image3 is null. * * @since JAI 1.1 */ protected static Vector vectorize(RenderedImage image1, RenderedImage image2, RenderedImage image3) { if(image1 == null || image2 == null || image3 == null) { throw new IllegalArgumentException(JaiI18N.getString("OpImage3")); } Vector v = new Vector(3); v.addElement(image1); v.addElement(image2); v.addElement(image3); return v; } /** * Checks the source Vector. * *

    Checks whether the sources parameter is null * and optionally whether all elements are non-null. * * @param sources The source Vector. * @param checkElements Whether the elements are to be checked. * * @return The sources parameter unmodified. * * @throws IllegalArgumentException If sources * is null. * @throws IllegalArgumentException If checkElements * is true, sources * is non-null and any object in * sources is null. */ static Vector checkSourceVector(Vector sources, boolean checkElements) { // Check for null source Vector. if(sources == null) { throw new IllegalArgumentException(JaiI18N.getString("OpImage2")); } if(checkElements) { // Check Vector elements. int numSources = sources.size(); for(int i = 0; i < numSources; i++) { // Check for null element. if(sources.get(i) == null) { throw new IllegalArgumentException(JaiI18N.getString("OpImage3")); } } } return sources; } /** * Returns the tile cache object of this image by reference. * If this image does not have a tile cache, this method returns * null. * * @since JAI 1.1 */ public TileCache getTileCache() { return cache; } /** * Sets the tile cache object of this image. A null * input indicates that this image should have no tile cache and * subsequently computed tiles will not be cached. * *

    The existing cache object is informed to release all the * currently cached tiles of this image. * * @param cache A cache object to be used for caching this image's * tiles, or null if no tile caching is desired. */ public void setTileCache(TileCache cache) { if (this.cache != null) { this.cache.removeTiles(this); } this.cache = cache; } /** * Retrieves a tile from the tile cache. If this image does not * have a tile cache, or the requested tile is not currently in * the cache, this method returns null. * * @param tileX The X index of the tile. * @param tileY The Y index of the tile. * * @return The requested tile as a Raster or * null. */ protected Raster getTileFromCache(int tileX, int tileY) { return cache != null ? cache.getTile(this, tileX, tileY) : null; } /** * Adds a tile to the tile cache. If this image does not have * a tile cache, this method does nothing. * * @param tileX The X index of the tile. * @param tileY The Y index of the tile. * @param tile The tile to be added to the cache. */ protected void addTileToCache(int tileX, int tileY, Raster tile) { if (cache != null) { cache.add(this, tileX, tileY, tile, tileCacheMetric); } } /** * Returns the tileCacheMetric instance variable by reference. * * @since JAI 1.1 */ public Object getTileCacheMetric() { return tileCacheMetric; } /** * Returns a tile of this image as a Raster. If the * requested tile is completely outside of this image's bounds, * this method returns null. * *

    This method attempts to retrieve the requested tile from the * cache. If the tile is not currently in the cache, it schedules * the tile for computation and adds it to the cache once the tile * has been computed. * *

    If a subclass overrides this method, then it needs to handle * tile caching and scheduling. It should also override * computeTile() which may be invoked directly by the * TileScheduler. * * @param tileX The X index of the tile. * @param tileY The Y index of the tile. */ public Raster getTile(int tileX, int tileY) { Raster tile = null; // the requested tile, to be returned // Make sure the requested tile is inside this image's boundary. if (tileX >= getMinTileX() && tileX <= getMaxTileX() && tileY >= getMinTileY() && tileY <= getMaxTileY()) { // Check if tile is available in the cache. tile = getTileFromCache(tileX, tileY); if (tile == null) { // tile not in cache try { tile = scheduler.scheduleTile(this, tileX, tileY); } catch (OutOfMemoryError e) { // Empty the cache and call System.gc() if(cache != null) { cache.flush(); System.gc(); //slow } // Need to reissue the tile scheduling. tile = scheduler.scheduleTile(this, tileX, tileY); } // Cache the result tile. addTileToCache(tileX, tileY, tile); } } return tile; } /** * Computes the image data of a tile. * *

    When a tile is requested via the getTile method * and that tile is not in this image's tile cache, this method is * invoked by the TileScheduler to compute the data of * the new tile. Even though this method is marked public, * it should not be called by the applications directly. Rather, it * is meant to be called by the TileScheduler for the * actual computation. * *

    The implementation of this method in this class assumes that * the requested tile either intersects the image, or is within the * image's bounds. It creates a new Raster to * represent the requested tile, then calls one of the two variants * of computeRect to calculate the pixels of the * tile that are within the image's bounds. The value of * cobbleSources determines which variant of * computeRect is invoked, as described in the class * comments. * *

    Subclasses may provide a more optimized implementation of this * method. If they override this method not to call either variant of * computeRect, then neither variant of * computeRect needs to be implemented. * * @param tileX The X index of the tile. * @param tileY The Y index of the tile. */ public Raster computeTile(int tileX, int tileY) { // Create a new Raster. WritableRaster dest = createWritableRaster(sampleModel, new Point(tileXToX(tileX), tileYToY(tileY))); // Determine the active area; tile intersects with image's bounds. Rectangle destRect = getTileRect(tileX, tileY); int numSources = getNumSources(); if (cobbleSources) { Raster[] rasterSources = new Raster[numSources]; // Cobble areas for (int i = 0; i < numSources; i++) { PlanarImage source = getSource(i); Rectangle srcRect = mapDestRect(destRect, i); // If srcRect is empty, set the Raster for this source to // null; otherwise pass srcRect to getData(). If srcRect // is null, getData() will return a Raster containing the // data of the entire source image. rasterSources[i] = srcRect != null && srcRect.isEmpty() ? null : source.getData(srcRect); } computeRect(rasterSources, dest, destRect); for (int i = 0; i < numSources; i++) { Raster sourceData = rasterSources[i]; if(sourceData != null) { PlanarImage source = getSourceImage(i); // Recycle the source tile if(source.overlapsMultipleTiles(sourceData.getBounds())) { recycleTile(sourceData); } } } } else { PlanarImage[] imageSources = new PlanarImage[numSources]; for (int i = 0; i < numSources; i++) { imageSources[i] = getSource(i); } computeRect(imageSources, dest, destRect); } return dest; } /** * Computes a rectangle of output, given Raster * sources. This method should be overridden by * OpImage subclasses that make use of cobbled * sources, as determined by the setting of the * cobbleSources constructor argument to this class. * *

    The source Rasters are guaranteed to include * at least the area specified by mapDestRect(destRect) * unless this area is empty or does not intersect the corresponding * source in which case the source Raster * will be null. Only the specified destination region * should be written.

    * *

    Since the subclasses of OpImage may choose * between the cobbling and non-cobbling versions of * computeRect, it is not possible to leave this * method abstract in OpImage. Instead, a default * implementation is provided that throws a * RuntimeException.

    * * @param sources an array of source Rasters, one per * source image. * @param dest a WritableRaster to be filled in. * @param destRect the Rectangle within the * destination to be written. * * @throws RuntimeException If this method is invoked on the subclass * that sets cobbleSources to true * but does not supply an implementation of this method. */ protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { String className = this.getClass().getName(); throw new RuntimeException(className + " " + JaiI18N.getString("OpImage0")); } /** * Computes a rectangle of output, given PlanarImage * sources. This method should be overridden by * OpImage subclasses that do not require cobbled * sources; typically they will instantiate iterators to perform * source access, but they may access sources directly (via the * SampleModel/DataBuffer interfaces) if * they wish. * *

    Since the subclasses of OpImage may choose * between the cobbling and non-cobbling versions of * computeRect, it is not possible to leave this * method abstract in OpImage. Instead, a default * implementation is provided that throws a * RuntimeException. * * @param sources an array of PlanarImage sources. * @param dest a WritableRaster to be filled in. * @param destRect the Rectangle within the * destination to be written. * * @throws RuntimeException If this method is invoked on the subclass * that sets cobbleSources to false * but does not supply an implementation of this method. */ protected void computeRect(PlanarImage[] sources, WritableRaster dest, Rectangle destRect) { String className = this.getClass().getName(); throw new RuntimeException(className + " " + JaiI18N.getString("OpImage1")); } /** * Returns a list of indices of the tiles of a given source image * that may be required in order to compute a given tile. * Ideally, only tiles that will be requested by means of calls to * the source's getTile() method should be reported. * The default implementation uses mapDestRect() to * obtain a conservative estimate. * *

    If no dependencies exist, this method returns * null. * *

    This method may be used by optimized implementations of JAI * in order to predict future work and create an optimized * schedule for performing it. * *

    A given OpImage may mix calls to * getTile() with calls to other methods such as * getData() and copyData() in order to * avoid requesting entire tiles where only a small portion is * needed. In such a case, this method may be overridden to * provide a more accurate estimate of the set of * getTile() calls that will actually be performed. * * @param tileX the X index of the tile. * @param tileY the Y index of the tile. * @param sourceIndex the index of the source image. * * @return An array of Points indicating the source * tile dependencies. * * @throws IllegalArgumentException If sourceIndex is * negative or greater than the index of the last source. */ public Point[] getTileDependencies(int tileX, int tileY, int sourceIndex) { if (sourceIndex < 0 || sourceIndex >= getNumSources()) { // Specified source does not exist for this image. throw new IllegalArgumentException( JaiI18N.getString("Generic1")); } Rectangle rect = getTileRect(tileX, tileY); if (rect.isEmpty()) { // The tile is outside of the image bounds. return null; } // Returns a list of tiles that belong to the source specified by // the sourceIndex argument, which are need to compute // the pixels within the rectangle region specified by the // rect argument of this image. // // This method uses mapDestRect to conservatively // determine the source region required. However, only those tiles // actually inside the source image bound are returned. If the // region of interest maps completely outside of the source image, // null is returned. PlanarImage src = getSource(sourceIndex); Rectangle srcRect = mapDestRect(rect, sourceIndex); int minTileX = src.XToTileX(srcRect.x); int maxTileX = src.XToTileX(srcRect.x + srcRect.width - 1); int minTileY = src.YToTileY(srcRect.y); int maxTileY = src.YToTileY(srcRect.y + srcRect.height - 1); // Make sure the tiles are really inside the source image. minTileX = Math.max(minTileX, src.getMinTileX()); maxTileX = Math.min(maxTileX, src.getMaxTileX()); minTileY = Math.max(minTileY, src.getMinTileY()); maxTileY = Math.min(maxTileY, src.getMaxTileY()); int numXTiles = maxTileX - minTileX + 1; int numYTiles = maxTileY - minTileY + 1; if (numXTiles <= 0 || numYTiles <= 0) { // The tile maps outside of source image bound. return null; } Point[] ret = new Point[numYTiles*numXTiles]; int i = 0; for (int y = minTileY; y <= maxTileY; y++) { for (int x = minTileX; x <= maxTileX; x++) { ret[i++] = new Point(x, y); } } return ret; } /** * Computes the tiles indicated by the given tile indices. This * call is preferable to a series of getTile() calls * because certain implementations can make optimizations based on * the knowledge that multiple tiles are being asked for at once. * *

    The implementation of this method in this class uses multiple * threads to compute multiple tiles at a time. * * @param tileIndices An array of Points representing * tile indices. * * @return An array of Rasters containing the tiles * corresponding to the given tile indices. * * @throws IllegalArgumentException If tileIndices is * null. */ public Raster[] getTiles(Point[] tileIndices) { if (tileIndices == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } int numTiles = tileIndices.length; // number of tiles requested // The requested tiles, to be returned. Raster[] tiles = new Raster[numTiles]; // Indicator for those tiles that actually need to be computed. boolean[] computeTiles = new boolean[numTiles]; int minTileX = getMinTileX(); int maxTileX = getMaxTileX(); int minTileY = getMinTileY(); int maxTileY = getMaxTileY(); int count = 0; // number of tiles need to be computed for (int i = 0; i < numTiles; i++) { int tileX = tileIndices[i].x; int tileY = tileIndices[i].y; // Make sure the tile is inside image boundary. if (tileX >= minTileX && tileX <= maxTileX && tileY >= minTileY && tileY <= maxTileY) { // Check if tile is available in the cache. tiles[i] = getTileFromCache(tileX, tileY); if (tiles[i] == null) { // Tile not in cache. needs computation. computeTiles[i] = true; count++; } } } if (count > 0) { // need to compute some tiles if (count == numTiles) { // None of the tiles is in cache. tiles = scheduler.scheduleTiles(this, tileIndices); if (cache != null) { // cache these tiles if(cache != null) { for (int i = 0; i < numTiles; i++) { cache.add(this, tileIndices[i].x, tileIndices[i].y, tiles[i], tileCacheMetric); } } } } else { // Only schedule those tiles not in cache for computation. Point[] indices = new Point[count]; count = 0; for (int i = 0; i < numTiles; i++) { if (computeTiles[i]) { indices[count++] = tileIndices[i]; } } // Schedule needed tiles and return. Raster[] newTiles = scheduler.scheduleTiles(this, indices); count = 0; for (int i = 0; i < numTiles; i++) { if (computeTiles[i]) { tiles[i] = newTiles[count++]; addTileToCache(tileIndices[i].x, tileIndices[i].y, tiles[i]); } } } } return tiles; } private static TileComputationListener[] prependListener(TileComputationListener[] listeners, TileComputationListener listener) { if(listeners == null) { return new TileComputationListener[] {listener}; } TileComputationListener[] newListeners = new TileComputationListener[listeners.length+1]; newListeners[0] = listener; System.arraycopy(listeners, 0, newListeners, 1, listeners.length); return newListeners; } /** * Returns an array of indices of tiles which are not cached or * null if all are cached. */ /* XXX private Point[] pruneIndices(Point[] tileIndices) { if(true)return tileIndices;//XXX int numIndices = tileIndices.length; ArrayList uncachedIndices = new ArrayList(numIndices); for(int i = 0; i < numIndices; i++) { Point p = tileIndices[i]; if(getTileFromCache(p.x, p.y) == null) { uncachedIndices.add(p); } } int numUncached = uncachedIndices.size(); return numUncached > 0 ? (Point[])uncachedIndices.toArray(new Point[numUncached]) : null; } */ /** * Queues a list of tiles for computation. Registered listeners will * be notified after each tile has been computed. The event source * parameter passed to such listeners will be the TileScheduler * and the image parameter will be this image. * * @param tileIndices A list of tile indices indicating which tiles * to schedule for computation. * @throws IllegalArgumentException If tileIndices is * null. * * @since JAI 1.1 */ public TileRequest queueTiles(Point[] tileIndices) { if (tileIndices == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } /* XXX bad idea probably // Remove any tile indices corresponding to cached tiles. tileIndices = pruneIndices(tileIndices); // Return if no tiles remain, i.e., all are cached. if(tileIndices == null) { return; } */ // Get registered listeners. TileComputationListener[] tileListeners = getTileComputationListeners(); // Add a listener to cache tiles only if not a SunTileScheduler. // The SunTileScheduler caches tiles generated by an OpImage but // this is not a requirement of the specification. if(!isSunTileScheduler) { // Create a local listener. TileComputationListener localListener = new TCL(this); // Prepend local listener to array. tileListeners = prependListener(tileListeners, localListener); } // Queue the tiles to the scheduler. return scheduler.scheduleTiles(this, tileIndices, tileListeners); } /** * Issue an advisory cancellation request to nullify processing of * the indicated tiles via the TileScheduler for this image. This * method should merely forward the request to the associated * TileScheduler. * * @param request The request for which tiles are to be cancelled. * @param tileIndices The tiles to be cancelled; may be null. * Any tiles not actually in the TileRequest will be * ignored. * @throws IllegalArgumentException If request is * null. * * @since JAI 1.1 */ public void cancelTiles(TileRequest request, Point[] tileIndices) { if (request == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic4")); } scheduler.cancelTiles(request, tileIndices); } /** * Hints that the given tiles might be needed in the near future. * Some implementations may spawn one or more threads * to compute the tiles, while others may ignore the hint. * * @param tileIndices A list of tile indices indicating which tiles * to prefetch. * * @throws IllegalArgumentException If tileIndices is * null. */ public void prefetchTiles(Point[] tileIndices) { if (tileIndices == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } /* XXX bad idea probably // Remove any tile indices corresponding to cached tiles. tileIndices = pruneIndices(tileIndices); */ // Return if no tiles remain, i.e., all are cached. if(tileIndices == null) { return; } // Prefetch any remaining tiles. scheduler.prefetchTiles(this, tileIndices); } /** * Computes the position in the specified source that best * matches the supplied destination image position. If it * is not possible to compute the requested position, * null will be returned. If the point is mapped * outside the source bounds, the coordinate value or null * may be returned at the discretion of the implementation. * *

    Floating-point input and output coordinates are supported, * and recommended when possible. Subclass implementations may * however use integer computation if necessary for simplicity.

    * *

    The implementation in this class returns the value of * pt in the following code snippet: * *

         * Rectangle destRect = new Rectangle((int)destPt.getX(),
         *                                    (int)destPt.getY(),
         *                                    1, 1);
         * Rectangle sourceRect = mapDestRect(destRect, sourceIndex);
         * Point2D pt = (Point2D)destPt.clone();
         * pt.setLocation(sourceRect.x + (sourceRect.width - 1.0)/2.0,
         *                sourceRect.y + (sourceRect.height - 1.0)/2.0);
         * 
    * * Subclasses requiring different behavior should override this * method.

    * * @param destPt the position in destination image coordinates * to map to source image coordinates. * @param sourceIndex the index of the source image. * * @return a Point2D of the same class as * destPt or null. * * @throws IllegalArgumentException if destPt is * null. * @throws IndexOutOfBoundsException if sourceIndex is * negative or greater than or equal to the number of sources. * * @since JAI 1.1.2 */ public Point2D mapDestPoint(Point2D destPt, int sourceIndex) { if (destPt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } else if (sourceIndex < 0 || sourceIndex >= getNumSources()) { throw new IndexOutOfBoundsException(JaiI18N.getString("Generic1")); } Rectangle destRect = new Rectangle((int)destPt.getX(), (int)destPt.getY(), 1, 1); Rectangle sourceRect = mapDestRect(destRect, sourceIndex); Point2D pt = (Point2D)destPt.clone(); pt.setLocation(sourceRect.x + (sourceRect.width - 1.0)/2.0, sourceRect.y + (sourceRect.height - 1.0)/2.0); return pt; } /** * Computes the position in the destination that best * matches the supplied source image position. If it * is not possible to compute the requested position, * null will be returned. If the point is mapped * outside the destination bounds, the coordinate value or * null may be returned at the discretion of the * implementation. * *

    Floating-point input and output coordinates are supported, * and recommended when possible. Subclass implementations may * however use integer computation if necessary for simplicity.

    * *

    The implementation in this class returns the value of * pt in the following code snippet: * *

         * Rectangle sourceRect = new Rectangle((int)sourcePt.getX(),
         *                                      (int)sourcePt.getY(),
         *                                      1, 1);
         * Rectangle destRect = mapSourceRect(sourceRect, sourceIndex);
         * Point2D pt = (Point2D)sourcePt.clone();
         * pt.setLocation(destRect.x + (destRect.width - 1.0)/2.0,
         *                destRect.y + (destRect.height - 1.0)/2.0);
         * 
    * * @param sourcePt the position in source image coordinates * to map to destination image coordinates. * @param sourceIndex the index of the source image. * * @return a Point2D of the same class as * sourcePt or null. * * @throws IllegalArgumentException if sourcePt is * null. * @throws IndexOutOfBoundsException if sourceIndex is * negative or greater than or equal to the number of sources. * * @since JAI 1.1.2 */ public Point2D mapSourcePoint(Point2D sourcePt, int sourceIndex) { if (sourcePt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } else if (sourceIndex < 0 || sourceIndex >= getNumSources()) { throw new IndexOutOfBoundsException(JaiI18N.getString("Generic1")); } Rectangle sourceRect = new Rectangle((int)sourcePt.getX(), (int)sourcePt.getY(), 1, 1); Rectangle destRect = mapSourceRect(sourceRect, sourceIndex); // Return null of destination rectangle is not computable. if(destRect == null) { return null; } Point2D pt = (Point2D)sourcePt.clone(); pt.setLocation(destRect.x + (destRect.width - 1.0)/2.0, destRect.y + (destRect.height - 1.0)/2.0); return pt; } /** * Returns a conservative estimate of the destination region that * can potentially be affected by the pixels of a rectangle of a * given source. An empty Rectangle may be returned * if the destination is unaffected by the contents of the source * rectangle. This is distinct from a null return value * which serves rather to indicate that it is not possible to * determine the bounds of the affected region. The safest * interpretation of a null return value is that the * entire destination might be affected by any pixel within the * given source rectangle. * * @param sourceRect The Rectangle in source coordinates. * @param sourceIndex The index of the source image. * * @return A Rectangle indicating the potentially * affected destination region, or null if * the region is unknown. * * @throws IllegalArgumentException If the source index is * negative or greater than that of the last source. * @throws IllegalArgumentException If sourceRect is * null. */ public abstract Rectangle mapSourceRect(Rectangle sourceRect, int sourceIndex); /** * Returns a conservative estimate of the region of a specified * source that is required in order to compute the pixels of a * given destination rectangle. The computation may as appropriate * clip the mapped Rectangle to the actual bounds of the * source or may treat the source as having infinite extent. * It is therefore the responsibility of the invoking * object to constrain the region in accordance with its needs. * Returning an empty Rectangle should indicate that * the data of the source image in question are not required for the * computation of the specified destination region. If the entire * source image might be required to compute this destination * region, then getSourceImage(sourceIndex).getBounds() * should be returned. * *

    To illustrate the issue of whether the source should be thought * to have infinite extent, consider the case wherein computing a * destination pixel requires multiple source pixels of context. At * the source image boundary, these pixels might only be available if the * source data were extrapolated, e.g., using a {@link BorderExtender}. * If such an extender were available, destination pixels could be * computed even if they mapped to a region on the source boundary so * in this case the source could be considered to have infinite extent. * If no such extender were available, only destination pixels with * source context contained within the source image bounds could be * considered so that it might be preferable to clip the rectangle to * the source bounds.

    * * @param destRect The Rectangle in destination coordinates. * @param sourceIndex The index of the source image. * * @return A non-null Rectangle indicating * the required source region. * * @throws IllegalArgumentException If the source index is * negative or greater than that of the last source. * @throws IllegalArgumentException If destRect is * null. */ public abstract Rectangle mapDestRect(Rectangle destRect, int sourceIndex); /** * Returns one of OP_COMPUTE_BOUND, * OP_IO_BOUND, or OP_NETWORK_BOUND to * indicate how the operation is likely to spend its time. The * answer does not affect the output of the operation, but may * allow a scheduler to parallelize the computation of multiple * operations more effectively. * *

    The implementation of this method in this class * returns OP_COMPUTE_BOUND. */ public int getOperationComputeType() { return OP_COMPUTE_BOUND; } /** * Returns true if the OpImage returns an * unique Raster object every time computeTile * is called. OpImages that internally cache * Rasters and return them via computeTile * should return false for this method. * *

    The implementation of this method in this class always returns * true. */ public boolean computesUniqueTiles() { return true; } /** * Uncaches all tiles and calls super.dispose(). * If a TileRecycler was defined via the configuration * variable JAI.KEY_TILE_RECYCLER when this image was * constructed and tile recycling was enabled via the configuration * variable JAI.KEY_CACHED_TILE_RECYCLING_ENABLED, then each * of this image's tiles which is currently in the cache will be * recycled. This method may be invoked more than once although * invocations after the first one may do nothing. * *

    The results of referencing an image after a call to * dispose() are undefined.

    * * @since JAI 1.1.2 */ public synchronized void dispose() { if(isDisposed) { return; } isDisposed = true; if (cache != null) { if(isCachedTileRecyclingEnabled && tileRecycler != null) { Raster[] tiles = cache.getTiles(this); if(tiles != null) { int numTiles = tiles.length; for(int i = 0; i < numTiles; i++) { tileRecycler.recycleTile(tiles[i]); } } } cache.removeTiles(this); } super.dispose(); } /** * Indicates whether the source with the given index has a * BorderExtender. If the source index is out of bounds * for the source vector of this OpImage then an * ArrayIndexOutOfBoundsException may be thrown. * * @param sourceIndex The index of the source in question. * @return true if the indicated source has an extender. * * @deprecated as of JAI 1.1. */ public boolean hasExtender(int sourceIndex) { if(sourceIndex != 0) { throw new ArrayIndexOutOfBoundsException(); } else if(this instanceof AreaOpImage) { return ((AreaOpImage)this).getBorderExtender() != null; } else if(this instanceof GeometricOpImage) { return ((GeometricOpImage)this).getBorderExtender() != null; } return false; } /** * Returns the effective number of bands of an image with a given * SampleModel and ColorModel. * Normally, this is given by * sampleModel.getNumBands(), but for images with an * IndexColorModel the effective number of bands is * given by colorModel.getNumComponents(), since * a single physical sample represents multiple color components. * * @deprecated as of JAI 1.1. */ public static int getExpandedNumBands(SampleModel sampleModel, ColorModel colorModel) { if (colorModel instanceof IndexColorModel) { return colorModel.getNumComponents(); } else { return sampleModel.getNumBands(); } } /** * Returns the image's format tags to be used with * a RasterAccessor. * *

    This method will compute and cache the tags the first time * it is called on a particular image. The image's * SampleModel and ColorModel must be * set to their final values before calling this method. * * @return An array containing RasterFormatTags for the * sources in the first getNumSources() elements and a * RasterFormatTag for the destination in the last element. */ // XXX This method should be removed if we stop using RasterAccessor. protected synchronized RasterFormatTag[] getFormatTags() { if (formatTags == null) { RenderedImage[] sourceArray = new RenderedImage[getNumSources()]; if(sourceArray.length > 0) { getSources().toArray(sourceArray); } formatTags = RasterAccessor.findCompatibleTags(sourceArray, this); } return formatTags; } /** * Returns the value of the instance variable tileRecycler. * * @since JAI 1.1.2 */ public TileRecycler getTileRecycler() { return tileRecycler; } /** * Creates a WritableRaster at the given tile grid position. * The superclass method {@link #createWritableRaster(SampleModel,Point)} * will be invoked with this image's SampleModel and the * location of the specified tile. * *

    Subclasses should ideally use this method to create destination * tiles as this method will take advantage of any * TileFactory specified to the OpImage at * construction.

    * * @since JAI 1.1.2 */ protected final WritableRaster createTile(int tileX, int tileY) { return createWritableRaster(sampleModel, new Point(tileXToX(tileX), tileYToY(tileY))); } /** * A tile recycling convenience method. * *

    If tileRecycler is non-null, the call * is forwarded to {@link TileRecycler.recycleTile(Raster)}; otherwise * the method does nothing.

    * *

    This method is for use by subclasses which create * Rasters with limited scope which therefore may easily * be identified as safe candidates for recycling. This might occur * for example within * {@link #computeRect(Raster[],WritableRaster,Rectangle)} or * {@link #computeTile(int,int)} wherein Rasters may be * created for use within the method but be eligible for garbage * collection once the method is exited.

    * * @throws IllegalArgumentException if tile is * null. * * @since JAI 1.1.2 */ protected void recycleTile(Raster tile) { if (tile == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); if(tileRecycler != null) { tileRecycler.recycleTile(tile); } } } jai-core-1.1.4/src/share/classes/javax/media/jai/TileRecycler.java0000644000175000017500000000420710203035544024571 0ustar mathieumathieu/* * $RCSfile: TileRecycler.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:22 $ * $State: Exp $ */ package javax.media.jai; import java.awt.image.Raster; /** * An interface to a mechanism which is capable of recycling tiles. * In general the term recycle in this context is taken to * mean re-using memory allocated to the tile. This would usually * be accomplished by reclaiming the data bank (array) associated * with the DataBuffer of the tile Raster. * It would also be possible by simple translation of a * WritableRaster provided the SampleModel * was compatible with the tile required by its eventual user. * *

    Tile recycling should be used with caution. In particular, * the calling code must be certain that any tile submitted for * recycling not be used elsewhere. If one or more references to * tiles submitted to a recycler are held by the calling code then * undefined and unexpected behavior may be observed. A similar * caution applies to the tile's DataBuffer and the * data bank array contained therein.

    * * @since JAI 1.1.2 */ public interface TileRecycler { /** * Suggests to the TileRecycler that the parameter * tile is no longer needed and may be used in creating a new * Raster. This will inevitably result in at least * the internal array being overwritten. If a reference to * the tile, its DataBuffer, or the data bank(s) of its * DataBuffer is held elsewhere in the caller's code, * undefined behavior may result. It is the responsibilty of * the calling code to ensure that this does not occur. * * @param tile A tile which mey be re-used either directly or * by reclaiming its internal DataBuffer * or primitive data array. * @throws IllegalArgumentException if tile is * null. */ void recycleTile(Raster tile); } jai-core-1.1.4/src/share/classes/javax/media/jai/KernelJAI.java0000644000175000017500000004425010203035544023751 0ustar mathieumathieu/* * $RCSfile: KernelJAI.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:11 $ * $State: Exp $ */ package javax.media.jai; import java.awt.image.Kernel; import java.io.Serializable; import javax.media.jai.JaiI18N; /** * A kernel representing a matrix with a key position, * used by operators such as Convolve . * *

    A KernelJAI is characterized by its width, height, and * origin, or key element. The key element is the element which is placed * over the current source pixel to perform convolution or error diffusion. * *

    A kernel K is separable it the outer product of two one-dimensional * vectors. It can speed up computation. One can construct a kernel * from two one-dimensional vectors. * * <>The symmetry can be useful (such as computation speedup). Currently * the protected instance variables isHorizonallySymmetric * and isVerticallySymmetric are set to false. * * * @see javax.media.jai.operator.ConvolveDescriptor * @see javax.media.jai.operator.OrderedDitherDescriptor * @see javax.media.jai.operator.ErrorDiffusionDescriptor */ public class KernelJAI extends Object implements Serializable { /** * Floyd and Steinberg error filter (1975). *

         * (1/16 x)  [   * 7 ]
         *           [ 3 5 1 ]
         * 
    */ public static final KernelJAI ERROR_FILTER_FLOYD_STEINBERG = new KernelJAI(3, 2, 1, 0, new float[] {0.0F/16.0F, 0.0F/16.0F, 7.0F/16.0F, 3.0F/16.0F, 5.0F/16.0F, 1.0F/16.0F}); /** * Jarvis, Judice, and Ninke error filter (1976). *
         *           [     * 7 5 ]
         * (1/48 x)  [ 3 5 7 5 3 ]
         *           [ 1 3 5 3 1 ]
         * 
    */ public static final KernelJAI ERROR_FILTER_JARVIS = new KernelJAI(5, 3, 2, 0, new float[] {0.0F, 0.0F, 0.0F, 7.0F/48.0F, 5.0F/48.0F, 3.0F/48.0F, 5.0F/48.0F, 7.0F/48.0F, 5.0F/48.0F, 3.0F/48.0F, 1.0F/48.0F, 3.0F/48.0F, 5.0F/48.0F, 3.0F/48.0F, 1.0F/48.0F}); /** * Stucki error filter (1981). *
         *           [     * 7 5 ]
         * (1/42 x)  [ 2 4 8 4 2 ]
         *           [ 1 2 4 2 1 ]
         * 
    */ public static final KernelJAI ERROR_FILTER_STUCKI = new KernelJAI(5, 3, 2, 0, new float[] {0.0F, 0.0F, 0.0F, 7.0F/42.0F, 5.0F/42.0F, 2.0F/42.0F, 4.0F/42.0F, 8.0F/42.0F, 4.0F/42.0F, 2.0F/42.0F, 1.0F/42.0F, 2.0F/42.0F, 4.0F/42.0F, 2.0F/42.0F, 1.0F/42.0F}); /** * 4x4x1 mask useful for dithering 8-bit grayscale images to 1-bit images. */ public static final KernelJAI[] DITHER_MASK_441 = new KernelJAI[] { new KernelJAI(4, 4, 1, 1, new float[] {0.9375F, 0.4375F, 0.8125F, 0.3125F, 0.1875F, 0.6875F, 0.0625F, 0.5625F, 0.7500F, 0.2500F, 0.8750F, 0.3750F, 0.0000F, 0.5000F, 0.1250F, 0.6250F}) }; /** * 4x4x3 mask useful for dithering 24-bit color images to 8-bit * pseudocolor images. */ public static final KernelJAI[] DITHER_MASK_443 = new KernelJAI[] { new KernelJAI(4, 4, 1, 1, new float[] {0.0000F, 0.5000F, 0.1250F, 0.6250F, 0.7500F, 0.2500F, 0.8750F, 0.3750F, 0.1875F, 0.6875F, 0.0625F, 0.5625F, 0.9375F, 0.4375F, 0.8125F, 0.3125F}), new KernelJAI(4, 4, 1, 1, new float[] {0.6250F, 0.1250F, 0.5000F, 0.0000F, 0.3750F, 0.8750F, 0.2500F, 0.7500F, 0.5625F, 0.0625F, 0.6875F, 0.1875F, 0.3125F, 0.8125F, 0.4375F, 0.9375F}), new KernelJAI(4, 4, 1, 1, new float[] {0.9375F, 0.4375F, 0.8125F, 0.3125F, 0.1875F, 0.6875F, 0.0625F, 0.5625F, 0.7500F, 0.2500F, 0.8750F, 0.3750F, 0.0000F, 0.5000F, 0.1250F, 0.6250F}) }; /** * Gradient Mask for SOBEL_VERTICAL. */ public static final KernelJAI GRADIENT_MASK_SOBEL_VERTICAL = new KernelJAI(3, 3, 1, 1, new float[] {-1, -2 , -1, 0, 0, 0, 1, 2, 1}); /** * Gradient Mask for SOBEL_HORIZONTAL. */ public static final KernelJAI GRADIENT_MASK_SOBEL_HORIZONTAL = new KernelJAI(3, 3, 1, 1, new float[] {-1, 0, 1, -2, 0, 2, -1, 0, 1}); /** The width of the kernel. */ protected int width; /** The height of the kernel. */ protected int height; /** The X coordinate of the key element. */ protected int xOrigin; /** The Y coordinate of the key element. */ protected int yOrigin; /** The kernel data in row-major format. */ protected float[] data = null; /** The horizontal data for a separable kernel */ protected float[] dataH = null; /** The vertical data for a separable kernel */ protected float[] dataV = null; /** True if the kernel is separable. */ protected boolean isSeparable = false; /** True if the kernel has horizontal (Y axis) symmetry. */ protected boolean isHorizontallySymmetric = false; /** True if the kernel has vertical (X axis) symmetry. */ protected boolean isVerticallySymmetric = false; /** Variable to cache a copy of the rotated kernel */ protected KernelJAI rotatedKernel = null; private synchronized void checkSeparable() { // Define a local constant for single precision floating // point tolerance. float floatZeroTol = (float)1.0E-5; if (isSeparable) { return; } // already separable if (width <= 1 || height <= 1) { return; } // 1D kernel is non-separable unless constructed to explicitly so // (either dataH or dataV will be a 1x1. // else: // Check to see if given kernel can be factored into separable kernels // previous approach: if data[0]==0, then not separable; // new approach: find the largest element (and its row number) first then // check to see if rows are multiples of that row // Normalize is also important: separable kernel implimentation has // hash table look ups... and expecting things in range float maxData = 0.0F; int imax = 0, jmax = 0; for (int k=0; k < this.data.length; k++){ float tmp = Math.abs(this.data[k]); if (tmp > maxData){ imax = k; maxData = tmp; } } // check for 0 kernel // a case that should not happen in meaningful convolution if (maxData < floatZeroTol/(float)data.length){ isSeparable = false; return; } float tmpRow[] = new float[width]; float fac = 1.0F / data[imax]; // position of the max data element in the kernel matrix jmax = imax%width; imax = imax/width; for(int j = 0; j < width; j++){ tmpRow[j] = data[imax*width + j] * fac; } // // Rank 1 checking: every row should be a multiple of tmpRow // if separable (a rank one kernel matrix) for(int i = 0, i0 = 0; i < height; i++, i0 += width) { for(int j = 0; j < width; j++ ) { float tmp = Math.abs(data[i0+jmax]*tmpRow[j]-data[i0+j]); if (tmp > floatZeroTol) { isSeparable = false; return; } } } dataH = tmpRow; dataV = new float[height]; for (int i = 0; i < height; i++) { dataV[i] = data[jmax + i * width]; } isSeparable = true; // normalizing - so that dataH and dataV add up to 1 // in some cases, it may not be possible for both if // the original kernel does not add up to 1. // Row adds up to 1 as 1st choice. // If both dataH and dataV add up small, // no normalization is done. // NOTE: non-positive kernels, normalization may be skipped float sumH = 0.0F, sumV =0.0F; for (int j = 0; j < width; j++) { sumH += dataH[j]; } for (int j = 0; j < height; j++) { sumV += dataV[j]; } if (Math.abs(sumH)>= Math.abs(sumV) && Math.abs(sumH) > floatZeroTol){ fac = 1.0F/sumH; for (int j = 0; j < width; j++) { dataH[j] *= fac; } for (int j = 0; j < height; j++) { dataV[j] *= sumH; } }else if (Math.abs(sumH)< Math.abs(sumV) && Math.abs(sumV) > floatZeroTol){ fac = 1.0F/sumV; for (int j = 0; j < width; j++) { dataH[j] *= sumV; } for (int j = 0; j < height; j++) { dataV[j] *= fac; } } } private void classifyKernel() { if (isSeparable == false) { checkSeparable(); } isHorizontallySymmetric = false; isVerticallySymmetric = false; } /** * Constructs a KernelJAI with the given parameters. The data * array is copied. * * @param width the width of the kernel. * @param height the height of the kernel. * @param xOrigin the X coordinate of the key kernel element. * @param yOrigin the Y coordinate of the key kernel element. * @param data the float data in row-major format. * * @throws IllegalArgumentException if data is null. * @throws IllegalArgumentException if width is not a positive number. * @throws IllegalArgumentException if height is not a positive number. * @throws IllegalArgumentException if kernel data array does not have * width * height number of elements. * @classifies as non-separable if width or height is 1. */ public KernelJAI(int width, int height, int xOrigin, int yOrigin, float[] data) { if ( data == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.width = width; this.height = height; this.xOrigin = xOrigin; this.yOrigin = yOrigin; this.data = (float[])data.clone(); if (width <= 0) { throw new IllegalArgumentException(JaiI18N.getString("KernelJAI0")); } if (height <= 0) { throw new IllegalArgumentException(JaiI18N.getString("KernelJAI1")); } if (width*height != data.length) { throw new IllegalArgumentException(JaiI18N.getString("KernelJAI2")); } classifyKernel(); } /** * Constructs a separable KernelJAI from two float arrays. * The data arrays are copied. * * A Separable kernel K = dataH * dataV^T, the outer product of two * one dimensional vectors dataH and dataV. It can often speed up * compution. * * @param width the width of the kernel. * @param height the height of the kernel. * @param xOrigin the X coordinate of the key kernel element. * @param yOrigin the Y coordinate of the key kernel element. * @param dataH the float data for the horizontal direction. * @param dataV the float data for the vertical direction. * * @throws IllegalArgumentException if dataH is null. * @throws IllegalArgumentException if dataV is null. * @throws IllegalArgumentException if width is not a positive number. * @throws IllegalArgumentException if height is not a positive number. * @throws IllegalArgumentException if dataH does not have width elements. * @throws IllegalArgumentException if dataV does not have height elements. * @must use the other constructor when dataH or dataV is null */ public KernelJAI(int width, int height, int xOrigin, int yOrigin, float[] dataH, float[] dataV) { if ( dataH == null || dataV == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (width <= 0) { throw new IllegalArgumentException(JaiI18N.getString("KernelJAI0")); } if (height <= 0) { throw new IllegalArgumentException(JaiI18N.getString("KernelJAI1")); } if (width != dataH.length) { throw new IllegalArgumentException(JaiI18N.getString("KernelJAI3")); } if (height != dataV.length) { throw new IllegalArgumentException(JaiI18N.getString("KernelJAI4")); } this.width = width; this.height = height; this.xOrigin = xOrigin; this.yOrigin = yOrigin; this.dataH = (float[])dataH.clone(); this.dataV = (float[])dataV.clone(); this.data = new float[dataH.length*dataV.length]; int rowOffset = 0; for (int i = 0; i < dataV.length; i++) { float vValue = dataV[i]; for (int j = 0; j < dataH.length; j++) { data[rowOffset+j] = vValue*dataH[j]; } rowOffset += dataH.length; } isSeparable = true; classifyKernel(); } /** * Constructs a kernel with the given parameters. The data * array is copied. The key element is set to * (trunc(width/2), trunc(height/2)). * * @param width the width of the kernel. * @param height the height of the kernel. * @param data the float data in row-major format. * * @throws IllegalArgumentException if data is null. * @throws IllegalArgumentException if width is not a positive number. * @throws IllegalArgumentException if height is not a positive number. * @throws IllegalArgumentException if data does not have * width * height number of elements. */ public KernelJAI(int width, int height, float[] data) { this(width, height, width/2, height/2, data); } /** * Constructs a KernelJAI from a java.awt.image.Kernel * object. * * @throws NullPointerException if k is null. */ public KernelJAI(Kernel k) { // XXX - NullPointerException (inconsistent style) this(k.getWidth(), k.getHeight(), k.getXOrigin(), k.getYOrigin(), k.getKernelData(null)); } /** Returns the width of the kernel. */ public int getWidth() { return width; } /** Returns the height of the kernel. */ public int getHeight() { return height; } /** Returns the X coordinate of the key kernel element. */ public int getXOrigin() { return xOrigin; } /** Returns the Y coordinate of the key kernel element. */ public int getYOrigin() { return yOrigin; } /** Returns a copy of the kernel data in row-major format. */ public float[] getKernelData() { return (float[])data.clone(); } /** * Returns the horizontal portion of the kernel if the * kernel is separable, or null otherwise. The kernel may * be tested for separability by calling isSeparable(). */ public float[] getHorizontalKernelData() { if (dataH == null) { return null; } return (float[])dataH.clone(); } /** * Returns the vertical portion of the kernel if the * kernel is separable, or null otherwise. The kernel may * be tested for separability by calling isSeparable(). */ public float[] getVerticalKernelData() { if (dataV == null) { return null; } return (float[])dataV.clone(); } /** * Returns a given element of the kernel. * * @throws ArrayIndexOutOfBoundsException if either xIndex or yIndex is * an invalid index. */ public float getElement(int xIndex, int yIndex) { if (!isSeparable) { return data[yIndex*width + xIndex]; } else { return dataH[xIndex]*dataV[yIndex]; } } /** * Returns true if the kernel is separable. */ public boolean isSeparable() { return isSeparable; } /** Returns true if the kernel has horizontal (Y axis) symmetry. */ public boolean isHorizontallySymmetric() { return isHorizontallySymmetric; } /** Returns true if the kernel has vertical (X axis) symmetry. */ public boolean isVerticallySymmetric() { return isVerticallySymmetric; } /** * Returns the number of pixels required to the left of the key element. */ public int getLeftPadding() { return xOrigin; } /** * Returns the number of pixels required to the right of the key element. */ public int getRightPadding() { return width - xOrigin - 1; } /** * Returns the number of pixels required above the key element. */ public int getTopPadding() { return yOrigin; } /** * Returns the number of pixels required below the key element. */ public int getBottomPadding() { return height - yOrigin - 1; } /** * Returns a 180 degree rotated version of the kernel. This is * needed by most convolve operations to get the correct results. * * @return the rotated kernel. */ public KernelJAI getRotatedKernel() { if (rotatedKernel == null) { if ( this.isSeparable){ float rotDataH[] = new float[this.width]; float rotDataV[] = new float[this.height]; for (int i = 0; i < this.width; i++) { rotDataH[i] = this.dataH[width-1-i]; } for (int i = 0; i < this.height; i++) { rotDataV[i] = this.dataV[height-1-i]; } rotatedKernel = new KernelJAI(width, height, width-1-xOrigin, height-1-yOrigin, rotDataH, rotDataV); }else{ int length = data.length; float newData[] = new float[data.length]; for (int i = 0; i < length; i++) { newData[i] = data[length-1-i]; } rotatedKernel = new KernelJAI(width, height, width-1-xOrigin, height-1-yOrigin, newData); } } return rotatedKernel; } } jai-core-1.1.4/src/share/classes/javax/media/jai/DescriptorCache.java0000644000175000017500000010655210203035544025253 0ustar mathieumathieu/* * $RCSfile: DescriptorCache.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:07 $ * $State: Exp $ */ package javax.media.jai; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Vector; import javax.media.jai.util.CaselessStringKey; /** * A class to manage the descriptors belong to a certain * RegistryMode * * The RegistryElementDescriptor names are used in a * case-insensitive manner. */ class DescriptorCache { /** * The name of the mode for which the cache of descriptors is * being maintained. */ final String modeName; /** * Cache the RegistryMode since it is bound to get used * many, many times. */ final RegistryMode mode; /** Does the registry mode for this cache support preferences. */ final boolean arePreferencesSupported; /** Does the registry mode for this cache support properties. */ final boolean arePropertiesSupported; /** * A Hashtable of all the RegistryElementDescriptors, * hashed by their name. */ private Hashtable descriptorNames; /** * A Hashtable of all the products, hashed by the * name of the RegistryElementDescriptor to which they belong. */ private Hashtable products; /** * A Hashtable of all the product preferences, hashed by the * descriptor name that the products belong to. The product * preferences are stored in a Vector. The elements * of the Vector consist of String[2] * objects, each storing the preferred product's name, and the other * product's name within it. */ private Hashtable productPrefs; // // Property related tables... // /** * A Hashtable of Vectors containing all the * PropertyGenerators, hashed by the descriptor * name that the PropertyGenerators belong to. */ private Hashtable properties; /** * A Hashtable of Vectors containing the names * of all the properties to be suppressed, hashed by the descriptor * name whose properties are to be suppressed. */ private Hashtable suppressed; /** * A Hashtable containing information about which source a property * should be copied from, hashed by the descriptor name to which the * property belongs. The information about which source a property * should be copied from is stored in a Hashtable containing the * index of the source, hashed by the property name. */ private Hashtable sourceForProp; /** * A Hashtable that stores the PropertySources for all * the properties, hashed by the descriptor name that the properties * belong to. The PropertySources are stored in a * Hashtable, hashed by the name of the property. */ private Hashtable propNames; /** * The Constructor. Create a RegistryElementDescriptor * cache for maintaining descriptors for the specified mode. * * @param modeName the registry mode name. * */ DescriptorCache(String modeName) { this.modeName = modeName; this.mode = RegistryMode.getMode(modeName); arePreferencesSupported = mode.arePreferencesSupported(); arePropertiesSupported = mode.arePropertiesSupported(); descriptorNames = new Hashtable(); products = new Hashtable(); if (arePreferencesSupported) productPrefs = new Hashtable(); // Property related tables. properties = new Hashtable(); suppressed = new Hashtable(); sourceForProp = new Hashtable(); propNames = new Hashtable(); } /** * Adds a RegistryElementDescriptor to the cache. * *

    An RegistryElementDescriptor cannot be added against a * descriptor name under which another RegistryElementDescriptor * was added previously. * * @param rdesc an RegistryElementDescriptor containing * information about the descriptor. * * @return false, if one already existed. true otherwise. * * @throws IllegalArgumentException is rdesc in null * @throws IllegalArgumentException if the descriptor has * already been registered in this cache. */ boolean addDescriptor(RegistryElementDescriptor rdesc) { if (rdesc == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } String descriptorName = rdesc.getName(); // Use a caseless version of the key. CaselessStringKey key = new CaselessStringKey(descriptorName); // If the key has already been added bail out ... if (descriptorNames.containsKey(key) == true) { throw new IllegalArgumentException( JaiI18N.formatMsg("DescriptorCache0", new Object[] {descriptorName, modeName})); } // Store the RegistryElementDescriptor hashed by its global name descriptorNames.put(key, rdesc); // Store the ProductOperationGraph hashed by the caseless // descriptor name if (arePreferencesSupported) products.put(key, new ProductOperationGraph()); // if properties arent supported by this descriptor we are done. if (rdesc.arePropertiesSupported() == false) return true; // Store the Property Generators associated with this descriptor // for the specified mode. PropertyGenerator props[] = rdesc.getPropertyGenerators(modeName); if (props != null) { for (int i=0; iRegistryElementDescriptor from the cache. * * @param descriptorName the descriptor name as a String. * * @return false, if one wasnt previously registered, true otherwise. * * @throws IllegalArgumentException if descriptorName is null or * was not previously registered. * @throws IllegalArgumentException if any of the * PropertyGenerators associated with the * RegistryElementDescriptor to be removed is null. */ boolean removeDescriptor(String descriptorName) { // Use a caseless version of the key. CaselessStringKey key = new CaselessStringKey(descriptorName); // If it is not present in the cache already, then return false. if (descriptorNames.containsKey(key) == false) { throw new IllegalArgumentException( JaiI18N.formatMsg("DescriptorCache1", new Object[] {descriptorName, modeName})); } RegistryElementDescriptor rdesc = (RegistryElementDescriptor)descriptorNames.get(key); PropertyGenerator props[] = null; // if properties arent supported by this descriptor we are done. if (rdesc.arePropertiesSupported() == true) props = rdesc.getPropertyGenerators(modeName); // Remove the Property Generators associated with this descriptor if (props != null) { for (int i=0; iRegistryElementDescriptor from the cache. * * @param rdesc an RegistryElementDescriptor to be removed. * * @return false, if one wasnt previously registered, true otherwise. * * @throws IllegalArgumentException if rdesc is null. * @throws IllegalArgumentException if rdesc was not * previously registered. * @throws IllegalArgumentException if any of the * PropertyGenerators associated with the * RegistryElementDescriptor to be removed is null. */ boolean removeDescriptor(RegistryElementDescriptor rdesc) { if (rdesc == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return removeDescriptor(rdesc.getName()); } /** * Returns the RegistryElementDescriptor that is * currently registered under the given name, or null if none * exists. * * @param descriptorName the String to be queried. * * @return an RegistryElementDescriptor. * * @throws IllegalArgumentException if descriptorName is null */ RegistryElementDescriptor getDescriptor(String descriptorName) { // Use a caseless version of the key. CaselessStringKey key = new CaselessStringKey(descriptorName); return (RegistryElementDescriptor)descriptorNames.get(key); } /** * Returns a List of all currently registered * RegistryElementDescriptors. * * @return a List of RegistryElementDescriptors. */ List getDescriptors() { ArrayList list = new ArrayList(); for (Enumeration en = descriptorNames.elements(); en.hasMoreElements(); ) { list.add(en.nextElement()); } return list; } /** * Returns a list of names under which all the * RegistryElementDescriptors in the registry are registered. * * @return a list of currently existing descriptor names. */ String[] getDescriptorNames() { Enumeration e = descriptorNames.keys(); int size = descriptorNames.size(); String names[] = new String[size]; for (int i = 0; i < size; i++) { CaselessStringKey key = (CaselessStringKey)e.nextElement(); names[i] = key.getName(); } return names; } /** * Registers a product name against a descriptor. The descriptor * must already exist in the cache. If the product already existed * under the descriptor, the old one is returned without adding * another. * * @param descriptorName the descriptor name as a String * @param productName the product name as a String. * * @return null, if the descriptor or the product did not exist or the product * * @throws IllegalArgumentException if descriptorName is null */ OperationGraph addProduct(String descriptorName, String productName) { // Use a caseless version of the key. CaselessStringKey key = new CaselessStringKey(descriptorName); if (productName == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); ProductOperationGraph pog = (ProductOperationGraph)products.get(key); if (pog == null) return null; PartialOrderNode pon = pog.lookupOp(productName); if (pon == null) { pog.addProduct(productName); pon = pog.lookupOp(productName); } return (OperationGraph)pon.getData(); } /** * Unregisters a product name against a descriptor. The * descriptor must already exist in the cache and the procduct * must have been registered against the descriptor * * @param descriptorName the descriptor name as a String * @param productName the product name as a String. * * @return false, if the descriptor did not exist or the product * was not already registered. * * @throws IllegalArgumentException if descriptorName is null */ boolean removeProduct(String descriptorName, String productName) { // Use a caseless version of the key. CaselessStringKey key = new CaselessStringKey(descriptorName); if (productName == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); ProductOperationGraph pog = (ProductOperationGraph)products.get(key); if (pog == null) return false; PartialOrderNode pon = pog.lookupOp(productName); if (pon == null) return false; pog.removeOp(productName); return true; } /** * Looks up a product name against a descriptor. * * @param descriptorName the descriptor name as a String * @param productName the product name as a String. * * @return null, if the descriptor did not exist or the product * was not already registered. Otherwise returns the * PartialOrderNode corresponding to this product * * @throws IllegalArgumentException if descriptorName is null */ OperationGraph lookupProduct(String descriptorName, String productName) { // Use a caseless version of the key. CaselessStringKey key = new CaselessStringKey(descriptorName); if (productName == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); ProductOperationGraph pog = (ProductOperationGraph)products.get(key); if (pog == null) return null; PartialOrderNode pon = pog.lookupOp(productName); if (pon == null) return null; return (OperationGraph)pon.getData(); } /** * Sets a preference between two products registered under * a common RegistryElementDescriptor. * if the descriptor was not registered previously and no preference * will be set. Any attempt to set a preference between a product * and itself will be ignored. * * @param descriptorName the operation name as a String. * @param preferredProductName the product to be preferred. * @param otherProductName the other product. * * @return false, if the descriptor was not registered previously * or if either if the products were not already * added against the descriptor. * * @throws IllegalArgumentException if this registry mode does * not support preferences. * @throws IllegalArgumentException if any of the args is null * @throws IllegalArgumentException if descriptorName or either of * the products were not previously registered. */ boolean setProductPreference(String descriptorName, String preferredProductName, String otherProductName) { if (!arePreferencesSupported) { throw new IllegalArgumentException( JaiI18N.formatMsg("DescriptorCache6", new Object[] {modeName})); } if ((descriptorName == null) || (preferredProductName == null) || (otherProductName == null)) throw new IllegalArgumentException( JaiI18N.getString("Generic0")); // Attempt to set preference of a product with itself, do nothing. if (preferredProductName.equalsIgnoreCase(otherProductName)) { return false; } // Use a caseless version of the key. CaselessStringKey key = new CaselessStringKey(descriptorName); if (descriptorNames.containsKey(key) == false) { throw new IllegalArgumentException( JaiI18N.formatMsg("DescriptorCache1", new Object[] {descriptorName, modeName})); } ProductOperationGraph og = (ProductOperationGraph)products.get(key); if (og == null) { throw new IllegalArgumentException( JaiI18N.formatMsg("DescriptorCache3", new Object[] {descriptorName, modeName})); } if (og.lookupOp(preferredProductName) == null) { throw new IllegalArgumentException( JaiI18N.formatMsg("DescriptorCache4", new Object[] {descriptorName, modeName, preferredProductName})); } if (og.lookupOp(otherProductName) == null) { throw new IllegalArgumentException( JaiI18N.formatMsg("DescriptorCache4", new Object[] {descriptorName, modeName, otherProductName})); } og.setPreference(preferredProductName, otherProductName); String[] prefs = { preferredProductName, otherProductName }; // Update structures to reflect this new product preference. if (productPrefs.containsKey(key) == false) { Vector v = new Vector(); v.addElement(prefs); productPrefs.put(key, v); } else { Vector v = (Vector)productPrefs.get(key); v.addElement(prefs); } return true; } /** * Removes a preference between two products registered under * a common RegistryElementDescriptor. An error message will * be printed out if the operation was not registered previously. * * @param descriptorName the operation name as a String. * @param preferredProductName the product formerly preferred. * @param otherProductName the other product. * * @return false, if the descriptor was not registered previously * or if either if the products were not already * added against the descriptor. * * @throws IllegalArgumentException if this registry mode does * not support preferences. * @throws IllegalArgumentException if any of the args is null * @throws IllegalArgumentException if descriptorName or either of * the products were not previously registered. */ boolean unsetProductPreference(String descriptorName, String preferredProductName, String otherProductName) { if (!arePreferencesSupported) { throw new IllegalArgumentException( JaiI18N.formatMsg("DescriptorCache6", new Object[] {modeName})); } if ((descriptorName == null) || (preferredProductName == null) || (otherProductName == null)) throw new IllegalArgumentException( JaiI18N.getString("Generic0")); // Attempt to unset preference of a product with itself, do nothing. if (preferredProductName.equalsIgnoreCase(otherProductName)) { return false; } // Use a caseless version of the key. CaselessStringKey key = new CaselessStringKey(descriptorName); if (descriptorNames.containsKey(key) == false) { throw new IllegalArgumentException( JaiI18N.formatMsg("DescriptorCache1", new Object[] {descriptorName, modeName})); } ProductOperationGraph og = (ProductOperationGraph)products.get(key); if (og == null) { throw new IllegalArgumentException( JaiI18N.formatMsg("DescriptorCache3", new Object[] {descriptorName, modeName})); } if (og.lookupOp(preferredProductName) == null) { throw new IllegalArgumentException( JaiI18N.formatMsg("DescriptorCache4", new Object[] {descriptorName, modeName, preferredProductName})); } if (og.lookupOp(otherProductName) == null) { throw new IllegalArgumentException( JaiI18N.formatMsg("DescriptorCache4", new Object[] {descriptorName, modeName, otherProductName})); } og.unsetPreference(preferredProductName, otherProductName); // Update structures to reflect removal of this product preference. if (productPrefs.containsKey(key) == false) { throw new IllegalArgumentException( JaiI18N.formatMsg("DescriptorCache5", new Object[] {descriptorName, modeName})); } Vector v = (Vector)productPrefs.get(key); Iterator it = v.iterator(); while(it.hasNext()) { String[] prefs = (String[])it.next(); if (prefs[0].equalsIgnoreCase(preferredProductName) && prefs[1].equalsIgnoreCase(otherProductName)) { it.remove(); break; } } return true; } /** * Removes all preferences between products registered under * a common RegistryElementDescriptor. An error message will * be printed out if the operation was not registered previously. * * @param descriptorName the operation name as a String. * * @throws IllegalArgumentException if this registry mode does * not support preferences. * @throws IllegalArgumentException if descriptorName is null. */ boolean clearProductPreferences(String descriptorName) { if (!arePreferencesSupported) { throw new IllegalArgumentException( JaiI18N.formatMsg("DescriptorCache6", new Object[] {modeName})); } // Use a caseless version of the key. CaselessStringKey key = new CaselessStringKey(descriptorName); if (descriptorNames.containsKey(key) == false) { throw new IllegalArgumentException( JaiI18N.formatMsg("DescriptorCache1", new Object[] {descriptorName, modeName})); } ProductOperationGraph og = (ProductOperationGraph)products.get(key); if (og == null) { throw new IllegalArgumentException( JaiI18N.formatMsg("DescriptorCache3", new Object[] {descriptorName, modeName})); } // if there are no preferences to clear.. if (productPrefs.containsKey(key) == false) return true; Vector v = (Vector)productPrefs.get(key); Enumeration e = v.elements(); while(e.hasMoreElements()) { String prefs[] = (String[])e.nextElement(); String pref = prefs[0]; String other = prefs[1]; if (og.lookupOp(pref) == null) { throw new IllegalArgumentException( JaiI18N.formatMsg("DescriptorCache4", new Object[] {descriptorName, modeName, pref})); } if (og.lookupOp(other) == null) { throw new IllegalArgumentException( JaiI18N.formatMsg("DescriptorCache4", new Object[] {descriptorName, modeName, other})); } og.unsetPreference(pref, other); } productPrefs.remove(key); return true; } /** * Returns a list of the pairwise product preferences * under a particular RegistryElementDescriptor. If no product * preferences have been set, returns null. * * @param descriptorName the operation name as a String. * @return an array of 2-element arrays of Strings. * * @throws IllegalArgumentException if this registry mode does * not support preferences. * @throws IllegalArgumentException if descriptorName is null */ String[][] getProductPreferences(String descriptorName) { if (!arePreferencesSupported) { throw new IllegalArgumentException( JaiI18N.formatMsg("DescriptorCache6", new Object[] {modeName})); } // Use a caseless version of the key. CaselessStringKey key = new CaselessStringKey(descriptorName); Vector v; if (productPrefs.containsKey(key) == false) { // No product preferences have been set. return null; } else { v = (Vector)productPrefs.get(key); int s = v.size(); if (s == 0) { return null; } String productPreferences[][] = new String[s][2]; int count = 0; Enumeration e = v.elements(); while(e.hasMoreElements()) { String[] o = (String[])e.nextElement(); productPreferences[count][0] = o[0]; productPreferences[count++][1] = o[1]; } return productPreferences; } } /** * Returns a list of the products registered under a particular * RegistryElementDescriptor, in an ordering that * satisfies all of the pairwise preferences that have been * set. Returns null if cycles exist. Returns * null if no RegistryElementDescriptor * has been registered under this descriptorName, or if no products * exist for this operation. * * @param descriptorName the operation name as a String. * * @return a Vector of Strings representing product names. returns * null if this registry mode does not support preferences. * * @throws IllegalArgumentException if descriptorName is null. */ Vector getOrderedProductList(String descriptorName) { if (!arePreferencesSupported) return null; // Use a caseless version of the key. CaselessStringKey key = new CaselessStringKey(descriptorName); if (descriptorNames.containsKey(key) == false) { return null; } ProductOperationGraph productGraph = (ProductOperationGraph)products.get(key); // If no products exist under this Operation Name if (productGraph == null) { return null; } // Get the ordered vector of PartialOrderNodes Vector v1 = productGraph.getOrderedOperationList(); if (v1 == null) return null; int size = v1.size(); if (size == 0) { // no element return null; } else { Vector v2 = new Vector(); for (int i = 0; i < size; i++) { v2.addElement(((PartialOrderNode)v1.elementAt(i)).getName()); } return v2; } } // Property management private boolean arePropertiesSupported(String descriptorName) { CaselessStringKey key = new CaselessStringKey(descriptorName); RegistryElementDescriptor rdesc = (RegistryElementDescriptor)descriptorNames.get(key); if (rdesc == null) { throw new IllegalArgumentException( JaiI18N.formatMsg("DescriptorCache1", new Object[] {descriptorName, modeName})); } return arePropertiesSupported; } /** * Removes all property associated information from this * DescriptorCache. */ void clearPropertyState() { if (arePropertiesSupported == false) { throw new IllegalArgumentException( JaiI18N.formatMsg("DescriptorCache7", new Object[] {modeName})); } properties = new Hashtable(); suppressed = new Hashtable(); } /** * Adds a PropertyGenerator to the a particular * descriptor. * * @param descriptorName the operation name as a String. * @param generator the PropertyGenerator to be added. */ void addPropertyGenerator(String descriptorName, PropertyGenerator generator) { if ((descriptorName == null) || (generator == null)) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); if (arePropertiesSupported(descriptorName) == false) { throw new IllegalArgumentException( JaiI18N.formatMsg("DescriptorCache7", new Object[] {modeName})); } CaselessStringKey key = new CaselessStringKey(descriptorName); Vector v = (Vector)properties.get(key); if (v == null) { v = new Vector(); properties.put(key, v); } v.addElement(generator); v = (Vector)suppressed.get(key); Hashtable h = (Hashtable)sourceForProp.get(key); String names[] = generator.getPropertyNames(); for (int j=0; jPropertyGenerator from its association * with a particular descriptor. If the generator was not associated * with the descriptor, nothing happens. * * @param descriptorName the operation name as a String. * @param generator the PropertyGenerator to be removed. * * @throws IllegalArgumentException if descriptorName is null. * @throws IllegalArgumentException if generator is null. */ void removePropertyGenerator(String descriptorName, PropertyGenerator generator) { if ((descriptorName == null) || (generator == null)) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (arePropertiesSupported(descriptorName) == false) { throw new IllegalArgumentException( JaiI18N.formatMsg("DescriptorCache7", new Object[] {modeName})); } CaselessStringKey key = new CaselessStringKey(descriptorName); Vector v = (Vector)properties.get(key); if (v != null) { v.removeElement(generator); } } /** * Forces a particular property to be suppressed by nodes associated * with a particular descriptor. By default, properties are passed * through unchanged. * * @param descriptorName the operation name as a String. * @param propertyName the name of the property to be suppressed. * * @throws IllegalArgumentException if descriptorName is null. * @throws IllegalArgumentException if propertyName is null. */ void suppressProperty(String descriptorName, String propertyName) { if ((descriptorName == null) || (propertyName == null)) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (arePropertiesSupported(descriptorName) == false) { throw new IllegalArgumentException( JaiI18N.formatMsg("DescriptorCache7", new Object[] {modeName})); } CaselessStringKey key = new CaselessStringKey(descriptorName); CaselessStringKey propertyKey = new CaselessStringKey(propertyName); // Mark the property name as suppressed. Vector v = (Vector)suppressed.get(key); if (v == null) { v = new Vector(); suppressed.put(key, v); } v.addElement(propertyKey); Hashtable h = (Hashtable)sourceForProp.get(key); if (h != null) { h.remove(propertyKey); } } /** * Forces all properties to be suppressed by nodes associated with a * particular descriptor. By default, properties are passed through * unchanged. * * @param descriptorName the operation name as a String. * * @throws IllegalArgumentException if descriptorName is null. */ void suppressAllProperties(String descriptorName) { if (arePropertiesSupported(descriptorName) == false) { throw new IllegalArgumentException( JaiI18N.formatMsg("DescriptorCache7", new Object[] {modeName})); } // In this method synchronized takes care of the fact that all the // operations take place in a sequential fashion, while // suppressProperty's writeLock insures that all changes are // made by only one thread. CaselessStringKey key = new CaselessStringKey(descriptorName); // Get names of all properties that this descriptorName // is associated with Vector v = (Vector)properties.get(key); if (v != null) { PropertyGenerator pg; for (Iterator it = v.iterator(); it.hasNext(); ) { pg = (PropertyGenerator)it.next(); String propertyNames[] = pg.getPropertyNames(); for (int i=0; i 0) { String names[] = new String[h.size()]; int count = 0; for (Enumeration e = h.keys(); e.hasMoreElements(); ) { CaselessStringKey str = (CaselessStringKey)e.nextElement(); names[count++] = str.getName(); } return count > 0 ? names : null; } return null; } /** * Merge mode-specific property environment with mode-independent * property environment of the descriptor. Array elements of * "sources" are expected to be in the same ordering as referenced * by the "sourceIndex" parameter of copyPropertyFromSource(). * * @param descriptorName the descriptor name as a String * @param sources the PropertySources corresponding to * the sources of the object representing the named descriptor * in the indicated mode. * @param op the Object from which properties will * be generated. * * @return A PropertySource which encapsulates * the global property environment for the object representing * the named descriptor in the indicated mode. * * @throws IllegalArgumentException if any of the arguments * is null * @throws IllegalArgumentException if there is no * RegistryElementDescriptor registered against * the descriptorName * @throws IllegalArgumentException if the specified mode does not * support properties. * * @since JAI 1.1 */ PropertySource getPropertySource(String descriptorName, Object op, Vector sources) { if ((descriptorName == null) || (op == null) || (sources == null)) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (arePropertiesSupported(descriptorName) == false) { throw new IllegalArgumentException( JaiI18N.formatMsg("DescriptorCache7", new Object[] {modeName})); } CaselessStringKey key = new CaselessStringKey(descriptorName); Vector pg = (Vector)properties.get(key); Vector sp = (Vector)suppressed.get(key); Hashtable sfp = (Hashtable)sourceForProp.get(key); return new PropertyEnvironment(sources, pg, sp, sfp, op); } } jai-core-1.1.4/src/share/classes/javax/media/jai/InterpolationTable.java0000644000175000017500000017345510203035544026016 0ustar mathieumathieu/* * $RCSfile: InterpolationTable.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:11 $ * $State: Exp $ */ package javax.media.jai; /** * A subclass of Interpolation that uses tables to store the * interpolation kernels. The set of subpixel positions is broken up * into a fixed number of "bins" and a distinct kernel is used for * each bin. The number of bins must be a power of two. * *

    An InterpolationTable defines a separable interpolation, with a * set of kernels for each dimension. The number of bins may vary * between the two dimensions. Both the horizontal and vertical * interpolation kernels have a "key" element. This element is positioned * over the which The kernels are stored in double precision, * floating- and fixed-point form. The fixed point representation has * a user-specified fractional precision. It is the user's * responsibility to specify an appropriate level of precision that * will not cause overflow when accumulating the results of a * convolution against a set of source pixels, using 32-bit integer * arithmetic. */ public class InterpolationTable extends Interpolation { /** The number of fractional bits used to describe filter coefficients. */ protected int precisionBits; /** The scaled (by 2precisionBits) value of 0.5 for rounding */ private int round; /** The number of horizontal subpixel positions within a pixel. */ private int numSubsamplesH; /** The number of vertical subpixel positions within a pixel. */ private int numSubsamplesV; /** The horizontal coefficient data in double format. */ protected double[] dataHd; /** The vertical coefficient data in double format. */ protected double[] dataVd; /** The horizontal coefficient data in floating-point format. */ protected float[] dataHf; /** The vertical coefficient data in floating-point format. */ protected float[] dataVf; /** The horizontal coefficient data in fixed-point format. */ protected int[] dataHi; /** The vertical coefficient data in fixed-point format. */ protected int[] dataVi; /** * Constructs an InterpolationTable with specified horizontal and * vertical extents (support), number of horizontal and vertical * bins, fixed-point fractional precision, and int kernel entries. * The kernel data values are organized as * 2subsampleBits entries each * containing width ints. * *

    dataH and dataV are required to contain width * 2subsampleBitsH * and height * 2subsampleBitsV entries respectively, otherwise * an IllegalArgumentException will be thrown. * *

    If dataV is null, it is assumed to be a copy of dataH * and the keyY, height, and subsampleBitsV parameters * are ignored. * * @param keyX The array offset of the horizontal resampling kernel center * @param keyY The array offset of the vertical resampling kernel center * @param width the width of a horizontal resampling kernel. * @param height the height of a vertical resampling kernel. Ignored * if dataV is null. * @param subsampleBitsH the log (base 2) of the number of horizontal * subsample positions. Must be positive. * @param subsampleBitsV the log (base 2) of the number of vertical * subsample positions. Must be positive. Ignored if dataV is null. * @param precisionBits the number of bits of fractional precision * to be used when resampling integral sample values. Must be positive. * The same value is used for both horizontal and vertical * resampling. * @param dataH the horizontal table entries, as an int array of * 2subsampleBitsH entries each of length width. The array is cloned internally. * @param dataV the vertical table entries, as an int array of * 2subsampleBitsV entries each of length height, or null. The array is cloned internally. * If null, the dataH table is used for vertical interpolation * as well and the keyY, height, and subsampleBitsV * parameters are ignored. * @throws IllegalArgumentException if the size of the data arrays * are incorrect. */ public InterpolationTable(int keyX, int keyY, int width, int height, int subsampleBitsH, int subsampleBitsV, int precisionBits, int[] dataH, int[] dataV) { // dataH has width*2^subsampleBitsH entries // dataV has height*2^subsampleBitsV entries super(); this.leftPadding = keyX; this.topPadding = keyY; this.width = width; this.rightPadding = width - keyX - 1; this.precisionBits = precisionBits; if (precisionBits > 0) { round = 1 << (precisionBits - 1); } this.subsampleBitsH = subsampleBitsH; this.numSubsamplesH = (1 << subsampleBitsH); int entriesH = width*numSubsamplesH; if (dataH.length != entriesH) { throw new IllegalArgumentException(JaiI18N.getString("InterpolationTable0")); } double prec = (double)(1 << precisionBits); int i; this.dataHi = (int[])dataH.clone(); this.dataHf = new float[entriesH]; this.dataHd = new double[entriesH]; for (i = 0; i < entriesH; i++) { double d = (double)dataHi[i] / prec; this.dataHf[i] = (float)d; this.dataHd[i] = d; } if (dataV != null) { this.height = height; this.subsampleBitsV = subsampleBitsV; this.numSubsamplesV = (1 << subsampleBitsV); int entriesV = height*numSubsamplesV; if (dataV.length != entriesV) { throw new IllegalArgumentException(JaiI18N.getString("InterpolationTable1")); } this.dataVi = (int[])dataV.clone(); this.dataVf = new float[entriesV]; this.dataVd = new double[entriesV]; for (i = 0; i < entriesV; i++) { double d = (double)dataVi[i] / prec; this.dataVf[i] = (float)d; this.dataVd[i] = d; } } else { this.height = width; this.subsampleBitsV = subsampleBitsH; this.numSubsamplesV = numSubsamplesH; this.dataVf = dataHf; this.dataVi = dataHi; this.dataVd = dataHd; } this.bottomPadding = this.height - keyY - 1; } /** * Constructs an InterpolationTable with identical horizontal and * vertical resampling kernels. * * @param key The array offset of the central sample to be used during resampling. * @param width the width or height of a resampling kernel. * @param subsampleBits the log (base 2) of the number of * subsample positions. Must be positive. * @param precisionBits the number of bits of fractional precision * to be used when resampling integral sample values. Must be positive. * @param data the kernel entries, as an int array of * width*2subsampleBits entries */ public InterpolationTable(int key, int width, int subsampleBits, int precisionBits, int[] data) { this(key, key, width, width, subsampleBits, subsampleBits, precisionBits, data, null); } /** * Constructs an InterpolationTable with specified horizontal and * vertical extents (support), number of horizontal and vertical * bins, fixed-point fractional precision, and float kernel entries. * The kernel data values are organized as 2subsampleBits entries each * containing width floats. * *

    dataH and dataV are required to contain width * 2subsampleBitsH * and height * 2subsampleBitsV entries respectively, otherwise * an IllegalArgumentException will be thrown. * *

    If dataV is null, it is assumed to be a copy of dataH * and the keyY, height, and subsampleBitsV parameters * are ignored. * * @param keyX The array offset of the horizontal resampling kernel center * @param keyY The array offset of the vertical resampling kernel center * @param width the width of a horizontal resampling kernel. * @param height the height of a vertical resampling kernel. Ignored * if dataV is null. * @param subsampleBitsH the log (base 2) of the number of horizontal * subsample positions. Must be positive. * @param subsampleBitsV the log (base 2) of the number of vertical * subsample positions. Must be positive. Ignored if dataV is null. * @param precisionBits the number of bits of fractional precision * to be used when resampling integral sample values. * The same value is used for both horizontal and vertical * resampling. Must be positive. * @param dataH the horizontal table entries, as a float array of * 2subsampleBitsH entries each of length width. * @param dataV the vertical table entries, as a float array of * 2subsampleBitsV entries each of length height, or null. * If null, the dataH table is used for vertical interpolation * as well and the keyY, height, and subsampleBitsV * parameters are ignored. * @throws IllegalArgumentException if the size of the data arrays * are incorrect. */ public InterpolationTable(int keyX, int keyY, int width, int height, int subsampleBitsH, int subsampleBitsV, int precisionBits, float[] dataH, float[] dataV) { // dataH has width*2^subsampleBitsH entries // dataV has height*2^subsampleBitsV entries super(); this.leftPadding = keyX; this.topPadding = keyY; this.width = width; this.rightPadding = width - keyX - 1; this.precisionBits = precisionBits; if (precisionBits > 0) { round = 1 << (precisionBits - 1); } this.subsampleBitsH = subsampleBitsH; this.numSubsamplesH = (1 << subsampleBitsH); int entriesH = width*numSubsamplesH; if (dataH.length != entriesH) { throw new IllegalArgumentException(JaiI18N.getString("InterpolationTable0")); } float prec = (float)(1 << precisionBits); int i; this.dataHf = (float[])dataH.clone(); this.dataHi = new int[entriesH]; this.dataHd = new double[entriesH]; for (i = 0; i < entriesH; i++) { float f = dataHf[i]; this.dataHi[i] = Math.round(f * prec); this.dataHd[i] = f; } if (dataV != null) { this.height = height; this.subsampleBitsV = subsampleBitsV; this.numSubsamplesV = (1 << subsampleBitsV); int entriesV = height*numSubsamplesV; if (dataV.length != entriesV) { throw new IllegalArgumentException(JaiI18N.getString("InterpolationTable1")); } this.dataVf = (float[])dataV.clone(); this.dataVi = new int[entriesV]; this.dataVd = new double[entriesV]; for (i = 0; i < entriesV; i++) { float f = dataVf[i]; this.dataVi[i] = Math.round(f * prec); this.dataVd[i] = f; } } else { this.height = width; this.subsampleBitsV = subsampleBitsH; this.numSubsamplesV = numSubsamplesH; this.dataVf = dataHf; this.dataVi = dataHi; this.dataVd = dataHd; } this.bottomPadding = this.height - keyY - 1; } /** * Constructs an InterpolationTable with identical horizontal and * vertical resampling kernels. * * @param key The number of samples to the left or above the * central sample to be used during resampling. * @param width the width or height of a resampling kernel. * @param subsampleBits the log (base 2) of the number of * subsample positions. Must be positive. * @param precisionBits the number of bits of fractional precision * to be used when resampling integral sample values. Must be positive. * @param data the kernel entries, as a float array of * width*2subsampleBits entries */ public InterpolationTable(int key, int width, int subsampleBits, int precisionBits, float[] data) { this(key, key, width, width, subsampleBits, subsampleBits, precisionBits, data, null); } /** * Constructs an InterpolationTable with specified horizontal and * vertical extents (support), number of horizontal and vertical * bins, fixed-point fractional precision, and double kernel entries. * The kernel data values are organized as 2subsampleBits entries each * containing width doubles. * *

    dataH and dataV are required to contain width * 2subsampleBitsH * and height * 2subsampleBitsV entries respectively, otherwise * an IllegalArgumentException will be thrown. * *

    If dataV is null, it is assumed to be a copy of dataH * and the keyY, height, and subsampleBitsV parameters * are ignored. * * @param keyX The array offset of the horizontal resampling kernel center * @param keyY The array offset of the vertical resampling kernel center * @param width the width of a horizontal resampling kernel. * @param height the height of a vertical resampling kernel. Ignored * if dataV is null. * @param subsampleBitsH the log (base 2) of the number of horizontal * subsample positions. Must be positive. * @param subsampleBitsV the log (base 2) of the number of vertical * subsample positions. Must be positive. Ignored if dataV is null. * @param precisionBits the number of bits of fractional precision * to be used when resampling integral sample values. * The same value is used for both horizontal and vertical * resampling. Must be positive. * @param dataH the horizontal table entries, as a double array of * 2subsampleBitsH entries each of length width. * @param dataV the vertical table entries, as a double array of * 2subsampleBitsV entries each of length height, or null. * If null, the dataH table is used for vertical interpolation * as well and the keyY, height, and subsampleBitsV * parameters are ignored. */ public InterpolationTable(int keyX, int keyY, int width, int height, int subsampleBitsH, int subsampleBitsV, int precisionBits, double[] dataH, double[] dataV) { // dataH has width*2^subsampleBitsH entries // dataV has height*2^subsampleBitsV entries super(); this.leftPadding = keyX; this.topPadding = keyY; this.width = width; this.rightPadding = width - keyX - 1; this.precisionBits = precisionBits; if (precisionBits > 0) { round = 1 << (precisionBits - 1); } this.subsampleBitsH = subsampleBitsH; this.numSubsamplesH = (1 << subsampleBitsH); int entriesH = width*numSubsamplesH; if (dataH.length != entriesH) { throw new IllegalArgumentException(JaiI18N.getString("InterpolationTable0")); } double prec = (double)(1 << precisionBits); int i; this.dataHd = (double[])dataH.clone(); this.dataHi = new int[entriesH]; this.dataHf = new float[entriesH]; for (i = 0; i < entriesH; i++) { double d = dataHd[i]; this.dataHi[i] = (int)Math.round(d*prec); this.dataHf[i] = (float)d; } if (dataV != null) { this.height = height; this.subsampleBitsV = subsampleBitsV; this.numSubsamplesV = (1 << subsampleBitsV); int entriesV = height*numSubsamplesV; if (dataV.length != entriesV) { throw new IllegalArgumentException(JaiI18N.getString("InterpolationTable1")); } this.dataVd = (double[])dataV.clone(); this.dataVi = new int[entriesV]; this.dataVf = new float[entriesV]; for (i = 0; i < entriesV; i++) { double d = dataVd[i]; this.dataVi[i] = (int)Math.round(d * prec); this.dataVf[i] = (float)d; } } else { this.height = width; this.subsampleBitsV = subsampleBitsH; this.numSubsamplesV = numSubsamplesH; this.dataVd = dataHd; this.dataVf = dataHf; this.dataVi = dataHi; } this.bottomPadding = this.height - keyY - 1; } /** * Constructs an InterpolationTable with identical horizontal and * vertical resampling kernels. * * @param key The number of samples to the left or above the * central sample to be used during resampling. * @param width the width or height of a resampling kernel. * @param subsampleBits the log (base 2) of the number of * subsample positions. Must be positive. * @param precisionBits the number of bits of fractional precision * to be used when resampling integral sample values. Must be positive. * @param data the kernel entries, as a double array of * width*2subsampleBitsH entries */ public InterpolationTable(int key, int width, int subsampleBits, int precisionBits, double[] data) { this(key, key, width, width, subsampleBits, subsampleBits, precisionBits, data, null); } /** * Returns the number of bits of fractional precision used to * store the fixed-point table entries. */ public int getPrecisionBits() { return precisionBits; } /** * Returns the integer (fixed-point) horizontal table data. The * output is an int array of length * getWidth() * 2getSubsampleBitsH(). * *

    The following code, given an instance interp * of class InterpolationTable, will perform * interpolation of a set of getWidth() samples * at a given fractional position (bin) xfrac * between 0 and 2getSubsampleBitsH() - 1: * *

         * int interpolateH(InterpolationTable interp, int[] samples, int xfrac) {
         *     int[] dataH = interp.getHorizontalTableData();
         *     int precisionBits = interp.getPrecisionBits();
         *     int round = 1 << (precisionBits - 1);
         *     int width = interp.getWidth();
         *     int offset = width*xfrac;
         *
         *     int sum = 0;
         *     for (int i = 0; i < width; i++) {
         *         sum += dataH[offset + i]*samples[i];
         *     }
         *     return (sum + round) >> precisionBits;
         * }
         * 
    * *

    In practice, the values dataH, * precisionBits, etc., may be extracted once and * reused to interpolate multiple output pixels. * * @return An array of ints. */ public int[] getHorizontalTableData() { return dataHi; } /** * Returns the integer (fixed-point) vertical table data. The * output is an int array of length * getHeight() * 2getSubsampleBitsV(). * *

    The following code, given an instance interp * of class InterpolationTable, will perform * interpolation of a set of getHeight() samples * at a given fractional position (bin) yfrac * between 0 and 2getSubsampleBitsV() - 1: * *

         * int interpolateV(InterpolationTable interp, int[] samples, int yfrac) {
         *     int[] dataV = interp.getVerticalTableData();
         *     int precisionBits = interp.getPrecisionBits();
         *     int round = 1 << (precisionBits - 1);
         *     int height = interp.getHeight();
         *     int offset = height*yfrac;
         *
         *     int sum = 0;
         *     for (int i = 0; i < height; i++) {
         *         sum += dataV[offset + i]*samples[i];
         *     }
         *     return (sum + round) >> precisionBits;
         * }
         * 
    * *

    In practice, the values dataV, * precisionBits, etc., may be extracted once and * reused to interpolate multiple output pixels. * * @return An array of ints. */ public int[] getVerticalTableData() { return dataVi; } /** * Returns the floating-point horizontal table data. The output is a * float array of length * getWidth() * 2getSubsampleBitsH(). * *

    The following code, given an instance interp * of class InterpolationTable, will perform * interpolation of a set of getWidth() * floating-point samples at a given fractional position * xfrac between 0.0F and 1.0F: * *

         * float interpolateH(InterpolationTable interp,
         *                    float[] samples, float xfrac) {
         *     float[] dataH = interp.getHorizontalTableDataFloat();
         *     int width = interp.getWidth();
         *     int numSubsamplesH = 1 << getSubsampleBitsH();
         *     int ifrac = (int)(xfrac*numSubsamplesH);
         *     int offset = width*ifrac;
         *
         *     float sum = 0.0F;
         *     for (int i = 0; i < width; i++) {
         *         sum += dataH[offset + i]*samples[i];
         *     }
         *     return sum;
         * }
         * 
    * *

    In practice, the values dataH, * numSubsamplesH, etc., may be extracted once and * reused to interpolate multiple output pixels. * * @return An array of floats. */ public float[] getHorizontalTableDataFloat() { return dataHf; } /** * Returns the floating-point vertical table data. The output is a * float array of length * getWidth() * 2getSubsampleBitsV(). * *

    The following code, given an instance interp * of class InterpolationTable, will perform * interpolation of a set of getHeight() * floating-point samples at a given fractional position * yfrac between 0.0F and 1.0F: * *

         * float interpolateV(InterpolationTable interp,
         *                    float[] samples, float yfrac) {
         *     float[] dataV = interp.getVerticalTableDataFloat();
         *     int height = interp.getHeight();
         *     int numSubsamplesV = 1 << getSubsampleBitsV();
         *     int ifrac = (int)(yfrac*numSubsamplesV);
         *     int offset = height*ifrac;
         *
         *     float sum = 0.0F;
         *     for (int i = 0; i < height; i++) {
         *         sum += dataV[offset + i]*samples[i];
         *     }
         *     return sum;
         * }
         * 
    * *

    In practice, the values dataV, * numSubsamplesV, etc., may be extracted once and * reused to interpolate multiple output pixels. * * @return An array of floats. */ public float[] getVerticalTableDataFloat() { return dataVf; } /** * Returns the double horizontal table data. The output is a * double array of length * getWidth() * 2getSubsampleBitsH(). * *

    The following code, given an instance interp * of class InterpolationTable, will perform * interpolation of a set of getWidth() * double samples at a given fractional position * xfrac between 0.0F and 1.0F: * *

         * double interpolateH(InterpolationTable interp,
         *                     double[] samples, float xfrac) {
         *     double[] dataH = interp.getHorizontalTableDataDouble();
         *     int width = interp.getWidth();
         *     int numSubsamplesH = 1 << getSubsampleBitsH();
         *     int ifrac = (int)(xfrac*numSubsamplesH);
         *     int offset = width*ifrac;
         *
         *     double sum = 0.0;
         *     for (int i = 0; i < width; i++) {
         *         sum += dataH[offset + i]*samples[i];
         *     }
         *     return sum;
         * }
         * 
    * *

    In practice, the values dataH, * numSubsamplesH, etc., may be extracted once and * reused to interpolate multiple output pixels. * * @return An array of doubles. */ public double[] getHorizontalTableDataDouble() { return dataHd; } /** * Returns the double vertical table data. The output is a * double array of length * getHeight() * 2getSubsampleBitsV()). * *

    The following code, given an instance interp * of class InterpolationTable, will perform * interpolation of a set of getHeight() * double samples at a given fractional position * yfrac between 0.0F and 1.0F: * *

         * double interpolateV(InterpolationTable interp,
         *                     double[] samples, float yfrac) {
         *     double[] dataV = interp.getVerticalTableDataDouble();
         *     int height = interp.getHeight();
         *     int numSubsamplesV = 1 << getSubsampleBitsV();
         *     int ifrac = (int)(yfrac*numSubsamplesV);
         *     int offset = height*ifrac;
         *
         *     double sum = 0.0;
         *     for (int i = 0; i < height; i++) {
         *         sum += dataV[offset + i]*samples[i];
         *     }
         *     return sum;
         * }
         * 
    * *

    In practice, the values dataV, * numSubsamplesV, etc., may be extracted once and * reused to interpolate multiple output pixels. * * @return An array of doubles. */ public double[] getVerticalTableDataDouble() { return dataVd; } /** * Performs horizontal interpolation on a one-dimensional array of * integral samples. * * If xfrac does not lie between 0 and 2subsampleBitsH-1, an * ArrayIndexOutOfBoundsException may occur, where width is the width * of the horizontal resampling kernel. * * @param samples an array of ints. * @param xfrac the subsample position, multiplied by 2subsampleBitsH. * @return the interpolated value as an int. * @throws ArrayIndexOutOfBoundsException if xfrac is out of bounds. */ public int interpolateH(int[] samples, int xfrac) { int sum = 0; int offset = width*xfrac; for (int i = 0; i < width; i++) { sum += dataHi[offset + i]*samples[i]; } return (sum + round) >> precisionBits; } /** * Performs vertical interpolation on a one-dimensional array of * integral samples. * * If yfrac does not lie between 0 and 2subsampleBitsV-1, an * ArrayIndexOutOfBoundsException may occur, where height is the * height of the vertical resampling kernel. * * @param samples an array of ints. * @param yfrac the Y subsample position, multiplied by 2subsampleBitsV. * @return the interpolated value as an int. * @throws ArrayIndexOutOfBoundsException if yfrac is out of bounds. */ public int interpolateV(int[] samples, int yfrac) { int sum = 0; int offset = width*yfrac; for (int i = 0; i < width; i++) { sum += dataVi[offset + i]*samples[i]; } return (sum + round) >> precisionBits; } /** * Performs horizontal interpolation on a pair of integral samples. * This method may be used instead of the array version for speed. * It should only be called if width == 2. * * If xfrac does not lie between 0 and 2subsampleBitsH-1, an * ArrayIndexOutOfBoundsException may occur, where width is the width * of the horizontal resampling kernel. * * @param s0 the central sample. * @param s1 the sample to the right of the central sample. * @param xfrac the subsample position, multiplied by 2subsampleBitsH. * @return the interpolated value as an int. * @throws ArrayIndexOutOfBoundsException if xfrac is out of bounds. */ public int interpolateH(int s0, int s1, int xfrac) { // Assume width == 2 int offset = 2*xfrac; int sum = dataHi[offset]*s0; sum += dataHi[offset + 1]*s1; return (sum + round) >> precisionBits; } /** * Performs horizontal interpolation on a quadruple of integral samples. * This method may be used instead of the array version for speed. * It should only be called if width == 4 and keyX == 1. * * If xfrac does not lie between 0 and 2subsampleBitsH-1, an * ArrayIndexOutOfBoundsException may occur, where width is the width * of the horizontal resampling kernel. * * @param s_ the sample to the left of the central sample. * @param s0 the central sample. * @param s1 the sample to the right of the central sample. * @param s2 the sample to the right of s1. * @param xfrac the subsample position, multiplied by 2subsampleBitsH. * @return the interpolated value as an int. * @throws ArrayIndexOutOfBoundsException if xfrac is out of bounds. */ public int interpolateH(int s_, int s0, int s1, int s2, int xfrac) { // Assume width == 4 int offset = 4*xfrac; int sum = dataHi[offset]*s_; sum += dataHi[offset + 1]*s0; sum += dataHi[offset + 2]*s1; sum += dataHi[offset + 3]*s2; return (sum + round) >> precisionBits; } /** * Performs vertical interpolation on a pair of integral samples. * This method may be used instead of the array version for speed. * It should only be called if height == 2 and keyY == 0. * * If yfrac does not lie between 0 and 2subsampleBitsV-1, an * ArrayIndexOutOfBoundsException may occur, where height is the * height of the vertical resampling kernel. * * @param s0 the central sample. * @param s1 the sample below the central sample. * @param yfrac the Y subsample position, multiplied by 2subsampleBitsV. * @return the interpolated value as an int. * @throws ArrayIndexOutOfBoundsException if yfrac is out of bounds. */ public int interpolateV(int s0, int s1, int yfrac) { // Assume width == 2 int offset = 2*yfrac; int sum = dataVi[offset]*s0; sum += dataVi[offset + 1]*s1; return (sum + round) >> precisionBits; } /** * Performs vertical interpolation on a quadruple of integral samples. * This method may be used instead of the array version for speed. * It should only be called if height == 4 and keyY == 1. * * If yfrac does not lie between 0 and 2subsampleBitsV-1, an * ArrayIndexOutOfBoundsException may occur, where height is the * height of the vertical resampling kernel. * * @param s_ the sample above the central sample. * @param s0 the central sample. * @param s1 the sample below the central sample. * @param s2 the sample below s1. * @param yfrac the Y subsample position, multiplied by 2subsampleBitsV. * @return the interpolated value as an int. * @throws ArrayIndexOutOfBoundsException if yfrac is out of bounds. */ public int interpolateV(int s_, int s0, int s1, int s2, int yfrac) { // Assume width == 4 int offset = 4*yfrac; int sum = dataVi[offset]*s_; sum += dataVi[offset + 1]*s0; sum += dataVi[offset + 2]*s1; sum += dataVi[offset + 3]*s2; return (sum + round) >> precisionBits; } /** * Performs interpolation on a 2x2 grid of integral samples. * It should only be called if width == height == 2 and * keyX == keyY == 0. * * If xfrac does not lie between 0 and 2subsampleBitsH-1, or * yfrac does not lie between 0 and 2subsampleBitsV-1, an * ArrayIndexOutOfBoundsException may occur, where width and height * are the width and height of the horizontal and vertical resampling * kernels respectively. * * @param s00 the central sample. * @param s01 the sample to the right of the central sample. * @param s10 the sample below the central sample. * @param s11 the sample below and to the right of the central sample. * @param xfrac the X subsample position, multiplied by 2subsampleBitsH. * @param yfrac the Y subsample position, multiplied by 2subsampleBitsV. * @return the interpolated value as an int. * @throws ArrayIndexOutOfBoundsException if xfrac or yfrac are out of bounds. */ public int interpolate(int s00, int s01, int s10, int s11, int xfrac, int yfrac) { // Interpolate in X int offsetX = 2*xfrac; int sum0 = dataHi[offsetX]*s00 + dataHi[offsetX + 1]*s01; int sum1 = dataHi[offsetX]*s10 + dataHi[offsetX + 1]*s11; // Intermediate rounding sum0 = (sum0 + round) >> precisionBits; sum1 = (sum1 + round) >> precisionBits; // Interpolate in Y int offsetY = 2*yfrac; int sum = dataVi[offsetY]*sum0 + dataVi[offsetY + 1]*sum1; return (sum + round) >> precisionBits; } /** * Performs interpolation on a 4x4 grid of integral samples. * It should only be called if width == height == 4 and * keyX == keyY == 1. * * If xfrac does not lie between 0 and 2subsampleBitsH-1, or * yfrac does not lie between 0 and 2subsampleBitsV-1, an * ArrayIndexOutOfBoundsException may occur, where width and height * are the the width and height of the horizontal and vertical * resampling kernels respectively. * * @param s__ the sample above and to the left of the central sample. * @param s_0 the sample above the central sample. * @param s_1 the sample above and one to the right of the central sample. * @param s_2 the sample above and two to the right of the central sample. * @param s0_ the sample to the left of the central sample. * @param s00 the central sample. * @param s01 the sample to the right of the central sample. * @param s02 the sample two to the right of the central sample. * @param s1_ the sample below and one to the left of the central sample. * @param s10 the sample below the central sample. * @param s11 the sample below and one to the right of the central sample. * @param s12 the sample below and two to the right of the central sample. * @param s2_ the sample two below and one to the left of the central sample. * @param s20 the sample two below the central sample. * @param s21 the sample two below and one to the right of the central sample. * @param s22 the sample two below and two to the right of the central sample. * @param xfrac the X subsample position, multiplied by 2subsampleBitsH. * @param yfrac the Y subsample position, multiplied by 2subsampleBitsV. * @return the interpolated value as an int. * @throws ArrayIndexOutOfBoundsException if xfrac or yfrac are out of bounds. */ public int interpolate(int s__, int s_0, int s_1, int s_2, int s0_, int s00, int s01, int s02, int s1_, int s10, int s11, int s12, int s2_, int s20, int s21, int s22, int xfrac, int yfrac) { // Interpolate in X int offsetX = 4*xfrac; int offsetX1 = offsetX + 1; int offsetX2 = offsetX + 2; int offsetX3 = offsetX + 3; long sum_ = (long)dataHi[offsetX]*s__; sum_ += (long)dataHi[offsetX1]*s_0; sum_ += (long)dataHi[offsetX2]*s_1; sum_ += (long)dataHi[offsetX3]*s_2; long sum0 = (long)dataHi[offsetX]*s0_; sum0 += (long)dataHi[offsetX1]*s00; sum0 += (long)dataHi[offsetX2]*s01; sum0 += (long)dataHi[offsetX3]*s02; long sum1 = (long)dataHi[offsetX]*s1_; sum1 += (long)dataHi[offsetX1]*s10; sum1 += (long)dataHi[offsetX2]*s11; sum1 += (long)dataHi[offsetX3]*s12; long sum2 = (long)dataHi[offsetX]*s2_; sum2 += (long)dataHi[offsetX1]*s20; sum2 += (long)dataHi[offsetX2]*s21; sum2 += (long)dataHi[offsetX3]*s22; // Intermediate rounding sum_ = (sum_ + round) >> precisionBits; sum0 = (sum0 + round) >> precisionBits; sum1 = (sum1 + round) >> precisionBits; sum2 = (sum2 + round) >> precisionBits; // Interpolate in Y int offsetY = 4*yfrac; long sum = (long)dataVi[offsetY]*sum_; sum += (long)dataVi[offsetY + 1]*sum0; sum += (long)dataVi[offsetY + 2]*sum1; sum += (long)dataVi[offsetY + 3]*sum2; return (int)((sum + round) >> precisionBits); } /** * Performs interpolation on a 4x4 grid of integral samples. All * internal calculations are performed in floating-point. * It should only be called if width == height == 4 and * keyX == keyY == 1. * * If xfrac does not lie between 0 and 2subsampleBitsH-1, or * yfrac does not lie between 0 and 2subsampleBitsV-1, an * ArrayIndexOutOfBoundsException may occur, where width and height * are the width and height of horizontal and vertical resampling * kernels respectively. * * @param s__ the sample above and to the left of the central sample. * @param s_0 the sample above the central sample. * @param s_1 the sample above and one to the right of the central sample. * @param s_2 the sample above and two to the right of the central sample. * @param s0_ the sample to the left of the central sample. * @param s00 the central sample. * @param s01 the sample to the right of the central sample. * @param s02 the sample two to the right of the central sample. * @param s1_ the sample below and one to the left of the central sample. * @param s10 the sample below the central sample. * @param s11 the sample below and one to the right of the central sample. * @param s12 the sample below and two to the right of the central sample. * @param s2_ the sample two below and one to the left of the central sample. * @param s20 the sample two below the central sample. * @param s21 the sample two below and one to the right of the central sample. * @param s22 the sample two below and two to the right of the central sample. * @param xfrac the X subsample position, multiplied by 2subsampleBitsH. * @param yfrac the Y subsample position, multiplied by 2subsampleBitsV. * @return the interpolated value as an int. * @throws ArrayIndexOutOfBoundsException if xfrac or yfrac are out of bounds. */ public int interpolateF(int s__, int s_0, int s_1, int s_2, int s0_, int s00, int s01, int s02, int s1_, int s10, int s11, int s12, int s2_, int s20, int s21, int s22, int xfrac, int yfrac) { // Interpolate in X int offsetX = 4*xfrac; float sum_ = dataHf[offsetX]*s__; sum_ += dataHf[offsetX + 1]*s_0; sum_ += dataHf[offsetX + 2]*s_1; sum_ += dataHf[offsetX + 3]*s_2; float sum0 = dataHf[offsetX]*s0_; sum0 += dataHf[offsetX + 1]*s00; sum0 += dataHf[offsetX + 2]*s01; sum0 += dataHf[offsetX + 3]*s02; float sum1 = dataHf[offsetX]*s1_; sum1 += dataHf[offsetX + 1]*s10; sum1 += dataHf[offsetX + 2]*s11; sum1 += dataHf[offsetX + 3]*s12; float sum2 = dataHf[offsetX]*s2_; sum2 += dataHf[offsetX + 1]*s20; sum2 += dataHf[offsetX + 2]*s21; sum2 += dataHf[offsetX + 3]*s22; // Interpolate in Y int offsetY = 4*yfrac; float sum = dataVf[offsetY]*sum_; sum += dataVf[offsetY + 1]*sum0; sum += dataVf[offsetY + 2]*sum1; sum += dataVf[offsetY + 3]*sum2; int isum = (int)sum; return isum; } /** * Performs horizontal interpolation on a one-dimensional array of * floating-point samples representing a row of samples. * * If xfrac does not lie between the range [0.0, 1.0F), an * ArrayIndexOutOfBoundsException may occur. * * @param samples an array of floats. * @param xfrac the X subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a float. * @throws ArrayIndexOutOfBoundsException if xfrac is out of bounds. */ public float interpolateH(float[] samples, float xfrac) { float sum = 0.0F; int ifrac = (int)(xfrac*numSubsamplesH); int offset = width*ifrac; for (int i = 0; i < width; i++) { sum += dataHf[offset + i]*samples[i]; } return sum; } /** * Performs vertical interpolation on a one-dimensional array of * floating-point samples representing a column of samples. * * If yfrac does not lie between the range [0.0, 1.0F), an * ArrayIndexOutOfBoundsException may occur. * * @param samples an array of floats. * @param yfrac the Y subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a float. * @throws ArrayIndexOutOfBoundsException if yfrac is out of bounds. */ public float interpolateV(float[] samples, float yfrac) { float sum = 0.0F; int ifrac = (int)(yfrac*numSubsamplesV); int offset = width*ifrac; for (int i = 0; i < width; i++) { sum += dataVf[offset + i]*samples[i]; } return sum; } /** * Performs horizontal interpolation on a pair of floating-point samples. * This method may be used instead of the array version for speed. * It should only be called if width == 2. * * If xfrac does not lie between the range [0.0, 1.0F), an * ArrayIndexOutOfBoundsException may occur. * * @param s0 the central sample. * @param s1 the sample to the right of the central sample. * @param xfrac the X subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a float. * @throws ArrayIndexOutOfBoundsException if xfrac is out of bounds. */ public float interpolateH(float s0, float s1, float xfrac) { float sum = 0.0F; int ifrac = (int)(xfrac * numSubsamplesH); // Assume width == 2 int offset = 2 * ifrac; sum = dataHf[offset] * s0 + dataHf[offset+1] * s1; return sum; } /** * Performs horizontal interpolation on a quadruple of floating-point * samples. This method may be used instead of the array version for * speed. It should only be called if width == 4 and keyX == 1. * * If xfrac does not lie between the range [0.0, 1.0F), an * ArrayIndexOutOfBoundsException may occur. * * @param s_ the sample to the left of the central sample. * @param s0 the central sample. * @param s1 the sample to the right of the central sample. * @param s2 the sample to the right of s1. * @param xfrac the X subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a float. * @throws ArrayIndexOutOfBoundsException if xfrac is out of bounds. */ public float interpolateH(float s_, float s0, float s1, float s2, float xfrac) { int ifrac = (int)(xfrac * numSubsamplesH); // Assume width == 4 int offset = 4 * ifrac; float sum = dataHf[offset] * s_; sum += dataHf[offset + 1] * s0; sum += dataHf[offset + 2] * s1; sum += dataHf[offset + 3] * s2; return sum; } /** * Performs vertical interpolation on a pair of floating-point samples. * This method may be used instead of the array version for speed. * It should only be called if height == 2 and keyY == 0. * * If yfrac does not lie between the range [0.0, 1.0F), an * ArrayIndexOutOfBoundsException may occur. * * @param s0 the central sample. * @param s1 the sample below the central sample. * @param yfrac the Y subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a float. * @throws ArrayIndexOutOfBoundsException if yfrac is out of bounds. */ public float interpolateV(float s0, float s1, float yfrac) { int ifrac = (int)(yfrac * numSubsamplesV); // Assume width == 2 int offset = 2 * ifrac; float sum = dataVf[offset] * s0; sum += dataVf[offset + 1] * s1; return sum; } /** * Performs vertical interpolation on a quadruple of floating-point * samples. This method may be used instead of the array version for * speed. It should only be called if height == 4 and keyY == 1. * * If yfrac does not lie between the range [0.0, 1.0F), an * ArrayIndexOutOfBoundsException may occur. * * @param s_ the sample above the central sample. * @param s0 the central sample. * @param s1 the sample below the central sample. * @param s2 the sample below s1. * @param yfrac the Y subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a float. * @throws ArrayIndexOutOfBoundsException if yfrac is out of bounds. */ public float interpolateV(float s_, float s0, float s1, float s2, float yfrac) { int ifrac = (int)(yfrac * numSubsamplesV); // Assume width == 4 int offset = 4 * ifrac; float sum = dataVf[offset] * s_; sum += dataVf[offset + 1] * s0; sum += dataVf[offset + 2] * s1; sum += dataVf[offset + 3] * s2; return sum; } /** * Performs interpolation on a 2x2 grid of floating-point samples. * It should only be called if width == height == 2 and * keyX == keyY == 0. * * If either xfrac or yfrac does not lie between the range [0.0, 1.0F), an * ArrayIndexOutOfBoundsException may occur. * * @param s00 the central sample. * @param s01 the sample to the right of the central sample. * @param s10 the sample below the central sample. * @param s11 the sample below and to the right of the central sample. * @param xfrac the X subsample position, in the range [0.0F, 1.0F). * @param yfrac the Y subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a float. * @throws ArrayIndexOutOfBoundsException if xfrac or yfrac are out of bounds. */ public float interpolate(float s00, float s01, float s10, float s11, float xfrac, float yfrac) { int ifrac = (int)(xfrac * numSubsamplesH); // Interpolate in X int offsetX = 2 * ifrac; float sum0 = dataHf[offsetX]*s00 + dataHf[offsetX + 1]*s01; float sum1 = dataHf[offsetX]*s10 + dataHf[offsetX + 1]*s11; // Interpolate in Y ifrac = (int)(yfrac * numSubsamplesV); int offsetY = 2 * ifrac; float sum = dataVf[offsetY]*sum0 + dataVf[offsetY + 1]*sum1; return sum; } /** * Performs interpolation on a 4x4 grid of floating-point samples. * It should only be called if width == height == 4 and * keyX == keyY == 1. * * If either xfrac or yfrac does not lie between the range [0.0, 1.0F), an * ArrayIndexOutOfBoundsException may occur. * * @param s__ the sample above and to the left of the central sample. * @param s_0 the sample above the central sample. * @param s_1 the sample above and one to the right of the central sample. * @param s_2 the sample above and two to the right of the central sample. * @param s0_ the sample to the left of the central sample. * @param s00 the central sample. * @param s01 the sample to the right of the central sample. * @param s02 the sample two to the right of the central sample. * @param s1_ the sample below and one to the left of the central sample. * @param s10 the sample below the central sample. * @param s11 the sample below and one to the right of the central sample. * @param s12 the sample below and two to the right of the central sample. * @param s2_ the sample two below and one to the left of the central sample. * @param s20 the sample two below the central sample. * @param s21 the sample two below and one to the right of the central sample. * @param s22 the sample two below and two to the right of the central sample. * @param xfrac the X subsample position, in the range [0.0F, 1.0F). * @param yfrac the Y subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a float. * @throws ArrayIndexOutOfBoundsException if xfrac or yfrac are out of bounds. */ public float interpolate(float s__, float s_0, float s_1, float s_2, float s0_, float s00, float s01, float s02, float s1_, float s10, float s11, float s12, float s2_, float s20, float s21, float s22, float xfrac, float yfrac) { int ifrac = (int)(xfrac * numSubsamplesH); // Interpolate in X int offsetX = 4 * ifrac; int offsetX1 = offsetX + 1; int offsetX2 = offsetX + 2; int offsetX3 = offsetX + 3; float sum_ = dataHf[offsetX]*s__; sum_ += dataHf[offsetX1]*s_0; sum_ += dataHf[offsetX2]*s_1; sum_ += dataHf[offsetX3]*s_2; float sum0 = dataHf[offsetX]*s0_; sum0 += dataHf[offsetX1]*s00; sum0 += dataHf[offsetX2]*s01; sum0 += dataHf[offsetX3]*s02; float sum1 = dataHf[offsetX]*s1_; sum1 += dataHf[offsetX1]*s10; sum1 += dataHf[offsetX2]*s11; sum1 += dataHf[offsetX3]*s12; float sum2 = dataHf[offsetX]*s2_; sum2 += dataHf[offsetX1]*s20; sum2 += dataHf[offsetX2]*s21; sum2 += dataHf[offsetX3]*s22; // Interpolate in Y ifrac = (int)(yfrac * numSubsamplesV); int offsetY = 4 * ifrac; float sum = dataVf[offsetY]*sum_; sum += dataVf[offsetY + 1]*sum0; sum += dataVf[offsetY + 2]*sum1; sum += dataVf[offsetY + 3]*sum2; return sum; } /** * Performs horizontal interpolation on a one-dimensional array of * double samples representing a row of samples. * * If xfrac does not lie between the range [0.0, 1.0F), an * ArrayIndexOutOfBoundsException may occur. * * @param samples an array of doubles. * @param xfrac the X subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a double. * @throws ArrayIndexOutOfBoundsException if xfrac is out of bounds. */ public double interpolateH(double[] samples, float xfrac) { double sum = 0.0; int ifrac = (int)(xfrac * numSubsamplesH); int offset = width * ifrac; for (int i = 0; i < width; i++) { sum += dataHd[offset+i] * samples[i]; } return sum; } /** * Performs vertical interpolation on a one-dimensional array of * double samples representing a column of samples. * * If yfrac does not lie between the range [0.0, 1.0F), an * ArrayIndexOutOfBoundsException may occur. * * @param samples an array of doubles. * @param yfrac the Y subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a double. * @throws ArrayIndexOutOfBoundsException if yfrac is out of bounds. */ public double interpolateV(double[] samples, float yfrac) { double sum = 0.0; int ifrac = (int)(yfrac * numSubsamplesV); int offset = width * ifrac; for (int i = 0; i < width; i++) { sum += dataVd[offset+i] * samples[i]; } return sum; } /** * Performs horizontal interpolation on a pair of double samples. * This method may be used instead of the array version for speed. * It should only be called if width == 2. * * If xfrac does not lie between the range [0.0, 1.0F), an * ArrayIndexOutOfBoundsException may occur. * * @param s0 the central sample. * @param s1 the sample to the right of the central sample. * @param xfrac the X subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a double. * @throws ArrayIndexOutOfBoundsException if xfrac is out of bounds. */ public double interpolateH(double s0, double s1, float xfrac) { double sum = 0.0F; int ifrac = (int)(xfrac * numSubsamplesH); // Assume width == 2 int offset = 2 * ifrac; sum = dataHd[offset] * s0 + dataHd[offset+1] * s1; return sum; } /** * Performs horizontal interpolation on a quadruple of double * samples. This method may be used instead of the array version for * speed. It should only be called if width == 4 and keyX == 1. * * If xfrac does not lie between the range [0.0, 1.0F), an * ArrayIndexOutOfBoundsException may occur. * * @param s_ the sample to the left of the central sample. * @param s0 the central sample. * @param s1 the sample to the right of the central sample. * @param s2 the sample to the right of s1. * @param xfrac the X subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a double. * @throws ArrayIndexOutOfBoundsException if xfrac is out of bounds. */ public double interpolateH(double s_, double s0, double s1, double s2, float xfrac) { int ifrac = (int)(xfrac * numSubsamplesH); // Assume width == 4 int offset = 4 * ifrac; double sum = dataHd[offset] * s_; sum += dataHd[offset + 1] * s0; sum += dataHd[offset + 2] * s1; sum += dataHd[offset + 3] * s2; return sum; } /** * Performs vertical interpolation on a pair of double samples. * This method may be used instead of the array version for speed. * It should only be called if height == 2 and keyY == 0. * * If yfrac does not lie between the range [0.0, 1.0F), an * ArrayIndexOutOfBoundsException may occur. * * @param s0 the central sample. * @param s1 the sample below the central sample. * @param yfrac the Y subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a double. * @throws ArrayIndexOutOfBoundsException if yfrac is out of bounds. */ public double interpolateV(double s0, double s1, float yfrac) { int ifrac = (int)(yfrac * numSubsamplesV); // Assume width == 2 int offset = 2 * ifrac; double sum = dataVd[offset] * s0; sum += dataVd[offset + 1] * s1; return sum; } /** * Performs vertical interpolation on a quadruple of double * samples. This method may be used instead of the array version for * speed. It should only be called if height == 4 and keyY == 1. * * If yfrac does not lie between the range [0.0, 1.0F), an * ArrayIndexOutOfBoundsException may occur. * * @param s_ the sample above the central sample. * @param s0 the central sample. * @param s1 the sample below the central sample. * @param s2 the sample below s1. * @param yfrac the Y subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a double. * @throws ArrayIndexOutOfBoundsException if yfrac is out of bounds. */ public double interpolateV(double s_, double s0, double s1, double s2, float yfrac) { int ifrac = (int)(yfrac * numSubsamplesV); // Assume width == 4 int offset = 4 * ifrac; double sum = dataVd[offset] * s_; sum += dataVd[offset + 1] * s0; sum += dataVd[offset + 2] * s1; sum += dataVd[offset + 3] * s2; return sum; } /** * Performs interpolation on a 2x2 grid of double samples. * It should only be called if width == height == 2 and * keyX == keyY == 0. * * If either xfrac or yfrac does not lie between the range [0.0, 1.0F), an * ArrayIndexOutOfBoundsException may occur. * * @param s00 the central sample. * @param s01 the sample to the right of the central sample. * @param s10 the sample below the central sample. * @param s11 the sample below and to the right of the central sample. * @param xfrac the X subsample position, in the range [0.0F, 1.0F). * @param yfrac the Y subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a double. * @throws ArrayIndexOutOfBoundsException if xfrac or yfrac are out of bounds. */ public double interpolate(double s00, double s01, double s10, double s11, float xfrac, float yfrac) { int ifrac = (int)(xfrac * numSubsamplesH); // Interpolate in X int offsetX = 2 * ifrac; double sum0 = dataHd[offsetX]*s00 + dataHd[offsetX + 1]*s01; double sum1 = dataHd[offsetX]*s10 + dataHd[offsetX + 1]*s11; // Interpolate in Y ifrac = (int)(yfrac * numSubsamplesV); int offsetY = 2 * ifrac; double sum = dataVd[offsetY]*sum0 + dataVd[offsetY + 1]*sum1; return sum; } /** * Performs interpolation on a 4x4 grid of double samples. * It should only be called if width == height == 4 and * keyX == keyY == 1. * * If either xfrac or yfrac does not lie between the range [0.0, 1.0F), an * ArrayIndexOutOfBoundsException may occur. * * @param s__ the sample above and to the left of the central sample. * @param s_0 the sample above the central sample. * @param s_1 the sample above and one to the right of the central sample. * @param s_2 the sample above and two to the right of the central sample. * @param s0_ the sample to the left of the central sample. * @param s00 the central sample. * @param s01 the sample to the right of the central sample. * @param s02 the sample two to the right of the central sample. * @param s1_ the sample below and one to the left of the central sample. * @param s10 the sample below the central sample. * @param s11 the sample below and one to the right of the central sample. * @param s12 the sample below and two to the right of the central sample. * @param s2_ the sample two below and one to the left of the central sample. * @param s20 the sample two below the central sample. * @param s21 the sample two below and one to the right of the central sample. * @param s22 the sample two below and two to the right of the central sample. * @param xfrac the X subsample position, in the range [0.0F, 1.0F). * @param yfrac the Y subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a double. * @throws ArrayIndexOutOfBoundsException if xfrac or yfrac are out of bounds. */ public double interpolate(double s__, double s_0, double s_1, double s_2, double s0_, double s00, double s01, double s02, double s1_, double s10, double s11, double s12, double s2_, double s20, double s21, double s22, float xfrac, float yfrac) { int ifrac = (int)(xfrac * numSubsamplesH); // Interpolate in X int offsetX = 4 * ifrac; int offsetX1 = offsetX + 1; int offsetX2 = offsetX + 2; int offsetX3 = offsetX + 3; double sum_ = dataHd[offsetX]*s__; sum_ += dataHd[offsetX1]*s_0; sum_ += dataHd[offsetX2]*s_1; sum_ += dataHd[offsetX3]*s_2; double sum0 = dataHd[offsetX]*s0_; sum0 += dataHd[offsetX1]*s00; sum0 += dataHd[offsetX2]*s01; sum0 += dataHd[offsetX3]*s02; double sum1 = dataHd[offsetX]*s1_; sum1 += dataHd[offsetX1]*s10; sum1 += dataHd[offsetX2]*s11; sum1 += dataHd[offsetX3]*s12; double sum2 = dataHd[offsetX]*s2_; sum2 += dataHd[offsetX1]*s20; sum2 += dataHd[offsetX2]*s21; sum2 += dataHd[offsetX3]*s22; // Interpolate in Y ifrac = (int)(yfrac * numSubsamplesV); int offsetY = 4 * ifrac; double sum = dataVd[offsetY]*sum_; sum += dataVd[offsetY + 1]*sum0; sum += dataVd[offsetY + 2]*sum1; sum += dataVd[offsetY + 3]*sum2; return sum; } } jai-core-1.1.4/src/share/classes/javax/media/jai/FactoryCache.java0000644000175000017500000003231110203035544024533 0ustar mathieumathieu/* * $RCSfile: FactoryCache.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:08 $ * $State: Exp $ */ package javax.media.jai; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Vector; import javax.media.jai.util.CaselessStringKey; /** * A class to manage the various instances of a descriptor factory. * This also manages preferences between factory instances for * a specified descriptor/product. * * @since JAI 1.1 */ class FactoryCache { /** * The registry mode name. */ final String modeName; /** * Cache the RegistryMode since it is bound to get used * many, many times. */ final RegistryMode mode; /** The Class corresponding to the factory. */ final Class factoryClass; /** * The name of the method in this factory used to * do a "create" */ final Method factoryMethod; /** * does the factory support preferences both among products * and among multiple instances of the factory within the * same product ? */ final boolean arePreferencesSupported; /** * A Hashtable of all the instances, hashed by a filename that * uniquely identifies each registered factory. */ private Hashtable instances; /** * A Hashtable of all the unique factory filenames, hashed by * the factory they represent. */ private Hashtable instancesByName; /** A count to give a number to each registered factory. */ private int count = 0; /** * A Hashtable of a Hashtable of all the factory preferences, hashed * by the descriptor name first and then the product name that the * factory belongs to. Each element of the per product Hashtable is * a Vector which contains a list of pairwise factory preferences * stored as Vectors. */ private Hashtable prefs; /** * Constructor. Create a FactoryCache to hold factory objects * for a specific mode. * * @param modeName the registry mode name. */ FactoryCache(String modeName) { this.modeName = modeName; mode = RegistryMode.getMode(modeName); factoryClass = mode.getFactoryClass(); factoryMethod = mode.getFactoryMethod(); arePreferencesSupported = mode.arePreferencesSupported(); instances = new Hashtable(); if (arePreferencesSupported) { instancesByName = new Hashtable(); prefs = new Hashtable(); } } /** * Invoke the create method of the given factory instance * * @param factoryInstance an instance of this factory * @param parameterValues the parameterValues to be passed in to the * the create method. * * @return the object created by the create method * * @throws IllegalArgumentException thrown by Method.invoke * @throws InvocationTargetException thrown by Method.invoke * @throws IllegalAccessException thrown by Method.invoke */ Object invoke(Object factoryInstance, Object[] parameterValues) throws InvocationTargetException, IllegalAccessException { return factoryMethod.invoke(factoryInstance, parameterValues); } /** * Add a factory instance to this factory. If the factory has * NO preferences add it to a table hashed by just the operation name. * Otherwise add it to two tables, one hashed by a unique filename * (modeName + count) and the other hashed by the factory interface * name. * * @param descriptorName operation that this factory instance implements * @param productName product to which this factory instance belongs * @param factoryInstance the factory instance */ void addFactory(String descriptorName, String productName, Object factoryInstance) { checkInstance(factoryInstance); if (arePreferencesSupported) { if (productName == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); // Update structures to reflect the addition of // this factory instance. Vector v = new Vector(); v.add(factoryInstance.getClass().getName()); v.add(productName); v.add(descriptorName); CaselessStringKey fileName = new CaselessStringKey(modeName + count); instancesByName.put(factoryInstance, fileName); instances.put(fileName, v); count++; } else instances.put( new CaselessStringKey(descriptorName), factoryInstance); } /** * Remove a facory instance associated with the specified operation * * @param descriptorName operation that this factory instance implements * @param productName product to which this factory instance belongs * @param factoryInstance the factory instance * */ void removeFactory(String descriptorName, String productName, Object factoryInstance) { checkInstance(factoryInstance); checkRegistered(descriptorName, productName, factoryInstance); if (arePreferencesSupported) { // Update structures to reflect the removal of // this factoryInstance. CaselessStringKey fileName = (CaselessStringKey)instancesByName.get(factoryInstance); instancesByName.remove(factoryInstance); instances.remove(fileName); count--; } else { instances.remove(new CaselessStringKey(descriptorName)); } } /** * Sets a preference between two factory instances for the given * operation and product. * * @param descriptorName operation that this factory instance implements * @param productName product to which this factory instance belongs * @param preferredOp the preferred factory instance * @param otherOp the not-so preferred/other factory instance */ void setPreference(String descriptorName, String productName, Object preferredOp, Object otherOp) { if (!arePreferencesSupported) { throw new IllegalArgumentException( JaiI18N.formatMsg("FactoryCache1", new Object[] {modeName})); } if ((preferredOp == null) || (otherOp == null)) { throw new IllegalArgumentException( JaiI18N.getString("Generic0")); } checkRegistered(descriptorName, productName, preferredOp); checkRegistered(descriptorName, productName, otherOp); if (preferredOp == otherOp) return; checkInstance(preferredOp); checkInstance(otherOp); CaselessStringKey dn = new CaselessStringKey(descriptorName); CaselessStringKey pn = new CaselessStringKey(productName); Hashtable dht = (Hashtable)prefs.get(dn); if (dht == null) { prefs.put(dn, dht = new Hashtable()); } Vector pv = (Vector)dht.get(pn); if (pv == null) { dht.put(pn, pv = new Vector()); } pv.addElement(new Object[] { preferredOp, otherOp }); } /** * Unets a preference between two factory instances for the given * operation and product. * * @param descriptorName operation that this factory instance implements * @param productName product to which this factory instance belongs * @param preferredOp the preferred factory instance * @param otherOp the not-so preferred/other factory instance */ void unsetPreference(String descriptorName, String productName, Object preferredOp, Object otherOp) { if (!arePreferencesSupported) { throw new IllegalArgumentException( JaiI18N.formatMsg("FactoryCache1", new Object[] {modeName})); } if ((preferredOp == null) || (otherOp == null)) { throw new IllegalArgumentException( JaiI18N.getString("Generic0")); } checkRegistered(descriptorName, productName, preferredOp); checkRegistered(descriptorName, productName, otherOp); if (preferredOp == otherOp) return; checkInstance(preferredOp); checkInstance(otherOp); // Update structures to reflect removal of this pref. Hashtable dht = (Hashtable)prefs.get( new CaselessStringKey(descriptorName)); boolean found = false; if (dht != null) { Vector pv = (Vector)dht.get( new CaselessStringKey(productName)); if (pv != null) { Iterator it = pv.iterator(); while(it.hasNext()) { Object[] objs = (Object[])it.next(); if ((objs[0] == preferredOp) && (objs[1] == otherOp)) { it.remove(); found = true; } } } } if (!found) throw new IllegalArgumentException( JaiI18N.formatMsg("FactoryCache2", new Object[] { preferredOp.getClass().getName(), otherOp.getClass().getName(), modeName, descriptorName, productName })); } /** * Gets an iterator over all preferences set between factory * instances for a given descriptor and product. * * @param descriptorName operation that this factory instance implements * @param productName product to which this factory instance belongs */ Object[][] getPreferences(String descriptorName, String productName) { if (!arePreferencesSupported) { throw new IllegalArgumentException( JaiI18N.formatMsg("FactoryCache1", new Object[] {modeName})); } if ((descriptorName == null) || (productName == null)) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); // Update structures to reflect removal of this pref. Hashtable dht = (Hashtable)prefs.get( new CaselessStringKey(descriptorName)); if (dht != null) { Vector pv = (Vector)dht.get( new CaselessStringKey(productName)); if (pv != null) { return (Object[][])pv.toArray(new Object[0][]); } } return null; } /** * Removes all preferences set between factory * instances for a given descriptor and product. * * @param descriptorName operation that this factory instance implements * @param productName product to which this factory instance belongs */ void clearPreferences(String descriptorName, String productName) { if (!arePreferencesSupported) { throw new IllegalArgumentException( JaiI18N.formatMsg("FactoryCache1", new Object[] {modeName})); } // Update structures to reflect removal of this pref. Hashtable dht = (Hashtable)prefs.get( new CaselessStringKey(descriptorName)); if (dht != null) dht.remove(new CaselessStringKey(productName)); } /** * Get a list of factory objects registered under the descriptor * and the product (in no particular order). */ List getFactoryList(String descriptorName, String productName) { if (arePreferencesSupported) { ArrayList list = new ArrayList(); Enumeration keys = instancesByName.keys(); while (keys.hasMoreElements()) { Object instance = keys.nextElement(); CaselessStringKey fileName = (CaselessStringKey)instancesByName.get(instance); Vector v = (Vector)instances.get(fileName); String dn = (String)v.get(2); String pn = (String)v.get(1); if (descriptorName.equalsIgnoreCase(dn) && productName.equalsIgnoreCase(pn)) list.add(instance); } return list; } else { Object obj = instances.get(new CaselessStringKey(descriptorName)); ArrayList list = new ArrayList(1); list.add(obj); return list; } } /** * Get the local name of a factoryInstance */ String getLocalName(Object factoryInstance) { CaselessStringKey fileName = (CaselessStringKey)instancesByName.get(factoryInstance); if (fileName != null) return fileName.getName(); return null; } /** * Check to see if the factoryInstance is valid object * of this registry mode. */ private boolean checkInstance(Object factoryInstance) { if (!factoryClass.isInstance(factoryInstance)) throw new IllegalArgumentException( JaiI18N.formatMsg("FactoryCache0", new Object[] { factoryInstance.getClass().getName(), modeName, factoryClass.getName() })); return true; } /** * Check to see if factoryInstance is registered against * the specified descriptorName and productName. */ private void checkRegistered(String descriptorName, String productName, Object factoryInstance) { if (arePreferencesSupported) { if (productName == null) throw new IllegalArgumentException( "productName : " + JaiI18N.getString("Generic0")); CaselessStringKey fileName = (CaselessStringKey)instancesByName.get(factoryInstance); if (fileName != null) { Vector v = (Vector)instances.get(fileName); String pn = (String)v.get(1); String dn = (String)v.get(2); if ((dn != null) && dn.equalsIgnoreCase(descriptorName) && (pn != null) && pn.equalsIgnoreCase(productName)) { return; } } throw new IllegalArgumentException( JaiI18N.formatMsg("FactoryCache3", new Object[] { factoryInstance.getClass().getName(), descriptorName, productName})); } else { CaselessStringKey key = new CaselessStringKey(descriptorName); if (factoryInstance != instances.get(key)) { throw new IllegalArgumentException( JaiI18N.formatMsg("FactoryCache4", new Object[] { factoryInstance.getClass().getName(), descriptorName})); } } } } jai-core-1.1.4/src/share/classes/javax/media/jai/SnapshotImage.java0000644000175000017500000004161410240717543024757 0ustar mathieumathieu/* * $RCSfile: SnapshotImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-05-12 18:24:34 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Point; import java.awt.image.Raster; import java.awt.image.TileObserver; import java.awt.image.WritableRaster; import java.awt.image.WritableRenderedImage; import java.util.Enumeration; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; /** * A (Raster, X, Y) tuple. */ final class TileCopy { /** The tile's Raster data. */ Raster tile; /** The tile's column within the image tile grid. */ int tileX; /** The tile's row within the image tile grid. */ int tileY; /** * Constructs a TileCopy object given the tile's Raster data * and its location in the tile grid. * * @param tile the Raster containing the tile's data. * @param tileX the tile's X position in the tile grid. * @param tileY the tile's X position in the tile grid. */ TileCopy(Raster tile, int tileX, int tileY) { this.tile = tile; this.tileX = tileX; this.tileY = tileY; } } /** * A proxy for Snapshot that calls * Snapshot.dispose() when finalized. * No references to a SnapshotProxy are held internally, only user * references. Thus it will be garbage collected when the last user * reference is relinquished. The Snapshot's * dispose() method * is called from SnapshotProxy.finalize(), ensuring that all * of the resources held by the Snapshot will become collectable. */ final class SnapshotProxy extends PlanarImage { /** * The parent Snapshot to which we forward * getTile() calls. */ Snapshot parent; /** * Construct a new proxy for a given Snapshot. * * @param parent the Snapshot to which method calls will * be forwarded. */ SnapshotProxy(Snapshot parent) { super(new ImageLayout(parent), null, null); this.parent = parent; } /** * Forwards a tile request to the parent Snapshot. * * @param tileX the X index of the tile. * @param tileY the Y index of the tile. * @return the tile as a Raster. */ public Raster getTile(int tileX, int tileY) { return parent.getTile(tileX, tileY); } /** Disposes of resources held by this proxy. */ public void dispose() { parent.dispose(); } } /** * A non-public class that holds a portion of the state associated * with a SnapShotImage. A Snapshot provides the * appearance of a PlanarImage with fixed contents. In order to * provide this illusion, however, the Snapshot relies on the * fact that it belongs to a linked list of Snapshots rooted in a * particular SnapShotImage; it cannot function independently. * */ final class Snapshot extends PlanarImage { /** The creator of this image. */ SnapshotImage parent; /** The next Snapshot in a doubly-linked list. */ Snapshot next; /** The previous Snapshot in a doubly-linked list. */ Snapshot prev; /** A set of cached TileCopy elements. */ Hashtable tiles = new Hashtable(); /** True if dispose() has been called. */ boolean disposed = false; /** * Constructs a Snapshot that will provide a synchronous * view of a SnapshotImage at a particular moment in time. * * @param parent a SnapshotImage this image will be viewing. */ Snapshot(SnapshotImage parent) { super(new ImageLayout(parent), null, null); this.parent = parent; } /** * Returns the version of a tile "seen" by this Snapshot. * The tile "seen" is the oldest copy of the tile made after * the creation of this Snapshot; it may be held in the * tiles Hashtable of this Snapshot or one of * its successors. If no later Snapshot holds a copy of * the tile, the current version of the tile from the source image is * returned. * *

    getTile() is synchronized in order to prevent calls to * dispose(), which will cause the list of * Snapshots to change, from occurring at the same time as * the walking of the list. * * @param tileX the X index of the tile. * @param tileY the Y index of the tile. * @return the tile as a Raster. */ public Raster getTile(int tileX, int tileY) { // Make sure dispose() and getTile() are mutually exclusive synchronized(parent) { // Check local set of tile copies, if not there move // forward to the next Snapshot, if last image // get the tile from the real source image. TileCopy tc = (TileCopy)tiles.get(new Point(tileX, tileY)); if (tc != null) { return tc.tile; } else if (next != null) { return next.getTile(tileX, tileY); } else { return parent.getTrueSource().getTile(tileX, tileY); } } } /** * Sets the next Snapshot in the list to a given * Snapshot. * * @param next the next Snapshot in the list. */ void setNext(Snapshot next) { this.next = next; } /** * Sets the previous Snapshot in the list to a given * Snapshot. * * @param prev the previous Snapshot in the list. */ void setPrev(Snapshot prev) { this.prev = prev; } /** * Returns true if this Snapshot already stores a version * of a specified tile. * * @param tileX the X index of the tile. * @param tileY the Y index of the tile. * @return true if this Snapshot holds a copy of the tile. */ boolean hasTile(int tileX, int tileY) { TileCopy tc = (TileCopy)tiles.get(new Point(tileX, tileY)); return tc != null; } /** * Stores a given tile in this Snapshot. The caller should * not attempt to store more than one version of a given tile. * * @param tile a Raster containing the tile data. * @param tileX the tile's column within the image tile grid. * @param tileY the tile's row within the image tile grid. */ void addTile(Raster tile, int tileX, int tileY) { TileCopy tc = new TileCopy(tile, tileX, tileY); tiles.put(new Point(tileX, tileY), tc); } /** This image will no longer be referenced by the user. */ public void dispose() { // Make sure dispose() and getTile() are mutually exclusive synchronized(parent) { // Make it idempotent if (disposed) { return; } disposed = true; // If this is the last Snapshot, inform the parent if (parent.getTail() == this) { parent.setTail(prev); } // Remove 'this' from the chain if (prev != null) { prev.setNext(next); } if (next != null) { next.setPrev(prev); } // If there is a previous node, push tiles back to it if (prev != null) { // Push tiles back to the previous Snapshot Enumeration enumeration = tiles.elements(); while (enumeration.hasMoreElements()) { TileCopy tc = (TileCopy)enumeration.nextElement(); if (!prev.hasTile(tc.tileX, tc.tileY)) { prev.addTile(tc.tile, tc.tileX, tc.tileY); } } } // Null out links to help the GC parent = null; next = prev = null; tiles = null; } } } /** * A class providing an arbitrary number of synchronous views of a * possibly changing WritableRenderedImage. * SnapshotImage is responsible for stabilizing changing sources * in order to allow deferred execution of operations dependent on such * sources. * *

    Any RenderedImage may be used as the source of a * SnapshotImage; if it is a WritableRenderedImage, * the SnapshotImage will register itself as a * TileObserver and make copies of tiles that are about to change. * Multiple versions of each tile are maintained internally, as long as they * are in demand. SnapshotImage is able to track demand and * should be able to simply forward requests for tiles to the source most * of the time, without the need to make a copy. * *

    When used as a source, calls to getTile will simply be passed * along to the source. In other words, SnapshotImage is * completely transparent. However, by calling createSnapshot() * an instance of a non-public PlanarImage subclass (called * Snapshot in this implementation) will be created and returned. * This image will always return tile data with contents as of the time of its * construction. * *

    When a particular Snapshot is no longer needed, its * dispose() method may be called. The dispose() * method will be called automatically when the Snapshot is * finalized by the garbage collector. Disposing of the Snapshot * allows tile data held by the Snapshot that is not needed by * any other Snapshot to be disposed of as well. * *

    This implementation of SnapshotImage makes use of a * doubly-linked list of Snapshot objects. A new * Snapshot is added to the tail of the list whenever * createSnapshot() is called. Each Snapshot * has a cache containing copies of any tiles that were writable at the * time of its construction, as well as any tiles that become writable * between the time of its construction and the construction of the next * Snapshot. * *

    When asked for a tile, a Snapshot checks its local cache * and returns its version of the tile if one is found. Otherwise, it * forwards the request onto its successor. This process continues * until the latest Snapshot is reached; if it does not contain * a copy of the tile, the tile is requested from the real source image. * *

    When a Snapshot is no longer needed, its * dispose() method attempts to push the contents of its tile * cache back to the previous Snapshot in the linked list. If * that image possesses a version of the same tile, the tile is not pushed * back and may be discarded. * * @see java.awt.image.RenderedImage * @see java.awt.image.TileObserver * @see java.awt.image.WritableRenderedImage * @see PlanarImage * */ public class SnapshotImage extends PlanarImage implements TileObserver { /** The real image source. */ private PlanarImage source; /** The last entry in the list of Snapshot, initially null. */ private Snapshot tail = null; /** The set of active tiles, represented as a HashSet of Points. */ private HashSet activeTiles = new HashSet(); /** * Constructs a SnapshotImage from a PlanarImage * source. * * @param source a PlanarImage source. * @throws IllegalArgumentException if source is null. */ public SnapshotImage(PlanarImage source) { super(new ImageLayout(source), null, null); // Record the source image this.source = source; // Set image parameters to match the source // Determine which tiles of the source image are writable if (source instanceof WritableRenderedImage) { WritableRenderedImage wri = (WritableRenderedImage)source; wri.addTileObserver(this); Point[] pts = wri.getWritableTileIndices(); if (pts != null) { int num = pts.length; for (int i = 0; i < num; i++) { // Add these tiles to the active list Point p = pts[i]; activeTiles.add(new Point(p.x, p.y)); } } } } /** * Returns the PlanarImage source of this * SnapshotImage. * * @return a PlanarImage that is the source of data for this * image. */ protected PlanarImage getTrueSource() { return source; } /** * Sets the reference to the most current Snapshot to a given * Snapshot. * * @param tail a reference to the new most current Snapshot. */ void setTail(Snapshot tail) { this.tail = tail; } /** * Returns a reference to the most current Snapshot. * * @return the Snapshot at the tail end of the list. */ Snapshot getTail() { return tail; } /** * Creates and returns a Raster copy of a given source tile. * * @param tileX the X index of the tile. * @param tileY the Y index of the tile. * @return a newly-constructed Raster containing a copy * of the tile data. */ private Raster createTileCopy(int tileX, int tileY) { int x = tileXToX(tileX); int y = tileYToY(tileY); Point p = new Point(x, y); WritableRaster tile = RasterFactory.createWritableRaster(sampleModel, p); source.copyData(tile); return tile; } /** * Creates a snapshot of this image. This snapshot may be used * indefinitely, and will always appear to have the pixel data that * this image has currently. The snapshot is semantically a copy * of this image but may be implemented in a more efficient manner. * Multiple snapshots taken at different times may share tiles that * have not changed, and tiles that are currently static in this * image's source do not need to be copied at all. * * @return a PlanarImage snapshot. */ public PlanarImage createSnapshot() { if (source instanceof WritableRenderedImage) { // Create a new Snapshot Snapshot snap = new Snapshot(this); // For each active tile: Iterator iter = activeTiles.iterator(); while (iter.hasNext()) { Point p = (Point)iter.next(); // Make a copy and store it in the Snapshot Raster tile = createTileCopy(p.x, p.y); snap.addTile(tile, p.x, p.y); } // Add the new Snapshot to the list of snapshots if (tail == null) { tail = snap; } else { tail.setNext(snap); snap.setPrev(tail); tail = snap; } // Create a proxy and return it return new SnapshotProxy(snap); } else { return source; } } /** * Receives the information that a tile is either about to become * writable, or is about to become no longer writable. * * @param source the WritableRenderedImage for which we * are an observer. * @param tileX the X index of the tile. * @param tileY the Y index of the tile. * @param willBeWritable true if the tile is becoming writable. */ public void tileUpdate(WritableRenderedImage source, int tileX, int tileY, boolean willBeWritable) { if (willBeWritable) { // If the last Snapshot doesn't have the tile, copy it if ((tail != null) && (!tail.hasTile(tileX, tileY))) { tail.addTile(createTileCopy(tileX, tileY), tileX, tileY); } // Add the tile to the active list activeTiles.add(new Point(tileX, tileY)); } else { // Remove the tile from the active list activeTiles.remove(new Point(tileX, tileY)); } } /** * Returns a non-snapshotted tile from the source. * * @param tileX the X index of the tile. * @param tileY the Y index of the tile. * @return the tile as a Raster. */ public Raster getTile(int tileX, int tileY) { // Return the current source tile (X, Y) return source.getTile(tileX, tileY); } } jai-core-1.1.4/src/share/classes/javax/media/jai/util/0000755000175000017500000000000011633360403022315 5ustar mathieumathieujai-core-1.1.4/src/share/classes/javax/media/jai/util/Range.java0000644000175000017500000005752010203035544024222 0ustar mathieumathieu/* * $RCSfile: Range.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:57 $ * $State: Exp $ */package javax.media.jai.util; import java.io.Serializable; /** * A class to represent ranges of values. A range is defined to contain * all the values between the minimum and maximum values, where * the minimum/maximum value can be considered either included or excluded * from the range. * *

    This example creates a range of Integers whose minimum * value is 1 and the maximum value is 5. The range is inclusive at both * ends: * *

    * Range intRange = new Range(Integer.class, new Integer(1), new Integer(5)); * * *

    A Range can be unbounded at either or both of its ends. * An unbounded end is specified by passing null for the value of that end. * A Range unbounded at both of its ends represents a range of * all possible values for the Class of elements in that * Range. The isMinIncluded() method will always * return true for a Range unbounded on the minimum side and * correspondingly the isMaxIncluded() method will always * return true for a Range unbounded on the maximum side. * *

    An empty range is defined as a Range whose minimum value * is greater than it's maximum value if the ends are included, or as a * Range whose minimum value is greater than or equal to it's * maximum value, if the minimum or the maximum value is excluded. * * @since JAI 1.1 */ public class Range implements Serializable { // The class of the elements in this Range. private Class elementClass; // The minimum and maximum values of the range. private Comparable minValue, maxValue; // The the minimum/maximum value is included in the range.i // The default value is true, that is, included. private boolean isMinIncluded =true, isMaxIncluded = true ; /** * Constructs a Range object given the Class * of the elements in the Range, the minimum value and * the maximum value. The minimum and the maximum value are considered * inclusive. * *

    An unbounded range can be specified by passing in a null for * either of the two values, in which case the Range is * unbounded on one side, or for both, in which case the * Range represents an all inclusive set. * * @param elementClass The Class of the Range * elements. * @param minValue The lowest value included in the Range. * @param maxValue The highest value included in the Range. * * @throws IllegalArgumentException if minValue and maxValue are both null, * and elementClass is not one of the * subclasses of Comparable. * @throws IllegalArgumentException if minValue is not the same * Class as elementClass. * @throws IllegalArgumentException if maxValue is not the same * Class as elementClass. */ public Range(Class elementClass, Comparable minValue, Comparable maxValue) { // If both minValue and maxValue are null, check whether elementClass // is an instanceof Comparable. if ((minValue == null) && (maxValue == null)) { Class c = null ; try { c = Class.forName("java.lang.Comparable") ; } catch (ClassNotFoundException e) { } if (!c.isAssignableFrom(elementClass)) throw new IllegalArgumentException(JaiI18N.getString("Range0") ) ; } this.elementClass = elementClass; if (minValue != null && minValue.getClass() != this.elementClass) { throw new IllegalArgumentException(JaiI18N.getString("Range1")) ; } this.minValue = minValue; if (maxValue != null && maxValue.getClass() != this.elementClass) { throw new IllegalArgumentException(JaiI18N.getString("Range2")) ; } this.maxValue = maxValue; } /** * Constructs a Range object given the Class * of the elements in the Range, the minimum value and * the maximum value. Whether the minimum value and the maximum value * are considered inclusive is specified via the * isMinIncluded and isMaxIncluded variables. * *

    An unbounded range can be specified by passing in a null for * either of the two values, in which case the Range is * unbounded at one end, or for both, in which case the * Range represents an all inclusive set. If null is passed * in for either variable, the boolean variables have * no effect. * * @param elementClass The Class of the Range * elements. * @param minValue The lowest value for the Range. * @param isMinIncluded A boolean that defines whether the minimum value is * included in the Range. * @param maxValue The highest value for the Range. * @param isMaxIncluded A boolean that defines whether the maximum value is * included in the Range. * * @throws IllegalArgumentException if minValue and maxValue are both null, * and elementClass is not one of the * subclasses of Comparable. * @throws IllegalArgumentException if minValue is not the same * Class as elementClass. * @throws IllegalArgumentException if maxValue is not the same * Class as elementClass. */ public Range(Class elementClass, Comparable minValue, boolean isMinIncluded, Comparable maxValue, boolean isMaxIncluded) { this(elementClass, minValue, maxValue) ; this.isMinIncluded = isMinIncluded ; this.isMaxIncluded = isMaxIncluded ; } /** * Returns true if the minimum value is included within this * Range. If the range is unbounded at this end, this * method will return true. */ public boolean isMinIncluded() { if (this.minValue == null) return true ; return isMinIncluded ; } /** * Returns true if the maximum value is included within this * Range. If the range is unbounded at this end, this * method will return true. */ public boolean isMaxIncluded() { if (this.maxValue == null) return true ; return isMaxIncluded ; } /** * Returns the Class of the elements of this Range. */ public Class getElementClass() { return elementClass; } /** * Returns the minimum value of this Range. * Returns null if the Range is unbounded at this end. */ public Comparable getMinValue() { return minValue; } /** * Returns the maximum value of this Range. * Returns null if the Range is unbounded at this end. */ public Comparable getMaxValue() { return maxValue; } /** * Returns true if the specified value is within this Range, * i.e. is either equal to or greater than the minimum value of this * Range and is either lesser than or equal to the maximum * value of this Range. * * @param value The value to be checked for being within this * Range. * @throws IllegalArgumentException if the Class of the value * parameter is not the same as the elementClass of this Range. */ public boolean contains(Comparable value) { if (value != null && value.getClass() != elementClass) { throw new IllegalArgumentException( JaiI18N.getString("Range3")); } // First check if the Range is empty if (isEmpty() == true) return false; // check both bounds. return isUnderUpperBound(value) && isOverLowerBound(value) ; } /** * Return true if the specific value is smaller than the maximum of * this range. If this range is unbounded at the maximum end, return true; * if the specific value is null and the maximum end is bounded, return * false, that is, suppose this null is the "positive infinite". */ private boolean isUnderUpperBound(Comparable value ) { // if the maximum side is unbounded, return true if (this.maxValue == null) return true; // if the object passed in is null, return false: suppose it is // the "positive infinite". So be care when use this method. if (value == null) return false; if (isMaxIncluded) return maxValue.compareTo(value) >= 0; return maxValue.compareTo(value) > 0; } /** Return true if the specific value is larger than the minimum of * this range. If this range is unbounded at the minimum end, return true; * if the specific value is null, return false; */ private boolean isOverLowerBound( Comparable value ) { // if the minimum side is unbounded, return true if (this.minValue == null) return true; // if the object passed in is null, return false: suppose it is // the "negative infinite". So be care when use this method. if (value == null) return false; if (isMinIncluded) return minValue.compareTo(value) <= 0; else return minValue.compareTo(value) < 0; } /** * Returns true if the supplied Range is fully contained * within this Range. Fully contained is defined as having * the minimum and maximum values of the fully contained range lie * within the range of values of the containing Range. * * @throws IllegalArgumentException if the Class of the * elements of the given Range is not the same as the * Class of the elements of this Range. * @throws IllegalArgumentException if the given Range is null */ public boolean contains(Range range) { if (range == null) throw new IllegalArgumentException(JaiI18N.getString("Range5")); if (elementClass != range.getElementClass()) throw new IllegalArgumentException(JaiI18N.getString("Range4")); if (range.isEmpty()) return true; Comparable min = range.getMinValue(); Comparable max = range.getMaxValue(); boolean maxSide, minSide; if (max == null) maxSide = (maxValue == null); else maxSide = isUnderUpperBound(max) || (isMaxIncluded == range.isMaxIncluded() && max.equals(maxValue)); if (min == null) minSide = (minValue == null); else minSide = isOverLowerBound(min) || (isMinIncluded == range.isMinIncluded() && min.equals(minValue)); return minSide && maxSide; } /** * Returns true if this Range intersects the * given Range. * * @throws IllegalArgumentException if the Class of the * elements of the given Range is not the same as the * Class of the elements of this Range. * @throws IllegalArgumentException if the given Range is null */ public boolean intersects(Range range) { if (range == null) throw new IllegalArgumentException(JaiI18N.getString("Range5")); if (elementClass != range.getElementClass()) throw new IllegalArgumentException(JaiI18N.getString("Range4")); return !intersect( range ).isEmpty(); } /** * Returns the union of this Range with the given * Range. If this Range and the given * Range are disjoint, the Range returned * as a result of the union will have a minimum value set to the * minimum of the two disjoint range's minimum values, and the maximum * set to the maximum of the two disjoint range's maximum values, thus * including the disjoint range within it. * * @throws IllegalArgumentException if the Class of the * elements of the given Range is not the same as the * Class of the elements of this Range. * @throws IllegalArgumentException if the given Range is null */ public Range union(Range range) { if (range == null) throw new IllegalArgumentException(JaiI18N.getString("Range5")); if (elementClass != range.getElementClass()) throw new IllegalArgumentException(JaiI18N.getString("Range4")); if (this.isEmpty()) return new Range(elementClass, range.getMinValue(), range.isMinIncluded(), range.getMaxValue(), range.isMaxIncluded()); if (range.isEmpty()) return new Range(elementClass, this.minValue, this.isMinIncluded, this.maxValue, this.isMaxIncluded); boolean containMin = !isOverLowerBound(range.getMinValue()); boolean containMax = !isUnderUpperBound(range.getMaxValue()); // If the minimum of this range is contained in the given range, the // minimum of the union is the minimum of the given range; otherwise // it is the minimum of this range. So does the boolean isMinIncluded. // Similar for the maximum end Comparable minValue = containMin ? range.getMinValue() : this.minValue; Comparable maxValue = containMax ? range.getMaxValue() : this.maxValue; boolean isMinIncluded = containMin ? range.isMinIncluded() : this.isMinIncluded; boolean isMaxIncluded = containMax ? range.isMaxIncluded() : this.isMaxIncluded; return new Range(elementClass, minValue, isMinIncluded, maxValue, isMaxIncluded); } /** * Returns the intersection of this Range with the * given Range. * * @throws IllegalArgumentException if the Class of the * elements of the given Range is not the same as the * Class of the elements of this Range. * @throws IllegalArgumentException if the given Range is null */ public Range intersect(Range range) { if (range == null) throw new IllegalArgumentException(JaiI18N.getString("Range5")); if (elementClass != range.getElementClass()) throw new IllegalArgumentException(JaiI18N.getString("Range4")); if (this.isEmpty()) { Comparable temp = this.minValue; if (temp == null) temp = this.maxValue; // get a non-null object to create an empty Range // because the range is empty, so temp will not be // null return new Range(elementClass, temp, false, temp, false); } if (range.isEmpty()) { Comparable temp = range.getMinValue(); if (temp == null) temp = range.getMaxValue(); // get a non-null object to create an empty Range // because the range is empty, so temp will not be // null return new Range(elementClass, temp, false, temp, false); } boolean containMin = !isOverLowerBound(range.getMinValue()); boolean containMax = !isUnderUpperBound(range.getMaxValue()); // If the minimum of this range is contained in the given range, the // minimum of the intersect range should be the one of this range; // similarly, we can get the maximum and the booleans. Comparable minValue = containMin ? this.minValue : range.getMinValue(); Comparable maxValue = containMax ? this.maxValue : range.getMaxValue(); boolean isMinIncluded = containMin ? this.isMinIncluded : range.isMinIncluded(); boolean isMaxIncluded = containMax ? this.isMaxIncluded : range.isMaxIncluded(); return new Range(elementClass, minValue, isMinIncluded, maxValue, isMaxIncluded); } /** * Returns the Range of values that are in this * Range but not in the given Range. If * the subtraction results in two disjoint Ranges, they * will be returned as two elements of a Range array, * otherwise the resultant Range will be returned as the * first element of a one element array. * * When this Range and the given Range are * both unbounded at both the ends (i.e both the Ranges * are all inclusive), this method will return null as the first * element of one element array, as a result of the subtraction. * * When this Range is completely contained in the * given Range, an empty Range is returned. * * @throws IllegalArgumentException if the Class of the * elements of the given Range is not the same as the * Class of the elements of this Range. */ public Range[] subtract(Range range) { if (range == null) throw new IllegalArgumentException(JaiI18N.getString("Range5")); if (elementClass != range.getElementClass()) throw new IllegalArgumentException(JaiI18N.getString("Range4")); // if this range is empty, return an empty range by copying this range; // if the given range is empty, return this range if (this.isEmpty() || range.isEmpty()) { Range[] ra = {new Range(elementClass, this.minValue, this.isMinIncluded, this.maxValue, this.isMaxIncluded)}; return ra; } Comparable min = range.getMinValue(); Comparable max = range.getMaxValue(); boolean minIn = range.isMinIncluded(); boolean maxIn = range.isMaxIncluded(); if (this.minValue == null && this.maxValue == null && min == null && max == null) { Range[] ra = {null}; return ra; } boolean containMin = this.contains(min); boolean containMax = this.contains(max); // this range may be a full range [null, null] if (containMin && containMax) { Range r1 = new Range(elementClass, this.minValue, this.isMinIncluded, min, !minIn); Range r2 = new Range(elementClass, max, !maxIn, this.maxValue, this.isMaxIncluded); // if r1 is empty , return the second section only; // or if this range and the given range are all unbounded // at the minimum end, r1 is [null, null] but // should be empty. so we need to treat it as a special case; // otherwise, a full range is returned if (r1.isEmpty() || (this.minValue == null && min == null)){ Range[] ra = {r2}; return ra; } // similar to above if (r2.isEmpty() || (this.maxValue == null && max == null)) { Range[] ra = {r1}; return ra; } Range[] ra = {r1, r2}; return ra; } // if the max of the given range is in this range, return the range // from max of given range to the max of this range else if (containMax) { Range[] ra = {new Range(elementClass, max, !maxIn, this.maxValue, this.isMaxIncluded)}; return ra; } // if the min of the given range is in this range, return the range // from the min of this range to the min of the given range else if (containMin){ Range[] ra = {new Range(elementClass, this.minValue, this.isMinIncluded, min, !minIn)}; return ra; } // no overlap, just copy this range if ((min != null && !isUnderUpperBound(min)) || (max != null && !isOverLowerBound(max))) { Range[] ra = {new Range(elementClass, this.minValue, this.isMinIncluded, this.maxValue, this.isMaxIncluded)}; return ra; } // this range is contained in the given range, return an empty range min = (this.minValue == null) ? this.maxValue : this.minValue; Range[] ra = {new Range(elementClass, min, false, min, false)}; return ra; } /** * Compute a hash code value for this Range object. * * @return a hash code value for this Range object. */ public int hashCode() { int code = this.elementClass.hashCode(); if (isEmpty()) return code; code ^= Integer.MAX_VALUE; if (this.minValue != null) { code ^= this.minValue.hashCode(); if (this.isMinIncluded) code ^= 0xFFFF0000; } if (this.maxValue != null) { code ^= this.maxValue.hashCode() * 31; if (this.isMaxIncluded) code ^= 0xFFFF; } return code; } /** * Returns true if this Range and the given * Range both have elements of the same Class, * their minimum and maximum values are the same, and their isMinIncluded() * and isMaxIncluded() methods return the same values. * * If this Range and the given Range are both * empty and the Class of their elements is the same, they * will be found to be equal and true will be returned. */ public boolean equals(Object other) { // this range is not null, so if the given object is null, // return false if (other == null) return false; // if the given object is not a range, return false if (!(other instanceof Range)) return false; Range r = (Range)other ; // if the element class is not same, return false if (this.elementClass != r.getElementClass()) return false; // two empty ranges are equal if (this.isEmpty() && r.isEmpty()) return true; Comparable min = r.getMinValue() ; // if the minimum is not null, compare both minValue and the boolean if (this.minValue != null) { if (!this.minValue.equals(min)) return false; if (this.isMinIncluded != r.isMinIncluded()) return false; } // if the minimum is unbounded, just check the given range is bounded // or not else if (min != null) return false; Comparable max = r.getMaxValue() ; // if the maximum is not null, compare both maxValue and the boolean if (this.maxValue != null) { if (!this.maxValue.equals(max)) return false; if (this.isMaxIncluded != r.isMaxIncluded()) return false; } // if the maximum is unbounded, just check the given range is bounded // or not else if (max != null) return false; return true; } /** * Returns true if this Range is empty, i.e. if the minimum * value is greater than the maximum value, if both are included, or if * the minimum value is greater than equal to the maximum value if either * the minimum or maximum value is excluded. */ public boolean isEmpty() { // an unbounded range is not empty if (minValue == null || maxValue == null) return false; int cmp = this.minValue.compareTo(this.maxValue); // if the minimum is larger than the maximum, this range is empty if (cmp > 0) return true; // if the minimum equals to the maximum and one side is not // inclusive, then it is empty if (cmp == 0) return !(isMinIncluded & isMaxIncluded) ; // otherwise, not empty return false ; } /** * Returns a String representation of this Range. */ public String toString() { // if inclusive, display '[' otherwise display '(' char c1 = isMinIncluded ? '[' : '(' ; char c2 = isMaxIncluded ? ']' : ')' ; // if both ends are bounded if (minValue != null && maxValue != null) return new String(c1 + this.minValue.toString() + ", " + this.maxValue.toString() + c2); // if the maximum end is bounded if (maxValue != null) return new String(c1 + "---, " + this.maxValue.toString() + c2) ; // if the minimum end is bounded if (minValue != null) return new String(c1+ this.minValue.toString() + ", " + "---" + c2); // both ends are unbounded return new String(c1 + "---, ---" + c2); } } jai-core-1.1.4/src/share/classes/javax/media/jai/util/CaselessStringKey.java0000644000175000017500000000565510444633025026577 0ustar mathieumathieu/* * $RCSfile: CaselessStringKey.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2006-06-16 22:52:04 $ * $State: Exp $ */ package javax.media.jai.util; import java.io.Serializable; import java.util.Locale; /** * Class to use as the key in a java.util.Map. * The case of the name is maintained but the equals() * method performs case-insensitive comparison. * * @see javax.media.jai.PropertySourceImpl * @see java.util.Map * * @since JAI 1.1 */ public final class CaselessStringKey implements Cloneable, Serializable { private String name; private String lowerCaseName; /** * Creates a CaselessStringKey for the given name. * The parameter name is stored by reference. * * @throws IllegalArgumentException if name is * null. */ public CaselessStringKey(String name) { setName(name); } /** * Returns a hash code value for the CaselessStringKey. */ public int hashCode() { return lowerCaseName.hashCode(); } /** * Returns the internal name by reference. */ public String getName() { return name; } /** * Returns a lower case version of the internal name. */ private String getLowerCaseName() { return lowerCaseName; } /** * Stores the parameter by reference in the internal name. * * @throws IllegalArgumentException if name is * null. */ public void setName(String name) { if(name == null) { throw new IllegalArgumentException(JaiI18N.getString("CaselessStringKey0")); } this.name = name; lowerCaseName = name.toLowerCase(Locale.ENGLISH); } /** * Returns a clone of the CaselessStringKey as an * Object. */ public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { // this shouldn't happen, since object is Cloneable throw new InternalError(); } } /** * Whether another Object equals this one. This will obtain * if and only if the parameter is non-null and is a * CaselessStringKey whose lower case name equals the * lower case name of this CaselessStringKey. */ public boolean equals(Object o) { if(o != null && o instanceof CaselessStringKey) { return lowerCaseName.equals(((CaselessStringKey)o).getLowerCaseName()); } return false; } /** * Returns the value returned by getName(). */ public String toString() { return getName(); } } jai-core-1.1.4/src/share/classes/javax/media/jai/util/javax.media.jai.util.properties0000644000175000017500000000110610203035544030333 0ustar mathieumathieu# # $RCSfile: javax.media.jai.util.properties,v $ # # Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. # # Use is subject to license terms. # # $Revision: 1.1 $ # $Date: 2005-02-11 04:57:57 $ # $State: Exp $ # CaselessStringKey0=The name parameter may not be null. Range0=The class type must be one of subclasses of Comparable. Range1=Minimum value must of the specified class type. Range2=Maximum value must of the specified class type. Range3=Value of incorrect Class provided. Range4=The element classes must be same. Range5=The supplied range parameter is null. jai-core-1.1.4/src/share/classes/javax/media/jai/util/ImagingListener.java0000644000175000017500000001230310203035544026235 0ustar mathieumathieu/* * $RCSfile: ImagingListener.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:57 $ * $State: Exp $ */ package javax.media.jai.util; /** * An ImagingListener has the capability to report the * abnormal situations in the image processing procedures. * The concrete class either holds the reported information and processes * it, or passes them to other processing or logging mechanisms. * *

    A default ImagingListener resides in an instance of * JAI (by calling the method * setImagingListener), and can be retrieved by calling * the method getImagingListener. This listener should not * propagate the Throwable reported from an * OperationRegistry. Otherwise, it may break the loop through * the image factories. The typical JAI to be used will * be the default JAI instance. * *

    An ImagingListener can also be attached to a rendering node * as a rendering hint with a key JAI.KEY_IMAGING_LISTENER. * This listener can monitor errors occurring in the rendering process. * The default value for this rendering hint is the listener registered * to the default JAI instance. * *

    The typical situations where ImagingListener * objects can be called are: * (1) The create method of a concrete * RenderedImageFactory or * ContextualRenderedImageFactory. (2) The rendering of the node. * For the latter case, the I/O, network, and arithmetic problems * will be reported. * *

    When errors are encountered in user-written operations, those * operations have two choices. The typical choice will be to simply * throw an exception and let the JAI framework call * errorOccurred. However, it is also acceptable to * obtain the proper ImagingListener and call * errorOccurred directly. This might be useful if * for example special retry options were available to the user * operation. Care should be taken in this case to avoid an * infinite retry loop. * *

    For backward compatibility, an instance of a simple * implementation of this interface is used as the default in all the * JAI instances. It re-throws the Throwable * if it is a RuntimeException. For the other types of * Throwable, it only prints the message and the stack trace * to the stream System.err, and returns false. * To process the reported errors or warnings an alternate implementation * of ImagingListener should be written.

    * *

    The provided Throwable, its cause, or its root cause * may be re-thrown directly, or wrapped into a subclass of * RuntimeException and thrown if this listener cannot * handle it properly, in which case the Throwable will * be propogated back to the calling application. * *

    In the JAI 1.1.2 implementation from Sun, when the method * errorOccurred is called, the parameter * isRetryable is always false; future * implementations may activate retry capability.

    * * @since JAI 1.1.2 */ public interface ImagingListener { /** * Reports the occurrence of an error or warning generated * from JAI or one of its operation nodes. * * @param message The message that describes what is reported. * @param thrown The Throwable that caused this * method to be called.. * @param where The object from which this Throwable is caught * and sent to this listener. The typical type for this * argument will be RenderedOp, * OpImage, RenderedImageFactory, * or other image types such as the * RenderedImage generated from codecs. * @param isRetryable Indicates whether or not the caller is capable of * retrying the operation, if the problem is corrected * in this method. If this parameter is false, * the return value should also be false. * This parameter can be used to stop the retry, e.g., * if a maximum retry number is reached. * * @return Returns true if the recovery is a success * and the caller should attempt a retry; otherwise * returns false (in which case no retry * should be attempted). The return value may be * ignored by the caller if isRetryable * is false. * @throws RuntimeException Propagates the Throwable to * the caller. */ boolean errorOccurred(String message, Throwable thrown, Object where, boolean isRetryable) throws RuntimeException; } jai-core-1.1.4/src/share/classes/javax/media/jai/util/ImagingException.java0000644000175000017500000001276210203035544026417 0ustar mathieumathieu/* * $RCSfile: ImagingException.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:57 $ * $State: Exp $ */ package javax.media.jai.util; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.io.PrintStream; import java.io.PrintWriter; import java.security.PrivilegedActionException; /** * This class is designed to contain information of the (abnormal) * situations that happen in JAI and the operations plugged into JAI. * The behavior of this class imitates the Exception class * in JavaTM 2 Platform version 1.4 * to define a chained exception and is call-compatible * with JavaTM 2 Platform version 1.4: * The cause can be stored and retrieved from the * instance of this class. Also, the root cause for an instance * can be retrieved. * * @since JAI 1.1.2 */ public class ImagingException extends RuntimeException { /** The cached cause. */ private Throwable cause = null; /** The default constructor. */ public ImagingException() { super(); } /** * The constructor to accept the message that describes the situation. * * @param message The message to describe the situation. */ public ImagingException(String message) { super(message); } /** * The constructor to accept the cause of this ImagingException. * * @param cause The cause of this ImagingException. */ public ImagingException(Throwable cause) { super(); this.cause = cause; } /** * The constructor to accept the cause of this ImagingException * and the message that describes the situation. * * @param message The message that describes the situation. * @param cause The cause of this ImagingException */ public ImagingException(String message, Throwable cause) { super(message); this.cause = cause; } /** * Returns the cause of this ImagingException. */ public Throwable getCause() { return cause; } /** * Recursively retrieves the root cause of this * ImagingException. */ public Throwable getRootCause() { Throwable rootCause = cause; Throwable atop = this; while(rootCause != atop && rootCause != null) { try { atop = rootCause; Method getCause = rootCause.getClass().getMethod("getCause", null); rootCause = (Throwable)getCause.invoke(rootCause, null); } catch (Exception e) { // do nothing. This happens (1) getCause method is not defined. // (2) Reflection error. So rootCause will be the same as // atop. Then stop the loop. if (rootCause instanceof InvocationTargetException) rootCause = ((InvocationTargetException)rootCause).getTargetException(); else if (rootCause instanceof PrivilegedActionException) rootCause = ((PrivilegedActionException)rootCause).getException(); else rootCause = atop; } finally { if (rootCause == null) rootCause = atop; } } return rootCause; } /** * Prints the stack trace. For JavaTM 2 * Platform version 1.4 and up, calls the same * method in the super class. For JavaTM * 2 Platform version 1.3, prints the stack trace of * this ImagingException and the trace of its cause. */ public void printStackTrace() { printStackTrace(System.err); } /** * Prints the stack trace. For JavaTM * 2 Platform version 1.4 and up, calls the same * method in the super class. For JavaTM * 2 Platform version 1.4, prints the stack trace of * this ImagingException and the trace of its cause. */ public void printStackTrace(PrintStream s) { synchronized (s) { super.printStackTrace(s); boolean is14 = false; try { String version = System.getProperty("java.version"); is14 = version.indexOf("1.4") >=0 ; } catch(Exception e) { } if (!is14 && cause != null) { s.println("Caused by:"); cause.printStackTrace(s); } } } /** * Prints the stack trace. For JavaTM 2 * Platform version 1.4 and up, calls the same * method in the super class. For JavaTM * 2 Platform version 1.43, prints the stack trace of * this ImagingException and the trace of its cause. */ public void printStackTrace(PrintWriter s) { synchronized (s) { super.printStackTrace(s); boolean is14 = false; try { String version = System.getProperty("java.version"); is14 = version.indexOf("1.4") >=0 ; } catch(Exception e) { } if (!is14 && cause != null) { s.println("Caused by:"); cause.printStackTrace(s); } } } /* public static void main(String[] args) { Exception ce = new ImagingException(new RuntimeException("test")); Throwable cause = ce.getCause(); System.out.println("Cause: " + cause); System.out.println("Root cause: " + ((ImagingException)ce).getRootCause()); ce.printStackTrace(); } */ } jai-core-1.1.4/src/share/classes/javax/media/jai/util/JaiI18N.java0000644000175000017500000000073710203035544024267 0ustar mathieumathieu/* * $RCSfile: JaiI18N.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:57 $ * $State: Exp $ */ package javax.media.jai.util; import com.sun.media.jai.util.PropertyUtil; class JaiI18N { static String packageName = "javax.media.jai.util"; public static String getString(String key) { return PropertyUtil.getString(packageName, key); } } jai-core-1.1.4/src/share/classes/javax/media/jai/LookupTableJAI.java0000644000175000017500000023523410203035544024756 0ustar mathieumathieu/* * $RCSfile: LookupTableJAI.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:12 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Rectangle; import java.awt.Point; import java.awt.image.BandedSampleModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferInt; import java.awt.image.DataBufferShort; import java.awt.image.DataBufferUShort; import java.awt.image.Raster; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import javax.media.jai.remote.SerializableState; import javax.media.jai.remote.SerializerFactory; import com.sun.media.jai.util.DataBufferUtils; /** * A lookup table object associated with the "Lookup" operation. The * "Lookup" operation is described in * javax.media.jai.operator.LookupDescriptor. * *

    This object represents a single- or multi-banded table of any * JAI supported data type. A single- or multi-banded source image * of integral data types is passed through the table and transformed * into a single- or multi-banded destination image of either integral * and floating point data types. * *

    The table data may cover only a subrange of the legal range of the * input data type. The subrange is selected by means of an offset parameter * which is to be subtracted from the input value before indexing into the * table array. When only a subranged table is used with a source image, it * is up to the user to make certain that the source image does not have * pixel values outside of the table range. Otherwise, * an ArrayIndexOutOfBoundsException can occur. * *

    The table data is saved by reference only. * * @see javax.media.jai.operator.LookupDescriptor * */ public class LookupTableJAI extends Object implements Serializable { /** The table data. */ transient DataBuffer data; /** The band offset values */ private int[] tableOffsets; /** * Constructs a single-banded byte lookup table. The index offset is 0. * * @param data The single-banded byte data. * @throws IllegalArgumentException if data is null. */ public LookupTableJAI(byte[] data) { if ( data == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.data = new DataBufferByte(data, data.length); this.initOffsets(1, 0); } /** * Constructs a single-banded byte lookup table with an index offset. * * @param data The single-banded byte data. * @param offset The offset. * @throws IllegalArgumentException if data is null. */ public LookupTableJAI(byte[] data, int offset) { if ( data == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.initOffsets(1, offset); this.data = new DataBufferByte(data, data.length); } /** * Constructs a multi-banded byte lookup table. The index offset for * each band is 0. * * @param data The multi-banded byte data in [band][index] format. * @throws IllegalArgumentException if data is null. */ public LookupTableJAI(byte[][] data) { if ( data == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.initOffsets(data.length, 0); this.data = new DataBufferByte(data, data[0].length); } /** * Constructs a multi-banded byte lookup table where all bands have * the same index offset. * * @param data The multi-banded byte data in [band][index] format. * @param offset The common offset for all bands. * @throws IllegalArgumentException if data is null. */ public LookupTableJAI(byte[][] data, int offset) { if ( data == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.initOffsets(data.length, offset); this.data = new DataBufferByte(data, data[0].length); } /** * Constructs a multi-banded byte lookup table where each band has * a different index offset. * * @param data The multi-banded byte data in [band][index] format. * @param offsets The offsets for the bands. * @throws IllegalArgumentException if data is null. */ public LookupTableJAI(byte[][] data, int[] offsets) { if ( data == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.initOffsets(data.length, offsets); this.data = new DataBufferByte(data, data[0].length); } /** * Constructs a single-banded short or unsigned short lookup table. * The index offset is 0. * * @param data The single-banded short data. * @param isUShort True if data type is DataBuffer.TYPE_USHORT; * false if data type is DataBuffer.TYPE_SHORT. * @throws IllegalArgumentException if data is null. */ public LookupTableJAI(short[] data, boolean isUShort) { if ( data == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.initOffsets(1, 0); if (isUShort) { this.data = new DataBufferUShort(data, data.length); } else { this.data = new DataBufferShort(data, data.length); } } /** * Constructs a single-banded short or unsigned short lookup table with * an index offset. * * @param data The single-banded short data. * @param offset The offset. * @param isUShort True if data type is DataBuffer.TYPE_USHORT; * false if data type is DataBuffer.TYPE_SHORT. * @throws IllegalArgumentException if data is null. */ public LookupTableJAI(short[] data, int offset, boolean isUShort) { if ( data == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.initOffsets(1, offset); if (isUShort) { this.data = new DataBufferUShort(data, data.length); } else { this.data = new DataBufferShort(data, data.length); } } /** * Constructs a multi-banded short or unsigned short lookup table. * The index offset for each band is 0. * * @param data The multi-banded short data in [band][index] format. * @param isUShort True if data type is DataBuffer.TYPE_USHORT; * false if data type is DataBuffer.TYPE_SHORT. * @throws IllegalArgumentException if data is null. */ public LookupTableJAI(short[][] data, boolean isUShort) { if ( data == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.initOffsets(data.length, 0); if (isUShort) { this.data = new DataBufferUShort(data, data[0].length); } else { this.data = new DataBufferShort(data, data[0].length); } } /** * Constructs a multi-banded short or unsigned short lookup table where all * bands have the same index offset. * * @param data The multi-banded short data in [band][index] format. * @param offset The common offset for all bands. * @param isUShort True if data type is DataBuffer.TYPE_USHORT; * false if data type is DataBuffer.TYPE_SHORT. * @throws IllegalArgumentException if data is null. */ public LookupTableJAI(short[][] data, int offset, boolean isUShort) { if ( data == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.initOffsets(data.length, offset); if (isUShort) { this.data = new DataBufferUShort(data, data[0].length); } else { this.data = new DataBufferShort(data, data[0].length); } } /** * Constructs a multi-banded short or unsigned short lookup table where * each band has a different index offset. * * @param data The multi-banded short data in [band][index] format. * @param offsets The offsets for the bands. * @param isUShort True if data type is DataBuffer.TYPE_USHORT; * false if data type is DataBuffer.TYPE_SHORT. * @throws IllegalArgumentException if data is null. */ public LookupTableJAI(short[][] data, int[] offsets, boolean isUShort) { if ( data == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.initOffsets(data.length, offsets); if (isUShort) { this.data = new DataBufferUShort(data, data[0].length); } else { this.data = new DataBufferShort(data, data[0].length); } } /** * Constructs a single-banded int lookup table. The index offset is 0. * * @param data The single-banded int data. * @throws IllegalArgumentException if data is null. */ public LookupTableJAI(int[] data) { if ( data == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.initOffsets(1, 0); this.data = new DataBufferInt(data, data.length); } /** * Constructs a single-banded int lookup table with an index offset. * * @param data The single-banded int data. * @param offset The offset. * @throws IllegalArgumentException if data is null. */ public LookupTableJAI(int[] data, int offset) { if ( data == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.initOffsets(1, offset); this.data = new DataBufferInt(data, data.length); } /** * Constructs a multi-banded int lookup table. The index offset for * each band is 0. * * @param data The multi-banded int data in [band][index] format. * @throws IllegalArgumentException if data is null. */ public LookupTableJAI(int[][] data) { if ( data == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.initOffsets(data.length, 0); this.data = new DataBufferInt(data, data[0].length); } /** * Constructs a multi-banded int lookup table where all bands have * the same index offset. * * @param data The multi-banded int data in [band][index] format. * @param offset The common offset for all bands. * @throws IllegalArgumentException if data is null. */ public LookupTableJAI(int[][] data, int offset) { if ( data == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.initOffsets(data.length, offset); this.data = new DataBufferInt(data, data[0].length); } /** * Constructs a multi-banded int lookup table where each band has * a different index offset. * * @param data The multi-banded int data in [band][index] format. * @param offsets The offsets for the bands. * @throws IllegalArgumentException if data is null. */ public LookupTableJAI(int[][] data, int[] offsets) { if ( data == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.initOffsets(data.length, offsets); this.data = new DataBufferInt(data, data[0].length); } /** * Constructs a single-banded float lookup table. The index offset is 0. * * @param data The single-banded float data. * @throws IllegalArgumentException if data is null. */ public LookupTableJAI(float[] data) { if ( data == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.initOffsets(1, 0); this.data = DataBufferUtils.createDataBufferFloat(data, data.length); } /** * Constructs a single-banded float lookup table with an index offset. * * @param data The single-banded float data. * @param offset The offset. * @throws IllegalArgumentException if data is null. */ public LookupTableJAI(float[] data, int offset) { if ( data == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.initOffsets(1, offset); this.data = DataBufferUtils.createDataBufferFloat(data, data.length); } /** * Constructs a multi-banded float lookup table. The index offset for * each band is 0. * * @param data The multi-banded float data in [band][index] format. * @throws IllegalArgumentException if data is null. */ public LookupTableJAI(float[][] data) { if ( data == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.initOffsets(data.length, 0); this.data = DataBufferUtils.createDataBufferFloat(data, data[0].length); } /** * Constructs a multi-banded float lookup table where all bands have * the same index offset. * * @param data The multi-banded float data in [band][index] format. * @param offset The common offset for all bands. * @throws IllegalArgumentException if data is null. */ public LookupTableJAI(float[][] data, int offset) { if ( data == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.initOffsets(data.length, offset); this.data = DataBufferUtils.createDataBufferFloat(data, data[0].length); } /** * Constructs a multi-banded float lookup table where each band has * a different index offset. * * @param data The multi-banded float data in [band][index] format. * @param offsets The offsets for the bands. * @throws IllegalArgumentException if data is null. */ public LookupTableJAI(float[][] data, int[] offsets) { if ( data == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.initOffsets(data.length, offsets); this.data = DataBufferUtils.createDataBufferFloat(data, data[0].length); } /** * Constructs a single-banded double lookup table. The index offset is 0. * * @param data The single-banded double data. * @throws IllegalArgumentException if data is null. */ public LookupTableJAI(double[] data) { if ( data == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.initOffsets(1, 0); this.data = DataBufferUtils.createDataBufferDouble(data, data.length); } /** * Constructs a single-banded double lookup table with an index offset. * * @param data The single-banded double data. * @param offset The offset. * @throws IllegalArgumentException if data is null. */ public LookupTableJAI(double[] data, int offset) { if ( data == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.initOffsets(1, offset); this.data = DataBufferUtils.createDataBufferDouble(data, data.length); } /** * Constructs a multi-banded double lookup table. The index offset for * each band is 0. * * @param data The multi-banded double data in [band][index] format. * @throws IllegalArgumentException if data is null. */ public LookupTableJAI(double[][] data) { if ( data == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.initOffsets(data.length, 0); this.data = DataBufferUtils.createDataBufferDouble(data, data[0].length); } /** * Constructs a multi-banded double lookup table where all bands have * the same index offset. * * @param data The multi-banded double data in [band][index] format. * @param offset The common offset for all bands. * @throws IllegalArgumentException if data is null. */ public LookupTableJAI(double[][] data, int offset) { if ( data == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.initOffsets(data.length, offset); this.data = DataBufferUtils.createDataBufferDouble(data, data[0].length); } /** * Constructs a multi-banded double lookup table where each band has * a different index offset. * * @param data The multi-banded double data in [band][index] format. * @param offsets The offsets for the bands. * @throws IllegalArgumentException if data is null. */ public LookupTableJAI(double[][] data, int[] offsets) { if ( data == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.initOffsets(data.length, offsets); this.data = DataBufferUtils.createDataBufferDouble(data, data[0].length); } /** * Returns the table data as a DataBuffer. */ public DataBuffer getData() { return data; } /** * Returns the byte table data in array format, or null if the * table's data type is not byte. */ public byte[][] getByteData() { return data instanceof DataBufferByte ? ((DataBufferByte)data).getBankData() : null; } /** * Returns the byte table data of a specific band in array format, * or null if the table's data type is not byte. */ public byte[] getByteData(int band) { return data instanceof DataBufferByte ? ((DataBufferByte)data).getData(band) : null; } /** * Returns the short table data in array format, or null if the * table's data type is not short. This includes both signed and * unsigned short table data. * */ public short[][] getShortData() { if (data instanceof DataBufferUShort) { return ((DataBufferUShort)data).getBankData(); } else if (data instanceof DataBufferShort) { return ((DataBufferShort)data).getBankData(); } else { return null; } } /** * Returns the short table data of a specific band in array format, * or null if the table's data type is not short. * */ public short[] getShortData(int band) { if (data instanceof DataBufferUShort) { return ((DataBufferUShort)data).getData(band); } else if (data instanceof DataBufferShort) { return ((DataBufferShort)data).getData(band); } else { return null; } } /** * Returns the integer table data in array format, or null if the * table's data type is not int. * */ public int[][] getIntData() { return data instanceof DataBufferInt ? ((DataBufferInt)data).getBankData() : null; } /** * Returns the integer table data of a specific band in array format, * or null if table's data type is not int. * */ public int[] getIntData(int band) { return data instanceof DataBufferInt ? ((DataBufferInt)data).getData(band) : null; } /** * Returns the float table data in array format, or null if the * table's data type is not float. * */ public float[][] getFloatData() { return data.getDataType() == DataBuffer.TYPE_FLOAT ? DataBufferUtils.getBankDataFloat(data) : null; } /** * Returns the float table data of a specific band in array format, * or null if table's data type is not float. * */ public float[] getFloatData(int band) { return data.getDataType() == DataBuffer.TYPE_FLOAT ? DataBufferUtils.getDataFloat(data, band) : null; } /** * Returns the double table data in array format, or null if the * table's data type is not double. * */ public double[][] getDoubleData() { return data.getDataType() == DataBuffer.TYPE_DOUBLE ? DataBufferUtils.getBankDataDouble(data) : null; } /** * Returns the double table data of a specific band in array format, * or null if table's data type is not double. * */ public double[] getDoubleData(int band) { return data.getDataType() == DataBuffer.TYPE_DOUBLE ? DataBufferUtils.getDataDouble(data, band) : null; } /** Returns the index offsets of entry 0 for all bands. */ public int[] getOffsets() { return tableOffsets; } /** * Returns the index offset of entry 0 for the default band. * */ public int getOffset() { return tableOffsets[0]; } /** * Returns the index offset of entry 0 for a specific band. * */ public int getOffset(int band) { return tableOffsets[band]; } /** Returns the number of bands of the table. */ public int getNumBands() { return data.getNumBanks(); } /** * Returns the number of entries per band of the table. * */ public int getNumEntries() { return data.getSize(); } /** Returns the data type of the table data. * */ public int getDataType() { return data.getDataType(); } /** * Returns the number of bands of the destination image, based on * the number of bands of the source image and lookup table. * * @param srcNumBands The number of bands of the source image. * @return the number of bands in destination image. */ public int getDestNumBands(int srcNumBands) { int tblNumBands = getNumBands(); return srcNumBands == 1 ? tblNumBands : srcNumBands; } /** * Returns a SampleModel suitable for holding the output * of a lookup operation on the source data described by a given * SampleModel with this table. The width and height of the destination * SampleModel are the same as that of the source. This method will * return null if the source SampleModel has a non-integral data type. * * @param srcSampleModel The SampleModel of the source image. * * @throws IllegalArgumentException if srcSampleModel is null. * @return sampleModel suitable for the destination image. */ public SampleModel getDestSampleModel(SampleModel srcSampleModel) { if ( srcSampleModel == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return getDestSampleModel(srcSampleModel, srcSampleModel.getWidth(), srcSampleModel.getHeight()); } /** * Returns a SampleModel suitable for holding the output * of a lookup operation on the source data described by a given * SampleModel with this table. This method will return null if the * source SampleModel has a non-integral data type. * * @param srcSampleModel The SampleModel of the source image. * @param width The width of the destination SampleModel. * @param height The height of the destination SampleModel. * * @throws IllegalArgumentException if srcSampleModel is null. * @return sampleModel suitable for the destination image. */ public SampleModel getDestSampleModel(SampleModel srcSampleModel, int width, int height) { if ( srcSampleModel == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (!isIntegralDataType(srcSampleModel)) { return null; // source has non-integral data type } return RasterFactory.createComponentSampleModel(srcSampleModel, getDataType(), width, height, getDestNumBands(srcSampleModel.getNumBands())); } /** * Validates data type. Returns true if it's one of the integral * data types; false otherwise. * * @throws IllegalArgumentException if sampleModel is null. */ public boolean isIntegralDataType(SampleModel sampleModel) { if ( sampleModel == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return isIntegralDataType(sampleModel.getTransferType()); } /** * Returns true if the specified data type is * an integral data type, such as byte, ushort, short, or int. */ public boolean isIntegralDataType(int dataType) { if ((dataType == DataBuffer.TYPE_BYTE) || (dataType == DataBuffer.TYPE_USHORT) || (dataType == DataBuffer.TYPE_SHORT) || (dataType == DataBuffer.TYPE_INT)){ return true; } else { return false; } } /** * Performs lookup on a given value belonging to a given source * band, and returns the result as an int. * * @param band The source band the value is from. * @param value The source value to be placed through the lookup table. */ public int lookup(int band, int value) { return data.getElem(band, value-tableOffsets[band]); } /** * Performs lookup on a given value belonging to a given source * band, and returns the result as a float. * * @param band The source band the value is from. * @param value The source value to be placed through the lookup table. */ public float lookupFloat(int band, int value) { return data.getElemFloat(band, value-tableOffsets[band]); } /** * Performs lookup on a given value belonging to a given source * band, and returns the result as a double. * * @param band The source band the value is from. * @param value The source value to be placed through the lookup table. */ public double lookupDouble(int band, int value) { return data.getElemDouble(band, value-tableOffsets[band]); } /** * Performs table lookup in place on a given WritableRaster. The * The lookup operation must preserve the data type and * SampleModel of the source. A reference to the supplied * WritableRaster will be returned. * * @throws IllegalArgumentException if the src is null. * @throws IllegalArgumentException if the source's SampleModel * is not of integral type. * @throws IllegalArgumentException if the lookup operation would * result in a change in the data type or number of bands * of the Raster. */ public WritableRaster lookup(WritableRaster src) { if ( src == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return lookup(src, src, src.getBounds()); } /** * Performs table lookup on a source Raster, writing the result * into a supplied WritableRaster. The destination must have a * data type and SampleModel appropriate to the results of the * lookup operation. The table lookup operation is performed * within a specified rectangle. * *

    The dst argument may be null, in which case a new * WritableRaster is created using the appropriate SampleModel. * *

    The rectangle of interest may be null, in which case the * operation will be performed on the intersection of the source * and destination bounding rectangles. * * @param src A Raster containing the source pixel data. * @param dst The WritableRaster to be computed, or null. * If supplied, its data type and number of bands must * be suitable for the source and lookup table. * @param rect The rectangle within the tile to be computed. * If rect is null, the intersection of the source and * destination bounds will be used. Otherwise, it * will be clipped to the intersection of the source * and destination bounds. * @return A reference to the supplied WritableRaster, or to a * new WritableRaster if the supplied one was null. * * @throws IllegalArgumentException if the source is null. * @throws IllegalArgumentException if the source's SampleModel * is not of integral type. * @throws IllegalArgumentException if the destination's data type * or number of bands differ from those returned by * getDataType() and getDestNumBands(). */ public WritableRaster lookup(Raster src, WritableRaster dst, Rectangle rect) { // Validate source. if (src == null) { throw new IllegalArgumentException(JaiI18N.getString("LookupTableJAI1")); } SampleModel srcSampleModel = src.getSampleModel(); if (!isIntegralDataType(srcSampleModel)) { throw new IllegalArgumentException(JaiI18N.getString("LookupTableJAI2")); } // Validate rectangle. if (rect == null) { rect = src.getBounds(); } else { rect = rect.intersection(src.getBounds()); } if (dst != null) { rect = rect.intersection(dst.getBounds()); } // Validate destination. SampleModel dstSampleModel; if (dst == null) { // create dst according to table dstSampleModel = getDestSampleModel(srcSampleModel, rect.width, rect.height); dst = RasterFactory.createWritableRaster(dstSampleModel, new Point(rect.x, rect.y)); } else { dstSampleModel = dst.getSampleModel(); if (dstSampleModel.getTransferType() != getDataType() || dstSampleModel.getNumBands() != getDestNumBands(srcSampleModel.getNumBands())) { throw new IllegalArgumentException(JaiI18N.getString("LookupTableJAI3")); } } // Add bit support? int sTagID = RasterAccessor.findCompatibleTag(null, srcSampleModel); int dTagID = RasterAccessor.findCompatibleTag(null, dstSampleModel); RasterFormatTag sTag = new RasterFormatTag(srcSampleModel,sTagID); RasterFormatTag dTag = new RasterFormatTag(dstSampleModel,dTagID); RasterAccessor s = new RasterAccessor(src, rect, sTag, null); RasterAccessor d = new RasterAccessor(dst, rect, dTag, null); int srcNumBands = s.getNumBands(); int srcDataType = s.getDataType(); int tblNumBands = getNumBands(); int tblDataType = getDataType(); int dstWidth = d.getWidth(); int dstHeight = d.getHeight(); int dstNumBands = d.getNumBands(); int dstDataType = d.getDataType(); // Source information. int srcLineStride = s.getScanlineStride(); int srcPixelStride = s.getPixelStride(); int[] srcBandOffsets = s.getBandOffsets(); byte[][] bSrcData = s.getByteDataArrays(); short[][] sSrcData = s.getShortDataArrays(); int[][] iSrcData = s.getIntDataArrays(); if (srcNumBands < dstNumBands) { int offset0 = srcBandOffsets[0]; srcBandOffsets = new int[dstNumBands]; for (int i = 0; i < dstNumBands; i++) { srcBandOffsets[i] = offset0; } switch (srcDataType) { case DataBuffer.TYPE_BYTE: byte[] bData0 = bSrcData[0]; bSrcData = new byte[dstNumBands][]; for (int i = 0; i < dstNumBands; i++) { bSrcData[i] = bData0; } break; case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: short[] sData0 = sSrcData[0]; sSrcData = new short[dstNumBands][]; for (int i = 0; i < dstNumBands; i++) { sSrcData[i] = sData0; } break; case DataBuffer.TYPE_INT: int[] iData0 = iSrcData[0]; iSrcData = new int[dstNumBands][]; for (int i = 0; i < dstNumBands; i++) { iSrcData[i] = iData0; } break; } } // Table information. int[] tblOffsets = getOffsets(); byte[][] bTblData = getByteData(); short[][] sTblData = getShortData(); int[][] iTblData = getIntData(); float[][] fTblData = getFloatData(); double[][] dTblData = getDoubleData(); if (tblNumBands < dstNumBands) { int offset0 = tblOffsets[0]; tblOffsets = new int[dstNumBands]; for (int i = 0; i < dstNumBands; i++) { tblOffsets[i] = offset0; } switch (tblDataType) { case DataBuffer.TYPE_BYTE: byte[] bData0 = bTblData[0]; bTblData = new byte[dstNumBands][]; for (int i = 0; i < dstNumBands; i++) { bTblData[i] = bData0; } break; case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: short[] sData0 = sTblData[0]; sTblData = new short[dstNumBands][]; for (int i = 0; i < dstNumBands; i++) { sTblData[i] = sData0; } break; case DataBuffer.TYPE_INT: int[] iData0 = iTblData[0]; iTblData = new int[dstNumBands][]; for (int i = 0; i < dstNumBands; i++) { iTblData[i] = iData0; } break; case DataBuffer.TYPE_FLOAT: float[] fData0 = fTblData[0]; fTblData = new float[dstNumBands][]; for (int i = 0; i < dstNumBands; i++) { fTblData[i] = fData0; } break; case DataBuffer.TYPE_DOUBLE: double[] dData0 = dTblData[0]; dTblData = new double[dstNumBands][]; for (int i = 0; i < dstNumBands; i++) { dTblData[i] = dData0; } } } // Destination information. int dstLineStride = d.getScanlineStride(); int dstPixelStride = d.getPixelStride(); int[] dstBandOffsets = d.getBandOffsets(); byte[][] bDstData = d.getByteDataArrays(); short[][] sDstData = d.getShortDataArrays(); int[][] iDstData = d.getIntDataArrays(); float[][] fDstData = d.getFloatDataArrays(); double[][] dDstData = d.getDoubleDataArrays(); switch (dstDataType) { case DataBuffer.TYPE_BYTE: switch (srcDataType) { case DataBuffer.TYPE_BYTE: lookup(srcLineStride, srcPixelStride, srcBandOffsets, bSrcData, dstWidth, dstHeight, dstNumBands, dstLineStride, dstPixelStride, dstBandOffsets, bDstData, tblOffsets, bTblData); break; case DataBuffer.TYPE_USHORT: lookupU(srcLineStride, srcPixelStride, srcBandOffsets, sSrcData, dstWidth, dstHeight, dstNumBands, dstLineStride, dstPixelStride, dstBandOffsets, bDstData, tblOffsets, bTblData); break; case DataBuffer.TYPE_SHORT: lookup(srcLineStride, srcPixelStride, srcBandOffsets, sSrcData, dstWidth, dstHeight, dstNumBands, dstLineStride, dstPixelStride, dstBandOffsets, bDstData, tblOffsets, bTblData); break; case DataBuffer.TYPE_INT: lookup(srcLineStride, srcPixelStride, srcBandOffsets, iSrcData, dstWidth, dstHeight, dstNumBands, dstLineStride, dstPixelStride, dstBandOffsets, bDstData, tblOffsets, bTblData); break; } break; case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: switch (srcDataType) { case DataBuffer.TYPE_BYTE: lookup(srcLineStride, srcPixelStride, srcBandOffsets, bSrcData, dstWidth, dstHeight, dstNumBands, dstLineStride, dstPixelStride, dstBandOffsets, sDstData, tblOffsets, sTblData); break; case DataBuffer.TYPE_USHORT: lookupU(srcLineStride, srcPixelStride, srcBandOffsets, sSrcData, dstWidth, dstHeight, dstNumBands, dstLineStride, dstPixelStride, dstBandOffsets, sDstData, tblOffsets, sTblData); break; case DataBuffer.TYPE_SHORT: lookup(srcLineStride, srcPixelStride, srcBandOffsets, sSrcData, dstWidth, dstHeight, dstNumBands, dstLineStride, dstPixelStride, dstBandOffsets, sDstData, tblOffsets, sTblData); break; case DataBuffer.TYPE_INT: lookup(srcLineStride, srcPixelStride, srcBandOffsets, iSrcData, dstWidth, dstHeight, dstNumBands, dstLineStride, dstPixelStride, dstBandOffsets, sDstData, tblOffsets, sTblData); break; } break; case DataBuffer.TYPE_INT: switch (srcDataType) { case DataBuffer.TYPE_BYTE: lookup(srcLineStride, srcPixelStride, srcBandOffsets, bSrcData, dstWidth, dstHeight, dstNumBands, dstLineStride, dstPixelStride, dstBandOffsets, iDstData, tblOffsets, iTblData); break; case DataBuffer.TYPE_USHORT: lookupU(srcLineStride, srcPixelStride, srcBandOffsets, sSrcData, dstWidth, dstHeight, dstNumBands, dstLineStride, dstPixelStride, dstBandOffsets, iDstData, tblOffsets, iTblData); break; case DataBuffer.TYPE_SHORT: lookup(srcLineStride, srcPixelStride, srcBandOffsets, sSrcData, dstWidth, dstHeight, dstNumBands, dstLineStride, dstPixelStride, dstBandOffsets, iDstData, tblOffsets, iTblData); break; case DataBuffer.TYPE_INT: lookup(srcLineStride, srcPixelStride, srcBandOffsets, iSrcData, dstWidth, dstHeight, dstNumBands, dstLineStride, dstPixelStride, dstBandOffsets, iDstData, tblOffsets, iTblData); break; } break; case DataBuffer.TYPE_FLOAT: switch (srcDataType) { case DataBuffer.TYPE_BYTE: lookup(srcLineStride, srcPixelStride, srcBandOffsets, bSrcData, dstWidth, dstHeight, dstNumBands, dstLineStride, dstPixelStride, dstBandOffsets, fDstData, tblOffsets, fTblData); break; case DataBuffer.TYPE_USHORT: lookupU(srcLineStride, srcPixelStride, srcBandOffsets, sSrcData, dstWidth, dstHeight, dstNumBands, dstLineStride, dstPixelStride, dstBandOffsets, fDstData, tblOffsets, fTblData); break; case DataBuffer.TYPE_SHORT: lookup(srcLineStride, srcPixelStride, srcBandOffsets, sSrcData, dstWidth, dstHeight, dstNumBands, dstLineStride, dstPixelStride, dstBandOffsets, fDstData, tblOffsets, fTblData); break; case DataBuffer.TYPE_INT: lookup(srcLineStride, srcPixelStride, srcBandOffsets, iSrcData, dstWidth, dstHeight, dstNumBands, dstLineStride, dstPixelStride, dstBandOffsets, fDstData, tblOffsets, fTblData); break; } break; case DataBuffer.TYPE_DOUBLE: switch (srcDataType) { case DataBuffer.TYPE_BYTE: lookup(srcLineStride, srcPixelStride, srcBandOffsets, bSrcData, dstWidth, dstHeight, dstNumBands, dstLineStride, dstPixelStride, dstBandOffsets, dDstData, tblOffsets, dTblData); break; case DataBuffer.TYPE_USHORT: lookupU(srcLineStride, srcPixelStride, srcBandOffsets, sSrcData, dstWidth, dstHeight, dstNumBands, dstLineStride, dstPixelStride, dstBandOffsets, dDstData, tblOffsets, dTblData); break; case DataBuffer.TYPE_SHORT: lookup(srcLineStride, srcPixelStride, srcBandOffsets, sSrcData, dstWidth, dstHeight, dstNumBands, dstLineStride, dstPixelStride, dstBandOffsets, dDstData, tblOffsets, dTblData); break; case DataBuffer.TYPE_INT: lookup(srcLineStride, srcPixelStride, srcBandOffsets, iSrcData, dstWidth, dstHeight, dstNumBands, dstLineStride, dstPixelStride, dstBandOffsets, dDstData, tblOffsets, dTblData); break; } break; } d.copyDataToRaster(); return dst; } // byte to byte private void lookup(int srcLineStride, int srcPixelStride, int[] srcBandOffsets, byte[][] srcData, int width, int height, int bands, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, byte[][] dstData, int[] tblOffsets, byte[][] tblData) { for (int b = 0; b < bands; b++) { byte[] s = srcData[b]; byte[] d = dstData[b]; byte[] t = tblData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; int tblOffset = tblOffsets[b]; for (int h = 0; h < height; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < width; w++) { d[dstPixelOffset] = t[(s[srcPixelOffset]&0xFF) - tblOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } // ushort to byte private void lookupU(int srcLineStride, int srcPixelStride, int[] srcBandOffsets, short[][] srcData, int width, int height, int bands, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, byte[][] dstData, int[] tblOffsets, byte[][] tblData) { for (int b = 0; b < bands; b++) { short[] s = srcData[b]; byte[] d = dstData[b]; byte[] t = tblData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; int tblOffset = tblOffsets[b]; for (int h = 0; h < height; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < width; w++) { d[dstPixelOffset] = t[(s[srcPixelOffset]&0xFFFF) - tblOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } // short to byte private void lookup(int srcLineStride, int srcPixelStride, int[] srcBandOffsets, short[][] srcData, int width, int height, int bands, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, byte[][] dstData, int[] tblOffsets, byte[][] tblData) { for (int b = 0; b < bands; b++) { short[] s = srcData[b]; byte[] d = dstData[b]; byte[] t = tblData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; int tblOffset = tblOffsets[b]; for (int h = 0; h < height; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < width; w++) { d[dstPixelOffset] = t[s[srcPixelOffset] - tblOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } // int to byte private void lookup(int srcLineStride, int srcPixelStride, int[] srcBandOffsets, int[][] srcData, int width, int height, int bands, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, byte[][] dstData, int[] tblOffsets, byte[][] tblData) { for (int b = 0; b < bands; b++) { int[] s = srcData[b]; byte[] d = dstData[b]; byte[] t = tblData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; int tblOffset = tblOffsets[b]; for (int h = 0; h < height; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < width; w++) { d[dstPixelOffset] = t[s[srcPixelOffset] - tblOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } // byte to short or ushort private void lookup(int srcLineStride, int srcPixelStride, int[] srcBandOffsets, byte[][] srcData, int width, int height, int bands, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, short[][] dstData, int[] tblOffsets, short[][] tblData) { for (int b = 0; b < bands; b++) { byte[] s = srcData[b]; short[] d = dstData[b]; short[] t = tblData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; int tblOffset = tblOffsets[b]; for (int h = 0; h < height; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < width; w++) { d[dstPixelOffset] = t[(s[srcPixelOffset]&0xFF) - tblOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } // ushort to short or ushort private void lookupU(int srcLineStride, int srcPixelStride, int[] srcBandOffsets, short[][] srcData, int width, int height, int bands, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, short[][] dstData, int[] tblOffsets, short[][] tblData) { for (int b = 0; b < bands; b++) { short[] s = srcData[b]; short[] d = dstData[b]; short[] t = tblData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; int tblOffset = tblOffsets[b]; for (int h = 0; h < height; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < width; w++) { d[dstPixelOffset] = t[(s[srcPixelOffset]&0xFFFF) - tblOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } // short to short or ushort private void lookup(int srcLineStride, int srcPixelStride, int[] srcBandOffsets, short[][] srcData, int width, int height, int bands, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, short[][] dstData, int[] tblOffsets, short[][] tblData) { for (int b = 0; b < bands; b++) { short[] s = srcData[b]; short[] d = dstData[b]; short[] t = tblData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; int tblOffset = tblOffsets[b]; for (int h = 0; h < height; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < width; w++) { d[dstPixelOffset] = t[s[srcPixelOffset] - tblOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } // int to short or ushort private void lookup(int srcLineStride, int srcPixelStride, int[] srcBandOffsets, int[][] srcData, int width, int height, int bands, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, short[][] dstData, int[] tblOffsets, short[][] tblData) { for (int b = 0; b < bands; b++) { int[] s = srcData[b]; short[] d = dstData[b]; short[] t = tblData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; int tblOffset = tblOffsets[b]; for (int h = 0; h < height; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < width; w++) { d[dstPixelOffset] = t[s[srcPixelOffset] - tblOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } // byte to int private void lookup(int srcLineStride, int srcPixelStride, int[] srcBandOffsets, byte[][] srcData, int width, int height, int bands, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, int[][] dstData, int[] tblOffsets, int[][] tblData) { if (tblData == null) { for (int b = 0; b < bands; b++) { byte[] s = srcData[b]; int[] d = dstData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < height; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < width; w++) { d[dstPixelOffset] = data.getElem(b, s[srcPixelOffset]&0xFF); srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } else { for (int b = 0; b < bands; b++) { byte[] s = srcData[b]; int[] d = dstData[b]; int[] t = tblData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; int tblOffset = tblOffsets[b]; for (int h = 0; h < height; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < width; w++) { d[dstPixelOffset] = t[(s[srcPixelOffset]&0xFF) - tblOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } } // ushort to int private void lookupU(int srcLineStride, int srcPixelStride, int[] srcBandOffsets, short[][] srcData, int width, int height, int bands, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, int[][] dstData, int[] tblOffsets, int[][] tblData) { if (tblData == null) { for (int b = 0; b < bands; b++) { short[] s = srcData[b]; int[] d = dstData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < height; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < width; w++) { d[dstPixelOffset] = data.getElem(b, s[srcPixelOffset]&0xFFFF); srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } else { for (int b = 0; b < bands; b++) { short[] s = srcData[b]; int[] d = dstData[b]; int[] t = tblData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; int tblOffset = tblOffsets[b]; for (int h = 0; h < height; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < width; w++) { d[dstPixelOffset] = t[(s[srcPixelOffset]&0xFFFF) - tblOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } } // short to int private void lookup(int srcLineStride, int srcPixelStride, int[] srcBandOffsets, short[][] srcData, int width, int height, int bands, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, int[][] dstData, int[] tblOffsets, int[][] tblData) { if (tblData == null) { for (int b = 0; b < bands; b++) { short[] s = srcData[b]; int[] d = dstData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < height; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < width; w++) { d[dstPixelOffset] = data.getElem(b, s[srcPixelOffset]); srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } else { for (int b = 0; b < bands; b++) { short[] s = srcData[b]; int[] d = dstData[b]; int[] t = tblData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; int tblOffset = tblOffsets[b]; for (int h = 0; h < height; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < width; w++) { d[dstPixelOffset] = t[s[srcPixelOffset] - tblOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } } // int to int private void lookup(int srcLineStride, int srcPixelStride, int[] srcBandOffsets, int[][] srcData, int width, int height, int bands, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, int[][] dstData, int[] tblOffsets, int[][] tblData) { if (tblData == null) { for (int b = 0; b < bands; b++) { int[] s = srcData[b]; int[] d = dstData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; for (int h = 0; h < height; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < width; w++) { d[dstPixelOffset] = data.getElem(b, s[srcPixelOffset]); srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } else { for (int b = 0; b < bands; b++) { int[] s = srcData[b]; int[] d = dstData[b]; int[] t = tblData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; int tblOffset = tblOffsets[b]; for (int h = 0; h < height; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < width; w++) { d[dstPixelOffset] = t[s[srcPixelOffset] - tblOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } } // byte to float private void lookup(int srcLineStride, int srcPixelStride, int[] srcBandOffsets, byte[][] srcData, int width, int height, int bands, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, float[][] dstData, int[] tblOffsets, float[][] tblData) { for (int b = 0; b < bands; b++) { byte[] s = srcData[b]; float[] d = dstData[b]; float[] t = tblData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; int tblOffset = tblOffsets[b]; for (int h = 0; h < height; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < width; w++) { d[dstPixelOffset] = t[(s[srcPixelOffset]&0xFF) - tblOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } // ushort to float private void lookupU(int srcLineStride, int srcPixelStride, int[] srcBandOffsets, short[][] srcData, int width, int height, int bands, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, float[][] dstData, int[] tblOffsets, float[][] tblData) { for (int b = 0; b < bands; b++) { short[] s = srcData[b]; float[] d = dstData[b]; float[] t = tblData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; int tblOffset = tblOffsets[b]; for (int h = 0; h < height; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < width; w++) { d[dstPixelOffset] = t[(s[srcPixelOffset]&0xFFFF) - tblOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } // short to float private void lookup(int srcLineStride, int srcPixelStride, int[] srcBandOffsets, short[][] srcData, int width, int height, int bands, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, float[][] dstData, int[] tblOffsets, float[][] tblData) { for (int b = 0; b < bands; b++) { short[] s = srcData[b]; float[] d = dstData[b]; float[] t = tblData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; int tblOffset = tblOffsets[b]; for (int h = 0; h < height; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < width; w++) { d[dstPixelOffset] = t[s[srcPixelOffset] - tblOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } // int to float private void lookup(int srcLineStride, int srcPixelStride, int[] srcBandOffsets, int[][] srcData, int width, int height, int bands, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, float[][] dstData, int[] tblOffsets, float[][] tblData) { for (int b = 0; b < bands; b++) { int[] s = srcData[b]; float[] d = dstData[b]; float[] t = tblData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; int tblOffset = tblOffsets[b]; for (int h = 0; h < height; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < width; w++) { d[dstPixelOffset] = t[s[srcPixelOffset] - tblOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } // byte to double private void lookup(int srcLineStride, int srcPixelStride, int[] srcBandOffsets, byte[][] srcData, int width, int height, int bands, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, double[][] dstData, int[] tblOffsets, double[][] tblData) { for (int b = 0; b < bands; b++) { byte[] s = srcData[b]; double[] d = dstData[b]; double[] t = tblData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; int tblOffset = tblOffsets[b]; for (int h = 0; h < height; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < width; w++) { d[dstPixelOffset] = t[(s[srcPixelOffset]&0xFF) - tblOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } // ushort to double private void lookupU(int srcLineStride, int srcPixelStride, int[] srcBandOffsets, short[][] srcData, int width, int height, int bands, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, double[][] dstData, int[] tblOffsets, double[][] tblData) { for (int b = 0; b < bands; b++) { short[] s = srcData[b]; double[] d = dstData[b]; double[] t = tblData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; int tblOffset = tblOffsets[b]; for (int h = 0; h < height; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < width; w++) { d[dstPixelOffset] = t[(s[srcPixelOffset]&0xFFFF) - tblOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } // short to double private void lookup(int srcLineStride, int srcPixelStride, int[] srcBandOffsets, short[][] srcData, int width, int height, int bands, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, double[][] dstData, int[] tblOffsets, double[][] tblData) { for (int b = 0; b < bands; b++) { short[] s = srcData[b]; double[] d = dstData[b]; double[] t = tblData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; int tblOffset = tblOffsets[b]; for (int h = 0; h < height; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < width; w++) { d[dstPixelOffset] = t[s[srcPixelOffset] - tblOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } // int to double private void lookup(int srcLineStride, int srcPixelStride, int[] srcBandOffsets, int[][] srcData, int width, int height, int bands, int dstLineStride, int dstPixelStride, int[] dstBandOffsets, double[][] dstData, int[] tblOffsets, double[][] tblData) { for (int b = 0; b < bands; b++) { int[] s = srcData[b]; double[] d = dstData[b]; double[] t = tblData[b]; int srcLineOffset = srcBandOffsets[b]; int dstLineOffset = dstBandOffsets[b]; int tblOffset = tblOffsets[b]; for (int h = 0; h < height; h++) { int srcPixelOffset = srcLineOffset; int dstPixelOffset = dstLineOffset; srcLineOffset += srcLineStride; dstLineOffset += dstLineStride; for (int w = 0; w < width; w++) { d[dstPixelOffset] = t[s[srcPixelOffset] - tblOffset]; srcPixelOffset += srcPixelStride; dstPixelOffset += dstPixelStride; } } } } /** * Determine which entry in the LookupTableJAI is closest * in Euclidean distance to the argument pixel. * * @param pixel The pixel the closest entry to which is to be found. * * @return the index of the closest entry. If the data array of the * lookup table is in the format data[numBands][numEntries], then the * value v for band b of the closest entry is *

         *     v = data[b][index - lookup.getOffset()]
         * 
    * where index is the returned value of this method. * * @throws IllegalArgumentException if pixel is null. */ public int findNearestEntry(float[] pixel) { if ( pixel == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } int dataType = data.getDataType(); int numBands = getNumBands(); int numEntries = getNumEntries(); int index = -1; if(dataType == DataBuffer.TYPE_BYTE) { byte buffer[][] = getByteData(); // Find the distance to the first entry and set result to 0. float minDistance = 0.0F; index = 0; for(int b = 0; b < numBands; b++) { float delta = pixel[b] - (float)(buffer[b][0] & 0xff); minDistance += delta*delta; } // Find the distance to each entry and set the result to // the index which is closest to the argument. for(int i = 1; i < numEntries; i++) { float distance = 0.0F; for(int b = 0; b < numBands; b++) { float delta = pixel[b] - (float)(buffer[b][i] & 0xff); distance += delta*delta; } if(distance < minDistance) { minDistance = distance; index = i; } } } else if(dataType == DataBuffer.TYPE_SHORT) { short buffer[][] = getShortData(); // Find the distance to the first entry and set result to 0. float minDistance = 0.0F; index = 0; for(int b = 0; b < numBands; b++) { float delta = pixel[b] - buffer[b][0]; minDistance += delta*delta; } // Find the distance to each entry and set the result to // the index which is closest to the argument. for(int i = 1; i < numEntries; i++) { float distance = 0.0F; for(int b = 0; b < numBands; b++) { float delta = pixel[b] - buffer[b][i]; distance += delta*delta; } if(distance < minDistance) { minDistance = distance; index = i; } } } else if(dataType == DataBuffer.TYPE_USHORT) { short buffer[][] = getShortData(); // Find the distance to the first entry and set result to 0. float minDistance = 0.0F; index = 0; for(int b = 0; b < numBands; b++) { float delta = pixel[b] - (float)(buffer[b][0] & 0xffff); minDistance += delta*delta; } // Find the distance to each entry and set the result to // the index which is closest to the argument. for(int i = 1; i < numEntries; i++) { float distance = 0.0F; for(int b = 0; b < numBands; b++) { float delta = pixel[b] - (float)(buffer[b][i] & 0xffff); distance += delta*delta; } if(distance < minDistance) { minDistance = distance; index = i; } } } else if(dataType == DataBuffer.TYPE_INT) { int buffer[][] = getIntData(); // Find the distance to the first entry and set result to 0. float minDistance = 0.0F; index = 0; for(int b = 0; b < numBands; b++) { float delta = pixel[b] - buffer[b][0]; minDistance += delta*delta; } // Find the distance to each entry and set the result to // the index which is closest to the argument. for(int i = 1; i < numEntries; i++) { float distance = 0.0F; for(int b = 0; b < numBands; b++) { float delta = pixel[b] - buffer[b][i]; distance += delta*delta; } if(distance < minDistance) { minDistance = distance; index = i; } } } else if(dataType == DataBuffer.TYPE_FLOAT) { float buffer[][] = getFloatData(); // Find the distance to the first entry and set result to 0. float minDistance = 0.0F; index = 0; for(int b = 0; b < numBands; b++) { float delta = pixel[b] - buffer[b][0]; minDistance += delta*delta; } // Find the distance to each entry and set the result to // the index which is closest to the argument. for(int i = 1; i < numEntries; i++) { float distance = 0.0F; for(int b = 0; b < numBands; b++) { float delta = pixel[b] - buffer[b][i]; distance += delta*delta; } if(distance < minDistance) { minDistance = distance; index = i; } } } else if(dataType == DataBuffer.TYPE_DOUBLE) { double buffer[][] = getDoubleData(); // Find the distance to the first entry and set result to 0. double minDistance = 0.0F; index = 0; for(int b = 0; b < numBands; b++) { double delta = pixel[b] - buffer[b][0]; minDistance += delta*delta; } // Find the distance to each entry and set the result to // the index which is closest to the argument. for(int i = 1; i < numEntries; i++) { double distance = 0.0F; for(int b = 0; b < numBands; b++) { double delta = pixel[b] - buffer[b][i]; distance += delta*delta; } if(distance < minDistance) { minDistance = distance; index = i; } } } else { // This can't happen since we control the type of data throw new RuntimeException(JaiI18N.getString("LookupTableJAI0")); } // Return the index of the closest color plus the offset of the // default band or -1 on error. return index == -1 ? index : index + getOffset(); } /** * Serialize the LookupTableJAI. * * @param out The ObjectOutputStream. */ private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeObject(SerializerFactory.getState(data)); } /** * Deserialize the LookupTableJAI. * * @param in The ObjectInputStream. */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); Object object = in.readObject(); SerializableState ss = (SerializableState)object; data = (DataBuffer)ss.getObject(); } private void initOffsets(int nbands, int offset) { tableOffsets = new int[nbands]; for (int i=0; inull, i.e., * no property names are recognized. * * @return an array of Strings representing valid property names. * * @since JAI 1.1 */ public String[] getPropertyNames() { return properties.getPropertyNames(); } /** * Returns an array of Strings recognized as names by * this property source that begin with the supplied prefix. If * no property names are recognized, or no property names match, * null will be returned. * The comparison is done in a case-independent manner. * * @return An array of Strings giving the valid * property names. * * @param prefix the supplied prefix for the property source. * * @throws IllegalArgumentException if prefix is * null. */ public String[] getPropertyNames(String prefix) { if ( prefix == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return properties.getPropertyNames(prefix); } /** * Returns the class expected to be returned by a request for * the property with the specified name. If this information * is unavailable, null will be returned. * * @return The Class expected to be return by a * request for the value of this property or null. * * @exception IllegalArgumentException if name * is null. * * @since JAI 1.1 */ public Class getPropertyClass(String name) { return properties.getPropertyClass(name); } /** * Gets a property from the property set of this image. * If the property name is not recognized, java.awt.Image.UndefinedProperty * will be returned. The default implementation returns * java.awt.Image.UndefinedProperty. * * @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. * * @exception IllegalArgumentException if name * is null. */ public Object getProperty(String name) { return properties.getProperty(name); } /** * Sets a property on a MultiResolutionRenderableImage. * * @param name a String containing the property's name. * @param value the property, as a general Object. * * @throws IllegalArgumentException If name or * value is null. * * @since JAI 1.1 */ public void setProperty(String name, Object value) { properties.setProperty(name, value); } /** * Removes the named property from the * MultiResolutionRenderableImage. * * @return The value of the property removed or * java.awt.Image.UndefinedProperty if it was * not present in the property set. * * @exception IllegalArgumentException if name * is null. * * @since JAI 1.1 */ public void removeProperty(String name) { properties.removeProperty(name); } /** * Add a PropertyChangeListener to the listener list. The * listener is registered for all properties. * * @since JAI 1.1 */ public void addPropertyChangeListener(PropertyChangeListener listener) { eventManager.addPropertyChangeListener(listener); } /** * Add a PropertyChangeListener for a specific property. The * listener will be invoked only when a call on * firePropertyChange names that specific property. The case of * the name is ignored. * * @since JAI 1.1 */ public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { eventManager.addPropertyChangeListener(propertyName, listener); } /** * Remove a PropertyChangeListener from the listener list. This * removes a PropertyChangeListener that was registered for all * properties. * * @since JAI 1.1 */ public void removePropertyChangeListener(PropertyChangeListener listener) { eventManager.removePropertyChangeListener(listener); } /** * Remove a PropertyChangeListener for a specific property. The case * of the name is ignored. * * @since JAI 1.1 */ public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { eventManager.removePropertyChangeListener(propertyName, listener); } /** * Returns the floating-point width of the RenderableImage. */ public float getWidth() { return width; } /** * Returns the floating-point height of the RenderableImage. */ public float getHeight() { return height; } /** * Returns the floating-point min X coordinate of the * RenderableImage. */ public float getMinX() { return minX; } /** * Returns the floating-point max X coordinate of the * RenderableImage. */ public float getMaxX() { return minX + width; } /** * Returns the floating-point min Y coordinate of the * RenderableImage. */ public float getMinY() { return minY; } /** * Returns the floating-point max Y coordinate of the * RenderableImage. */ public float getMaxY() { return minY + height; } /** * Returns false since successive renderings (that is, calls to * createRendering() or createScaledRendering()) with the same * arguments will never produce different results. */ public boolean isDynamic() { return false; } /** * Returns a rendering with a given width, height, and rendering * hints. * *

    If a JAI rendering hint named * JAI.KEY_INTERPOLATION is provided, its * corresponding Interpolation object is used as an * argument to the JAI operator used to scale the image. If no * such hint is present, an instance of * InterpolationNearest is used. * * @param width the width of the rendering in pixels. * @param height the height of the rendering in pixels. * @param hints a Hashtable of rendering hints. * @throws IllegalArgumentException if width or height are non-positive. */ public RenderedImage createScaledRendering(int width, int height, RenderingHints hints) { if(width <= 0 && height <= 0) { throw new IllegalArgumentException( JaiI18N.getString("MultiResolutionRenderableImage1")); } int res = numSources - 1; while (res > 0) { if(height > 0) { int imh = renderedSource[res].getHeight(); if (imh >= height) { break; } } else { int imw = renderedSource[res].getWidth(); if (imw >= width) { break; } } res--; } RenderedImage source = renderedSource[res]; if(width <= 0) { width = (int)Math.round(height*source.getWidth()/source.getHeight()); } else if(height <= 0) { height = (int)Math.round(width*source.getHeight()/source.getWidth()); } double sx = (double)width/source.getWidth(); double sy = (double)height/source.getHeight(); double tx = (getMinX() - source.getMinX())*sx; double ty = (getMinY() - source.getMinY())*sy; Interpolation interp = Interpolation.getInstance(Interpolation.INTERP_NEAREST); if (hints != null) { Object obj = hints.get(JAI.KEY_INTERPOLATION); if (obj != null) { interp = (Interpolation)obj; } } ParameterBlock pb = new ParameterBlock(); pb.addSource(source); pb.add((float)sx); pb.add((float)sy); pb.add((float)tx); pb.add((float)ty); pb.add(interp); return JAI.create("scale", pb, null); } /** * Returns the full resolution source RenderedImage * with no rendering hints. */ public RenderedImage createDefaultRendering() { return renderedSource[0]; } /** * Returns a rendering based on a RenderContext. * *

    If a JAI rendering hint named * JAI.KEY_INTERPOLATION is provided, its * corresponding Interpolation object is used as an * argument to the JAI operator used to transform the image. If * no such hint is present, an instance of * InterpolationNearest is used. * *

    The RenderContext may contain a Shape * that represents the area-of-interest (aoi). If the aoi is specifed, * it is still legal to return an image that's larger than this aoi. * Therefore, by default, the aoi, if specified, is ignored at the * rendering. * * @param renderContext a RenderContext describing the transform * rendering hints. * @throws IllegalArgumentException if renderContext is null. */ public RenderedImage createRendering(RenderContext renderContext) { if ( renderContext == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } // Get a clone of the context's transform AffineTransform usr2dev = renderContext.getTransform(); RenderingHints hints = renderContext.getRenderingHints(); int type = usr2dev.getType(); if (type == AffineTransform.TYPE_UNIFORM_SCALE || type == AffineTransform.TYPE_GENERAL_SCALE) { int width = (int)Math.ceil(usr2dev.getScaleX()*getWidth()); int height = (int)Math.ceil(usr2dev.getScaleY()*getHeight()); return createScaledRendering(width, height, hints); } // Use the square root of the determinant as an estimate of // the single-axis scale factor. int height = (int)Math.ceil(Math.sqrt(usr2dev.getDeterminant())*getHeight()); int res = numSources - 1; while (res > 0) { int imh = renderedSource[res].getHeight(); if (imh >= height) { break; } res--; } RenderedImage source = renderedSource[res]; double sx = (double)getWidth()/source.getWidth(); double sy = (double)getHeight()/source.getHeight(); AffineTransform transform = new AffineTransform(); transform.translate(-source.getMinX(), -source.getMinY()); transform.scale(sx, sy); transform.translate(getMinX(), getMinY()); transform.preConcatenate(usr2dev); Interpolation interp = Interpolation.getInstance(Interpolation.INTERP_NEAREST); if (hints != null) { Object obj = hints.get(JAI.KEY_INTERPOLATION); if (obj != null) { interp = (Interpolation)obj; } } ParameterBlock pb = new ParameterBlock(); pb.addSource(source); pb.add(transform); pb.add(interp); return JAI.create("affine", pb, null); } /** * Serialize the MultiResolutionRenderableImage. * * @param out The stream provided by the VM to which to write the object. */ private void writeObject(ObjectOutputStream out) throws IOException { // Create an array for the serializable form of the sources. Object[] sources = new Object[numSources]; // Copy each source converting it to a serializable form if necessary. for(int i = 0; i < numSources; i++) { if(renderedSource[i] instanceof Serializable) { // Image is already serializable. sources[i] = renderedSource[i]; } else { // Derive a serializable form. sources[i] = SerializerFactory.getState(renderedSource[i]); } } // Write non-transient fields. out.defaultWriteObject(); // Write array of serializable sources. out.writeObject(sources); } /** * Deserialize the MultiResolutionRenderableImage. * * @param in The stream provided by the VM from which to read the object. */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { // Read non-transient fields. in.defaultReadObject(); // Read array of sources. Object[] source = (Object[])in.readObject(); numSources = source.length; renderedSource = new RenderedImage[numSources]; for (int i = 0; i < numSources; i++) { if (source[i] instanceof SerializableState) { SerializableState ss = (SerializableState)source[i]; renderedSource[i] = (RenderedImage)ss.getObject(); } else renderedSource[i] = (RenderedImage)source[i]; } } } jai-core-1.1.4/src/share/classes/javax/media/jai/RegistryMode.java0000644000175000017500000002173710203035544024627 0ustar mathieumathieu/* * $RCSfile: RegistryMode.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:19 $ * $State: Exp $ */ package javax.media.jai; import java.lang.reflect.Method; import java.util.Enumeration; import java.util.HashSet; import java.util.Hashtable; import java.util.Set; import javax.media.jai.registry.CollectionRegistryMode; import javax.media.jai.registry.RemoteRenderableRegistryMode; import javax.media.jai.registry.RemoteRenderedRegistryMode; import javax.media.jai.registry.RenderableCollectionRegistryMode; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; import javax.media.jai.registry.TileDecoderRegistryMode; import javax.media.jai.registry.TileEncoderRegistryMode; import javax.media.jai.util.CaselessStringKey; /** * A class which provides information about a registry mode. The * static methods of the class act to maintain a global list of * known modes. All RegistryModes known * to JAI are added to this list when this class is loaded. The * RegistryModes installed by JAI cannot be * replaced or removed. * * The Strings used to represent the registry modes * are all used in a case-insensitive manner. * * @since JAI 1.1 */ public class RegistryMode { /** * Cache of known RegistryMode-s hashed by * CaselessStringKeys which wraps a String and performs * case-insensitive equals() */ private static Hashtable registryModes; /** * Set of CaselessStringKeys of registryModes which * cannot be replaced or removed. */ private static HashSet immutableNames; // Instance variables. private CaselessStringKey name; private Class descriptorClass; private Class productClass; private Method factoryMethod; private boolean arePreferencesSupported; private boolean arePropertiesSupported; // Load all JAI-defined registryModes. static { registryModes = new Hashtable(4); immutableNames = new HashSet(); // operation modes addMode(new RenderedRegistryMode(), true); addMode(new RenderableRegistryMode(), true); addMode(new CollectionRegistryMode(), true); addMode(new RenderableCollectionRegistryMode(), true); // remote modes addMode(new RemoteRenderedRegistryMode(), true); addMode(new RemoteRenderableRegistryMode(), true); // Tilecodec modes addMode(new TileEncoderRegistryMode(), true); addMode(new TileDecoderRegistryMode(), true); } /** * Adds a new RegistryMode to the existing list. If immutable is * "true" then the CaselessStringKey is added to immutableNames * also. This mode must not already exist in the list. */ private static boolean addMode(RegistryMode mode, boolean immutable) { if (registryModes.containsKey(mode.name)) return false; registryModes.put(mode.name, mode); if (immutable) immutableNames.add(mode.name); return true; } /** * Adds a new RegistryMode to the existing list. This * succeeds only if the mode is not already present in the * list. New RegistryMode names can not clash (in a * case insensitive manner) with the ones installed by JAI (done * statically when this class is loaded) * * @param mode the new RegistryMode to be added to list * * @return false if the mode was already in the list. true otherwise */ public synchronized static boolean addMode(RegistryMode mode) { return addMode(mode, false); } /** * Removes a mode from the existing list of known registryModes. * If the mode is one of the JAI-installed ones, it can not * be removed. * * @param mode the RegistryMode to be removed from the list * * @return false if the mode can not be removed because it was added * by JAI or because the mode was not previously add. * returns true otherwise. */ public synchronized static boolean removeMode(String name) { CaselessStringKey key = new CaselessStringKey(name); if (immutableNames.contains(key)) return false; return registryModes.remove(key) != null; } /** * Get the list of the known registry mode names. * * @return null, if there are no registered modes. * Otherwise returns an array of Strings of registered * mode names. */ public static synchronized String[] getModeNames() { String names[] = new String[registryModes.size()]; int i = 0; for (Enumeration e = registryModes.keys(); e.hasMoreElements();) { CaselessStringKey key = (CaselessStringKey)e.nextElement(); names[i++] = key.getName(); } if (i <= 0) return null; return names; } /** * Get a list of all known registry modes associated with the * specified descriptorClass. * * @param descriptorClass a Class * * @return null if there are no modes registered * against the specified descriptorClass. Otherwise returns an * array of Strings of mode names associated with the * descriptorClass. */ public static synchronized String[] getModeNames(Class descriptorClass) { String names[] = new String[registryModes.size()]; int i = 0; for (Enumeration e = registryModes.elements(); e.hasMoreElements();) { RegistryMode mode = (RegistryMode)e.nextElement(); if (mode.getDescriptorClass() == descriptorClass) names[i++] = mode.getName(); } if (i <= 0) return null; String matchedNames[] = new String[i]; for (int j = 0; j < i; j++) matchedNames[j] = names[j]; return matchedNames; } /** * Get the registry mode corresponding to this name. */ public static RegistryMode getMode(String name) { CaselessStringKey key = new CaselessStringKey(name); return (RegistryMode)registryModes.get(key); } /** * Get a Set of all descriptor classes over * all registry modes. */ public static synchronized Set getDescriptorClasses() { HashSet set = new HashSet(); for (Enumeration e = registryModes.elements(); e.hasMoreElements();) { RegistryMode mode = (RegistryMode)e.nextElement(); set.add(mode.descriptorClass); } return set; } /** * Constructor. Protected access allows only instantiation of * subclasses. * * @param name name of the registry mode * @param descriptorClass the specific sub-class of * RegistryElementDescriptor associated with * this registry mode. * @param productClass the Class of the objects * produced by this registry mode. This would typically * be factoryMethod.getReturnType(). * @param factoryMethod the method used to "create" an object. * @param arePreferencesSupported does this registry mode support * preferences between products or instances of the "modes" * @param arePropertiesSupported do properties have to be managed * for this registry mode. */ protected RegistryMode(String name, Class descriptorClass, Class productClass, Method factoryMethod, boolean arePreferencesSupported, boolean arePropertiesSupported) { this.name = new CaselessStringKey(name); this.descriptorClass = descriptorClass; this.productClass = productClass; this.factoryMethod = factoryMethod; this.arePreferencesSupported = arePreferencesSupported; this.arePropertiesSupported = arePropertiesSupported; } /** Get the registry mode name (case-preserved) */ public final String getName() { return name.getName(); } /** Get the factory method that corresponds to "create" */ public final Method getFactoryMethod() { return factoryMethod; } /** Does this registry mode support preferences ? */ public final boolean arePreferencesSupported() { return arePreferencesSupported; } /** * Are properties to be managed for this registry mode ? */ public final boolean arePropertiesSupported() { return arePropertiesSupported; } /** * Returns the descriptor class that corresponds to this registry mode. * * For eg. this would be OperationDescriptor for rendered, renderable, * collection ... and TileCodecDescriptor for tilecodec etc. */ public final Class getDescriptorClass() { return descriptorClass; } /** * The Class of the objects produced by this * registry mode. */ public final Class getProductClass() { return productClass; } /** * A convenience method which essentially returns * getFactoryMethod().getDeclaringClass() */ public final Class getFactoryClass() { return factoryMethod.getDeclaringClass(); } } jai-core-1.1.4/src/share/classes/javax/media/jai/remote/0000755000175000017500000000000011633360403022633 5ustar mathieumathieujai-core-1.1.4/src/share/classes/javax/media/jai/remote/RemoteRenderedImage.java0000644000175000017500000001045510203035544027347 0ustar mathieumathieu/* * $RCSfile: RemoteRenderedImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:53 $ * $State: Exp $ */package javax.media.jai.remote; import java.awt.image.RenderedImage; import javax.media.jai.remote.NegotiableCapabilitySet; /** * RemoteRenderedImage is an interface commonly used to * represent objects which contain or can produce image data in the form of * Rasters from locations that are remote. The image data may be * stored/produced as a single tile or a regular array of tiles. * *

    This class is the remote equivalent of the RenderedImage * interface and adds methods that deal with the remote location aspect of * the image. * * @see RenderedImage * * @since JAI 1.1 */ public interface RemoteRenderedImage extends RenderedImage { /** * Returns the String that identifies the server. */ String getServerName(); /** * Returns the String that identifies the remote imaging * protocol. */ String getProtocolName(); /** * Returns the amount of time between retries in milliseconds. */ int getRetryInterval(); /** * Sets the amount of time between retries in milliseconds. * * @param retryInterval The amount of time (in milliseconds) to wait * between retries. * @throws IllegalArgumentException if retryInterval is negative. */ void setRetryInterval(int retryInterval); /** * Returns the number of retries. */ int getNumRetries(); /** * Sets the number of retries. * * @param numRetries The number of times an operation should be retried * in case of a network error. * @throws IllegalArgumentException if numRetries is negative. */ void setNumRetries(int numRetries); /** * Returns the current negotiation preferences or null, if none were * set previously. */ NegotiableCapabilitySet getNegotiationPreferences(); /** * Sets the preferences to be used in the client-server * communication. These preferences are utilized in the negotiation * process. Note that preferences for more than one category can be * specified using this method. Also each preference can be a list * of values in decreasing order of preference, each value specified * as a NegotiableCapability. The * NegotiableCapability first (for a particular category) * in this list is given highest priority in the negotiation process * (for that category). * *

    It may be noted that this method allows for multiple negotiation * cycles. Everytime this method is called, new preferences are * specified for the negotiation, which can be utilized to perform * a new round of negotiation to produce new negotiated values to be * used in the remote communication. * * @param preferences The preferences to be used in the negotiation * process. * @throws IllegalArgumentException if preferences is null. */ void setNegotiationPreferences(NegotiableCapabilitySet preferences); /** * Returns the results of the negotiation process. This will return null * if no negotiation preferences were set, and no negotiation was * performed, or if the negotiation failed. */ NegotiableCapabilitySet getNegotiatedValues() throws RemoteImagingException; /** * Returns the results of the negotiation process for the given category. * This will return null if no negotiation preferences were set, and no * negotiation was performed, or if the negotiation failed. * * @param String category The category to get the negotiated results for. * @throws IllegalArgumentException if category is null. */ NegotiableCapability getNegotiatedValue(String category) throws RemoteImagingException; /** * Informs the server of the negotiated values that are the result of * a successful negotiation. * * @param negotiatedValues The result of the negotiation. */ void setServerNegotiatedValues(NegotiableCapabilitySet negotiatedValues) throws RemoteImagingException; } jai-core-1.1.4/src/share/classes/javax/media/jai/remote/Negotiable.java0000644000175000017500000000620610203035544025550 0ustar mathieumathieu/* * $RCSfile: Negotiable.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:50 $ * $State: Exp $ */package javax.media.jai.remote; import java.io.Serializable; /** * An interface that defines objects that can be negotiated upon. * Negotiation amongst objects is performed using the * negotiate() method. This method can be used to * perform a chaining of successful negotiations, i.e., the results * of one successful negotiation can be used to negotiate with another * Negotiable and so on. In order to retrieve a single * negotiated value out of the Negotiable, the * getNegotiatedValue() method can be used at any point * during this series of negotiations. * * @since JAI 1.1 */ public interface Negotiable extends Serializable { /** * Returns a Negotiable object that represents the * set intersection of this Negotiable with the * given Negotiable. The returned Negotiable * represents the support that is common to both the * Negotiables. If the negotiation fails, i.e there is * no common subset, null is returned. * *

    If the supplied argument is null, negotiation will fail and * null will be returned, as it is not possible to negotiate with a * null value. It may, however, be noted that it is valid for * getNegotiatedValue() to return null, i.e the single * value result of the negotiation can be null. * * @param other The Negotiable object to negotiate with. * @returns The Negotiable that represents the subset * common to this and the given Negotiable. * null is returned if there is no common subset. */ Negotiable negotiate(Negotiable other); /** * Returns a value that is valid for this Negotiable. * If more than one value is valid for this Negotiable, * this method can choose one arbitrarily, e.g. picking the first one * or by having static preferences amongst the valid values. Which of the * many valid values is returned is upto the particular implementation * of this method. * * @returns The negotiated value. */ Object getNegotiatedValue(); /** * Returns the Class of the object that would be returned * from the getNegotiatedValue method on a successful * negotiation. This method can be used to learn what the * Class of the negotiated value will be if the negotiation * is successful. Implementing classes are encouraged to return an * accurate Class from this method if at all possible. * However it is permissible to return null, if the Class * of the negotiated value is indeterminate for any reason. * * @returns the Class of the negotiated value. */ Class getNegotiatedValueClass(); } jai-core-1.1.4/src/share/classes/javax/media/jai/remote/RemoteRenderableOp.java0000644000175000017500000007371410240717543027234 0ustar mathieumathieu/* * $RCSfile: RemoteRenderableOp.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-05-12 18:24:35 $ * $State: Exp $ */package javax.media.jai.remote; import java.awt.RenderingHints; import java.awt.geom.Rectangle2D; import java.awt.geom.AffineTransform; import java.awt.image.ImageConsumer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.renderable.ContextualRenderedImageFactory; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderContext; import java.awt.image.renderable.RenderableImage; import java.awt.image.renderable.RenderableImageOp; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.text.MessageFormat; import java.util.Enumeration; import java.util.Hashtable; import java.util.Locale; import java.util.Map; import java.util.Vector; import javax.media.jai.JAI; import javax.media.jai.OperationRegistry; import javax.media.jai.PlanarImage; import javax.media.jai.PropertyChangeEventJAI; import javax.media.jai.PropertyGenerator; import javax.media.jai.PropertySource; import javax.media.jai.RenderedOp; import javax.media.jai.RenderableOp; import javax.media.jai.RegistryMode; import javax.media.jai.WritablePropertySource; import javax.media.jai.remote.SerializableRenderedImage; import javax.media.jai.registry.RemoteCRIFRegistry; import javax.media.jai.util.ImagingException; import javax.media.jai.util.ImagingListener; import com.sun.media.jai.util.PropertyUtil; import com.sun.media.jai.rmi.RasterProxy; import com.sun.media.jai.util.ImageUtil; /** * A subclass of RenderableOp for remote operations. This * class represents a node in a remote renderable imaging * chain. A RemoteRenderableOp stores a protocol name (as a * String), a server name (as a String), an * operation name (as a String), and a * ParameterBlock containing sources and miscellaneous * parameters. * *

    By virtue of being a subclass of RemoteRenderableOp, * this class participates in Java Bean-style events as specified by * RenderableOp. RemoteRenderableOps add the * server name and the protocol name to the critical attributes, the * editing (chaging) of which may cause a PropertyChangeEventJAI * to be emitted. * * @see javax.media.jai.RenderableOp * * @since JAI 1.1 */ public class RemoteRenderableOp extends RenderableOp { /** The name of the protocol this class provides an implementation for. */ protected String protocolName; /** The name of the server. */ protected String serverName; // The RemoteCRIF used to create a rendering. private transient RemoteCRIF remoteCRIF = null; // The NegotiableCapabilitySet representing the negotiated values. private NegotiableCapabilitySet negotiated = null; // A reference to the RMIServerProxy which connects to the corresponding // RenderableOp on the server. Holding this reference ensures that the // RMIServerProxy doesn't get garbage-collected. private transient RenderedImage linkToRemoteOp; /** * Constructs a RemoteRenderableOp using the default * operation registry, given the name of the remote imaging protocol, * the name of the server to perform the operation on, the name of the * operation to be performed remotely and a ParameterBlock * containing RenderableImage sources and other parameters. * Any RenderedImage sources referenced by the * ParameterBlock will be ignored. * *

    An IllegalArgumentException may * be thrown by the protocol specific classes at a later point, if * null is provided as the serverName argument and null is not * considered a valid server name by the specified protocol. * * @param protocolName The protocol name as a String. * @param serverName The server name as a String. * @param opName The operation name. * @param pb The sources and other parameters. If * null, it is assumed that this node has * no sources and parameters. * * @throws IllegalArgumentException if protocolName is * null. * @throws IllegalArgumentException if opName is * null. */ public RemoteRenderableOp(String protocolName, String serverName, String opName, ParameterBlock pb) { this(null, protocolName, serverName, opName, pb); } /** * Constructs a RemoteRenderableOp using the specified * operation registry, given the name of the remote imaging protocol, * the name of the server to perform the operation on, the name of the * operation to be performed remotely and a ParameterBlock * containing RenderableImage sources and other parameters. * Any RenderedImage sources referenced by the * ParameterBlock will be ignored. * *

    An IllegalArgumentException may * be thrown by the protocol specific classes at a later point, if * null is provided as the serverName argument and null is not * considered a valid server name by the specified protocol. * * @param registry The OperationRegistry to be used for * instantiation. if null, the default * registry is used. * @param protocolName The protocol name as a String. * @param serverName The server name as a String. * @param opName The operation name. * @param pb The sources and other parameters. If * null, it is assumed that this node has * no sources and parameters. * * @throws IllegalArgumentException if protocolName is * null. * @throws IllegalArgumentException if opName is * null. */ public RemoteRenderableOp(OperationRegistry registry, String protocolName, String serverName, String opName, ParameterBlock pb) { super(registry, opName, pb); if (protocolName == null || opName == null) { throw new IllegalArgumentException(); } this.protocolName = protocolName; this.serverName = serverName; } /** * Returns the name of the RegistryMode corresponding to * this RenderableOp. This method overrides the * implementation in RenderableOp to always returns the * String "remoteRenderable". */ public String getRegistryModeName() { return RegistryMode.getMode("remoteRenderable").getName(); } /** * Returns the String that identifies the server. */ public String getServerName() { return serverName; } /** * Sets a String identifying the server. * *

    If the supplied name does not equal the current server name, a * PropertyChangeEventJAI named "ServerName" * will be fired. The oldValue field in the * PropertyChangeEventJAI will contain the old server * name String and the newValue field will contain the * new server name String. * * @param serverName A String identifying the server. * @throws IllegalArgumentException if serverName is null. */ public void setServerName(String serverName) { if (serverName == null) throw new IllegalArgumentException(JaiI18N.getString("Generic2")); if (serverName.equalsIgnoreCase(this.serverName)) return; String oldServerName = this.serverName; this.serverName = serverName; fireEvent("ServerName", oldServerName, serverName); nodeSupport.resetPropertyEnvironment(false); } /** * Returns the String that identifies the remote imaging * protocol. */ public String getProtocolName() { return protocolName; } /** * Sets a String identifying the remote imaging protocol. * *

    If the supplied name does not equal the current protocol name, a * PropertyChangeEventJAI named "ProtocolName" * will be fired. The oldValue field in the * PropertyChangeEventJAI will contain the old protocol * name String and the newValue field will contain the * new protocol name String. * * @param protocolName A String identifying the protocol. * @throws IllegalArgumentException if protocolName is null. */ public void setProtocolName(String protocolName) { if (protocolName == null) throw new IllegalArgumentException( JaiI18N.getString("Generic1")); if (protocolName.equalsIgnoreCase(this.protocolName)) return; String oldProtocolName = this.protocolName; this.protocolName = protocolName; fireEvent("ProtocolName", oldProtocolName, protocolName); nodeSupport.resetPropertyEnvironment(false); } /** * Sets the protocol name and the server name of this * RemoteRenderableOp to the specified arguments.. * *

    If both the supplied protocol name and the supplied server * name values do not equal the current values, a * PropertyChangeEventJAI named "ProtocolAndServerName" * will be fired. The oldValue field in the * PropertyChangeEventJAI will contain a two element * array of Strings, the old protocol name being the * first element and the old server name being the second. Similarly * the newValue field of the PropertyChangeEventJAI will * contain a two element array of Strings, the new protocol * name being the first element and the new server name being the * second. If only the supplied protocol name does not equal * the current protocol name, a PropertyChangeEventJAI * named "ProtocolName" will be fired. If only the supplied server * name does not equal the current server name, a * PropertyChangeEventJAI named "ServerName" * will be fired. * * @param protocolName A String identifying the protocol. * @param serverName A String identifying the server. * @throws IllegalArgumentException if protocolName is null. * @throws IllegalArgumentException if serverName is null. */ public void setProtocolAndServerNames(String protocolName, String serverName) { if (serverName == null) throw new IllegalArgumentException(JaiI18N.getString("Generic2")); if (protocolName == null) throw new IllegalArgumentException(JaiI18N.getString("Generic1")); boolean protocolNotChanged = protocolName.equalsIgnoreCase(this.protocolName); boolean serverNotChanged = serverName.equalsIgnoreCase(this.serverName); if (protocolNotChanged) { if (serverNotChanged) // Neither changed return; else { // Only serverName changed setServerName(serverName); return; } } else { if (serverNotChanged) { // Only protocolName changed setProtocolName(protocolName); return; } } String oldProtocolName = this.protocolName; String oldServerName = this.serverName; this.protocolName = protocolName; this.serverName = serverName; // Both changed fireEvent("ProtocolAndServerName", new String[] {oldProtocolName, oldServerName}, new String[] {protocolName, serverName}); nodeSupport.resetPropertyEnvironment(false); } // Fire an event to all listeners registered with this node. private void fireEvent(String propName, Object oldVal, Object newVal) { if (eventManager != null) { Object eventSource = eventManager.getPropertyChangeEventSource(); PropertyChangeEventJAI evt = new PropertyChangeEventJAI(eventSource, propName, oldVal, newVal); eventManager.firePropertyChange(evt); } } /** * Overrides the method in RenderableOp to return the * rendering-independent width of the image, as queried from the remote * server. * * @return the image width as a float. */ public float getWidth() { findRemoteCRIF(); Rectangle2D boundingBox = remoteCRIF.getBounds2D(serverName, nodeSupport.getOperationName(), nodeSupport.getParameterBlock()); return (float)boundingBox.getWidth(); } /** * Overrides the method in RenderableOp to return the * rendering-independent height of the image, as queried from the remote * server. * * @return the image height as a float. */ public float getHeight() { findRemoteCRIF(); Rectangle2D boundingBox = remoteCRIF.getBounds2D(serverName, nodeSupport.getOperationName(), nodeSupport.getParameterBlock()); return (float)boundingBox.getHeight(); } /** * Overrides the method in RenderableOp to return the * minimum X coordinate of the rendering-independent image data, as * queried from the remote server. */ public float getMinX() { findRemoteCRIF(); Rectangle2D boundingBox = remoteCRIF.getBounds2D(serverName, nodeSupport.getOperationName(), nodeSupport.getParameterBlock()); return (float)boundingBox.getX(); } /** * Overrides the method in RenderableOp to return the * maximum X coordinate of the rendering-independent image data, as * queried from the remote server. */ public float getMinY() { findRemoteCRIF(); Rectangle2D boundingBox = remoteCRIF.getBounds2D(serverName, nodeSupport.getOperationName(), nodeSupport.getParameterBlock()); return (float)boundingBox.getY(); } /** * Overrides the RenderableOp method to return a * RemoteRenderedImage that represents the remote rendering * of this image using a given RenderContext. This is the * most general way to obtain a rendering of a * RemoteRenderableOp. * *

    This method does not validate sources and parameters supplied * in the ParameterBlock against the specification * of the operation this node represents. It is the caller's * responsibility to ensure that the data in the * ParameterBlock are suitable for this operation. * Otherwise, some kind of exception or error will occur. * *

    RemoteJAI.createRenderable() is the method that does * the validation. Therefore, it is strongly recommended that all * RemoteRenderableOps are created using * RemoteJAI.createRenderable(). * *

    The RenderContext may contain a Shape * that represents the area-of-interest (aoi). If the aoi is specifed, * it is still legal to return an image that's larger than this aoi. * Therefore, by default, the aoi, if specified, is ignored at the * rendering. * *

    The RenderingHints in the RenderContext * may contain negotiation preferences specified under the * KEY_NEGOTIATION_PREFERENCES key. These preferences * can be ignored by the rendering if it so chooses. * * @param renderContext the RenderContext to use to produce the rendering. * @return a RemoteRenderedImage containing the rendered data. */ public RenderedImage createRendering(RenderContext renderContext) { findRemoteCRIF(); // Clone the original ParameterBlock; if the ParameterBlock // contains RenderableImage sources, they will be replaced by // RenderedImages. ParameterBlock renderedPB = (ParameterBlock)nodeSupport.getParameterBlock().clone(); // If there are any hints set on the node, create a new // RenderContext which merges them with those in the RenderContext // passed in with the passed in hints taking precedence. RenderContext rcIn = renderContext; RenderingHints nodeHints = nodeSupport.getRenderingHints(); if(nodeHints != null) { RenderingHints hints = renderContext.getRenderingHints(); RenderingHints mergedHints; if (hints == null) { mergedHints = nodeHints; } else if (nodeHints == null || nodeHints.isEmpty()) { mergedHints = hints; } else { mergedHints = new RenderingHints((Map)nodeHints); mergedHints.add(hints); } if(mergedHints != hints) { rcIn = new RenderContext(renderContext.getTransform(), renderContext.getAreaOfInterest(), mergedHints); } } // Get all sources - whether rendered or renderable. Vector sources = nodeSupport.getParameterBlock().getSources(); try { if (sources != null) { Vector renderedSources = new Vector(); for (int i = 0; i < sources.size(); i++) { RenderedImage rdrdImage = null; Object source = sources.elementAt(i); if (source instanceof RenderableImage) { RenderContext rcOut = remoteCRIF.mapRenderContext( serverName, nodeSupport.getOperationName(), i, renderContext, nodeSupport.getParameterBlock(), this); RenderableImage src = (RenderableImage)source; rdrdImage = src.createRendering(rcOut); } else if (source instanceof RenderedOp) { rdrdImage = ((RenderedOp)source).getRendering(); } else if (source instanceof RenderedImage) { rdrdImage = (RenderedImage)source; } if (rdrdImage == null) { return null; } // Add this rendered image to the ParameterBlock's // list of RenderedImages. renderedSources.addElement(rdrdImage); } if (renderedSources.size() > 0) { renderedPB.setSources(renderedSources); } } RenderedImage rendering = remoteCRIF.create(serverName, nodeSupport.getOperationName(), renderContext, renderedPB); if (rendering instanceof RenderedOp) { rendering = ((RenderedOp)rendering).getRendering(); } // Save a reference to the RMIServerProxy that is the link to // the RenderableOp on the server. We don't want this to be // garbage collected. linkToRemoteOp = rendering; // Copy properties to the rendered node. if(rendering != null && rendering instanceof WritablePropertySource) { String[] propertyNames = getPropertyNames(); if(propertyNames != null){ WritablePropertySource wps = (WritablePropertySource)rendering; for(int j = 0; j < propertyNames.length; j++) { String name = propertyNames[j]; Object value = getProperty(name); if(value != null && value != java.awt.Image.UndefinedProperty) { wps.setProperty(name, value); } } } } return rendering; } catch (ArrayIndexOutOfBoundsException e) { // This should never happen return null; } } /** Use registry to find an appropriate RemoteCRIF */ private RemoteCRIF findRemoteCRIF() { if (remoteCRIF == null) { // find the RemoteCRIF from the registry. remoteCRIF = RemoteCRIFRegistry.get(nodeSupport.getRegistry(), protocolName); if (remoteCRIF == null) { throw new ImagingException( JaiI18N.getString("RemoteRenderableOp0")); } } return remoteCRIF; } /** * Returns the amount of time between retries in milliseconds. If * a value for the retry interval has been set previously by * setRetryInterval(), the same value is returned, else * the default retry interval as defined by * RemoteJAI.DEFAULT_RETRY_INTERVAL is returned. */ public int getRetryInterval() { RenderingHints rh = nodeSupport.getRenderingHints(); if (rh == null) { return RemoteJAI.DEFAULT_RETRY_INTERVAL; } else { Integer i = (Integer)rh.get(JAI.KEY_RETRY_INTERVAL); if (i == null) return RemoteJAI.DEFAULT_RETRY_INTERVAL; else return i.intValue(); } } /** * Sets the amount of time between retries in milliseconds. * *

    This new value for retry interval will be stored and will * be passed as RenderingHints as part * of the RenderContext used to create the rendering. The * RenderingHints in the RenderContext will * contain this information under the * KEY_RETRY_INTERVAL key. If the * RenderingHints in the RenderContext already * contains a retry interval value specified by the user, that will * take preference over the one stored in this class. * * @param retryInterval The amount of time (in milliseconds) to wait * between retries. * @throws IllegalArgumentException if retryInterval is negative. */ public void setRetryInterval(int retryInterval) { if (retryInterval < 0) throw new IllegalArgumentException(JaiI18N.getString("Generic3")); RenderingHints rh = nodeSupport.getRenderingHints(); if (rh == null) { RenderingHints hints = new RenderingHints(null); nodeSupport.setRenderingHints(hints); } nodeSupport.getRenderingHints().put(JAI.KEY_RETRY_INTERVAL, new Integer(retryInterval)); } /** * Returns the number of retries. If a value for the number of retries * has been set previously by setNumRetries(), the same * value is returned, else the default number of retries as defined by * RemoteJAI.DEFAULT_NUM_RETRIES is returned. */ public int getNumRetries() { RenderingHints rh = nodeSupport.getRenderingHints(); if (rh == null) { return RemoteJAI.DEFAULT_NUM_RETRIES; } else { Integer i = (Integer)rh.get(JAI.KEY_NUM_RETRIES); if (i == null) return RemoteJAI.DEFAULT_NUM_RETRIES; else return i.intValue(); } } /** * Sets the number of retries. * *

    This new value for number of retries will be stored and will * be passed as RenderingHints as part * of the RenderContext used to create the rendering. The * RenderingHints in the RenderContext will * contain this information under the * KEY_NUM_RETRIES key. If the * RenderingHints in the RenderContext already * contains a number of retries value specified by the user, that will * take preference over the one stored in this class. * * @param numRetries The number of times an operation should be retried * in case of a network error. * @throws IllegalArgumentException if numRetries is negative. */ public void setNumRetries(int numRetries) { if (numRetries < 0) throw new IllegalArgumentException(JaiI18N.getString("Generic4")); RenderingHints rh = nodeSupport.getRenderingHints(); if (rh == null) { RenderingHints hints = new RenderingHints(null); nodeSupport.setRenderingHints(hints); } nodeSupport.getRenderingHints().put(JAI.KEY_NUM_RETRIES, new Integer(numRetries)); } /** * Returns the current negotiation preferences or null, if none were * set previously. */ public NegotiableCapabilitySet getNegotiationPreferences() { RenderingHints rh = nodeSupport.getRenderingHints(); NegotiableCapabilitySet ncs = rh == null ? null : (NegotiableCapabilitySet)rh.get( JAI.KEY_NEGOTIATION_PREFERENCES); return ncs; } /** * Sets the preferences to be used in the client-server * communication. These preferences are utilized in the negotiation * process. Note that preferences for more than one category can be * specified using this method. Also each preference can be a list * of values in decreasing order of preference, each value specified * as a NegotiableCapability. The * NegotiableCapability first (for a particular category) * in this list is given highest priority in the negotiation process * (for that category). * *

    It may be noted that this method allows for multiple negotiation * cycles by allowing negotiation preferences to be set * multiple times. Every time this method is called, the new preferences * specified will be stored, a negotiation with these new preferences * will be initiated and the results stored. These new preferences which * have been stored will be passed as RenderingHints as part * of the RenderContext used to create the rendering. The * RenderingHints in the RenderContext will * contain this information under the * KEY_NEGOTIATION_PREFERENCES key. If the * RenderingHints in the RenderContext already * contains negotiation preferences specified by the user, the user * specified negotiation preferences will take preference over the ones * stored in this class. * *

    If preferences to be set are null, the negotiation will become * a two-way negotiation between the client and server capabilities. * * @param preferences The preferences to be used in the negotiation * process. */ public void setNegotiationPreferences(NegotiableCapabilitySet preferences) { RenderingHints rh = nodeSupport.getRenderingHints(); // If there are preferences to set if (preferences != null) { // Check whether RenderingHints exists, if not, create it. if (rh == null) { RenderingHints hints = new RenderingHints(null); nodeSupport.setRenderingHints(hints); } // Set the provided preferences into the RenderingHints nodeSupport.getRenderingHints().put( JAI.KEY_NEGOTIATION_PREFERENCES, preferences); } else { // Preferences is null // Remove any previous values set for negotiation preferences if (rh != null) { rh.remove(JAI.KEY_NEGOTIATION_PREFERENCES); } } negotiated = negotiate(preferences); } private NegotiableCapabilitySet negotiate(NegotiableCapabilitySet prefs) { OperationRegistry registry = nodeSupport.getRegistry(); NegotiableCapabilitySet serverCap = null; // Get the RemoteDescriptor for protocolName RemoteDescriptor descriptor = (RemoteDescriptor) registry.getDescriptor(RemoteDescriptor.class, protocolName); if (descriptor == null) { Object[] msgArg0 = {new String(protocolName)}; MessageFormat formatter = new MessageFormat(""); formatter.setLocale(Locale.getDefault()); formatter.applyPattern(JaiI18N.getString("RemoteJAI16")); throw new RuntimeException(formatter.format(msgArg0)); } int count=0; int numRetries = getNumRetries(); int retryInterval = getRetryInterval(); Exception rieSave = null; while (count++ < numRetries) { try { serverCap = descriptor.getServerCapabilities(serverName); break; } catch (RemoteImagingException rie) { // Print that an Exception occured System.err.println(JaiI18N.getString("RemoteJAI24")); rieSave = rie; // Sleep for retryInterval milliseconds try { Thread.sleep(retryInterval); } catch (InterruptedException ie) { // throw new RuntimeException(ie.toString()); sendExceptionToListener(JaiI18N.getString("Generic5"), new ImagingException(JaiI18N.getString("Generic5"), ie)); } } } if (serverCap == null && count > numRetries) { sendExceptionToListener(JaiI18N.getString("RemoteJAI18"), rieSave); // throw new RemoteImagingException(JaiI18N.getString("RemoteJAI18") // + "\n" + rieSave.getMessage()); } RemoteRIF rrif = (RemoteRIF)registry.getFactory("remoteRenderable", protocolName); return RemoteJAI.negotiate(prefs, serverCap, rrif.getClientCapabilities()); } /** * Returns the results of the negotiation between the client and server * capabilities according to the preferences set via the * setNegotiationPreferences() method. This will return null * if no negotiation preferences were set, and no negotiation was * performed, or if the negotiation failed. */ public NegotiableCapabilitySet getNegotiatedValues() throws RemoteImagingException { return negotiated; } /** * Returns the results of the negotiation between the client and server * capabilities for the given catgory according to the preferences set * via the setNegotiationPreferences() method. This will * return null if no negotiation preferences were set, and no * negotiation was performed, or if the negotiation failed. * * @param category The category to return negotiated results for. */ public NegotiableCapability getNegotiatedValues(String category) throws RemoteImagingException { if (negotiated != null) return negotiated.getNegotiatedValue(category); return null; } void sendExceptionToListener(String message, Exception e) { ImagingListener listener = (ImagingListener)getRenderingHints().get(JAI.KEY_IMAGING_LISTENER); listener.errorOccurred(message, e, this, false); } } jai-core-1.1.4/src/share/classes/javax/media/jai/remote/RemoteDescriptor.java0000644000175000017500000001712310203035544026771 0ustar mathieumathieu/* * $RCSfile: RemoteDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:52 $ * $State: Exp $ */package javax.media.jai.remote; import java.awt.RenderingHints; import java.awt.image.renderable.ParameterBlock; import java.net.URL; import javax.media.jai.RegistryElementDescriptor; import javax.media.jai.OperationDescriptor; import javax.media.jai.OperationNode; /** * This interface provides a description of a specific remote imaging * protocol. Information regarding the remote imaging protocol such as * its name, the list of operations supported on a particular server, * the capabilities of a server implementing this protocol, human readable * documentation detailing how the String identifying the * server is structured should all be provided through this interface. * Each remote imaging protocol registered with the * OperationRegistry must have a RemoteDescriptor. * *

    Any implementation of the getName method of * RegistryElementDescriptor is expected to return the name * of the remote imaging protocol. This is the name under which this * RemoteDescriptor will be registered in the * OperationRegistry. * *

    There are two RegistryModes associated with remote imaging. * The first is "remoteRendered" which signifies that the remote imaging * operations lie in the rendered mode, the other is "remoteRenderable" * which signifies that the remote imaging operations lie in the renderable * domain and deal with renderable operations. * *

    The getServerCapabilities() method returns the * capabilities of the specified server. This information may already * be known by virtue of being specified in the imaging protocol, or may * have to be determined from the server, in which case, the implementation * must communicate with the server to get this information. To get the * capabilities of the client, the getClientCapabilities() * method, which exists on RemoteRIF can be used. The reason * for the getClientCapabilities method being defined on the * RemoteRIF instead of on the RemoteDescriptor * is that the descriptor does not have any way to reference the client. * Thus there is no way for the descriptor to report the client * capabilities. On the other hand, the RemoteRIF is the * factory that creates the client, and therefore can be expected to either * know or determine the capabilities of the client. * * @see javax.media.jai.registry.RemoteRenderedRegistryMode * @see javax.media.jai.registry.RemoteRenderableRegistryMode * * @since JAI 1.1 */ public interface RemoteDescriptor extends RegistryElementDescriptor { /** * Returns the list of OperationDescriptors that describe * the operations supported by the server. It is the * implementing class's responsibility to extract this information from * either the server or from its own knowledge of the remote imaging * protocol. * * The format of the serverName argument is protocol-dependent. Thus * different protocol specific subclasses may treat the same * serverName argument in different ways, i.e. one protocol may allow * the serverName argument to be null (if this protocol defines a * default server), while another may consider null an invalid * serverName and throw an Exception. * * @param serverName The String identifying the server. */ OperationDescriptor[] getServerSupportedOperationList(String serverName) throws RemoteImagingException; /** * Returns the set of capabilites supported by the server. It is the * implementing class's responsibility to extract this information from * either the server or from its own knowledge of the remote imaging * protocol. * * The format of the serverName argument is protocol-dependent. Thus * different protocol specific subclasses may treat the same * serverName argument in different ways, i.e. one protocol may allow * the serverName argument to be null (if this protocol defines a * default server), while another may consider null an invalid * serverName and throw an Exception. * * @param serverName The String identifying the server. */ NegotiableCapabilitySet getServerCapabilities(String serverName) throws RemoteImagingException; /** * Returns a URL that points to documentation * containing instructions on constructing a server name string for * the protocol with which this class is associated. */ URL getServerNameDocs(); /** * Calculates the region over which two distinct remote renderings * of an operation may be expected to differ. The operation is * represented by the OperationNode argument to this * method. The String that identifies the operation * can be retrieved via the OperationNode's * getOperationName() method. * *

    The class of the returned object will vary as a function of * the nature of the operation. For rendered and renderable two- * dimensional images this should be an instance of a class which * implements java.awt.Shape. * * @param registryModeName The name of the mode. * @param oldServerName The previous server name. * @param oldParamBlock The previous sources and parameters. * @param oldHints The previous hints. * @param newServerName The current server name. * @param newParamBlock The current sources and parameters. * @param newHints The current hints. * @param node The affected node in the processing chain. * * @return The region over which the data of two renderings of this * operation may be expected to be invalid or null * if there is no common region of validity. If an empty * java.awt.Shape is returned, this indicates * that all pixels within the bounds of the old rendering * remain valid. * * @throws IllegalArgumentException if registryModeName * is null or if the operation requires either * sources or parameters and either oldParamBlock * or newParamBlock is null. * @throws IllegalArgumentException if there is no OperationDescriptor * for the specified operation on any one or both of the * servers identified by oldServerName and * newServerName, or if the number of sources or * the name, number and Class of the operation's * parameters is not the same on both the servers. * @throws IllegalArgumentException if oldParamBlock or * newParamBlock do not contain sufficient sources * or parameters for the operation in question. */ Object getInvalidRegion(String registryModeName, String oldServerName, ParameterBlock oldParamBlock, RenderingHints oldHints, String newServerName, ParameterBlock newParamBlock, RenderingHints newHints, OperationNode node) throws RemoteImagingException; } jai-core-1.1.4/src/share/classes/javax/media/jai/remote/NegotiableCapability.java0000644000175000017500000014247610203035544027564 0ustar mathieumathieu/* * $RCSfile: NegotiableCapability.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:51 $ * $State: Exp $ */package javax.media.jai.remote; import java.io.Serializable; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.Hashtable; import java.util.Vector; import javax.media.jai.OperationDescriptor; import javax.media.jai.ParameterList; import javax.media.jai.ParameterListImpl; import javax.media.jai.ParameterListDescriptor; import javax.media.jai.ParameterListDescriptorImpl; import javax.media.jai.util.CaselessStringKey; /** * A NegotiableCapability represents the capabilities of an * object. These capabilities can be used to negotiate with the capabilities * of a similar object. Each NegotiableCapability is * characterized by the category it belongs to, as returned by the * getCategory() method, by the actual name of the capability * it represents, as returned by the getCapabilityName() method * and by a list of parameter name-value pairs that define the * NegotiableCapability. Every NegotiableCapability * object also holds references to a representation of the objects that * generated it. These can be accessed via the getGenerators() * method. The creator or generator of a NegotiableCapability * can supply any representation of itself while constructing the * NegotiableCapability. No interpretation is forced by this * class, that is left upto the generating class and to the class that * utilizes the NegotiableCapability to get the negotiated * results. The negotiation is performed by the * negotiate() method. Since this method returns a * NegotiableCapability, this method can be used repeatedly to * perform multiple negotiations. If the negotiation fails, null will be * returned from the negotiate() method. Every successful * negotiation will add the generator of the NegotiableCapability * negotiated with, to the set of generators of the resultant * NegotiableCapability. The generators are intended to help * the user of NegotiableCapability identify the object that * created the NegotiableCapability and therefore the object * that can be relied on to be able to handle the parameters agreed on during * negotiation. For example, if the negotiation is to be performed to choose * a compatible TileEncoder, TileDecoder pair * for data compression/decompression, the category would be "tileCodec", * the capabilityName would be a specific tile encoding format, say "jpeg" * and the generator for the NegotiableCapability could be the * TileDecoderFactory/TileEncoderFactory object * that generated that NegotiableCapability. After a * successful negotiation, the NegotiableCapability that is * the result of the negotiation will contain a * TileEncoderFactory and a TileDecoderFactory * object as the generators for that NegotiableCapability. * These two objects can then be retrieved using the * getGenerators method and used to do the encoding and * decoding and can be relied to be compatible, since the negotiation * was successful between their respective * NegotiableCapability objects. * *

    The number, name, Class type and default values for the parameters in * this class is specified by the ParameterListDescriptor * returned from getParameterListDescriptor method. Each * parameter value in this class must be a class that implements the * Negotiable interface. It is for this reason that all of * the ParameterList set methods that take primitive data * types as arguments and all the ParameterList get methods * that return primitive data types are overridden in this class * to throw an IllegalArgumentException, as this class only accepts * Negotiable's as parameter values in order to facilitate * negotiation on parameters. It may be noted that the implementation of * the version of ParameterList.setParameter that takes * an Object as the parameter value, in this class * throws an IllegalArgumentException if the supplied * Object to be set does not implement the * Negotiable interface. If no Negotiable value is * available as the value for a particular parameter, null * should be set as the value. A null value returned from the * getNegotiatedValue(String) method is however valid, since * the single value result of the negotiation can be null. * Similarly the Object returned from the * ParameterList.getObjectParameter implementation in this class * is always a class that implements the Negotiable interface, * and not a wrapper class of a primitive data type, as documented for this * method in ParameterList. The * getParamValueRange(String parameterName) and the * getEnumeratedParameterValues(String parameterName) methods * of the ParameterListDescriptor returned from * getParameterListDescriptor method of this class should be * implemented to return null, since these methods are not meaningful when * the parameter values are Negotiable. * *

    In order for the negotiation to be successful, the category and the * capabilityName of the two NegotiableCapability objects must be * the same. In addition, negotiation on each of the parameters must be * successful. Since each parameter is represented as a * Negotiable, negotiation on it can be performed using the * Negotiable.negotiate(Negotiable negotiable) method. The * NegotiableCapability returned from the * negotiate(NegotiableCapability capability) method * contains the same category and capabilityName as that of the * NegotiableCapability objects being negotiated as well as * including the negotiated values for each parameter. If the negotiation fails * for any one parameter, the negotiation for the * NegotiableCapabilitys as a whole is said to fail (unless * preference NegotiableCapability objects are involved in * the negotiation, as described below) and a null is returned. * *

    In order to get a single negotiated value from the set of valid * values represented as the Negotiable value for a parameter, * the getNegotiatedValue(String parameterName) method can be * called. If the negotiation was successful, an Object which * is the negotiated result will be returned, otherwise a * null (signifying that the negotiation failed) will be * returned. * *

    NegotiableCapability objects can be classified as being * either preferences or non-preferences. A non-preference describes the * capabilities of an object completely by specifying Negotiable * values for each and every parameter defined in the * ParameterListDescriptor returned from * getParameterListDescriptor method. A non-preference is allowed * to not specify the value of a particular parameter, if a default value * for that parameter exists (i.e. the default value is not * null). When a non-preference is created, all parameter * values are initialized to their default values, and therefore if any * parameter value is left unset at the time of the negotiation, the * default value that was set at time of initialization will be used for * the negotiation. If the default value happened to be null, * the negotiation in this case would fail. Note that all references to * values in this paragraph, whether default or not, refered to the * objects implementing the Negotiable interface that are * the values set for a particular parameter name. * * A preference on the other hand specifies preferences for the selection of * a prefered set of (maybe even a single) parameter value from the set of * valid ones at negotiation time. * A preference is allowed to specify Negotiable parameter * values for a subset of parameters, if it so wishes. For those parameters * for whom the preference does not specify values, the preference is * indicating a don't-care attitude, and the result of the negotiation for * such a parameter will be the Negotiable value from the * non-preference object the preference is negotiating with. Note that the * default value is not substituted for a parameter whose value has not been * specified in a preference. A NegotiableCapability which is * a preference should return true from the isPreference method, * a non-preference object that defines values for all the parameters (or * relies on defaults) should return false from this method. As a rule, the * result of negotiation between one non-preference and another is a * non-preference, between a preference and a non-preference is a * non-preference and that between two preferences is a preference, if * the negotiation is successful. It may be noted that preferences are * not expected to specify their generators, since in general, preferences * don't come from objects that can support them. However if generators are * specified within a preference, they will be added to the set of generators * of the resultant NegotiableCapability in the event of a * successful negotiation. * *

    Negotiation between a preference and a non-preference * NegotiableCapability results in a non-preference * NegotiableCapability. For each parameter, if a value is * specified (i.e the value is not null) * in both the preference and the non-preference, then if these values * have a common subset, the negotiation will succeed on this parameter, * if there is no commonality, then the negotiation will fail on this * parameter and thus also fail as a whole. If the preference doesn't * specify a value for a parameter (i.e the value is null), * then the value specified by the non-preference for that same parameter * is chosen as a result of the successful negotiation on that parameter. * *

    Negotiation between two preference NegotiableCapability * objects results in a preference NegotiableCapability. For * each parameter, if a value is specified (i.e the value is not * null) in both the preference objects, the negotiation on * that parameter will have a value which is the portion that is common * to both. If there is no commonality, negotiation will fail on this * parameter (null will be returned) and thus also fail as * a whole. If the value for a particular parameter is specified in one * preference and not in the other, the negotiated value will be the one * specified. If for a particular parameter, no value is specified in * either preference, the negotiated value for that parameter will be * null, and the negotiation as a whole on the * NegotiableCapability will not fail. * *

    When a preference NegotiableCapability is constructed, * the values of all the parameters defined in the * ParameterListDescriptor returned from * getParameterListDescriptor method, are initialized to * null. null within this class represents a * value that has not been specified. Such values are only allowed on * a preference NegotiableCapability. On the other hand when * a non-preference NegotiableCapability is * constructed, all the values are initialized to their default values. * *

    All names are treated in a case-retentive and case-insensitive manner. * * @since JAI 1.1 */ public class NegotiableCapability extends ParameterListImpl implements Serializable { private String category; private String capabilityName; private List generators; private boolean isPreference = false; /** * Creates a NegotiableCapability with the specified * category and capabilityName. * * @param category The category this capability belongs to. * @param capabilityName The name of this capability. * @param generators A List containing representations * of the objects that generated this * NegotiableCapability or null, if * there are none. * @param descriptor The descriptor that describes the parameters for * this class. * @param isPreference Boolean specifying whether this class represents * a preference or a non-preference. * * @throws IllegalArgumentException if category is null. * @throws IllegalArgumentException if capabilityName is null. * @throws IllegalArgumentException if descriptor is null. * @throws IllegalArgumentException if any of the default values returned * from the supplied descriptor's getParamDefaults() method is * ParameterListDescriptor.NO_PARAMETER_DEFAULT. null should be used to * represent the absence of a default. * @throws IllegalArgumentException if any of the Class * types returned from the supplied descriptor's getParamClasses() method * does not implement Negotiable. */ public NegotiableCapability(String category, String capabilityName, List generators, ParameterListDescriptor descriptor, boolean isPreference) { super(descriptor); if (category == null) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCapability0")); } if (capabilityName == null) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCapability1")); } ParameterListDescriptor desc = getParameterListDescriptor(); int numParams = desc.getNumParameters(); String names[] = desc.getParamNames(); Class classes[] = desc.getParamClasses(); Object defaults[] = desc.getParamDefaults(); for (int i=0; iNegotiableCapability. */ public String getCategory() { return category; } /** * Returns the name of this NegotiableCapability. */ public String getCapabilityName() { return capabilityName; } /** * Returns the List containing representations of the * objects that generated this NegotiableCapability. This * method will return null, if there are no generators for this * NegotiableCapability. */ public List getGenerators() { return generators; } /** * Set the specified List as the generators for this * NegotiableCapability. A generator is a representation * of the object that generated this NegotiableCapability. * * @param generators The List of generators. */ public void setGenerators(List generators) { this.generators = generators; } /** * Returns true if this NegotiableCapability is a * preference, false otherwise. */ public boolean isPreference() { return isPreference; } /** * Returns a single negotiated value from the Negotiable that * represents the set of valid values for the given parameter. This * method uses the Negotiable.getNegotiatedValue to get * the negotiated value for the Negotiable value of the * parameter specified by parameterName. If this * NegotiableCapability is a non-preference, then a valid * Negotiable must be present as the value of the specified * parameter, and a single value from that Negotiable will * be returned. If this NegotiableCapability is a preference * the specified parameter may have a null as its value. * In this case, this null will be returned as the * negotiated value. * * @param parameterName The name of parameter to return the negotiated * value for. * @throws IllegalArgumentException if the parameterName is not one of * those described by the associated ParameterListDescriptor. */ public Object getNegotiatedValue(String parameterName) { Negotiable value = (Negotiable)getObjectParameter(parameterName); if (value == null) return null; return value.getNegotiatedValue(); } /** * Performs negotiation between this NegotiableCapability * and the given NegotiableCapability. Returns the common * subset supported by this NegotiableCapability and the given * NegotiableCapability if the negotiation is successful, * null otherwise. * *

    In order for the negotiation to be successful, the category and the * capabilityName of the supplied NegotiableCapability object * must be the same as of this class. In addition, negotiation on each of * the parameters must be successful. Since each parameter is represented * as a Negotiable, negotiation on it can be performed using * the Negotiable.negotiate() method. The * NegotiableCapability returned contains the same category, * capabilityName as that of this class and also includes the negotiated * values for each parameter. If the negotiation fails for any one * parameter, the negotiation for the NegotiableCapabilitys * as a whole is said to fail and a null is returned. The result of * negotiation between one non-preference and another is a non-preference, * between a preference and a non-preference is a non-preference and * that between two preferences is a preference, if the negotiation is * successful. * * If this NegotiableCapability is a non-preference, i.e * the isPreference() method returns false, and the * supplied NegotiableCapability argument is also a * non-preference, then the negotiation will fail if the number and * Class of parameters in both the * NegotiableCapability objects is not the same. * If either one of the NegotiableCapability objects is * a preference and the other is a non-preference, the number of * parameters are not required to match. For those parameters whose names * are the same in both the NegotiableCapability objects, * the Class types have to match, otherwise the negotiation * will fail. Those parameters that exist in the non-preference * NegotiableCapability object but not in the preference * NegotiableCapability object do not take part in the * negotiation, but are directly set on the resultant * NegotiableCapability object if the negotiation * is successful on the common parameters. Those parameters that * exist in the preference NegotiableCapability object but * not in the non-preference NegotiableCapability object * are ignored, do not take part in the negotiation and are not * reflected in the resultant NegotiableCapability in the * event of a successful negotiation. If both the * NegotiableCapability objects are preferences, then * only the common parameters take part in the negotiation and the * ones that aren't present in both the NegotiableCapabilitys * are directly set on the resultant NegotiableCapability * object if the negotiation is successful on the common parameters. * For the common parameters, the Class types have to match, * otherwise the negotiation will fail. The check for the compatibility * of the ParameterListDescriptor of the supplied * NegotiableCapability with the current * NegotiableCapability's ParameterListDescriptor * is done using the areParameterListDescriptorsCompatible() * method. * It may be noted that the ParameterListDescriptor of * the NegotiableCapability returned as a result of a * successful negotiation will implement the getParamDefaults() and * the getParamValueRange() methods in terms of the values returned * from the same methods on the ParameterListDescriptor * associated with this class, if the negotiation took place between * two preferences, or from the same methods on the * ParameterListDescriptor associated with the * non-preference otherwise. * *

    If the supplied NegotiableCapability is null, then * the negotiation will fail and null will be returned. * * @param capability The NegotiableCapability to negotiate * with. * @returns the NegotiableCapability that is the result of a * successful negotiation, null if the negotiation failed. */ public NegotiableCapability negotiate(NegotiableCapability capability) { if (capability == null) { return null; } if (capability.getCategory().equalsIgnoreCase(category) == false || capability.getCapabilityName().equalsIgnoreCase(capabilityName) == false) { // Negotiation failed return null; } // If the PLD's are not compatible for negotiation, fail the // negotiation if (areParameterListDescriptorsCompatible(capability) == false) { return null; } int negStatus; if (capability.isPreference() == true) { if (isPreference == true) { negStatus = 0; } else { negStatus = 1; } } else { if (isPreference == true) { negStatus = 2; } else { negStatus = 3; } } ParameterListDescriptor pld = getParameterListDescriptor(); ParameterListDescriptor otherPld = capability.getParameterListDescriptor(); String thisNames[] = pld.getParamNames(); if (thisNames == null) thisNames = new String[0]; String otherNames[] = otherPld.getParamNames(); if (otherNames == null) otherNames = new String[0]; Hashtable thisHash = hashNames(thisNames); Hashtable otherHash = hashNames(otherNames); Class thisClasses[] = pld.getParamClasses(); Class otherClasses[] = otherPld.getParamClasses(); Object thisDefaults[] = pld.getParamDefaults(); Object otherDefaults[] = otherPld.getParamDefaults(); NegotiableCapability result = null; String currParam; Negotiable thisValue, otherValue, resultValue; ArrayList resultGenerators = new ArrayList(); if (generators != null) resultGenerators.addAll(generators); if (capability.getGenerators() != null) resultGenerators.addAll(capability.getGenerators()); switch (negStatus) { case 0: Vector commonNames = commonElements(thisHash, otherHash); Hashtable commonHash = hashNames(commonNames); Vector thisExtras = removeAll(thisHash, commonHash); Vector otherExtras = removeAll(otherHash, commonHash); int thisExtraLength = thisExtras.size(); int otherExtraLength = otherExtras.size(); // Create a new PLD which is the amalgamation of the two // NC's PLD's Vector resultParams = new Vector(commonNames); resultParams.addAll(thisExtras); resultParams.addAll(otherExtras); int resultLength = resultParams.size(); String resultNames[] = new String[resultLength]; for (int i=0; iParameterListDescriptor of the * supplied NegotiableCapability is compatible with the * ParameterListDescriptor of this class for negotiation * purposes. If both the NegotiableCapability objects are * non-preferences, both the number of parameters as well as the * Class type of the parameters has to match for this * method to return true. If either one or both of the * NegotiableCapability objects is a preference, then * the Class type of the same named parameters in both * the NegotiableCapability object's * ParameterListDescriptors has to match for this * method to return true. * * @param other The NegotiableCapability to check * compatibility for negotiation purposes for. * @throws IllegalArgumentException if other is null. */ public boolean areParameterListDescriptorsCompatible(NegotiableCapability other) { if (other == null) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCapability6")); } ParameterListDescriptor thisDesc = getParameterListDescriptor(); ParameterListDescriptor otherDesc = other.getParameterListDescriptor(); String thisNames[] = thisDesc.getParamNames(); if (thisNames == null) thisNames = new String[0]; String otherNames[] = otherDesc.getParamNames(); if (otherNames == null) otherNames = new String[0]; Hashtable thisHash = hashNames(thisNames); Hashtable otherHash = hashNames(otherNames); if (isPreference == false && other.isPreference() == false) { // The number of parameters must be the same. if (thisDesc.getNumParameters() != otherDesc.getNumParameters()) return false; // The same names should be present in both in the same order. if (containsAll(thisHash, otherHash) == false) return false; Class thisParamClasses[] = thisDesc.getParamClasses(); Class otherParamClasses[] = otherDesc.getParamClasses(); for (int i=0; iParameterListImpl to throw * an IllegalArgumentException since parameter values set on this class * must be a Negotiable. * * @param paramName a String naming a parameter. * @param b a byte value for the parameter. * * @throws IllegalArgumentException since the value being set is not a * Negotiable. */ public ParameterList setParameter(String paramName, byte b) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCapability2")); } /** * Overrides the method in ParameterListImpl to throw * an IllegalArgumentException since parameter values set on this class * must be a Negotiable. * * @param paramName a String naming a parameter. * @param b a boolean value for the parameter. * * @throws IllegalArgumentException since the value being set is not a * Negotiable. */ public ParameterList setParameter(String paramName, boolean b) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCapability2")); } /** * Overrides the method in ParameterListImpl to throw * an IllegalArgumentException since parameter values set on this class * must be a Negotiable. * * @param paramName a String naming a parameter. * @param c a char value for the parameter. * * @throws IllegalArgumentException since the value being set is not a * Negotiable. */ public ParameterList setParameter(String paramName, char c) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCapability2")); } /** * Overrides the method in ParameterListImpl to throw * an IllegalArgumentException since parameter values set on this class * must be a Negotiable. * * @param paramName a String naming a parameter. * @param s a short value for the parameter. * * @throws IllegalArgumentException since the value being set is not a * Negotiable. */ public ParameterList setParameter(String paramName, short s) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCapability2")); } /** * Overrides the method in ParameterListImpl to throw * an IllegalArgumentException since parameter values set on this class * must be a Negotiable. * * @param paramName a String naming a parameter. * @param i an int value for the parameter. * * @throws IllegalArgumentException since the value being set is not a * Negotiable. */ public ParameterList setParameter(String paramName, int i) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCapability2")); } /** * Overrides the method in ParameterListImpl to throw * an IllegalArgumentException since parameter values set on this class * must be a Negotiable. * * @param paramName a String naming a parameter. * @param l a long value for the parameter. * * @throws IllegalArgumentException since the value being set is not a * Negotiable. */ public ParameterList setParameter(String paramName, long l) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCapability2")); } /** * Overrides the method in ParameterListImpl to throw * an IllegalArgumentException since parameter values set on this class * must be a Negotiable. * * @param paramName a String naming a parameter. * @param f a float value for the parameter. * * @throws IllegalArgumentException since the value being set is not a * Negotiable. */ public ParameterList setParameter(String paramName, float f) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCapability2")); } /** * Overrides the method in ParameterListImpl to throw * an IllegalArgumentException since parameter values set on this class * must be a Negotiable. * * @param paramName a String naming a parameter. * @param d a double value for the parameter. * * @throws IllegalArgumentException since the value being set is not a * Negotiable. */ public ParameterList setParameter(String paramName, double d) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCapability2")); } /** * Overrides the superclass method to ensure only a Negotiable * object can be added as the value of the parameter. * * @param paramName A String naming a parameter. * @param obj An Object value for the parameter. * * @throws IllegalArgumentException if obj is not an instance of * Negotiable. */ public ParameterList setParameter(String paramName, Object obj) { if (obj != null && !(obj instanceof Negotiable)) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCapability2")); } super.setParameter(paramName, obj); return this; } // getObjectParameter method doesn't need to be overridden since it // is implemented in ParameterListImpl and can return a Negotiable as // an Object /** * Overrides the method in ParameterListImpl to throw * an IllegalArgumentException since parameter values in this class * are Negotiable and therefore cannot be returned as * a primitive data type. * * @param paramName the name of the parameter to be returned. * @throws IllegalArgumentException since a Negotiable value * cannot be returned as a primitive data type. */ public byte getByteParameter(String paramName) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCapability3")); } /** * Overrides the method in ParameterListImpl to throw * an IllegalArgumentException since parameter values in this class * are Negotiable and therefore cannot be returned as * a primitive data type. * * @param paramName the name of the parameter to be returned. * @throws IllegalArgumentException since a Negotiable value * cannot be returned as a primitive data type. */ public boolean getBooleanParameter(String paramName) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCapability3")); } /** * Overrides the method in ParameterListImpl to throw * an IllegalArgumentException since parameter values in this class * are Negotiable and therefore cannot be returned as * a primitive data type. * * @param paramName the name of the parameter to be returned. * @throws IllegalArgumentException since a Negotiable value * cannot be returned as a primitive data type. */ public char getCharParameter(String paramName) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCapability3")); } /** * Overrides the method in ParameterListImpl to throw * an IllegalArgumentException since parameter values in this class * are Negotiable and therefore cannot be returned as * a primitive data type. * * @param paramName the name of the parameter to be returned. * @throws IllegalArgumentException since a Negotiable value * cannot be returned as a primitive data type. */ public short getShortParameter(String paramName) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCapability3")); } /** * Overrides the method in ParameterListImpl to throw * an IllegalArgumentException since parameter values in this class * are Negotiable and therefore cannot be returned as * a primitive data type. * * @param paramName the name of the parameter to be returned. * @throws IllegalArgumentException since a Negotiable value * cannot be returned as a primitive data type. */ public int getIntParameter(String paramName) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCapability3")); } /** * Overrides the method in ParameterListImpl to throw * an IllegalArgumentException since parameter values in this class * are Negotiable and therefore cannot be returned as * a primitive data type. * * @param paramName the name of the parameter to be returned. * @throws IllegalArgumentException since a Negotiable value * cannot be returned as a primitive data type. */ public long getLongParameter(String paramName) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCapability3")); } /** * Overrides the method in ParameterListImpl to throw * an IllegalArgumentException since parameter values in this class * are Negotiable and therefore cannot be returned as * a primitive data type. * * @param paramName the name of the parameter to be returned. * @throws IllegalArgumentException since a Negotiable value * cannot be returned as a primitive data type. */ public float getFloatParameter(String paramName) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCapability3")); } /** * Overrides the method in ParameterListImpl to throw * an IllegalArgumentException since parameter values in this class * are Negotiable and therefore cannot be returned as * a primitive data type. * * @param paramName the name of the parameter to be returned. * @throws IllegalArgumentException since a Negotiable value * cannot be returned as a primitive data type. */ public double getDoubleParameter(String paramName) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCapability3")); } } jai-core-1.1.4/src/share/classes/javax/media/jai/remote/NegotiableNumericRange.java0000644000175000017500000001246010203035544030047 0ustar mathieumathieu/* * $RCSfile: NegotiableNumericRange.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:51 $ * $State: Exp $ */package javax.media.jai.remote; import java.math.BigInteger; import java.math.BigDecimal; import javax.media.jai.util.Range; /** * A class that wraps a Range which contains numeric elements, * to implement the Negotiable interface. * NegotiableNumericRange is a convenience class to specify a * Negotiable parameter whose valid numeric values are * specified by a Range. * * @since JAI 1.1 */ public class NegotiableNumericRange implements Negotiable { private Range range; /** * Creates a NegotiableNumericRange given an * Range containing elements of a subclass of * Number. * * @throws IllegalArgumentException if range is null. * @throws IllegalArgumentException if the elements of the supplied range * are not a Number subclass. */ public NegotiableNumericRange(Range range) { if (range == null) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableNumericRange0")); } // If the elementClass of the supplied Range is not a subclass of // Number, throw an IllegalArgumentException if (!(Number.class.isAssignableFrom(range.getElementClass()))) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableNumericRange1")); } this.range = range; } /** * Returns the Range of values which are currently valid * for this class, null if there are no valid values. */ public Range getRange() { if (range.isEmpty()) return null; return range; } /** * Returns a NegotiableNumericRange that contains the range * of values that are common to this NegotiableNumericRange * and the one supplied. If the supplied Negotiable is not * a NegotiableNumericRange with its elements being of the * same Class as this class', or if there is no common * range of values, the negotiation will fail and * null will be returned. * * @param other The Negotiable to negotiate with. */ public Negotiable negotiate(Negotiable other) { if (other == null) return null; // if other is not an instance of NegotiableNumericRange, // or the element class doesn't match, negotiation fails if (!(other instanceof NegotiableNumericRange)) return null; NegotiableNumericRange otherNNRange = (NegotiableNumericRange)other; Range otherRange = otherNNRange.getRange(); // If the range is null, i.e there are no valid values, then // negotiation fails. if (otherRange == null) return null; // If the elementClass' don't match, negotiation fails. if (otherRange.getElementClass() != range.getElementClass()) return null; Range result = range.intersect(otherRange); // If there are no valid values, negotiation failed. if (result.isEmpty()) return null; return new NegotiableNumericRange(result); } /** * Returns a single value that is valid for this * NegotiableNumericRange. The returned value is the lowest * value contained in this NegotiableNumericRange if the * range is not unbounded on the minimum end, or the highest value * in the range, if the range is unbounded on the minimum end. If both * ends are unbounded, 0 will be returned wrapped in the appropriate * Number wrapper. Returns null if there * are no valid elements in this NegotiableNumericRange. */ public Object getNegotiatedValue() { // If there are no valid values, negotiation fails. if (range.isEmpty()) return null; Number minValue = (Number)range.getMinValue(); // Is minimum end unbounded if (minValue == null) { Number maxValue = (Number)range.getMaxValue(); // Is maximum unbounded if (maxValue == null) { // Both ends are unbounded Class elementClass = range.getElementClass(); // Have elementClass specific case statements, and get the // negotiated value on the datatype basis if (elementClass == Byte.class) { return new Byte((byte)0); } else if (elementClass == Short.class) { return new Short((short)0); } else if (elementClass == Integer.class) { return new Integer(0); } else if (elementClass == Long.class) { return new Long((long)0); } else if (elementClass == Float.class) { return new Float(0); } else if (elementClass == Double.class) { return new Double(0); } else if (elementClass == BigInteger.class) { return BigInteger.ZERO; } else if (elementClass == BigDecimal.class) { return new BigDecimal(BigInteger.ZERO); } } else { return maxValue; } } return minValue; } /** * Returns the Class of the Object returned as the result * of the negotiation. This will be a subclass of Number. */ public Class getNegotiatedValueClass() { return range.getElementClass(); } } jai-core-1.1.4/src/share/classes/javax/media/jai/remote/SerializableState.java0000644000175000017500000000200710203035544027101 0ustar mathieumathieu/* * $RCSfile: SerializableState.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:53 $ * $State: Exp $ */ package javax.media.jai.remote; import java.io.Serializable; /** * An interface to be implemented by classes instances of which act as * serializable proxies for instances of non-serializable classes. * * @see java.io.Serializable * * @since JAI 1.1 */ public interface SerializableState extends Serializable { /** * Retrieve the class of the object which would be returned by * invoking getObject(). * * @return The class of the object which would be returned by * getObject(). */ Class getObjectClass(); /** * Reconstitutes an object from a serializable version of its state * wrapped by an implementation of this interface. * * @return Deserialized form of the state. */ Object getObject(); } jai-core-1.1.4/src/share/classes/javax/media/jai/remote/javax.media.jai.remote.properties0000644000175000017500000001610410203035544031173 0ustar mathieumathieu# # $RCSfile: javax.media.jai.remote.properties,v $ # # Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. # # Use is subject to license terms. # # $Revision: 1.1 $ # $Date: 2005-02-11 04:57:54 $ # $State: Exp $ # Generic0=The method parameter is null. Generic1=protocolName argument is null. Generic2=serverName argument is null. Generic3=The retryInterval argument is negative. Generic4=The numRetries argument is negative. Generic5=The retry to get the server capabilities is interrupted. JAIRMIDescriptor1=There is no OperationDescriptor available on the old server for the specified operationName. JAIRMIDescriptor2=There is no OperationDescriptor available on the server for the specified operationName. JAIRMIDescriptor3=The OperationDescriptors from the old and new servers require different number of sources. JAIRMIDescriptor4=The OperationDescriptors from the old and new servers require different number of parameters. JAIRMIDescriptor5=The input argument(s) may not be null. JAIRMIDescriptor6=operation "{0}" requires {1,number,integer} source object(s). JAIRMIDescriptor7=operation "{0}" requires {1,number,integer} parameter object(s). JAIRMIDescriptor8=The parameter names are not the same for the OperationDescriptors on the old and new server. JAIRMIDescriptor9=The Class types for parameter(s) is not the same for the OperationDescriptors on the old and new server. JAIRMIDescriptor10=Cannot compute invalid region; node's protocol is not jairmi. JAIRMIDescriptor11=registryModeName argument is null. JAIRMIDescriptor12=Fail to get the operation description list from the server. JAIRMIDescriptor13=Fail to get the host address of the server. JAIRMIDescriptor14=Fail to look up the image server object. JAIRMIDescriptor15=Fail to get the server capabilities. JAIRMIDescriptor16=Fail to get the invalid region. NegotiableCapability0=The category argument is null. NegotiableCapability1=The capabilityName argument is null. NegotiableCapability2=Parameter values must implement the Negotiable interface. NegotiableCapability3=A Negotiable value cannot be returned as a primitive data type. NegotiableCapability4=All Class objects returned from the supplied ParameterListDescriptor's getParamClasses() method must implement Negotiable. NegotiableCapability5=Any default value as reported by the supplied ParameterListDescriptor's getParamDefaults() method cannot be OperationDescriptor.NO_PARAMETER_DEFAULT. NegotiableCapability6=The other NegotiableCapability argument is null. NegotiableCapabilitySet0=The supplied capability argument is null. NegotiableCapabilitySet1=The supplied capability has an incompatible preference type. NegotiableCapabilitySet2=The supplied capability argument was not added previously. NegotiableCapabilitySet3=The supplied category argument is null. NegotiableCapabilitySet4=The supplied capabilityName argument is null. NegotiableNumeric0=The supplied Number argument is null. NegotiableNumeric1=Cannot return value as the requested Class type. NegotiableNumericRange0=The supplied Range argument is null. NegotiableNumericRange1=The supplied Range argument's elementClass is not a subclass of Number. NegotiableCollection0=The supplied argument is null. NegotiableCollection1=The supplied argument's elements are of differing Class types. PlanarImageServerProxy0=Encountered RemoteImagingException, sleeping for retryInterval milliseconds before retrying operation. PlanarImageServerProxy1=The operationName argument is null. PlanarImageServerProxy2=Limit of retries exceeded. PlanarImageServerProxy3=All the fields of the ImageLayout object must be initialized. RemoteDescriptorImpl1=modeName argument is null. RemoteDescriptorImpl2=Properties are not supported by this descriptor. RemoteJAI4=The OperationRegistry to set is null. RemoteJAI5=The TileCache to set is null. RemoteJAI6=The RenderingHints to set is null. RemoteJAI7=The key argument is null. RemoteJAI8=The value argument is null. RemoteJAI9=The opName argument is null. RemoteJAI10=The args argument is null. RemoteJAI11=There is no OperationDescriptor available from the server for the specified operation name. RemoteJAI12=The specified operation name does not support the rendered mode. RemoteJAI13=The specified operation does not produce a RenderedImage as its result. RemoteJAI14=The specified operation name does not support the renderable mode. RemoteJAI15=The specified operation does not produce a RenderableImage as its result. RemoteJAI16=There is no RemoteDescriptor registered for the protocol - {0}. RemoteJAI17=There is no factory object registered for the protocol - {0}. RemoteJAI18=Limit of retries reached when calling getServerCapabilities(). RemoteJAI19=The preferences argument must return true from its isPreference() method. RemoteJAI20=The serverCapabilities argument must return false from its isPreference() method. RemoteJAI21=The clientCapabilities argument must return false from its isPreference() method. RemoteJAI22=The category argument is null. RemoteJAI23=Limit of retries reached when calling getServerSupportedOperationList(). RemoteJAI24=Error occurred during getServerCapabilities()...Retrying RemoteJAI25=Error occurred during getServerSupportedOperationList()...Retrying RemoteJAI26=The category argument is null. RemoteRenderedOp2= - Unable to render RenderedOp for this operation. RemoteRenderableOp0=No adequate RemoteCRIF exists in the registry. SerializerFactory0=The Object parameter is null. SerializerFactory1=The supplied Object is not an instance of a supported class and does not implement a supported interface. SerializerFactory2=The supplied Object is not an instance of Serializable. SerializableRenderedImage0=The source parameter cannot be null. SerializableRenderedImage1=The formatName parameter cannot be null. SerializableRenderedImage2=No Serializers available for the SampleModel. SerializableRenderedImage3=No Serializers available for the ColorModel. SerializableRenderedImage4=No Serializers available for the DataBuffer. SerializableRenderedImage5=SocketException occurs when open the server. SerializableRenderedImage6=IOException occurs when open the server. SerializableRenderedImage7=IOException occurs when open the streams of the socket. SerializableRenderedImage8=IOException occurs when read objects. SerializableRenderedImage9=The class of read object is not found. SerializableRenderedImage10=IOException occurs when write a raster. SerializableRenderedImage11=IOException occurs when close the socket. SerializableRenderedImage12=IOException occurs when write a rectangle. SerializableRenderedImage13=IOException occurs when write the close message. SerializableRenderedImage14=IOException occurs when create the socket. SerializableRenderedImage15=IOException occurs when encode a tile. SerializableRenderedImage16=IOException occurs when decode a tile. SerializableRenderedImage17=IOException occured when writing CLOSE_ACK object. UseTileCodec0=The format name in the encoding parameter should be the same as the provided one. UseTileCodec1=The format name in the decoding parameter should be the same as the provided one. UseTileCodec2=The encoder or decoder factory is not registered for the provided format. jai-core-1.1.4/src/share/classes/javax/media/jai/remote/NegotiableNumeric.java0000644000175000017500000001707510203035544027101 0ustar mathieumathieu/* * $RCSfile: NegotiableNumeric.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:51 $ * $State: Exp $ */package javax.media.jai.remote; /** * A class that wraps a numeric primitive data type or a subclass of * Number to implement the Negotiable interface. * NegotiableNumeric is a convenience class to specify a * Negotiable value for a parameter which has a single * valid numeric value. * * @since JAI 1.1 */ public class NegotiableNumeric implements Negotiable { // The numeric value stored as a Number subclass; Number number; // The Class of the Number. Class elementClass; /** * Creates a NegotiableNumeric given a byte. * * @param b The byte to be wrapped to implement * Negotiable. */ public NegotiableNumeric(byte b) { number = new Byte(b); elementClass = number.getClass(); } /** * Creates a NegotiableNumeric given a short. * * @param s The short to be wrapped to implement * Negotiable. */ public NegotiableNumeric(short s) { number = new Short(s); elementClass = number.getClass(); } /** * Creates a NegotiableNumeric given an int. * * @param i The int to be wrapped to implement * Negotiable. */ public NegotiableNumeric(int i) { number = new Integer(i); elementClass = number.getClass(); } /** * Creates a NegotiableNumeric given a long. * * @param l The long to be wrapped to implement * Negotiable. */ public NegotiableNumeric(long l) { number = new Long(l); elementClass = number.getClass(); } /** * Creates a NegotiableNumeric given a float. * * @param f The float to be wrapped to implement * Negotiable. */ public NegotiableNumeric(float f) { number = new Float(f); elementClass = number.getClass(); } /** * Creates a NegotiableNumeric given a double. * * @param d The double to be wrapped to implement * Negotiable. */ public NegotiableNumeric(double d) { number = new Double(d); elementClass = number.getClass(); } /** * Creates a NegotiableNumeric given a Number. * * @param n The Number to be wrapped to implement * Negotiable. * * @throws IllegalArgumentException if n is null. */ public NegotiableNumeric(Number n) { if (n == null) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableNumeric0")); } number = n; elementClass = number.getClass(); } /** * Returns the Number that is currently the valid value * for this class. A valid primitive data type value, such as int, * will be returned as a member of the corresponding wrapper class, * such as Integer. */ public Number getNumber() { return number; } /** * Returns a NegotiableNumeric that contains the value * that is common to this NegotiableNumeric * and the one supplied, i.e the Number encapsulated in * both the NegotiableNumeric are equal. If the supplied * Negotiable is not a NegotiableNumeric with * its element being of the same Class as this class', or * if there is no common value (i.e the values are not equal), the * negotiation will fail and null will be returned. * * @param other The Negotiable to negotiate with. */ public Negotiable negotiate(Negotiable other) { if (other == null) return null; if (!(other instanceof NegotiableNumeric) || other.getNegotiatedValueClass() != elementClass) { return null; } NegotiableNumeric otherNN = (NegotiableNumeric)other; if (number.equals(otherNN.getNumber())) { return new NegotiableNumeric(number); } else { return null; } } /** * Returns the result of the negotiation as a Number * subclass. Values belonging to a base type, such as int, * will be returned as a member of the corresponding Number * subclass, such as Integer. */ public Object getNegotiatedValue() { return number; } /** * Returns the Class of the negotiated value. Values * belonging to a base type, such as int, will be returned * as a member of the corresponding Number subclass, such as * Integer. The Class returned similarly will be * a Number subclass. */ public Class getNegotiatedValueClass() { return elementClass; } /** * A convenience method to return the single negotiated value as a * byte. * * @throws ClassCastException if the value is of a different Class type. */ public byte getNegotiatedValueAsByte() { if (elementClass != Byte.class) throw new ClassCastException( JaiI18N.getString("NegotiableNumeric1")); return number.byteValue(); } /** * A convenience method to return the single negotiated value as a * short. * * @throws ClassCastException if the value is of a different Class type. */ public short getNegotiatedValueAsShort() { if (elementClass != Short.class) throw new ClassCastException( JaiI18N.getString("NegotiableNumeric1")); return number.shortValue(); } /** * A convenience method to return the single negotiated value as a * int. * * @throws ClassCastException if the value is of a different Class type. */ public int getNegotiatedValueAsInt() { if (elementClass != Integer.class) throw new ClassCastException( JaiI18N.getString("NegotiableNumeric1")); return number.intValue(); } /** * A convenience method to return the single negotiated value as a * long. * * @throws ClassCastException if the value is of a different Class type. */ public long getNegotiatedValueAsLong() { if (elementClass != Long.class) throw new ClassCastException( JaiI18N.getString("NegotiableNumeric1")); return number.longValue(); } /** * A convenience method to return the single negotiated value as a * float. * * @throws ClassCastException if the value is of a different Class type. */ public float getNegotiatedValueAsFloat() { if (elementClass != Float.class) throw new ClassCastException( JaiI18N.getString("NegotiableNumeric1")); return number.floatValue(); } /** * A convenience method to return the single negotiated value as a * double. * * @throws ClassCastException if the value is of a different Class type. */ public double getNegotiatedValueAsDouble() { if (elementClass != Double.class) throw new ClassCastException( JaiI18N.getString("NegotiableNumeric1")); return number.doubleValue(); } } jai-core-1.1.4/src/share/classes/javax/media/jai/remote/NegotiableCollection.java0000644000175000017500000001344110203035544027563 0ustar mathieumathieu/* * $RCSfile: NegotiableCollection.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:51 $ * $State: Exp $ */package javax.media.jai.remote; import java.util.Collection; import java.util.Iterator; import java.util.Vector; /** * A class that wraps an Collection to implement the * Negotiable interface. NegotiableCollection * is a convenience class to specify a Negotiable value for * a parameter whose valid values are contained in an Collection. * * @since JAI 1.1 */ public class NegotiableCollection implements Negotiable { private Vector elements; private Class elementClass; /** * Creates a NegotiableCollection given an * Collection. * * @throws IllegalArgumentException if collection is null. * @throws IllegalArgumentException if all the elements of collection * are not of the same Class type. */ public NegotiableCollection(Collection collection) { if (collection == null) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCollection0")); } elements = new Vector(); Object obj; Iterator i = collection.iterator(); if (i.hasNext()) { obj = i.next(); elements.add(obj); elementClass = obj.getClass(); } else { // no elements, so elementClass will be initialized to null, // which is correct. elements will also be null, which is // also correct. } for ( ;i.hasNext(); ) { obj = i.next(); if (obj.getClass() != elementClass) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCollection1")); } elements.add(obj); } } /** * Creates a NegotiableCollection given an array of * Objects. The elements of the Object * array are treated as being the elements of an Collection. * * @throws IllegalArgumentException if objects is null. * @throws IllegalArgumentException if all the elements of objects are not * of the same Class type. */ public NegotiableCollection(Object objects[]) { if (objects == null) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCollection0")); } int length = objects.length; if (length != 0) { elementClass = objects[0].getClass(); } else { // no elements, so elementClass will be initialized to null, // which is correct. elements will also be null, which is // also correct. } elements = new Vector(length); for (int i=0; iCollection of values which are currently * valid for this class, null if there are no valid values. */ public Collection getCollection() { if (elements.isEmpty()) return null; return elements; } /** * Returns a NegotiableCollection that contains those * elements that are common to this NegotiableCollection * and the one supplied. If the supplied Negotiable is not * a NegotiableCollection with its elements being of the * same Class as this class', or if there are no common * elements, the negotiation will fail and null (signifying * the failure of the negotiation) will be returned. * * @param other The Negotiable to negotiate with. */ public Negotiable negotiate(Negotiable other) { if (other == null) { return null; } // if other is not an instance of NegotiableCollection if (!(other instanceof NegotiableCollection) || other.getNegotiatedValueClass() != elementClass) { return null; } Object obj; Vector result = new Vector(); Collection otherCollection = ((NegotiableCollection)other).getCollection(); // If the collection is null, i.e there are no valid values, then // negotiation fails. if (otherCollection == null) return null; // Return a NegotiableCollection whose elements are those that // were common to both the collections. for (Iterator i=elements.iterator(); i.hasNext(); ) { obj = i.next(); // If element is present in both the collections if (otherCollection.contains(obj)) { // Do not insert duplicates if (!result.contains(obj)) { result.add(obj); } } } // If there are no common elements, negotiation failed. if (result.isEmpty()) { return null; } return new NegotiableCollection(result); } /** * Returns a single value that is valid for this * NegotiableCollection. The returned value is the first * element contained in this NegotiableCollection. Returns * null if there are no valid elements in this * NegotiableCollection. */ public Object getNegotiatedValue() { // Return the first element in this NegotiableCollection // else return // null if (elements != null && elements.size() > 0) { return elements.elementAt(0); } else { return null; } } /** * Returns the Class of the Object returned as the result * of the negotiation. If the Collection used to construct * this NegotiableCollection was empty, i.e. had no * elements, the Class of the elements is indeterminate, * therefore null will be returned from this method in such a case. */ public Class getNegotiatedValueClass() { return elementClass; } } jai-core-1.1.4/src/share/classes/javax/media/jai/remote/JAIRMIDescriptor.java0000644000175000017500000005275410203035544026522 0ustar mathieumathieu/* * $RCSfile: JAIRMIDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:50 $ * $State: Exp $ */package javax.media.jai.remote; import java.awt.RenderingHints; import java.awt.Rectangle; import java.awt.image.renderable.ParameterBlock; import java.net.URL; import java.net.InetAddress; import java.rmi.Naming; import java.rmi.RemoteException; import java.text.MessageFormat; import java.util.Iterator; import java.util.Enumeration; import java.util.Hashtable; import java.util.Locale; import java.util.List; import java.util.Vector; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptor; import javax.media.jai.OperationNode; import javax.media.jai.RenderedOp; import javax.media.jai.ParameterListDescriptor; import javax.media.jai.util.CaselessStringKey; import javax.media.jai.util.ImagingException; import javax.media.jai.util.ImagingListener; import com.sun.media.jai.rmi.ImageServer; import com.sun.media.jai.rmi.RMIServerProxy; import com.sun.media.jai.rmi.JAIRMIUtil; import com.sun.media.jai.util.ImageUtil; /** * This class describes the "jairmi" remote imaging protocol. This protocol * assumes that both the client and the server are running JAI. The * communication between the client and the server takes place using * the Remote Method Invocation (RMI) mechanism. * *

    In order to locate the "jairmi" server, a RMI registry must be * running on this server, and the "jairmi" server must have registered * itself with this RMI registry by binding itself under the * IMAGE_SERVER_BIND_NAME String. The RMI * registry is a simple remote object name service that allows remote * clients to get a reference to a remote object by name. * *

    The "jairmi" protocol expects the String that * represents the server to be a URL formatted * String of the form: * *

     * //host:port
     * 
    * * where host is the name, or IP address of the "jairmi" * remote imaging server, and port is the port number * where a rmiregistry is running on the same host. A protocol like * "rmi:" does not need to be included in this URL formatted * String. If the serverName * String is null, the local host is used as a default. * If the port is not included in the serverName String, it * defaults to the well-known port for rmiregistry, 1099. * *

    If the serverName supplied to any "jairmi" protocol implementing * class's method is null, then the local host will be used instead. * *

    The default "jairmi" server provided with JAI is * com.sun.media.jai.rmi.JAIRMIRemoteServer. This server * can be run in the following manner, after starting a rmiregistry on * the host where the server will be run: * *

     * java -Djava.rmi.server.codebase="file:$JAI/lib/jai_core.jar file:$JAI/lib/jai_codec.jar" -Djava.rmi.server.useCodebaseOnly=false -Djava.security.policy=file:$JAI/policy com.sun.media.jai.rmi.JAIRMIImageServer
     * 
    * * where $JAI refers to the directory where JAI is installed. This server * binds itself with the running rmiregistry under the * IMAGE_SERVER_BIND_NAME String bind name, and * can be used to serve "jairmi" requests. The policy file specified * above needs to be created by the user. Information on policy * files and permissions can be found at *

    http://java.sun.com/j2se/1.3/docs/guide/security/PolicyFiles.html *

    http://java.sun.com/j2se/1.3/docs/guide/security/permissions.html * *

    The JAI instance used by the "jairmi" remote imaging server can be * configured by providing an implementation of the * com.sun.media.jai.remote.JAIServerConfigurationSpi interface * on the CLASSPATH when starting the server. * For more details, please refer to * {@link com.sun.media.jai.remote.JAIServerConfigurationSpi} * *

    The "jairmi" remote imaging server supports the following * configurable parameters whose values can be specified on the command * line when starting the server : * * * -host The server name or server IP address * -port The port that rmiregistry is running on * -rmiRegistryPort Same as -port option * -serverPort The port that the server should listen on, * for connections from clients * -cacheMemCapacity The memory capacity in bytes. * -cacheMemThreshold The memory threshold, which is the * fractional amount of cache memory to * retain during tile removal * -disableDefaultCache Disable use of default tile cache. * -schedulerParallelism The degree of parallelism of the * default TileScheduler * -schedulerPrefetchParallelism The degree of parallelism * of the default TileScheduler for tile prefetching * -schedulerPriority The priority of tile scheduling for * the default TileScheduler * -schedulerPrefetchPriority The priority of tile prefetch * scheduling for the default TileScheduler * -defaultTileSize x The default tile dimensions in * the form x * -defaultRenderingSize x The default size to render * a RenderableImage to, in the form x * -serializeDeepCopy Whether a deep copy of the image data * should be used when serializing images * -tileCodecFormat The default format to be used for tile * serialization via TileCodecs * -retryInterval The retry interval value to be used for * dealing with network errors during remote imaging * -numRetries The number of retries to be used for dealing * with network errors during remote imaging * * *

    It should be noted that if a parameter * was set via JAIServerConfigurationSpi, and the command line option for * the same parameter specifies a different value, then the command line * specified parameter value will be honored. That is to say that the * JAIServerConfigurationSpi specified configuration happens first, followed * by command line parameter configuration, and the last configuration to * be applied overwrites all previous settings. * @since JAI 1.1 */ public class JAIRMIDescriptor extends RemoteDescriptorImpl { /** * The bind name for the remote "jairmi" server. This is also the * name that the "jairmi" client looks for when trying to locate * a "jairmi" server. */ public static final String IMAGE_SERVER_BIND_NAME = "JAIRMIRemoteServer1.1"; // A MessageFormat object to format the error strings. private MessageFormat formatter; /** * Creates a JAIRMIDescriptor. */ public JAIRMIDescriptor() throws java.net.MalformedURLException { super("jairmi", new URL("http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/remote/JAIRMIDescriptor.html")); formatter = new MessageFormat(""); formatter.setLocale(Locale.getDefault()); } /** * Returns the list of OperationDescriptors that describe * the operations supported by the server. It is the * implementing class's responsibility to extract this information from * either the server or from its own knowledge of the remote imaging * protocol. The "jairmi" protocol gets this information from the server. * *

    If the supplied serverName argument is null, then the local * host will be used instead. * * @param serverName The String identifying the server. */ public OperationDescriptor[] getServerSupportedOperationList(String serverName) throws RemoteImagingException { List odList = null; try { odList = getImageServer(serverName).getOperationDescriptors(); } catch (Exception e) { sendExceptionToListener(JaiI18N.getString("JAIRMIDescriptor12"), new RemoteImagingException(JaiI18N.getString("JAIRMIDescriptor12"), e)); // throw new RemoteImagingException(ImageUtil.getStackTraceString(e)); } OperationDescriptor[] od = new OperationDescriptor[odList.size()]; int count=0; for (Iterator i = odList.iterator(); i.hasNext(); ) { od[count++] = (OperationDescriptor)i.next(); } return od; } private ImageServer getImageServer(String serverName) { if (serverName == null) { try { serverName = InetAddress.getLocalHost().getHostAddress(); } catch(Exception e) { sendExceptionToListener(JaiI18N.getString("JAIRMIDescriptor13"), new ImagingException(JaiI18N.getString("JAIRMIDescriptor13"), e)); // throw new RuntimeException(e.getMessage()); } } // Derive the service name. String serviceName = new String("rmi://"+serverName+"/"+ IMAGE_SERVER_BIND_NAME); ImageServer imageServer = null; // Look up the remote object. try { imageServer = (ImageServer)Naming.lookup(serviceName); } catch (Exception e) { sendExceptionToListener(JaiI18N.getString("JAIRMIDescriptor14"), new RemoteImagingException(JaiI18N.getString("JAIRMIDescriptor14"), e)); // throw new RemoteImagingException(ImageUtil.getStackTraceString(e)); } return imageServer; } /** * Returns the set of capabilites supported by the server. It is the * implementing class's responsibility to extract this information from * either the server or from its own knowledge of the remote imaging * protocol. The "jairmi" protocol gets this information from the server. * *

    If the supplied serverName argument is null, then the local * host will be used instead. * * @param serverName The String identifying the server. */ public NegotiableCapabilitySet getServerCapabilities(String serverName) throws RemoteImagingException { NegotiableCapabilitySet serverCapabilities = null; try { serverCapabilities = getImageServer(serverName).getServerCapabilities(); } catch (Exception e) { sendExceptionToListener(JaiI18N.getString("JAIRMIDescriptor15"), new RemoteImagingException(JaiI18N.getString("JAIRMIDescriptor15"), e)); // throw new RemoteImagingException(ImageUtil.getStackTraceString(e)); } return serverCapabilities; } /** * Calculates the region over which two distinct remote renderings * of an operation may be expected to differ. The operation is * represented by the OperationNode argument to this * method. The String that identifies the operation * can be retrieved via the OperationNode's * getOperationName() method. * *

    The class of the returned object will vary as a function of * the nature of the operation. For rendered and renderable two- * dimensional images this should be an instance of a class which * implements java.awt.Shape. * * @param registryModeName The name of the mode. * @param oldServerName The previous server name. * @param oldParamBlock The previous sources and parameters. * @param oldHints The previous hints. * @param newServerName The current server name. * @param newParamBlock The current sources and parameters. * @param newHints The current hints. * @param node The affected node in the processing chain. * * @return The region over which the data of two renderings of this * operation may be expected to be invalid or null * if there is no common region of validity. If an empty * java.awt.Shape is returned, this indicates * that all pixels within the bounds of the old rendering * remain valid. * * @throws IllegalArgumentException if registryModeName * is null or if the operation requires either * sources or parameters and either oldParamBlock * or newParamBlock is null. * @throws IllegalArgumentException if there is no OperationDescriptor * for the specified operationName on any one or both of the * servers identified by oldServerName and * newServerName, or if the number of sources or * the name, number and Class of the operation's * parameters is not the same on both the servers. * @throws IllegalArgumentException if oldParamBlock or * newParamBlock do not contain sufficient sources * or parameters for the operation in question. */ public Object getInvalidRegion(String registryModeName, String oldServerName, ParameterBlock oldParamBlock, RenderingHints oldHints, String newServerName, ParameterBlock newParamBlock, RenderingHints newHints, OperationNode node) throws RemoteImagingException { if (registryModeName == null) throw new IllegalArgumentException( JaiI18N.getString("JAIRMIDescriptor11")); String operationName = node.getOperationName(); OperationDescriptor oldDescs[] = getServerSupportedOperationList(oldServerName); OperationDescriptor oldOD = getOperationDescriptor(oldDescs, operationName); if (oldOD == null) throw new IllegalArgumentException( JaiI18N.getString("JAIRMIDescriptor1")); int numSources = oldOD.getNumSources(); // If the supplied registryModeName is "remoteRendered" or // "remoteRenderable", in order to get the OperationDescriptor's // ParameterListDescriptor, we need to actually use the "rendered" // or "renderable" mode respectively. ParameterListDescriptor oldPLD = null; if (registryModeName.equalsIgnoreCase("remoteRendered")) { oldPLD = oldOD.getParameterListDescriptor("rendered"); } else if (registryModeName.equalsIgnoreCase("remoteRenderable")) { oldPLD = oldOD.getParameterListDescriptor("renderable"); } else { oldPLD = oldOD.getParameterListDescriptor(registryModeName); } int numParams = oldPLD.getNumParameters(); // If the serverNames are same, nothing to be done for that if (oldServerName != newServerName) { // Check whether they both support the supplied operation name OperationDescriptor newDescs[] = getServerSupportedOperationList(newServerName); OperationDescriptor newOD; if ((newOD = getOperationDescriptor(newDescs, operationName)) == null) throw new IllegalArgumentException( JaiI18N.getString("JAIRMIDescriptor2")); // Check the OperationDescriptor equivalence // Sources if (numSources != newOD.getNumSources()) throw new IllegalArgumentException( JaiI18N.getString("JAIRMIDescriptor3")); // Parameters ParameterListDescriptor newPLD = newOD.getParameterListDescriptor(registryModeName); if (numParams != newPLD.getNumParameters()) throw new IllegalArgumentException( JaiI18N.getString("JAIRMIDescriptor4")); // Param names String oldParamNames[] = oldPLD.getParamNames(); if (oldParamNames == null) oldParamNames = new String[0]; String newParamNames[] = newPLD.getParamNames(); if (newParamNames == null) newParamNames = new String[0]; Hashtable oldHash = hashNames(oldParamNames); Hashtable newHash = hashNames(newParamNames); // The same names should be present in both in the same order. if (containsAll(oldHash, newHash) == false) throw new IllegalArgumentException( JaiI18N.getString("JAIRMIDescriptor8")); // Param class types Class thisParamClasses[] = oldPLD.getParamClasses(); Class otherParamClasses[] = newPLD.getParamClasses(); for (int i=0; i 0 || numParams > 0) && (oldParamBlock == null || newParamBlock == null))) { throw new IllegalArgumentException( JaiI18N.getString("JAIRMIDescriptor5")); } // Both the old and new ParameterBlock should contain the // required number of sources. if ((numSources > 0) && (oldParamBlock.getNumSources() != numSources || newParamBlock.getNumSources() != numSources)) { Object[] msgArg0 = { operationName, new Integer(numParams) }; formatter.applyPattern(JaiI18N.getString("JAIRMIDescriptor6")); throw new IllegalArgumentException(formatter.format(msgArg0)); } // Both the old and new ParameterBlock should contain the // required number of parameters. if ((numParams > 0) && (oldParamBlock.getNumParameters() != numParams || newParamBlock.getNumParameters() != numParams)) { Object[] msgArg0 = { operationName, new Integer(numParams) }; formatter.applyPattern(JaiI18N.getString("JAIRMIDescriptor7")); throw new IllegalArgumentException(formatter.format(msgArg0)); } // Find the id that refers to the corresponding RenderedOp on the // server RenderedOp op = (RenderedOp)node; Object rendering = op.getRendering(); Long id = null; if (rendering instanceof RMIServerProxy) { id = ((RMIServerProxy)rendering).getRMIID(); } else { throw new RuntimeException( JaiI18N.getString("JAIRMIDescriptor10")); } // Check whether any of the sources of this operation are on // remote "jairmi" servers and if so, replace the source with // it's id. boolean samePBs = false; if (oldParamBlock == newParamBlock) samePBs = true; Vector oldSources = oldParamBlock.getSources(); oldParamBlock.removeSources(); // Ensure that any images which are parameters are replaced byte // suitable representations JAIRMIUtil.checkClientParameters(oldParamBlock, oldServerName); oldParamBlock.setSources( JAIRMIUtil.replaceSourcesWithId(oldSources, oldServerName)); if (samePBs) { newParamBlock = oldParamBlock; } else { Vector newSources = newParamBlock.getSources(); newParamBlock.removeSources(); // Ensure that any images which are parameters are replaced byte // suitable representations JAIRMIUtil.checkClientParameters(newParamBlock, oldServerName); newParamBlock.setSources( JAIRMIUtil.replaceSourcesWithId(newSources, oldServerName)); } // Serialize the old and new RenderingHints SerializableState oldRHS = SerializerFactory.getState(oldHints, null); SerializableState newRHS = SerializerFactory.getState(newHints, null); SerializableState shapeState = null; try { shapeState = getImageServer(oldServerName).getInvalidRegion(id, oldParamBlock, oldRHS, newParamBlock, newRHS); } catch (Exception e) { sendExceptionToListener(JaiI18N.getString("JAIRMIDescriptor16"), new RemoteImagingException(JaiI18N.getString("JAIRMIDescriptor16"), e)); // throw new RemoteImagingException(ImageUtil.getStackTraceString(e)); } return shapeState.getObject(); } private Hashtable hashNames(String paramNames[]) { Hashtable h = new Hashtable(); if (paramNames != null) { for (int i=0; iPlanarImage which represents an image on a * remote server machine. This class is also an implementation of the * RemoteRenderedImage interface. This class allows processing * to occur on the remote server machine. * *

    Conceptually this class is like a No-op, all it provides is a mechanism * allowing the processing to occur on a server. Note that this class does * not mandate that the client-server communication rely on any particular * wire protocol or communication protocol. A subclass can choose any wire * or communication protocol to communicate with its server. This is * accomplished by having the subclass implement the methods declared to * be abstract in this class. All functionality in this class is then * implemented in terms of these abstract methods. * *

    Network errors (detected via throws of * RemoteImagingException) are dealt with through the use of * retry intervals and retries. Retries refers to the maximum number of * times a remote operation will be retried. The retry interval refers to * the amount of time in milliseconds between two consecutive retries. If * errors are encountered at each retry and the number of specified retries * has been exhausted, a RemoteImagingException will be thrown. * Time outs (When the amount of time taken to get a response or * the result of an operation from the remote machine exceeds a limit) are * not dealt with, and must be taken care of by the network * imaging protocol implementation. The implementation must be responsible * for monitoring time outs, but on encountering one can deal with it by * throwing a RemoteImagingException, which will then be dealt * with using retries and retry intervals. * *

    The resultant image layout is computed and provided by the concrete * subclass by implementing the abstract method getImageLayout. * All the accessor methods dealing with the layout variables namely * getMinX(), getMinY(), getWidth(), * getHeight(), getMaxX(), getMaxY(), * getTileWidth(), getTileHeight(), * getTileGridXOffset(), getTileGridYOffset(), * getColorModel() and getSampleModel() are * implemented in terms of the getImageLayout() method. The * implementation of these methods uses retries and retry intervals to * deal with Network errors, such that the subclass implementing * getImageLayout() does not need to worry about Network errors * except to signal them by throwing a RemoteImagingException. * The same applies to the other abstract methods implemented by sub-classes * namely getRemoteProperty(), * getRemotePropertyNames() and computeTile(). * *

    The getTile method (abstract in this class' superclass), * is implemented in terms of the computeTile method. It provides * the additional functionality of caching the tiles on the client, as well * as that of dealing with Network errors as mentioned above. * * @see RemoteImagingException * * @since JAI 1.1 */ public abstract class PlanarImageServerProxy extends PlanarImage implements RemoteRenderedImage { /** Time in milliseconds between retries. */ protected int retryInterval; /** The number of retries. */ protected int numRetries; /** A reference to a centralized TileCache object. */ protected transient TileCache cache; /** * Metric used to produce an ordered list of tiles. This determines * which tiles are removed from the cache first if a memory control * operation is required. */ protected Object tileCacheMetric; /** A reference to the OperationRegistry object. */ protected transient OperationRegistry registry; /** The String representing the remote server machine. */ protected String serverName; /** The name of the protocol to be used for remote communication. */ protected String protocolName; /** The name of the operation to be performed remotely. */ protected String operationName; /** The sources and/or arguments to the operation. */ protected ParameterBlock paramBlock; /** The RenderingHints for the operation. */ protected RenderingHints hints; // The layout of the image private ImageLayout layout = null; /** The preferences to be utilized in the negotiation. */ protected NegotiableCapabilitySet preferences; /** * The set of properties agreed upon after the negotiation process * between the client and the server has been completed. */ protected NegotiableCapabilitySet negotiated; /** The server capabilities. */ NegotiableCapabilitySet serverCapabilities; /** The client capabilities. */ NegotiableCapabilitySet clientCapabilities; /** * Checks that all the layout parameters have been specified. * * @throws IllegalArgumentException if layout is null. * @throws Error if all the layout fields are not initialized. */ private static void checkLayout(ImageLayout layout) { if (layout == null) { throw new IllegalArgumentException("layout is null."); } if (layout.getValidMask() != 0x3ff) { throw new Error(JaiI18N.getString("PlanarImageServerProxy3")); } } /** * Constructs a PlanarImageServerProxy using the specified * name of the server to perform the specified operation on, using the * sources and parameters specified by the supplied * ParameterBlock and supplied RenderingHints. * If hints relating to the OperationRegistry, * TileCache, retry interval, number of retries, * tile caching metric or negotiation preferences are included in the * specified RenderingHints object, they will be honored. * *

    An IllegalArgumentException may * be thrown by the protocol specific classes at a later point, if * null is provided as the serverName argument and null is not * considered a valid server name by the specified protocol. * * @param serverName The String identifying the remote * server machine. * @param protocolName The name of the remote imaging protocol. * @param operationName The name of the operation. * @param paramBlock The source(s) and/or parameter(s) for the operation. * @param hints The hints for the operation. * * @throws IllegalArgumentException if operationName is null. */ public PlanarImageServerProxy(String serverName, String protocolName, String operationName, ParameterBlock paramBlock, RenderingHints hints) { // To initialize property and event management stuff, as done by // superclass PlanarImage constructor. super (null, null, null); if (operationName == null) { throw new IllegalArgumentException( JaiI18N.getString("PlanarImageServerProxy1")); } this.serverName = serverName; this.protocolName = protocolName; this.operationName = operationName; this.paramBlock = paramBlock; this.hints = hints; if (hints == null) { // If there are no hints specified, use default values registry = JAI.getDefaultInstance().getOperationRegistry(); cache = JAI.getDefaultInstance().getTileCache(); retryInterval = RemoteJAI.DEFAULT_RETRY_INTERVAL; numRetries = RemoteJAI.DEFAULT_NUM_RETRIES; // Do negotiation even though there are no preferences, so that // negotiation takes place between the client and the server. setNegotiationPreferences(null); } else { registry = (OperationRegistry)hints.get(JAI.KEY_OPERATION_REGISTRY); if (registry == null) { registry = JAI.getDefaultInstance().getOperationRegistry(); } cache = (TileCache)hints.get(JAI.KEY_TILE_CACHE); if (cache == null) { cache = JAI.getDefaultInstance().getTileCache(); } Integer integer = (Integer)hints.get(JAI.KEY_RETRY_INTERVAL); if (integer == null) { retryInterval = RemoteJAI.DEFAULT_RETRY_INTERVAL; } else { retryInterval = integer.intValue(); } integer = (Integer)hints.get(JAI.KEY_NUM_RETRIES); if (integer == null) { numRetries = RemoteJAI.DEFAULT_NUM_RETRIES; } else { numRetries = integer.intValue(); } tileCacheMetric = (Object)hints.get(JAI.KEY_TILE_CACHE_METRIC); // Cause negotiation to take place. setNegotiationPreferences((NegotiableCapabilitySet) hints.get(JAI.KEY_NEGOTIATION_PREFERENCES)); } if (paramBlock != null) { setSources(paramBlock.getSources()); } } /** * Returns the String that identifies the server. */ public String getServerName() { return serverName; } /** * Returns the String that identifies the remote imaging * protocol. */ public String getProtocolName() { return protocolName; } /** * Returns the operation name as a String. */ public String getOperationName() { return operationName; } /** * Returns the ParameterBlock that specifies the * sources and parameters for the operation to be performed by * this PlanarImageServerProxy. */ public ParameterBlock getParameterBlock() { return paramBlock; } /** * Returns the RenderingHints associated with the * operation to be performed by this PlanarImageServerProxy. */ public RenderingHints getRenderingHints() { return hints; } /** * Returns the tile cache object of this image by reference. * If this image does not have a tile cache, this method returns * null. */ public TileCache getTileCache() { return cache; } /** * Sets the tile cache object of this image. A null * input indicates that this image should have no tile cache and * subsequently computed tiles will not be cached. * *

    The existing cache object is informed to release all the * currently cached tiles of this image. * * @param cache A cache object to be used for caching this image's * tiles, or null if no tile caching is desired. */ public void setTileCache(TileCache cache) { if (this.cache != null) { this.cache.removeTiles(this); } this.cache = cache; } /** * Returns the tileCacheMetric instance variable by reference. */ public Object getTileCacheMetric() { return tileCacheMetric; } /** * Get the layout of the image. This could be implemented by either * asking the server to specify the layout, or have the client compute * the image layout. The ImageLayout object returned must * have all its fields initialized, else an Error will be * thrown. Network errors encountered should be signalled * by throwing a RemoteImagingException. * * @throws RemoteImagingException if an error condition during remote * image processing occurs * @throws Error if all the fields in the ImageLayout are not initialized. */ public abstract ImageLayout getImageLayout() throws RemoteImagingException; /** * Returns a property from the property set generated on the remote * server machine. Network errors encountered should be signalled * by throwing a RemoteImagingException. If the property * name is not recognized, java.awt.Image.UndefinedProperty will be * returned. * * @throws RemoteImagingException if an error condition during remote * image processing occurs * @throws IllegalArgumentException if name is null. */ public abstract Object getRemoteProperty(String name) throws RemoteImagingException; /** * Returns a list of names recognized by the getRemoteProperty * method. Network errors encountered should be signalled by * throwing a RemoteImagingException. * * @throws RemoteImagingException if an error condition during remote * image processing occurs */ public abstract String[] getRemotePropertyNames() throws RemoteImagingException; /** * Returns a conservative estimate of the destination region that * can potentially be affected by the pixels of a rectangle of a * given source. This can be implemented by either asking the server * to compute the destination region, or by having the client compute * the destination region. Network errors encountered should be * signalled by throwing a RemoteImagingException. * * @param sourceRect The Rectangle in source coordinates. * @param sourceIndex The index of the source image. * * @return A Rectangle indicating the potentially * affected destination region, or null if * the region is unknown. * * @throws IllegalArgumentException If the source index is * negative or greater than that of the last source. * @throws IllegalArgumentException If sourceRect is * null. */ public abstract Rectangle mapSourceRect(Rectangle sourceRect, int sourceIndex) throws RemoteImagingException; /** * Returns a conservative estimate of the region of a specified * source that is required in order to compute the pixels of a * given destination rectangle. Either the server or the client can * compute the source region to implement this method. Network errors * encountered should be signalled by throwing a * RemoteImagingException. * * @param destRect The Rectangle in destination coordinates. * @param sourceIndex The index of the source image. * * @return A Rectangle indicating the required source region. * * @throws IllegalArgumentException If the source index is * negative or greater than that of the last source. * @throws IllegalArgumentException If destRect is * null. */ public abstract Rectangle mapDestRect(Rectangle destRect, int sourceIndex) throws RemoteImagingException; /** * Returns tile (tileX, tileY) as computed on the remote server machine. * Note that tileX and tileY are indices into the tile array, not pixel * locations. The Raster that is returned is a copy. * Network errors encountered should be signalled by throwing a * RemoteImagingException. * *

    Subclasses must implement this method to return a * non-null value for all tile indices between * getMinTile{X,Y} and getMaxTile{X,Y}, * inclusive. Tile indices outside of this region should result * in a return value of null. * * @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. * @throws RemoteImagingException if an error condition during remote * image processing occurs */ public abstract Raster computeTile(int tileX, int tileY) throws RemoteImagingException; /** * Returns the amount of time between retries in milliseconds. */ public int getRetryInterval() { return retryInterval; } /** * Sets the amount of time between retries in milliseconds. * * @param retryInterval The amount of time (in milliseconds) to wait * between retries. * @throws IllegalArgumentException if retryInterval is negative. */ public void setRetryInterval(int retryInterval) { if (retryInterval < 0) { throw new IllegalArgumentException(JaiI18N.getString("Generic3")); } this.retryInterval = retryInterval; } /** * Returns the number of retries. */ public int getNumRetries() { return numRetries; } /** * Sets the number of retries. * * @param numRetries The number of times an operation should be retried * in case of a network error. * @throws IllegalArgumentException if numRetries is negative. */ public void setNumRetries(int numRetries) { if (numRetries < 0) { throw new IllegalArgumentException(JaiI18N.getString("Generic4")); } this.numRetries = numRetries; } /** * Overrides the method in PlanarImage to return the X * coordinate of the leftmost column of the remote image. */ public int getMinX() { requestLayout(); return minX; } /** * Overrides the method in PlanarImage to return the X * coordinate of the column immediately to the right of the rightmost * column of the remote image. */ public int getMaxX() { requestLayout(); return minX + width; } /** * Overrides the method in PlanarImage to return the Y * coordinate of the uppermost row of the remote image. */ public int getMinY() { requestLayout(); return minY; } /** * Overrides the method in PlanarImage to return the Y * coordinate of the row immediately below the bottom row of the * remote image. */ public int getMaxY() { requestLayout(); return minY + height; } /** * Overrides the method in PlanarImage to return the width * of the remote image. */ public int getWidth() { requestLayout(); return width; } /** * Overrides the method in PlanarImage to return the height * of the remote image. */ public int getHeight() { requestLayout(); return height; } /** * Overrides the method in PlanarImage to return the width * of a tile remotely. */ public int getTileWidth() { requestLayout(); return tileWidth; } /** * Overrides the method in PlanarImage to return the height * of a tile remotely. */ public int getTileHeight() { requestLayout(); return tileHeight; } /** * Overrides the method in PlanarImage to return the X * coordinate of the upper-left pixel of tile (0, 0) remotely. */ public int getTileGridXOffset() { requestLayout(); return tileGridXOffset; } /** * Overrides the method in PlanarImage to return the Y * coordinate of the upper-left pixel of tile (0, 0) remotely. */ public int getTileGridYOffset() { requestLayout(); return tileGridYOffset; } /** * Overrides the method in PlanarImage to return the * SampleModel of the remote image. */ public SampleModel getSampleModel() { requestLayout(); return sampleModel; } /** * Overrides the method in PlanarImage to return the * ColorModel of the remote image. */ public ColorModel getColorModel() { requestLayout(); return colorModel; } /** * Cause the subclass' getImageLayout method to be called, * caching the ImageLayout object locally. * * @throws IllegalArgumentException if getImageLayout returns null. * @throws Error if all the fields of the layout are not initialized. * @throws RemoteImagingException if the limit of retries is exceeded. */ private ImageLayout requestLayout() { if (layout != null) return layout; Exception rieSave = null; int count = 0; while (count++ < numRetries) { try { layout = getImageLayout(); // Check that all the fields are initialized checkLayout(layout); // Set all the super class variables minX = layout.getMinX(null); minY = layout.getMinY(null); width = layout.getWidth(null); height = layout.getHeight(null); tileWidth = layout.getTileWidth(null); tileHeight = layout.getTileHeight(null); tileGridXOffset = layout.getTileGridXOffset(null); tileGridYOffset = layout.getTileGridYOffset(null); sampleModel = layout.getSampleModel(null); colorModel = layout.getColorModel(null); break; } catch (RemoteImagingException e) { System.err.println( JaiI18N.getString("PlanarImageServerProxy0")); rieSave = e; // Sleep for retryInterval milliseconds before retrying. try { Thread.sleep(retryInterval); } catch (InterruptedException f) { } } } if (layout == null) { sendExceptionToListener(rieSave); } return layout; } /** * Gets a property from the property set of this image. If the * property name is not recognized, * java.awt.Image.UndefinedProperty will be returned. * The property to be returned is first looked for in the set of * locally cached properties. If not found, the * getRemoteProperty method is called to retrieve the * property. Network errors that might be encountered during the * getRemoteProperty call are dealt with by retries and * retry intervals. * * @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. * * @throws RemoteImagingException if the limit of retries is exceeded. */ public Object getProperty(String name) { // Try to get property locally. Object property = super.getProperty(name); if (property == null || property == Image.UndefinedProperty) { Exception rieSave = null; int count=0; while(count++ < numRetries) { try { property = getRemoteProperty(name); if(property != Image.UndefinedProperty) { setProperty(name, property); // Cache property locally } return property; } catch (RemoteImagingException rie) { System.err.println( JaiI18N.getString("PlanarImageServerProxy0")); rieSave = rie; try { Thread.sleep(retryInterval); } catch (InterruptedException ie) { } } } sendExceptionToListener(rieSave); return property; } else { return property; } } /** * Returns a list of property names that are recognized by this image * or null if none are recognized. The list of recognized * property names consists of the locally cached property names * (retrieved via super.getPropertyNames) as well as * those that might be generated by the operations performed on the * remote server machine (retrieved via * getRemotePropertyNames). Network errors that might be * encountered during the getRemotePropertyNames method * are dealt with by retries and retry intervals. * * @return an array of Strings containing valid * property names. * * @throws RemoteImagingException if the limit of retries is exceeded. */ public String[] getPropertyNames() { // Retrieve local property names. String[] localPropertyNames = super.getPropertyNames(); Vector names = new Vector(); // Put local names in a Vector, if any if (localPropertyNames != null) { for(int i = 0; i < localPropertyNames.length; i++) { names.add(localPropertyNames[i]); } } int count=0; String[] remotePropertyNames = null; Exception rieSave = null; while(count++ < numRetries) { try { remotePropertyNames = getRemotePropertyNames(); break; } catch (RemoteImagingException rie) { System.err.println( JaiI18N.getString("PlanarImageServerProxy0")); rieSave = rie; try { Thread.sleep(retryInterval); } catch (InterruptedException ie) { } } } if (count > numRetries) { sendExceptionToListener(rieSave); } // Put the remote names, if any, in the Vector. if(remotePropertyNames != null) { for(int i = 0; i < remotePropertyNames.length; i++) { if(!names.contains(remotePropertyNames[i])) { names.add(remotePropertyNames[i]); } } } // Set the return value from the vector. String propertyNames[] = names.size() == 0 ? null : (String[])names.toArray(new String[names.size()]); return propertyNames; } /** * Returns the tile (tileX, tileY). Note the tileX and tileY are indices * into the tile array and not pixel positions. This method is * implemented in terms of the computeTile method. This * method deals with Network errors (recognized as * RemoteImagingExceptions) through retries and retry * intervals. This method also performs caching of tiles, so that * an already computed tile does not need to be re-computed. * * @param tileX the X index of the tile. * @param tileY the Y index of the tile. * @throws RemoteImagingException if limit of retries is exceeded. */ public Raster getTile(int tileX, int tileY) { Raster tile = null; // Make sure the requested tile is inside this image's boundary. if (tileX >= getMinTileX() && tileX <= getMaxTileX() && tileY >= getMinTileY() && tileY <= getMaxTileY()) { // Check if tile is available in the cache. tile = cache != null ? cache.getTile(this, tileX, tileY) : null; if (tile == null) { // tile not in cache // Ask the subclass for the tile int count = 0; Exception rieSave = null; while (count++ < numRetries) { try { tile = computeTile(tileX, tileY); break; } catch (RemoteImagingException rie) { System.err.println( JaiI18N.getString("PlanarImageServerProxy0")); rieSave = rie; try { Thread.sleep(retryInterval); } catch (InterruptedException ie) { } } } if (count > numRetries) { sendExceptionToListener(rieSave); } // Cache the result tile. if (cache != null) { cache.add(this, tileX, tileY, tile, tileCacheMetric); } } } return tile; } /** * Uncaches all the tiles when this image is garbage collected. */ protected void finalize() throws Throwable { if (cache != null) { cache.removeTiles(this); } super.finalize(); } // // NEGOTIATION RELATED METHODS // /** * Returns the current negotiation preferences or null, if none were * set previously. */ public NegotiableCapabilitySet getNegotiationPreferences() { return preferences; } /** * Sets the preferences to be used in the client-server * communication. These preferences are utilized in the negotiation * process. Note that preferences for more than one category can be * specified using this method. Also each preference can be a list * of values in decreasing order of preference, each value specified * as a NegotiableCapability. The * NegotiableCapability first (for a particular category) * in this list is given highest priority in the negotiation process * (for that category). * *

    It may be noted that this method allows for multiple negotiation * cycles. Everytime this method is called, new preferences are * specified for the negotiation, which takes place anew to produce * a new set of negotiated resultant values to be used in the * remote communication. If the subclass wants to ignore the * negotiation preferences newly set, this method can be overridden to * do so. * * @param preferences The preferences to be used in the negotiation * process. */ public void setNegotiationPreferences(NegotiableCapabilitySet preferences) { this.preferences = preferences; // Every time new preferences are set, invalidate old Negotiation // results and do the negotiation again. negotiated = null; getNegotiatedValues(); } /** * Returns the results of the negotiation process. This will return null * if no negotiation preferences were set, and no negotiation was * performed, or if the negotiation failed. */ public synchronized NegotiableCapabilitySet getNegotiatedValues() throws RemoteImagingException { // If negotiation was not performed before, or if new preferences // have invalidated the old negotiated results. if (negotiated == null) { // Initialize the clientCapabilities and serverCapabilities // variables getCapabilities(); // Do the negotiation negotiated = RemoteJAI.negotiate(preferences, serverCapabilities, clientCapabilities); // Inform the server of the negotiated values. setServerNegotiatedValues(negotiated); } return negotiated; } /** * Returns the results of the negotiation process for the given category. * This will return null if no negotiation preferences were set, and * no negotiation was performed, or if the negotiation failed. * * @param category The category to return the negotiated results for. * @throws IllegalArgumentException if category is null. */ public NegotiableCapability getNegotiatedValue(String category) throws RemoteImagingException { // We do not need to check for category being null, since that // check will be made by the methods called from within this method. // If negotiation was not performed before, or if new preferences // have invalidated the old negotiated results. if (negotiated == null) { getCapabilities(); // Do the negotiation return RemoteJAI.negotiate(preferences, serverCapabilities, clientCapabilities, category); } else { // If negotiated is not null, then the negotiated results are // current and the result for the given category can just be // extracted from there and returned. return negotiated.getNegotiatedValue(category); } } // Get the client and server capabilities private void getCapabilities() { String mode = "remoteRendered"; if (serverCapabilities == null) { RemoteDescriptor desc = (RemoteDescriptor)registry.getDescriptor(mode, protocolName); int count = 0; Exception rieSave = null; while (count++ < numRetries) { try { serverCapabilities = desc.getServerCapabilities(serverName); break; } catch (RemoteImagingException rie) { System.err.println( JaiI18N.getString("PlanarImageServerProxy0")); rieSave = rie; try { Thread.sleep(retryInterval); } catch (InterruptedException ie) { } } } if (count > numRetries) { sendExceptionToListener(rieSave); } } if (clientCapabilities == null) { RemoteRIF rrif = (RemoteRIF)registry.getFactory(mode, protocolName); clientCapabilities = rrif.getClientCapabilities(); } } void sendExceptionToListener(Exception e) { ImagingListener listener = null; if (hints != null) listener = (ImagingListener)hints.get(JAI.KEY_IMAGING_LISTENER); else listener = JAI.getDefaultInstance().getImagingListener(); String message = JaiI18N.getString("PlanarImageServerProxy2"); listener.errorOccurred(message, new RemoteImagingException(message, e), this, false); } } jai-core-1.1.4/src/share/classes/javax/media/jai/remote/NegotiableCapabilitySet.java0000644000175000017500000004302610203035544030227 0ustar mathieumathieu/* * $RCSfile: NegotiableCapabilitySet.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:51 $ * $State: Exp $ */package javax.media.jai.remote; import java.io.Serializable; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Vector; import javax.media.jai.util.CaselessStringKey; /** * This class is an aggregation of NegotiableCapability objects. * This class can be used to represent all the capabilities of a machine. * *

    This class can be classified as either a preference or a non-preference. * For an explanation of the concept of preference, refer to the class * comments on javax.media.jai.remote.NegotiableCapability. * *

    If multiple NegotiableCapability objects with the * same category and capability name are added to this class, the * NegotiableCapability added earliest has the highest * preference. * *

    All names are treated in a case-retentive and case-insensitive manner. * * @since JAI 1.1 */ public class NegotiableCapabilitySet implements Serializable { // Implementation specific data structures. Each entry into this // Hashtable is a SequentialMap object hashed by the category of the // NegotiableCapability. The SequentialMap stores NegotiableCapability // objects for the same category but different capabilityNames in // separate bins. Within each bin, the NegotiableCapability that was // added first will always be the first one to be accessed. private Hashtable categories = new Hashtable(); // Whether this NegotiableCapabilitySet is a preference or not. private boolean isPreference = false; /** * Creates a NegotiableCapabilitySet. The * isPreference argument specifies whether this * NegotiableCapabilitySet should be treated as a preference * or non-preference. If this NegotiableCapabilitySet is * specified to be a non-preference, only non-preference * NegotiableCapability's will be allowed to be added to it, * otherwise an IllegalArgumentException will be thrown * at the time of addition. Similarly only preference * NegotiableCapability objects can be added to this * NegotiableCapabilitySet if isPreference * is true. * * @param isPreference a boolean that specifies whether the component * NegotiableCapability's are preferences. */ public NegotiableCapabilitySet(boolean isPreference) { this.isPreference = isPreference; } /** * Returns true if this NegotiableCapabilitySet is an * aggregation of preference NegotiableCapability's, * false otherwise. */ public boolean isPreference() { return isPreference; } /** * Adds a new NegotiableCapability to this * NegotiableCapabilitySet. If a * NegotiableCapability with the same category and same * capability name as the one currently being added has been added * previously, the previous one will have a higher preference. * * @param capability The NegotiableCapability to be added. * @throws IllegalArgumentException if capability is null. * @throws IllegalArgumentException if isPreference() * returns false, and capability is a preference * NegotiableCapability. * @throws IllegalArgumentException if isPreference() * returns true, and capability is a non-preference * NegotiableCapability. */ public void add(NegotiableCapability capability) { if (capability == null) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCapabilitySet0")); } if (isPreference != capability.isPreference()) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCapabilitySet1")); } SequentialMap map = getCategoryMap(capability.getCategory()); map.put(capability); } /** * Removes the specified NegotiableCapability from this * NegotiableCapabilitySet. If the specified * NegotiableCapability was not added previously, an * IllegalArgumentException will be thrown. * * @param capability The NegotiableCapability to be removed. * @throws IllegalArgumentException if capability is null. * @throws IllegalArgumentException if the specified * NegotiableCapabilitySet was not added previously. */ public void remove(NegotiableCapability capability) { if (capability == null) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCapabilitySet0")); } SequentialMap map = getCategoryMap(capability.getCategory()); map.remove(capability); } /** * Returns all the NegotiableCapabilitys with the given * category and capability name added previously, as a List. * If none were added, returns an empty List. * * @param category The category of the NegotiableCapability. * @param capabilityName The name of the NegotiableCapability. * * @throws IllegalArgumentException if category is null. * @throws IllegalArgumentException if capabilityName is null. */ public List get(String category, String capabilityName) { if (category == null) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCapabilitySet3")); } if (capabilityName == null) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCapabilitySet4")); } SequentialMap map = getCategoryMap(category); return map.getNCList(capabilityName); } /** * Returns all the NegotiableCapabilitys with the given * category as a List. Returns an empty List * if no such NegotiableCapabilitys were added. * * @param category The category of the NegotiableCapabilitys * to return. * @throws IllegalArgumentException if category is null. */ public List get(String category) { if (category == null) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCapabilitySet3")); } SequentialMap map = getCategoryMap(category); Vector capNames = map.getCapabilityNames(); Vector curr, allNC = new Vector(); Object obj; for (Iterator e = capNames.iterator(); e.hasNext(); ) { // Get the next vector of NCs curr = (Vector)map.getNCList((String)e.next()); for (Iterator i=curr.iterator(); i.hasNext(); ) { // Get the elements of the Vector obj = i.next(); // If it isn't already present in the resultant Vector, add it if (!(allNC.contains(obj))) { allNC.add(obj); } } } return (List)allNC; } /** * Returns the category of all the NegotiableCapability * objects that have been added previously, as a List. * Returns an empty List, if no * NegotiableCapability objects have been added. * *

    The returned List does not contain any * duplicates. */ public List getCategories() { CaselessStringKey key; Vector v = new Vector(); for (Enumeration e = categories.keys(); e.hasMoreElements(); ) { key = (CaselessStringKey)e.nextElement(); v.add(key.toString()); } return (List)v; } /** * Returns the capability names of all the * NegotiableCapability objects that have been added * previously, as a List. Returns an empty * List if no such NegotiableCapability * objects have been added. * *

    The returned List does not contain any * duplicates. * * @param category The category of the NegotiableCapability. * @throws IllegalArgumentException if category is null. */ public List getCapabilityNames(String category) { if (category == null) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCapabilitySet3")); } SequentialMap map = getCategoryMap(category); Vector names = map.getCapabilityNames(); return (List)names; } /** * Returns the common subset supported by this * NegotiableCapabilitySet and the given * NegotiableCapabilitySet, if the negotiation succeeds. * This method returns null if negotiation fails for all categories. * *

    For those categories that are common to both the * NegotiableCapabilitySet objects, negotiation is * performed on a per category basis. Within each category, negotiation * is performed on a per capabilityName basis. The categories which exist * only in one or the other NegotiableCapabilitySet are * not negotiated upon and do not show up in the resultant * NegotiableCapabilitySet, if the negotiation is successful. * If this class contains 'm' NegotiableCapability objects * for the same category and capabilityName for which the * NegotiableCapabilitySet being negotiated with contains * 'n' NegotiableCapability objects, then the negotiation * for this category and capabilityName will require m x n number of * negotiations between two NegotiableCapability objects. * The ones that succeed will produce new NegotiableCapability * objects which will be added to the returned * NegotiableCapabilitySet. * *

    If the supplied NegotiableCapabilitySet is null, * then the negotiation will fail, and null will be returned. * * @param other The NegotiableCapabilitySet to negotiate with. */ public NegotiableCapabilitySet negotiate(NegotiableCapabilitySet other) { if (other == null) return null; NegotiableCapabilitySet negotiated = new NegotiableCapabilitySet(isPreference & other.isPreference()); // Get only the common categories Vector commonCategories = new Vector(getCategories()); commonCategories.retainAll(other.getCategories()); String capName, otherCapName; NegotiableCapability thisCap, otherCap, negCap; List thisCapabilities, otherCapabilities; for (Iterator c = commonCategories.iterator(); c.hasNext(); ) { String currCategory = (String)c.next(); thisCapabilities = get(currCategory); otherCapabilities = other.get(currCategory); for (Iterator t=thisCapabilities.iterator(); t.hasNext(); ) { thisCap = (NegotiableCapability)t.next(); for (Iterator o=otherCapabilities.iterator(); o.hasNext(); ) { otherCap = (NegotiableCapability)o.next(); negCap = thisCap.negotiate(otherCap); if (negCap != null) negotiated.add(negCap); } } } if (negotiated.isEmpty()) { return null; } return negotiated; } /** * Returns the single NegotiableCapability that is the * negotiated result for the given category from the current class. * Returns null if negotiation for this category failed. If the * negotiation is successful, then this method will return the most * prefered (the one that was added first i.e. the one that is first * in the list) NegotiableCapability from the list of * NegotiableCapability that are valid for this category. * * @param category The category to find the negotiated result for. * @throws IllegalArgumentException if category is null. */ public NegotiableCapability getNegotiatedValue(String category) { if (category == null) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCapabilitySet3")); } List thisCapabilities = get(category); if (thisCapabilities.isEmpty()) return null; else return (NegotiableCapability)(thisCapabilities.get(0)); } /** * Performs negotiation with the given NegotiableCapabilitySet * for the specified category and returns the single * NegotiableCapability that is the negotiated result for * the given category, if the negotiation succeeds, null * otherwise. * *

    Negotiation is only performed for the specified category. For * the specified category, if there are 'm' * NegotiableCapability objects for which the * NegotiableCapabilitySet being negotiated with contains * 'n' NegotiableCapability objects, then the negotiation * for this category may require m x n number of negotiations at a * maximum and one negotiation at a minimum as the negotiation process * stops as soon as a negotiation is successful. The results of this * successful negotiation are then returned. If all the m x n * negotiations fail, null is returned. * *

    If the supplied NegotiableCapabilitySet is null, * then the negotiation will fail and null will be returned. * * @param other The NegotiableCapabilitySet to negotiate with. * @param category The category to negotiate for. * @throws IllegalArgumentException if category is null. */ public NegotiableCapability getNegotiatedValue(NegotiableCapabilitySet other, String category) { if (other == null) return null; if (category == null) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCapabilitySet3")); } List thisCapabilities = get(category); List otherCapabilities = other.get(category); NegotiableCapability thisCap, otherCap, negCap; for (Iterator t=thisCapabilities.iterator(); t.hasNext(); ) { thisCap = (NegotiableCapability)t.next(); for (Iterator o=otherCapabilities.iterator(); o.hasNext(); ) { otherCap = (NegotiableCapability)o.next(); negCap = thisCap.negotiate(otherCap); // The first negotiation to succeed is returned if (negCap != null) return negCap; } } return null; } /** * Returns true if no NegotiableCapability objects have been * added. */ public boolean isEmpty() { return categories.isEmpty(); } // Method to get the SequentialMap for a particular category, creating // one if necessary private SequentialMap getCategoryMap(String category) { CaselessStringKey categoryKey = new CaselessStringKey(category); SequentialMap map = (SequentialMap)categories.get(categoryKey); if (map == null) { map = new SequentialMap(); categories.put(categoryKey, map); } return map; } /** * A Class to manage storage of NegotiableCapability objects by category * and within that by capabilityName. This class also maintains the * order of NegotiableCapability in which they were added under a * particular category and capabilityName. */ class SequentialMap implements Serializable { // Vector of CaselessStringKey objects, will be the capabilityNames. Vector keys; // Vector of vectors, each vector containing all the NCs for a // particular capabilityName. Vector values; /** * Creates a new SequentialMap. */ SequentialMap() { keys = new Vector(); values = new Vector(); } /** * Add a capability to this SequentialMap. */ void put(NegotiableCapability capability) { CaselessStringKey capNameKey = new CaselessStringKey(capability.getCapabilityName()); int index = keys.indexOf(capNameKey); Vector v; if (index == -1) { keys.add(capNameKey); v = new Vector(); v.add(capability); values.add(v); } else { v = (Vector)values.elementAt(index); if (v == null) v = new Vector(); v.add(capability); } } /** * Let a List of all NegotiableCapability objects stored for the * given capabilityName. */ List getNCList(String capabilityName) { CaselessStringKey capNameKey = new CaselessStringKey(capabilityName); int index = keys.indexOf(capNameKey); Vector v; if (index == -1) { v = new Vector(); return (List)v; } else { v = (Vector)values.elementAt(index); return (List)v; } } /** * Remove a NegotiableCapability from this SequentialMap. */ void remove(NegotiableCapability capability) { CaselessStringKey capNameKey = new CaselessStringKey(capability.getCapabilityName()); int index = keys.indexOf(capNameKey); if (index == -1) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCapabilitySet2")); } Vector v = (Vector)values.elementAt(index); if (v.remove(capability) == false) { throw new IllegalArgumentException( JaiI18N.getString("NegotiableCapabilitySet2")); } // If this was the only element in the capabilityName Vector if (v.isEmpty()) { keys.remove(capNameKey); values.remove(index); } if (keys.isEmpty()) categories.remove(new CaselessStringKey( capability.getCategory())); } /** * Get capability names of all NegotiableCapabilitySets in this * SequentialMap. */ Vector getCapabilityNames() { Vector v = new Vector(); CaselessStringKey name; for (Iterator i = keys.iterator(); i.hasNext(); ) { name = (CaselessStringKey)i.next(); v.add(name.getName()); } return v; } } } jai-core-1.1.4/src/share/classes/javax/media/jai/remote/RemoteCRIF.java0000644000175000017500000001650210203035544025376 0ustar mathieumathieu/* * $RCSfile: RemoteCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:52 $ * $State: Exp $ */package javax.media.jai.remote; import java.awt.geom.Rectangle2D; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderContext; import java.awt.image.renderable.RenderableImage; import java.awt.image.renderable.ParameterBlock; /** * The RemoteCRIF interface is equivalent to the * ContextualRenderedImageFactory for operations that are * intended to be performed remotely. * * RemoteCRIF provides an interface for the functionality * that may differ between instances of RemoteRenderableOp. * Thus different remote operations on RenderableImages may be * performed by a single class such as RemoteRenderedOp through * the use of multiple instances of RemoteCRIF. * *

    All remote operations that are to be used in a rendering-independent * chain must provide a factory that implements RemoteCRIF. * *

    Classes that implement this interface must provide a * constructor with no arguments. * * @since JAI 1.1 */ public interface RemoteCRIF extends RemoteRIF { /** * 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 serverName A String specifying the name of the * server to perform the remote operation on. * @param operationName The String specifying the name of the * operation to be performed remotely. * @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. */ RenderContext mapRenderContext(String serverName, String operationName, int i, RenderContext renderContext, ParameterBlock paramBlock, RenderableImage image) throws RemoteImagingException; /** * Creates a rendering, given a RenderContext and a * ParameterBlock containing the operation's sources * and parameters. The output is a RemoteRenderedImage * 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 serverName A String specifying the name of the * server to perform the remote operation on. * @param operationName The String specifying the name of the * operation to be performed remotely. * @param renderContext The RenderContext specifying the * rendering context. * @param paramBlock A ParameterBlock containing the * operation's sources and parameters. */ RemoteRenderedImage create(String serverName, String operationName, RenderContext renderContext, ParameterBlock paramBlock) throws RemoteImagingException; /** * 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 serverName A String specifying the name of the * server to perform the remote operation on. * @param operationName The String specifying the name of the * operation to be performed remotely. * @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(String serverName, String operationName, ParameterBlock paramBlock) throws RemoteImagingException; /** * 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 serverName A String specifying the name of the * server to perform the remote operation on. * @param operationName The String specifying the name of the * operation to be performed remotely. * @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(String serverName, String operationName, ParameterBlock paramBlock, String name) throws RemoteImagingException; /** * Returns a list of names recognized by getProperty. * * @param serverName A String specifying the name of the * server to perform the remote operation on. * @param operationName The String specifying the name of the * operation to be performed remotely. */ String[] getPropertyNames(String serverName, String operationName) throws RemoteImagingException; /** * 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. * * @param serverName A String specifying the name of the * server to perform the remote operation on. * @param operationName The String specifying the name of the * operation to be performed remotely. */ boolean isDynamic(String serverName, String operationName) throws RemoteImagingException; } jai-core-1.1.4/src/share/classes/javax/media/jai/remote/SerializableRenderedImage.java0000644000175000017500000015727010203035544030531 0ustar mathieumathieu/* * $RCSfile: SerializableRenderedImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:53 $ * $State: Exp $ */ package javax.media.jai.remote; /* XXX: RFE (from Bob): If the SM can't be serialized perhaps a different SM know to be serializable could be created and the data copied. */ import java.awt.Image; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.io.InterruptedIOException; import java.io.IOException; import java.io.NotSerializableException; import java.io.OutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.net.InetAddress; import java.net.Socket; import java.net.SocketException; import java.net.ServerSocket; import java.net.UnknownHostException; import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; import javax.media.jai.JAI; import javax.media.jai.OperationRegistry; import javax.media.jai.PlanarImage; import javax.media.jai.ParameterListDescriptor; import javax.media.jai.RasterAccessor; import javax.media.jai.RasterFormatTag; import javax.media.jai.RemoteImage; import javax.media.jai.TileCache; import javax.media.jai.remote.SerializableState; import javax.media.jai.remote.SerializerFactory; import javax.media.jai.tilecodec.TileCodecDescriptor; import javax.media.jai.tilecodec.TileCodecParameterList; import javax.media.jai.tilecodec.TileDecoder; import javax.media.jai.tilecodec.TileDecoderFactory; import javax.media.jai.tilecodec.TileEncoder; import javax.media.jai.tilecodec.TileEncoderFactory; import javax.media.jai.util.CaselessStringKey; import javax.media.jai.util.ImagingException; import javax.media.jai.util.ImagingListener; import com.sun.media.jai.util.ImageUtil; /** * A serializable wrapper class for classes which implement the * RenderedImage interface. * *

    A SerializableRenderedImage provides a means to serialize * a RenderedImage. Transient fields are handled using * Serializers registered with SerializerFactory. * Two means are available for providing the wrapped * RenderedImage data to a remote version of a * SerializableRenderedImage object: either via deep copy or by * "on-demand" copying. If a deep copy is requested, the entire image * Raster is copied during object serialization and tiles are * extracted from it as needed using the Raster.createChild() * method. If a deep copy is not used, the image data are transmitted * "on-demand" using socket communications. If the request is made on the * local host, the image data are provided in both cases merely by forwarding * the request to the wrapped RenderedImage. Note that a single * SerializableRenderedImage object should be able to service * multiple remote hosts. * *

    The properties associated with the RenderedImage being * wrapped are serialized and accessible to a remote version of a * SerializableRenderedImage object. However it should be noted * that only those properties which are serializable are available to the * SerializableRenderedImage object. * *

    This class makes no guarantee as to the stability of the data of the * wrapped image, at least in the case where a deep copy is not made. * Consequently if the data of a RenderedImage change but * affected tiles have already been transmitted then the modifications will * not be visible remotely. For example, this implies that a * SerializableRenderedImage should not be used to wrap a * RenderedOp the data of which are subject to change if the * chain in which the node is present is edited. Instead the * SerializableRenderedImage should be used to wrap the image * returned by invoking either getRendering() or * createInstance() on the RenderedOp. A similar * situation will obtain if the wrapped image is a * WritableRenderedImage. If in this case the wrapped image * is also a PlanarImage, then the image returned by * createSnapshot() should be wrapped instead. * *

    An example of the usage of this class is as follows: * *

     * import java.io.IOException;
     * import java.io.ObjectInputStream;
     * import java.io.ObjectOutputStream;
     * import java.io.Serializable;
     *
     * public class SomeSerializableClass implements Serializable {
     *     protected transient RenderedImage image;
     *
     *     // Fields omitted.
     *
     *     public SomeSerializableClass(RenderedImage image) {
     *         this.image = image;
     *     }
     *
     *     // Methods omitted.
     *
     *     // Serialization method.
     *     private void writeObject(ObjectOutputStream out) throws IOException {
     *         out.defaultWriteObject();
     *         out.writeObject(new SerializableRenderedImage(image));
     *     }
     *
     *     // Deserialization method.
     *     private void readObject(ObjectInputStream in)
     *         throws IOException, ClassNotFoundException {
     *         in.defaultReadObject();
     *         image = (RenderedImage)in.readObject();
     *     }
     * }
     * 
    * * @see java.awt.image.RenderedImage * @see java.awt.image.WritableRenderedImage * @see javax.media.jai.PlanarImage * @see javax.media.jai.RenderedOp * * * @since JAI 1.1 */ // NB: This class was added in EA3 to com.sun.media.jai.rmi and made // public only in JAI 1.1. public final class SerializableRenderedImage implements RenderedImage, Serializable { /** Value to indicate the server socket timeout period (milliseconds). */ private static final int SERVER_TIMEOUT = 60000; // XXX 1 minute? /** Message indicating that a client will not connect again. */ private static final String CLOSE_MESSAGE = "CLOSE"; /** Message indicating that the server read the client's close message. */ private static final String CLOSE_ACK = "CLOSE_ACK"; /** The unique ID of this image. */ private Object UID; /** Flag indicating whether this is a data server. */ private transient boolean isServer; /** Flag indicating whether the source image is a RemoteImage. */ private boolean isSourceRemote; /** The RenderedImage source of this object (server only). */ private transient RenderedImage source; /** The X coordinate of the image's upper-left pixel. */ private int minX; /** The Y coordinate of the image's upper-left pixel. */ private int minY; /** The image's width in pixels. */ private int width; /** The image's height in pixels. */ private int height; /** The horizontal index of the leftmost column of tiles. */ private int minTileX; /** The vertical index of the uppermost row of tiles. */ private int minTileY; /** The number of tiles along the tile grid in the horizontal direction. */ private int numXTiles; /** The number of tiles along the tile grid in the vertical direction. */ private int numYTiles; /** The width of a tile. */ private int tileWidth; /** The height of a tile. */ private int tileHeight; /** The X coordinate of the upper-left pixel of tile (0, 0). */ private int tileGridXOffset; /** The Y coordinate of the upper-left pixel of tile (0, 0). */ private int tileGridYOffset; /** The image's SampleModel. */ private transient SampleModel sampleModel = null; /** The image's ColorModel. */ private transient ColorModel colorModel = null; /** The image's sources, stored in a Vector. */ private transient Vector sources = null; /** A Hashtable containing the image properties. */ private transient Hashtable properties = null; /** Flag indicating whether to use a deep copy of the source image. */ private boolean useDeepCopy; /** A Rectangle indicating the entire image bounds. */ private Rectangle imageBounds; /** The entire image Raster (client only). */ private transient Raster imageRaster; /** The Internet Protocol (IP) address of the instantiating host. */ private InetAddress host; /** The port on which the data server is listening. */ private int port; /** Flag indicating that the server is available for connections. */ private transient boolean serverOpen = false; /** The server socket for image data transfer (server only). */ private transient ServerSocket serverSocket = null; /** The thread in which the data server is running (server only). */ private transient Thread serverThread; /** The tile codec format name is TileCodec is used */ private String formatName; /** The specified OperationRegistry when TileCodec is used */ private transient OperationRegistry registry; /** * A table of counts of remote references to instances of this class * (server only). * *

    This table consists of entries with the keys being instances of * SerializableRenderedImage and the values being * Integers the int value of which represents the number * of remote SerializableRenderedImage objects which could * potentially request a socket connection with the associated key. This * table is necessary to prevent the garbage collector of the interpreter * in which the server SerializableRenderedImage object is * instantiated from finalizing the object - and thereby closing its * server socket - when that object could still receive socket connection * requests from its remote clients. The reference to the object in the * static class variable ensures that the object will not be prematurely * finalized. */ private static transient Hashtable remoteReferenceCount; /** Indicate that tilecodec is used in the transfering or not */ private boolean useTileCodec = false; /** Cache the encoder factory */ private transient TileDecoderFactory tileDecoderFactory = null; /** Cache the decoder factory */ private transient TileEncoderFactory tileEncoderFactory = null; /** Cache the encoding/decoding parameters */ private TileCodecParameterList encodingParam = null; private TileCodecParameterList decodingParam = null; /** * Increment the remote reference count of the argument. * *

    If the argument is not already in the remote reference table, * add it to the table with a count value of unity. If it exists in * table, increment its count value. * * @parameter o The object the count value of which is to be incremented. */ private static synchronized void incrementRemoteReferenceCount(Object o) { if (remoteReferenceCount == null) { remoteReferenceCount = new Hashtable(); remoteReferenceCount.put(o, new Integer(1)); } else { Integer count = (Integer)remoteReferenceCount.get(o); if (count == null) { remoteReferenceCount.put(o, new Integer(1)); } else { remoteReferenceCount.put(o, new Integer(count.intValue()+1)); } } } /** * Decrement the remote reference count of the argument. * *

    If the count value of the argument exists in the table its count * value is decremented unless the count value is unity in which case the * entry is removed from the table. * * @parameter o The object the count value of which is to be decremented. */ private static synchronized void decrementRemoteReferenceCount(Object o) { if (remoteReferenceCount != null) { Integer count = (Integer)remoteReferenceCount.get(o); if (count != null) { if (count.intValue() == 1) { remoteReferenceCount.remove(o); } else { remoteReferenceCount.put(o, new Integer(count.intValue()-1)); } } } } /** * The default constructor. */ SerializableRenderedImage() {} /** * Constructs a SerializableRenderedImage wrapper for a * RenderedImage source. Image data may be serialized * tile-by-tile or via a single deep copy. Tile encoding and * decoding may be effected via a TileEncoder and * TileDecoder specified by format name. * *

    It may be noted that if the TileCodec utilizes * Serializers for encoding the image data, and none * is available for the DataBuffer of the supplied * image, an error/exception may be encountered. * * @param source The RenderedImage source. * @param useDeepCopy Whether a deep copy of the entire image Raster * will be made during object serialization. * @param registry The OperationRegistry to use in * creating the TileEncoder. The * TileDecoder will of necessity be * created using the default OperationRegistry * as the specified OperationRegistry is not * serialized. If null the default registry * will be used. * @param formatName The name of the format used to encode the data. * If null simple tile serialization will * be performed either directly or by use of a "raw" * TileCodec. * @param encodingParam The parameters to be used for data encoding. If * null the default encoding * TileCodecParameterList for this * format will be used. Ignored if * formatName is null. * @param decodingParam The parameters to be used for data decoding. If * null a complementary * TileCodecParameterList will be * derived from encodingParam. Ignored * if formatName is null. * * @exception IllegalArgumentException if source * is null. * @exception IllegalArgumentException if no Serializers * are available for the types of * SampleModel, and ColorModel * contained in the specified image. */ public SerializableRenderedImage(RenderedImage source, boolean useDeepCopy, OperationRegistry registry, String formatName, TileCodecParameterList encodingParam, TileCodecParameterList decodingParam) throws NotSerializableException { this(source, useDeepCopy, false); // When the provided format name is null, return to directly serialize // this image if (formatName == null) return; this.formatName = formatName; // When the provided registry is null, use the default one if (registry == null) registry = JAI.getDefaultInstance().getOperationRegistry(); this.registry = registry; // Fix 4640094: When the provided encodingParam is null, use the default one if (encodingParam == null) { TileCodecDescriptor tcd = getTileCodecDescriptor("tileEncoder", formatName); encodingParam = tcd.getDefaultParameters("tileEncoder"); } else if (!formatName.equals(encodingParam.getFormatName())) { throw new IllegalArgumentException(JaiI18N.getString("UseTileCodec0")); } // Fix 4640094: When the provided decodingParam is null, use the default one if (decodingParam == null) { TileCodecDescriptor tcd = getTileCodecDescriptor("tileDecoder", formatName); decodingParam = tcd.getDefaultParameters("tileDecoder"); } else if (!formatName.equals(decodingParam.getFormatName())) { throw new IllegalArgumentException(JaiI18N.getString("UseTileCodec1")); } tileEncoderFactory = (TileEncoderFactory)registry.getFactory("tileEncoder", formatName); tileDecoderFactory = (TileDecoderFactory)registry.getFactory("tileDecoder", formatName); if (tileEncoderFactory == null || tileDecoderFactory == null) throw new RuntimeException(JaiI18N.getString("UseTileCodec2")); this.encodingParam = encodingParam; this.decodingParam = decodingParam; useTileCodec = true; } /** * Constructs a SerializableRenderedImage wrapper for a * RenderedImage source. Image data may be serialized * tile-by-tile or via a single deep copy. No TileCodec * will be used, i.e., data will be transmitted using the serialization * protocol for Rasters. * * @param source The RenderedImage source. * @param useDeepCopy Whether a deep copy of the entire image Raster * will be made during object serialization. * * @exception IllegalArgumentException if source * is null. * @exception IllegalArgumentException if no Serializers * are available for the types of DataBuffer, * SampleModel, and ColorModel * contained in the specified image. */ public SerializableRenderedImage(RenderedImage source, boolean useDeepCopy) { this(source, useDeepCopy, true); } /** * Constructs a SerializableRenderedImage wrapper for a * RenderedImage source. Image data will be serialized * tile-by-tile if possible. No TileCodec * will be used, i.e., data will be transmitted using the serialization * protocol for Rasters. * * @param source The RenderedImage source. * @exception IllegalArgumentException if source * is null. * @exception IllegalArgumentException if no Serializers * are available for the types of DataBuffer, * SampleModel, and ColorModel * contained in the specified image. */ public SerializableRenderedImage(RenderedImage source) { this(source, false, true); } /** * Constructs a SerializableRenderedImage wrapper for a * RenderedImage source. * * @param source The RenderedImage source. * @param useDeepCopy Whether a deep copy of the entire image Raster * will be made during object serialization. * @param checkDataBuffer Whether checking serializable for DataBuffer * or not. If no TileCodec will be used, set it to true. * If TileCodec will be used, it is set to false. */ private SerializableRenderedImage(RenderedImage source, boolean useDeepCopy, boolean checkDataBuffer) { UID = ImageUtil.generateID(this); if (source == null){ throw new IllegalArgumentException(JaiI18N.getString("SerializableRenderedImage0")); } SampleModel sm = source.getSampleModel(); if (sm != null && SerializerFactory.getSerializer(sm.getClass()) == null) { throw new IllegalArgumentException(JaiI18N.getString("SerializableRenderedImage2")); } ColorModel cm = source.getColorModel(); if (cm != null && SerializerFactory.getSerializer(cm.getClass()) == null) { throw new IllegalArgumentException(JaiI18N.getString("SerializableRenderedImage3")); } if (checkDataBuffer) { Raster ras = source.getTile(source.getMinTileX(), source.getMinTileY()); if (ras != null) { DataBuffer db = ras.getDataBuffer(); if (db != null && SerializerFactory.getSerializer(db.getClass()) == null) throw new IllegalArgumentException(JaiI18N.getString("SerializableRenderedImage4")); } } // Set server flag. isServer = true; // Cache the deep copy flag. this.useDeepCopy = useDeepCopy; // Cache the parameter. this.source = source; // Set remote source flag. this.isSourceRemote = source instanceof RemoteImage; // Initialize RenderedImage fields. minX = source.getMinX(); minY = source.getMinY(); width = source.getWidth(); height = source.getHeight(); minTileX = source.getMinTileX(); minTileY = source.getMinTileY(); numXTiles = source.getNumXTiles(); numYTiles = source.getNumYTiles(); tileWidth = source.getTileWidth(); tileHeight = source.getTileHeight(); tileGridXOffset = source.getTileGridXOffset(); tileGridYOffset = source.getTileGridYOffset(); sampleModel = source.getSampleModel(); colorModel = source.getColorModel(); sources = new Vector(); sources.add(source); properties = new Hashtable(); // XXX Property names should use CaselessStringKey for the // keys so that case is preserved. String[] propertyNames = source.getPropertyNames(); if (propertyNames != null) { for (int i = 0; i < propertyNames.length; i++) { properties.put(propertyNames[i], source.getProperty(propertyNames[i])); } } // Initialize the image bounds. imageBounds = new Rectangle(minX, minY, width, height); // Initialize the host field. try { host = InetAddress.getLocalHost(); } catch (UnknownHostException e) { throw new RuntimeException(e.getMessage()); } // Unset the server availability flag. serverOpen = false; } /** * Private implementation of tile server. */ private class TileServer implements Runnable { /** * Provide Rasters to clients on request. * *

    This method is called by the data server thread when a deep copy * of the source image Raster is not being used. A socket connection is * set up at a well known address to which clients may connect. After a * client connects it transmits a Rectangle object which is read by * this method. The Raster corresponding to this Rectangle is then * retrieved from the source image and transmitted back over the * socket connection. * *

    The server loop will continue until this object is garbage * collected. */ public void run() { // Loop while the server availability flag is set. while (serverOpen) { // Wait for a client connection request. Socket socket = null; try { socket = serverSocket.accept(); socket.setSoLinger(true,1); } catch (InterruptedIOException e) { // accept() timeout: restart loop to check // availability flag. continue; } catch (SocketException e) { sendExceptionToListener(JaiI18N.getString("SerializableRenderedImage5"), new ImagingException(JaiI18N.getString("SerializableRenderedImage5"), e)); // throw new RuntimeException(e.getMessage()); } catch (IOException e) { sendExceptionToListener(JaiI18N.getString("SerializableRenderedImage6"), new ImagingException(JaiI18N.getString("SerializableRenderedImage6"), e)); } // Get the socket input and output streams and wrap object // input and output streams around them, respectively. InputStream in = null; OutputStream out = null; ObjectInputStream objectIn = null; ObjectOutputStream objectOut = null; try { in = socket.getInputStream(); out = socket.getOutputStream(); objectIn = new ObjectInputStream(in); objectOut = new ObjectOutputStream(out); } catch (IOException e) { sendExceptionToListener(JaiI18N.getString("SerializableRenderedImage7"), new ImagingException(JaiI18N.getString("SerializableRenderedImage7"), e)); // throw new RuntimeException(e.getMessage()); } // Read the Object from the object stream. Object obj = null; try { obj = objectIn.readObject(); } catch (IOException e) { sendExceptionToListener(JaiI18N.getString("SerializableRenderedImage8"), new ImagingException(JaiI18N.getString("SerializableRenderedImage8"), e)); // throw new RuntimeException(e.getMessage()); } catch (ClassNotFoundException e) { sendExceptionToListener(JaiI18N.getString("SerializableRenderedImage9"), new ImagingException(JaiI18N.getString("SerializableRenderedImage9"), e)); } // Switch according to object class; ignore unsupported types. if (obj instanceof String && ((String)obj).equals(CLOSE_MESSAGE)) { try { objectOut.writeObject(CLOSE_ACK); } catch (IOException e) { sendExceptionToListener(JaiI18N.getString( "SerializableRenderedImage17"), new ImagingException(JaiI18N.getString( "SerializableRenderedImage17"), e)); // throw new RuntimeException(e.getMessage()); } // Decrement the remote reference count. decrementRemoteReferenceCount(this); } else if (obj instanceof Rectangle) { // Retrieve the Raster of data from the source image. Raster raster = source.getData((Rectangle)obj); // Write the serializable Raster to the // object output stream. if (useTileCodec) { byte[] buf = encodeRasterToByteArray(raster); try { objectOut.writeObject(buf); } catch (IOException e) { sendExceptionToListener(JaiI18N.getString("SerializableRenderedImage10"), new ImagingException(JaiI18N.getString("SerializableRenderedImage10"), e)); // throw new RuntimeException(e.getMessage()); } } else { try { objectOut.writeObject(SerializerFactory.getState(raster, null)); } catch (IOException e) { sendExceptionToListener(JaiI18N.getString("SerializableRenderedImage10"), new ImagingException(JaiI18N.getString("SerializableRenderedImage10"), e)); // throw new RuntimeException(e.getMessage()); } } } // XXX Concerning serialization of properties, perhaps the // best approach would be to serialize all the properties up // front if a deep copy were being made but otherwise to wait // until the first property request was received before // transmitting any property values. When the first request // was made, all property values would be transmitted and then // cached. Up front serialization might in both cases include // transmitting all names. If property serialization were // deferred, then a new message branch would be added here // to retrieve the properties which could be obtained as // a PropertySourceImpl. If properties are also served up // then this inner class should be renamed "DataServer". // Close the various streams and the socket itself. try { objectOut.flush(); socket.shutdownOutput(); socket.shutdownInput(); objectOut.close(); objectIn.close(); out.close(); in.close(); socket.close(); } catch (IOException e) { sendExceptionToListener(JaiI18N.getString("SerializableRenderedImage10"), new ImagingException(JaiI18N.getString("SerializableRenderedImage10"), e)); // throw new RuntimeException(e.getMessage()); } } } } // --- Begin implementation of java.awt.image.RenderedImage. --- public WritableRaster copyData(WritableRaster dest) { if (isServer || isSourceRemote) { return source.copyData(dest); } Rectangle region; if(dest == null) { region = imageBounds; SampleModel destSM = getSampleModel().createCompatibleSampleModel(region.width, region.height); dest = Raster.createWritableRaster(destSM, new Point(region.x, region.y)); } else { region = dest.getBounds().intersection(imageBounds); } if(!region.isEmpty()) { int startTileX = PlanarImage.XToTileX(region.x, tileGridXOffset, tileWidth); int startTileY = PlanarImage.YToTileY(region.y, tileGridYOffset, tileHeight); int endTileX = PlanarImage.XToTileX(region.x + region.width - 1, tileGridXOffset, tileWidth); int endTileY = PlanarImage.YToTileY(region.y + region.height - 1, tileGridYOffset, tileHeight); SampleModel[] sampleModels = { getSampleModel() }; int tagID = RasterAccessor.findCompatibleTag(sampleModels, dest.getSampleModel()); RasterFormatTag srcTag = new RasterFormatTag(getSampleModel(),tagID); RasterFormatTag dstTag = new RasterFormatTag(dest.getSampleModel(),tagID); for (int ty = startTileY; ty <= endTileY; ty++) { for (int tx = startTileX; tx <= endTileX; tx++) { Raster tile = getTile(tx, ty); Rectangle subRegion = region.intersection(tile.getBounds()); RasterAccessor s = new RasterAccessor(tile, subRegion, srcTag, getColorModel()); RasterAccessor d = new RasterAccessor(dest, subRegion, dstTag, null); ImageUtil.copyRaster(s, d); } } } return dest; } public ColorModel getColorModel() { return colorModel; } public Raster getData() { if (isServer || isSourceRemote) { return source.getData(); } return getData(imageBounds); } public Raster getData(Rectangle rect) { Raster raster = null; // Branch according to whether the object is a data server or, if not, // according to whether it is a data client and using a deep copy of // the source data or pulling the data as needed over a socket. if (isServer || isSourceRemote) { raster = source.getData(rect); } else if (useDeepCopy) { raster = imageRaster.createChild(rect.x, rect.y, rect.width, rect.height, rect.x, rect.y, null); } else { // TODO: Use a Hashtable to store Rasters as they are pulled over // the network and look them up here using "rect" as key? // Connect to the data server. Socket socket = connectToServer(); // Get the socket input and output streams and wrap object // input and output streams around them, respectively. OutputStream out = null; ObjectOutputStream objectOut = null; InputStream in = null; ObjectInputStream objectIn = null; try { out = socket.getOutputStream(); objectOut = new ObjectOutputStream(out); in = socket.getInputStream(); objectIn = new ObjectInputStream(in); } catch (IOException e) { sendExceptionToListener(JaiI18N.getString("SerializableRenderedImage7"), new ImagingException(JaiI18N.getString("SerializableRenderedImage7"), e)); // throw new RuntimeException(e.getMessage()); } // Write the Rectangle to the object output stream. try { objectOut.writeObject(rect); } catch (IOException e) { sendExceptionToListener(JaiI18N.getString("SerializableRenderedImage10"), new ImagingException(JaiI18N.getString("SerializableRenderedImage10"), e)); // throw new RuntimeException(e.getMessage()); } // Read serialized form of the Raster from object output stream. Object object = null; try { object = objectIn.readObject(); } catch (IOException e) { sendExceptionToListener(JaiI18N.getString("SerializableRenderedImage8"), new ImagingException(JaiI18N.getString("SerializableRenderedImage8"), e)); // throw new RuntimeException(e.getMessage()); } catch (ClassNotFoundException e) { sendExceptionToListener(JaiI18N.getString("SerializableRenderedImage9"), new ImagingException(JaiI18N.getString("SerializableRenderedImage9"), e)); } if (useTileCodec) { byte[] buf = (byte[])object; raster = decodeRasterFromByteArray(buf); } else { if (!(object instanceof SerializableState)) raster = null; // Reconstruct the Raster from the serialized form. SerializableState ss = (SerializableState)object; Class c = ss.getObjectClass(); if (Raster.class.isAssignableFrom(c)) { raster = (Raster)ss.getObject(); } else raster = null; } // Close the various streams and the socket. try { objectOut.flush(); socket.shutdownOutput(); socket.shutdownInput(); objectOut.close(); out.close(); objectIn.close(); in.close(); socket.close(); } catch (IOException e) { String message = JaiI18N.getString("SerializableRenderedImage11"); sendExceptionToListener(message, new ImagingException(message, e)); // throw new RuntimeException(e.getMessage()); } // If the rectangle equals the image bounds, cache the Raster, // switch to "deep copy" mode, and notify the data server. if (imageBounds.equals(rect)) { closeClient(); imageRaster = raster; useDeepCopy = true; } } return raster; } public int getHeight() { return height; } public int getMinTileX() { return minTileX; } public int getMinTileY() { return minTileY; } public int getMinX() { return minX; } public int getMinY() { return minY; } public int getNumXTiles() { return numXTiles; } public int getNumYTiles() { return numYTiles; } // XXX Should getProperty() request property values over a socket // connection also? public Object getProperty(String name) { // XXX Use CaselessStringKeys for the property name. Object property = properties.get(name); return property == null ? Image.UndefinedProperty : property; } public String[] getPropertyNames() { String[] names = null; if (!properties.isEmpty()) { names = new String[properties.size()]; Enumeration keys = properties.keys(); int index = 0; while (keys.hasMoreElements()) { // XXX If CaselessStringKey keys are used then // getName() would have to be called here to get the // prop name from the key. names[index++] = (String)keys.nextElement(); } } return names; } public SampleModel getSampleModel() { return sampleModel; } /** * If this SerializableRenderedImage has not been * serialized, this method returns a Vector containing * only the RenderedImage passed to the constructor; if * this image has been deserialized, it returns null. */ public Vector getSources() { return sources; } public Raster getTile(int tileX, int tileY) { if (isServer || isSourceRemote) { return source.getTile(tileX, tileY); } TileCache cache = JAI.getDefaultInstance().getTileCache(); if (cache != null) { Raster tile = cache.getTile(this, tileX, tileY); if (tile != null) return tile; } // Determine the active area; tile intersects with image's bounds. Rectangle imageBounds = new Rectangle(getMinX(), getMinY(), getWidth(), getHeight()); Rectangle destRect = imageBounds.intersection(new Rectangle(tileXToX(tileX), tileYToY(tileY), getTileWidth(), getTileHeight())); Raster tile = getData(destRect); if (cache != null) { cache.add(this, tileX, tileY, tile); } return tile; } /** * Returns a unique identifier (UID) for this RenderedImage. * This UID may be used when the potential redundancy of the value * returned by the hashCode() method is unacceptable. * An example of this is in generating a key for storing image tiles * in a cache. */ public Object getImageID() { return UID; } /** * Converts a horizontal tile index into the X coordinate of its * upper left pixel. No attempt is made to detect out-of-range * indices. * *

    This method is implemented in terms of the PlanarImage * static method tileXToX() applied to the values returned * by primitive layout accessors. * * @param tx the horizontal index of a tile. * @return the X coordinate of the tile's upper left pixel. */ private int tileXToX(int tx) { return PlanarImage.tileXToX(tx, getTileGridXOffset(), getTileWidth()); } /** * Converts a vertical tile index into the Y coordinate of its * upper left pixel. No attempt is made to detect out-of-range * indices. * *

    This method is implemented in terms of the * PlanarImage static method tileYToY() * applied to the values returned by primitive layout accessors. * * @param ty the vertical index of a tile. * @return the Y coordinate of the tile's upper left pixel. */ private int tileYToY(int ty) { return PlanarImage.tileYToY(ty, getTileGridYOffset(), getTileHeight()); } public int getTileGridXOffset() { return tileGridXOffset; } public int getTileGridYOffset() { return tileGridYOffset; } public int getTileHeight() { return tileHeight; } public int getTileWidth() { return tileWidth; } public int getWidth() { return width; } // --- End implementation of java.awt.image.RenderedImage. --- /** * Create a server socket and start the server in a separate thread. * *

    Note that this method should be called only the first time this * object is serialized and only if a deep copy is not being used. If * a deep copy is used there is no need to serve clients data on demand. * However if data service is being provided, there is no need to create * multiple threads for the single object as a single server thread * should be able to service multiple remote objects. */ private synchronized void openServer() throws IOException, SocketException { if (!serverOpen) { // Create a ServerSocket. serverSocket = new ServerSocket(0); // Set the ServerSocket accept() method timeout period. serverSocket.setSoTimeout(SERVER_TIMEOUT); // Initialize the port field. port = serverSocket.getLocalPort(); // Set the server availability flag. serverOpen = true; // Spawn a child thread and return the parent thread to the caller. serverThread = new Thread(new TileServer()); serverThread.setDaemon(true); serverThread.start(); // Increment the remote reference count. incrementRemoteReferenceCount(this); } } /** * Transmit a message to the data server to indicate that the client * will no longer request socket connections. */ private void closeClient() { // Connect to the data server. Socket socket = connectToServer(); // Get the socket output stream and wrap an object // output stream around it. OutputStream out = null; ObjectOutputStream objectOut = null; ObjectInputStream objectIn = null; try { out = socket.getOutputStream(); objectOut = new ObjectOutputStream(out); objectIn = new ObjectInputStream(socket.getInputStream()); } catch (IOException e) { sendExceptionToListener(JaiI18N.getString("SerializableRenderedImage7"), new ImagingException(JaiI18N.getString("SerializableRenderedImage7"), e)); // throw new RuntimeException(e.getMessage()); } // Write CLOSE_MESSAGE to the object output stream. try { objectOut.writeObject(CLOSE_MESSAGE); } catch (IOException e) { sendExceptionToListener(JaiI18N.getString("SerializableRenderedImage13"), new ImagingException(JaiI18N.getString("SerializableRenderedImage13"), e)); // throw new RuntimeException(e.getMessage()); } try { objectIn.readObject(); } catch (IOException e) { sendExceptionToListener(JaiI18N.getString( "SerializableRenderedImage8"), new ImagingException(JaiI18N.getString( "SerializableRenderedImage8"), e)); } catch (ClassNotFoundException cnfe) { sendExceptionToListener(JaiI18N.getString( "SerializableRenderedImage9"), new ImagingException(JaiI18N.getString( "SerializableRenderedImage9"), cnfe)); } // Close the streams and the socket. try { objectOut.flush(); socket.shutdownOutput(); objectOut.close(); out.close(); objectIn.close(); socket.close(); } catch (IOException e) { sendExceptionToListener(JaiI18N.getString("SerializableRenderedImage11"), new ImagingException(JaiI18N.getString( "SerializableRenderedImage11"), e)); // throw new RuntimeException(e.getMessage()); } } /** * Obtain a connection to the data server socket. This is used only if a * deep copy of the image Raster has not been made. */ private Socket connectToServer() { // Open a connection to the data server. Socket socket = null; try { socket = new Socket(host, port); socket.setSoLinger(true,1); } catch (IOException e) { sendExceptionToListener(JaiI18N.getString("SerializableRenderedImage14"), new ImagingException(JaiI18N.getString("SerializableRenderedImage14"), e)); // throw new RuntimeException(e.getMessage()); } return socket; } /** * When useTileCodec is set, encode the provided raster into * a byte array. */ private byte[] encodeRasterToByteArray(Raster raster) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); TileEncoder encoder = tileEncoderFactory.createEncoder(bos, encodingParam, raster.getSampleModel()); try { encoder.encode(raster); return bos.toByteArray(); } catch (IOException e) { sendExceptionToListener(JaiI18N.getString("SerializableRenderedImage15"), new ImagingException(JaiI18N.getString("SerializableRenderedImage15"), e)); // throw new RuntimeException(e.getMessage()); } return null; } /** * When useTileCodec is set, decode the raster from a byte array. */ private Raster decodeRasterFromByteArray(byte[] buf) { ByteArrayInputStream bis = new ByteArrayInputStream(buf); // Fix 4640094 Tilecodec doesn't work well in SerializableRenderedImage // Currently, ParameterListDescriptor is singleton to a specific // tile codec and mode. After deserialization this property is gone. // So need to copy the parameter values into the newly created object if (tileDecoderFactory == null) { // Use the default operation registry as described in the spec // of the constructor. if (registry == null) registry = JAI.getDefaultInstance().getOperationRegistry(); tileDecoderFactory = (TileDecoderFactory)registry.getFactory("tileDecoder", formatName); TileCodecParameterList temp = decodingParam; if (temp != null) { TileCodecDescriptor tcd = getTileCodecDescriptor("tileDecoder", formatName); ParameterListDescriptor pld = tcd.getParameterListDescriptor("tileDecoder"); decodingParam = new TileCodecParameterList(formatName, new String[]{"tileDecoder"}, pld); String[] names = pld.getParamNames(); if (names != null) for (int i = 0; i < names.length; i++) decodingParam.setParameter(names[i], temp.getObjectParameter(names[i])); } else decodingParam = getTileCodecDescriptor("tileDecoder", formatName). getDefaultParameters("tileDecoder"); } TileDecoder decoder = tileDecoderFactory.createDecoder(bis, decodingParam); try { return decoder.decode(); } catch (IOException e) { sendExceptionToListener(JaiI18N.getString("SerializableRenderedImage16"), new ImagingException(JaiI18N.getString("SerializableRenderedImage16"), e)); // throw new RuntimeException(e.getMessage()); } return null; } /** * If a deep copy is not being used, unset the data server availability * flag and wait for the server thread to rejoin the current thread. */ protected void finalize() throws Throwable { dispose(); // Forward to the parent class. super.finalize(); } /** * Provides a hint that an image will no longer be accessed from a * reference in user space. The results are equivalent to those * that occur when the program loses its last reference to this * image, the garbage collector discovers this, and finalize is * called. This can be used as a hint in situations where waiting * for garbage collection would be overly conservative, e.g., there * are a large number of socket connections which may be opened to * transmit tile data. * *

    SerializableRenderedImage defines this method to * behave as follows: *

      *
    • if the image is acting as a server, i.e., has never been * serialized and may be providing data to serialized * versions of itself, it makes itself unavailable to further * client requests and closes its socket;
    • *
    • if the image is acting as a client, i.e., has been serialized * and may be requesting data from a remote, pre-serialization version * of itself, it sends a message to its remote self indicating that it * will no longer be making requests.
    • *
    * *

    The results of referencing an image after a call to * dispose() are undefined. */ public void dispose() { // Rejoin the server thread if using a socket-based server. if (isServer) { if (serverOpen) { // Unset availability flag so server loop exits. serverOpen = false; // Wait for the server (child) thread to die. try { serverThread.join(2*SERVER_TIMEOUT); } catch (Exception e) { // Ignore the Exception. } // Close the server socket. try { serverSocket.close(); } catch (Exception e) { // Ignore the Exception. } } } else { // client // Transmit a message to the server to indicate the child's exit. closeClient(); } } /** * Custom serialization method. In addition to all non-transient fields, * the SampleModel, source vector, and properties table are serialized. * If a deep copy of the source image Raster is being used this is also * serialized. */ private void writeObject(ObjectOutputStream out) throws IOException { if (!useDeepCopy) { // Start the data server. try { openServer(); } catch (Exception e1) { if (e1 instanceof SocketException) { // setSoTimeout() failed. if (serverSocket != null) { // XXX Facultative try { serverSocket.close(); } catch (IOException e2) { // Ignore the exception. } } } // Since server socket creation failed, use a deep copy. serverOpen = false; // XXX Facultative useDeepCopy = true; } } // Write non-static and non-transient fields. out.defaultWriteObject(); // Write RMI properties of RemoteImage. if (isSourceRemote) { String remoteClass = source.getClass().getName(); out.writeObject(source.getProperty(remoteClass+".serverName")); out.writeObject(source.getProperty(remoteClass+".id")); } // Remove non-serializable elements from table of properties. Hashtable propertyTable = properties; boolean propertiesCloned = false; Enumeration keys = propertyTable.keys(); while(keys.hasMoreElements()) { Object key = keys.nextElement(); if (!(properties.get(key) instanceof Serializable)) { if (!propertiesCloned) { propertyTable = (Hashtable)properties.clone(); propertiesCloned = true; } propertyTable.remove(key); } } // Write the source vector and properties table. out.writeObject(SerializerFactory.getState(sampleModel, null)); out.writeObject(SerializerFactory.getState(colorModel, null)); out.writeObject(propertyTable); // Make a deep copy of the image raster. if (useDeepCopy) { if (useTileCodec) out.writeObject(encodeRasterToByteArray(source.getData())); else { out.writeObject(SerializerFactory.getState(source.getData(), null)); } } } /** * Custom deserialization method. In addition to all non-transient fields, * the SampleModel, source vector, and properties table are deserialized. * If a deep copy of the source image Raster is being used this is also * deserialized. */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { isServer = false; source = null; serverOpen = false; serverSocket = null; serverThread = null; colorModel = null; // Read non-static and non-transient fields. in.defaultReadObject(); if (isSourceRemote) { // Read RMI properties of RemoteImage. String serverName = (String)in.readObject(); Long id = (Long)in.readObject(); // Recreate remote source using the ID directly. source = new RemoteImage(serverName+"::"+id.longValue(), (RenderedImage)null); } // Read the source vector and properties table. SerializableState smState = (SerializableState)in.readObject(); sampleModel = (SampleModel)smState.getObject(); SerializableState cmState = (SerializableState)in.readObject(); colorModel = (ColorModel)cmState.getObject(); properties = (Hashtable)in.readObject(); // Read the image Raster. if (useDeepCopy) { if (useTileCodec) imageRaster = decodeRasterFromByteArray((byte[])in.readObject()); else { SerializableState rasState = (SerializableState)in.readObject(); imageRaster = (Raster)rasState.getObject(); } } } private TileCodecDescriptor getTileCodecDescriptor(String registryMode, String formatName) { if (registry == null) return (TileCodecDescriptor)JAI.getDefaultInstance(). getOperationRegistry(). getDescriptor(registryMode, formatName); return (TileCodecDescriptor)registry.getDescriptor(registryMode, formatName); } void sendExceptionToListener(String message, Exception e) { ImagingListener listener= JAI.getDefaultInstance().getImagingListener(); listener.errorOccurred(message, e, this, false); } } jai-core-1.1.4/src/share/classes/javax/media/jai/remote/RemoteImagingException.java0000644000175000017500000000450110203035544030101 0ustar mathieumathieu/* * $RCSfile: RemoteImagingException.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:52 $ * $State: Exp $ */ package javax.media.jai.remote; import javax.media.jai.util.ImagingException; /** * RemoteImagingException is an Exception thrown * to indicate that an error condition was * encountered during remote image processing. All methods which might * encounter error conditions during remote image processing should * be tagged as throwing this exception, as this is something an * application might want to catch. * *

    From JAI 1.1.2 on, this class is re-parented to * ImagingException which behaves as a chained exception. * Thus, the cause of a RemoteImagingException can be * retrieved by using the method getCause.

    * * @since JAI 1.1 */ public class RemoteImagingException extends ImagingException { /** * Constructs a RemoteImagingException with no detail * message. A detail message is a String that describes * this particular exception. */ public RemoteImagingException() { super(); } /** * Constructs a RemoteImagingException with the * specified detail message. A detail message is a String * that describes this particular exception. * * @param message the String that contains a detailed message. */ public RemoteImagingException(String message) { super(message); } /** * Constructs a RemoteImagingException with the * provided cause. * * @param cause The cause of this RemoteImagingException. * * @since JAI 1.1.2 */ public RemoteImagingException(Throwable cause) { super(cause); } /** * The constructor to accept the cause of this * RemoteImagingException and the message that * describes the situation. * * @param message The message that describes the situation. * @param cause The cause of this RemoteImagingException * * @since JAI 1.1.2 */ public RemoteImagingException(String message, Throwable cause) { super(message, cause); } } jai-core-1.1.4/src/share/classes/javax/media/jai/remote/RemoteRIF.java0000644000175000017500000001445210203035544025275 0ustar mathieumathieu/* * $RCSfile: RemoteRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:52 $ * $State: Exp $ */package javax.media.jai.remote; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.OperationDescriptor; import javax.media.jai.OperationNode; import javax.media.jai.PropertyChangeEventJAI; /** * The RemoteRIF interface is intended to be implemented by * classes that wish to act as factories to produce different renderings * remotely, for example by executing a series of remote operations on * a set of sources, depending on a specific set of parameters, properties, * and rendering hints. * *

    All factories that produce renderings for operations remotely * must implement RemoteRIF. * *

    Classes that implement this interface must provide a * constructor with no arguments. * * @since JAI 1.1 */ public interface RemoteRIF { /** * Creates a RemoteRenderedImage representing the results * of an imaging operation (or chain of operations) for a given * ParameterBlock and RenderingHints. The * RemoteRIF may also query any source images * referenced by the ParameterBlock for their dimensions, * SampleModels, properties, etc., as necessary. * *

    The create() method should return null if the * RemoteRIF (representing the server) is not capable of * producing output for the given set of source images and parameters. * For example, if a server (represented by a RemoteRIF) 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 RemoteRenderedImage 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 RemoteRenderedImage may have such * a property. * * @param serverName A String specifying the name of the * server to perform the remote operation on. * @param operationName The String specifying the name of the * operation to be performed remotely. * @param paramBlock A ParameterBlock containing sources * and parameters for the * RemoteRenderedImage to be created. * @param hints A RenderingHints object containing * hints. * @return A RemoteRenderedImage containing the desired * output. */ RemoteRenderedImage create(String serverName, String operationName, ParameterBlock paramBlock, RenderingHints hints) throws RemoteImagingException; /** * Creates a RemoteRenderedImage representing the results * of an imaging operation represented by the given * OperationNode, whose given old rendering is updated * according to the given PropertyChangeEventJAI. This * factory method should be used to create a new rendering updated * according to the changes reported by the given * PropertyChangeEventJAI. The RemoteRIF * can query the supplied OperationNode for * references to the server name, operation name, parameter block, * and rendering hints. If only a new rendering of the node is desired * in order to handle the supplied PropertyChangeEventJAI, * the rendering can be obtained by calling the default * create() method, the arguments to which can be * retrieved from the supplied OperationNode. * The RemoteRIF may also query * any source images referenced by the ParameterBlock * for their dimensions, SampleModels, properties, etc., * as necessary. The supplied OperationNode should * not be edited during the creation of the new rendering, otherwise * the OperationNode might have an inconsistent state. * *

    The create() method can return null if the * RemoteRIF (representing the server) is not capable of * producing output for the given set of source images and parameters. * For example, if a server (represented by a RemoteRIF) 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 RemoteRenderedImage 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 RemoteRenderedImage may have such * a property. * * @param oldRendering The old rendering of the imaging operation. * @param node The OperationNode that represents the * imaging operation. * @param event An event that specifies the changes made to the * imaging operation. * @return A RemoteRenderedImage containing the desired * output. */ RemoteRenderedImage create(PlanarImageServerProxy oldRendering, OperationNode node, PropertyChangeEventJAI event) throws RemoteImagingException; /** * Returns the set of capabilities supported by the client object. */ NegotiableCapabilitySet getClientCapabilities(); } jai-core-1.1.4/src/share/classes/javax/media/jai/remote/JaiI18N.java0000644000175000017500000000074310203035544024602 0ustar mathieumathieu/* * $RCSfile: JaiI18N.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:50 $ * $State: Exp $ */ package javax.media.jai.remote; import com.sun.media.jai.util.PropertyUtil; class JaiI18N { static String packageName = "javax.media.jai.remote"; public static String getString(String key) { return PropertyUtil.getString(packageName, key); } } jai-core-1.1.4/src/share/classes/javax/media/jai/remote/Serializer.java0000644000175000017500000001071310203035544025606 0ustar mathieumathieu/* * $RCSfile: Serializer.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:53 $ * $State: Exp $ */ package javax.media.jai.remote; import java.awt.RenderingHints; /** * An interface to be implemented by classes which are capable of * converting an instance of a supported class into a state representation * of the object which is serializable. Supported classes may in fact be * classes or interfaces. * *

    When possible, classes (not interfaces) should be supported explicitly, * i.e., the getObjectClass() method of the * SerializableState returned by getState(Object) * should return the same value which would be returned by invoking * getClass() on the object passed to * getState(Object). In particular, whenever feasible a * Serializer should not be used to serialize a given object * into a form which when deserialized would yield an instance of the * superclass of the class of which the object is an instance. When it is * not possible to provide a class-specific Serializer, such * as when a factory class generate subclasses of itself via * factory methods, then permitsSubclasses() should return * true; in the case of class-specific Serializers * it should return false. * * @see SerializableState * @see java.io.Serializable * * @since JAI 1.1 */ public interface Serializer { /** * Returns the class or interface which is supported by this * Serializer. * * @return The supported Class. */ Class getSupportedClass(); /** * Returns true if and only if it is legal for this * Serializer to be used to serialize a subclass. In * general this method should return false, i.e., a specific * Serializer should be registered explicitly for each class. * In some cases this is however not expedient. An example of this is a * Serializer of classes created by a factory class: the * exact subclasses may not be known but sufficient information may be * able to be extracted from the subclass instance to permit its * serialization by the Serializer registered for the factory * class. */ boolean permitsSubclasses(); /** * Converts an object into a state-preserving object which may be * serialized. If the value returned by getSupportedClass() * is a class, i.e., not an interface, then the parameter of * getState() should be an instance of that class; if * getSupportedClass() returns an interface, then the * parameter of getState() should implement that interface. * If the apposite condition is not satisfied then an * IllegalArgumentException will be thrown. * *

    If the class of the parameter is supported explicitly, i.e., is an * instance of the class returned by getSupportedClass(), then * the object returned by the getObject() method of the * generated SerializableState will be an instance of the * same class. If getSupportedClass() returns an interface * which the class of the parameter implements, then the object returned * by the getObject() method of the generated * SerializableState will be an instance of an unspecified * class which implements that interface. * * @param o The object to be converted into a serializable form. * @param h Configuration parameters the exact nature of which is * Serializer-dependent. If null, * reasonable default settings should be used. * @return A serializable form of the supplied object. * @exception IllegalArgumentException if o is * null, the supported class is an interface * not implemented by the class of which o is * an instance, or the supported class is not an interface * and getSupportedClass().equals(o.getClass()) * returns false. */ SerializableState getState(Object o, RenderingHints h); } jai-core-1.1.4/src/share/classes/javax/media/jai/remote/SerializerFactory.java0000644000175000017500000004776310203035544027155 0ustar mathieumathieu/* * $RCSfile: SerializerFactory.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:54 $ * $State: Exp $ */ package javax.media.jai.remote; import java.awt.RenderingHints; import java.io.Serializable; import java.util.ArrayList; import java.util.Hashtable; import java.util.Iterator; import java.util.Vector; import com.sun.media.jai.rmi.SerializerImpl; import com.sun.media.jai.rmi.InterfaceState; /** * A utility class which provides factory methods for obtaining * Serializer instances. * *

    The Serializers are maintained in a centralized repository * which is organized based on the classes supported by the * Serializers and the order in which the Serializers * were registered. Convenience methods similar to those defined in the * Serializer class are also provided. These enable * functionality equivalent to a single Serializer which * supports all the classes supported by the aggregate of all * Serializers resident in the repository. * *

    By default Serializers for the following classes * are registered by JAI: * *

      *
    • java.awt.RenderingHints *
      (entries which are neither Serializable nor supported by * SerializerFactory are omitted; support for specific * RenderingHints.Key subclasses may be added by new * Serializers);
    • *
    • java.awt.RenderingHints.Key *
      (limited to RenderingHints.Keys defined in * java.awt.RenderingHints and javax.media.jai.JAI); *
    • *
    • java.awt.Shape;
    • *
    • java.awt.image.DataBufferByte;
    • *
    • java.awt.image.DataBufferShort;
    • *
    • java.awt.image.DataBufferUShort;
    • *
    • java.awt.image.DataBufferInt;
    • *
    • javax.media.jai.DataBufferFloat;
    • *
    • javax.media.jai.DataBufferDouble;
    • *
    • java.awt.image.ComponentSampleModel;
    • *
    • java.awt.image.BandedSampleModel;
    • *
    • java.awt.image.PixelInterleavedSampleModel;
    • *
    • java.awt.image.SinglePixelPackedSampleModel;
    • *
    • java.awt.image.MultiPixelPackedSampleModel;
    • *
    • javax.media.jai.ComponentSampleModelJAI;
    • *
    • java.awt.image.Raster *
      (limited to Rasters which have a DataBuffer * and SampleModel supported by a Serializer);
    • *
    • java.awt.image.WritableRaster *
      (limited to WritableRasters which have a * DataBuffer and SampleModel supported by a * Serializer);
    • *
    • java.awt.image.ComponentColorModel;
    • *
    • java.awt.image.IndexColorModel;
    • *
    • java.awt.image.DirectColorModel;
    • *
    • javax.media.jai.FloatColorModel;
    • *
    • java.awt.image.renderable.RenderContext;
    • *
      (constrained by the aforementioned limitations of * the RenderingHints Serializer); *
    • java.awt.image.RenderedImage *
      (limited to RenderedImages which have Rasters * and a ColorModel supported by a Serializer);
    • *
    • java.awt.image.WritableRenderedImage *
      (limited to WritableRenderedImages which have * Rasters and a ColorModel supported by a * Serializer);
    • *
    • java.io.Serializable;
    • *
    • java.util.HashSet *
      (elements which are neither Serializable nor supported by * SerializerFactory are omitted);
    • *
    • java.util.Hashtable *
      (entries which are neither Serializable nor supported by * SerializerFactory are omitted);
    • *
    • java.util.Vector *
      (elements which are neither Serializable nor supported by * SerializerFactory are omitted);
    • *
    * * @see SerializableState * @see Serializer * @see java.io.Serializable * * @since JAI 1.1 */ public final class SerializerFactory { /** * Serializer hashed by supported Class. * The value is a Serializer if there is only one for the * given Class or a Vector if there are more. */ private static Hashtable repository = new Hashtable(); /** * Singleton instance of Serializer for use with already * Serializable classes. */ private static Serializer serializableSerializer = new SerSerializer(); static final SerializableState NULL_STATE = new SerializableState() { public Class getObjectClass() { return Object.class; } public Object getObject() { return null; } }; static { // Load all Serializers defined in com.sun.media.jai.rmi. SerializerImpl.registerSerializers(); } protected SerializerFactory() {} /** * Adds a Serializer to the repository. * * @param s The Serializers to be added to the repository. * @exception IllegalArgumentException if s is * null */ public static synchronized void registerSerializer(Serializer s) { if(s == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } Class c = s.getSupportedClass(); if(repository.containsKey(c)) { Object value = repository.get(c); if(value instanceof Vector) { ((Vector)value).add(0, s); } else { Vector v = new Vector(2); v.add(0, s); v.add(1, value); repository.put(c, v); } } else { repository.put(c, s); } } /** * Removes a Serializer from the repository. * * @param s The Serializers to be removed from the repository. * @exception IllegalArgumentException if s is * null */ public static synchronized void unregisterSerializer(Serializer s) { if(s == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } Class c = s.getSupportedClass(); Object value = repository.get(c); if(value != null) { if(value instanceof Vector) { Vector v = (Vector)value; v.remove(s); if(v.size() == 1) { repository.put(c, v.get(0)); } } else { repository.remove(c); } } } /** * Retrieves an array of all Serializers currently * resident in the repository which directly support the specified * Class. Serializers which support * a superclass of the specified class and permit subclass * serialization will not be included. * * @param c The class for which Serializers will be * retrieved. * @exception IllegalArgumentException if c is * null. */ public static synchronized Serializer[] getSerializers(Class c) { if(c == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } Object value = repository.get(c); Serializer[] result = null; if(value == null && Serializable.class.isAssignableFrom(c)) { result = new Serializer[] {serializableSerializer}; } else if(value instanceof Vector) { result = (Serializer[])((Vector)value).toArray(new Serializer[0]); } else if(value != null) { result = new Serializer[] {(Serializer)value}; } return result; } /** * Retrieves a Serializer for a given class c. * If more than one Serializer is available for the class * then the most recently registered Serializer will be * returned. If no registered Serializer exists which * directly supports the specified class, i.e., one for which the * getSupportedClass() returns a value equal to the * specified class, then a Serializer may be returned * which is actually registered against a superclass but permits * subclass serialization. * * @param c The class for which Serializers will be * retrieved. * @return A Serializer which supports the specified class. * or null if none is available. * @exception IllegalArgumentException if c is * null. * * @see java.awt.image.BandedSampleModel * @see java.awt.image.ComponentSampleModel */ public static synchronized Serializer getSerializer(Class c) { if(c == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } // Get the value from the repository. Object value = repository.get(c); // If null, attempt to find a superclass Serializer. if(value == null) { Class theClass = c; while(theClass != java.lang.Object.class) { Class theSuperclass = theClass.getSuperclass(); if(isSupportedClass(theSuperclass)) { Serializer s = getSerializer(theSuperclass); if(s.permitsSubclasses()) { value = s; break; } } theClass = theSuperclass; } } if(value == null && Serializable.class.isAssignableFrom(c)) { value = serializableSerializer; } // Return the highest priority Serializer or null. return value instanceof Vector ? (Serializer)((Vector)value).get(0) : (Serializer)value; } /** * Whether there is currently resident in the repository a * Serializer the getSupportedClass() * method of which returns a value equal to the parameter supplied * to this method according to equals(). * * @param c The class to be tested for compatibility. * @return Whether the specified class is directly supported. * @exception IllegalArgumentException if c is * null */ public static boolean isSupportedClass(Class c) { if(c == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } else if(Serializable.class.isAssignableFrom(c)) { return true; } return repository.containsKey(c); } /** * Returns an array listing all classes and interfaces on which the * isSupportedClass() method of this class may be invoked * and return true. * * @return An array of all supported classes and interfaces. */ public static Class[] getSupportedClasses() { Class[] classes = new Class[repository.size() + 1]; repository.keySet().toArray(classes); classes[classes.length-1] = Serializable.class; return classes; } /** * Determines the Class of which the deserialized form of the * supplied Class will be an instance. Specifically, this * method returns the Class of the Object * returned by invoking getObject() on the * SerializableState returned by getState() * after the state object has been serialized and deserialized. The * returned value will equal the supplied argument unless there is no * Serializer explicitly registered for this class but there * is a Serializer registered for a superclass with a * permitsSubclasses() method that returns * true. * * @param The Class for which the deserialized class type is * requested. * @return The deserialized Class or null. * @exception IllegalArgumentException if c is * null */ public static Class getDeserializedClass(Class c) { if(c == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } Class deserializedClass = null; // Try to find a superclass Serializer. if(isSupportedClass(c)) { deserializedClass = c; } else { Class theClass = c; while(theClass != java.lang.Object.class) { Class theSuperclass = theClass.getSuperclass(); if(isSupportedClass(theSuperclass)) { Serializer s = getSerializer(theSuperclass); if(s.permitsSubclasses()) { deserializedClass = theSuperclass; break; } } theClass = theSuperclass; } } return deserializedClass; } /** * Converts an object into a state-preserving object which may * be serialized. If the class of the object parameter is supported * explicitly, i.e., isSupportedClass(o.getClass()) * returns true, then the object will be converted into * a form which may be deserialized into an instance of the same class. * If the class is not supported explicitly but implements one or * more supported interfaces, then it will be converted into a * form which may be deserialized into an instance of an unspecified * class which implements all interfaces which are both implemented by * the class of the object and supported by some Serializer * currently resident in the repository. If the object is * null, the returned SerializableState will * return null from its getObject() method * and java.lang.Object.class from its * getObjectClass() method. * * @param o The object to be converted into a serializable form. * @param h Configuration parameters the exact nature of which is * Serializer-dependent. If null, * reasonable default settings should be used. * @return A serializable form of the supplied object. * @exception IllegalArgumentException if o is * non-null and either * isSupportedClass(o.getClass()) returns * false, or o * is not an instance of a class supported by a * Serializer in the repository or which * implements at least one interface supported by some * Serializers in the repository. */ public static SerializableState getState(Object o, RenderingHints h) { if(o == null) { return NULL_STATE; } Class c = o.getClass(); SerializableState state = null; if(isSupportedClass(c)) { // Found an explicit Serializer. Serializer s = getSerializer(c); state = s.getState(o, h); } else { // Try to find a superclass Serializer. Class theClass = c; while(theClass != java.lang.Object.class) { Class theSuperclass = theClass.getSuperclass(); if(isSupportedClass(theSuperclass)) { Serializer s = getSerializer(theSuperclass); if(s.permitsSubclasses()) { state = s.getState(o, h); break; } } theClass = theSuperclass; } if(state == null) { // Try an interface Serializer. Class[] interfaces = getInterfaces(c); Vector serializers = null; int numInterfaces = (interfaces == null) ? 0: interfaces.length; for(int i = 0; i < numInterfaces; i++) { Class iface = interfaces[i]; if(isSupportedClass(iface)) { if(serializers == null) { serializers = new Vector(); } serializers.add(getSerializer(iface)); } } int numSupportedInterfaces = serializers == null ? 0 : serializers.size(); if(numSupportedInterfaces == 0) { throw new IllegalArgumentException( JaiI18N.getString("SerializerFactory1")); } else if(numSupportedInterfaces == 1) { state = ((Serializer)serializers.get(0)).getState(o, h); } else { Serializer[] sArray = (Serializer[])serializers.toArray(new Serializer[0]); state = new InterfaceState(o, sArray, h); } } } return state; } /** * Retrieve the interfaces implemented by the specified class and all * its superclasses. */ private static Class[] getInterfaces(Class c) { if(c == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } ArrayList interfaces = new ArrayList(); Class laClasse = c; while(!(laClasse == java.lang.Object.class)) { Class[] iFaces = laClasse.getInterfaces(); if(iFaces != null) { for(int i = 0; i < iFaces.length; i++) { interfaces.add(iFaces[i]); } } laClasse = laClasse.getSuperclass(); } return interfaces.size() == 0 ? null : (Class[])interfaces.toArray(new Class[interfaces.size()]); } /** * A convenience wrapper around * getState(Object o, RenderingHints h) with * the RenderingHints parameter h set * to null. */ public static final SerializableState getState(Object o) { return getState(o, null); } } /** * A Serializer for Serializable objects. */ class SerSerializer implements Serializer { SerSerializer() {} public Class getSupportedClass() { return Serializable.class; } public boolean permitsSubclasses() { return true; } public SerializableState getState(Object o, RenderingHints h) { if(o == null) { return SerializerFactory.NULL_STATE; } else if(!(o instanceof Serializable)) { throw new IllegalArgumentException(JaiI18N.getString("SerializerFactory2")); } return new SerState((Serializable)o); } } /** * SerializableState which simply wraps an object that is * already an instance of Serializable. */ class SerState implements SerializableState { private Serializable object; SerState(Serializable object) { if(object == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.object = object; } public Class getObjectClass() { return object.getClass(); } public Object getObject() { return object; } } jai-core-1.1.4/src/share/classes/javax/media/jai/remote/RemoteRenderedOp.java0000644000175000017500000015446610444633025026723 0ustar mathieumathieu/* * $RCSfile: RemoteRenderedOp.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2006-06-16 22:52:05 $ * $State: Exp $ */package javax.media.jai.remote; import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.geom.Area; import java.awt.geom.GeneralPath; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Locale; import java.util.Set; import java.util.Vector; import java.text.MessageFormat; import javax.media.jai.CollectionChangeEvent; import javax.media.jai.CollectionOp; import javax.media.jai.JAI; import javax.media.jai.OperationRegistry; import javax.media.jai.PlanarImage; import javax.media.jai.PropertyChangeEventJAI; import javax.media.jai.PropertySourceChangeEvent; import javax.media.jai.RegistryMode; import javax.media.jai.RenderedOp; import javax.media.jai.RenderingChangeEvent; import javax.media.jai.TileCache; import javax.media.jai.registry.RemoteRIFRegistry; import javax.media.jai.util.ImagingException; import javax.media.jai.util.ImagingListener; import com.sun.media.jai.util.ImageUtil; /** * A node in a remote rendered imaging chain. This class is a concrete * implementation of the RemoteRenderedImage interface. A * RemoteRenderedOp stores a protocol name (as a * String), a server name (as a String), an * operation name (as a String), a * ParameterBlock containing sources and miscellaneous * parameters, and a RenderingHints containing rendering * hints. A set of nodes may be joined together via the source * Vectors within their ParameterBlocks to * form a directed acyclic graph (DAG). The topology * i.e., connectivity of the graph may be altered by changing the * ParameterBlocks; the operation name, parameters, and * rendering hints may also be changed. * *

    Such chains represent and handle operations that are being * performed remotely. They convey the structure of an imaging * chain in a compact representation and can be used to influence the * remote imaging process (through the use of retry interval, retries and * negotiation preferences). * *

    RemoteRenderedOps are a client side representation of * the chain of operations taking place on the server. * *

    The translation between RemoteRenderedOp chains and * RemoteRenderedImage (usually * PlanarImageServerProxy) chains makes use of two levels of * indirection provided by the OperationRegistry and * RemoteRIF facilities. First, the * local OperationRegistry is used to map the protocol * name into a RemoteRIF. This RemoteRIF then * constructs one or more RemoteRenderedImages (usually * PlanarImageServerProxys) to do the actual work (or * returns a RemoteRenderedImage by other means. The * OperationRegistry maps a protocol name into a * RemoteRIF, since there is one to one correspondence * between a protocol name and a RemoteRIF. This differs from * the case of RenderedOps, where the * OperationRegistry maps each operation name to a * RenderedImageFactory (RIF), since there is a one to one * correspondence between an operation name and a RIF. The * RemoteRIFs are therefore protocol-specific and not operation * specific, while a RIF is operation specific. * *

    Once a protocol name has been mapped into a RemoteRIF, * the RemoteRIF.create() method is used to create a rendering. * This rendering is responsible for communicating with the server to * perform the specified operation remotely. * *

    By virtue of being a subclass of RenderedOp, this class * participates in Java Bean-style events as specified by * RenderedOp. This means that PropertyChangeEmitter * methods may be used to register and unregister * PropertyChangeListeners. RemoteRenderedOps * are also PropertyChangeListeners so that they may be * registered as listeners of other PropertyChangeEmitters * or the equivalent. Each RemoteRenderedOp also automatically * receives any RenderingChangeEvents emitted by any of its * sources which are RenderedOps. * *

    RemoteRenderedOps add the server name and the protocol * name to the critical attributes, the editing (changing) of which, * coupled with a difference in the old and new rendering over some * non-empty region, may cause a RenderingChangeEvent to * be emitted. As with RenderedOp, editing of a critical * attribute of a RemoteRenderedOp will cause a * PropertyChangeEventJAI detailing the change to be fired * to all registered PropertyChangeListeners. * RemoteRenderedOp registers itself as a * PropertyChangeListener for all critical attributes, and * thus receives all PropertyChangeEventJAI events generated * by itself. This is done in order to allow the event handling code * to generate a new rendering and reuse any tiles that might be valid * after the critical argument change. * *

    When a RemoteRenderedOp node receives a * PropertyChangeEventJAI from itself, the region of * the current rendering which is invalidated is computed using * RemoteDescriptor.getInvalidRegion(). When a * RemoteRenderedOp node receives a * RenderingChangeEvent from one of its sources, the region of * the current rendering which is invalidated is computed using * the mapSourceRect() method of the current rendering and * the invalid region of the source (retrieved using * RenderingChangeEvent.getInvalidRegion()) * If the complement of the invalid region contains any tiles of the * current rendering, a new rendering of the node will be generated using * the new source node and its rendering generated using that version of * RemoteRIF.create() that updates the rendering of the node * according to the specified PropertyChangeEventJAI. The * identified tiles will be retained from the old rendering insofar as * possible. This might involve for example adding tiles to a * TileCache under the ownership of the new rendering. * A RenderingChangeEvent will then be fired to all * PropertyChangeListeners of the node, and to any node sinks * that are PropertyChangeListeners. The * newRendering parameter of the event constructor * (which may be retrieved via the getNewValue() method of * the event) will be set to either the new rendering of the node or to * null if it was not possible to retain any tiles of the * previous rendering. * * @see RenderedOp * @see RemoteRenderedImage * * @since JAI 1.1 */ public class RemoteRenderedOp extends RenderedOp implements RemoteRenderedImage { /** The name of the protocol this class provides an implementation for. */ protected String protocolName; /** The name of the server. */ protected String serverName; // The NegotiableCapabilitySet representing the negotiated values. private NegotiableCapabilitySet negotiated; /** * The RenderingHints when the node was last rendered, i.e., when * "theImage" was set to its current value. */ private transient RenderingHints oldHints; /** Node event names. */ private static Set nodeEventNames = null; static { nodeEventNames = new HashSet(); nodeEventNames.add("protocolname"); nodeEventNames.add("servername"); nodeEventNames.add("protocolandservername"); nodeEventNames.add("operationname"); nodeEventNames.add("operationregistry"); nodeEventNames.add("parameterblock"); nodeEventNames.add("sources"); nodeEventNames.add("parameters"); nodeEventNames.add("renderinghints"); } /** * Constructs a RemoteRenderedOp that will be used to * instantiate a particular rendered operation to be performed remotely * using the default operation registry, the name of the remote imaging * protocol, the name of the server to perform the operation on, an * operation name, a ParameterBlock, and a set of * rendering hints. All input parameters are saved by reference. * *

    An IllegalArgumentException may * be thrown by the protocol specific classes at a later point, if * null is provided as the serverName argument and null is not * considered a valid server name by the specified protocol. * *

    The RenderingHints may contain negotiation * preferences specified under the KEY_NEGOTIATION_PREFERENCES * key. * * @param protocolName The protocol name as a String. * @param serverName The server name as a String. * @param opName The operation name. * @param pb The sources and parameters. If null, * it is assumed that this node has no sources and * parameters. * @param hints The rendering hints. If null, it is * assumed that no hints are associated with the * rendering. * * @throws IllegalArgumentException if protocolName is * null. * @throws IllegalArgumentException if opName is * null. */ public RemoteRenderedOp(String protocolName, String serverName, String opName, ParameterBlock pb, RenderingHints hints) { this(null, protocolName, serverName, opName, pb, hints); } /** * Constructs a RemoteRenderedOp that will be used to * instantiate a particular rendered operation to be performed remotely * using the specified operation registry, the name of the remote imaging * protocol, the name of the server to perform the operation on, an * operation name, a ParameterBlock, and a set of * rendering hints. All input parameters are saved by reference. * *

    An IllegalArgumentException may * be thrown by the protocol specific classes at a later point, if * null is provided as the serverName argument and null is not * considered a valid server name by the specified protocol. * *

    The RenderingHints may contain negotiation * preferences specified under the KEY_NEGOTIATION_PREFERENCES * key. * * @param registry The OperationRegistry to be used for * instantiation. if null, the default * registry is used. * @param protocolName The protocol name as a String. * @param serverName The server name as a String. * @param opName The operation name. * @param pb The sources and parameters. If null, * it is assumed that this node has no sources and * parameters. * @param hints The rendering hints. If null, it is * assumed that no hints are associated with the * rendering. * * @throws IllegalArgumentException if protocolName is * null. * @throws IllegalArgumentException if opName is * null. */ public RemoteRenderedOp(OperationRegistry registry, String protocolName, String serverName, String opName, ParameterBlock pb, RenderingHints hints) { // This will throw IAE for opName if null super(registry, opName, pb, hints); if (protocolName == null) throw new IllegalArgumentException(JaiI18N.getString("Generic1")); this.protocolName = protocolName; this.serverName = serverName; // Add the node as a PropertyChangeListener of itself for // the critical attributes of the node. Superclass RenderedOp // takes care of all critical attributes except the following. // Case is ignored in the property names but infix caps are // used here anyway. addPropertyChangeListener("ServerName", this); addPropertyChangeListener("ProtocolName", this); addPropertyChangeListener("ProtocolAndServerName", this); } /** * Returns the String that identifies the server. */ public String getServerName() { return serverName; } /** * Sets a String identifying the server. * *

    If the supplied name does not equal the current server name, a * PropertyChangeEventJAI named "ServerName" * will be fired and a RenderingChangeEvent may be * fired if the node has already been rendered. The oldValue * field in the PropertyChangeEventJAI will contain * the old server name String and the newValue * field will contain the new server name String. * * @param serverName A String identifying the server. * @throws IllegalArgumentException if serverName is null. */ public void setServerName(String serverName) { if (serverName == null) throw new IllegalArgumentException(JaiI18N.getString("Generic2")); if (serverName.equalsIgnoreCase(this.serverName)) return; String oldServerName = this.serverName; this.serverName = serverName; fireEvent("ServerName", oldServerName, serverName); nodeSupport.resetPropertyEnvironment(false); } /** * Returns the String that identifies the remote imaging * protocol. */ public String getProtocolName() { return protocolName; } /** * Sets a String identifying the remote imaging protocol. * This method causes this RemoteRenderedOp to use * the new protocol name with the server name set on this node * previously. If the server is not compliant with the new * protocol name, the setProtocolAndServerNames() * method should be used to set a new protocol name and a compliant * new server name at the same time. * *

    If the supplied name does not equal the current protocol name, a * PropertyChangeEventJAI named "ProtocolName" * will be fired and a RenderingChangeEvent may be * fired if the node has already been rendered. The oldValue * field in the PropertyChangeEventJAI will contain * the old protocol name String and the newValue * field will contain the new protocol name String. * * @param protocolName A String identifying the server. * @throws IllegalArgumentException if protocolName is null. */ public void setProtocolName(String protocolName) { if (protocolName == null) throw new IllegalArgumentException(JaiI18N.getString("Generic1")); if (protocolName.equalsIgnoreCase(this.protocolName)) return; String oldProtocolName = this.protocolName; this.protocolName = protocolName; fireEvent("ProtocolName", oldProtocolName, protocolName); nodeSupport.resetPropertyEnvironment(false); } /** * Sets the protocol name and the server name of this * RemoteRenderedOp to the specified arguments.. * *

    If both the supplied protocol name and the supplied server * name values do not equal the current values, a * PropertyChangeEventJAI named "ProtocolAndServerName" * will be fired. The oldValue field in the * PropertyChangeEventJAI will contain a two element * array of Strings, the old protocol name being the * first element and the old server name being the second. Similarly * the newValue field of the PropertyChangeEventJAI will * contain a two element array of Strings, the new protocol * name being the first element and the new server name being the * second. If only the supplied protocol name does not equal * the current protocol name, a PropertyChangeEventJAI * named "ProtocolName" will be fired. If only the supplied server * name does not equal the current server name, a * PropertyChangeEventJAI named "ServerName" * will be fired. * * @param protocolName A String identifying the protocol. * @param serverName A String identifying the server. * @throws IllegalArgumentException if protocolName is null. * @throws IllegalArgumentException if serverName is null. */ public void setProtocolAndServerNames(String protocolName, String serverName) { if (serverName == null) throw new IllegalArgumentException(JaiI18N.getString("Generic2")); if (protocolName == null) throw new IllegalArgumentException(JaiI18N.getString("Generic1")); boolean protocolNotChanged = protocolName.equalsIgnoreCase(this.protocolName); boolean serverNotChanged = serverName.equalsIgnoreCase(this.serverName); if (protocolNotChanged) { if (serverNotChanged) // Neither changed return; else { // Only serverName changed setServerName(serverName); return; } } else { if (serverNotChanged) { // Only protocolName changed setProtocolName(protocolName); return; } } String oldProtocolName = this.protocolName; String oldServerName = this.serverName; this.protocolName = protocolName; this.serverName = serverName; // Both changed fireEvent("ProtocolAndServerName", new String[] {oldProtocolName, oldServerName}, new String[] {protocolName, serverName}); nodeSupport.resetPropertyEnvironment(false); } /** * Returns the name of the RegistryMode corresponding to * this RemoteRenderedOp. This method overrides the * implementation in RenderedOp to always returns the * String "remoteRendered". */ public String getRegistryModeName() { return RegistryMode.getMode("remoteRendered").getName(); } /** * Overrides the RenderedOp method to allow the operation * to be performed remotely. */ protected synchronized PlanarImage createInstance(boolean isNodeRendered) { ParameterBlock pb = new ParameterBlock(); pb.setParameters(getParameters()); int numSources = getNumSources(); for (int i = 0; i < numSources; i++) { Object source = getNodeSource(i); Object ai = null; if (source instanceof RenderedOp) { RenderedOp src = (RenderedOp)source; ai = isNodeRendered ? src.getRendering() : src.createInstance(); } else if ((source instanceof RenderedImage) || (source instanceof Collection)) { ai = source; } else if (source instanceof CollectionOp) { ai = ((CollectionOp)source).getCollection(); } else { /* Source is some other type. Pass on (for now). */ ai = source; } pb.addSource(ai); } RemoteRenderedImage instance = RemoteRIFRegistry.create(nodeSupport.getRegistry(), protocolName, serverName, nodeSupport.getOperationName(), pb, nodeSupport.getRenderingHints()); // Throw an exception if the rendering is null. if (instance == null) { throw new ImagingException(JaiI18N.getString("RemoteRenderedOp2")); } // Save the state of the node. RenderingHints rh = nodeSupport.getRenderingHints(); oldHints = rh == null ? null : (RenderingHints)rh.clone(); // Ensure that the rendering is a PlanarImage. return PlanarImage.wrapRenderedImage(instance); } /* ----- PropertyChangeListener method. ----- */ /** * Implementation of PropertyChangeListener. * *

    When invoked with an event which is an instance of * RenderingChangeEvent the node will respond by * re-rendering itself while retaining any tiles possible. */ // XXX Update javadoc both here and at class level. public synchronized void propertyChange(PropertyChangeEvent evt) { // // React if and only if the node has been rendered and // A: a non-PropertySourceChangeEvent PropertyChangeEventJAI // was received from this node, or // B: a RenderingChangeEvent was received from a source node. // // Cache event and node sources. Object evtSrc = evt.getSource(); Vector nodeSources = nodeSupport.getParameterBlock().getSources(); // Get the name of the bean property and convert it to lower // case now for efficiency later. String propName = evt.getPropertyName().toLowerCase(Locale.ENGLISH); if (theImage != null && ((evt instanceof PropertyChangeEventJAI && evtSrc == this && !(evt instanceof PropertySourceChangeEvent) && nodeEventNames.contains(propName)) || ((evt instanceof RenderingChangeEvent || evt instanceof CollectionChangeEvent || (evt instanceof PropertyChangeEventJAI && evtSrc instanceof RenderedImage && propName.equals("invalidregion"))) && nodeSources.contains(evtSrc)))) { // Save the previous rendering. PlanarImage theOldImage = theImage; // Initialize the event flag. boolean shouldFireEvent = false; // Set default invalid region to null (the entire image). Shape invalidRegion = null; if (evtSrc == this && (propName.equals("operationregistry") || propName.equals("protocolname") || propName.equals("protocolandservername"))) { // invalidate the entire rendering. shouldFireEvent = true; theImage = null; } else if (evt instanceof RenderingChangeEvent || (evtSrc instanceof RenderedImage && propName.equals("invalidregion"))) { // Set the event flag. shouldFireEvent = true; Shape srcInvalidRegion = null; if (evt instanceof RenderingChangeEvent) { // RenderingChangeEvent presumably from a source // RenderedOp. RenderingChangeEvent rcEvent = (RenderingChangeEvent)evt; // Get the invalidated region of the source. srcInvalidRegion = rcEvent.getInvalidRegion(); // If entire source is invalid replace with source bounds. if (srcInvalidRegion == null) { srcInvalidRegion = ((PlanarImage)rcEvent.getOldValue()).getBounds(); } } else { // Get the invalidated region of the source. srcInvalidRegion = (Shape)evt.getNewValue(); // If entire source is invalid replace with source bounds. if (srcInvalidRegion == null) { RenderedImage rSrc = (RenderedImage)evtSrc; srcInvalidRegion = new Rectangle(rSrc.getMinX(), rSrc.getMinY(), rSrc.getWidth(), rSrc.getHeight()); } } // Only process further if the rendering is a // PlanarImageServerProxy. if (!(theImage instanceof PlanarImageServerProxy)) { // Clear the current rendering. theImage = null; } else { // Save the previous rendering as a PlanarImageServerProxy. PlanarImageServerProxy oldPISP = (PlanarImageServerProxy)theImage; // Cache source invalid bounds. Rectangle srcInvalidBounds = srcInvalidRegion.getBounds(); // If bounds are empty, replace srcInvalidRegion with // the complement of the image bounds within the // bounds of all tiles. if (srcInvalidBounds.isEmpty()) { int x = oldPISP.tileXToX(oldPISP.getMinTileX()); int y = oldPISP.tileYToY(oldPISP.getMinTileY()); int w = oldPISP.getNumXTiles() * oldPISP.getTileWidth(); int h = oldPISP.getNumYTiles() * oldPISP.getTileHeight(); Rectangle tileBounds = new Rectangle(x, y, w, h); Rectangle imageBounds = oldPISP.getBounds(); if (!tileBounds.equals(imageBounds)) { Area tmpArea = new Area(tileBounds); tmpArea.subtract(new Area(imageBounds)); srcInvalidRegion = tmpArea; srcInvalidBounds = srcInvalidRegion.getBounds(); } } // ----- Determine invalid destination region. ----- boolean saveAllTiles = false; ArrayList validTiles = null; if (srcInvalidBounds.isEmpty()) { invalidRegion = srcInvalidRegion; saveAllTiles = true; } else { // Get index of source which changed. int idx = nodeSources.indexOf(evtSrc); // Determine bounds of invalid destination region. Rectangle dstRegionBounds = oldPISP.mapSourceRect(srcInvalidBounds, idx); if (dstRegionBounds == null) { dstRegionBounds = oldPISP.getBounds(); } // Determine invalid destination region. Point[] indices = getTileIndices(dstRegionBounds); int numIndices = indices != null ? indices.length : 0; GeneralPath gp = null; for(int i = 0; i < numIndices; i++) { if (i % 1000 == 0 && gp != null) gp = new GeneralPath(new Area(gp)); Rectangle dstRect = getTileRect(indices[i].x, indices[i].y); Rectangle srcRect = oldPISP.mapDestRect(dstRect, idx); if(srcRect == null) { gp = null; break; } if(srcInvalidRegion.intersects(srcRect)) { if(gp == null) { gp = new GeneralPath(dstRect); } else { gp.append(dstRect, false); } } else { if(validTiles == null) { validTiles = new ArrayList(); } validTiles.add(indices[i]); } } invalidRegion = (gp == null) ? null : new Area(gp); } // Retrieve the old TileCache. TileCache oldCache = oldPISP.getTileCache(); theImage = null; // Only perform further processing if there is a cache // and there are tiles to save. if (oldCache != null && (saveAllTiles || validTiles != null)) { // Create new rendering newEventRendering(protocolName, oldPISP, (PropertyChangeEventJAI)evt); // Only perform further processing if the new // rendering is an OpImage with a non-null TileCache. if (theImage instanceof PlanarImageServerProxy && ((PlanarImageServerProxy)theImage).getTileCache() != null) { PlanarImageServerProxy newPISP = (PlanarImageServerProxy)theImage; TileCache newCache = newPISP.getTileCache(); Object tileCacheMetric = newPISP.getTileCacheMetric(); if (saveAllTiles) { Raster[] tiles = oldCache.getTiles(oldPISP); int numTiles = tiles == null ? 0 : tiles.length; for(int i = 0; i < numTiles; i++) { Raster tile = tiles[i]; int tx = newPISP.XToTileX(tile.getMinX()); int ty = newPISP.YToTileY(tile.getMinY()); newCache.add(newPISP, tx, ty, tile, tileCacheMetric); } } else { // save some, but not all, tiles int numValidTiles = validTiles.size(); for(int i = 0; i < numValidTiles; i++) { Point tileIndex = (Point)validTiles.get(i); Raster tile = oldCache.getTile(oldPISP, tileIndex.x, tileIndex.y); if (tile != null) { newCache.add(newPISP, tileIndex.x, tileIndex.y, tile, tileCacheMetric); } } } } } } } else { // not op name or registry change nor RenderingChangeEvent ParameterBlock oldPB = null; ParameterBlock newPB = null; String oldServerName = serverName; String newServerName = serverName; boolean checkInvalidRegion = false; if (propName.equals("operationname")) { if (theImage instanceof PlanarImageServerProxy) { newEventRendering(protocolName, (PlanarImageServerProxy)theImage, (PropertyChangeEventJAI)evt); } else { theImage = null; createRendering(); } // Do not set checkInvalidRegion to true, since there // are no tiles to save for this case. shouldFireEvent = true; // XXX Do we need to do any evaluation of any // DeferredData parameters. } else if (propName.equals("parameterblock")) { oldPB = (ParameterBlock)evt.getOldValue(); newPB = (ParameterBlock)evt.getNewValue(); checkInvalidRegion = true; } else if (propName.equals("sources")) { // Replace source(s) Vector params = nodeSupport.getParameterBlock().getParameters(); oldPB = new ParameterBlock((Vector)evt.getOldValue(), params); newPB = new ParameterBlock((Vector)evt.getNewValue(), params); checkInvalidRegion = true; } else if (propName.equals("parameters")) { // Replace parameter(s) oldPB = new ParameterBlock(nodeSources, (Vector)evt.getOldValue()); newPB = new ParameterBlock(nodeSources, (Vector)evt.getNewValue()); checkInvalidRegion = true; } else if (propName.equals("renderinghints")) { oldPB = newPB = nodeSupport.getParameterBlock(); checkInvalidRegion = true; } else if (propName.equals("servername")) { oldPB = newPB = nodeSupport.getParameterBlock(); oldServerName = (String)evt.getOldValue(); newServerName = (String)evt.getNewValue(); checkInvalidRegion = true; } else if (evt instanceof CollectionChangeEvent) { // Event from a CollectionOp source. // Replace appropriate source. int collectionIndex = nodeSources.indexOf(evtSrc); Vector oldSources = (Vector)nodeSources.clone(); Vector newSources = (Vector)nodeSources.clone(); oldSources.set(collectionIndex, evt.getOldValue()); newSources.set(collectionIndex, evt.getNewValue()); Vector params = nodeSupport.getParameterBlock().getParameters(); oldPB = new ParameterBlock(oldSources, params); newPB = new ParameterBlock(newSources, params); checkInvalidRegion = true; } if (checkInvalidRegion) { // Set event flag. shouldFireEvent = true; // Get the associated RemoteDescriptor. OperationRegistry registry = nodeSupport.getRegistry(); RemoteDescriptor odesc = (RemoteDescriptor) registry.getDescriptor(RemoteDescriptor.class, protocolName); // XXX // Evaluate any DeferredData parameters. oldPB = ImageUtil.evaluateParameters(oldPB); newPB = ImageUtil.evaluateParameters(newPB); // Determine the invalid region. invalidRegion = (Shape) odesc.getInvalidRegion("rendered", oldServerName, oldPB, oldHints, newServerName, newPB, nodeSupport.getRenderingHints(), this); if (invalidRegion == null || !(theImage instanceof PlanarImageServerProxy)) { // Can't save any tiles; clear the rendering. theImage = null; } else { // Create a new rendering. PlanarImageServerProxy oldRendering = (PlanarImageServerProxy)theImage; newEventRendering(protocolName, oldRendering, (PropertyChangeEventJAI)evt); // If the new rendering is also a // PlanarImageServerProxy, save some tiles. if (theImage instanceof PlanarImageServerProxy && oldRendering.getTileCache() != null && ((PlanarImageServerProxy)theImage).getTileCache() != null) { PlanarImageServerProxy newRendering = (PlanarImageServerProxy)theImage; TileCache oldCache = oldRendering.getTileCache(); TileCache newCache = newRendering.getTileCache(); Object tileCacheMetric = newRendering.getTileCacheMetric(); // If bounds are empty, replace invalidRegion with // the complement of the image bounds within the // bounds of all tiles. if (invalidRegion.getBounds().isEmpty()) { int x = oldRendering.tileXToX( oldRendering.getMinTileX()); int y = oldRendering.tileYToY( oldRendering.getMinTileY()); int w = oldRendering.getNumXTiles() * oldRendering.getTileWidth(); int h = oldRendering.getNumYTiles() * oldRendering.getTileHeight(); Rectangle tileBounds = new Rectangle(x, y, w, h); Rectangle imageBounds = oldRendering.getBounds(); if (!tileBounds.equals(imageBounds)) { Area tmpArea = new Area(tileBounds); tmpArea.subtract(new Area(imageBounds)); invalidRegion = tmpArea; } } if (invalidRegion.getBounds().isEmpty()) { // Save all tiles. Raster[] tiles = oldCache.getTiles(oldRendering); int numTiles = tiles == null ? 0 : tiles.length; for(int i = 0; i < numTiles; i++) { Raster tile = tiles[i]; int tx = newRendering.XToTileX(tile.getMinX()); int ty = newRendering.YToTileY(tile.getMinY()); newCache.add(newRendering, tx, ty, tile, tileCacheMetric); } } else { // Copy tiles not in invalid region from old // TileCache to new TileCache. Raster[] tiles = oldCache.getTiles(oldRendering); int numTiles = tiles == null ? 0 : tiles.length; for(int i = 0; i < numTiles; i++) { Raster tile = tiles[i]; Rectangle bounds = tile.getBounds(); if (!invalidRegion.intersects(bounds)) { newCache.add( newRendering, newRendering.XToTileX(bounds.x), newRendering.YToTileY(bounds.y), tile, tileCacheMetric); } } } } } } } // Re-render the node. This will only occur if theImage // has been set to null above. if (theOldImage instanceof PlanarImageServerProxy && theImage == null) { newEventRendering(protocolName, (PlanarImageServerProxy)theOldImage, (PropertyChangeEventJAI)evt); } else { createRendering(); } // Fire an event if the flag was set. if (shouldFireEvent) { // Clear the synthetic and cached properties and reset the // property source. resetProperties(true); // Create the event object. RenderingChangeEvent rcEvent = new RenderingChangeEvent(this, theOldImage, theImage, invalidRegion); // Fire to all registered listeners. eventManager.firePropertyChange(rcEvent); // Fire an event to all PropertyChangeListener sinks. Vector sinks = getSinks(); if (sinks != null) { int numSinks = sinks.size(); for (int i = 0; i < numSinks; i++) { Object sink = sinks.get(i); if (sink instanceof PropertyChangeListener) { ((PropertyChangeListener)sink).propertyChange(rcEvent); } } } } } } /** * Creates a new rendering in response to the provided event, and * assigns the new rendering to "theImage" variable. */ private void newEventRendering(String protocolName, PlanarImageServerProxy oldPISP, PropertyChangeEventJAI event) { RemoteRIF rrif = (RemoteRIF)nodeSupport.getRegistry(). getFactory("remoterendered", protocolName); theImage = (PlanarImage)rrif.create(oldPISP, this, event); } /** * Fire an events to all registered listeners. */ private void fireEvent(String propName, Object oldVal, Object newVal) { if (eventManager != null) { Object eventSource = eventManager.getPropertyChangeEventSource(); PropertyChangeEventJAI evt = new PropertyChangeEventJAI(eventSource, propName, oldVal, newVal); eventManager.firePropertyChange(evt); } } /** * Returns the amount of time between retries in milliseconds. * *

    If this RemoteRenderedOp has been rendered, then its * getRetryInterval() method will be called to return * the current retry interval. If no rendering has been created, and * a value was set using the setRetryInterval() method), that * value will be returned, else the default retry interval as defined by * RemoteJAI.DEFAULT_RETRY_INTERVAL is returned. */ public int getRetryInterval() { if (theImage != null) { return ((RemoteRenderedImage)theImage).getRetryInterval(); } else { RenderingHints rh = nodeSupport.getRenderingHints(); if (rh == null) { return RemoteJAI.DEFAULT_RETRY_INTERVAL; } else { Integer i = (Integer)rh.get(JAI.KEY_RETRY_INTERVAL); if (i == null) return RemoteJAI.DEFAULT_RETRY_INTERVAL; else return i.intValue(); } } } /** * Sets the amount of time between retries in milliseconds. If this * RemoteRenderedOp has already been rendered, the * setRetryInterval() method is called on the rendering * to inform it of the new retry interval. The rendering can choose to * ignore this new setting, in which case getRetryInterval() * will still return the old value, or the rendering can honor these * settings, in which case getRetryInterval() will return * the new settings. If this RemoteRenderedOp has not been * rendered, the new retry interval specified will be stored. * These new stored retry interval will be passed as * part of the RenderingHints using the * KEY_RETRY_INTERVAL key, to the new rendering * when it is created. * * @param retryInterval The amount of time (in milliseconds) to wait * between retries. * @throws IllegalArgumentException if retryInterval is negative. */ public void setRetryInterval(int retryInterval) { if (retryInterval < 0) throw new IllegalArgumentException(JaiI18N.getString("Generic3")); if (theImage != null) { ((RemoteRenderedImage)theImage).setRetryInterval(retryInterval); } RenderingHints rh = nodeSupport.getRenderingHints(); if (rh == null) { nodeSupport.setRenderingHints(new RenderingHints(null)); rh = nodeSupport.getRenderingHints(); } rh.put(JAI.KEY_RETRY_INTERVAL, new Integer(retryInterval)); } /** * Returns the number of retries. * *

    If this RemoteRenderedOp has been rendered, then its * getNumRetries() method will be called to return * the current number of retries. If no rendering has been created, and * a value was set using the setNumRetries() method), that * value will be returned, else the default retry interval as defined by * RemoteJAI.DEFAULT_NUM_RETRIES is returned. */ public int getNumRetries() { if (theImage != null) { return ((RemoteRenderedImage)theImage).getNumRetries(); } else { RenderingHints rh = nodeSupport.getRenderingHints(); if (rh == null) { return RemoteJAI.DEFAULT_NUM_RETRIES; } else { Integer i = (Integer)rh.get(JAI.KEY_NUM_RETRIES); if (i == null) return RemoteJAI.DEFAULT_NUM_RETRIES; else return i.intValue(); } } } /** * Sets the number of retries. If this RemoteRenderedOp * has already been rendered, the setNumRetries() method * is called on the rendering to inform it of the new number of retries. * The rendering can choose to ignore these new settings, in which case * getNunRetries() will still return the old values, or * the rendering can honor these new settings in which * case getNumRetries() will return the new value. * If this RemoteRenderedOp has not been rendered, * the new setting specified will be stored. * These new settings which have been stored will be passed as * part of the RenderingHints using the * KEY_NUM_RETRIES key, to the new rendering * when it is created. * * @param numRetries The number of times an operation should be retried * in case of a network error. * @throws IllegalArgumentException if numRetries is negative. */ public void setNumRetries(int numRetries) { if (numRetries < 0) throw new IllegalArgumentException( JaiI18N.getString("Generic4")); if (theImage != null) { ((RemoteRenderedImage)theImage).setNumRetries(numRetries); } RenderingHints rh = nodeSupport.getRenderingHints(); if (rh == null) { nodeSupport.setRenderingHints(new RenderingHints(null)); rh = nodeSupport.getRenderingHints(); } rh.put(JAI.KEY_NUM_RETRIES, new Integer(numRetries)); } /** * Sets the preferences to be used in the client-server * communication. These preferences are utilized in the negotiation * process. Note that preferences for more than one category can be * specified using this method. Also each preference can be a list * of values in decreasing order of preference, each value specified * as a NegotiableCapability. The * NegotiableCapability first (for a particular category) * in this list is given highest priority in the negotiation process * (for that category). * *

    It may be noted that this method allows for multiple negotiation * cycles by allowing negotiation preferences to be set * multiple times. If this RemoteRenderedOp has already * been rendered, the setNegotiationPreferences() method * is called on the rendering to inform it of the new preferences. The * rendering can choose to ignore these new preferences, in which case * getNegotiatedValues() will still return the results of * the old negotiation, or the rendering can re-perform the negotiation, * (using the RemoteJAI.negotiate, for example) in which * case getNegotiatedValues() will return the new * negotiated values. If this RemoteRenderedOp has not been * rendered, the new preferences specified will be stored, a negotiation * with these new preferences will be initiated and the results stored. * These new preferences which have been stored will be passed as * part of the RenderingHints using the * KEY_NEGOTIATION_PREFERENCES key, to the new rendering * when it is created. * * @param preferences The preferences to be used in the negotiation * process. */ public void setNegotiationPreferences(NegotiableCapabilitySet preferences) { if (theImage != null) { ((RemoteRenderedImage)theImage).setNegotiationPreferences( preferences); } RenderingHints rh = nodeSupport.getRenderingHints(); if (preferences != null) { if (rh == null) { nodeSupport.setRenderingHints(new RenderingHints(null)); rh = nodeSupport.getRenderingHints(); } rh.put(JAI.KEY_NEGOTIATION_PREFERENCES, preferences); } else { // Remove any previous values set for negotiation preferences if (rh != null) { rh.remove(JAI.KEY_NEGOTIATION_PREFERENCES); } } negotiated = negotiate(preferences); } /** * Returns the current negotiation preferences or null, if none were * set previously. */ public NegotiableCapabilitySet getNegotiationPreferences() { RenderingHints rh = nodeSupport.getRenderingHints(); return rh == null ? null : (NegotiableCapabilitySet)rh.get( JAI.KEY_NEGOTIATION_PREFERENCES); } // do the negotiation private NegotiableCapabilitySet negotiate(NegotiableCapabilitySet prefs) { OperationRegistry registry = nodeSupport.getRegistry(); NegotiableCapabilitySet serverCap = null; // Get the RemoteDescriptor for protocolName RemoteDescriptor descriptor = (RemoteDescriptor) registry.getDescriptor(RemoteDescriptor.class, protocolName); if (descriptor == null) { Object[] msgArg0 = {new String(protocolName)}; MessageFormat formatter = new MessageFormat(""); formatter.setLocale(Locale.getDefault()); formatter.applyPattern(JaiI18N.getString("RemoteJAI16")); throw new ImagingException(formatter.format(msgArg0)); } int count=0; int numRetries = getNumRetries(); int retryInterval = getRetryInterval(); Exception rieSave = null; while (count++ < numRetries) { try { serverCap = descriptor.getServerCapabilities(serverName); break; } catch (RemoteImagingException rie) { // Print that an Exception occured System.err.println(JaiI18N.getString("RemoteJAI24")); rieSave = rie; // Sleep for retryInterval milliseconds try { Thread.sleep(retryInterval); } catch (InterruptedException ie) { // throw new RuntimeException(ie.toString()); sendExceptionToListener(JaiI18N.getString("Generic5"), new ImagingException(JaiI18N.getString("Generic5"), ie)); } } } if (serverCap == null && count > numRetries) { sendExceptionToListener(JaiI18N.getString("RemoteJAI18"), rieSave); // throw new RemoteImagingException(JaiI18N.getString("RemoteJAI18")+"\n"+rieSave.getMessage()); } RemoteRIF rrif = (RemoteRIF)registry.getFactory("remoteRendered", protocolName); return RemoteJAI.negotiate(prefs, serverCap, rrif.getClientCapabilities()); } /** * Returns the results of the negotiation between the client and server * capabilities according to the preferences set via the * setNegotiationPreferences() method. This will return * null if no negotiation preferences were set, and no negotiation * was performed, or if the negotiation failed. * *

    If this RemoteRenderedOp has been rendered, then its * getNegotiatedValues() method will be called to return * the current negotiated values. If no rendering has been created, then * the internally stored negotiated value (calculated when the new * preferences were set using the setNegotiationPreferences() * method) will be returned. */ public NegotiableCapabilitySet getNegotiatedValues() throws RemoteImagingException { if (theImage != null) { return ((RemoteRenderedImage)theImage).getNegotiatedValues(); } else { return negotiated; } } /** * Returns the results of the negotiation between the client and server * capabilities for the given category according to the preferences * set via the setNegotiationPreferences() method. This * will return null if no negotiation preferences were set, and no * negotiation was performed, or if the negotiation failed. * *

    If this RemoteRenderedOp has been rendered, then its * getNegotiatedValues() method will be called to return * the current negotiated values. If no rendering has been created, then * the internally stored negotiated value (calculated when the new * preferences were set using the setNegotiationPreferences() * method) will be returned. * * @param category The category to return the negotiated results for. */ public NegotiableCapability getNegotiatedValue(String category) throws RemoteImagingException { if (theImage != null) { return ((RemoteRenderedImage)theImage).getNegotiatedValue( category); } else { return negotiated == null ? null : negotiated.getNegotiatedValue(category); } } /** * Informs the server of the negotiated values that are the result of * a successful negotiation. If this RemoteRenderedOp has * been rendered, then the rendering's * setServerNegotiatedValues method will be called to * inform the server of the negotiated results. If no rendering has * been created, this method will do nothing. * * @param negotiatedValues The result of the negotiation. */ public void setServerNegotiatedValues(NegotiableCapabilitySet negotiatedValues) throws RemoteImagingException { if (theImage != null) { ((RemoteRenderedImage)theImage).setServerNegotiatedValues( negotiatedValues); } } void sendExceptionToListener(String message, Exception e) { ImagingListener listener = (ImagingListener)getRenderingHints().get(JAI.KEY_IMAGING_LISTENER); listener.errorOccurred(message, e, this, false); } } jai-core-1.1.4/src/share/classes/javax/media/jai/remote/RemoteDescriptorImpl.java0000644000175000017500000002241010203035544027606 0ustar mathieumathieu/* * $RCSfile: RemoteDescriptorImpl.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:52 $ * $State: Exp $ */package javax.media.jai.remote; import java.awt.RenderingHints; import java.awt.image.renderable.ParameterBlock; import java.net.URL; import javax.media.jai.PropertyGenerator; import javax.media.jai.ParameterListDescriptor; import javax.media.jai.OperationNode; /** * This abstract class provides a partial implementation of the * RemoteDescriptor interface, and is suitable for * subclassing. * * @see RemoteDescriptor * * @since JAI 1.1 */ public abstract class RemoteDescriptorImpl implements RemoteDescriptor { /** * The name of the protocol that this descriptor describes. */ protected String protocolName; /** * The URL pointing to the documentation regarding * the format of the server name String. */ protected URL serverNameDocURL; /** * Creates a RemoteDescriptorImpl given the protocol name * and the URL that points to documentation regarding the * format of the server name String. * *

    While the serverNameDocURL argument is allowed to * be null, this is strongly discouraged, since this URL * is the only description available to the user to help with creating * a serverName String correctly. * * @param protocolName The name of the protocol. * @param serverNameDocURL The URL pointing to server name * format documentation. * @throws IllegalArgumentException if protocolName is null. */ public RemoteDescriptorImpl(String protocolName, URL serverNameDocURL) { if (protocolName == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic1")); } this.protocolName = protocolName; this.serverNameDocURL = serverNameDocURL; } /** * Returns the name of the remote imaging protocol under which this * RemoteDescriptor will be registered in the * OperationRegistry. */ public String getName() { return protocolName; } /** * The registry modes supported by this descriptor. The default * implementation in this class returns two modes - "remoteRendered" * and "remoteRenderable". If the subclass does not support both * these modes it should override this method to reflect that. * * @see javax.media.jai.RegistryMode */ public String[] getSupportedModes() { return new String[] {"remoteRendered", "remoteRenderable"}; } /** * Returns true if the supplied modeName is supported by this * descriptor. The default implementation in this class returns true * only if the supplied modeName is one of either "remoteRendered" * or "remoteRenderable". * * @param modeName The mode name to check support for. * * @return true, if the implementation of this descriptor supports * the specified mode. false otherwise. * * @throws IllegalArgumentException if modeName is null. */ public boolean isModeSupported(String modeName) { if (modeName == null) { throw new IllegalArgumentException( JaiI18N.getString("RemoteDescriptorImpl1")); } if (modeName.equalsIgnoreCase("remoteRendered") || modeName.equalsIgnoreCase("remoteRenderable")) { return true; } return false; } /** * Returns true, if the implementation of this descriptor supports * properties, false otherwise. The default implementation in this class * returns false, signifying that no properties are supported independent * of the operations themselves. * * @see PropertyGenerator */ public boolean arePropertiesSupported() { return false; } /** * Returns an array of PropertyGenerators implementing * the property inheritance for this descriptor. Since neither the * "remoteRendered" or "remoteRendered" modes support properties * independent of the operations themselves, the default * implementation throws an UnsupportedOperationException. * Subclasses should override this method if they wish to produce * inherited properties. * * @param modeName The mode name to get PropertyGenerators * for. * @throws IllegalArgumentException if modeName is null. * @throws UnsupportedOperationException if * arePropertiesSupported() returns false * * @return An array of PropertyGenerators, or * null if this operation does not have any of * its own PropertyGenerators. */ public PropertyGenerator[] getPropertyGenerators(String modeName) { if (modeName == null) { throw new IllegalArgumentException( JaiI18N.getString("RemoteDescriptorImpl1")); } throw new UnsupportedOperationException( JaiI18N.getString("RemoteDescriptorImpl2")); } /** * Returns a URL that points to an HTML page containing * instructions on constructing a server name string for the protocol * with which this class is associated. */ public URL getServerNameDocs() { return serverNameDocURL; } /** * Calculates the region over which two distinct remote renderings * of an operation may be expected to differ. The operation is * represented by the OperationNode argument to this * method. The String that identifies the operation * can be retrieved via the OperationNode's * getOperationName() method. * *

    The class of the returned object will vary as a function of * the nature of the operation. For rendered and renderable two- * dimensional images this should be an instance of a class which * implements java.awt.Shape. * *

    The implementation in this class always returns null as the * invalid region signifying that there is no common region of validity. * Since null is always returned, in the interests of efficiency, none * of the checks for ensuring that the ParameterBlock * arguments passed to this method contain the correct number and * Class of sources and parameters are performed in this * implementation. * * @param registryModeName The name of the mode. * @param oldServerName The previous server name. * @param oldParamBlock The previous sources and parameters. * @param oldHints The previous hints. * @param newServerName The current server name. * @param newParamBlock The current sources and parameters. * @param newHints The current hints. * @param node The affected node in the processing chain. * * @return The region over which the data of two renderings of this * operation may be expected to be invalid or null * if there is no common region of validity. If an empty * java.awt.Shape is returned, this indicates * that all pixels within the bounds of the old rendering * remain valid. * * @throws IllegalArgumentException if registryModeName * is null or if the operation requires either * sources or parameters and either oldParamBlock * or newParamBlock is null. * @throws IllegalArgumentException if there is no OperationDescriptor * for the specified operationName on any one or both of the * servers identified by oldServerName and * newServerName, or if the number of sources or * the name, number and Class of the operation's * parameters is not the same on both the servers. * @throws IllegalArgumentException if oldParamBlock or * newParamBlock do not contain sufficient sources * or parameters for the operation in question. */ public Object getInvalidRegion(String registryModeName, String oldServerName, ParameterBlock oldParamBlock, RenderingHints oldHints, String newServerName, ParameterBlock newParamBlock, RenderingHints newHints, OperationNode node) throws RemoteImagingException { return null; } /** * The two modes supported by this descriptor are "remoteRendered" and * "remoteRenderable". Since neither of these modes supports any * parameters, this default implementation always returns null. * * @param modeName The mode name to get the * ParameterListDescriptor for. * * @throws IllegalArgumentException if modeName is null. */ public ParameterListDescriptor getParameterListDescriptor(String modeName) { if (modeName == null) { throw new IllegalArgumentException( JaiI18N.getString("RemoteDescriptorImpl1")); } return null; } } jai-core-1.1.4/src/share/classes/javax/media/jai/remote/RemoteJAI.java0000644000175000017500000013741710240717543025276 0ustar mathieumathieu/* * $RCSfile: RemoteJAI.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-05-12 18:24:34 $ * $State: Exp $ */package javax.media.jai.remote; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Vector; import javax.media.jai.JAI; import javax.media.jai.OperationRegistry; import javax.media.jai.OperationDescriptor; import javax.media.jai.PlanarImage; import javax.media.jai.RenderedOp; import javax.media.jai.RenderableOp; import javax.media.jai.TileCache; import javax.media.jai.util.CaselessStringKey; import javax.media.jai.util.ImagingException; import javax.media.jai.util.ImagingListener; import com.sun.media.jai.util.ImageUtil; /** * A convenience class for instantiating operations on remote machines. * * This class also provides information related to the server and allows * for setting of parameters for the remote communication with the server. * *

    Conceptually this class is very similar to the JAI * class, except that the RemoteJAI class deals with * remote operations. This class allows programmers to use the syntax: * *

     * import javax.media.jai.remote.RemoteJAI;
     * RemoteJAI rc = new RemoteJAI(protocolName, serverName);
     * RemoteRenderedOp im = rc.create("convolve", paramBlock, renderHints);
     * 
    * * to create new images by applying operators that are executed remotely on * the specified server. The create() method returns a * RemoteRenderedOp encapsulating the protocol name, server * name, operation name, parameter block, and rendering hints. Additionally, * it performs validity checking on the operation parameters. The operation * parameters are determined from the OperationDescriptor * retrieved using the getServerSupportedOperationList() method. * Programmers may also refer to * RemoteJAI.createRenderable("opname", paramBlock, renderHints); * *

    If the OperationDescriptor associated with the * named operation returns true from its * isImmediate() method, the create() * method will ask the RemoteRenderedOp it constructs to render * itself immediately. If this rendering is null, * create() will itself return null * rather than returning an instance of RemoteRenderedOp * as it normally does. * *

    The registry being used by this class may be * inspected or set using the getOperationRegistry() and * setOperationRegistry() methods. Only experienced * users should attempt to set the registry. This registry is used to * map protocol names into either a RemoteRIF or a * RemoteCRIF. * *

    The TileCache associated with an instance may be * similarly accessed. * *

    Each instance of RemoteJAI contains a set of * default rendering hints which will be used for all image creations. * These hints are merged with any hints supplied to the * create method; directly supplied hints take precedence * over the common hints. When a new RemoteJAI instance is * constructed, its hints are initialized to a copy of the default * hints. Thus when an instance of RemoteJAI is * constructed, hints for the default registry, tile cache, number of * retries, and the retry interval are added to the set of common * rendering hints. Similarly, invoking setOperationRegistry(), * setTileCache(), setNumRetries() or * setRetryInterval() on a RemoteJAI instance * will cause the respective entity to be added to the common rendering * hints. The hints associated with any instance may be manipulated * using the getRenderingHints(), * setRenderingHints(), clearRenderingHints() methods. * *

    The TileCache to be used by a particular operation * may be set during construction, or by calling * the setTileCache() method. This will result in the * provided tile cache being added to the set of common rendering * hints. * *

    Network errors are dealt with through the use of retry intervals and * retries. Retries refers to the maximum number of times a remote operation * will be retried. The retry interval refers to the amount of time (in * milliseconds) between two consecutive retries. If errors are encountered * at each retry and the number of specified retries has been exhausted, a * RemoteImagingException will be thrown. By default, the * number of retries is set to five, and the retry interval * is set to a thousand milliseconds. These values can be changed by using * the setNumRetries() and the setRetryInterval * methods and can also be specified via the RenderingHints * object passed as an argument to RemoteJAI.create(). Time * outs (When the amount of time taken to get a response or * the result of an operation from the remote machine exceeds a limit) are * not dealt with, and must be taken care of by the network imaging * protocol implementation itself. The implementation must be responsible * for monitoring time outs, but on encountering one can deal with it by * throwing a RemoteImagingException, which will then be dealt * with using retries and retry intervals. * *

    This class provides the capability of negotiating capabilities * between the client and the server. The negotiate * method uses the preferences specified via the * setNegotiationPreferences method alongwith the server * and client capabilities retrieved via the getServerCapabilities * and getClientCapabilities respectively to negotiate on each * of the preferences. This negotiation treats the client and server * capabilities as being non-preferences, and the user set * NegotiableCapabilitySet as being a preference. The * negotiation is performed according to the rules described in the class * documentation for NegotiableCapability. * *

    Note that negotiation preferences can be set either prior to * specifying a particular rendered or renderable operation (by using * RemoteJAI.create() or * RemoteJAI.createRenderable()) or afterwards. The currently * set negotiation preferences are passed to the RemoteRenderedOp * on its construction through the RenderingHints using the * KEY_NEGOTIATION_PREFERENCES key. Since * RemoteRenderableOp does not accept a * RenderingHints object as a construction argument, the newly * created RemoteRenderableOp is informed of these preferences * using it's setRenderingHints() method. These preferences * can be changed after the construction using the * setNegotiationPreferences() method on both * RemoteRenderedOp and RemoteRenderableOp. * * The same behavior applies to the number of retries and the retry interval, * whether they be the default values contained in the default * RenderingHints or whether they are set using the * setNumRetries or setRetryInterval methods, the * existing values are passed to RemoteRenderedOp's when they * are created through the RenderingHints argument, and are set * on the newly created RemoteRenderableOp using the * setNumRetries or setRetryInterval methods on * RemoteRenderableOp. * * @see JAI * @see JAIRMIDescriptor * @see RemoteImagingException * * @since JAI 1.1 */ public class RemoteJAI { /** The String representing the remote server machine. */ protected String serverName; /** The name of the protocol used for client-server communication. */ protected String protocolName; /** The OperationRegistry instance used for instantiating operations. */ private OperationRegistry operationRegistry = JAI.getDefaultInstance().getOperationRegistry(); /** The amount of time to wait between retries (in Millseconds). */ public static final int DEFAULT_RETRY_INTERVAL = 1000; /** The default number of retries. */ public static final int DEFAULT_NUM_RETRIES = 5; /** * Time in milliseconds between retries, initialized to default value. */ private int retryInterval = DEFAULT_RETRY_INTERVAL; // Milliseconds /** The number of retries, initialized to default value. */ private int numRetries = DEFAULT_NUM_RETRIES; /** A reference to a centralized TileCache object. */ private transient TileCache cache = JAI.getDefaultInstance().getTileCache(); /** * The RenderingHints object used to retrieve the TileCache, * OperationRegistry hints. */ private RenderingHints renderingHints; /** * The set of preferences to be used for the communication between * the client and the server. */ private NegotiableCapabilitySet preferences = null; /** * The set of properties agreed upon after the negotiation process * between the client and the server has been completed. */ private static NegotiableCapabilitySet negotiated; /** The client and server capabilities. */ private NegotiableCapabilitySet serverCapabilities = null; private NegotiableCapabilitySet clientCapabilities = null; /** * A Hashtable containing OperationDescriptors hashed by their * operation names. */ private Hashtable odHash = null; /** The array of descriptors supported by the server. */ private OperationDescriptor descriptors[] = null; /** Required to I18N compound messages. */ private static MessageFormat formatter; /** * Constructs a RemoteJAI instance with the given * protocol name and server name. The semantics of the serverName * are defined by the particular protocol used to create this * class. Instructions on how to create a serverName that is * compatible with this protocol can be retrieved from the * getServerNameDocs() method on the * RemoteDescriptor associated with the given * protocolName. An IllegalArgumentException may * be thrown by the protocol specific classes at a later point, if * null is provided as the serverName argument and null is not * considered a valid serverName by the specified protocol. * * @param protocolName The String that identifies the * remote imaging protocol. * @param serverName The String that identifies the server. * * @throws IllegalArgumentException if protocolName is null. */ public RemoteJAI(String protocolName, String serverName) { this(protocolName, serverName, null, null); } /** * Constructs a RemoteJAI instance with the given * protocol name, server name, OperationRegistry * and TileCache. If the specified * OperationRegistry is null, the registry associated * with the default JAI instance will be used. If the * specified TileCache is null, the TileCache * associated with the default JAI instance will be used. * *

    An IllegalArgumentException may * be thrown by the protocol specific classes at a later point, if * null is provided as the serverName argument and null is not * considered a valid serverName by the specified protocol. * * @param serverName The String that identifies * the server. * @param protocolName The String that identifies * the remote imaging protocol. * @param operationRegistry The OperationRegistry associated * with this class, if null, default will be used. * @param tileCache The TileCache associated with * this class, if null, default will be used. * @throws IllegalArgumentException if protocolName is null. */ public RemoteJAI(String protocolName, String serverName, OperationRegistry registry, TileCache tileCache) { if (protocolName == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic1")); } // For formatting error strings. formatter = new MessageFormat(""); formatter.setLocale(Locale.getDefault()); this.protocolName = protocolName; this.serverName = serverName; // operationRegistry and cache variables are already initialized // via static initializers, so change them only if the user has // provided a non-null value for them. if (registry != null) { this.operationRegistry = registry; } if (tileCache != null) { this.cache = tileCache; } this.renderingHints = new RenderingHints(null); this.renderingHints.put(JAI.KEY_OPERATION_REGISTRY, operationRegistry); this.renderingHints.put(JAI.KEY_TILE_CACHE, cache); this.renderingHints.put(JAI.KEY_RETRY_INTERVAL, new Integer(retryInterval)); this.renderingHints.put(JAI.KEY_NUM_RETRIES, new Integer(numRetries)); } /** * Returns a String identifying the remote server machine. */ public String getServerName() { return serverName; } /** * Returns the protocol name. */ public String getProtocolName() { return protocolName; } /** * Sets the amount of time between retries in milliseconds. The * specified retryInterval parameter will be added * to the common RenderingHints of this * RemoteJAI instance, under the * JAI.KEY_RETRY_INTERVAL key. * * @param retryInterval The time interval between retries (milliseconds). * @throws IllegalArgumentException if retryInterval is negative. */ public void setRetryInterval(int retryInterval) { if (retryInterval < 0) { throw new IllegalArgumentException(JaiI18N.getString("Generic3")); } this.retryInterval = retryInterval; renderingHints.put(JAI.KEY_RETRY_INTERVAL, new Integer(retryInterval)); } /** * Returns the amount of time between retries in milliseconds. */ public int getRetryInterval() { return retryInterval; } /** * Sets the number of retries. The specified numRetries * parameter will be added to the common RenderingHints * of this RemoteJAI instance, under the * JAI.KEY_NUM_RETRIES key. * * @param numRetries The number of retries. * @throws IllegalArgumentException if numRetries is negative. */ public void setNumRetries(int numRetries) { if (numRetries < 0) { throw new IllegalArgumentException(JaiI18N.getString("Generic4")); } this.numRetries = numRetries; renderingHints.put(JAI.KEY_NUM_RETRIES, new Integer(numRetries)); } /** * Returns the number of retries. */ public int getNumRetries() { return numRetries; } /** * Returns the OperationRegistry being used by this * RemoteJAI instance. */ public OperationRegistry getOperationRegistry() { return operationRegistry; } /** * Sets theOperationRegistry to be used by this * RemoteJAI instance. The operationRegistry * parameter will be added to the RenderingHints of this * RemoteJAI instance. * * @throws IllegalArgumentException if operationRegistry is null. */ public void setOperationRegistry(OperationRegistry operationRegistry) { if (operationRegistry == null) { throw new IllegalArgumentException( JaiI18N.getString("RemoteJAI4")); } this.operationRegistry = operationRegistry; this.renderingHints.put(JAI.KEY_OPERATION_REGISTRY, operationRegistry); } /** * Sets the TileCache to be used by this * RemoteJAI. The tileCache parameter * will be added to the RenderingHints of this * RemoteJAI instance. * * @throws IllegalArgumentException if tileCache is null. */ public void setTileCache(TileCache tileCache) { if (tileCache == null) { throw new IllegalArgumentException( JaiI18N.getString("RemoteJAI5")); } this.cache = tileCache; renderingHints.put(JAI.KEY_TILE_CACHE, cache); } /** * Returns the TileCache being used by this * RemoteJAI instance. */ public TileCache getTileCache() { return cache; } /** * Returns the RenderingHints associated with this * RemoteJAI instance. These rendering hints will be * merged with any hints supplied as an argument to the * create() method. */ public RenderingHints getRenderingHints() { return renderingHints; } /** * Sets the RenderingHints associated with this * RemoteJAI instance. These rendering hints will be * merged with any hints supplied as an argument to the * create() method. * * @throws IllegalArgumentException if hints is null. */ public void setRenderingHints(RenderingHints hints) { if (hints == null) { throw new IllegalArgumentException( JaiI18N.getString("RemoteJAI6")); } this.renderingHints = hints; } /** * Clears the RenderingHints associated with this * RemoteJAI instance. */ public void clearRenderingHints() { this.renderingHints = new RenderingHints(null); } /** * Returns the hint value associated with a given key * in this RemoteJAI instance, or null * if no value is associated with the given key. * * @throws IllegalArgumentException if key is null. */ public Object getRenderingHint(RenderingHints.Key key) { if (key == null) { throw new IllegalArgumentException( JaiI18N.getString("RemoteJAI7")); } return renderingHints.get(key); } /** * Sets the hint value associated with a given key * in this RemoteJAI instance. * * @throws IllegalArgumentException if key is * null. * @throws IllegalArgumentException if value is * null. * @throws IllegalArgumentException if value is * not of the correct type for the given hint. */ public void setRenderingHint(RenderingHints.Key key, Object value) { if (key == null) { throw new IllegalArgumentException( JaiI18N.getString("RemoteJAI7")); } if (value == null) { throw new IllegalArgumentException( JaiI18N.getString("RemoteJAI8")); } try { renderingHints.put(key, value); } catch (Exception e) { throw new IllegalArgumentException(e.toString()); } } /** * Removes the hint value associated with a given key * in this RemoteJAI instance. */ public void removeRenderingHint(RenderingHints.Key key) { renderingHints.remove(key); } /** * Creates a RemoteRenderedOp which represents the named * operation to be performed remotely, using the source(s) and/or * parameter(s) specified in the ParameterBlock, and * applying the specified hints to the destination. This method * should only be used when the final result returned is a single * RemoteRenderedImage. * *

    The supplied operation name is validated against the * names of the OperationDescriptors returned from * the getServerSupportedOperationList() method. The * source(s) and/or parameter(s) in the ParameterBlock * are validated against the named operation's descriptor, both in * their numbers and types. Additional restrictions placed on the * sources and parameters by an individual operation are also * validated by calling its * OperationDescriptor.validateArguments() method. * *

    Parameters are allowed to have a null input * value, if that particular parameter has a default value specified * in its operation's descriptor. In this case, the default value * will replace the null input. * *

    Unspecified tailing parameters are allowed, if these * parameters have default values specified in the operation's * descriptor. However, if a parameter, which has a default value, * is followed by one or more parameters that * have no default values, this parameter must be specified in the * ParameterBlock, even if it only has a value of * code>null. * *

    The rendering hints associated with this instance of * RemoteJAI are overlaid with the hints passed to this * method. That is, the set of keys will be the union of the * keys from the instance's hints and the hints parameter. * If the same key exists in both places, the value from the * hints parameter will be used. * * @param opName The name of the operation. * @param args The source(s) and/or parameter(s) for the operation. * @param hints The hints for the operation. * * @throws IllegalArgumentException if opName is * null. * @throws IllegalArgumentException if args is * null. * @throws IllegalArgumentException if no * OperationDescriptor is available from the server * with the specified operation name. * @throws IllegalArgumentException if the * OperationDescriptor for the specified * operation name on the server does not * support the "rendered" registry mode. * @throws IllegalArgumentException if the specified operation does * not produce a * java.awt.image.RenderedImage. * @throws IllegalArgumentException if the specified operation is * unable to handle the sources and parameters specified in * args. * * @return A RemoteRenderedOp that represents the named * operation to be performed remotely, or null * if the specified operation * is in the "immediate" mode and the rendering of the * PlanarImage failed. */ public RemoteRenderedOp create(String opName, ParameterBlock args, RenderingHints hints) { if (opName == null) { throw new IllegalArgumentException( JaiI18N.getString("RemoteJAI9")); } if (args == null) { throw new IllegalArgumentException( JaiI18N.getString("RemoteJAI10")); } // Initialize the odHash hashtable getServerSupportedOperationList(); // Get the OperationDescriptor associated with this name. OperationDescriptor odesc = (OperationDescriptor)odHash.get(new CaselessStringKey(opName)); if (odesc == null) { throw new IllegalArgumentException( JaiI18N.getString("RemoteJAI11")); } // Does this operation support rendered mode? if (!odesc.isModeSupported("rendered")) { throw new IllegalArgumentException( JaiI18N.getString("RemoteJAI12")); } // Does the operation produce a RenderedImage? if (!RenderedImage.class.isAssignableFrom( odesc.getDestClass("rendered"))) { throw new IllegalArgumentException( JaiI18N.getString("RemoteJAI13")); } // Validate input arguments. The ParameterBlock is cloned here // because OperationDescriptor.validateArguments() may change // its content. StringBuffer msg = new StringBuffer(); args = (ParameterBlock)args.clone(); if (!odesc.validateArguments("rendered", args, msg)) { throw new IllegalArgumentException(msg.toString()); } // Merge rendering hints. Hints passed in take precedence. RenderingHints mergedHints; if (hints == null) { mergedHints = renderingHints; } else if (renderingHints.isEmpty()) { mergedHints = hints; } else { mergedHints = new RenderingHints((Map)renderingHints); mergedHints.add(hints); } RemoteRenderedOp op = new RemoteRenderedOp(operationRegistry, protocolName, serverName, opName, args, mergedHints); // If the operation requests immediate rendering, do so. if (odesc.isImmediate()) { PlanarImage im = null; im = op.getRendering(); if (im == null) { // Op could not be rendered, return null. return null; } } // Return the RemoteRenderedOp associated with this operation. return op; } /** * Creates a RemoteRenderableOp that represents the named * operation to be performed remotely, using the source(s) and/or * parameter(s) specified in the ParameterBlock. * This method should only be used when the final result returned * is a single RenderableImage. * *

    The supplied operation name is validated against the names * of the OperationDescriptors returned from * the getServerSupportedOperationList() method. * The source(s) and/or parameter(s) in the * ParameterBlock are validated against the named * operation's descriptor, both in their numbers and types. * Additional restrictions placed on the sources and parameters * by an individual operation are also validated by calling its * OperationDescriptor.validateRenderableArguments() * method. * *

    Parameters are allowed to have a null input * value, if that particular parameter has a default value specified * in its operation's descriptor. In this case, the default value * will replace the null input. * *

    Unspecified tailing parameters are allowed, if these * parameters have default values specified in the operation's * descriptor. However, if a parameter, which * has a default value, is followed by one or more parameters that * have no default values, this parameter must be specified in the * ParameterBlock, even if it only has a value of * code>null. * * @param opName The name of the operation. * @param args The source(s) and/or parameter(s) for the operation. * * @throws IllegalArgumentException if opName is * null. * @throws IllegalArgumentException if args is * null. * @throws IllegalArgumentException if no * OperationDescriptor is available from the server * with the specified operation name. * @throws IllegalArgumentException if the * OperationDescriptor for the specified * operation name on the server does not * support "renderable" registry mode. * @throws IllegalArgumentException if the specified operation does * not produce a * java.awt.image.renderable.RenderableImage. * @throws IllegalArgumentException if the specified operation is * unable to handle the sources and parameters specified in * args. * * @return A RemoteRenderableOp that represents the named * operation to be performed remotely. */ public RemoteRenderableOp createRenderable(String opName, ParameterBlock args) { if (opName == null) { throw new IllegalArgumentException( JaiI18N.getString("RemoteJAI9")); } if (args == null) { throw new IllegalArgumentException( JaiI18N.getString("RemoteJAI10")); } // Initialize the odHash hashtable getServerSupportedOperationList(); // Get the OperationDescriptor associated with this name. OperationDescriptor odesc = (OperationDescriptor)odHash.get(new CaselessStringKey(opName)); if (odesc == null) { throw new IllegalArgumentException( JaiI18N.getString("RemoteJAI11")); } // Does this operation support rendered mode? if (!odesc.isModeSupported("renderable")) { throw new IllegalArgumentException( JaiI18N.getString("RemoteJAI14")); } // Does the operation produce a RenderedImage? if (!RenderableImage.class.isAssignableFrom( odesc.getDestClass("renderable"))) { throw new IllegalArgumentException( JaiI18N.getString("RemoteJAI15")); } // Validate input arguments. The ParameterBlock is cloned here // because OperationDescriptor.validateRenderableArguments() // may change its content. StringBuffer msg = new StringBuffer(); args = (ParameterBlock)args.clone(); if (!odesc.validateArguments("renderable", args, msg)) { throw new IllegalArgumentException(msg.toString()); } RemoteRenderableOp op = new RemoteRenderableOp(operationRegistry, protocolName, serverName, opName, args); // Set the node-scope hints op.setRenderingHints(renderingHints); // Return the RemoteRenderableOp. return op; } // // NEGOTIATION RELATED METHODS // /** * Sets the preferences to be used in the client-server * communication. These preferences are utilized in the negotiation * process. Note that preferences for more than one category can be * specified using this method since NegotiableCapabilitySet * allows different NegotiableCapability objects to be * bundled up in one NegotiableCapabilitySet class. Even * under the same category (as specified by the getCategory() method * on NegotiableCapability), multiple * NegotiableCapability objects can be added to the * preferences. The preference added first for a particular category is * given highest priority in the negotiation process. * *

    Since a new set of preferences is set everytime this method is * called, this method allows for changing negotiation preferences * multiple times. However it should be noted that preferences set on * this method are relevant only prior to the creation of an * operation (using the RemoteJAI.create method). To * change negotiation preferences on an operation after it has been * created, the setNegotiationPreferences() method on the * created RemoteRenderedOp should be used. The * preferences parameter will be added to the * RenderingHints of this RemoteJAI instance. */ public void setNegotiationPreferences(NegotiableCapabilitySet preferences) { this.preferences = preferences; if (preferences == null) renderingHints.remove(JAI.KEY_NEGOTIATION_PREFERENCES); else renderingHints.put(JAI.KEY_NEGOTIATION_PREFERENCES, preferences); // Every time new preferences are set, invalidate old Negotiation // results and do the negotiation again. negotiated = null; getNegotiatedValues(); } /** * Returns the results of the negotiation between the client and server * capabilities according to the user preferences specified at an * earlier time. This will return null if the negotiation failed. * *

    If a negotiation cycle has not been initiated prior to calling * this method, or the negotiation preferences have been * changed, this method will initiate a new negotiation cycle, which will * create and return a new set of negotiated values. * * @returns A NegotiableCapabilitySet that is the * result of the negotiation process, if negotiation is successful, * otherwise returns null. */ public NegotiableCapabilitySet getNegotiatedValues() throws RemoteImagingException { // If negotiation was not performed before, or if new preferences // have invalidated the old negotiated results. if (negotiated == null) { if (serverCapabilities == null) { serverCapabilities = getServerCapabilities(); } if (clientCapabilities == null) { clientCapabilities = getClientCapabilities(); } // Do the negotiation negotiated = negotiate(preferences, serverCapabilities, clientCapabilities); } return negotiated; } /** * Returns the results of the negotiation between the client and server * capabilities according to the user preferences specified at an * earlier time for the given category. This method returns a * NegotiableCapability object, that represents the result * of the negotiation for the given category. If the negotiation failed, * null will be returned. * *

    If a negotiation cycle has not been initiated prior to calling * this method, or the negotiation preferences have been * changed, this method will initiate a new negotiation cycle, which will * create and return a new negotiated value for the given category. * * @param category The category to negotiate on. * @throws IllegalArgumentException if category is null. * @returns A NegotiableCapabilitySet that is the * result of the negotiation process, if negotiation is successful, * otherwise returns null. */ public NegotiableCapability getNegotiatedValues(String category) throws RemoteImagingException { // We do not need to check for category being null, since that // check will be made by the methods called from within this method. // If negotiation was not performed before, or if new preferences // have invalidated the old negotiated results. if (negotiated == null) { if (serverCapabilities == null) { serverCapabilities = getServerCapabilities(); } if (clientCapabilities == null) { clientCapabilities = getClientCapabilities(); } // Do the negotiation return negotiate(preferences, serverCapabilities, clientCapabilities, category); } else { // If negotiated is not null, then the negotiated results are // current and the result for the given category can just be // extracted from there and returned. return negotiated.getNegotiatedValue(category); } } /** * This method negotiates the capabilities to be used in the remote * communication. Upon completion of the negotiation process, * this method returns a NegotiableCapabilitySet which * contains an aggregation of the NegotiableCapability * objects that represent the results of negotiation. If the negotiation * fails, null will be returned. * *

    The negotiation process treats the serverCapabilities and the * clientCapabilities as non-preferences and will throw an * IllegalArgumentException if the * isPreference method for either of these returns * true. The preferences NegotiableCapabilitySet should * return true from its isPreference method, otherwise an * IllegalArgumentException will be thrown. The negotiation * is done in accordance with the rules described in the class comments * for NegotiableCapability. * *

    If either the serverCapabilities or the clientCapabilities * is null, then the negotiation will fail, and null will be returned. * If preferences is null, the negotiation will become a two-way * negotiation between the two non-null * NegotiableCapabilitySets. * * @param preferences The user preferences for the negotiation. * @param serverCapabilities The capabilities of the server. * @param clientCapabilities The capabilities of the client. * * @throws IllegalArgumentException if serverCapabilities is a * preference, i.e., if it's isPreference() method * returns true. * @throws IllegalArgumentException if clientCapabilities is a * preference, i.e., if it's isPreference() method * returns true. * @throws IllegalArgumentException if preferences is a * non-preference, i.e., if it's isPreference() method * returns false. */ public static NegotiableCapabilitySet negotiate( NegotiableCapabilitySet preferences, NegotiableCapabilitySet serverCapabilities, NegotiableCapabilitySet clientCapabilities) { if (serverCapabilities == null || clientCapabilities == null) return null; if (serverCapabilities != null && serverCapabilities.isPreference() == true) throw new IllegalArgumentException( JaiI18N.getString("RemoteJAI20")); if (clientCapabilities != null && clientCapabilities.isPreference() == true) throw new IllegalArgumentException( JaiI18N.getString("RemoteJAI21")); if (preferences == null) { return serverCapabilities.negotiate(clientCapabilities); } else { if (preferences.isPreference() == false) throw new IllegalArgumentException( JaiI18N.getString("RemoteJAI19")); NegotiableCapabilitySet clientServerCap = serverCapabilities.negotiate(clientCapabilities); if (clientServerCap == null) return null; return clientServerCap.negotiate(preferences); } } /** * This method negotiates the capabilities to be used in the remote * communication for the given category. Upon completion of the * negotiation process, this method returns a * NegotiableCapability object, that represents the result * of the negotiation for the given category. If the negotiation fails, * null will be returned. * *

    The negotiation process treats the serverCapabilities and the * clientCapabilities as non-preferences and will throw an * IllegalArgumentException if the * isPreference method for either of these returns * true. The preferences NegotiableCapabilitySet should * return true from its isPreference method or an * IllegalArgumentException will be thrown. The negotiation * is done in accordance with the rules described in the class comments * for NegotiableCapability. * *

    If either the serverCapabilities or the clientCapabilities * is null, then the negotiation will fail, and null will be returned. * If preferences is null, the negotiation will become a two-way * negotiation between the two non-null * NegotiableCapabilitySets. * * @param preferences The user preferences for the negotiation. * @param serverCapabilities The capabilities of the server. * @param clientCapabilities The capabilities of the client. * @param category The category to perform the negotiation on. * * @throws IllegalArgumentException if preferences is a * non-preference, i.e., if it's isPreference() method * returns false. * @throws IllegalArgumentException if serverCapabilities is a * preference, i.e., if it's isPreference() method * returns true. * @throws IllegalArgumentException if clientCapabilities is a * preference, i.e., if it's isPreference() method * returns true. * @throws IllegalArgumentException if category is null. */ public static NegotiableCapability negotiate( NegotiableCapabilitySet preferences, NegotiableCapabilitySet serverCapabilities, NegotiableCapabilitySet clientCapabilities, String category) { if (serverCapabilities == null || clientCapabilities == null) return null; if (serverCapabilities != null && serverCapabilities.isPreference() == true) throw new IllegalArgumentException( JaiI18N.getString("RemoteJAI20")); if (clientCapabilities != null && clientCapabilities.isPreference() == true) throw new IllegalArgumentException( JaiI18N.getString("RemoteJAI21")); if (preferences != null && preferences.isPreference() == false) throw new IllegalArgumentException( JaiI18N.getString("RemoteJAI19")); if (category == null) throw new IllegalArgumentException( JaiI18N.getString("RemoteJAI26")); if (preferences == null || preferences.isEmpty()) { return serverCapabilities.getNegotiatedValue(clientCapabilities, category); } else { List prefList = preferences.get(category); List serverList = serverCapabilities.get(category); List clientList = clientCapabilities.get(category); Iterator p = prefList.iterator(); NegotiableCapability server, client, result; NegotiableCapability pref = null; //If there are no preferences for the current category if (p.hasNext() == false) pref = null; else pref = (NegotiableCapability)p.next(); Vector results = new Vector(); // Negotiate every server NC with every client NC for (Iterator s = serverList.iterator(); s.hasNext(); ) { server = (NegotiableCapability)s.next(); for (Iterator c = clientList.iterator(); c.hasNext(); ) { client = (NegotiableCapability)c.next(); result = server.negotiate(client); if (result == null) { // This negotiation failed, continue to the next one continue; } else { // Negotiation between client and server succeeded, // add to results array results.add(result); if (pref != null) { // Negotiate with the pref, if negotiation is // successful, return the result from this method. result = result.negotiate(pref); } if (result != null) { return result; } // else move onto next negotiation } } } for (; p.hasNext(); ) { pref = (NegotiableCapability)p.next(); for (int r=0; rRemoteImagingException), they * will be dealt with by the use of retries and retry intervals. */ public NegotiableCapabilitySet getServerCapabilities() throws RemoteImagingException { if (serverCapabilities == null) { // Get the RemoteDescriptor for protocolName RemoteDescriptor descriptor = (RemoteDescriptor) operationRegistry.getDescriptor(RemoteDescriptor.class, protocolName); if (descriptor == null) { Object[] msgArg0 = {new String(protocolName)}; formatter.applyPattern(JaiI18N.getString("RemoteJAI16")); throw new RuntimeException(formatter.format(msgArg0)); } Exception rieSave = null; int count=0; while (count++ < numRetries) { try { serverCapabilities = descriptor.getServerCapabilities(serverName); break; } catch (RemoteImagingException rie) { // Print that an Exception occured System.err.println(JaiI18N.getString("RemoteJAI24")); rieSave = rie; // Sleep for retryInterval milliseconds try { Thread.sleep(retryInterval); } catch (InterruptedException ie) { sendExceptionToListener(JaiI18N.getString("Generic5"), new ImagingException(JaiI18N.getString("Generic5"), ie)); // throw new RuntimeException(ie.toString()); } } } if (serverCapabilities == null && count > numRetries) { sendExceptionToListener(JaiI18N.getString("RemoteJAI18"), rieSave); // throw new RemoteImagingException( // JaiI18N.getString("RemoteJAI18")+"\n"+rieSave.getMessage()); } } return serverCapabilities; } /** * Returns the set of capabilities supported by the client. */ public NegotiableCapabilitySet getClientCapabilities() { if (clientCapabilities == null) { RemoteRIF rrif = (RemoteRIF)operationRegistry.getFactory("remoteRendered", protocolName); if (rrif == null) { rrif = (RemoteRIF)operationRegistry.getFactory("remoteRenderable", protocolName); } if (rrif == null) { Object[] msgArg0 = {new String(protocolName)}; formatter.applyPattern(JaiI18N.getString("RemoteJAI17")); throw new RuntimeException(formatter.format(msgArg0)); } clientCapabilities = rrif.getClientCapabilities(); } return clientCapabilities; } /** * Returns the list of OperationDescriptors that describe * the operations supported by the server. If any * network related errors are encountered by this method (identified * as such by receiving a RemoteImagingException), they * will be dealt with by the use of retries and retry intervals. */ public OperationDescriptor[] getServerSupportedOperationList() throws RemoteImagingException { if (descriptors == null) { // Get the RemoteDescriptor for protocolName RemoteDescriptor descriptor = (RemoteDescriptor) operationRegistry.getDescriptor(RemoteDescriptor.class, protocolName); if (descriptor == null) { Object[] msgArg0 = {new String(protocolName)}; formatter.applyPattern(JaiI18N.getString("RemoteJAI16")); throw new RuntimeException(formatter.format(msgArg0)); } Exception rieSave = null; int count = 0; while (count++ < numRetries) { try { descriptors = descriptor.getServerSupportedOperationList(serverName); break; } catch (RemoteImagingException rie) { // Print that an Exception occured System.err.println(JaiI18N.getString("RemoteJAI25")); rieSave = rie; // Sleep for retryInterval milliseconds try { Thread.sleep(retryInterval); } catch (InterruptedException ie) { // throw new ImagingException(ie); sendExceptionToListener(JaiI18N.getString("Generic5"), new ImagingException(JaiI18N.getString("Generic5"), ie)); } } } if (descriptors == null && count > numRetries) { sendExceptionToListener(JaiI18N.getString("RemoteJAI23"), rieSave); // throw new RemoteImagingException( // JaiI18N.getString("RemoteJAI23")+"\n"+rieSave.getMessage()); } // Store the descriptors into a Hashtable hashed by // their operation name. odHash = new Hashtable(); for (int i=0; i InterpolationBicubic is a subclass of Interpolation that * performs interpolation using the piecewise cubic polynomial: * *

     * r(x) = (a + 2)|x|^3 - (a + 3)|x|^2         +  1 , 0 <= |x| < 1
     * r(x) =       a|x|^3 -      5a|x|^2 + 8a|x| - 4a , 1 <= |x| < 2
     * r(x) = 0                                        , otherwise
     * 
    * * with 'a' set to -0.5. * * This definition is also sometimes known as "cubic convolution", * using the parameter 'a' recommended by Rifman. * (Reference: Digital Image Warping, George Wolberg, 1990, pp 129-131, * IEEE Computer Society Press, ISBN 0-8186-8944-7) * *

    A neighborhood extending one sample to the left of and above the * central sample, and two samples to the right of and below the central * sample is required to perform bicubic interpolation. * *

    This implementation creates an InterpolationTable * whose integer coefficients have eight bits of precision to the * right of the binary point. * *

    The diagrams below illustrate the pixels involved in one-dimensional * interpolation. Point s0 is the interpolation kernel key position. * xfrac and yfrac, indicated by the dots, represent the point of interpolation * between two pixels. This value lies between 0.0 and 1.0 exclusive for * floating point and 0 and 2subsampleBits exclusive for integer * interpolations. * *

     * 
     *         Horizontal              Vertical
     *
     *    s_    s0 .  s1    s2            s_                             
     *             ^                                                     
     *            xfrac                   s0                        
     *                                     .< yfrac                  
     *                                    s1                         
     *                                                                      
     *                                    s2                         
     * 
     * 
    * *

    The diagram below illustrates the pixels involved in * two-dimensional interpolation. * *

     * 
     *               s__    s_0    s_1    s_2                              
     *                                                                      
     *                                                                      
     *                                                                      
     *               s0_    s00    s01    s02                              
     *                                                                      
     *                          .             < yfrac                      
     *                                                                      
     *               s1_    s10    s11    s12                              
     *                                                                      
     *                                                                      
     *                                                                      
     *               s2_    s20    s21    s22                              
     *                          ^                                           
     *                         xfrac                                        
     * 
     * 
    * *

    The class is marked 'final' so that it may be more easily inlined. * * @see Interpolation */ public final class InterpolationBicubic extends InterpolationTable { private static final int PRECISION_BITS = 8; private static float[] dataHelper(int subsampleBits) { int one = 1 << subsampleBits; int arrayLength = one * 4; float tableValues[] = new float[arrayLength]; float f; float onef = (float)one; float t; int count = 0; for (int i=0; i=1) { return (((B3 * x) + B2) * x + B1) * x + B0; } else { return ((A3 * x) + A2) * x * x + A0; } } /** * Constructs an InterpolationBicubic with a given subsample * precision, in bits. This precision is applied to both axes. * *

    This implementation creates an InterpolationTable * whose integer coefficients have eight bits of precision to the * right of the binary point. * * @param subsampleBits the subsample precision. */ public InterpolationBicubic(int subsampleBits) { super(1, 1, 4, 4, subsampleBits, subsampleBits, PRECISION_BITS, dataHelper(subsampleBits), null); } } jai-core-1.1.4/src/share/classes/javax/media/jai/GraphicsJAI.java0000644000175000017500000005031010203035544024263 0ustar mathieumathieu/* * $RCSfile: GraphicsJAI.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:08 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Color; import java.awt.Component; import java.awt.Composite; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.Image; import java.awt.Paint; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.RenderingHints.Key; import java.awt.Shape; import java.awt.Stroke; import java.awt.font.FontRenderContext; import java.awt.font.GlyphVector; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.awt.image.BufferedImageOp; import java.awt.image.ImageObserver; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderableImage; import java.text.AttributedCharacterIterator; import java.util.Map; /** * A JAI wrapper for a Graphics2D object derived from a Component. * When drawing JAI images to a Component such as a Canvas, a new * GraphicsJAI may be constructed to wrap the Graphics2D object * provided by that Component. This GraphicsJAI object may provide * acceleration for calls to drawRenderedImage(), * drawRenderableImage(), and possibly other methods. * *

    If it is possible to use a CanvasJAI object instead of a * generic Canvas, or other Canvas subclass, then the Graphics objects * obtained from getGraphics() or received as an argument in paint() * will automatically be instances of GraphicsJAI. * *

    The portion of the GraphicsJAI interface that * deals with adding and retrieving new hardware-specific implementations * has not been finalized and does not appear in the current API. * * @see CanvasJAI */ public class GraphicsJAI extends Graphics2D { Graphics2D g; Component component; /** * Constructs a new instance of GraphicsJAI that * wraps a given instance of Graphics2D for drawing * to a given Component. */ protected GraphicsJAI(Graphics2D g, Component component) { this.g = g; this.component = component; } /** * Returns an instance of GraphicsJAI suitable * for rendering to the given Component via the * given Graphics2D instance. * *

    If one is available, his method will select a hardware-specific * implementation, that is specialized for the display device containing * the component. */ public static GraphicsJAI createGraphicsJAI(Graphics2D g, Component component) { return new GraphicsJAI(g, component); } /** * Creates a new GraphicsJAI object that is * a copy of this GraphicsJAI object. * * @see java.awt.Graphics#create() */ public Graphics create() { return new GraphicsJAI(g, component); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#getColor() */ public Color getColor() { return g.getColor(); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#setColor(Color) */ public void setColor(Color c) { g.setColor(c); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#setPaintMode() */ public void setPaintMode() { g.setPaintMode(); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#setXORMode(Color) */ public void setXORMode(Color c1) { g.setXORMode(c1); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#getFont */ public Font getFont() { return g.getFont(); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#setFont(Font) */ public void setFont(Font font) { g.setFont(font); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#getFontMetrics(Font) */ public FontMetrics getFontMetrics(Font f) { return g.getFontMetrics(f); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#getClipBounds */ public Rectangle getClipBounds() { return g.getClipBounds(); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#clipRect(int, int, int, int) */ public void clipRect(int x, int y, int width, int height) { g.clipRect(x, y, width, height); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#setClip(int, int, int, int) */ public void setClip(int x, int y, int width, int height) { g.setClip(x, y, width, height); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#getClip */ public Shape getClip() { return g.getClip(); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#setClip(Shape) */ public void setClip(Shape clip) { g.setClip(clip); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#copyArea(int, int, int, int, int, int) */ public void copyArea(int x, int y, int width, int height, int dx, int dy) { g.copyArea(x, y, width, height, dx, dy); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#drawLine(int, int, int, int) */ public void drawLine(int x1, int y1, int x2, int y2) { g.drawLine(x1, y1, x2, y2); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#fillRect(int, int, int, int) */ public void fillRect(int x, int y, int width, int height) { g.fillRect(x, y, width, height); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#clearRect(int, int, int, int) */ public void clearRect(int x, int y, int width, int height) { g.clearRect(x, y, width, height); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#drawRoundRect(int, int, int, int, int, int) */ public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { g.drawRoundRect(x, y, width, height, arcWidth, arcHeight); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#fillRoundRect(int, int, int, int, int, int) */ public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { g.fillRoundRect(x, y, width, height, arcWidth, arcHeight); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#drawOval(int, int, int, int) */ public void drawOval(int x, int y, int width, int height) { g.drawOval(x, y, width, height); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#fillOval(int, int, int, int) */ public void fillOval(int x, int y, int width, int height) { g.fillOval(x, y, width, height); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#drawArc(int, int, int, int, int, int) */ public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) { g.drawArc(x, y, width, height, startAngle, arcAngle); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#fillArc(int, int, int, int, int, int) */ public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) { g.fillArc(x, y, width, height, startAngle, arcAngle); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#drawPolyline(int[], int[], int) */ public void drawPolyline(int xPoints[], int yPoints[], int nPoints) { g.drawPolyline(xPoints, yPoints, nPoints); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#drawPolygon(int[], int[], int) */ public void drawPolygon(int xPoints[], int yPoints[], int nPoints) { g.drawPolygon(xPoints, yPoints, nPoints); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#fillPolygon(int[], int[], int) */ public void fillPolygon(int xPoints[], int yPoints[], int nPoints) { g.fillPolygon(xPoints, yPoints, nPoints); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#drawImage(Image, int, int, ImageObserver) */ public boolean drawImage(Image img, int x, int y, ImageObserver observer) { return g.drawImage(img, x, y, observer); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#drawImage(Image, int, int, int, int, ImageObserver) */ public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) { return g.drawImage(img, x, y, width, height, observer); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#drawImage(Image, int, int, Color, ImageObserver) */ public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) { return g.drawImage(img, x, y, bgcolor, observer); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#drawImage(Image, int, int, int, int, Color, ImageObserver) */ public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) { return g.drawImage(img, x, y, width, height, bgcolor, observer); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#drawImage(Image, int, int, int, int, int, int, int, int, ImageObserver) */ public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) { return g.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#drawImage(Image, int, int, int, int, int, int, int, int, Color, ImageObserver) */ public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer) { return g.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, bgcolor, observer); } /** * See comments in java.awt.Graphics. * * @see java.awt.Graphics#dispose */ public void dispose() { g.dispose(); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#draw(Shape) */ public void draw(Shape s) { g.draw(s); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#drawImage(Image, AffineTransform, ImageObserver) */ public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) { return g.drawImage(img, xform, obs); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#drawImage(BufferedImage, BufferedImageOp, int, int) */ public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) { g.drawImage(img, op, x, y); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#drawRenderedImage(RenderedImage, AffineTransform) */ public void drawRenderedImage(RenderedImage img, AffineTransform xform) { g.drawRenderedImage(img, xform); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#drawRenderableImage(RenderableImage, AffineTransform) */ public void drawRenderableImage(RenderableImage img, AffineTransform xform) { g.drawRenderableImage(img, xform); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#drawString(String, int, int) */ public void drawString(String str, int x, int y) { g.drawString(str, x, y); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#drawString(String, float, float) */ public void drawString(String s, float x, float y) { g.drawString(s, x, y); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#drawString(AttributedCharacterIterator, int, int) */ public void drawString(AttributedCharacterIterator iterator, int x, int y) { g.drawString(iterator, x, y); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#drawString(AttributedCharacterIterator, float, float) */ public void drawString(AttributedCharacterIterator iterator, float x, float y) { g.drawString(iterator, x, y); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#drawGlyphVector(GlyphVector, float, float) */ public void drawGlyphVector(GlyphVector g, float x, float y) { (this.g).drawGlyphVector(g, x, y); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#fill(Shape) */ public void fill(Shape s) { g.fill(s); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#hit(Rectangle, Shape, boolean) */ public boolean hit(Rectangle rect, Shape s, boolean onStroke) { return g.hit(rect, s, onStroke); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#getDeviceConfiguration */ public GraphicsConfiguration getDeviceConfiguration() { return g.getDeviceConfiguration(); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#setComposite(Composite) */ public void setComposite(Composite comp) { g.setComposite(comp); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#setPaint(Paint) */ public void setPaint(Paint paint) { g.setPaint(paint); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#setStroke(Stroke) */ public void setStroke(Stroke s) { g.setStroke(s); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#setRenderingHint(RenderingHints.Key, Object) */ public void setRenderingHint(Key hintKey, Object hintValue) { g.setRenderingHint(hintKey, hintValue); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#getRenderingHint(RenderingHints.Key) */ public Object getRenderingHint(Key hintKey) { return g.getRenderingHint(hintKey); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#setRenderingHints(Map) */ public void setRenderingHints(Map hints) { g.setRenderingHints(hints); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#addRenderingHints(Map) */ public void addRenderingHints(Map hints) { g.addRenderingHints(hints); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#getRenderingHints */ public RenderingHints getRenderingHints() { return g.getRenderingHints(); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#translate(int, int) */ public void translate(int x, int y) { g.translate(x, y); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#translate(double, double) */ public void translate(double tx, double ty) { g.translate(tx, ty); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#rotate(double) */ public void rotate(double theta) { g.rotate(theta); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#rotate(double, double, double) */ public void rotate(double theta, double x, double y) { g.rotate(theta, x, y); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#scale(double, double) */ public void scale(double sx, double sy) { g.scale(sx, sy); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#shear(double, double) */ public void shear(double shx, double shy) { g.shear(shx, shy); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#transform(AffineTransform) */ public void transform(AffineTransform Tx) { g.transform(Tx); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#setTransform(AffineTransform) */ public void setTransform(AffineTransform Tx) { g.setTransform(Tx); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#getTransform */ public AffineTransform getTransform() { return g.getTransform(); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#getPaint */ public Paint getPaint() { return g.getPaint(); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#getComposite */ public Composite getComposite() { return g.getComposite(); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#setBackground(Color) */ public void setBackground(Color color) { g.setBackground(color); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#getBackground */ public Color getBackground() { return g.getBackground(); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#getStroke */ public Stroke getStroke() { return g.getStroke(); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#clip(Shape) */ public void clip(Shape s) { g.clip(s); } /** * See comments in java.awt.Graphics2D. * * @see java.awt.Graphics2D#getFontRenderContext */ public FontRenderContext getFontRenderContext() { return g.getFontRenderContext(); } } jai-core-1.1.4/src/share/classes/javax/media/jai/DeferredData.java0000644000175000017500000001214310203035544024513 0ustar mathieumathieu/* * $RCSfile: DeferredData.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:07 $ * $State: Exp $ */ package javax.media.jai; import java.io.Serializable; import java.util.Observable; /** * Class to be used as a wrapper for data which will be calculated * at a later time. For example, an instance of a subclass of this * class may be passed as a parameter in the ParameterBlock * set on a RenderedOp node. The data wrapped by the * DeferredData object will not however actually be * requested until the node is rendered. * * @see DeferredProperty * @see RenderedOp * * @since JAI 1.1 */ public abstract class DeferredData extends Observable implements Serializable { /** * The class of the wrapped data. */ protected Class dataClass; /** * The data wrapped by this class. This field is marked * transient so that subclasses are obligated to provide * specific serialization methods if they desire that this field be * serialized. */ protected transient Object data; /** * Creates a DeferredData wrapper for an object of the * indicated class. The parameter must be non-null or an * IllegalArgumentException will be thrown. * * @throws IllegalArgumentException if dataClass is * null. */ protected DeferredData(Class dataClass) { if(dataClass == null) { throw new IllegalArgumentException(JaiI18N.getString("DeferredData0")); } this.dataClass = dataClass; } /** * Returns the class of the object wrapped by this DeferredData. */ public Class getDataClass() { return dataClass; } /** * Whether the data value has already been computed. * * @return true if and inly if the internal data value is * non-null, i.e., has already been computed. */ public boolean isValid() { return data != null; } /** * Computes the value of the data object wrapped by this object. * The returned object must be an instance of a class assignable * to the class specified at construction. */ protected abstract Object computeData(); /** * Returns the object wrapped by this DeferredData. If * the data have not yet been computed, computeData() * will be invoked to provide the data. If computeData() * is invoked, then setData() will be invoked with * its parameter set to the value returned by computeData(). */ public synchronized final Object getData() { if(data == null) { setData(computeData()); } return data; } /** * Sets the instance variable associated with the data to the supplied * parameter. If the parameter is non-null and not an * instance of the class specified at construction, an * IllegalArgumentException will be thrown. If the * supplied parameter differs from the current data value, then * setChanged() will be invoked and * notifyObservers() will be called with its argument set * to the previous value of the data object. This implies that the * update() method of any registered Observers * will be invoked with the Observable parameter set to this * DeferredData object and its Object parameter * set to the previous value of the data field of this * DeferredData instance. The current value of the * object can be retrieved by an Observer by casting the * Observable parameter of update() to * DeferredData and invoking getData(). To * avoid provoking computation of deferred data by calling * getData(), isValid() could first be called * to determine whether data is non-null. * If an Observer detects that the data of the observed * DeferredData object is invalid, i.e., null, * then this indicates that the data should be re-requested by invoking * getData(). * * @throws IllegalArgumentException if data is * non-null and not an instance of the class * specified at construction. */ protected final void setData(Object data) { if(data != null && !dataClass.isInstance(data)) { throw new IllegalArgumentException(JaiI18N.getString("DeferredData1")); } if(this.data == null || !this.data.equals(data)) { Object oldData = this.data; this.data = data; setChanged(); notifyObservers(oldData); } } } jai-core-1.1.4/src/share/classes/javax/media/jai/ColorCube.java0000644000175000017500000007553210203035544024071 0ustar mathieumathieu/* * $RCSfile: ColorCube.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:06 $ * $State: Exp $ */ package javax.media.jai; import java.awt.image.DataBuffer; import javax.media.jai.LookupTableJAI; /** * A subclass of LookupTableJAI which represents a lookup * table which is a color cube. A color cube provides a fixed, * invertible mapping between table indices and sample values. * This allows the findNearestEntry method to be implemented * more efficiently than in the general case. * *

    All constructors are protected. The correct way to create a * ColorCube is to use one of the static * create methods defined in this class. * * @see javax.media.jai.LookupTableJAI * */ public class ColorCube extends LookupTableJAI { /** * A ColorCube for dithering RGB byte data into 216 colors. * The offset of this ColorCube is 38. */ public static final ColorCube BYTE_496 = ColorCube.createColorCube(DataBuffer.TYPE_BYTE, 38, new int[] {4, 9, 6}); /** * A ColorCube for dithering YCC byte data into 200 colors. * The offset of this ColorCube is 54. */ public static final ColorCube BYTE_855 = ColorCube.createColorCube(DataBuffer.TYPE_BYTE, 54, new int[] {8, 5, 5}); /** The signed array of sizes used to create the ColorCube. */ private int[] dimension; /** * An array of positive values each of whose elements is one less than the * absolute value of the corresponding element of the dimension array. */ private int[] dimsLessOne; /** * An array of multipliers. * *

    The magnitudes of the elements of the multiplier array are * defined as multipliers[0] = 1 and * multipliers[i] = * multipliers[i-1]*Math.abs(dimension[i-1]) where i * > 0. The elements are subsequently assigned the same * sign (positive or negative) as the corresponding elements of * the dimension array. */ private int[] multipliers; /** * An offset into the lookup table, accounting for negative dimensions. */ private int adjustedOffset; /** * The data type cached to accelerate findNearestEntry(). */ private int dataType; /** * The number of bands cached to accelerate findNearestEntry(). */ private int numBands; /** * Returns a multi-banded ColorCube of a specified data type. * * @param dataType The data type of the ColorCube, * one of DataBuffer.TYPE_BYTE, * TYPE_SHORT, * TYPE_USHORT, * TYPE_INT, * TYPE_FLOAT, * or TYPE_DOUBLE. * @param offset The common offset for all bands. * @param dimension The signed dimension of each band. * * @throws RuntimeExceptions for unsupported data types * @return An appropriate ColorCube. */ public static ColorCube createColorCube(int dataType, int offset, int dimension[]) { ColorCube colorCube; switch(dataType) { case DataBuffer.TYPE_BYTE: colorCube = createColorCubeByte(offset, dimension); break; case DataBuffer.TYPE_SHORT: colorCube = createColorCubeShort(offset, dimension); break; case DataBuffer.TYPE_USHORT: colorCube = createColorCubeUShort(offset, dimension); break; case DataBuffer.TYPE_INT: colorCube = createColorCubeInt(offset, dimension); break; case DataBuffer.TYPE_FLOAT: colorCube = createColorCubeFloat(offset, dimension); break; case DataBuffer.TYPE_DOUBLE: colorCube = createColorCubeDouble(offset, dimension); break; default: throw new RuntimeException(JaiI18N.getString("ColorCube0")); } return colorCube; } /** * Returns a multi-banded ColorCube of a specified * data type with zero offset for all bands. * * @param dataType The data type of the ColorCube. * @param dimension The signed dimension of each band. * * @throws IllegalArgumentException if dimension is null. * @return An appropriate ColorCube. */ public static ColorCube createColorCube(int dataType, int dimension[]) { if ( dimension == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return createColorCube(dataType, 0, dimension); } /** * Returns a multi-banded byte ColorCube with an index * offset common to all bands. * * @param offset The common offset for all bands. * @param dimension An array of signed sizes of each side of the color * cube. The color ramp in each dimension will be increasing or decreasing * according to whether the sign of the corresponding element of the * dimension array is positive or negative, respectively. * * @return A multi-banded byte ColorCube with offset. */ private static ColorCube createColorCubeByte(int offset, int dimension[]) { ColorCube colorCube = new ColorCube(createDataArrayByte(offset, dimension), offset); colorCube.initFields(offset, dimension); return colorCube; } /** * Returns a multi-banded short ColorCube with an index * offset common to all bands. * * @param offset The common offset for all bands. * @param dimension An array of signed sizes of each side of the color * cube. The color ramp in each dimension will be increasing or decreasing * according to whether the sign of the corresponding element of the * dimension array is positive or negative, respectively. * * @return A multi-banded short ColorCube with offset. */ private static ColorCube createColorCubeShort(int offset, int dimension[]) { ColorCube colorCube = new ColorCube(createDataArrayShort(offset, dimension), offset, false); colorCube.initFields(offset, dimension); return colorCube; } /** * Returns a multi-banded unsigned short ColorCube with an * index offset common to all bands. * * @param offset The common offset for all bands. * @param dimension An array of signed sizes of each side of the color * cube. The color ramp in each dimension will be increasing or decreasing * according to whether the sign of the corresponding element of the * dimension array is positive or negative, respectively. * * @return A multi-banded unsigned short ColorCube with * offset. */ private static ColorCube createColorCubeUShort(int offset, int dimension[]) { ColorCube colorCube = new ColorCube(createDataArrayUShort(offset, dimension), offset, true); colorCube.initFields(offset, dimension); return colorCube; } /** * Returns a multi-banded int ColorCube with an index * offset common to all bands. * * @param offset The common offset for all bands. * @param dimension An array of signed sizes of each side of the color * cube. The color ramp in each dimension will be increasing or decreasing * according to whether the sign of the corresponding element of the * dimension array is positive or negative, respectively. * * @return A multi-banded int ColorCube with offset. */ private static ColorCube createColorCubeInt(int offset, int dimension[]) { ColorCube colorCube = new ColorCube(createDataArrayInt(offset, dimension), offset); colorCube.initFields(offset, dimension); return colorCube; } /** * Returns a multi-banded float ColorCube with an index * offset common to all bands. * * @param offset The common offset for all bands. * @param dimension An array of signed sizes of each side of the color * cube. The color ramp in each dimension will be increasing or decreasing * according to whether the sign of the corresponding element of the * dimension array is positive or negative, respectively. * * @return A multi-banded float ColorCube with offset. */ private static ColorCube createColorCubeFloat(int offset, int dimension[]) { ColorCube colorCube = new ColorCube(createDataArrayFloat(offset, dimension), offset); colorCube.initFields(offset, dimension); return colorCube; } /** * Returns a multi-banded double ColorCube with an index * offset common to all bands. * * @param offset The common offset for all bands. * @param dimension An array of signed sizes of each side of the color * cube. The color ramp in each dimension will be increasing or decreasing * according to whether the sign of the corresponding element of the * dimension array is positive or negative, respectively. * * @return A multi-banded double ColorCube. */ private static ColorCube createColorCubeDouble(int offset, int dimension[]) { ColorCube colorCube = new ColorCube(createDataArrayDouble(offset, dimension), offset); colorCube.initFields(offset, dimension); return colorCube; } /** * Constructs a two-dimensional array of the requested data type which * represents the contents of a color cube. * * @param dataType The data type as defined by the static TYPE fields of * DataBuffer, e.g., DataBuffer.TYPE_BYTE. * @param offset The initial offset into the data array. * @param dimension An array of signed sizes of each side of the color * cube. The color ramp in each dimension will be increasing or decreasing * according to whether the sign of the corresponding element of the * dimension array is positive or negative, respectively. * * @return A two-dimensional array of the requested data type laid * out in color cube format. * * @throws RuntimeException for data types other than * DataBuffer.TYPE_BYTE DataBuffer.TYPE_USHORT * DataBuffer.TYPE_SHORT DataBuffer.TYPE_INT * DataBuffer.TYPE_FLOAT DataBuffer.TYPE_DOUBLE * @see java.awt.image.DataBuffer */ private static Object createDataArray(int dataType, int offset, int dimension[]) { // Make sure that the dimension array has non-zero length. int nbands = dimension.length; if (nbands == 0) { throw new RuntimeException(JaiI18N.getString("ColorCube1")); } // Ascertain that all dimension are non-zero. for (int band = 0; band < nbands; band++) { if (dimension[band] == 0) { throw new RuntimeException(JaiI18N.getString("ColorCube2")); } } // Copy the dimension into an array of dimension magnitudes. int[] dimensionAbs = new int[nbands]; for (int band = 0; band < nbands; band++) { dimensionAbs[band] = Math.abs(dimension[band]); } // Check that the color cube is not too large for the machine. double floatSize = dimensionAbs[0]; for (int band = 1; band < nbands; band++) { floatSize *= (double)dimensionAbs[band]; } if (floatSize > (double)Integer.MAX_VALUE) { // // Color cube is too large for 32 bit addressability // throw new RuntimeException(JaiI18N.getString("ColorCube3")); } int size = (int)floatSize; // Initialize the data array and extrema for this data type. double dataMin; double dataMax; Object dataArray; switch(dataType) { case DataBuffer.TYPE_BYTE: dataMin = 0.0; dataMax = 255.0; dataArray = (Object)new byte[nbands][size]; break; case DataBuffer.TYPE_SHORT: dataMin = Short.MIN_VALUE; dataMax = Short.MAX_VALUE; dataArray = (Object)new short[nbands][size]; break; case DataBuffer.TYPE_USHORT: dataMin = 0; dataMax = 65535; dataArray = (Object)new short[nbands][size]; break; case DataBuffer.TYPE_INT: dataMin = Integer.MIN_VALUE; dataMax = Integer.MAX_VALUE; dataArray = (Object)new int[nbands][size]; break; case DataBuffer.TYPE_FLOAT: dataMin = -Float.MAX_VALUE; dataMax = Float.MAX_VALUE; dataArray = (Object)new float[nbands][size]; break; case DataBuffer.TYPE_DOUBLE: dataMin = -Double.MAX_VALUE; dataMax = Double.MAX_VALUE; dataArray = (Object)new double[nbands][size]; break; default: throw new RuntimeException(JaiI18N.getString("ColorCube7")); } // Ensure that the parameters don't go out of range. if ((double)(size + offset) > dataMax) { throw new RuntimeException(JaiI18N.getString("ColorCube4")); } // Initialize the multipliers int[] multipliers = new int[nbands]; multipliers[0] = 1; for (int band = 1; band < nbands; band++) { multipliers[band] = multipliers[band-1]*dimensionAbs[band-1]; } // Populate each band of the lookup table data. for (int band = 0; band < nbands; band++) { // Determine the step size for this band. int idimension = dimensionAbs[band]; double delta; if (idimension == 1) { // A dimension of one means all entries will be the same delta = 0.0; } else if (dataType == DataBuffer.TYPE_FLOAT || dataType == DataBuffer.TYPE_DOUBLE) { delta = 1.0/(idimension - 1); } else { delta = (dataMax - dataMin)/(idimension - 1); } // Set the starting value and index step. double start; if (dimension[band] < 0) { delta = -delta; start = dataMax; } else { start = dataMin; } int repeatCount = multipliers[band]; // Load the actual lookup table values for this band int index; switch(dataType) { case DataBuffer.TYPE_BYTE: byte[][] byteData = (byte[][])dataArray; index = 0; while (index < size) { double val = start; for (int i = 0; i < idimension; i++) { for (int j = 0; j < repeatCount; j++) { byteData[band][index] = (byte)((int)(val + 0.5) & 0x000000ff); index++; } val += delta; } } break; case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_USHORT: short[][] shortData = (short[][])dataArray; index = 0; while (index < size) { double val = start; for (int i = 0; i < idimension; i++) { for (int j = 0; j < repeatCount; j++) { shortData[band][index] = (short)(val + 0.5); index++; } val += delta; } } break; case DataBuffer.TYPE_INT: int[][] intData = (int[][])dataArray; index = 0; while (index < size) { double val = start; for (int i = 0; i < idimension; i++) { for (int j = 0; j < repeatCount; j++) { intData[band][index] = (int)(val + 0.5); index++; } val += delta; } } break; case DataBuffer.TYPE_FLOAT: float[][] floatData = (float[][])dataArray; index = 0; while (index < size) { double val = start; for (int i = 0; i < idimension; i++) { for (int j = 0; j < repeatCount; j++) { floatData[band][index] = (float)val; index++; } val += delta; } } break; case DataBuffer.TYPE_DOUBLE: double[][] doubleData = (double[][])dataArray; index = 0; while (index < size) { double val = start; for (int i = 0; i < idimension; i++) { for (int j = 0; j < repeatCount; j++) { doubleData[band][index] = val; index++; } val += delta; } } break; default: throw new RuntimeException(JaiI18N.getString("ColorCube5")); } } return dataArray; } /** * Constructs a two-dimensional array of byte data which represent the * contents of a color cube. * * @param offset The initial offset into the data array. * @param dimension An array of signed sizes of each side of the color * cube. The color ramp in each dimension will be increasing or decreasing * according to whether the sign of the corresponding element of the * dimension array is positive or negative, respectively. * * @return A two-dimensional byte array of color cube data. */ private static byte[][] createDataArrayByte(int offset, int dimension[]) { return (byte[][])createDataArray(DataBuffer.TYPE_BYTE, offset, dimension); } /** * Constructs a two-dimensional array of short data which represent the * contents of a color cube. * * @param offset The initial offset into the data array. * @param dimension an array of signed sizes of each side of the color * cube. The color ramp in each dimension will be increasing or decreasing * according to whether the sign of the corresponding element of the * dimension array is positive or negative, respectively. * * @return A two-dimensional short array of color cube data. */ private static short[][] createDataArrayShort(int offset, int dimension[]) { return (short[][])createDataArray(DataBuffer.TYPE_SHORT, offset, dimension); } /** * Constructs a two-dimensional array of unsigned short data which * represent the contents of a color cube. * * @param offset The initial offset into the data array. * @param dimension an array of signed sizes of each side of the color * cube. The color ramp in each dimension will be increasing or decreasing * according to whether the sign of the corresponding element of the * dimension array is positive or negative, respectively. * * @return A two-dimensional short array of color cube data. */ private static short[][] createDataArrayUShort(int offset, int dimension[]) { return (short[][])createDataArray(DataBuffer.TYPE_USHORT, offset, dimension); } /** * Constructs a two-dimensional array of int data which represent the * contents of a color cube. * * @param offset The initial offset into the data array. * @param dimension an array of signed sizes of each side of the color * cube. The color ramp in each dimension will be increasing or decreasing * according to whether the sign of the corresponding element of the * dimension array is positive or negative, respectively. * * @return A two-dimensional int array of color cube data. */ private static int[][] createDataArrayInt(int offset, int dimension[]) { return (int[][])createDataArray(DataBuffer.TYPE_INT, offset, dimension); } /** * Constructs a two-dimensional array of float data which represent the * contents of a color cube. * * @param offset The initial offset into the data array. * @param dimension an array of signed sizes of each side of the color * cube. The color ramp in each dimension will be increasing or decreasing * according to whether the sign of the corresponding element of the * dimension array is positive or negative, respectively. * * @return A two-dimensional float array of color cube data. */ private static float[][] createDataArrayFloat(int offset, int dimension[]) { return (float[][])createDataArray(DataBuffer.TYPE_FLOAT, offset, dimension); } /** * Constructs a two-dimensional array of double data which represent the * contents of a color cube. * * @param offset The initial offset into the data array. * @param dimension an array of signed sizes of each side of the color * cube. The color ramp in each dimension will be increasing or decreasing * according to whether the sign of the corresponding element of the * dimension array is positive or negative, respectively. * * @return A two-dimensional double array of color cube data. */ private static double[][] createDataArrayDouble(int offset, int dimension[]) { return (double[][])createDataArray(DataBuffer.TYPE_DOUBLE, offset, dimension); } /** * Returns a multi-banded byte ColorCube with an index * offset common to all bands. * * @param data The multi-banded byte data in [band][index] format. * @param offset The common offset for all bands. * * @throws IllegalArgumentException if data is null. */ protected ColorCube(byte data[][], int offset) { super(data, offset); } /** * Returns a multi-banded short or unsigned short ColorCube * with an index offset common to all bands. * * @param data The multi-banded short data in [band][index] format. * @param offset The common offset for all bands. * @param isUShort True if data type is DataBuffer.TYPE_USHORT; * false if data type is DataBuffer.TYPE_SHORT. * * @throws IllegalArgumentException if data is null. */ protected ColorCube(short data[][], int offset, boolean isUShort) { super(data, offset, isUShort); } /** * Returns a multi-banded int ColorCube with an index * offset common to all bands. * * @param data The multi-banded int data in [band][index] format. * @param offset The common offset for all bands. * * @throws IllegalArgumentException if data is null. */ protected ColorCube(int data[][], int offset) { super(data, offset); } /** * Returns a multi-banded float ColorCube with an index * offset common to all bands. * * @param data The multi-banded float data in [band][index] format. * @param offset The common offset for all bands. * * @throws IllegalArgumentException if data is null. */ protected ColorCube(float data[][], int offset) { super(data, offset); } /** * Returns a multi-banded double ColorCube with an index * offset common to all bands. * * @param data The multi-banded double data in [band][index] format. * @param offset The common offset for all bands. * * @throws IllegalArgumentException if data is null. */ protected ColorCube(double data[][], int offset) { super(data, offset); } /** * Initialize the fields of a ColorCube. * * @param offset The common offset for all bands. * @param dimension The signed dimension for each band. */ private void initFields(int offset, int[] dimension) { // Save a reference to the dimension array. this.dimension = dimension; // Allocate memory multipliers = new int[dimension.length]; dimsLessOne = new int[dimension.length]; // Calculate multiplier magnitudes. multipliers[0] = 1; for (int i = 1; i < multipliers.length; i++) { multipliers[i] = multipliers[i-1]*Math.abs(dimension[i-1]); } // Set multiplier signs and initialize dimsLessOne. for (int i = 0; i < multipliers.length; i++) { if (dimension[i] < 0) { multipliers[i] = -multipliers[i]; } dimsLessOne[i] = Math.abs(dimension[i]) - 1; } // Calculate adjusted offset. adjustedOffset = offset; for (int i = 0; i < dimension.length; i++) { if (dimension[i] > 1 && multipliers[i] < 0) { adjustedOffset += Math.abs(multipliers[i]) * dimsLessOne[i]; } } // Cache the data type and number of bands to avoid repetitive calls // in findNearestEntry(). dataType = getDataType(); numBands = getNumBands(); } /** * Returns the array of signed dimensions used to construct the * ColorCube. * */ public int[] getDimension() { return dimension; } /** * Returns an array containing the signed dimensions, less one. * */ public int[] getDimsLessOne() { return dimsLessOne; } /** * Returns the multipliers as an array. * */ public int[] getMultipliers() { return multipliers; } /** * Returns the adjusted offset into the lookup table, accounting for * negative dimensions. * */ public int getAdjustedOffset() { return adjustedOffset; } /** * Finds the index of the nearest color in the color map to the * pixel value argument. * * @param pixel a float array of all samples of a pixel. * @return the index of the nearest color. * * @throws RuntimeException for dataTypes other than * DataBuffer.TYPE_BYTE DataBuffer.TYPE_USHORT * DataBuffer.TYPE_SHORT DataBuffer.TYPE_INT * DataBuffer.TYPE_FLOAT DataBuffer.TYPE_DOUBLE */ public int findNearestEntry(float[] pixel) { int index = -1; index = adjustedOffset; switch(dataType) { case DataBuffer.TYPE_BYTE: for (int band = 0; band < numBands; band++) { int tmp = (int)(pixel[band] * dimsLessOne[band]); if ((tmp & 0xFF) > 127) { tmp += (int)0x100; } index += (tmp>>8) * multipliers[band]; } break; case DataBuffer.TYPE_SHORT: for (int band = 0; band < numBands; band++) { int tmp = (int)(pixel[band] - Short.MIN_VALUE)*dimsLessOne[band]; if ((tmp & 0xFFFF) > Short.MAX_VALUE) { tmp += (int)0x10000; } index += (tmp>>16) * multipliers[band]; } break; case DataBuffer.TYPE_USHORT: for (int band = 0; band < numBands; band++) { int tmp = (int)(pixel[band] * dimsLessOne[band]); if ((tmp & 0xFFFF) > Short.MAX_VALUE) { tmp += (int)0x10000; } index += (tmp>>16) * multipliers[band]; } break; case DataBuffer.TYPE_INT: for (int band = 0; band < numBands; band++) { long tmp = (long)((pixel[band]-Integer.MIN_VALUE)*dimsLessOne[band]); if (tmp > Integer.MAX_VALUE) { tmp += ((long)0xffffffff + 1L); } index += ((int)(tmp>>32)) * multipliers[band]; } break; case DataBuffer.TYPE_FLOAT: for (int band = 0; band < numBands; band++) { float ftmp = pixel[band] * dimsLessOne[band]; int itmp = (int)ftmp; if ((ftmp - itmp) >= 0.5F) { itmp++; } index += itmp * multipliers[band]; } break; case DataBuffer.TYPE_DOUBLE: for (int band = 0; band < numBands; band++) { double ftmp = pixel[band] * dimsLessOne[band]; int itmp = (int)ftmp; if ((ftmp - itmp) >= 0.5) { itmp++; } index += itmp * multipliers[band]; } break; default: throw new RuntimeException(JaiI18N.getString("ColorCube6")); } return index; } } jai-core-1.1.4/src/share/classes/javax/media/jai/ImageLayout.java0000644000175000017500000007046310203035544024432 0ustar mathieumathieu/* * $RCSfile: ImageLayout.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:09 $ * $State: Exp $ */ package javax.media.jai; import java.awt.image.ColorModel; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import javax.media.jai.remote.SerializableState; import javax.media.jai.remote.SerializerFactory; /** * A class describing the desired layout of an OpImage. * *

    The ImageLayout class encapsulates three types of information about * an image: * *

      *
    • The image bounds, comprising the min X and Y coordinates, * image width, and image height; *
    • The tile grid layout, comprising the tile grid X and Y offsets, * the tile width, and the tile height; and *
    • The SampleModel and ColorModel of the image. *
    * *

    Each of these parameters may be set individually, or left unset. * An unset parameter will cause the corresponding value of a given * RenderedImage to be used. For example, the code: * *

     * ImageLayout layout;
     * RenderedImage im;
     *
     * int width = layout.getTileWidth(im);
     * 
    * * will return the tile width of the ImageLayout if it is set, * or the tile width of the image im if it is not. * *

    ImageLayout objects are primarily intended to be passed as part * of the renderingHints argument of the create() method of * RenderedImageFactory. The create() method may remove parameter * settings that it cannot deal with, prior to passing the * ImageLayout to any OpImage constructors. New OpImage subclasses * are not required to accept an ImageLayout parameter, but most will * at least need to synthesize one to be passed up the constructor * chain. * *

    Methods that modify the state of an ImageLayout return a reference * to 'this' following the change. This allows multiple modifications to * be made in a single expression. This provides a way of modifying an * ImageLayout within a superclass constructor call. * */ public class ImageLayout extends Object implements Cloneable, Serializable { /** A bitmask to specify the validity of minX. */ public static final int MIN_X_MASK = 0x1; /** A bitmask to specify the validity of minY. */ public static final int MIN_Y_MASK = 0x2; /** A bitmask to specify the validity of width. */ public static final int WIDTH_MASK = 0x4; /** A bitmask to specify the validity of height. */ public static final int HEIGHT_MASK = 0x8; /** A bitmask to specify the validity of tileGridXOffset. */ public static final int TILE_GRID_X_OFFSET_MASK = 0x10; /** A bitmask to specify the validity of tileGridYOffset. */ public static final int TILE_GRID_Y_OFFSET_MASK = 0x20; /** A bitmask to specify the validity of tileWidth. */ public static final int TILE_WIDTH_MASK = 0x40; /** A bitmask to specify the validity of tileHeight. */ public static final int TILE_HEIGHT_MASK = 0x80; /** A bitmask to specify the validity of sampleModel. */ public static final int SAMPLE_MODEL_MASK = 0x100; /** A bitmask to specify the validity of colorModel. */ public static final int COLOR_MODEL_MASK = 0x200; /** The image's minimum X coordinate. */ int minX = 0; /** The image's minimum Y coordinate. */ int minY = 0; /** The image's width. */ int width = 0; /** The image's height. */ int height = 0; /** The X coordinate of tile (0, 0). */ int tileGridXOffset = 0; /** The Y coordinate of tile (0, 0). */ int tileGridYOffset = 0; /** The width of a tile. */ int tileWidth = 0; /** The height of a tile. */ int tileHeight = 0; /** The image's SampleModel. */ transient SampleModel sampleModel = null; /** The image's ColorModel. */ transient ColorModel colorModel = null; /** The 'or'-ed together valid bitmasks. */ protected int validMask = 0; /** Constructs an ImageLayout with no parameters set. */ public ImageLayout() {} /** * Constructs an ImageLayout with all its parameters set. * The sampleModel and colorModel parameters are ignored if null. * * @param minX the image's minimum X coordinate. * @param minY the image's minimum Y coordinate. * @param width the image's width. * @param height the image's height. * @param tileGridXOffset the X coordinate of tile (0, 0). * @param tileGridYOffset the Y coordinate of tile (0, 0). * @param tileWidth the width of a tile. * @param tileHeight the height of a tile. * @param sampleModel the image's SampleModel. * @param colorModel the image's ColorModel. */ public ImageLayout(int minX, int minY, int width, int height, int tileGridXOffset, int tileGridYOffset, int tileWidth, int tileHeight, SampleModel sampleModel, ColorModel colorModel) { setMinX(minX); setMinY(minY); setWidth(width); setHeight(height); setTileGridXOffset(tileGridXOffset); setTileGridYOffset(tileGridYOffset); setTileWidth(tileWidth); setTileHeight(tileHeight); if (sampleModel != null) { setSampleModel(sampleModel); } if (colorModel != null) { setColorModel(colorModel); } } /** * Constructs an ImageLayout with only the image dimension * parameters set. * * @param minX the image's minimum X coordinate. * @param minY the image's minimum Y coordinate. * @param width the image's width. * @param height the image's height. */ public ImageLayout(int minX, int minY, int width, int height) { setMinX(minX); setMinY(minY); setWidth(width); setHeight(height); } /** * Constructs an ImageLayout with its tile grid layout, * SampleModel, and ColorModel parameters set. * The sampleModel and colorModel parameters are ignored if null. * * @param tileGridXOffset the X coordinate of tile (0, 0). * @param tileGridYOffset the Y coordinate of tile (0, 0). * @param tileWidth the width of a tile. * @param tileHeight the height of a tile. * @param sampleModel the image's SampleModel. * @param colorModel the image's ColorModel. */ public ImageLayout(int tileGridXOffset, int tileGridYOffset, int tileWidth, int tileHeight, SampleModel sampleModel, ColorModel colorModel) { setTileGridXOffset(tileGridXOffset); setTileGridYOffset(tileGridYOffset); setTileWidth(tileWidth); setTileHeight(tileHeight); if (sampleModel != null) { setSampleModel(sampleModel); } if (colorModel != null) { setColorModel(colorModel); } } /** * Constructs an ImageLayout with all its parameters set * to equal those of a given RenderedImage. * * @param im a RenderedImage whose layout will be copied. */ public ImageLayout(RenderedImage im) { this(im.getMinX(), im.getMinY(), im.getWidth(), im.getHeight(), im.getTileGridXOffset(), im.getTileGridYOffset(), im.getTileWidth(), im.getTileHeight(), im.getSampleModel(), im.getColorModel()); } /** * Returns the 'or'-ed together bitmask indicating parameter validity. * To determine the validity of a particular parameter, say tile width, * test getValidMask() & ImageLayout.TILE_WIDTH_MASK * against 0. * *

    To test a single mask value or set of mask values, the * convenience method isValid() may be used. * * @return an int that is the logical 'or' of the valid mask values, * with a '1' bit representing the setting of a value. */ public int getValidMask() { return validMask; } /** * Returns true if all the parameters specified by the argument are set. * * @param mask a bitmask. * @return a boolean truth value. */ public final boolean isValid(int mask) { return (validMask & mask) == mask; } /** * Sets selected bits of the valid bitmask. The valid bitmask is * set to the logical 'or' of its prior value and a new value. * * @param mask the new mask value to be 'or'-ed with the prior value. * @return a reference to this ImageLayout following the change. */ public ImageLayout setValid(int mask) { validMask |= mask; return this; } /** * Clears selected bits of the valid bitmask. The valid bitmask * is set to the logical 'and' of its prior value and the negation * of the new mask value. This effectively subtracts from the set of * valid parameters. * * @param mask the new mask value to be negated and 'and'-ed with * the prior value. * @return a reference to this ImageLayout following the change. */ public ImageLayout unsetValid(int mask) { validMask &= ~mask; return this; } /** * Marks the parameters dealing with the image bounds * (minX, minY, width, and height) as being invalid. * * @return a reference to this ImageLayout following the change. */ public ImageLayout unsetImageBounds() { unsetValid(MIN_X_MASK | MIN_Y_MASK | WIDTH_MASK | HEIGHT_MASK); return this; } /** * Marks the parameters dealing with the tile layout (tileGridXOffset, * tileGridYOffset, tileWidth, and tileHeight) as being invalid. * * @return a reference to this ImageLayout following the change. */ public ImageLayout unsetTileLayout() { unsetValid(TILE_GRID_X_OFFSET_MASK | TILE_GRID_Y_OFFSET_MASK | TILE_WIDTH_MASK | TILE_HEIGHT_MASK); return this; } /** * Returns the value of minX if it is valid, and * otherwise returns the value from the supplied RenderedImage. * If minX is not valid and fallback is null, 0 is returned. * * @param fallback the RenderedImage fallback. * @return the appropriate value of minX. */ public int getMinX(RenderedImage fallback) { if (isValid(MIN_X_MASK)) { return minX; } else { if (fallback == null) { return 0; } else { return fallback.getMinX(); } } } /** * Sets minX to the supplied value and marks it as valid. * * @param minX the minimum X coordinate of the image, as an int. * @return a reference to this ImageLayout following the change. */ public ImageLayout setMinX(int minX) { this.minX = minX; setValid(MIN_X_MASK); return this; } /** * Returns the value of minY if it is valid, and * otherwise returns the value from the supplied RenderedImage. * If minY is not valid and fallback is null, 0 is returned. * * @param fallback the RenderedImage fallback. * @return the appropriate value of minY. */ public int getMinY(RenderedImage fallback) { if (isValid(MIN_Y_MASK)) { return minY; } else { if (fallback == null) { return 0; } else { return fallback.getMinY(); } } } /** * Sets minY to the supplied value and marks it as valid. * * @param minY the minimum Y coordinate of the image, as an int. * @return a reference to this ImageLayout following the change. */ public ImageLayout setMinY(int minY) { this.minY = minY; setValid(MIN_Y_MASK); return this; } /** * Returns the value of width if it is valid, and * otherwise returns the value from the supplied RenderedImage. * If width is not valid and fallback is null, 0 is returned. * * @param fallback the RenderedImage fallback. * @return the appropriate value of width. */ public int getWidth(RenderedImage fallback) { if (isValid(WIDTH_MASK)) { return width; } else { if (fallback == null) { return 0; } else { return fallback.getWidth(); } } } /** * Sets width to the supplied value and marks it as valid. * * @param width the width of the image, as an int. * @return a reference to this ImageLayout following the change. * @throws IllegalArgumentException if width is non-positive. */ public ImageLayout setWidth(int width) { if(width <= 0) { throw new IllegalArgumentException(JaiI18N.getString("ImageLayout0")); } this.width = width; setValid(WIDTH_MASK); return this; } /** * Returns the value of height if it is valid, and * otherwise returns the value from the supplied RenderedImage. * If height is not valid and fallback is null, 0 is returned. * * @param fallback the RenderedImage fallback. * @return the appropriate value of height. */ public int getHeight(RenderedImage fallback) { if (isValid(HEIGHT_MASK)) { return height; } else { if (fallback == null) { return 0; } else { return fallback.getHeight(); } } } /** * Sets height to the supplied value and marks it as valid. * * @param height the height of the image, as an int. * @return a reference to this ImageLayout following the change. * @throws IllegalArgumentException if height is non-positive. */ public ImageLayout setHeight(int height) { if(height <= 0) { throw new IllegalArgumentException(JaiI18N.getString("ImageLayout0")); } this.height = height; setValid(HEIGHT_MASK); return this; } /** * Returns the value of tileGridXOffset if it is valid, and * otherwise returns the value from the supplied RenderedImage. * If tileGridXOffset is not valid and fallback is null, 0 is returned. * * @param fallback the RenderedImage fallback. * @return the appropriate value of tileGridXOffset. */ public int getTileGridXOffset(RenderedImage fallback) { if (isValid(TILE_GRID_X_OFFSET_MASK)) { return tileGridXOffset; } else { if (fallback == null) { return 0; } else { return fallback.getTileGridXOffset(); } } } /** * Sets tileGridXOffset to the supplied value and marks it as valid. * * @param tileGridXOffset the X coordinate of tile (0, 0), as an int. * @return a reference to this ImageLayout following the change. */ public ImageLayout setTileGridXOffset(int tileGridXOffset) { this.tileGridXOffset = tileGridXOffset; setValid(TILE_GRID_X_OFFSET_MASK); return this; } /** * Returns the value of tileGridYOffset if it is valid, and * otherwise returns the value from the supplied RenderedImage. * If tileGridYOffset is not valid and fallback is null, 0 is returned. * * @param fallback the RenderedImage fallback. * @return the appropriate value of tileGridYOffset. */ public int getTileGridYOffset(RenderedImage fallback) { if (isValid(TILE_GRID_Y_OFFSET_MASK)) { return tileGridYOffset; } else { if (fallback == null) { return 0; } else { return fallback.getTileGridYOffset(); } } } /** * Sets tileGridYOffset to the supplied value and marks it as valid. * * @param tileGridYOffset the Y coordinate of tile (0, 0), as an int. * @return a reference to this ImageLayout following the change. */ public ImageLayout setTileGridYOffset(int tileGridYOffset) { this.tileGridYOffset = tileGridYOffset; setValid(TILE_GRID_Y_OFFSET_MASK); return this; } /** * Returns the value of tileWidth if it is valid, and * otherwise returns the value from the supplied RenderedImage. * If tileWidth is not valid and fallback is null, 0 is returned. * * @param fallback the RenderedImage fallback. * @return the appropriate value of tileWidth. */ public int getTileWidth(RenderedImage fallback) { if (isValid(TILE_WIDTH_MASK)) { return tileWidth; } else { if (fallback == null) { return 0; } else { return fallback.getTileWidth(); } } } /** * Sets tileWidth to the supplied value and marks it as valid. * * @param tileWidth the width of a tile, as an int. * @return a reference to this ImageLayout following the change. * @throws IllegalArgumentException if tileWidth is * non-positive. */ public ImageLayout setTileWidth(int tileWidth) { if(tileWidth <= 0) { throw new IllegalArgumentException(JaiI18N.getString("ImageLayout0")); } this.tileWidth = tileWidth; setValid(TILE_WIDTH_MASK); return this; } /** * Returns the value of tileHeight if it is valid, and * otherwise returns the value from the supplied RenderedImage. * If tileHeight is not valid and fallback is null, 0 is returned. * * @param fallback the RenderedImage fallback. * @return the appropriate value of tileHeight. */ public int getTileHeight(RenderedImage fallback) { if (isValid(TILE_HEIGHT_MASK)) { return tileHeight; } else { if (fallback == null) { return 0; } else { return fallback.getTileHeight(); } } } /** * Sets tileHeight to the supplied value and marks it as valid. * * @param tileHeight the height of a tile, as an int. * @return a reference to this ImageLayout following the change. * @throws IllegalArgumentException if tileHeight is * non-positive. */ public ImageLayout setTileHeight(int tileHeight) { if(tileHeight <= 0) { throw new IllegalArgumentException(JaiI18N.getString("ImageLayout0")); } this.tileHeight = tileHeight; setValid(TILE_HEIGHT_MASK); return this; } /** * Returns the value of sampleModel if it is valid, and * otherwise returns the value from the supplied RenderedImage. * If sampleModel is not valid and fallback is null, null is returned. * * @param fallback the RenderedImage fallback. * @return the appropriate value of sampleModel. */ public SampleModel getSampleModel(RenderedImage fallback) { if (isValid(SAMPLE_MODEL_MASK)) { return sampleModel; } else { if (fallback == null) { return null; } else { return fallback.getSampleModel(); } } } /** * Sets sampleModel to the supplied value and marks it as valid. * * @param sampleModel the new SampleModel. * @return a reference to this ImageLayout following the change. */ public ImageLayout setSampleModel(SampleModel sampleModel) { this.sampleModel = sampleModel; setValid(SAMPLE_MODEL_MASK); return this; } /** * Returns the value of colorModel if it is valid, and * otherwise returns the value from the supplied RenderedImage. * If colorModel is not valid and fallback is null, null is returned. * * @param fallback the RenderedImage fallback. * @return the appropriate value of colorModel. */ public ColorModel getColorModel(RenderedImage fallback) { if (isValid(COLOR_MODEL_MASK)) { return colorModel; } else { if (fallback == null) { return null; } else { return fallback.getColorModel(); } } } /** * Sets colorModel to the supplied value and marks it as valid. * * @param colorModel the new ColorModel. * @return a reference to this ImageLayout following the change. */ public ImageLayout setColorModel(ColorModel colorModel) { this.colorModel = colorModel; setValid(COLOR_MODEL_MASK); return this; } /** Returns a String containing the values of all valid fields. */ public String toString() { String s = "ImageLayout["; boolean first = true; if (isValid(MIN_X_MASK)) { s += "MIN_X=" + minX; first = false; } if (isValid(MIN_Y_MASK)) { if (!first) { s += ", "; } s += "MIN_Y=" + minY; first = false; } if (isValid(WIDTH_MASK)) { if (!first) { s += ", "; } s += "WIDTH=" + width; first = false; } if (isValid(HEIGHT_MASK)) { if (!first) { s += ", "; } s += "HEIGHT=" + height; first = false; } if (isValid(TILE_GRID_X_OFFSET_MASK)) { if (!first) { s += ", "; } s += "TILE_GRID_X_OFFSET=" + tileGridXOffset; first = false; } if (isValid(TILE_GRID_Y_OFFSET_MASK)) { if (!first) { s += ", "; } s += "TILE_GRID_Y_OFFSET=" + tileGridYOffset; first = false; } if (isValid(TILE_WIDTH_MASK)) { if (!first) { s += ", "; } s += "TILE_WIDTH=" + tileWidth; first = false; } if (isValid(TILE_HEIGHT_MASK)) { if (!first) { s += ", "; } s += "TILE_HEIGHT=" + tileHeight; first = false; } if (isValid(SAMPLE_MODEL_MASK)) { if (!first) { s += ", "; } s += "SAMPLE_MODEL=" + sampleModel; first = false; } if (isValid(COLOR_MODEL_MASK)) { if (!first) { s += ", "; } s += "COLOR_MODEL=" + colorModel; } s += "]"; return s; } /** * Returns a clone of the ImageLayout as an Object. */ public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { return null; } } /** * Serialize the ImageLayout. * @throws IOException */ private void writeObject(ObjectOutputStream out) throws IOException { // Write the non-static and non-transient fields. out.defaultWriteObject(); // Create and write a serializable SampleModel. if(isValid(SAMPLE_MODEL_MASK)) { out.writeObject(SerializerFactory.getState(sampleModel, null)); } // Create and write a serializable ColorModel. if(isValid(COLOR_MODEL_MASK)) { out.writeObject(SerializerFactory.getState(colorModel, null)); } } /** * Deserialize the ImageLayout. * @throws IOException */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { // Read the non-static and non-transient fields. in.defaultReadObject(); // Read the serializable form of the SampleModel. if(isValid(SAMPLE_MODEL_MASK)) { Object object = in.readObject(); if (!(object instanceof SerializableState)) sampleModel = null; SerializableState ss = (SerializableState)object; Class c = ss.getObjectClass(); if (SampleModel.class.isAssignableFrom(c)) sampleModel = (SampleModel)ss.getObject(); else sampleModel = null; } // Read the serializable form of the ColorModel. if(isValid(COLOR_MODEL_MASK)) { Object object = in.readObject(); if (!(object instanceof SerializableState)) colorModel = null; SerializableState ss = (SerializableState)object; Class c = ss.getObjectClass(); if (ColorModel.class.isAssignableFrom(c)) colorModel = (ColorModel)ss.getObject(); else colorModel = null; } } /** * Tests if the specified Object equals this * ImageLayout. * * @param obj the Object to test for equality * * @return true if the specified Object * is an instance of ImageLayout and equals this * ImageLayout; false otherwise. * * @since JAI 1.1 */ public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof ImageLayout)) return false; ImageLayout il = (ImageLayout)obj; return (validMask == il.validMask ) && (width == il.width ) && (height == il.height ) && (minX == il.minX ) && (minY == il.minY ) && (tileHeight == il.tileHeight ) && (tileWidth == il.tileWidth ) && (tileGridXOffset == il.tileGridXOffset) && (tileGridYOffset == il.tileGridYOffset) && (sampleModel.equals(il.sampleModel )) && (colorModel.equals(il.colorModel)); } /** * Returns the hash code for this ImageLayout. * * @return a hash code for this ImageLayout. * * @since JAI 1.1 */ public int hashCode() { int code = 0, i = 1; // This implementation is quite arbitrary. // hashCode's NEED not be uniqe for two "different" objects code += (width * i++); code += (height * i++); code += (minX * i++); code += (minY * i++); code += (tileHeight * i++); code += (tileWidth * i++); code += (tileGridXOffset * i++); code += (tileGridYOffset * i++); code ^= sampleModel.hashCode(); code ^= validMask; code ^= colorModel.hashCode(); return code; } } jai-core-1.1.4/src/share/classes/javax/media/jai/UntiledOpImage.java0000644000175000017500000002551010203035544025051 0ustar mathieumathieu/* * $RCSfile: UntiledOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:23 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.util.Map; import java.util.Vector; import javax.media.jai.ImageLayout; import javax.media.jai.PlanarImage; /** * A general class for single-source operations which require cobbled * sources and create an image consisting of a single tile equal in location * and size to the image bounds. * *

    The output image will have a single tile, regardless of the * ImageLayout settings passed to the constructor. Any * specified settings for tile grid offset and tile dimensions will be * replaced by the image origin and tile dimensions, respectively. * *

    Subclasses should implement the computeImage method * which requests computation of the entire image at once. * * @see OpImage * @see javax.media.jai.operator.DCTDescriptor * @see javax.media.jai.operator.DFTDescriptor * @see javax.media.jai.operator.ErrorDiffusionDescriptor */ public abstract class UntiledOpImage extends OpImage { /** * Creates the ImageLayout for the image. If the * layout parameter is null, create a new ImageLayout * from the supplied RenderedImage. Also, force the tile * grid offset to equal the image origin and the tile width and height * to be equal to the image width and height, respectively, thereby * forcing the image to have a single tile. * * @param layout The ImageLayout to be cloned; may be null. * @param source The RenderedImage the attributes of which * are to be used as fallbacks in creating a new ImageLayout. * * @return The ImageLayout to be used. */ private static ImageLayout layoutHelper(ImageLayout layout, Vector sources) { if(sources.size() < 1) { throw new IllegalArgumentException(JaiI18N.getString("Generic5")); } RenderedImage source = (RenderedImage)sources.get(0); ImageLayout il = layout == null ? new ImageLayout() : (ImageLayout)layout.clone(); // Force the image to have one tile. For this to obtain with minimal // tile size the tile grid offset must coincide with the image origin. il.setTileGridXOffset(il.getMinX(source)); il.setTileGridYOffset(il.getMinY(source)); il.setTileWidth(il.getWidth(source)); il.setTileHeight(il.getHeight(source)); return il; } /** * Constructs an UntiledOpImage. The image origin and * dimensions, SampleModel, and ColorModel * may optionally be specified by an ImageLayout object. * In all cases the tile grid offset will be set to the image origin * and the tile dimensions to the image dimensions. If not specified * in the ImageLayout, the image origin and dimensions * are set to the corresponding attributes of the first source image. * Cobbling will be performed on the source(s) as needed. * * @param sources The immediate sources of this image. * @param configuration Configurable attributes of the image including * configuration variables indexed by * RenderingHints.Keys and image properties indexed * by Strings or CaselessStringKeys. * This is simply forwarded to the superclass constructor. * @param layout an ImageLayout optionally containing * the SampleModel, and * ColorModel. The tile grid layout * information will be overridden in order to ensure that * the image has a single tile. * * @throws IllegalArgumentException if sources * is null. * @throws IllegalArgumentException If sources * is non-null and any object in * sources is null. * @throws IllegalArgumentException if sources does not * contain at least one element. * @throws ClassCastException If the first object in sources * is not a RenderedImage. * * @since JAI 1.1 */ public UntiledOpImage(Vector sources, Map configuration, ImageLayout layout) { super(checkSourceVector(sources, true), layoutHelper(layout, sources), configuration, true); } /** * Constructs an UntiledOpImage. The image origin and * dimensions, SampleModel, and ColorModel * may optionally be specified by an ImageLayout object. * In all cases the tile grid offset will be set to the image origin * and the tile dimensions to the image dimensions. If not specified * in the ImageLayout, the image origin and dimensions * are set to the corresponding attributes of the source image. * Cobbling will be performed on the source as needed. * * @param source a RenderedImage. * @param configuration Configurable attributes of the image including * configuration variables indexed by * RenderingHints.Keys and image properties indexed * by Strings or CaselessStringKeys. * This is simply forwarded to the superclass constructor. * @param layout an ImageLayout optionally containing * the SampleModel, and * ColorModel. The tile grid layout * information will be overridden in order to ensure that * the image has a single tile. * * @throws IllegalArgumentException if source * is null. * * @since JAI 1.1 */ public UntiledOpImage(RenderedImage source, Map configuration, ImageLayout layout) { super(vectorize(source), // vectorize() checks for null source. layoutHelper(layout, vectorize(source)), configuration, true); } /** * Returns the image bounds. * * @param sourceRect the Rectangle in source coordinates * (ignored). * @param sourceIndex the index of the source image (ignored). * @return The image bounds. */ public Rectangle mapSourceRect(Rectangle sourceRect, int sourceIndex) { return getBounds(); } /** * Returns the bounds of the indicated source image. * * @param destRect the Rectangle in destination coordinates (ignored). * @param sourceIndex the index of the source image. * @return The bounds of the indicated source image. * * @throws IllegalArgumentException if sourceIndex is * negative or greater than the index of the last source. */ public Rectangle mapDestRect(Rectangle destRect, int sourceIndex) { if(sourceIndex < 0 || sourceIndex >= getNumSources()) { throw new IllegalArgumentException(JaiI18N.getString("Generic1")); } return getSource(sourceIndex).getBounds(); } /** * Computes a tile. All sources are cobbled together and * computeImage is called to produce the single * output tile. * * @param tileX The X index of the tile. * @param tileY The Y index of the tile. */ public Raster computeTile(int tileX, int tileY) { // Create a new raster. Point org = new Point(getMinX(), getMinY()); WritableRaster dest = createWritableRaster(sampleModel, org); // Determine the active area. Since the image has a single // tile equal in coverage to the image bounds just set this // to the image bounds. Rectangle destRect = getBounds(); // Cobble source image(s). int numSources = getNumSources(); Raster[] rasterSources = new Raster[numSources]; for(int i = 0; i < numSources; i++) { PlanarImage source = getSource(i); Rectangle srcRect = mapDestRect(destRect, i); rasterSources[i] = source.getData(srcRect); } // Compute the image. computeImage(rasterSources, dest, destRect); for (int i = 0; i < numSources; i++) { Raster sourceData = rasterSources[i]; if(sourceData != null) { PlanarImage source = getSourceImage(i); // Recycle the source tile if(source.overlapsMultipleTiles(sourceData.getBounds())) { recycleTile(sourceData); } } } return dest; } /** * Calculate the destination image from the source image. * * @param sources The source Rasters; should be the whole image for * each source. * @param dest The destination WritableRaster; should be the whole image. * @param destRect The destination Rectangle; should equal the destination * image bounds. * * @since JAI 1.1 */ protected abstract void computeImage(Raster[] sources, WritableRaster dest, Rectangle destRect); /** * Returns an array of points indicating the tile dependencies which in * this case is the set of all tiles in the specified source image. * * @since JAI 1.1 */ public Point[] getTileDependencies(int tileX, int tileY, int sourceIndex) { // Compute the tile dependencies only the first time that this // method is invoked. PlanarImage source = getSource(sourceIndex); int minTileX = source.getMinTileX(); int minTileY = source.getMinTileY(); int maxTileX = minTileX + source.getNumXTiles() - 1; int maxTileY = minTileY + source.getNumYTiles() - 1; Point[] tileDependencies = new Point[(maxTileX - minTileX + 1)*(maxTileY - minTileY + 1)]; int count = 0; for(int ty = minTileY; ty <= maxTileY; ty++) { for(int tx = minTileX; tx <= maxTileX; tx++) { tileDependencies[count++] = new Point(tx, ty); } } return tileDependencies; } } jai-core-1.1.4/src/share/classes/javax/media/jai/WritablePropertySource.java0000644000175000017500000000603610203035544026704 0ustar mathieumathieu/* * $RCSfile: WritablePropertySource.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:25 $ * $State: Exp $ */ package javax.media.jai; /** * Sub-interface of PropertySource which permits setting * the values of JAI properties in addition to obtaining their names * and values. As the values of properties managed by classes which * implement this interface may change, this is also a sub-interface * of PropertyChangeEmitter. This permits other objects * to register as listeners of particular JAI properties. * *

    The case of the names of properties added via this interface * should be retained although the case will be ignored in queries via * getProperty() and will be forced to lower case in * emitted PropertySourceChangeEvent. * * @see PropertySource * @see PropertyChangeEmitter * * @since JAI 1.1 */ public interface WritablePropertySource extends PropertySource, PropertyChangeEmitter { /** * Adds the property value associated with the supplied name to * the WritablePropertySource. Properties set by * this means will supersede any properties of the same name * which might otherwise be derived dynamically. * *

    Implementing classes which should * fire a PropertySourceChangeEvent with a name set to * that of the set property (retaining case), source set to the * WritablePropertySource, and old and new values set to * the previous and current values of the property, respectively. * Neither the old nor the new value may null: undefined * properties must as usual be indicated by an the constant value * java.awt.Image.UndefinedProperty. It is however * legal for either but not both of the old and new property values * to equal java.awt.Image.UndefinedProperty. * * @param propertyName the name of the property, as a String. * @param propertyValue the property, as a general Object. * * @exception IllegalArgumentException if propertyName * or propertyValue * is null. */ void setProperty(String propertyName, Object propertyValue); /** * Removes the named property from the WritablePropertySource. * This method will clear any locally cached (static) properties * but may have no effect on properties which would be derived * dynamically. * * @param propertyName the name of the property, as a String. * @param propertyValue the property, as a general Object. * * @exception IllegalArgumentException if propertyName * is null. */ void removeProperty(String propertyName); } jai-core-1.1.4/src/share/classes/javax/media/jai/AttributedImage.java0000644000175000017500000000432610203035544025257 0ustar mathieumathieu/* * $RCSfile: AttributedImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:03 $ * $State: Exp $ */ package javax.media.jai; /** * A class which associates a PlanarImage with an attribute * of unspecified type. The class is itself a PlanarImage * equivalent to the one which it wraps. * * @since JAI 1.1 */ public class AttributedImage extends RenderedImageAdapter { /** The attribute associated with the image. */ protected Object attribute; /** * Constructs an AttributedImage. The attribute parameter * may be null * * @throws IllegalArgumentException if theImage is * null. */ public AttributedImage(PlanarImage image, Object attribute) { super(image); this.attribute = attribute; } /** Retrieves the wrapped image. */ public PlanarImage getImage() { return (PlanarImage)theImage; } /** Stores the attribute. */ public void setAttribute(Object attribute) { this.attribute = attribute; } /** Retrieves the attribute. */ public Object getAttribute() { return attribute; } /** * Tests for equality. The parameter Object must be * an AttributedImage the image and attribute of which * are equal those of this object according to the equals() * methods of the image and attribute of this image, respectively. * Attributes are also considered equal if they are both null. */ public boolean equals(Object o) { if (o != null && o instanceof AttributedImage) { AttributedImage ai = (AttributedImage)o; Object a = ai.getAttribute(); return getImage().equals(ai.getImage()) && (attribute == null ? a == null : ((a != null) && attribute.equals(a))); } return false; } /** toString() method. */ public String toString() { return "Attribute=(" + getAttribute() + ") Image=" + getImage(); } } jai-core-1.1.4/src/share/classes/javax/media/jai/RasterFactory.java0000644000175000017500000014611710400713113024774 0ustar mathieumathieu/* * $RCSfile: RasterFactory.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2006-02-28 00:16:11 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Point; import java.awt.Rectangle; import java.awt.Transparency; import java.awt.color.ColorSpace; import java.awt.image.ComponentColorModel; import java.awt.image.ComponentSampleModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferShort; import java.awt.image.DataBufferInt; import java.awt.image.DataBufferUShort; import java.awt.image.BandedSampleModel; import java.awt.image.PixelInterleavedSampleModel; import java.awt.image.Raster; import java.awt.image.RasterFormatException; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import com.sun.media.jai.util.DataBufferUtils; /** * A convenience class for the construction of various types of * WritableRaster and SampleModel objects. * *

    This class provides the capability of creating * Rasters with the enumerated data types in the * java.awt.image.DataBuffer. * *

    In some cases, instances of * ComponentSampleModelJAI, a subclass of * java.awt.image.ComponentSampleModel are instantiated * instead of java.awt.image.BandedSampleModel in order * to work around bugs in the current release of the Java 2 SDK. */ public class RasterFactory { /** * Creates a WritableRaster based on a * PixelInterleavedSampleModel with the specified * data type, width, height, and number of bands. * *

    The upper left corner of the WritableRaster 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. * * @param dataType The data type of the SampleModel, * one of DataBuffer.TYPE_BYTE, * TYPE_USHORT, * TYPE_SHORT, * TYPE_INT, * TYPE_FLOAT, or * TYPE_DOUBLE. * @param width The desired width of the WritableRaster. * @param height The desired height of the WritableRaster. * @param numBands The desired number of bands. * @param location A Point indicating the starting * coordinates of the WritableRaster. * * @throws IllegalArgumentException if numbands is * <1. */ public static WritableRaster createInterleavedRaster(int dataType, int width, int height, int numBands, Point location) { if (numBands < 1) { throw new IllegalArgumentException(JaiI18N.getString("RasterFactory0")); } int[] bandOffsets = new int[numBands]; for (int i = 0; i < numBands; i++) { bandOffsets[i] = numBands - 1 - i; } return createInterleavedRaster(dataType, width, height, width*numBands, numBands, bandOffsets, location); } /** * Creates a WritableRaster 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 WritableRaster 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. * * @param dataType The data type of the WritableRaster, * one of the enumerated dataType values in * java.awt.image.DataBuffer. * @param width The desired width of the WritableRaster. * @param height The desired height of the WritableRaster. * @param scanlineStride The desired scanline stride. * @param pixelStride The desired pixel stride. * @param bandOffsets An array of ints indicating the * relative offsets of the bands within a pixel. * @param location A Point indicating the starting * coordinates of the WritableRaster. * * @throws IllegalArgumentException if bandOffsets is * null, dataType is not one of * the enumerated dataType value of java.awt.image.DataBuffer. * * @throws IllegalArgumentException if the number of array elements * required by the returned WritableRaster * would exceed Integer.MAX_VALUE. */ public static WritableRaster createInterleavedRaster(int dataType, int width, int height, int scanlineStride, int pixelStride, int bandOffsets[], Point location) { if (bandOffsets == null) { throw new IllegalArgumentException(JaiI18N.getString("RasterFactory4")); } DataBuffer d; int bands = bandOffsets.length; int maxBandOff = bandOffsets[0]; for (int i=1; i < bands; i++) { if (bandOffsets[i] > maxBandOff) { maxBandOff = bandOffsets[i]; } } long lsize = (long)maxBandOff + (long)scanlineStride*(height - 1) + (long)pixelStride*(width - 1) + 1L; if (lsize > (long)Integer.MAX_VALUE) { throw new IllegalArgumentException(JaiI18N.getString("RasterFactory16")); } int size = (int)lsize; switch(dataType) { case DataBuffer.TYPE_BYTE: d = new DataBufferByte(size); break; case DataBuffer.TYPE_USHORT: d = new DataBufferUShort(size); break; case DataBuffer.TYPE_SHORT: d = new DataBufferShort(size); break; case DataBuffer.TYPE_INT: d = new DataBufferInt(size); break; case DataBuffer.TYPE_FLOAT: d = DataBufferUtils.createDataBufferFloat(size); break; case DataBuffer.TYPE_DOUBLE: d = DataBufferUtils.createDataBufferDouble(size); break; default: throw new IllegalArgumentException(JaiI18N.getString("RasterFactory3")); } return createInterleavedRaster(d, width, height, scanlineStride, pixelStride, bandOffsets, location); } /** * Creates a WritableRaster based on a * ComponentSampleModel with the specified data type, * width, height, and number of bands. * *

    Note that the Raster's * SampleModel will be of type * ComponentSampleModel, not * BandedSampleModel as might be expected. * *

    The upper left corner of the WritableRaster 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. * * @param dataType The data type of the WritableRaster, * one of the enumerated dataType values in * java.awt.image.DataBuffer. * @param width The desired width of the WritableRaster. * @param height The desired height of the WritableRaster. * @param bands The desired number of bands. * @param location A Point indicating the starting * coordinates of the WritableRaster. * * @throws IllegalArgumentException if bands is * <1. */ public static WritableRaster createBandedRaster(int dataType, int width, int height, int bands, Point location) { if (bands < 1) { throw new IllegalArgumentException(JaiI18N.getString("RasterFactory0")); } 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, width, height, width, bankIndices, bandOffsets, location); } /** * Creates a WritableRaster based on a * ComponentSampleModel 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. * *

    Note that the Raster's * SampleModel will be of type * ComponentSampleModel, not * BandedSampleModel as might be expected. * *

    The upper left corner of the WritableRaster is * given by the location argument. The * dataType parameter should be one of the enumerated * values defined in the DataBuffer class. * * @param dataType The data type of the WritableRaster, * one of the enumerated dataType values in * java.awt.image.DataBuffer. * @param width The desired width of the WritableRaster. * @param height The desired height of the WritableRaster. * @param scanlineStride The desired scanline stride. * @param bankIndices An array of ints indicating the * bank index for each band. * @param bandOffsets An array of ints indicating the * relative offsets of the bands within a pixel. * @param location A Point indicating the starting * coordinates of the WritableRaster. * * @throws IllegalArgumentException if bankIndices is * null, bandOffsets is * null, if bandOffsets.length * is != bankIndices.length, * if dataType is not one of the enumerated * datatypes of java.awt.image.DataBuffer. */ public static WritableRaster createBandedRaster(int dataType, int width, int height, int scanlineStride, int bankIndices[], int bandOffsets[], Point location) { DataBuffer d; int bands = bandOffsets.length; if (bankIndices == null) { throw new IllegalArgumentException(JaiI18N.getString("RasterFactory1")); } if (bandOffsets == null) { throw new IllegalArgumentException(JaiI18N.getString("RasterFactory4")); } if (bandOffsets.length != bankIndices.length) { throw new IllegalArgumentException(JaiI18N.getString("RasterFactory2")); } // 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; long lsize = (long)maxBandOff + (long)scanlineStride*(height - 1) + (long)(width - 1) + 1L; if (lsize > (long)Integer.MAX_VALUE) { throw new IllegalArgumentException(JaiI18N.getString("RasterFactory16")); } int size = (int)lsize; 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_SHORT: d = new DataBufferShort(size, banks); break; case DataBuffer.TYPE_INT: d = new DataBufferInt(size, banks); break; case DataBuffer.TYPE_FLOAT: d = DataBufferUtils.createDataBufferFloat(size, banks); break; case DataBuffer.TYPE_DOUBLE: d = DataBufferUtils.createDataBufferDouble(size, banks); break; default: throw new IllegalArgumentException(JaiI18N.getString("RasterFactory3")); } return createBandedRaster(d, width, height, scanlineStride, bankIndices, bandOffsets, location); } /** * Creates a WritableRaster 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 WritableRaster 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. * * @param dataType The data type of the WritableRaster, * one of DataBuffer.TYPE_BYTE, * TYPE_USHORT or TYPE_INT. * @param width The desired width of the WritableRaster. * @param height The desired height of the WritableRaster. * @param location A Point indicating the starting * coordinates of the WritableRaster. * * @throws IllegalArgumentException is thrown if * the dataType is not of either TYPE_BYTE * or TYPE_USHORT or TYPE_INT. */ public static WritableRaster createPackedRaster(int dataType, int width, int height, int bandMasks[], Point location) { return Raster.createPackedRaster(dataType, width, height, bandMasks, location); } /** * Creates a WritableRaster 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 WritableRaster 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. * * @param dataType The data type of the WritableRaster, * one of DataBuffer.TYPE_BYTE, * TYPE_USHORT or TYPE_INT. * @param width The desired width of the WritableRaster. * @param height The desired height of the WritableRaster. * @param numBands The desired number of bands. * @param bitsPerBand The number of bits per band. * @param location A Point indicating the starting * coordinates of the WritableRaster. * * @throws IllegalArgumentException is thrown if * the dataType is not of either TYPE_BYTE * or TYPE_USHORT or TYPE_INT. * @throws IllegalArgumentException is thrown if bitsPerBand * is negative or zero. */ public static WritableRaster createPackedRaster(int dataType, int width, int height, int numBands, int bitsPerBand, Point location) { if (bitsPerBand <= 0) { throw new IllegalArgumentException(JaiI18N.getString("RasterFactory15")); } return Raster.createPackedRaster(dataType, width, height, numBands, bitsPerBand, location); } /** * Creates a WritableRaster 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 * WritableRaster is given by the * location argument. If location is * null, (0, 0) will be used. * * @param dataBuffer The DataBuffer to be used. * @param width The desired width of the WritableRaster. * @param height The desired height of the WritableRaster. * @param scanlineStride The desired scanline stride. * @param pixelStride The desired pixel stride. * @param bandOffsets An array of ints indicating the * relative offsets of the bands within a pixel. * @param location A Point indicating the starting * coordinates of the WritableRaster. * * @throws IllegalArgumentException if bandOffsets is * null, if pixelStride*width is * > scanlineStride, * if dataTypeof the DataBuffer is not one * the enumerated dataType value of java.awt.image.DataBuffer. */ public static WritableRaster createInterleavedRaster(DataBuffer dataBuffer, int width, int height, int scanlineStride, int pixelStride, int bandOffsets[], Point location) { if (bandOffsets == null) { throw new IllegalArgumentException(JaiI18N.getString("RasterFactory4")); } if (location == null) { location = new Point(0, 0); } int dataType = dataBuffer.getDataType(); switch(dataType) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: PixelInterleavedSampleModel csm = new PixelInterleavedSampleModel(dataType, width, height, pixelStride, scanlineStride, bandOffsets); return Raster.createWritableRaster(csm,dataBuffer,location); case DataBuffer.TYPE_INT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: int minBandOff=bandOffsets[0]; int maxBandOff=bandOffsets[0]; for (int i=1; i scanlineStride) { throw new IllegalArgumentException( JaiI18N.getString("RasterFactory5")); } if (pixelStride*width > scanlineStride) { throw new IllegalArgumentException( JaiI18N.getString("RasterFactory6")); } if (pixelStride < maxBandOff) { throw new IllegalArgumentException( JaiI18N.getString("RasterFactory7")); } SampleModel sm = new ComponentSampleModelJAI(dataType,width,height, pixelStride, scanlineStride, bandOffsets); return Raster.createWritableRaster(sm, dataBuffer, location); default: throw new IllegalArgumentException(JaiI18N.getString("RasterFactory3")); } } /** * Creates a WritableRaster based on a * ComponentSampleModel 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 WritableRaster is given * by the location argument. If * location is null, (0, 0) will be * used. * *

    Note that the Raster's * SampleModel will be of type * ComponentSampleModel, not * BandedSampleModel as might be expected. * * @param dataBuffer The DataBuffer to be used. * @param width The desired width of the WritableRaster. * @param height The desired height of the WritableRaster. * @param scanlineStride The desired scanline stride. * @param bankIndices An array of ints indicating the * bank index for each band. * @param bandOffsets An array of ints indicating the * relative offsets of the bands within a pixel. * @param location A Point indicating the starting * coordinates of the WritableRaster. * * @throws IllegalArgumentException if bankIndices is * null, if bandOffsets is * null, if bandOffsets.length * is != bankIndices.length, * if dataType is not one of the enumerated * datatypes of java.awt.image.DataBuffer. */ public static WritableRaster createBandedRaster(DataBuffer dataBuffer, int width, int height, int scanlineStride, int bankIndices[], int bandOffsets[], Point location) { if (location == null) { location = new Point(0,0); } int dataType = dataBuffer.getDataType(); if (bankIndices == null) { throw new IllegalArgumentException(JaiI18N.getString("RasterFactory1")); } if (bandOffsets == null) { throw new IllegalArgumentException(JaiI18N.getString("RasterFactory4")); } int bands = bankIndices.length; if (bandOffsets.length != bands) { throw new IllegalArgumentException(JaiI18N.getString("RasterFactory2")); } SampleModel bsm = new ComponentSampleModelJAI(dataType, width, height, 1, scanlineStride, bankIndices, bandOffsets); switch(dataType) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_INT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: return Raster.createWritableRaster(bsm, dataBuffer, location); default: throw new IllegalArgumentException(JaiI18N.getString("RasterFactory3")); } } /** * Creates a WritableRaster 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 * WritableRaster is given by the * location argument. If location is * null, (0, 0) will be used. * * @param dataBuffer The DataBuffer to be used. * @param width The desired width of the WritableRaster. * @param height The desired height of the WritableRaster. * @param scanlineStride The desired scanline stride. * @param bandMasks An array of ints indicating the * bitmasks for each band within a pixel. * @param location A Point indicating the starting * coordinates of the WritableRaster. * * @throws IllegalArgumentException is thrown if * the dataType is not of either TYPE_BYTE * or TYPE_USHORT or TYPE_INT. */ public static WritableRaster createPackedRaster(DataBuffer dataBuffer, int width, int height, int scanlineStride, int bandMasks[], Point location) { return Raster.createPackedRaster(dataBuffer, width, height, scanlineStride, bandMasks, location); } /** * Creates a WritableRaster based on a * MultiPixelPackedSampleModel with the specified * DataBuffer, width, height, and bits per pixel. * The upper left corner of the WritableRaster is * given by the location argument. If * location is null, (0, 0) will be * used. * * @param dataBuffer The DataBuffer to be used. * @param width The desired width of the WritableRaster. * @param height The desired height of the WritableRaster. * @param bitsPerPixel The desired pixel depth. * @param location A Point indicating the starting * coordinates of the WritableRaster. * * @throws IllegalArgumentException is thrown if * the dataType of the dataBuffer * is not of either TYPE_BYTE or TYPE_USHORT or TYPE_INT. */ public static WritableRaster createPackedRaster(DataBuffer dataBuffer, int width, int height, int bitsPerPixel, Point location) { return Raster.createPackedRaster(dataBuffer, width, height, bitsPerPixel, location); } /** * Creates a WritableRaster with the specified * SampleModel and DataBuffer. The * upper left corner of the WritableRaster is given * by the location argument. If * location is null, (0, 0) will be * used. * * @param sampleModel The SampleModel to be used. * @param dataBuffer The DataBuffer to be used. * @param location A Point indicating the starting * coordinates of the WritableRaster. */ public static Raster createRaster(SampleModel sampleModel, DataBuffer dataBuffer, Point location) { return Raster.createRaster(sampleModel, dataBuffer, location); } /** * Creates a WritableRaster with the specified * SampleModel. The upper left corner of the * WritableRaster is given by the * location argument. If location is * null, (0, 0) will be used. * * @param sampleModel The SampleModel to use. * @param location A Point indicating the starting * coordinates of the WritableRaster. */ public static WritableRaster createWritableRaster(SampleModel sampleModel, Point location) { if (location == null) { location = new Point(0,0); } return createWritableRaster(sampleModel, sampleModel.createDataBuffer(), location); } /** * Creates a WritableRaster with the specified * SampleModel and DataBuffer. The * upper left corner of the WritableRaster is given * by the location argument. If * location is null, (0, 0) will be * used. * * @param sampleModel The SampleModel to be used. * @param dataBuffer The DataBuffer to be used. * @param location A Point indicating the starting * coordinates of the WritableRaster. */ public static WritableRaster createWritableRaster(SampleModel sampleModel, DataBuffer dataBuffer, Point location) { return Raster.createWritableRaster(sampleModel, dataBuffer, location); } /** * Returns a new WritableRaster which shares all or part of the * supplied WritableRaster's DataBuffer. The new WritableRaster will * possess a reference to the supplied WritableRaster, accessible * through its getParent() and getWritableParent() methods. * *

    This method provides a workaround for a bug in the * implementation of WritableRaster.createWritableChild in * the initial relase of the Java2 platform. * *

    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 supplied * WritableRaster. * *

    The new WritableRaster may additionally be translated to a * different coordinate system for the plane than that used by the supplied * 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 supplied WritableRaster. * *

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

    To create a new WritableRaster that contains a subregion of * the supplied 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 raster The parent WritableRaster. * @param parentX X coordinate of the upper left corner of the shared * rectangle in this WritableRaster's coordinates. * @param parentY Y coordinate of the upper left corner of the shared * rectangle in this WritableRaster's coordinates. * @param width Width of the shared rectangle starting at * (parentX, parentY). * @param height Height of the shared rectangle 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. * * @throws RasterFormatException if the subregion is outside of the * raster bounds. */ public static WritableRaster createWritableChild(WritableRaster raster, int parentX, int parentY, int width, int height, int childMinX, int childMinY, int bandList[]) { // Simply forward the call to the equivalent WritableRaster method. // The WritableRaster bug referred to in the javadoc was 4212434 // and was fixed in Java SE 1.3, which is the minimum version // required for JAI. return raster.createWritableChild(parentX, parentY, width, height, childMinX, childMinY, bandList); } /** * Creates a banded SampleModel with a given data * type, width, height, number of bands, bank indices, and band * offsets. * *

    Note that the returned SampleModel will be of type * ComponentSampleModel, not * BandedSampleModel as might be expected. Its * behavior will be equivalent to that of a * BandedSampleModel, and in particular its pixel * stride will always be 1. * * @param dataType The data type of the SampleModel, * one of DataBuffer.TYPE_BYTE, * TYPE_USHORT, * TYPE_SHORT, * TYPE_INT, * TYPE_FLOAT, or * TYPE_DOUBLE. * @param width The desired width of the SampleModel. * @param height The desired height of the SampleModel. * @param numBands The desired number of bands. * @param bankIndices An array of ints indicating the * bank index for each band. * @param bandOffsets An array of ints indicating the * relative offsets of the bands within a pixel. * * @throws IllegalArgumentException if numBands is * <1, if bandOffsets.length is * != bankIndices.length. */ public static SampleModel createBandedSampleModel(int dataType, int width, int height, int numBands, int bankIndices[], int bandOffsets[]) { if (numBands < 1) { throw new IllegalArgumentException(JaiI18N.getString("RasterFactory0")); } if (bankIndices == null) { bankIndices = new int[numBands]; for (int i=0; i < numBands; i++) { bankIndices[i] = i; } } if (bandOffsets == null) { bandOffsets = new int[numBands]; for (int i=0; i < numBands; i++) { bandOffsets[i] = 0; } } if (bandOffsets.length != bankIndices.length) { throw new IllegalArgumentException(JaiI18N.getString("RasterFactory2")); } return new ComponentSampleModelJAI(dataType, width, height, 1, width, bankIndices, bandOffsets); } /** * Creates a banded SampleModel with a given data * type, width, height, and number of bands. The bank indices and * band offsets are set to default values. * *

    Note that the returned SampleModel will be of type * ComponentSampleModel, not * BandedSampleModel as might be expected. Its * behavior will be equivalent to that of a * BandedSampleModel, and in particular its pixel * stride will always be 1. * * @param dataType The data type of the SampleModel, * one of DataBuffer.TYPE_BYTE, * TYPE_USHORT, * TYPE_SHORT, * TYPE_INT, * TYPE_FLOAT, or * TYPE_DOUBLE. * @param width The desired width of the SampleModel. * @param height The desired height of the SampleModel. * @param numBands The desired number of bands. */ public static SampleModel createBandedSampleModel(int dataType, int width, int height, int numBands) { return createBandedSampleModel(dataType, width, height, numBands, null, null); } /** * Creates a pixel interleaved SampleModel with a * given data type, width, height, pixel and scanline strides, and * band offsets. * * @param dataType The data type of the SampleModel, * one of DataBuffer.TYPE_BYTE, * TYPE_USHORT, * TYPE_SHORT, * TYPE_INT, * TYPE_FLOAT, or * TYPE_DOUBLE. * @param width The desired width of the SampleModel. * @param height The desired height of the SampleModel. * @param pixelStride The desired pixel stride. * @param scanlineStride The desired scanline stride. * @param bandOffsets An array of ints indicating the * relative offsets of the bands within a pixel. * * @throws IllegalArgumentException if bandOffsets is * null, if the pixelStride*width * is > than scanlineStride, * if the dataType is not one of the above * mentioned datatypes. */ public static SampleModel createPixelInterleavedSampleModel(int dataType, int width, int height, int pixelStride, int scanlineStride, int bandOffsets[]) { if (bandOffsets == null) { throw new IllegalArgumentException(JaiI18N.getString("RasterFactory4")); } int minBandOff=bandOffsets[0]; int maxBandOff=bandOffsets[0]; for (int i=1; i scanlineStride) { throw new IllegalArgumentException( JaiI18N.getString("RasterFactory5")); } if (pixelStride*width > scanlineStride) { throw new IllegalArgumentException( JaiI18N.getString("RasterFactory6")); } if (pixelStride < maxBandOff) { throw new IllegalArgumentException( JaiI18N.getString("RasterFactory7")); } switch (dataType) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_USHORT: return new PixelInterleavedSampleModel(dataType, width, height, pixelStride, scanlineStride, bandOffsets); case DataBuffer.TYPE_INT: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_FLOAT: case DataBuffer.TYPE_DOUBLE: return new ComponentSampleModelJAI(dataType, width, height, pixelStride, scanlineStride, bandOffsets); default: throw new IllegalArgumentException(JaiI18N.getString("RasterFactory3")); } } /** * Creates a pixel interleaved SampleModel with a * given data type, width, height, and number of bands. The pixel * stride, scanline stride, and band offsets are set to default * values. * * @param dataType The data type of the SampleModel, * one of DataBuffer.TYPE_BYTE, * TYPE_USHORT, * TYPE_SHORT, * TYPE_INT, * TYPE_FLOAT, or * TYPE_DOUBLE. * @param width The desired width of the SampleModel. * @param height The desired height of the SampleModel. * @param numBands The desired number of bands. * * @throws IllegalArgumentException if numBands is * <1. */ public static SampleModel createPixelInterleavedSampleModel(int dataType, int width, int height, int numBands) { if (numBands < 1) { throw new IllegalArgumentException(JaiI18N.getString("RasterFactory0")); } int[] bandOffsets = new int[numBands]; for (int i = 0; i < numBands; i++) { bandOffsets[i] = numBands - 1 - i; } return createPixelInterleavedSampleModel(dataType, width, height, numBands, numBands * width, bandOffsets); } /** * Creates a component SampleModel with a given data * type, width, height, and number of bands that is "compatible" * with a given SampleModel. * * @param sm The SampleModel to be compatible with. * @param dataType The data type of the SampleModel, * one of DataBuffer.TYPE_BYTE, * TYPE_USHORT, * TYPE_SHORT, * TYPE_INT, * TYPE_FLOAT, or * TYPE_DOUBLE. * @param width The desired width of the SampleModel. * @param height The desired height of the SampleModel. * @param numBands The desired number of bands. */ public static SampleModel createComponentSampleModel(SampleModel sm, int dataType, int width, int height, int numBands) { if (sm instanceof BandedSampleModel) { return createBandedSampleModel(dataType, width, height, numBands); } else { // default SampleModel return createPixelInterleavedSampleModel( dataType, width, height, numBands); } } /** * Creates a component-based ColorModel with a given * data type, color space, and transparency type. Currently this * method does not support data type DataBuffer.TYPE_SHORT. * If useAlpha is false, both premultiplied and transparency input * are ignored and they are set to be false and * Transparency.OPQAUE , respectively. * * @param dataType The data type of the ColorModel, * one of DataBuffer.TYPE_BYTE, * TYPE_USHORT, * TYPE_INT, * TYPE_FLOAT, or * TYPE_DOUBLE. * @param colorSpace An instance of ColorSpace. * @param useAlpha true if alpha is to be used. * @param premultiplied true if alpha values are * premultiplied. If useAlpha is * false, the value of * premultiplied is ignored. * @param transparency One of Transparency.OPAQUE, * Transparency.BITMASK, or * Transparency.TRANSLUCENT. If * useAlpha is false, the value of * transparency is ignored. If useAlpha * is true, transparency must not equal * Transparency.OPQAUE. * * @throws IllegalArgumentExceptionException if colorSpace is * null. * @throws IllegalArgumentException if transparency * has an unknown value, if useAlpha == true but * transparency == Transparency.OPAQUE, or if * dataType is not one of the standard types listed * above. */ public static ComponentColorModel createComponentColorModel(int dataType, ColorSpace colorSpace, boolean useAlpha, boolean premultiplied, int transparency) { if ( colorSpace == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if ((transparency != Transparency.OPAQUE) && (transparency != Transparency.BITMASK) && (transparency != Transparency.TRANSLUCENT)) { // Illegal value for transparency throw new IllegalArgumentException(JaiI18N.getString("RasterFactory13")); } if (useAlpha && (transparency == Transparency.OPAQUE)) { throw new IllegalArgumentException(JaiI18N.getString("RasterFactory14")); } if (!useAlpha) { premultiplied = false; transparency = Transparency.OPAQUE; } int bands = colorSpace.getNumComponents(); if (useAlpha) { ++bands; } int dataTypeSize = DataBuffer.getDataTypeSize(dataType); int[] bits = new int[bands]; for (int i = 0; i < bands; i++) { bits[i] = dataTypeSize; } switch (dataType) { case DataBuffer.TYPE_BYTE: return new ComponentColorModel(colorSpace, bits, useAlpha, premultiplied, transparency, dataType); case DataBuffer.TYPE_USHORT: return new ComponentColorModel(colorSpace, bits, useAlpha, premultiplied, transparency, dataType); /// case DataBuffer.TYPE_SHORT: /// return new ShortComponentColorModel(colorSpace, /// bits, /// useAlpha, /// premultiplied, /// transparency); case DataBuffer.TYPE_INT: return new ComponentColorModel(colorSpace, bits, useAlpha, premultiplied, transparency, dataType); case DataBuffer.TYPE_FLOAT: return new FloatDoubleColorModel(colorSpace, useAlpha, premultiplied, transparency, dataType); case DataBuffer.TYPE_DOUBLE: return new FloatDoubleColorModel(colorSpace, useAlpha, premultiplied, transparency, dataType); default: throw new IllegalArgumentException( JaiI18N.getString("RasterFactory8")); } } } jai-core-1.1.4/src/share/classes/javax/media/jai/PerspectiveTransform.java0000644000175000017500000013752110203035544026376 0ustar mathieumathieu/* * $RCSfile: PerspectiveTransform.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:15 $ * $State: Exp $ */ package javax.media.jai; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.geom.NoninvertibleTransformException; import java.io.Serializable; /** * A 2D perspective (or projective) transform, used by various OpImages. * *

    A perspective transformation is capable of mapping an arbitrary * quadrilateral into another arbitrary quadrilateral, while * preserving the straightness of lines. Unlike an affine * transformation, the parallelism of lines in the source is not * necessarily preserved in the output. * *

    Such a coordinate transformation can be represented by a 3x3 * matrix which transforms homogenous source coordinates * (x, y, 1) into destination coordinates * (x', y', w). To convert back into non-homogenous * coordinates (X, Y), x' and y' are divided by * w. * *

     *	[ x']   [  m00  m01  m02  ] [ x ]   [ m00x + m01y + m02 ]
     *	[ y'] = [  m10  m11  m12  ] [ y ] = [ m10x + m11y + m12 ]
     *	[ w ]   [  m20  m21  m22  ] [ 1 ]   [ m20x + m21y + m22 ]
     *
     *	  x' = (m00x + m01y + m02)
     *	  y' = (m10x + m11y + m12)
     *
     *        w  = (m20x + m21y + m22)
     *
     *        X = x' / w
     *        Y = y' / w
     * 
    */ public final class PerspectiveTransform implements Cloneable, Serializable { private static final double PERSPECTIVE_DIVIDE_EPSILON = 1.0e-10; /** An element of the transform matrix. */ double m00, m01, m02, m10, m11, m12, m20, m21, m22; /** Constructs an identity PerspectiveTransform. */ public PerspectiveTransform() { m00 = m11 = m22 = 1.0; m01 = m02 = m10 = m12 = m20 = m21 = 0.0; } /** * Constructs a new PerspectiveTransform from 9 floats. * @deprecated as of JAI 1.1 Use PerspectiveTransform(double[][]) instead. */ public PerspectiveTransform(float m00, float m01, float m02, float m10, float m11, float m12, float m20, float m21, float m22) { this.m00 = m00; this.m01 = m01; this.m02 = m02; this.m10 = m10; this.m11 = m11; this.m12 = m12; this.m20 = m20; this.m21 = m21; this.m22 = m22; } /** * Constructs a new PerspectiveTransform from 9 doubles. * @deprecated as of JAI 1.1 Use PerspectiveTransform(double[][]) instead. */ public PerspectiveTransform(double m00, double m01, double m02, double m10, double m11, double m12, double m20, double m21, double m22) { this.m00 = m00; this.m01 = m01; this.m02 = m02; this.m10 = m10; this.m11 = m11; this.m12 = m12; this.m20 = m20; this.m21 = m21; this.m22 = m22; } /** * Constructs a new PerspectiveTransform from a one-dimensional * array of 9 floats, in row-major order. * The values in the array are assumed to be * { m00 m01 m02 m10 m11 m12 m20 m21 m22 }. * @throws IllegalArgumentException if flatmatrix is null * @throws ArrayIndexOutOfBoundsException if flatmatrix is too small * @deprecated as of JAI 1.1 Use PerspectiveTransform(double[][]) instead. */ public PerspectiveTransform(float[] flatmatrix) { if ( flatmatrix == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } m00 = flatmatrix[0]; m01 = flatmatrix[1]; m02 = flatmatrix[2]; m10 = flatmatrix[3]; m11 = flatmatrix[4]; m12 = flatmatrix[5]; m20 = flatmatrix[6]; m21 = flatmatrix[7]; m22 = flatmatrix[8]; } /** * Constructs a new PerspectiveTransform from a two-dimensional * array of floats. * @throws IllegalArgumentException if matrix is null * @throws ArrayIndexOutOfBoundsException if matrix is too small * * @deprecated as of JAI 1.1 Use PerspectiveTransform(double[][]) instead. */ public PerspectiveTransform(float[][] matrix) { if ( matrix == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } m00 = matrix[0][0]; m01 = matrix[0][1]; m02 = matrix[0][2]; m10 = matrix[1][0]; m11 = matrix[1][1]; m12 = matrix[1][2]; m20 = matrix[2][0]; m21 = matrix[2][1]; m22 = matrix[2][2]; } /** * Constructs a new PerspectiveTransform from a one-dimensional * array of 9 doubles, in row-major order. * The values in the array are assumed to be * { m00 m01 m02 m10 m11 m12 m20 m21 m22 }. * @throws IllegalArgumentException if flatmatrix is null * @throws ArrayIndexOutOfBoundsException if flatmatrix is too small * * @deprecated as of JAI 1.1 Use PerspectiveTransform(double[][]) instead. */ public PerspectiveTransform(double[] flatmatrix) { if ( flatmatrix == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } m00 = flatmatrix[0]; m01 = flatmatrix[1]; m02 = flatmatrix[2]; m10 = flatmatrix[3]; m11 = flatmatrix[4]; m12 = flatmatrix[5]; m20 = flatmatrix[6]; m21 = flatmatrix[7]; m22 = flatmatrix[8]; } /** * Constructs a new PerspectiveTransform from a two-dimensional * array of doubles. * @throws IllegalArgumentException if matrix is null * @throws ArrayIndexOutOfBoundsException if matrix is too small */ public PerspectiveTransform(double[][] matrix) { if ( matrix == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } m00 = matrix[0][0]; m01 = matrix[0][1]; m02 = matrix[0][2]; m10 = matrix[1][0]; m11 = matrix[1][1]; m12 = matrix[1][2]; m20 = matrix[2][0]; m21 = matrix[2][1]; m22 = matrix[2][2]; } /** * Constructs a new PerspectiveTransform with the same effect * as an existing AffineTransform. * @throws IllegalArgumentException if transform is null */ public PerspectiveTransform(AffineTransform transform) { if ( transform == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } m00 = transform.getScaleX(); m01 = transform.getShearX(); m02 = transform.getTranslateX(); m10 = transform.getShearY(); m11 = transform.getScaleY(); m12 = transform.getTranslateY(); m20 = 0.0; m21 = 0.0; m22 = 1.0; } /** * Replaces the matrix with its adjoint. */ private final void makeAdjoint() { double m00p = m11*m22 - m12*m21; double m01p = m12*m20 - m10*m22; // flipped sign double m02p = m10*m21 - m11*m20; double m10p = m02*m21 - m01*m22; // flipped sign double m11p = m00*m22 - m02*m20; double m12p = m01*m20 - m00*m21; // flipped sign double m20p = m01*m12 - m02*m11; double m21p = m02*m10 - m00*m12; // flipped sign double m22p = m00*m11 - m01*m10; // Transpose and copy sub-determinants m00 = m00p; m01 = m10p; m02 = m20p; m10 = m01p; m11 = m11p; m12 = m21p; m20 = m02p; m21 = m12p; m22 = m22p; } /** * Scales the matrix elements so m22 is equal to 1.0. * m22 must not be equal to 0. */ private final void normalize() { double invscale = 1.0/m22; m00 *= invscale; m01 *= invscale; m02 *= invscale; m10 *= invscale; m11 *= invscale; m12 *= invscale; m20 *= invscale; m21 *= invscale; m22 = 1.0; } private static final void getSquareToQuad(double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3, PerspectiveTransform tx) { double dx3 = x0 - x1 + x2 - x3; double dy3 = y0 - y1 + y2 - y3; tx.m22 = 1.0F; if ((dx3 == 0.0F) && (dy3 == 0.0F)) { // to do: use tolerance tx.m00 = x1 - x0; tx.m01 = x2 - x1; tx.m02 = x0; tx.m10 = y1 - y0; tx.m11 = y2 - y1; tx.m12 = y0; tx.m20 = 0.0F; tx.m21 = 0.0F; } else { double dx1 = x1 - x2; double dy1 = y1 - y2; double dx2 = x3 - x2; double dy2 = y3 - y2; double invdet = 1.0F/(dx1*dy2 - dx2*dy1); tx.m20 = (dx3*dy2 - dx2*dy3)*invdet; tx.m21 = (dx1*dy3 - dx3*dy1)*invdet; tx.m00 = x1 - x0 + tx.m20*x1; tx.m01 = x3 - x0 + tx.m21*x3; tx.m02 = x0; tx.m10 = y1 - y0 + tx.m20*y1; tx.m11 = y3 - y0 + tx.m21*y3; tx.m12 = y0; } } /** * Creates a PerspectiveTransform that maps the unit square * onto an arbitrary quadrilateral. * *
         * (0, 0) -> (x0, y0)
         * (1, 0) -> (x1, y1)
         * (1, 1) -> (x2, y2)
         * (0, 1) -> (x3, y3)
         * 
    */ public static PerspectiveTransform getSquareToQuad(double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3) { PerspectiveTransform tx = new PerspectiveTransform(); getSquareToQuad(x0, y0, x1, y1, x2, y2, x3, y3, tx); return tx; } /** * Creates a PerspectiveTransform that maps the unit square * onto an arbitrary quadrilateral. * *
         * (0, 0) -> (x0, y0)
         * (1, 0) -> (x1, y1)
         * (1, 1) -> (x2, y2)
         * (0, 1) -> (x3, y3)
         * 
    */ public static PerspectiveTransform getSquareToQuad(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3) { return getSquareToQuad((double)x0, (double)y0, (double)x1, (double)y1, (double)x2, (double)y2, (double)x3, (double)y3); } /** * Creates a PerspectiveTransform that maps an arbitrary * quadrilateral onto the unit square. * *
         * (x0, y0) -> (0, 0)
         * (x1, y1) -> (1, 0)
         * (x2, y2) -> (1, 1)
         * (x3, y3) -> (0, 1)
         * 
    */ public static PerspectiveTransform getQuadToSquare(double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3) { PerspectiveTransform tx = new PerspectiveTransform(); getSquareToQuad(x0, y0, x1, y1, x2, y2, x3, y3, tx); tx.makeAdjoint(); return tx; } /** * Creates a PerspectiveTransform that maps an arbitrary * quadrilateral onto the unit square. * *
         * (x0, y0) -> (0, 0)
         * (x1, y1) -> (1, 0)
         * (x2, y2) -> (1, 1)
         * (x3, y3) -> (0, 1)
         * 
    */ public static PerspectiveTransform getQuadToSquare(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3) { return getQuadToSquare((double)x0, (double)y0, (double)x1, (double)y1, (double)x2, (double)y2, (double)x3, (double)y3); } /** * Creates a PerspectiveTransform that maps an arbitrary * quadrilateral onto another arbitrary quadrilateral. * *
         * (x0, y0) -> (x0p, y0p)
         * (x1, y1) -> (x1p, y1p)
         * (x2, y2) -> (x2p, y2p)
         * (x3, y3) -> (x3p, y3p)
         * 
    */ public static PerspectiveTransform getQuadToQuad(double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3, double x0p, double y0p, double x1p, double y1p, double x2p, double y2p, double x3p, double y3p) { PerspectiveTransform tx1 = getQuadToSquare(x0, y0, x1, y1, x2, y2, x3, y3); PerspectiveTransform tx2 = getSquareToQuad(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p); tx1.concatenate(tx2); return tx1; } /** * Creates a PerspectiveTransform that maps an arbitrary * quadrilateral onto another arbitrary quadrilateral. * *
         * (x0, y0) -> (x0p, y0p)
         * (x1, y1) -> (x1p, y1p)
         * (x2, y2) -> (x2p, y2p)
         * (x3, y3) -> (x3p, y3p)
         * 
    */ public static PerspectiveTransform getQuadToQuad(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float x0p, float y0p, float x1p, float y1p, float x2p, float y2p, float x3p, float y3p) { return getQuadToQuad((double)x0, (double)y0, (double)x1, (double)y1, (double)x2, (double)y2, (double)x3, (double)y3, (double)x0p, (double)y0p, (double)x1p, (double)y1p, (double)x2p, (double)y2p, (double)x3p, (double)y3p); } /** * Returns the determinant of the matrix representation of the * transform. */ public double getDeterminant() { return ( (m00 * ((m11 * m22) - (m12 * m21))) - (m01 * ((m10 * m22) - (m12 * m20))) + (m02 * ((m10 * m21) - (m11 * m20))) ); } /** * Retrieves the 9 specifiable values in the 3x3 affine * transformation matrix into an array of double precision values. * The values are stored into the array as * { m00 m01 m02 m10 m11 m12 m20 m21 m22 }. * * @param flatmatrix The double array used to store the returned * values. The length of the array is assumed to be at * least 9. * @throws ArrayIndexOutOfBoundsException if flatmatrix is too small * @deprecated as of JAI 1.1 Use double[][] getMatrix(double[][] matrix) instead. */ public double[] getMatrix(double[] flatmatrix) { if (flatmatrix == null) { flatmatrix = new double[9]; } flatmatrix[0] = m00; flatmatrix[1] = m01; flatmatrix[2] = m02; flatmatrix[3] = m10; flatmatrix[4] = m11; flatmatrix[5] = m12; flatmatrix[6] = m20; flatmatrix[7] = m21; flatmatrix[8] = m22; return flatmatrix; } /** * Retrieves the 9 specifiable values in the 3x3 affine * transformation matrix into a 2-dimensional array of double * precision values. The values are stored into the 2-dimensional * array using the row index as the first subscript and the column * index as the second. * * @param matrix The 2-dimensional double array to store the * returned values. The array is assumed to be at least 3x3. * @throws ArrayIndexOutOfBoundsException if matrix is too small */ public double[][] getMatrix(double[][] matrix) { if (matrix == null) { matrix = new double[3][3]; } matrix[0][0] = m00; matrix[0][1] = m01; matrix[0][2] = m02; matrix[1][0] = m10; matrix[1][1] = m11; matrix[1][2] = m12; matrix[2][0] = m20; matrix[2][1] = m21; matrix[2][2] = m22; return matrix; } /** * Concatenates this transform with a translation transformation. * This is equivalent to calling concatenate(T), where T is an * PerspectiveTransform represented by the following matrix: *
         *		[   1    0    tx  ]
         *		[   0    1    ty  ]
         *		[   0    0    1   ]
         * 
    */ public void translate(double tx, double ty) { PerspectiveTransform Tx = new PerspectiveTransform(); Tx.setToTranslation(tx, ty); concatenate(Tx); } /** * Concatenates this transform with a rotation transformation. * This is equivalent to calling concatenate(R), where R is an * PerspectiveTransform represented by the following matrix: *
         *		[   cos(theta)    -sin(theta)    0   ]
         *		[   sin(theta)     cos(theta)    0   ]
         *		[       0              0         1   ]
         * 
    * Rotating with a positive angle theta rotates points on the positive * X axis toward the positive Y axis. * * @param theta The angle of rotation in radians. */ public void rotate(double theta) { PerspectiveTransform Tx = new PerspectiveTransform(); Tx.setToRotation(theta); concatenate(Tx); } /** * Concatenates this transform with a translated rotation transformation. * This is equivalent to the following sequence of calls: *
         *		translate(x, y);
         *		rotate(theta);
         *		translate(-x, -y);
         * 
    * Rotating with a positive angle theta rotates points on the positive * X axis toward the positive Y axis. * * @param theta The angle of rotation in radians. * @param x The X coordinate of the origin of the rotation * @param y The Y coordinate of the origin of the rotation */ public void rotate(double theta, double x, double y) { PerspectiveTransform Tx = new PerspectiveTransform(); Tx.setToRotation(theta, x, y); concatenate(Tx); } /** * Concatenates this transform with a scaling transformation. * This is equivalent to calling concatenate(S), where S is an * PerspectiveTransform represented by the following matrix: *
         *		[   sx   0    0   ]
         *		[   0    sy   0   ]
         *		[   0    0    1   ]
         * 
    * * @param sx The X axis scale factor. * @param sy The Y axis scale factor. */ public void scale(double sx, double sy) { PerspectiveTransform Tx = new PerspectiveTransform(); Tx.setToScale(sx, sy); concatenate(Tx); } /** * Concatenates this transform with a shearing transformation. * This is equivalent to calling concatenate(SH), where SH is an * PerspectiveTransform represented by the following matrix: *
         *		[   1   shx   0   ]
         *		[  shy   1    0   ]
         *		[   0    0    1   ]
         * 
    * * @param shx The factor by which coordinates are shifted towards * the positive X axis direction according to their Y * coordinate. * @param shy The factor by which coordinates are shifted towards * the positive Y axis direction according to their X * coordinate. */ public void shear(double shx, double shy) { PerspectiveTransform Tx = new PerspectiveTransform(); Tx.setToShear(shx, shy); concatenate(Tx); } /** * Resets this transform to the Identity transform. */ public void setToIdentity() { m00 = m11 = m22 = 1.0; m01 = m10 = m02 = m20 = m12 = m21 = 0.0; } /** * Sets this transform to a translation transformation. * The matrix representing this transform becomes: *
         *		[   1    0    tx  ]
         *		[   0    1    ty  ]
         *		[   0    0    1   ]
         * 
    * @param tx The distance by which coordinates are translated in the * X axis direction * @param ty The distance by which coordinates are translated in the * Y axis direction */ public void setToTranslation(double tx, double ty) { m00 = 1.0; m01 = 0.0; m02 = tx; m10 = 0.0; m11 = 1.0; m12 = ty; m20 = 0.0; m21 = 0.0; m22 = 1.0; } /** * Sets this transform to a rotation transformation. * The matrix representing this transform becomes: *
         *		[   cos(theta)    -sin(theta)    0   ]
         *		[   sin(theta)     cos(theta)    0   ]
         *		[       0              0         1   ]
         * 
    * Rotating with a positive angle theta rotates points on the positive * X axis toward the positive Y axis. * @param theta The angle of rotation in radians. */ public void setToRotation(double theta) { m00 = Math.cos(theta); m01 = -Math.sin(theta); m02 = 0.0; m10 = - m01; // Math.sin(theta); m11 = m00; // Math.cos(theta); m12 = 0.0; m20 = 0.0; m21 = 0.0; m22 = 1.0; } /** * Sets this transform to a rotation transformation * about a specified point (x, y). This is equivalent * to the following sequence of calls: * *
         *		setToTranslate(x, y);
         *		rotate(theta);
         *		translate(-x, -y);
         * 
    * * Rotating with a positive angle theta rotates points on the positive * X axis toward the positive Y axis. * * @param theta The angle of rotation in radians. * @param x The X coordinate of the origin of the rotation * @param y The Y coordinate of the origin of the rotation */ public void setToRotation(double theta, double x, double y) { setToRotation(theta); double sin = m10; double oneMinusCos = 1.0 - m00; m02 = x * oneMinusCos + y * sin; m12 = y * oneMinusCos - x * sin; } /** * Sets this transform to a scale transformation * with scale factors sx and sy. * The matrix representing this transform becomes: *
         *		[   sx   0    0   ]
         *		[   0    sy   0   ]
         *		[   0    0    1   ]
         * 
    * * @param sx The X axis scale factor. * @param sy The Y axis scale factor. */ public void setToScale(double sx, double sy) { m00 = sx; m01 = 0.0; m02 = 0.0; m10 = 0.0; m11 = sy; m12 = 0.0; m20 = 0.0; m21 = 0.0; m22 = 1.0; } /** * Sets this transform to a shearing transformation * with shear factors sx and sy. * The matrix representing this transform becomes: *
         *		[   1  shx    0   ]
         *		[ shy    1    0   ]
         *		[   0    0    1   ]
         * 
    * * @param shx The factor by which coordinates are shifted towards * the positive X axis direction according to their Y * coordinate. * @param shy The factor by which coordinates are shifted towards * the positive Y axis direction according to their X * coordinate. */ public void setToShear(double shx, double shy) { m00 = 1.0; m01 = shx; m02 = 0.0; m10 = shy; m11 = 1.0; m12 = 0.0; m20 = 0.0; m21 = 0.0; m22 = 1.0; } /** * Sets this transform to a given AffineTransform. * @throws IllegalArgumentException if Tx is null */ public void setTransform(AffineTransform Tx) { if ( Tx == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } m00 = Tx.getScaleX(); m01 = Tx.getShearX(); m02 = Tx.getTranslateX(); m10 = Tx.getShearY(); m11 = Tx.getScaleY(); m12 = Tx.getTranslateY(); m20 = 0.0; m21 = 0.0; m22 = 1.0; } /** * Sets this transform to a given PerspectiveTransform. * @throws IllegalArgumentException if Tx is null */ public void setTransform(PerspectiveTransform Tx) { if ( Tx == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } m00 = Tx.m00; m01 = Tx.m01; m02 = Tx.m02; m10 = Tx.m10; m11 = Tx.m11; m12 = Tx.m12; m20 = Tx.m20; m21 = Tx.m21; m22 = Tx.m22; } /** * Sets this transform to a given PerspectiveTransform, * expressed by the elements of its matrix. Important Note: The * matrix elements in the argument list are in column-major order * unlike those of the constructor, which are in row-major order. * @deprecated as of JAI 1.1 Use double[][] getMatrix(double[][] matrix) instead. */ public void setTransform(float m00, float m10, float m20, float m01, float m11, float m21, float m02, float m12, float m22) { this.m00 = (double)m00; this.m01 = (double)m01; this.m02 = (double)m02; this.m10 = (double)m10; this.m11 = (double)m11; this.m12 = (double)m12; this.m20 = (double)m20; this.m21 = (double)m21; this.m22 = (double)m22; } /** * Sets this transform using a two-dimensional array of double precision * values. The row index is first, and the column index is second. * * @param matrix The 2D double array to be used for setting this transform. * The array is assumed to be at least 3x3. * @throws IllegalArgumentException if matrix is null * @throws ArrayIndexOutOfBoundsException if matrix is too small * @since JAI 1.1 */ public void setTransform(double[][] matrix) { if ( matrix == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } m00 = matrix[0][0]; m01 = matrix[0][1]; m02 = matrix[0][2]; m10 = matrix[1][0]; m11 = matrix[1][1]; m12 = matrix[1][2]; m20 = matrix[2][0]; m21 = matrix[2][1]; m22 = matrix[2][2]; } /** * Post-concatenates a given AffineTransform to this transform. * @throws IllegalArgumentException if Tx is null */ public void concatenate(AffineTransform Tx) { if ( Tx == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } // Extend Tx: Tx.m20 = 0, Tx.m21 = 0, Tx.m22 = 1 double tx_m00 = Tx.getScaleX(); double tx_m01 = Tx.getShearX(); double tx_m02 = Tx.getTranslateX(); double tx_m10 = Tx.getShearY(); double tx_m11 = Tx.getScaleY(); double tx_m12 = Tx.getTranslateY(); double m00p = m00*tx_m00 + m10*tx_m01 + m20*tx_m02; double m01p = m01*tx_m00 + m11*tx_m01 + m21*tx_m02; double m02p = m02*tx_m00 + m12*tx_m01 + m22*tx_m02; double m10p = m00*tx_m10 + m10*tx_m11 + m20*tx_m12; double m11p = m01*tx_m10 + m11*tx_m11 + m21*tx_m12; double m12p = m02*tx_m10 + m12*tx_m11 + m22*tx_m12; double m20p = m20; double m21p = m21; double m22p = m22; m00 = m00p; m10 = m10p; m20 = m20p; m01 = m01p; m11 = m11p; m21 = m21p; m02 = m02p; m12 = m12p; m22 = m22p; } /** * Post-concatenates a given PerspectiveTransform to this transform. * @throws IllegalArgumentException if Tx is null */ public void concatenate(PerspectiveTransform Tx) { if ( Tx == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } double m00p = m00*Tx.m00 + m10*Tx.m01 + m20*Tx.m02; double m10p = m00*Tx.m10 + m10*Tx.m11 + m20*Tx.m12; double m20p = m00*Tx.m20 + m10*Tx.m21 + m20*Tx.m22; double m01p = m01*Tx.m00 + m11*Tx.m01 + m21*Tx.m02; double m11p = m01*Tx.m10 + m11*Tx.m11 + m21*Tx.m12; double m21p = m01*Tx.m20 + m11*Tx.m21 + m21*Tx.m22; double m02p = m02*Tx.m00 + m12*Tx.m01 + m22*Tx.m02; double m12p = m02*Tx.m10 + m12*Tx.m11 + m22*Tx.m12; double m22p = m02*Tx.m20 + m12*Tx.m21 + m22*Tx.m22; m00 = m00p; m10 = m10p; m20 = m20p; m01 = m01p; m11 = m11p; m21 = m21p; m02 = m02p; m12 = m12p; m22 = m22p; } /** * Pre-concatenates a given AffineTransform to this transform. * @throws IllegalArgumentException if Tx is null */ public void preConcatenate(AffineTransform Tx) { if ( Tx == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } // Extend Tx: Tx.m20 = 0, Tx.m21 = 0, Tx.m22 = 1 double tx_m00 = Tx.getScaleX(); double tx_m01 = Tx.getShearX(); double tx_m02 = Tx.getTranslateX(); double tx_m10 = Tx.getShearY(); double tx_m11 = Tx.getScaleY(); double tx_m12 = Tx.getTranslateY(); double m00p = tx_m00*m00 + tx_m10*m01; double m01p = tx_m01*m00 + tx_m11*m01; double m02p = tx_m02*m00 + tx_m12*m01 + m02; double m10p = tx_m00*m10 + tx_m10*m11; double m11p = tx_m01*m10 + tx_m11*m11; double m12p = tx_m02*m10 + tx_m12*m11 + m12; double m20p = tx_m00*m20 + tx_m10*m21; double m21p = tx_m01*m20 + tx_m11*m21; double m22p = tx_m02*m20 + tx_m12*m21 + m22; m00 = m00p; m10 = m10p; m20 = m20p; m01 = m01p; m11 = m11p; m21 = m21p; m02 = m02p; m12 = m12p; m22 = m22p; } /** * Pre-concatenates a given PerspectiveTransform to this transform. * @throws IllegalArgumentException if Tx is null */ public void preConcatenate(PerspectiveTransform Tx) { if ( Tx == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } double m00p = Tx.m00*m00 + Tx.m10*m01 + Tx.m20*m02; double m10p = Tx.m00*m10 + Tx.m10*m11 + Tx.m20*m12; double m20p = Tx.m00*m20 + Tx.m10*m21 + Tx.m20*m22; double m01p = Tx.m01*m00 + Tx.m11*m01 + Tx.m21*m02; double m11p = Tx.m01*m10 + Tx.m11*m11 + Tx.m21*m12; double m21p = Tx.m01*m20 + Tx.m11*m21 + Tx.m21*m22; double m02p = Tx.m02*m00 + Tx.m12*m01 + Tx.m22*m02; double m12p = Tx.m02*m10 + Tx.m12*m11 + Tx.m22*m12; double m22p = Tx.m02*m20 + Tx.m12*m21 + Tx.m22*m22; m00 = m00p; m10 = m10p; m20 = m20p; m01 = m01p; m11 = m11p; m21 = m21p; m02 = m02p; m12 = m12p; m22 = m22p; } /** * Returns a new PerpectiveTransform that is the inverse * of the current transform. * @throws NoninvertibleTransformException if transform cannot be inverted */ public PerspectiveTransform createInverse() throws NoninvertibleTransformException, CloneNotSupportedException { PerspectiveTransform tx = (PerspectiveTransform)clone(); tx.makeAdjoint(); if (Math.abs(tx.m22) < PERSPECTIVE_DIVIDE_EPSILON) { throw new NoninvertibleTransformException(JaiI18N.getString("PerspectiveTransform0")); } tx.normalize(); return tx; } /** * Returns a new PerpectiveTransform that is the adjoint, * of the current transform. The adjoint is defined as * the matrix of cofactors, which in turn are the determinants * of the submatrices defined by removing the row and column * of each element from the original matrix in turn. * *

    The adjoint is a scalar multiple of the inverse matrix. * Because points to be transformed are converted into homogeneous * coordinates, where scalar factors are irrelevant, the adjoint * may be used in place of the true inverse. Since it is unnecessary * to normalize the adjoint, it is both faster to compute and more * numerically stable than the true inverse. */ public PerspectiveTransform createAdjoint() throws CloneNotSupportedException{ PerspectiveTransform tx = (PerspectiveTransform)clone(); tx.makeAdjoint(); return tx; } /** * Transforms the specified ptSrc and stores the result in ptDst. * If ptDst is null, a new Point2D object will be allocated before * storing. In either case, ptDst containing the transformed point * is returned for convenience. * Note that ptSrc and ptDst can the same. In this case, the input * point will be overwritten with the transformed point. * * @param ptSrc The array containing the source point objects. * @param ptDst The array where the transform point objects are returned. * @throws IllegalArgumentException if ptSrc is null */ public Point2D transform(Point2D ptSrc, Point2D ptDst) { if ( ptSrc == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (ptDst == null) { if (ptSrc instanceof Point2D.Double) { ptDst = new Point2D.Double(); } else { ptDst = new Point2D.Float(); } } double x = ptSrc.getX(); double y = ptSrc.getY(); double w = m20 * x + m21 * y + m22; ptDst.setLocation((m00 * x + m01 * y + m02) / w, (m10 * x + m11 * y + m12) / w); return ptDst; } /** * Transforms an array of point objects by this transform. * @param ptSrc The array containing the source point objects. * @param ptDst The array where the transform point objects are returned. * @param srcOff The offset to the first point object to be transformed * in the source array. * @param dstOff The offset to the location where the first transformed * point object is stored in the destination array. * @param numPts The number of point objects to be transformed. * @throws IllegalArgumentException if ptSrc is null * @throws IllegalArgumentException if ptDst is null * @throws ArrayIndexOutOfBoundsException if ptSrc is too small */ public void transform(Point2D[] ptSrc, int srcOff, Point2D[] ptDst, int dstOff, int numPts) { if ( ptSrc == null || ptDst == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } while (numPts-- > 0) { /* Copy source coords into local variables in case src == dst. */ Point2D src = ptSrc[srcOff++]; Point2D dst = ptDst[dstOff++]; if (dst == null) { if (src instanceof Point2D.Double) { dst = new Point2D.Double(); } else { dst = new Point2D.Float(); } ptDst[dstOff - 1] = dst; } double x = src.getX(); double y = src.getY(); double w = m20 * x + m21 * y + m22; if (w == 0) { dst.setLocation(x, y); } else { dst.setLocation((m00 * x + m01 * y + m02) / w, (m10 * x + m11 * y + m12) / w); } } } /** * Transforms an array of floating point coordinates by this transform. * @param srcPts The array containing the source point coordinates. * Each point is stored as a pair of x,y coordinates. * @param srcOff The offset to the first point to be transformed * in the source array. * @param dstPts The array where the transformed point coordinates are * returned. Each point is stored as a pair of x,y coordinates. * @param dstOff The offset to the location where the first transformed * point is stored in the destination array. * @param numPts The number of points to be transformed. * @throws IllegalArgumentException if srcPts is null * @throws ArrayIndexOutOfBoundsException if srcPts is too small */ public void transform(float[] srcPts, int srcOff, float[] dstPts, int dstOff, int numPts) { if ( srcPts == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (dstPts == null) { dstPts = new float[numPts * 2 + dstOff]; } while (numPts-- > 0) { float x = srcPts[srcOff++]; float y = srcPts[srcOff++]; double w = m20 * x + m21 * y + m22; if (w == 0) { dstPts[dstOff++] = x; dstPts[dstOff++] = y; } else { dstPts[dstOff++] = (float)((m00 * x + m01 * y + m02) / w); dstPts[dstOff++] = (float)((m10 * x + m11 * y + m12) / w); } } } /** * Transforms an array of double precision coordinates by this transform. * @param srcPts The array containing the source point coordinates. * Each point is stored as a pair of x,y coordinates. * @param dstPts The array where the transformed point coordinates are * returned. Each point is stored as a pair of x,y coordinates. * @param srcOff The offset to the first point to be transformed * in the source array. * @param dstOff The offset to the location where the first transformed * point is stored in the destination array. * @param numPts The number of point objects to be transformed. * @throws IllegalArgumentException if srcPts is null * @throws ArrayIndexOutOfBoundsException if srcPts is too small */ public void transform(double[] srcPts, int srcOff, double[] dstPts, int dstOff, int numPts) { if ( srcPts == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (dstPts == null) { dstPts = new double[numPts * 2 + dstOff]; } while (numPts-- > 0) { double x = srcPts[srcOff++]; double y = srcPts[srcOff++]; double w = m20 * x + m21 * y + m22; if (w == 0) { dstPts[dstOff++] = x; dstPts[dstOff++] = y; } else { dstPts[dstOff++] = (m00 * x + m01 * y + m02) / w; dstPts[dstOff++] = (m10 * x + m11 * y + m12) / w; } } } /** * Transforms an array of floating point coordinates by this transform, * storing the results into an array of doubles. * @param srcPts The array containing the source point coordinates. * Each point is stored as a pair of x,y coordinates. * @param srcOff The offset to the first point to be transformed * in the source array. * @param dstPts The array where the transformed point coordinates are * returned. Each point is stored as a pair of x,y coordinates. * @param dstOff The offset to the location where the first transformed * point is stored in the destination array. * @param numPts The number of points to be transformed. * @throws IllegalArgumentException if srcPts is null * @throws ArrayIndexOutOfBoundsException if srcPts is too small */ public void transform(float[] srcPts, int srcOff, double[] dstPts, int dstOff, int numPts) { if ( srcPts == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (dstPts == null) { dstPts = new double[numPts * 2 + dstOff]; } while (numPts-- > 0) { float x = srcPts[srcOff++]; float y = srcPts[srcOff++]; double w = m20 * x + m21 * y + m22; if (w == 0) { dstPts[dstOff++] = x; dstPts[dstOff++] = y; } else { dstPts[dstOff++] = (m00 * x + m01 * y + m02) / w; dstPts[dstOff++] = (m10 * x + m11 * y + m12) / w; } } } /** * Transforms an array of double precision coordinates by this transform, * storing the results into an array of floats. * @param srcPts The array containing the source point coordinates. * Each point is stored as a pair of x,y coordinates. * @param dstPts The array where the transformed point coordinates are * returned. Each point is stored as a pair of x,y coordinates. * @param srcOff The offset to the first point to be transformed * in the source array. * @param dstOff The offset to the location where the first transformed * point is stored in the destination array. * @param numPts The number of point objects to be transformed. * @throws IllegalArgumentException if srcPts is null * @throws ArrayIndexOutOfBoundsException if srcPts is too small */ public void transform(double[] srcPts, int srcOff, float[] dstPts, int dstOff, int numPts) { if ( srcPts == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (dstPts == null) { dstPts = new float[numPts * 2 + dstOff]; } while (numPts-- > 0) { double x = srcPts[srcOff++]; double y = srcPts[srcOff++]; double w = m20 * x + m21 * y + m22; if (w == 0) { dstPts[dstOff++] = (float)x; dstPts[dstOff++] = (float)y; } else { dstPts[dstOff++] = (float)((m00 * x + m01 * y + m02) / w); dstPts[dstOff++] = (float)((m10 * x + m11 * y + m12) / w); } } } /** * Inverse transforms the specified ptSrc and stores the result in ptDst. * If ptDst is null, a new Point2D object will be allocated before * storing. In either case, ptDst containing the transformed point * is returned for convenience. * Note that ptSrc and ptDst can the same. In this case, the input * point will be overwritten with the transformed point. * @param ptSrc The point to be inverse transformed. * @param ptDst The resulting transformed point. * @throws NoninvertibleTransformException if the matrix cannot be * inverted. * @throws IllegalArgumentException if ptSrc is null */ public Point2D inverseTransform(Point2D ptSrc, Point2D ptDst) throws NoninvertibleTransformException { if ( ptSrc == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (ptDst == null) { if (ptSrc instanceof Point2D.Double) { ptDst = new Point2D.Double(); } else { ptDst = new Point2D.Float(); } } // Copy source coords into local variables in case src == dst double x = ptSrc.getX(); double y = ptSrc.getY(); double tmp_x = (m11*m22 - m12*m21) * x + (m02*m21 - m01*m22) * y + (m01*m12 - m02*m11); double tmp_y = (m12*m20 - m10*m22) * x + (m00*m22 - m02*m20) * y + (m02*m10 - m00*m12); double w = (m10*m21 - m11*m20) * x + (m01*m20 - m00*m21) * y + (m00*m11 - m01*m10); double wabs = w; if (w < 0) { wabs = - w; } if (wabs < PERSPECTIVE_DIVIDE_EPSILON) { throw new NoninvertibleTransformException( JaiI18N.getString("PerspectiveTransform1")); } ptDst.setLocation(tmp_x/w, tmp_y/w); return ptDst; } /** * Inverse transforms an array of double precision coordinates by * this transform. * @param srcPts The array containing the source point coordinates. * Each point is stored as a pair of x,y coordinates. * @param dstPts The array where the transformed point coordinates are * returned. Each point is stored as a pair of x,y coordinates. * @param srcOff The offset to the first point to be transformed * in the source array. * @param dstOff The offset to the location where the first transformed * point is stored in the destination array. * @param numPts The number of point objects to be transformed. * @throws NoninvertibleTransformException if the matrix cannot be * inverted. * @throws IllegalArgumentException if srcPts is null * @throws ArrayIndexOutOfBoundsException if srcPts is too small * @throws NoninvertibleTransformException transform cannot be inverted */ public void inverseTransform(double[] srcPts, int srcOff, double[] dstPts, int dstOff, int numPts) throws NoninvertibleTransformException { if ( srcPts == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (dstPts == null) { dstPts = new double[numPts * 2 + dstOff]; } while (numPts-- > 0) { double x = srcPts[srcOff++]; double y = srcPts[srcOff++]; double tmp_x = (m11*m22 - m12*m21) * x + (m02*m21 - m01*m22) * y + (m01*m12 - m02*m11); double tmp_y = (m12*m20 - m10*m22) * x + (m00*m22 - m02*m20) * y + (m02*m10 - m00*m12); double w = (m10*m21 - m11*m20) * x + (m01*m20 - m00*m21) * y + (m00*m11 - m01*m10); double wabs = w; if (w < 0) { wabs = - w; } if (wabs < PERSPECTIVE_DIVIDE_EPSILON) { throw new NoninvertibleTransformException( JaiI18N.getString("PerspectiveTransform1")); } dstPts[dstOff++] = tmp_x / w; dstPts[dstOff++] = tmp_y / w; } } /** * Returns a String that represents the value of this Object. */ public String toString() { StringBuffer sb = new StringBuffer(); sb.append("Perspective transform matrix\n"); sb.append(this.m00); sb.append("\t"); sb.append(this.m01); sb.append("\t"); sb.append(this.m02); sb.append("\n"); sb.append(this.m10); sb.append("\t"); sb.append(this.m11); sb.append("\t"); sb.append(this.m12); sb.append("\n"); sb.append(this.m20); sb.append("\t"); sb.append(this.m21); sb.append("\t"); sb.append(this.m22); sb.append("\n"); return new String(sb); } /** * Returns the boolean true value if this PerspectiveTransform is an * identity transform. Returns false otherwise. */ public boolean isIdentity() { return m01 == 0.0 && m02 == 0.0 && m10 == 0.0 && m12 == 0.0 && m20 == 0.0 && m21 == 0.0 && m22 != 0.0 && m00/m22 == 1.0 && m11/m22 == 1.0; } /** * Returns a copy of this PerspectiveTransform object. */ public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { // this shouldn't happen, since we are Cloneable throw new InternalError(); } } /** * Tests if this PerspectiveTransform equals a supplied one. * * @param obj The PerspectiveTransform to be compared to this one. */ public boolean equals(Object obj) { if (!(obj instanceof PerspectiveTransform)) { return false; } PerspectiveTransform a = (PerspectiveTransform)obj; return ((m00 == a.m00) && (m10 == a.m10) && (m20 == a.m20) && (m01 == a.m01) && (m11 == a.m11) && (m21 == a.m21) && (m02 == a.m02) && (m12 == a.m12) && (m22 == a.m22)); } } jai-core-1.1.4/src/share/classes/javax/media/jai/RenderableGraphics.java0000644000175000017500000012202010203035544025721 0ustar mathieumathieu/* * $RCSfile: RenderableGraphics.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:20 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Color; import java.awt.Composite; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.Image; import java.awt.Paint; import java.awt.PaintContext; import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.RenderingHints.Key; import java.awt.Shape; import java.awt.Stroke; import java.awt.Toolkit; import java.awt.color.ColorSpace; import java.awt.font.FontRenderContext; import java.awt.font.GlyphVector; import java.awt.font.TextLayout; import java.awt.geom.AffineTransform; import java.awt.geom.Area; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.image.BufferedImageOp; import java.awt.image.ColorModel; import java.awt.image.ComponentColorModel; import java.awt.image.DataBuffer; import java.awt.image.ImageConsumer; import java.awt.image.ImageObserver; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.awt.image.WritableRenderedImage; import java.awt.image.renderable.RenderableImage; import java.awt.image.renderable.RenderContext; import java.lang.reflect.Method; import java.text.AttributedCharacterIterator; import java.util.LinkedList; import java.util.ListIterator; import java.util.Map; import java.util.Vector; import javax.media.jai.util.ImagingException; import javax.media.jai.util.ImagingListener; import com.sun.media.jai.util.JDKWorkarounds; /** * An implementation of Graphics2D with * RenderableImage semantics. In other words, content may be * drawn into the image using the Graphics2D interface and later * be turned into RenderedImages with different resolutions and * characteristics. * *

    A RenderableGraphics occupies a region of the plane * specified at the time of construction. * *

    The contents of RenderableImages that are drawn onto a * RenderableGraphics are accessed only at the time of rendering, * not the time of drawing. * *

    Since the methods of this class all derive from Graphics2D * and RenderableImage, they are not all commented individually. * * @see java.awt.Graphics2D * @see java.awt.image.renderable.RenderableImage */ public class RenderableGraphics extends Graphics2D implements RenderableImage { // Constants private static final Class GRAPHICS2D_CLASS = Graphics2D.class; // Bounding rectangle private Rectangle2D dimensions; // Linked list of (Method, Object[]) pairs. private LinkedList opArgList; // Graphics state information private Point origin; private Shape clip; private Color color; private Font font; // Graphics2D state information private Color background; private Composite composite; private Paint paint; private Stroke stroke; private RenderingHints renderingHints = new RenderingHints(null); private AffineTransform transform; /** * Constructs a RenderableGraphics given a bounding * Rectangle2D. * * @param dimensions The bounding Rectangle2D. */ public RenderableGraphics(Rectangle2D dimensions) { this(dimensions, new LinkedList(), new Point(0, 0), null); } /** * Constructs a RenderableGraphics given a bounding * Rectangle2D, an origin, and a Graphics2D * object from which to initialize the RenderableGraphics * state. The Graphics2D may be null. * * @param dimensions The bounding Rectangle2D. * @param opArgList The list of operations and arguments. * @param dimensions The origin. * @param dimensions The Graphics2D state source; may be null. */ private RenderableGraphics(Rectangle2D dimensions, LinkedList opArgList, Point origin, Graphics2D g) { if(dimensions.isEmpty()) { throw new RuntimeException(JaiI18N.getString("RenderableGraphics0")); } // -- RenderableGraphics state -- this.dimensions = dimensions; this.opArgList = opArgList; // Use the Graphics2D passed in or create one. Graphics2D g2d = g; if(g2d == null) { g2d = getBogusGraphics2D(); } // -- java.awt.Graphics state -- this.origin = (Point)origin.clone(); setClip(g2d.getClip()); setColor(g2d.getColor()); setFont(g2d.getFont()); // -- java.awt.Graphics2D state -- setBackground(g2d.getBackground()); setComposite(g2d.getComposite()); setRenderingHints(g2d.getRenderingHints()); setStroke(g2d.getStroke()); setTransform(g2d.getTransform()); // Dispose of the Graphics2D if it was created within this method. if(g == null) g2d.dispose(); } /** * Creates a bogus Graphics2D object to be used to retrieve * information dependent on system aspects which are image-independent. * *

    The dispose() method of the Graphics2D * object returned should be called to free the associated resources as\ * soon as possible. * * @return A Graphics2D object. */ private Graphics2D getBogusGraphics2D() { TiledImage ti = createTiledImage(renderingHints, dimensions.getBounds()); return ti.createGraphics(); } /** * Create a TiledImage to be used as the canvas. * * @param hints RenderingHints from which to derive an ImageLayout. * @param bounds The bounding box of the TiledImage. * * @return A TiledImage. */ private TiledImage createTiledImage(RenderingHints hints, Rectangle bounds) { // Set the default tile size. int tileWidth = bounds.width; int tileHeight = bounds.height; // Retrieve layout information from the hints. The tile size hints // are ignored if a SampleModel hint is supplied. Set the hints // observed if any hints are used. SampleModel sm = null; ColorModel cm = null; RenderingHints hintsObserved = null; if(hints != null) { // Get the ImageLayout. ImageLayout layout = (ImageLayout)hints.get(JAI.KEY_IMAGE_LAYOUT); if(layout != null) { // Initialize the observed hint variables. hintsObserved = new RenderingHints(null); ImageLayout layoutObserved = new ImageLayout(); // Get the SampleModel. if(layout.isValid(ImageLayout.SAMPLE_MODEL_MASK)) { sm = layout.getSampleModel(null); if(sm.getWidth() != tileWidth || sm.getHeight() != tileHeight) { sm = sm.createCompatibleSampleModel(tileWidth, tileHeight); } if(layoutObserved != null) { layoutObserved.setSampleModel(sm); } } // Get the ColorModel. if(layout.isValid(ImageLayout.COLOR_MODEL_MASK)) { cm = layout.getColorModel(null); if(layoutObserved != null) { layoutObserved.setColorModel(cm); } } // Get the tile dimensions. if(layout.isValid(ImageLayout.TILE_WIDTH_MASK)) { tileWidth = layout.getTileWidth(null); if(layoutObserved != null) { layoutObserved.setTileWidth(tileWidth); } } else if(sm != null) { tileWidth = sm.getWidth(); } if(layout.isValid(ImageLayout.TILE_HEIGHT_MASK)) { tileHeight = layout.getTileHeight(null); if(layoutObserved != null) { layoutObserved.setTileHeight(tileHeight); } } else if(sm != null) { tileHeight = sm.getHeight(); } // Set the observed hints layout. hintsObserved.put(JAI.KEY_IMAGE_LAYOUT, layoutObserved); } // layout != null } // hints != null // Ensure that the SampleModel is compatible with the tile size. if(sm != null && (sm.getWidth() != tileWidth || sm.getHeight() != tileHeight)) { sm = sm.createCompatibleSampleModel(tileWidth, tileHeight); } // Attempt to derive compatible SampleModel/ColorModel combination. if(cm != null && (sm == null || !JDKWorkarounds.areCompatibleDataModels(sm, cm))) { // If the ColorModel is non-null and the SampleModel is null // or incompatible then create a new SampleModel. sm = cm.createCompatibleSampleModel(tileWidth, tileHeight); } else if(cm == null && sm != null) { // If the ColorModel is null but the SampleModel is not, // try to guess a reasonable ColorModel. cm = PlanarImage.createColorModel(sm); // If the ColorModel is still null, set it to the RGB default // ColorModel if the latter is compatible with the SampleModel. ColorModel cmRGB = ColorModel.getRGBdefault(); if(cm == null && JDKWorkarounds.areCompatibleDataModels(sm, cmRGB)) { cm = cmRGB; } } // Create the TiledImage. TiledImage ti = null; if(sm != null) { // Use the (derived) SampleModel and ColorModel. ti = new TiledImage(bounds.x, bounds.y, bounds.width, bounds.height, bounds.x, bounds.y, sm, cm); } else { // Default to a PixelInterleaved TiledImage. ti = TiledImage.createInterleaved(bounds.x, bounds.y, bounds.width, bounds.height, 3, DataBuffer.TYPE_BYTE, tileWidth, tileHeight, new int[] {0, 1, 2}); } // Set the HINTS_OBSERVED property of the TiledImage. if(hintsObserved != null) { ti.setProperty("HINTS_OBSERVED", hintsObserved); } return ti; } /** * Queue a Graphics2D operation and its argument list in the * linked list of operations and arguments. The name of the operation and * the array of class types of its arguments are used to determine the * associated Method object. The Method object * and array of Object arguments are appended to the list as * an ordered pair of the form (Method,Object[]). * * @param name The name of the Graphics2D operation. * @param argTypes An array of the Classes of the arguments * of the specified operation. * @param args The arguments of the operation as an array of * Objects. */ private void queueOpArg(String name, Class[] argTypes, Object[] args) { // Determine the Method object associated with the Graphics2D method // having the indicated name. The search begins with the Graphics2D // class and continues to its superclasses until the Method is found. Method method = null; try { method = GRAPHICS2D_CLASS.getMethod(name, argTypes); } catch(Exception e) { String message = JaiI18N.getString("TiledGraphicsGraphics2") + name; sendExceptionToListener(message, new ImagingException(e)); // throw new RuntimeException(e.getMessage()); } // Queue the Method object and the Object[] argument array as an // ordered pair (Method, Object[]). opArgList.addLast(method); opArgList.addLast(args); } /** * Evaulate the queue of Graphics2D operations on the * specified Graphics2D object. * * @param g2d The Graphics2D on which to evaluate the * operation queue. */ private void evaluateOpList(Graphics2D g2d) { if(opArgList == null) { return; } ListIterator li = opArgList.listIterator(0); while(li.hasNext()) { Method method = (Method)li.next(); Object[] args = (Object[])li.next(); try { method.invoke(g2d, args); } catch(Exception e) { String message = JaiI18N.getString("TiledGraphicsGraphics4") + method; sendExceptionToListener(message, new ImagingException(e)); // e.printStackTrace(); // throw new RuntimeException(e.getMessage()); } } } // ---------- Methods from java.awt.Graphics ---------- public Graphics create() { return new RenderableGraphics(dimensions, opArgList, origin, this); } // public Graphics create(int x, int y, int width, int height) // -- implemented in Graphics superclass. public Color getColor() { return color; } public void setColor(Color c) { color = c; queueOpArg("setColor", new Class[] {java.awt.Color.class}, new Object[] {c}); } public void setPaintMode() { queueOpArg("setPaintMode", null, null); } public void setXORMode(Color c1) { queueOpArg("setXORMode", new Class[] {java.awt.Color.class}, new Object[] {c1}); } public Font getFont() { return font; } public void setFont(Font font) { this.font = font; queueOpArg("setFont", new Class[] {java.awt.Font.class}, new Object[] {font}); } public FontMetrics getFontMetrics(Font f) { Graphics2D g2d = getBogusGraphics2D(); FontMetrics fontMetrics = g2d.getFontMetrics(f); g2d.dispose(); return fontMetrics; } public Rectangle getClipBounds() { return clip.getBounds(); } public void clipRect(int x, int y, int width, int height) { clip(new Rectangle(x, y, width, height)); } public void setClip(int x, int y, int width, int height) { clip = new Rectangle(x, y, width, height); queueOpArg("setClip", new Class[] {int.class, int.class, int.class, int.class}, new Object[] {new Integer(x), new Integer(y), new Integer(width), new Integer(height)}); } public Shape getClip() { return clip; } public void setClip(Shape clip) { this.clip = clip; queueOpArg("setClip", new Class[] {java.awt.Shape.class}, new Object[] {clip}); } public void copyArea(int x, int y, int width, int height, int dx, int dy) { queueOpArg("copyArea", new Class[] {int.class, int.class, int.class, int.class, int.class, int.class}, new Object[] {new Integer(x), new Integer(y), new Integer(width), new Integer(height), new Integer(dx), new Integer(dy)}); } public void drawLine(int x1, int y1, int x2, int y2) { queueOpArg("drawLine", new Class[] {int.class, int.class, int.class, int.class}, new Object[] {new Integer(x1), new Integer(y1), new Integer(x2), new Integer(y2)}); } public void fillRect(int x, int y, int width, int height) { queueOpArg("fillRect", new Class[] {int.class, int.class, int.class, int.class}, new Object[] {new Integer(x), new Integer(y), new Integer(width), new Integer(height)}); } // public void drawRect(int x, int y, int width, int height) // -- implemented in Graphics superclass public void clearRect(int x, int y, int width, int height) { queueOpArg("clearRect", new Class[] {int.class, int.class, int.class, int.class}, new Object[] {new Integer(x), new Integer(y), new Integer(width), new Integer(height)}); } public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { queueOpArg("drawRoundRect", new Class[] {int.class, int.class, int.class, int.class, int.class, int.class}, new Object[] {new Integer(x), new Integer(y), new Integer(width), new Integer(height), new Integer(arcWidth), new Integer(arcHeight)}); } public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) { queueOpArg("fillRoundRect", new Class[] {int.class, int.class, int.class, int.class, int.class, int.class}, new Object[] {new Integer(x), new Integer(y), new Integer(width), new Integer(height), new Integer(arcWidth), new Integer(arcHeight)}); } // draw3DRect() is implemented in the Graphics superclass but is // overridden in Graphics2D and so must be implemented here. public void draw3DRect(int x, int y, int width, int height, boolean raised) { queueOpArg("draw3DRect", new Class[] {int.class, int.class, int.class, int.class, boolean.class}, new Object[] {new Integer(x), new Integer(y), new Integer(width), new Integer(height), new Boolean(raised)}); } // fill3DRect() is implemented in the Graphics superclass but is // overridden in Graphics2D and so must be implemented here. public void fill3DRect(int x, int y, int width, int height, boolean raised) { queueOpArg("fill3DRect", new Class[] {int.class, int.class, int.class, int.class, boolean.class}, new Object[] {new Integer(x), new Integer(y), new Integer(width), new Integer(height), new Boolean(raised)}); } public void drawOval(int x, int y, int width, int height) { queueOpArg("drawOval", new Class[] {int.class, int.class, int.class, int.class}, new Object[] {new Integer(x), new Integer(y), new Integer(width), new Integer(height)}); } public void fillOval(int x, int y, int width, int height) { queueOpArg("fillOval", new Class[] {int.class, int.class, int.class, int.class}, new Object[] {new Integer(x), new Integer(y), new Integer(width), new Integer(height)}); } public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) { queueOpArg("drawArc", new Class[] {int.class, int.class, int.class, int.class, int.class, int.class}, new Object[] {new Integer(x), new Integer(y), new Integer(width), new Integer(height), new Integer(startAngle), new Integer(arcAngle)}); } public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) { queueOpArg("fillArc", new Class[] {int.class, int.class, int.class, int.class, int.class, int.class}, new Object[] {new Integer(x), new Integer(y), new Integer(width), new Integer(height), new Integer(startAngle), new Integer(arcAngle)}); } public void drawPolyline(int xPoints[], int yPoints[], int nPoints) { Class intArrayClass = xPoints.getClass(); queueOpArg("drawPolyline", new Class[] {intArrayClass, intArrayClass, int.class}, new Object[] {xPoints, yPoints, new Integer(nPoints)}); } public void drawPolygon(int xPoints[], int yPoints[], int nPoints) { Class intArrayClass = xPoints.getClass(); queueOpArg("drawPolygon", new Class[] {intArrayClass, intArrayClass, int.class}, new Object[] {xPoints, yPoints, new Integer(nPoints)}); } // public void drawPolygon -- implemented in Graphics superclass public void fillPolygon(int xPoints[], int yPoints[], int nPoints) { Class intArrayClass = xPoints.getClass(); queueOpArg("fillPolygon", new Class[] {intArrayClass, intArrayClass, int.class}, new Object[] {xPoints, yPoints, new Integer(nPoints)}); } // public void fillPolygon -- implemented in Graphics superclass public void drawString(String str, int x, int y) { queueOpArg("drawString", new Class[] {java.lang.String.class, int.class, int.class}, new Object[] {str, new Integer(x), new Integer(y)}); } // public void drawChars -- implemented in Graphics superclass // public void drawBytes -- implemented in Graphics superclass public boolean drawImage(Image img, int x, int y, ImageObserver observer) { queueOpArg("drawImage", new Class[] {java.awt.Image.class, int.class, int.class, java.awt.image.ImageObserver.class}, new Object[] {img, new Integer(x), new Integer(y), observer}); return true; } public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) { queueOpArg("drawImage", new Class[] {java.awt.Image.class, int.class, int.class, int.class, int.class, java.awt.image.ImageObserver.class}, new Object[] {img, new Integer(x), new Integer(y), new Integer(width), new Integer(height), observer}); return true; } public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) { queueOpArg("drawImage", new Class[] {java.awt.Image.class, int.class, int.class, java.awt.Color.class, java.awt.image.ImageObserver.class}, new Object[] {img, new Integer(x), new Integer(y), bgcolor, observer}); return true; } public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) { queueOpArg("drawImage", new Class[] {java.awt.Image.class, int.class, int.class, int.class, int.class, java.awt.Color.class, java.awt.image.ImageObserver.class}, new Object[] {img, new Integer(x), new Integer(y), new Integer(width), new Integer(height), bgcolor, observer}); return true; } public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer) { queueOpArg("drawImage", new Class[] {java.awt.Image.class, int.class, int.class, int.class, int.class, int.class, int.class, int.class, int.class, java.awt.image.ImageObserver.class}, new Object[] {img, new Integer(dx1), new Integer(dy1), new Integer(dx2), new Integer(dy2), new Integer(sx1), new Integer(sy1), new Integer(sx2), new Integer(sy2), observer}); return true; } public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color bgcolor, ImageObserver observer) { queueOpArg("drawImage", new Class[] {java.awt.Image.class, int.class, int.class, int.class, int.class, int.class, int.class, int.class, int.class, java.awt.Color.class, java.awt.image.ImageObserver.class}, new Object[] {img, new Integer(dx1), new Integer(dy1), new Integer(dx2), new Integer(dy2), new Integer(sx1), new Integer(sy1), new Integer(sx2), new Integer(sy2), bgcolor, observer}); return true; } public void dispose() { queueOpArg("dispose", null, null); } // public void finalize -- implemented in Graphics superclass // public String toString -- implemented in Graphics superclass // ---------- Methods from java.awt.Graphics2D ---------- public void addRenderingHints(Map hints) { renderingHints.putAll(hints); queueOpArg("addRenderingHints", new Class[] {java.util.Map.class}, new Object[] {hints}); } public void draw(Shape s) { queueOpArg("draw", new Class[] {java.awt.Shape.class}, new Object[] {s}); } public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) { queueOpArg("drawImage", new Class[] {java.awt.Image.class, java.awt.geom.AffineTransform.class, java.awt.image.ImageObserver.class}, new Object[] {img, xform, obs}); return true; } public void drawRenderedImage(RenderedImage img, AffineTransform xform) { queueOpArg("drawRenderedImage", new Class[] {java.awt.image.RenderedImage.class, java.awt.geom.AffineTransform.class}, new Object[] {img, xform}); } public void drawRenderableImage(RenderableImage img, AffineTransform xform) { queueOpArg("drawRenderableImage", new Class[] {java.awt.image.renderable.RenderableImage.class, java.awt.geom.AffineTransform.class}, new Object[] {img, xform}); } public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) { queueOpArg("drawImage", new Class[] {java.awt.image.BufferedImage.class, java.awt.image.BufferedImageOp.class, int.class, int.class}, new Object[] {img, op, new Integer(x), new Integer(y)}); } public void drawString(String s, float x, float y) { queueOpArg("drawString", new Class[] {java.lang.String.class, float.class, float.class}, new Object[] {s, new Float(x), new Float(y)}); } public void drawString(AttributedCharacterIterator iterator, int x, int y) { queueOpArg("drawString", new Class[] {java.text.AttributedCharacterIterator.class, int.class, int.class}, new Object[] {iterator, new Integer(x), new Integer(y)}); } public void drawString(AttributedCharacterIterator iterator, float x, float y) { queueOpArg("drawString", new Class[] {java.text.AttributedCharacterIterator.class, float.class, float.class}, new Object[] {iterator, new Float(x), new Float(y)}); } public void drawGlyphVector(GlyphVector v, float x, float y) { queueOpArg("drawGlyphVector", new Class[] {java.awt.font.GlyphVector.class, float.class, float.class}, new Object[] {v, new Float(x), new Float(y)}); } public void fill(Shape s) { queueOpArg("fill", new Class[] {java.awt.Shape.class}, new Object[] {s}); } public boolean hit(Rectangle rect, Shape s, boolean onStroke) { Graphics2D g2d = getBogusGraphics2D(); boolean hitTarget = g2d.hit(rect, s, onStroke); g2d.dispose(); return hitTarget; } public GraphicsConfiguration getDeviceConfiguration() { Graphics2D g2d = getBogusGraphics2D(); GraphicsConfiguration gConf = g2d.getDeviceConfiguration(); g2d.dispose(); return gConf; } public FontRenderContext getFontRenderContext() { Graphics2D g2d = getBogusGraphics2D(); FontRenderContext fontRenderContext = g2d.getFontRenderContext(); g2d.dispose(); return fontRenderContext; } public void setComposite(Composite comp) { composite = comp; queueOpArg("setComposite", new Class[] {java.awt.Composite.class}, new Object[] {comp}); } public void setPaint(Paint paint) { this.paint = paint; queueOpArg("setPaint", new Class[] {java.awt.Paint.class}, new Object[] {paint}); } public void setStroke(Stroke s) { stroke = s; queueOpArg("setStroke", new Class[] {java.awt.Stroke.class}, new Object[] {s}); } public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue) { renderingHints.put(hintKey, hintValue); queueOpArg("setRenderingHint", new Class[] {java.awt.RenderingHints.Key.class, java.lang.Object.class}, new Object[] {hintKey, hintValue}); } public Object getRenderingHint(RenderingHints.Key hintKey) { return renderingHints.get(hintKey); } public void setRenderingHints(Map hints) { renderingHints.putAll(hints); queueOpArg("setRenderingHints", new Class[] {java.util.Map.class}, new Object[] {hints}); } public RenderingHints getRenderingHints() { return renderingHints; } public void translate(int x, int y) { origin = new Point(x, y); transform.translate((double)x, (double)y); queueOpArg("translate", new Class[] {int.class, int.class}, new Object[] {new Integer(x), new Integer(y)}); } public void translate(double x, double y) { transform.translate(x, y); queueOpArg("translate", new Class[] {double.class, double.class}, new Object[] {new Double(x), new Double(y)}); } public void rotate(double theta) { transform.rotate(theta); queueOpArg("rotate", new Class[] {double.class}, new Object[] {new Double(theta)}); } public void rotate(double theta, double x, double y) { transform.rotate(theta, x, y); queueOpArg("rotate", new Class[] {double.class, double.class, double.class}, new Object[] {new Double(theta), new Double(x), new Double(y)}); } public void scale(double sx, double sy) { transform.scale(sx, sy); queueOpArg("scale", new Class[] {double.class, double.class}, new Object[] {new Double(sx), new Double(sy)}); } public void shear(double shx, double shy) { transform.shear(shx, shy); queueOpArg("shear", new Class[] {double.class, double.class}, new Object[] {new Double(shx), new Double(shy)}); } public void transform(AffineTransform Tx) { transform.concatenate(Tx); queueOpArg("transform", new Class[] {java.awt.geom.AffineTransform.class}, new Object[] {Tx}); } public void setTransform(AffineTransform Tx) { transform = Tx; queueOpArg("setTransform", new Class[] {java.awt.geom.AffineTransform.class}, new Object[] {Tx}); } public AffineTransform getTransform() { return transform; } public Paint getPaint() { return paint; } public Composite getComposite() { return composite; } public void setBackground(Color color) { background = color; queueOpArg("setBackground", new Class[] {java.awt.Color.class}, new Object[] {color}); } public Color getBackground() { return background; } public Stroke getStroke() { return stroke; } public void clip(Shape s) { if(clip == null) { clip = s; } else { Area clipArea = (clip instanceof Area ? (Area)clip : new Area(clip)); clipArea.intersect(s instanceof Area ? (Area)s : new Area(s)); clip = clipArea; } queueOpArg("clip", new Class[] {java.awt.Shape.class}, new Object[] {s}); } // ---------- Methods from RenderableImage ---------- public Vector getSources() { return null; } public Object getProperty(String name) { return Image.UndefinedProperty; } public String[] getPropertyNames() { return null; } public boolean isDynamic() { return false; } public float getWidth() { return (float)dimensions.getWidth(); } public float getHeight() { return (float)dimensions.getHeight(); } public float getMinX() { return (float)dimensions.getMinX(); } public float getMinY() { return (float)dimensions.getMinY(); } public RenderedImage createScaledRendering(int w, int h, RenderingHints hints) { if(w <= 0 && h <= 0) { throw new IllegalArgumentException(JaiI18N.getString("RenderableGraphics1")); } else if(w <= 0) { w = (int)Math.round(h*dimensions.getWidth()/dimensions.getHeight()); } else if(h <= 0) { h = (int)Math.round(w*dimensions.getHeight()/dimensions.getWidth()); } double sx = (double)w/dimensions.getWidth(); double sy = (double)h/dimensions.getHeight(); AffineTransform usr2dev = new AffineTransform(); usr2dev.setToScale(sx, sy); return createRendering(new RenderContext(usr2dev, hints)); } public RenderedImage createDefaultRendering() { return createRendering(new RenderContext(new AffineTransform())); } /** * Creates a RenderedImage that represents 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. * *

    The bounds of the RenderedImage are determined from * the dimensions parameter passed to the RenderableGraphics * constructor. These bounds will be transformed by any * AffineTransform from the RenderContext. * The RenderingHints from the RenderContext may * be used to specify the tile width and height, SampleModel, * and ColorModel by supplying an ImageLayout * hint. The precedence for determining tile width and height is to use * firstly values provided explicitly via the ImageLayout, * secondly the width and height of the SampleModel in the * hint, and thirdly the bounds of the RenderableGraphics * object after transformation. * *

    If either the SampleModel or ColorModel * is null, an attempt will be made to derive a compatible value for the * null object from the non-null object. If they are both null, a 3-band * byte TiledImage with a null ColorModel and a * PixelInterleavedSampleModel will be created. * * @param renderContext the RenderContext to use to produce the rendering. * @return a RenderedImage containing the rendered data. */ public RenderedImage createRendering(RenderContext renderContext) { // Unpack the RenderContext and set local variables based thereon AffineTransform usr2dev = renderContext.getTransform(); if(usr2dev == null) { usr2dev = new AffineTransform(); } RenderingHints hints = renderContext.getRenderingHints(); Shape aoi = renderContext.getAreaOfInterest(); if(aoi == null) { aoi = dimensions.getBounds(); } // Transform the area of interest. Shape transformedAOI = usr2dev.createTransformedShape(aoi); // Create a TiledImage to be used as the canvas. TiledImage ti = createTiledImage(hints, transformedAOI.getBounds()); // Create the associated Graphics2D object and translate it to // account for the initial origin specified in the RenderableGraphics // constructor. Graphics2D g2d = ti.createGraphics(); // NOTE: There is no need to copy the state to the TiledImageGraphics // object here as all modifications are contained in the opArgList. // Set Graphics2D state variables according to the RenderContext if(!usr2dev.isIdentity()) { AffineTransform tf = getTransform(); tf.concatenate(usr2dev); g2d.setTransform(tf); } if(hints != null) { g2d.addRenderingHints(hints); } g2d.setClip(aoi); // Evaluate the operation queue evaluateOpList(g2d); // Free Graphics2D resources g2d.dispose(); return ti; } void sendExceptionToListener(String message, Exception e) { ImagingListener listener = null; if (renderingHints != null) listener = (ImagingListener)renderingHints.get(JAI.KEY_IMAGING_LISTENER); if (listener == null) listener = JAI.getDefaultInstance().getImagingListener(); listener.errorOccurred(message, e, this, false); } } jai-core-1.1.4/src/share/classes/javax/media/jai/TileCache.java0000644000175000017500000002332710203035544024030 0ustar mathieumathieu/* * $RCSfile: TileCache.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:22 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Point; import java.awt.image.Raster; import java.util.Comparator; import java.awt.image.RenderedImage; /** * A class implementing a caching mechanism for image tiles. * *

    TileCache provides a mechanism by which an * OpImage may cache its computed tiles. There may be * multiple TileCaches used in an application up to the * point of having a different TileCache for each * OpImage. * *

    The TileCache used for a particular OpImage * is derived from the RenderingHints assigned to the * associated imaging chain node. If the node is constructed using * JAI.create() and no TileCache is specified * in the RenderingHints parameter, then one is derived * from the RenderingHints associated with the instance of the * JAI class being used. * *

    In the Sun reference implementation, the cache size is limited by * the memory capacity, which is set to a default value at construction * or subsequently using the setMemoryCapacity() method. * The initial value may be obtained using getMemoryCapacity(). * The tile capacity is not used as different images may have very different * tile sizes so that this metric is not a particularly meaningful control * of memory resource consumption in general. * * @see JAI * @see RenderedOp * @see java.awt.RenderingHints */ public interface TileCache { /** * Adds a tile to the cache. * * @param owner The RenderedImage that the tile belongs to. * @param tileX The X index of the tile in the owner's tile grid. * @param tileY The Y index of the tile in the owner's tile grid. * @param data A Raster containing the tile data. */ void add(RenderedImage owner, int tileX, int tileY, Raster data); /** * Adds a tile to the cache with an associated compute cost * * @param owner The RenderedImage that the tile belongs to. * @param tileX The X index of the tile in the owner's tile grid. * @param tileY The Y index of the tile in the owner's tile grid. * @param data A Raster containing the tile data. * @param tileCacheMetric An Object as a tile metric. * * @since JAI 1.1 */ void add(RenderedImage owner, int tileX, int tileY, Raster data, Object tileCacheMetric); /** * Advises the cache that a tile is no longer needed. It is legal * to implement this method as a no-op. * * @param owner The RenderedImage that the tile belongs to. * @param tileX The X index of the tile in the owner's tile grid. * @param tileY The Y index of the tile in the owner's tile grid. */ void remove(RenderedImage owner, int tileX, int tileY); /** * Retrieves a tile. Returns null if the tile is not * present in the cache. * * @param owner The RenderedImage that the tile belongs to. * @param tileX The X index of the tile in the owner's tile grid. * @param tileY The Y index of the tile in the owner's tile grid. */ Raster getTile(RenderedImage owner, int tileX, int tileY); /** * Retrieves an array of all tiles in the cache which are owned by the * specified image. * * @param owner The RenderedImage to which the tiles belong. * @return An array of all tiles owned by the specified image or * null if there are none currently in the cache. * * @since JAI 1.1 */ Raster[] getTiles(RenderedImage owner); /** * Advises the cache that all tiles associated with a given image * are no longer needed. It is legal to implement this method as * a no-op. * * @param owner The RenderedImage owner of the tiles * to be removed. */ void removeTiles(RenderedImage owner); /** * Adds an array of tiles to the tile cache. * * @param owner The RenderedImage that the tile belongs to. * @param tileIndices An array of Points containing the * tileX and tileY indices for each tile. * @param tiles The array of tile Rasters containing tile data. * @param tileCacheMetric Object which provides an ordering metric * associated with the RenderedImage owner. * * @since JAI 1.1 */ void addTiles(RenderedImage owner, Point[] tileIndices, Raster[] tiles, Object tileCacheMetric); /** * Returns an array of tile Rasters from the cache. * Any or all of the elements of the returned array may be * null if the corresponding tile is not in the cache. * The length of the returned array must be the same as that of the * parameter array and the ith Raster in the * returned array must correspond to the ith tile index in * the parameter array. * * @param owner The RenderedImage that the tile belongs to. * @param tileIndices An array of Points containing the * tileX and tileY indices for each tile. * * @since JAI 1.1 */ Raster[] getTiles(RenderedImage owner, Point[] tileIndices); /** * Advises the cache that all of its tiles may be discarded. It * is legal to implement this method as a no-op. */ void flush(); /** * Advises the cache that some of its tiles may be discarded. It * is legal to implement this method as a no-op. * * @since JAI 1.1 */ void memoryControl(); /** * Sets the tile capacity to a desired number of tiles. * If the capacity is smaller than the current capacity, * tiles are flushed from the cache. It is legal to * implement this method as a no-op. * * @param tileCapacity The new capacity, in tiles. * * @deprecated as of JAI 1.1. */ void setTileCapacity(int tileCapacity); /** * Returns the tile capacity in tiles. It is legal to * implement this method as a no-op which should be * signaled by returning zero. * * @deprecated as of JAI 1.1. */ int getTileCapacity(); /** * Sets the memory capacity to a desired number of bytes. * If the memory capacity is smaller than the amount of * memory currently used by the cache, tiles are flushed * until the TileCache's memory usage is less than * memoryCapacity. * * @param memoryCapacity The new capacity, in bytes. */ void setMemoryCapacity(long memoryCapacity); /** * Returns the memory capacity in bytes. */ long getMemoryCapacity(); /** * Sets the memoryThreshold value to a floating * point number that ranges from 0.0 to 1.0. * When the cache memory is full, the memory * usage will be reduced to this fraction of * the total cache memory capacity. For example, * a value of .75 will cause 25% of the memory * to be cleared, while retaining 75%. * * @param memoryThreshold. Retained fraction of memory * @throws IllegalArgumentException if the memoryThreshold * is less than 0.0 or greater than 1.0 * * @since JAI 1.1 */ void setMemoryThreshold(float memoryThreshold); /** * Returns the memory threshold, which is the fractional * amount of cache memory to retain during tile removal. * * @since JAI 1.1 */ float getMemoryThreshold(); /** * Sets a Comparator which imposes an order on the * CachedTiles stored in the TileCache. * This ordering is used in memoryControl() to determine * the sequence in which tiles will be removed from the * TileCache so as to reduce the memory to the level given * by the memory threshold. The Objects passed to the * compare() method of the Comparator will * be instances of CachedTile. CachedTiles * will be removed from the TileCache in the ascending * order imposed by this Comparator. If no * Comparator is currently set, the TileCache * should use an implementation-dependent default ordering. In the * Sun Microsystems, Inc., implementation of TileCache, * this ordering is the least recently used * ordering, i.e., the tiles least recently used will be removed first * by memoryControl(). * * @param comparator A Comparator which orders the * CachedTiles stored by the TileCache; * if null an implementation-dependent algorithm * will be used. * * @since JAI 1.1 */ void setTileComparator(Comparator comparator); /** * Returns the Comparator currently set for use in ordering * the CachedTiles stored by the TileCache. * * @return The tile Comparator or null if the * implementation-dependent ordering algorithm is being used. * * @since JAI 1.1 */ Comparator getTileComparator(); } jai-core-1.1.4/src/share/classes/javax/media/jai/RenderableCollectionImageFactory.java0000644000175000017500000000331710203035544030556 0ustar mathieumathieu/* * $RCSfile: RenderableCollectionImageFactory.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:19 $ * $State: Exp $ */ package javax.media.jai; import java.awt.image.renderable.ParameterBlock; /** * The RenderableCollectionImageFactory (RCIF) interface * is intended to be implemented by classes that wish to act as factories * to produce different collection image operators. In JAI, the * create() method defined by this interface will be * invoked in a chain of CollectionOps when the operation is * being executed in renderable mode. The images contained in the * generated CollectionImage would be expected to be * RenderableImages. * * @since JAI 1.1 */ public interface RenderableCollectionImageFactory { /** * Creates a CollectionImage that represents the * result of an operation (or chain of operations) for a given * ParameterBlock. * If the operation is unable to handle the input arguments, this * method should return null. * *

    Generally this method is expected to be invoked by an operation * being executed in renderable mode. Therefore the images contained * in the generated CollectionImage would be expected to * be RenderableImages. * * @param args Input arguments to the operation, including * sources and/or parameters. * * @return A CollectionImage containing the desired output. */ CollectionImage create(ParameterBlock parameters); } jai-core-1.1.4/src/share/classes/javax/media/jai/StatisticsOpImage.java0000644000175000017500000003362110203035544025601 0ustar mathieumathieu/* * $RCSfile: StatisticsOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:21 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.util.Vector; import com.sun.media.jai.util.PropertyUtil; /** * An abstract base class representing image operators that compute * statistics on a given region of an image, and with a given sampling * period. Such operators may only have one source image. * *

    The layout of this image is exactly the same as that of the source * image. Any user supplied layout values via the * RenderingHints are ignored. * The StatisticsOpImage simply passes the pixels of the * source image through unchanged. However, the desired statistics * are computed on demand and made available as a property or set of * properties on the image. * *

    All instances of StatisticsOpImage make use of a * region of interest, specified as a ROI object. If this * argument is null, the entire source image is used. * Additionally, they may perform spatial subsampling of the region of * interest according to xPeriod and yPeriod * parameters that may vary from 1 (sample every pixel of the region * of interest) upwards. This allows the speed and quality of * statistics gathering to be traded off against one another. * *

    Subclasses should provide implementations * of the getStatisticsNames, createStatistics, * and accumulateStatistics methods. * * @see OpImage */ public abstract class StatisticsOpImage extends OpImage { /** * The region of interest over which to compute the statistics. * If it is null, the entire image is used to compute * the statistics. */ protected ROI roi; /** The X coordinate of the initial sample. */ protected int xStart; /** The Y coordinate of the initial sample. */ protected int yStart; /** The horizontal sampling rate. */ protected int xPeriod; /** The vertical sampling rate. */ protected int yPeriod; /** Whether to check for skipped tiles. **/ private boolean checkForSkippedTiles; /** * Constructor. * *

    The layout of this image is exactly the same as that of the * source image. Any user supplied layout values via the * RenderingHints are ignored. * * @param source The source image over which the statistics * is accumulated. * @param roi The region of interest that specifies the region of the * source image over which to compute the statistics. If it * is null, the entire source image is used. * @param xStart The initial X sample coordinate. * @param yStart The initial Y sample coordinate. * @param xPeriod The horizontal sampling rate. * @param yPeriod The vertical sampling rate. * * @throws IllegalArgumentException If source is * null. * * @since JAI 1.1 */ public StatisticsOpImage(RenderedImage source, ROI roi, int xStart, int yStart, int xPeriod, int yPeriod) { super(vectorize(source), // vectorize() checks for null source. new ImageLayout(source), null, // configuration false); this.roi = roi == null ? new ROIShape(getSource(0).getBounds()) : roi; this.xStart = xStart; this.yStart = yStart; this.xPeriod = xPeriod; this.yPeriod = yPeriod; this.checkForSkippedTiles = xPeriod > tileWidth || yPeriod > tileHeight; } /** * Returns false as computeTile() invocations * are forwarded to the RenderedImage source and are * therefore not unique objects in the global sense. * * @since JAI 1.1 */ public boolean computesUniqueTiles() { return false; } /** * Returns a tile of this image as a Raster. If the * requested tile is completely outside of this image's bounds, * this method returns null. * *

    Statistics operators do not cache their tiles internally. * Rather, the implementation of this method in this class simply * forwards the request to the source image. * * @param tileX The X index of the tile. * @param tileY The Y index of the tile. * * @return The requested tile as a Raster or * null. */ public Raster getTile(int tileX, int tileY) { return getSource(0).getTile(tileX, tileY); } /** * Computes the image data of a tile. * *

    The implementation of this method in this class simply forwards * the request to the source image. * * @param tileX The X index of the tile. * @param tileY The Y index of the tile. * * @since JAI 1.1 */ public Raster computeTile(int tileX, int tileY) { return getSource(0).getTile(tileX, tileY); } /** * Returns a list of tiles. The request is simply * forwarded to the source image. * * @param tileIndices The indices of the tiles requested. * * @throws IllegalArgumentException If tileIndices is * null. */ public Raster[] getTiles(Point[] tileIndices) { if ( tileIndices == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return getSource(0).getTiles(tileIndices); } /** * Maps the source rectangle into destination space unchanged. * * @param sourceRect the Rectangle in source coordinates. * @param sourceIndex the index of the source image. * * @return A Rectangle indicating the valid destination region. * * @throws IllegalArgumentException If sourceIndex * is not 0. * @throws IllegalArgumentException If sourceRect is * null. */ public Rectangle mapSourceRect(Rectangle sourceRect, int sourceIndex) { if ( sourceRect == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sourceIndex != 0) { // there is only 1 source throw new IllegalArgumentException(JaiI18N.getString("Generic1")); } return new Rectangle(sourceRect); } /** * Maps the destination rectangle into source space unchanged. * * @param destRect the Rectangle in destination coordinates. * @param sourceIndex the index of the source image. * * @return A Rectangle indicating the required source region. * * @throws IllegalArgumentException If sourceIndex * is not 0. * @throws IllegalArgumentException If destRect is * null. */ public Rectangle mapDestRect(Rectangle destRect, int sourceIndex) { if ( destRect == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sourceIndex != 0) { // there is only 1 source throw new IllegalArgumentException(JaiI18N.getString("Generic1")); } return new Rectangle(destRect); } /** * Returns one of the available statistics as a property. If the * property name is not recognized, this method returns * java.awt.Image.UndefinedProperty. * * @throws IllegalArgumentException If name is * null. */ public Object getProperty(String name) { if (name == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } // Is this property already in the Hashtable? Object stats = super.getProperty(name); if (stats.equals(java.awt.Image.UndefinedProperty)) { // This property has not been generated; generate it. synchronized (this) { // lock other threads stats = createStatistics(name); if (!stats.equals(java.awt.Image.UndefinedProperty)) { PlanarImage source = getSource(0); // Cycle throw all source tiles. int minTileX = source.getMinTileX(); int maxTileX = source.getMaxTileX(); int minTileY = source.getMinTileY(); int maxTileY = source.getMaxTileY(); for (int y = minTileY; y <= maxTileY; y++) { for (int x = minTileX; x <= maxTileX; x++) { // Determine the required region of this tile. // (Note that getTileRect() instersects tile and // image bounds.) Rectangle tileRect = getTileRect(x, y); // Process if and only if within ROI bounds. if (roi.intersects(tileRect)) { // If checking for skipped tiles determine // whether this tile is "hit". if(checkForSkippedTiles && tileRect.x >= xStart && tileRect.y >= yStart) { // Determine the offset within the tile. int offsetX = (xPeriod - ((tileRect.x - xStart) % xPeriod)) % xPeriod; int offsetY = (yPeriod - ((tileRect.y - yStart) % yPeriod)) % yPeriod; // Continue with next tile if offset // is larger than either tile dimension. if(offsetX >= tileRect.width || offsetY >= tileRect.height) { continue; } } // Accumulate statistics for this tile. accumulateStatistics(name, source.getData(tileRect), stats); } } } // Store the generated property in Hastable. setProperty(name, stats); } } } return stats; } /** * Returns a list of property names that are recognized by this image. * * @return An array of Strings containing valid * property names. */ public String[] getPropertyNames() { // Get statistics names and names from superclass. String[] statsNames = getStatisticsNames(); String[] superNames = super.getPropertyNames(); // Return stats names if not superclass names. if(superNames == null) { return statsNames; } // Check for overlap between stats names and superclass names. Vector extraNames = new Vector(); for (int i = 0; i < statsNames.length; i++) { String prefix = statsNames[i]; String[] names = PropertyUtil.getPropertyNames(superNames, prefix); if(names != null) { for(int j = 0; j < names.length; j++) { if(names[j].equalsIgnoreCase(prefix)) { extraNames.add(prefix); } } } } // If no overlap then return. if (extraNames.size() == 0) { return superNames; } // Combine superclass and extra names. String[] propNames = new String[superNames.length + extraNames.size()]; System.arraycopy(superNames, 0, propNames, 0, superNames.length); int offset = superNames.length; for (int i = 0; i < extraNames.size(); i++) { propNames[offset++] = (String)extraNames.get(i); } // Return combined name set. return propNames; } /** * Returns a list of names of statistics understood * by this class. */ protected abstract String[] getStatisticsNames(); /** * Returns an object that will be used to gather the * named statistic. * * @param name The name of the statistic to be gathered. */ protected abstract Object createStatistics(String name); /** * Accumulates statistics on the specified region into * the previously created statistics object. The * region of interest and X and Y sampling rate * should be respected. * * @param name The name of the statistic to be gathered. * @param source A Raster containing source pixels. * The dimensions of the Raster will not * exceed maxWidth x maxHeight. * @param stats A statistics object generated by a previous call * to createStatistics. */ protected abstract void accumulateStatistics(String name, Raster source, Object stats); } jai-core-1.1.4/src/share/classes/javax/media/jai/ImageStack.java0000644000175000017500000001044310203035544024212 0ustar mathieumathieu/* * $RCSfile: ImageStack.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:10 $ * $State: Exp $ */ package javax.media.jai; import java.util.Collection; import java.util.Iterator; /** * A class representing a stack of images, each associated with a * spatial position/orientation defined in a common coordinate system. * The images are of the type javax.media.jai.PlanarImage; * the coordinates are of the type java.lang.Object. * The tuple (image, coordinate) is represented by class * javax.media.jai.CoordinateImage. * *

    This class can be used to represent medical or geophysical images. * * @see PlanarImage * * @deprecated as of JAI 1.1. Use * AttributedImageCollection instead. */ public abstract class ImageStack extends CollectionImage { /** The default constructor. */ protected ImageStack() {} /** * Constructor. * * @param images A collection of CoordinateImage. * * @throws IllegalArgumentException if images is null. */ public ImageStack(Collection images) { super(images); } /** * Returns the image associated with the specified coordinate, * or null if c is null or * if no match is found. * * @param c The specified coordinate object. */ public PlanarImage getImage(Object c) { if (c != null) { Iterator iter = iterator(); while (iter.hasNext()) { CoordinateImage ci = (CoordinateImage)iter.next(); if (ci.coordinate.equals(c)) { return ci.image; } } } return null; } /** * Returns the coordinate associated with the specified image, * or null if pi is null or * if no match is found. * * @param pi The specified planar image. */ public Object getCoordinate(PlanarImage pi) { if (pi != null) { Iterator iter = iterator(); while (iter.hasNext()) { CoordinateImage ci = (CoordinateImage)iter.next(); if (ci.image.equals(pi)) { return ci.coordinate; } } } return null; } /** * Adds a CoordinateImage to this collection. If the * specified image is null, it is not added to the * collection. * * @return true if and only if the CoordinateImage is added * to the collection. */ public boolean add(Object o) { if (o != null && o instanceof CoordinateImage) { return super.add(o); } else { return false; } } /** * Removes the CoordinateImage that contains the * specified image from this collection. * * @param pi The specified planar image. * @return true if and only if a CoordinateImage containing * the specified image is removed from the collection. */ public boolean remove(PlanarImage pi) { if (pi != null) { Iterator iter = iterator(); while (iter.hasNext()) { CoordinateImage ci = (CoordinateImage)iter.next(); if (ci.image.equals(pi)) { return super.remove(ci); } } } return false; } /** * Removes the CoordinateImage that contains the * specified coordinate from this collection. * * @param c The specified coordinate object. * @return true if and only if a CoordinateImage containing * the specified coordinate is removed from the collection. */ public boolean remove(Object c) { if (c != null) { Iterator iter = iterator(); while (iter.hasNext()) { CoordinateImage ci = (CoordinateImage)iter.next(); if (ci.coordinate.equals(c)) { return super.remove(ci); } } } return false; } } jai-core-1.1.4/src/share/classes/javax/media/jai/TileScheduler.java0000644000175000017500000002424010203035544024736 0ustar mathieumathieu/* * $RCSfile: TileScheduler.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:22 $ * $State: Exp $ */ package javax.media.jai; import java.awt.image.Raster; import java.awt.Point; /** * A class implementing a mechanism for scheduling tile calculation. * In various implementations tile computation may make use of multithreading * and multiple simultaneous network connections for improved performance. * *

    If multithreading is used then the implementation of the interface * must be thread-safe. In particular it must be possible to invoke any of * the tile scheduling methods on the same image simultaneously from different * threads and obtain the same results as if all invocations had been from * the same thread. * *

    Errors and exceptions which occur within the scheduler and which prevent * tile computation will be thrown via the usual mechanism for all blocking * methods, i.e., those which perform the computations while the invoking * thread blocks. Failure conditions encountered in computations effected * via non-blocking methods will be indicated by notifying any listeners. * In neither case is it expected that the tiles will be re-scheduled for * computation this instead being left to the application. */ public interface TileScheduler { /** * Schedules a tile for computation. Called by * OpImage.getTile(), this method makes * OpImage.computeTile() calls to calculate * the destination tile. This will provoke the computation * of any required source tiles as well. * * @param target An OpImage whose tile is to be computed. * @param tileX The X index of the tile to be computed. * @param tileY The Y index of the tile to be computed. * @return A Raster containing the contents of the tile. * * @throws IllegalArgumentException if target is * null. */ Raster scheduleTile(OpImage target, int tileX, int tileY); /** * Schedules a list of tiles for computation. Called by * OpImage.getTiles, this method makes * OpImage.computeTile() calls to calculate * the destination tiles. This will provoke the computation * of any required source tiles as well. * * @param target An OpImage whose tiles are to be computed. * @param tileIndices A list of tile indices indicating which tiles * to schedule for computation. * @return An array of Rasters containing a computed * raster for every tile index passed in. * * @throws IllegalArgumentException if target or * tileIndices is null. */ Raster[] scheduleTiles(OpImage target, Point tileIndices[]); /** * Schedule a list of tiles for computation. The supplied listeners * will be notified of the status of each tile, i.e., when each tile * is computed, cancelled, or encounters an error. This * method ideally should be non-blocking. If the TileScheduler * implementation uses multithreading, it is at the discretion of the * implementation which thread invokes the * TileComputationListener methods. The event source * parameter passed to each listener will be the TileScheduler * itself and the image parameter will be the specified target image. * *

    In the Sun Microsystems reference implementation of * TileScheduler the TileComputationListener * methods are invoked by the thread which performs the actual * tile computation. This will be the primary thread if the * parallelism is zero, or a worker thread if it is positive. * * @param target A PlanarImage whose tiles are to be computed. * @param tileIndices A list of tile indices indicating which tiles * to schedule for computation. * @param tileListeners TileComputationListeners to be * informed of tile computation status; may be null. * @return The TileRequest for this set of tiles. * @throws IllegalArgumentException if target or * tileIndices is null. * * @since JAI 1.1 */ TileRequest scheduleTiles(PlanarImage target, Point[] tileIndices, TileComputationListener[] tileListeners); /** * Issues an advisory cancellation request to the * TileScheduler stating that the indicated tiles of the * specified request should not be processed. The handling of cancellation * is at the discretion of the scheduler which may cancel tile processing * in progress and remove tiles from its internal queue, remove tiles from * the queue but not terminate current processing, or simply do nothing. * *

    In the Sun Microsystems reference implementation of * TileScheduler the second tile cancellation option is * implemented, i.e., tiles are removed from the internal queue but * computation already in progress is not terminated. If there is at * least one worker thread this method should be non-blocking. Any tiles * allowed to complete computation subsequent to this call are complete * and will be treated as if they had not been cancelled, e.g., with * respect to caching, notification of registered listeners, etc. * Furthermore, cancelling a tile request in no way invalidates the tile * as a candidate for future recomputation. * * @param request The request for which tiles are to be cancelled. * @param tileIndices The tiles to be cancelled; may be null. * Any tiles not actually in the TileRequest will be * ignored. * * @throws IllegalArgumentException if request is * null. * * @since JAI 1.1 */ void cancelTiles(TileRequest request, Point[] tileIndices); /** * Hints to the TileScheduler that the specified tiles from * the given PlanarImage might be needed in the near future. * Some TileScheduler implementations may spawn a low * priority thread to compute the tiles while others may ignore the hint. * * @param target The OpImage from which to prefetch tiles. * @param tileIndices A list of tile indices indicating which tiles * to prefetch. * * @throws IllegalArgumentException if target or * tileIndices is null. */ void prefetchTiles(PlanarImage target, Point[] tileIndices); /** * Suggests to the scheduler the degree of parallelism to use in * processing invocations of scheduleTiles(). For * example, this might set the number of threads to spawn. It is * legal to implement this method as a no-op. * *

    In the Sun Microsystems reference implementation of TileScheduler * this method sets the number of worker threads actually used for tile * computation. Ideally this number should equal the number of processors * actually available on the system. It is the responsibility of the * application to set this value as the number of processors is not * available via the virtual machine. A parallelism value of zero * indicates that all tile computation will be effected in the primary * thread. A parallelism value of N indicates that there will be * N worker threads in addition to the primary scheduler thread. * In JAI the parallelism defaults to a value of 2 unless explicity set * by the application. * * @param parallelism The suggested degree of parallelism. * @throws IllegalArgumentException if parallelism * is negative. * * @since JAI 1.1 */ void setParallelism(int parallelism); /** * Returns the degree of parallelism of the scheduler. * * @since JAI 1.1 */ int getParallelism(); /** * Identical to setParallelism() but applies only to * prefetchTiles(). * * @since JAI 1.1 */ void setPrefetchParallelism(int parallelism); /** * Identical to getParallelism() but applies only to * prefetchTiles(). * * @since JAI 1.1 */ int getPrefetchParallelism(); /** * Suggests to the scheduler the priority to assign to processing * effected by scheduleTiles(). For example, this might * set thread priority. Values outside of the accepted priority range * will be clamped to the nearest extremum. An implementation may clamp * the prefetch priority to less than the scheduling priority. It is * legal to implement this method as a no-op. * *

    In the Sun Microsystems reference implementation of TileScheduler * this method sets the priority of the worker threads used for tile * computation. Its initial value is Thread.NORM_PRIORITY. * * @param priority The suggested priority. * * @since JAI 1.1 */ void setPriority(int priority); /** * Returns the priority of scheduleTiles() processing. * * @since JAI 1.1 */ int getPriority(); /** * Identical to setPriority() but applies only to * prefetchTiles(). * *

    In the Sun Microsystems reference implementation of * TileScheduler, this method sets the priority of any threads * spawned to prefetch tiles. Its initial value is * Thread.MIN_PRIORITY. * * @since JAI 1.1 */ void setPrefetchPriority(int priority); /** * Identical to getPriority() but applies only to * prefetchTiles(). * * @since JAI 1.1 */ int getPrefetchPriority(); } jai-core-1.1.4/src/share/classes/javax/media/jai/WarpOpImage.java0000644000175000017500000005421310203035544024360 0ustar mathieumathieu/* * $RCSfile: WarpOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:24 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Point; import java.awt.Rectangle; import java.awt.geom.Point2D; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.util.Map; import javax.media.jai.util.CaselessStringKey; import com.sun.media.jai.util.ImageUtil; /** * A general implementation of image warping, and a superclass for * specific image warping operations. * *

    The image warp is specified by a Warp object * and an Interpolation object. * *

    Subclasses of WarpOpImage may choose whether they * wish to implement the cobbled or non-cobbled variant of * computeRect by means of the cobbleSources * constructor parameter. The class comments for OpImage * provide more information about how to override * computeRect. * * It should be noted that the superclass GeometricOpImage * automatically adds a value of Boolean.TRUE for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL to the given * configuration and passes it up to its superclass constructor * so that geometric operations are performed on the pixel values instead * of being performed on the indices into the color map for those * operations whose source(s) have an IndexColorModel. * This addition will take place only if a value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL has not already been * provided by the user. Note that the configuration Map * is cloned before the new hint is added to it. Regarding the value * for the JAI.KEY_REPLACE_INDEX_COLOR_MODEL * RenderingHints, the operator itself can be smart * based on the parameters, i.e. while the default value for * the JAI.KEY_REPLACE_INDEX_COLOR_MODEL is * Boolean.TRUE for operations that extend this class, * in some cases the operator could set the default. * * @see GeometricOpImage * @see OpImage * @see Warp * @see Interpolation */ public abstract class WarpOpImage extends GeometricOpImage { /** * The Warp object describing the backwards pixel * map. It can not be null. */ protected Warp warp; /** * If no bounds are specified, attempt to derive the image bounds by * forward mapping the source bounds. */ private static ImageLayout getLayout(ImageLayout layout, RenderedImage source, Warp warp) { // If a non-null layout with defined bounds is supplied, // return it directly. if(layout != null && layout.isValid(ImageLayout.MIN_X_MASK | ImageLayout.MIN_Y_MASK | ImageLayout.WIDTH_MASK | ImageLayout.HEIGHT_MASK)) { return layout; } // Get the source bounds. Rectangle sourceBounds = new Rectangle(source.getMinX(), source.getMinY(), source.getWidth(), source.getHeight()); // Attempt to forward map the source bounds. Rectangle destBounds = warp.mapSourceRect(sourceBounds); // If this failed, attempt to map the vertices. if(destBounds == null) { Point[] srcPts = new Point[] { new Point(sourceBounds.x, sourceBounds.y), new Point(sourceBounds.x + sourceBounds.width, sourceBounds.y), new Point(sourceBounds.x, sourceBounds.y + sourceBounds.height), new Point(sourceBounds.x + sourceBounds.width, sourceBounds.y + sourceBounds.height)}; boolean verticesMapped = true; double xMin = Double.MAX_VALUE; double xMax = -Double.MAX_VALUE; double yMin = Double.MAX_VALUE; double yMax = -Double.MAX_VALUE; for(int i = 0; i < 4; i++) { Point2D destPt = warp.mapSourcePoint(srcPts[i]); if(destPt == null) { verticesMapped = false; break; } double x = destPt.getX(); double y = destPt.getY(); if(x < xMin) { xMin = x; } if(x > xMax) { xMax = x; } if(y < yMin) { yMin = y; } if(y > yMax) { yMax = y; } } // If all vertices mapped, compute the bounds. if(verticesMapped) { destBounds = new Rectangle(); destBounds.x = (int)Math.floor(xMin); destBounds.y = (int)Math.floor(yMin); destBounds.width = (int)Math.ceil(xMax - destBounds.x); destBounds.height = (int)Math.ceil(yMax - destBounds.y); } } // If bounds still not computed, approximate the destination bounds // by the source bounds, compute an approximate forward mapping, // and use it to compute the destination bounds. If the warp is // a WarpAffine then skip it as mapSourceRect() already failed. if(destBounds == null && !(warp instanceof WarpAffine)) { Point[] destPts = new Point[] { new Point(sourceBounds.x, sourceBounds.y), new Point(sourceBounds.x + sourceBounds.width, sourceBounds.y), new Point(sourceBounds.x, sourceBounds.y + sourceBounds.height), new Point(sourceBounds.x + sourceBounds.width, sourceBounds.y + sourceBounds.height)}; float[] sourceCoords = new float[8]; float[] destCoords = new float[8]; int offset = 0; for(int i = 0; i < 4; i++) { Point2D dstPt = destPts[i]; Point2D srcPt = warp.mapDestPoint(destPts[i]); destCoords[offset] = (float)dstPt.getX(); destCoords[offset+1] = (float)dstPt.getY(); sourceCoords[offset] = (float)srcPt.getX(); sourceCoords[offset+1] = (float)srcPt.getY(); offset += 2; } // Guaranteed to be a WarpAffine as the degree is 1. WarpAffine wa = (WarpAffine)WarpPolynomial.createWarp(sourceCoords, 0, destCoords, 0, 8, 1.0F, 1.0F, 1.0F, 1.0F, 1); destBounds = wa.mapSourceRect(sourceBounds); } // If bounds available, clone or create a new ImageLayout // to be modified. if(destBounds != null) { if(layout == null) { layout = new ImageLayout(destBounds.x, destBounds.y, destBounds.width, destBounds.height); } else { layout = (ImageLayout)layout.clone(); layout.setMinX(destBounds.x); layout.setMinY(destBounds.y); layout.setWidth(destBounds.width); layout.setHeight(destBounds.height); } } return layout; } /** * Constructor. * *

    The image's layout is encapsulated in the layout * argument. The user-supplied layout values supersedes the default * settings. Any layout setting not specified by the user will take * the corresponding value of the source image's layout. * * @param layout The layout of this image. * @param source The source image; can not be null. * @param configuration Configurable attributes of the image including * configuration variables indexed by * RenderingHints.Keys and image properties indexed * by Strings or CaselessStringKeys. * This is simply forwarded to the superclass constructor. * @param cobbleSources A boolean indicating whether * computeRect() expects contiguous sources. * To use the default implementation of warping contained in * this class, set cobbleSources to false. * @param extender A BorderExtender, or null. * @param interp The Interpolation object describing the * interpolation method. * @param warp The Warp object describing the warp. * * @throws IllegalArgumentException if source * is null. * @throws IllegalArgumentException if combining the * source bounds with the layout parameter results in negative * output width or height. * @throws IllegalArgumentException If warp is * null. * @since JAI 1.1 */ public WarpOpImage(RenderedImage source, ImageLayout layout, Map configuration, boolean cobbleSources, BorderExtender extender, Interpolation interp, Warp warp) { this(source, layout, configuration, cobbleSources, extender, interp, warp, null); } /** * Constructor. * *

    The image's layout is encapsulated in the layout * argument. The user-supplied layout values supersedes the default * settings. Any layout setting not specified by the user will take * the corresponding value of the source image's layout. * * @param layout The layout of this image. * @param source The source image; can not be null. * @param configuration Configurable attributes of the image including * configuration variables indexed by * RenderingHints.Keys and image properties indexed * by Strings or CaselessStringKeys. * This is simply forwarded to the superclass constructor. * @param cobbleSources A boolean indicating whether * computeRect() expects contiguous sources. * To use the default implementation of warping contained in * this class, set cobbleSources to false. * @param extender A BorderExtender, or null. * @param interp The Interpolation object describing the * interpolation method. * @param warp The Warp object describing the warp. * @param backgroundValues The user-specified background values. If the * provided array length is smaller than the number of bands, all * the bands will be filled with the first element of the array. * If the provided array is null, it will be set to * new double[]{0.0} in the superclass. * * @throws IllegalArgumentException if source * is null. * @throws IllegalArgumentException if combining the * source bounds with the layout parameter results in negative * output width or height. * @throws IllegalArgumentException If warp is * null. * * @since JAI 1.1.2 */ public WarpOpImage(RenderedImage source, ImageLayout layout, Map configuration, boolean cobbleSources, BorderExtender extender, Interpolation interp, Warp warp, double[] backgroundValues) { super(vectorize(source), // vectorize() checks for null source. getLayout(layout, source, warp), configuration, cobbleSources, extender, interp, backgroundValues); if (warp == null) { throw new IllegalArgumentException( JaiI18N.getString("Generic0")); } this.warp = warp; if (cobbleSources && extender == null) { // Do a basic forward mapping, taking into account the // pixel energy is at (0.5, 0.5). int l = interp == null ? 0 : interp.getLeftPadding(); int r = interp == null ? 0 : interp.getRightPadding(); int t = interp == null ? 0 : interp.getTopPadding(); int b = interp == null ? 0 : interp.getBottomPadding(); int x = getMinX() + l; int y = getMinY() + t; int w = Math.max(getWidth() - l - r, 0); int h = Math.max(getHeight() - t - b, 0); computableBounds = new Rectangle(x, y, w, h); } else { // Extender is availabe, write the entire destination. computableBounds = getBounds(); } } /** * Returns the number of samples required to the left of the center. * * @return The left padding factor. * * @deprecated as of JAI 1.1. */ public int getLeftPadding() { return interp == null ? 0 : interp.getLeftPadding(); } /** * Returns the number of samples required to the right of the center. * * @return The right padding factor. * * @deprecated as of JAI 1.1. */ public int getRightPadding() { return interp == null ? 0 : interp.getRightPadding(); } /** * Returns the number of samples required above the center. * * @return The top padding factor. * * @deprecated as of JAI 1.1. */ public int getTopPadding() { return interp == null ? 0 : interp.getTopPadding(); } /** * Returns the number of samples required below the center. * * @return The bottom padding factor. * * @deprecated as of JAI 1.1. */ public int getBottomPadding() { return interp == null ? 0 : interp.getBottomPadding(); } /** * Computes the position in the specified source that best * matches the supplied destination image position. * *

    The implementation in this class returns the value returned by * warp.mapDestPoint(destPt). Subclasses requiring * different behavior should override this method.

    * * @param destPt the position in destination image coordinates * to map to source image coordinates. * @param sourceIndex the index of the source image. * * @return a Point2D of the same class as * destPt or null. * * @throws IllegalArgumentException if destPt is * null. * @throws IndexOutOfBoundsException if sourceIndex is * non-zero. * * @since JAI 1.1.2 */ public Point2D mapDestPoint(Point2D destPt, int sourceIndex) { if (destPt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } else if (sourceIndex != 0) { throw new IndexOutOfBoundsException(JaiI18N.getString("Generic1")); } return warp.mapDestPoint(destPt); } /** * Computes the position in the destination that best * matches the supplied source image position. *

    The implementation in this class returns the value returned by * warp.mapSourcePoint(sourcePt). Subclasses requiring * different behavior should override this method.

    * * @param sourcePt the position in source image coordinates * to map to destination image coordinates. * @param sourceIndex the index of the source image. * * @return a Point2D of the same class as * sourcePt or null. * * @throws IllegalArgumentException if sourcePt is * null. * @throws IndexOutOfBoundsException if sourceIndex is * non-zero. * * @since JAI 1.1.2 */ public Point2D mapSourcePoint(Point2D sourcePt, int sourceIndex) { if (sourcePt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } else if (sourceIndex != 0) { throw new IndexOutOfBoundsException(JaiI18N.getString("Generic1")); } return warp.mapSourcePoint(sourcePt); } /** * Returns the minimum bounding box of the region of the destination * to which a particular Rectangle of the specified source * will be mapped. * * @param sourceRect the Rectangle in source coordinates. * @param sourceIndex the index of the source image. * * @return a Rectangle indicating the destination * bounding box, or null if the bounding box * is unknown. * * @throws IllegalArgumentException if sourceIndex is * negative or greater than the index of the last source. * @throws IllegalArgumentException if sourceRect is * null. * * @since JAI 1.1 */ protected Rectangle forwardMapRect(Rectangle sourceRect, int sourceIndex) { if ( sourceRect == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sourceIndex != 0) { // this image only has one source throw new IllegalArgumentException( JaiI18N.getString("Generic1")); } return warp.mapSourceRect(sourceRect); } /** * Returns the minimum bounding box of the region of the specified * source to which a particular Rectangle of the * destination will be mapped. * * @param destRect the Rectangle in destination coordinates. * @param sourceIndex the index of the source image. * * @return a Rectangle indicating the source bounding box, * or null if the bounding box is unknown. * * @throws IllegalArgumentException if sourceIndex is * negative or greater than the index of the last source. * @throws IllegalArgumentException if destRect is * null. * * @since JAI 1.1 */ protected Rectangle backwardMapRect(Rectangle destRect, int sourceIndex) { if ( destRect == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sourceIndex != 0) { // this image only has one source throw new IllegalArgumentException( JaiI18N.getString("Generic1")); } Rectangle wrect = warp.mapDestRect(destRect); return wrect == null ? getSource(0).getBounds() : wrect; } /** * Computes a tile. A new WritableRaster is created to * represent the requested tile. Its width and height equals to this * image's tile width and tile height respectively. This method * assumes that the requested tile either intersects or is within * the bounds of this image. * *

    Whether or not this method performs source cobbling is determined * by the cobbleSources variable set at construction time. * If cobbleSources is true, cobbling is * performed on the source for areas that intersect multiple tiles, * and computeRect(Raster[], WritableRaster, Rectangle) * is called to perform the actual computation. Otherwise, * computeRect(PlanarImage[], WritableRaster, Rectangle) * is called to perform the actual computation. * * @param tileX The X index of the tile. * @param tileY The Y index of the tile. * * @return The tile as a Raster. */ public Raster computeTile(int tileX, int tileY) { // The origin of the tile. Point org = new Point(tileXToX(tileX), tileYToY(tileY)); // Create a new WritableRaster to represent this tile. WritableRaster dest = createWritableRaster(sampleModel, org); // Find the intersection between this tile and the writable bounds. Rectangle destRect = new Rectangle(org.x, org.y, tileWidth, tileHeight).intersection(computableBounds); if (destRect.isEmpty()) { if (setBackground) { ImageUtil.fillBackground(dest, destRect, backgroundValues); } return dest; // tile completely outside of computable bounds } PlanarImage source = getSource(0); Rectangle srcRect = mapDestRect(destRect, 0); if (!srcRect.intersects(source.getBounds())) { if (setBackground) { ImageUtil.fillBackground(dest, destRect, backgroundValues); } return dest; // outside of source bounds } // This image only has one source. if (cobbleSources) { Raster[] srcs = new Raster[1]; srcs[0] = extender != null ? source.getExtendedData(srcRect, extender) : source.getData(srcRect); // Compute the destination tile. computeRect(srcs, dest, destRect); // Recycle the source tile if(source.overlapsMultipleTiles(srcRect)) { recycleTile(srcs[0]); } } else { PlanarImage[] srcs = { source }; computeRect(srcs, dest, destRect); } return dest; } } jai-core-1.1.4/src/share/classes/javax/media/jai/WarpPolynomial.java0000644000175000017500000003270310665377773025213 0ustar mathieumathieu/* * $RCSfile: WarpPolynomial.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2007-08-29 23:23:39 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Rectangle; import java.awt.geom.Point2D; import com.sun.media.jai.util.PolyWarpSolver; /** * A polynomial-based description of an image warp. * *

    The mapping is defined by two bivariate polynomial functions * X(x, y) and Y(x, y) that map destination (x, y) coordinates * to source X and Y positions respectively * *

    The functions X(x, y) and Y(x, y) have the form: *

     * SUM{i = 0 to n} {SUM{j = 0 to i}{a_ij*x^(i - j)*y^j}}
     *
     * where n is the degree os the polynomial
     * 
    * *

    WarpAffine, WarpQuadratic, and WarpCubic are special cases of * WarpPolynomial for n equal to 1, 2, and 3 respectively. * WarpGeneralPolynomial provides a concrete implementation for * polynomials of higher degree. * * @see WarpAffine * @see WarpQuadratic * @see WarpCubic * @see WarpGeneralPolynomial * */ public abstract class WarpPolynomial extends Warp { /** * An array of coefficients that maps a destination point to * the source's X coordinate. */ protected float[] xCoeffs; /** * An array of coefficients that maps a destination point to * the source's Y coordinate. */ protected float[] yCoeffs; /** * A scaling factor applied to input (dest) x coordinates to * improve computational accuracy. */ protected float preScaleX; /** * A scaling factor applied to input (dest) y coordinates to * improve computational accuracy. */ protected float preScaleY; /** * A scaling factor applied to the result of the X polynomial * evaluation which compensates for the input scaling, so that * the correctly scaled result is obtained. */ protected float postScaleX; /** * A scaling factor applied to the result of the Y polynomial * evaluation which compensates for the input scaling, so that * the correctly scaled result is obtained. */ protected float postScaleY; /** * The degree of the polynomial, determined by the number of * coefficients supplied via the X and Y coefficients arrays. */ protected int degree; /** * Constructs a WarpPolynomial with a given transform mapping * destination pixels into source space. Note that this is * a backward mapping as opposed to the forward mapping used in * AffineOpImage. * *

    The xCoeffs and yCoeffs parameters * must contain the same number of coefficients of the form * (n + 1)(n + 2)/2 for some n, where * n is the non-negative degree power of the polynomial. * The coefficients, in order, are associated with the terms: * *

         * 1, x, y, x^2, x*y, y^2, ..., x^n, x^(n - 1)*y, ..., x*y^(n - 1), y^n
         * 
    * * and coefficients of value 0 cannot be omitted. * *

    The destination (x, y) coordinates are multiplied by the * factors preScaleX and preScaleY prior to the evaluation of the * polynomial. The results of the polynomial evaluations are * multiplied by postScaleX and postScaleY to produce the source * pixel coordinates. This process allows for better precision of * the results. * * @param xCoeffs The destination to source transform coefficients for * the X coordinate. * @param yCoeffs The destination to source transform coefficients for * the Y coordinate. * @param preScaleX The scale factor to apply to input (dest) X positions. * @param preScaleY The scale factor to apply to input (dest) Y positions. * @param postScaleX The scale factor to apply to the X polynomial output. * @param postScaleY The scale factor to apply to the Y polynomial output. * @throws IllegalArgumentException if xCoeff or yCoeff have an illegal number of entries. */ public WarpPolynomial(float[] xCoeffs, float[] yCoeffs, float preScaleX, float preScaleY, float postScaleX, float postScaleY) { if (xCoeffs == null || yCoeffs == null || xCoeffs.length < 1 || yCoeffs.length < 1 || xCoeffs.length != yCoeffs.length) { throw new IllegalArgumentException( JaiI18N.getString("WarpPolynomial0")); } int numCoeffs = xCoeffs.length; degree = -1; while (numCoeffs > 0) { degree++; numCoeffs -= degree + 1; } if (numCoeffs != 0) { throw new IllegalArgumentException( JaiI18N.getString("WarpPolynomial0")); } this.xCoeffs = (float[])(xCoeffs.clone()); this.yCoeffs = (float[])(yCoeffs.clone()); this.preScaleX = preScaleX; this.preScaleY = preScaleY; this.postScaleX = postScaleX; this.postScaleY = postScaleY; } /** * Constructs a WarpPolynomial with pre- and post-scale factors of 1. * * @param xCoeffs The destination to source transform coefficients for * the X coordinate. * @param yCoeffs The destination to source transform coefficients for * the Y coordinate. */ public WarpPolynomial(float[] xCoeffs, float[] yCoeffs) { this(xCoeffs, yCoeffs, 1.0F, 1.0F, 1.0F, 1.0F); } /** * Returns the raw coefficients array for the X coordinate mapping. * * @return A cloned array of floats giving the * polynomial coefficients for the X coordinate mapping. */ public float[] getXCoeffs() { return (float[])xCoeffs.clone(); } /** * Returns the raw coefficients array for the Y coordinate mapping. * * @return A cloned array of floats giving the * polynomial coefficients for the Y coordinate mapping. */ public float[] getYCoeffs() { return (float[])yCoeffs.clone(); } /** * Returns the raw coefficients array for both the X and Y coordinate mapping. * * @return A cloned two-dimensional array of floats giving the * polynomial coefficients for the X and Y coordinate mapping. */ public float[][] getCoeffs() { float[][] coeffs = new float[2][]; coeffs[0] = (float[])xCoeffs.clone(); coeffs[1] = (float[])yCoeffs.clone(); return coeffs; } /** Returns the scaling factor applied to input (dest) X coordinates. */ public float getPreScaleX() { return preScaleX; } /** Returns the scaling factor applied to input (dest) Y coordinates. */ public float getPreScaleY() { return preScaleY; } /** Returns the scaling factor applied to the result of the X polynomial. */ public float getPostScaleX() { return postScaleX; } /** Returns the scaling factor applied to the result of the Y polynomial. */ public float getPostScaleY() { return postScaleY; } /** * Returns the degree of the warp polynomials. * * @return The degree as an int. */ public int getDegree() { return degree; } /** * Returns an instance of WarpPolynomial or its * subclasses that approximately maps the given scaled destination * image coordinates into the given scaled source image * coordinates. The mapping is given by: * *

         * x' = postScaleX*(xpoly(x*preScaleX, y*preScaleY));
         * x' = postScaleY*(ypoly(x*preScaleX, y*preScaleY));
         * 
    * *

    Typically, it is useful to set preScaleX to * 1.0F/destImage.getWidth() and * postScaleX to srcImage.getWidth() so * that the input and output of the polynomials lie between 0 and * 1. * *

    The degree of the polynomial is supplied as an argument. * * @param sourceCoords An array of floats containing the * source coordinates with X and Y alternating. * @param sourceOffset the initial entry of sourceCoords * to be used. * @param destCoords An array of floats containing the * destination coordinates with X and Y alternating. * @param destOffset The initial entry of destCoords * to be used. * @param numCoords The number of coordinates from * sourceCoords and destCoords to be used. * @param preScaleX The scale factor to apply to input (dest) X positions. * @param preScaleY The scale factor to apply to input (dest) Y positions. * @param postScaleX The scale factor to apply to X polynomial output. * @param postScaleY The scale factor to apply to the Y polynomial output. * @param degree The desired degree of the warp polynomials. * * @return An instance of WarpPolynomial. * @throws IllegalArgumentException if arrays sourceCoords or destCoords * are too small */ public static WarpPolynomial createWarp(float[] sourceCoords, int sourceOffset, float[] destCoords, int destOffset, int numCoords, float preScaleX, float preScaleY, float postScaleX, float postScaleY, int degree) { int minNumPoints = (degree+1) * (degree+2); if ((sourceOffset + minNumPoints) > sourceCoords.length || (destOffset + minNumPoints) > destCoords.length) { throw new IllegalArgumentException( JaiI18N.getString("WarpPolynomial1")); } float[] coeffs = PolyWarpSolver.getCoeffs(sourceCoords, sourceOffset, destCoords, destOffset, numCoords, preScaleX, preScaleY, postScaleX, postScaleY, degree); int numCoeffs = coeffs.length / 2; float[] xCoeffs = new float[numCoeffs]; float[] yCoeffs = new float[numCoeffs]; for (int i = 0; i < numCoeffs; i++) { xCoeffs[i] = coeffs[i]; yCoeffs[i] = coeffs[i + numCoeffs]; } if (degree == 1) { return new WarpAffine(xCoeffs, yCoeffs, preScaleX, preScaleY, postScaleX, postScaleY); } else if (degree == 2) { return new WarpQuadratic(xCoeffs, yCoeffs, preScaleX, preScaleY, postScaleX, postScaleY); } else if (degree == 3) { return new WarpCubic(xCoeffs, yCoeffs, preScaleX, preScaleY, postScaleX, postScaleY); } else { return new WarpGeneralPolynomial(xCoeffs, yCoeffs, preScaleX, preScaleY, postScaleX, postScaleY); } } /** * Computes the source point corresponding to the supplied point. * *

    This method returns the value of pt in the following * code snippet: * *

         * double dx = (destPt.getX() + 0.5)*preScaleX;
         * double dy = (destPt.getY() + 0.5)*preScaleY;
         *
         * double sx = 0.0;
         * double sy = 0.0;
         * int c = 0;
         *
         * for(int nx = 0; nx <= degree; nx++) {
         *     for(int ny = 0; ny <= nx; ny++) {
         *         double t = Math.pow(dx, nx - ny)*Math.pow(dy, ny);
         *         sx += xCoeffs[c] * t;
         *         sy += yCoeffs[c] * t;
         *         c++;
         *     }
         * }
    
         * Point2D pt = (Point2D)destPt.clone();
         * pt.setLocation(sx*postScaleX - 0.5, sy*postScaleY - 0.5);
         * 
    *

    * * @param destPt the position in destination image coordinates * to map to source image coordinates. * * @return a Point2D of the same class as * destPt. * * @throws IllegalArgumentException if destPt is * null. * * @since JAI 1.1.2 */ public Point2D mapDestPoint(Point2D destPt) { if (destPt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } double dx = (destPt.getX() + 0.5)*preScaleX; double dy = (destPt.getY() + 0.5)*preScaleY; double sx = 0.0; double sy = 0.0; int c = 0; for(int nx = 0; nx <= degree; nx++) { for(int ny = 0; ny <= nx; ny++) { double t = Math.pow(dx, nx - ny)*Math.pow(dy, ny); sx += xCoeffs[c] * t; sy += yCoeffs[c] * t; c++; } } Point2D pt = (Point2D)destPt.clone(); pt.setLocation(sx*postScaleX - 0.5, sy*postScaleY - 0.5); return pt; } } jai-core-1.1.4/src/share/classes/javax/media/jai/OperationGraph.java0000644000175000017500000001605710203035544025133 0ustar mathieumathieu/* * $RCSfile: OperationGraph.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:13 $ * $State: Exp $ */ package javax.media.jai; import java.util.Enumeration; import java.util.Vector; /** * OperationGraph manages a list of PartialOrderNodes * and pairwise preferences between them. * * The getOrderedOperationList method performs a topological sort. * The topological sort follows the algorithm described in Horowitz * and Sahni, Fundamentals of Data Structures (1976), p. 315. * *

    Several minor changes are made to their implementation. First, * nodes are represented as objects, not as integers. The count * (in-degree) field is not used to link zero in-degree objects, but * instead a separate zeroLink field is used. The neighbor lists are * stored as Vectors, not linked lists, and enumerations are used * to iterate over them. * *

    This class is used by the implementation of the OperationRegistry * class and is not intended to be part of the API. * * - what was OperationGraph pre-JAI 1.1 is now FactoryOperationGraph * */ class OperationGraph implements java.io.Serializable { /** * A Vector of PartialOrderNodes, each * PartialOrderNode contains the name of the operation * and the OperationGraph that contains the image * factories implementing that operation. */ Vector operations = new Vector(); /** A cached version of the ordered product list */ Vector orderedOperations; /** Signifies whether the cached copy is out of date. */ boolean isChanged = true; /** * If true, use a case-insensitive compare of the name of the * PartialOrderNode for lookups. */ private boolean lookupByName = false; /** * Constructs an OperationGraph. * The default comparision for lookups is by object reference. */ OperationGraph() {} /** * Specify the comparator used to compare the PartialOrderNode * with an object (used for lookupOp) * * @param lookupByName if true lookup does a case-insensitive * compare of the object being looked up with the * PartialOrderNode name. Needless to say, * this works only if the objects being looked * up are Strings. */ OperationGraph(boolean lookupByName) { this.lookupByName = lookupByName; } /** * The comparison used for lookups. */ private boolean compare(PartialOrderNode poNode, Object op) { if (lookupByName) return poNode.getName().equalsIgnoreCase((String)op); else return poNode.getData() == op; } /** * Adds a PartialOrderNode to an OperationGraph. */ void addOp(PartialOrderNode poNode) { operations.addElement(poNode); isChanged = true; } /** * Removes the PartialOrderNode corresponding to the operation * from an OperationGraph. * * Does a "lookupOp" of the PartialOrderNode corresponding to "op" * and removes it. */ synchronized boolean removeOp(Object op) { boolean retval = false; PartialOrderNode poNode = lookupOp(op); if (poNode != null) { retval = operations.removeElement(poNode); if (retval) isChanged = true; } return retval; } /** * Locates an operation from within the vector of PartialOrderNodes * using the object provided. */ PartialOrderNode lookupOp(Object op) { int num = operations.size(); for (int i = 0; i < num; i++) { PartialOrderNode poNode = (PartialOrderNode)operations.elementAt(i); if (compare(poNode, op)) { PartialOrderNode tempNode = poNode; return tempNode; } } return null; } /** Sets a preference between two operations. */ synchronized boolean setPreference(Object preferred, Object other) { boolean retval = false; if ((preferred == null) || (other == null)) { throw new IllegalArgumentException( JaiI18N.getString("Generic0")); } if (preferred == other) return retval; PartialOrderNode preferredPONode = lookupOp(preferred); PartialOrderNode otherPONode = lookupOp(other); if ((preferredPONode != null) && (otherPONode != null)) { preferredPONode.addEdge(otherPONode); retval = true; isChanged = true; } return retval; } /** Removes a preference between two operations. */ synchronized boolean unsetPreference(Object preferred, Object other) { boolean retval = false; if ((preferred == null) || (other == null)) { throw new IllegalArgumentException( JaiI18N.getString("Generic0")); } if (preferred == other) return retval; PartialOrderNode preferredPONode = lookupOp(preferred); PartialOrderNode otherPONode = lookupOp(other); if ((preferredPONode != null) && (otherPONode != null)) { preferredPONode.removeEdge(otherPONode); retval = true; isChanged = true; } return retval; } /** * Performs a topological sort on the set of * PartialOrderNodes. */ public synchronized Vector getOrderedOperationList() { // If the cached copy is still current, return it if (isChanged == false) { Vector ordered = orderedOperations; return ordered; } int num = operations.size(); // The number of nodes in the digraph for (int i=0; iRenderedImage. Given a RenderedImage, * which represents the image at the highest resolution level, the * image at each lower resolution level may be derived by performing a * specific chain of operations to down sample the image at the next * higher resolution level repeatedly. The highest resolution level is * defined as level 0. * *

    The downSampler is a chain of operations that is * used to derive the image at the next lower resolution level from * the image at the current resolution level. That is, given an image * at resolution level i, the downSampler is * used to obtain the image at resolution level i+1. * The chain may contain one or more operation nodes; however, each * node must be a RenderedOp. The parameter points to the * last node in the chain. The very first node in the chain must be * a RenderedOp that takes one RenderedImage * as its source. All other nodes may have multiple sources. When * traversing back up the chain, if a node has more than one source, * the first source, source0, is used to move up the * chain. This parameter is saved by reference. * * @see ImagePyramid * */ public class ImageMIPMap implements ImageJAI { /** The image with the highest resolution. */ protected RenderedImage highestImage; /** The image at the current resolution level. */ protected RenderedImage currentImage; /** The current resolution level. */ protected int currentLevel = 0; /** The operation chain used to derive the lower resolution images. */ protected RenderedOp downSampler; /** * A helper object to manage firing events. * * @since JAI 1.1 */ protected PropertyChangeSupportJAI eventManager = null; /** * A helper object to manage the image properties. * * @since JAI 1.1 */ protected WritablePropertySourceImpl properties = null; /** The default constructor. */ protected ImageMIPMap() { eventManager = new PropertyChangeSupportJAI(this); properties = new WritablePropertySourceImpl(null, null, eventManager); } /** * Constructor. The down sampler is an "affine" operation that * uses the supplied AffineTransform and * Interpolation objects. * All input parameters are saved by reference. * * @param image The image with the highest resolution. * @param transform An affine matrix used with an "affine" operation * to derive the lower resolution images. * @param interpolation The interpolation method for the "affine" * operation. It may be null, in which case the * default "nearest neighbor" interpolation method is used. * * @throws IllegalArgumentException if image is * null. * @throws IllegalArgumentException if transform is * null. */ public ImageMIPMap(RenderedImage image, AffineTransform transform, Interpolation interpolation) { this(); if ( image == null || transform == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } ParameterBlock pb = new ParameterBlock(); pb.addSource(image); pb.add(transform); pb.add(interpolation); downSampler = JAI.create("affine", pb); downSampler.removeSources(); highestImage = image; currentImage = highestImage; } /** * Constructor. The downSampler points to the last * operation node in the RenderedOp chain. The very * first operation in the chain must not have any source images * specified; that is, its number of sources must be 0. All input * parameters are saved by reference. * * @param image The image with the highest resolution. * @param downSampler The operation chain used to derive the lower * resolution images. No validation is done on the first * operation in the chain. * * @throws IllegalArgumentException if image is null. * @throws IllegalArgumentException if downSampler is * null. */ public ImageMIPMap(RenderedImage image, RenderedOp downSampler) { this(); if (image == null || downSampler == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } highestImage = image; currentImage = highestImage; this.downSampler = downSampler; } /** * Constructs a new ImageMIPMap from a * RenderedOp chain. The downSampler * points to the last operation node in the * RenderedOp chain. The source image is determined * by traversing up the chain: starting at the bottom node, given by * the downSample parameter, we move to the first * source of the node and repeat until we find either a sourceless * RenderedOp or any other type of * RenderedImage. * * The downSampler parameter is saved by reference * and should not be modified during the lifetime of any * ImageMIPMap referring to it. * * @param downSampler The operation chain used to derive the lower * resolution images. The source of the first node in this * chain is taken as the image with the highest resolution. * * @throws IllegalArgumentException if downSampler is * null. * @throws IllegalArgumentException if downSampler * has no sources. * @throws IllegalArgumentException if an object other than a * RenderedImage is found in the * downSampler chain. */ public ImageMIPMap(RenderedOp downSampler) { this(); if ( downSampler == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (downSampler.getNumSources() == 0) { throw new IllegalArgumentException( JaiI18N.getString("ImageMIPMap0")); } // Find the highest resolution image from the chain. RenderedOp op = downSampler; while (true) { Object src = op.getNodeSource(0); if (src instanceof RenderedOp) { RenderedOp srcOp = (RenderedOp)src; if (srcOp.getNumSources() == 0) { highestImage = srcOp; op.removeSources(); break; } else { op = srcOp; } } else if (src instanceof RenderedImage) { highestImage = (RenderedImage)src; op.removeSources(); break; } else { throw new IllegalArgumentException( JaiI18N.getString("ImageMIPMap1")); } } currentImage = highestImage; this.downSampler = downSampler; } /** * Returns an array of Strings recognized as names by * this property source. If no property names match, * null will be returned. * *

    The default implementation returns null, i.e., * no property names are recognized. * * @return An array of Strings giving the valid * property names. */ public String[] getPropertyNames() { return properties.getPropertyNames(); } /** * Returns an array of Strings recognized as names by * this property source that begin with the supplied prefix. If * no property names are recognized, or no property names match, * null will be returned. * The comparison is done in a case-independent manner. * * @return An array of Strings giving the valid * property names. * * @param prefix the supplied prefix for the property source. * * @throws IllegalArgumentException if prefix is * null. */ public String[] getPropertyNames(String prefix) { return properties.getPropertyNames(prefix); } /** * Returns the class expected to be returned by a request for * the property with the specified name. If this information * is unavailable, null will be returned. * * @return The Class expected to be return by a * request for the value of this property or null. * * @exception IllegalArgumentException if name * is null. * * @since JAI 1.1 */ public Class getPropertyClass(String name) { return properties.getPropertyClass(name); } /** * Returns the specified property. The default implementation * returns java.awt.Image.UndefinedProperty. * * @param name The name of the property. * * @return The value of the property, as an Object. * * @exception IllegalArgumentException if name * is null. */ public Object getProperty(String name) { return properties.getProperty(name); } /** * Sets a property on a ImageMIPMap. * * @param name a String containing the property's name. * @param value the property, as a general Object. * * @throws IllegalArgumentException If name or * value is null. * * @since JAI 1.1 */ public void setProperty(String name, Object value) { properties.setProperty(name, value); } /** * Removes the named property from the ImageMIPMap. * * @exception IllegalArgumentException if name * is null. * * @since JAI 1.1 */ public void removeProperty(String name) { properties.removeProperty(name); } /** * Add a PropertyChangeListener to the listener list. The * listener is registered for all properties. * * @since JAI 1.1 */ public void addPropertyChangeListener(PropertyChangeListener listener) { eventManager.addPropertyChangeListener(listener); } /** * Add a PropertyChangeListener for a specific property. The * listener will be invoked only when a call on * firePropertyChange names that specific property. The case of * the name is ignored. * * @since JAI 1.1 */ public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { eventManager.addPropertyChangeListener(propertyName, listener); } /** * Remove a PropertyChangeListener from the listener list. This * removes a PropertyChangeListener that was registered for all * properties. * * @since JAI 1.1 */ public void removePropertyChangeListener(PropertyChangeListener listener) { eventManager.removePropertyChangeListener(listener); } /** * Remove a PropertyChangeListener for a specific property. The case * of the name is ignored. * * @since JAI 1.1 */ public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { eventManager.removePropertyChangeListener(propertyName, listener); } /** * Returns the current resolution level. The highest resolution * level is defined as level 0. */ public int getCurrentLevel() { return currentLevel; } /** Returns the image at the current resolution level. */ public RenderedImage getCurrentImage() { return currentImage; } /** * Returns the image at the specified resolution level. The * requested level must be greater than or equal to 0 or * null will be returned. * * @param level The specified level of resolution */ public RenderedImage getImage(int level) { if (level < 0) { return null; } if (level < currentLevel) { // restart from the highest image currentImage = highestImage; currentLevel = 0; } while (currentLevel < level) { getDownImage(); } return currentImage; } /** * Returns the image at the next lower resolution level, * obtained by applying the downSampler on the * image at the current resolution level. */ public RenderedImage getDownImage() { currentLevel++; /* Duplicate the downSampler op chain. */ RenderedOp op = duplicate(downSampler, vectorize(currentImage)); currentImage = op.getRendering(); return currentImage; } /** * Duplicates a RenderedOp chain. Each node in the * chain must be a RenderedOp. The op * parameter points to the last RenderedOp in the chain. * The very first op in the chain must have no sources and its source * will be set to the supplied image vector. When traversing up the * chain, if any node has more than one source, the first source will * be used. The first source of each node is duplicated; all other * sources are copied by reference. * * @param op RenderedOp chain * @param vector of source images * * @throws IllegalArgumentException if op is null. * @throws IllegalArgumentException if images is null. */ protected RenderedOp duplicate(RenderedOp op, Vector images) { if (images == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } // // Duplicates a RenderedOp with the original OperationRegistry, // OperationName, ParameterBlock, and RenderingHints copied over // by reference. No property information is copied. // op = new RenderedOp(op.getRegistry(), op.getOperationName(), op.getParameterBlock(), op.getRenderingHints()); ParameterBlock pb = new ParameterBlock(); pb.setParameters(op.getParameters()); Vector srcs = op.getSources(); int numSrcs = srcs.size(); if (numSrcs == 0) { // first op in the chain pb.setSources(images); } else { // recursively duplicate source0 pb.addSource(duplicate((RenderedOp)srcs.elementAt(0), images)); for (int i = 1; i < numSrcs; i++) { pb.addSource(srcs.elementAt(i)); } } op.setParameterBlock(pb); return op; } /** * Returns the current image as a RenderableImage. * This method returns a MultiResolutionRenderableImage. * The numImages parameter indicates the number of * RenderedImages used to construct the * MultiResolutionRenderableImage. Starting with the * current image, the images are obtained by finding the necessary * number of lower resolution images using the downSampler. * The current level and current image will not be changed. * If the width or height reaches 1, the downsampling will stop * and return the renderable image. * *

    The numImages should be greater than or equal to 1. * If a value of less than 1 is specified, this method uses 1 image, * which is the current image. * * @param numImages The number of lower resolution images. * @param minX The minimum X coordinate of the Renderable, as a float. * @param minY The minimum Y coordinate of the Renderable, as a float. * @param height The height of the Renderable, as a float. * * @throws IllegalArgumentException if height is less than 0. * * @see MultiResolutionRenderableImage */ public RenderableImage getAsRenderable(int numImages, float minX, float minY, float height) { Vector v = new Vector(); v.add(currentImage); RenderedImage image = currentImage; for (int i = 1; i < numImages; i++) { RenderedOp op = duplicate(downSampler, vectorize(image)); image = op.getRendering(); if ( image.getWidth() <= 1 || image.getHeight() <= 1 ) { break; } v.add(image); } return new MultiResolutionRenderableImage(v, minX, minY, height); } /** * Returns the current image as a RenderableImage. * This method returns a MultiResolutionRenderableImage * with the current image as the only source image, minX and minY * set to 0.0, and height set to 1.0. * * @see MultiResolutionRenderableImage */ public RenderableImage getAsRenderable() { return getAsRenderable(1, 0.0F, 0.0F, 1.0F); } // XXX - see OpImage vectorize and consolidate? // could be public static in PlanarImage /** * Creates and returns a Vector containing a single * element equal to the supplied RenderedImage. * * * @since JAI 1.1 */ protected final Vector vectorize(RenderedImage image) { Vector v = new Vector(1); v.add(image); return v; } /** * Creates and returns a Vector containing two * elements equal to the supplied RenderedImages * in the order given. * * * @since JAI 1.1 */ protected final Vector vectorize(RenderedImage im1, RenderedImage im2) { Vector v = new Vector(2); v.add(im1); v.add(im2); return v; } } jai-core-1.1.4/src/share/classes/javax/media/jai/OperationRegistrySpi.java0000644000175000017500000000603310203035544026347 0ustar mathieumathieu/* * $RCSfile: OperationRegistrySpi.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:13 $ * $State: Exp $ */ package javax.media.jai; // This uses the ImageIO idea of "services" to look for // concrete class that implement this interface. These concrete // classes must have been registered/listed in the // META-INF/services/javax.media.jai.OperationRegistrySpi file. /** *

    An interface definition to aid in the automatic loading of * user-defined JAI operations. * *

    All concrete classes that implement this * interface can register by listing themselves in the * "META-INF/services/javax.media.jai.OperationRegistrySpi" * file that can be found in the classpath (this file is often contained * in a jar file along with the class files). The file should contain * a list of fully-qualified concrete provider-class names, one per * line. Space and tab characters surrounding each name, as well as * blank lines, are ignored. The comment character is '#' * (0x23); on each line all characters following the first * comment character are ignored. The file must be encoded in UTF-8. * *

    If a particular concrete provider class is named in more than one * configuration file, or is named in the same configuration file more * than once, then the duplicates will be ignored. The configuration * file naming a particular provider need not be in the same jar file or * other distribution unit as the provider itself. The provider must be * accessible from the same class loader that was initially queried to * locate the configuration file; note that this is not necessarily the * class loader that found the file. * *

    All such concrete classes must have a zero-argument * constructor so that they may be instantiated during lookup. The * updateRegistry() method of all such registered * classes will be called with the default instance of the JAI * OperationRegistry after it has been initialized with the * default JAI registry file (META-INF/javax.media.jai.registryFile.jai) * and once all "META-INF/registryFile.jai"s found in the * classpath are loaded. There is no guarantee of the order * in which the updateRegistry() method of each * OperationRegistrySpi instance will be invoked. * *

    The OperationRegistrySpi could also be used to for * the registration of other JAI related objects done through static * methods such as the Serializer objects and image * codecs. * * @see javax.media.jai.remote.Serializer * @see javax.media.jai.OperationRegistry * @see javax.media.jai.OperationRegistry#writeExternal * * @since JAI 1.1 */ public interface OperationRegistrySpi { /** * This method will be called for all registered "service-providers" * of this interface just after the default registry has been * read-in. */ public void updateRegistry(OperationRegistry registry); } jai-core-1.1.4/src/share/classes/javax/media/jai/registry/0000755000175000017500000000000011633360402023207 5ustar mathieumathieujai-core-1.1.4/src/share/classes/javax/media/jai/registry/RenderableRegistryMode.java0000644000175000017500000000422110203035544030450 0ustar mathieumathieu/* * $RCSfile: RenderableRegistryMode.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:49 $ * $State: Exp $ */ package javax.media.jai.registry; import java.awt.image.renderable.RenderableImage; import java.lang.reflect.Method; import javax.media.jai.JAI; import javax.media.jai.RegistryMode; import javax.media.jai.util.ImagingListener; /** * A class that provides information about the "renderable" registry * (operation) mode. * * @since JAI 1.1 */ public class RenderableRegistryMode extends RegistryMode { public static final String MODE_NAME = "renderable"; // The Method used to "create" objects from this factory. private static Method factoryMethod = null; private static Method getThisFactoryMethod() { if (factoryMethod != null) return factoryMethod; // The factory Class that this registry mode represents. Class factoryClass = java.awt.image.renderable.ContextualRenderedImageFactory.class; try { Class[] paramTypes = new Class[] {java.awt.image.renderable.RenderContext.class, java.awt.image.renderable.ParameterBlock.class}; factoryMethod = factoryClass.getMethod("create", paramTypes); } catch (NoSuchMethodException e) { ImagingListener listener = JAI.getDefaultInstance().getImagingListener(); String message = JaiI18N.getString("RegistryMode0") + " " + factoryClass.getName() + "."; listener.errorOccurred(message, e, RenderableRegistryMode.class, false); // e.printStackTrace(); } return factoryMethod; } /** * Constructor. A RegistryMode that represents a * ContextualRenderedImageFactory keyed in a case * insensitive fashion by the string "renderable". The "renderable" * mode has no preferences but supports properties. */ public RenderableRegistryMode() { super(MODE_NAME, javax.media.jai.OperationDescriptor.class, RenderableImage.class, getThisFactoryMethod(), false, true); } } jai-core-1.1.4/src/share/classes/javax/media/jai/registry/RemoteRenderedRegistryMode.java0000644000175000017500000000407510203035544031320 0ustar mathieumathieu/* * $RCSfile: RemoteRenderedRegistryMode.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:49 $ * $State: Exp $ */package javax.media.jai.registry; import java.lang.reflect.Method; import javax.media.jai.JAI; import javax.media.jai.RegistryMode; import javax.media.jai.util.ImagingListener; import javax.media.jai.remote.RemoteDescriptor; /** * A class which provides information about the "remoteRendered" registry * mode. * * @since JAI 1.1 */ public class RemoteRenderedRegistryMode extends RegistryMode { public static final String MODE_NAME = "remoteRendered"; // The Method used to "create" objects from this factory. private static Method factoryMethod = null; private static Method getThisFactoryMethod() { if (factoryMethod != null) return factoryMethod; // The factory Class that this registry mode represents. Class factoryClass = javax.media.jai.remote.RemoteRIF.class; try { Class[] paramTypes = new Class[] {java.lang.String.class, java.lang.String.class, java.awt.image.renderable.ParameterBlock.class, java.awt.RenderingHints.class}; factoryMethod = factoryClass.getMethod("create", paramTypes); } catch (NoSuchMethodException e) { ImagingListener listener = JAI.getDefaultInstance().getImagingListener(); String message = JaiI18N.getString("RegistryMode0") + " " + factoryClass.getName() + "."; listener.errorOccurred(message, e, RemoteRenderedRegistryMode.class, false); // e.printStackTrace(); } return factoryMethod; } /** * Creates a RemoteRenderedRegistryMode for describing * the "remoteRendered" registry mode. */ public RemoteRenderedRegistryMode() { super(MODE_NAME, RemoteDescriptor.class, getThisFactoryMethod().getReturnType(), getThisFactoryMethod(), false, false); } } jai-core-1.1.4/src/share/classes/javax/media/jai/registry/RenderedRegistryMode.java0000644000175000017500000000372010203035544030140 0ustar mathieumathieu/* * $RCSfile: RenderedRegistryMode.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:49 $ * $State: Exp $ */ package javax.media.jai.registry; import java.lang.reflect.Method; import javax.media.jai.JAI; import javax.media.jai.RegistryMode; import javax.media.jai.util.ImagingListener; /** * A class that provides information about the "rendered" registry * (operation) mode. * * @since JAI 1.1 */ public class RenderedRegistryMode extends RegistryMode { public static final String MODE_NAME = "rendered"; // The Method used to "create" objects from this factory. private static Method factoryMethod = null; private static Method getThisFactoryMethod() { if (factoryMethod != null) return factoryMethod; // The factory Class that this registry mode represents. Class factoryClass = java.awt.image.renderable.RenderedImageFactory.class; try { Class[] paramTypes = new Class[] {java.awt.image.renderable.ParameterBlock.class, java.awt.RenderingHints.class}; factoryMethod = factoryClass.getMethod("create", paramTypes); } catch (NoSuchMethodException e) { ImagingListener listener = JAI.getDefaultInstance().getImagingListener(); String message = JaiI18N.getString("RegistryMode0") + " " + factoryClass.getName() + "."; listener.errorOccurred(message, e, RenderedRegistryMode.class, false); // e.printStackTrace(); } return factoryMethod; } /** * Constructor. A RegistryMode that represents a * RenderedImageFactory keyed by the string "rendered". */ public RenderedRegistryMode() { super(MODE_NAME, javax.media.jai.OperationDescriptor.class, getThisFactoryMethod().getReturnType(), getThisFactoryMethod(), true, true); } } jai-core-1.1.4/src/share/classes/javax/media/jai/registry/CRIFRegistry.java0000644000175000017500000001600610203035544026327 0ustar mathieumathieu/* * $RCSfile: CRIFRegistry.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:47 $ * $State: Exp $ */ package javax.media.jai.registry; import java.awt.image.RenderedImage; import java.awt.image.renderable.ContextualRenderedImageFactory; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderContext; import java.util.Vector; import javax.media.jai.JAI; import javax.media.jai.OperationNode; import javax.media.jai.OperationRegistry; import javax.media.jai.PropertySource; import javax.media.jai.RenderableOp; /** * Utility class to provide type-safe interaction * with the OperationRegistry for * ContextualRenderedImageFactory objects. * * If the OperationRegistry is null, then * JAI.getDefaultInstance().getOperationRegistry() will be used. * * @since JAI 1.1 */ public final class CRIFRegistry { private static final String MODE_NAME = RenderableRegistryMode.MODE_NAME; /** * Register a CRIF with a particular operation against * a specified mode. This is JAI 1.0.x equivalent of * registry.registerCRIF(...) * * @param registry the OperationRegistry to register with. * if this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param operationName the operation name as a String * @param crif the ContextualRenderedImageFactory to be registered * * @throws IllegalArgumentException if operationName or crif is * null * @throws IllegalArgumentException if there is no * OperationDescriptor registered against * the operationName */ public static void register(OperationRegistry registry, String operationName, ContextualRenderedImageFactory crif) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); registry.registerFactory(MODE_NAME, operationName, null, crif); } /** * Unregister a CRIF previously registered with an operation * against the specified mode. * * @param registry the OperationRegistry to unregister from. * if this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param operationName the operation name as a String * @param crif the ContextualRenderedImageFactory to be unregistered * * @throws IllegalArgumentException if operationName or crif is * null * @throws IllegalArgumentException if there is no * OperationDescriptor registered against * the operationName * @throws IllegalArgumentException if the crif was not previously * registered against operationName */ public static void unregister(OperationRegistry registry, String operationName, ContextualRenderedImageFactory crif) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); registry.unregisterFactory(MODE_NAME, operationName, null, crif); } /** * Returns the ContextualRenderedImageFactory object * registered against the operation name. * * @param registry the OperationRegistry to use. * if this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param operationName the operation name as a String * * @return a registered ContextualRenderedImageFactory object * * @throws IllegalArgumentException if operationName is null * @throws IllegalArgumentException if there is no * OperationDescriptor registered against * the operationName */ public static ContextualRenderedImageFactory get( OperationRegistry registry, String operationName) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); return (ContextualRenderedImageFactory) registry.getFactory(MODE_NAME, operationName); } /** * Creates a rendering, given a RenderContext and a ParameterBlock * containing the operation's sources and parameters. The registry * is used to determine the CRIF to be used to instantiate the * operation. * * @param registry the OperationRegistry to use. * if this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param operationName the operation name as a String * @param context a RenderContext object containing * the rendering context. * @param paramBlock the operation's ParameterBlock. * * @throws IllegalArgumentException if operationName is null * @throws IllegalArgumentException if there is no * OperationDescriptor registered against * the operationName */ public static RenderedImage create(OperationRegistry registry, String operationName, RenderContext context, ParameterBlock paramBlock) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); Object args[] = { context, paramBlock }; return (RenderedImage) registry.invokeFactory(MODE_NAME, operationName, args); } /** * Constructs and returns a PropertySource suitable for * use by a given RenderableOp. The * PropertySource includes properties copied from prior * nodes as well as those generated at the node itself. Additionally, * property suppression is taken into account. The actual implementation * of getPropertySource() may make use of deferred * execution and caching. * * @param op the RenderableOp requesting its * PropertySource. * * @throws IllegalArgumentException if op is null */ public static PropertySource getPropertySource(RenderableOp op) { if (op == null) throw new IllegalArgumentException("op - " + JaiI18N.getString("Generic0")); return op.getRegistry().getPropertySource((OperationNode)op); } } jai-core-1.1.4/src/share/classes/javax/media/jai/registry/javax.media.jai.registry.properties0000644000175000017500000000073310203035544032126 0ustar mathieumathieu# # $RCSfile: javax.media.jai.registry.properties,v $ # # Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. # # Use is subject to license terms. # # $Revision: 1.1 $ # $Date: 2005-02-11 04:57:50 $ # $State: Exp $ # Generic0=The input argument(s) may not be null. CIFRegistry0=The CollectionOp must be not be renderable. CIFRegistry1=The CollectionOp must be be renderable. RegistryMode0=The method create(ParamterBlock, RenderingHints) is not found in the class jai-core-1.1.4/src/share/classes/javax/media/jai/registry/TileDecoderRegistry.java0000644000175000017500000005762210203035544030000 0ustar mathieumathieu/* * $RCSfile: TileDecoderRegistry.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:49 $ * $State: Exp $ */package javax.media.jai.registry; import java.awt.Point; import java.awt.image.Raster; import java.awt.image.SampleModel; import java.io.InputStream; import java.io.IOException; import java.util.Iterator; import java.util.List; import javax.media.jai.JAI; import javax.media.jai.OperationRegistry; import javax.media.jai.tilecodec.TileCodecParameterList; import javax.media.jai.tilecodec.TileDecoder; import javax.media.jai.tilecodec.TileDecoderFactory; /** * Utility class to provide type-safe interaction with the * OperationRegistry for TileDecoderFactory objects. * * If the OperationRegistry specified as an argument to the * methods in this class is null, then JAI.getOperationRegistry() * will be used. * * @since JAI 1.1 */ public final class TileDecoderRegistry { private static final String MODE_NAME = TileDecoderRegistryMode.MODE_NAME; /** * Registers the given TileDecoderFactory with the given * OperationRegistry under the given formatName and * productName. * * @param registry The OperationRegistry to register the * TileDecoderFactory with. If this is * null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param formatName The formatName to register the * TileDecoderFactory under. * @param productName The productName to register the * TileDecoderFactory under. * @param tdf The TileDecoderFactory to register. * * @throws IllegalArgumentException if formatName is null. * @throws IllegalArgumentException if productName is null. * @throws IllegalArgumentException if tdf is null. * @throws IllegalArgumentException if there is no * TileCodecDescriptor registered against the * given formatName */ public static void register(OperationRegistry registry, String formatName, String productName, TileDecoderFactory tdf) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); registry.registerFactory(MODE_NAME, formatName, productName, tdf); } /** * Unregisters the given TileDecoderFactory previously * registered under the given formatName and productName in the given * OperationRegistry. * * @param registry The OperationRegistry to unregister the * TileDecoderFactory from. If this is * null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param formatName The formatName to unregister the * TileDecoderFactory from. * @param productName The productName to unregister the * TileDecoderFactory from. * @param tdf The TileDecoderFactory to unregister. * * @throws IllegalArgumentException if formatName is null. * @throws IllegalArgumentException if productName is null. * @throws IllegalArgumentException if tdf is null. * @throws IllegalArgumentException if there is no * TileCodecDescriptor registered against the * given formatName * @throws IllegalArgumentException if the tdf was not previously * registered against the given formatName and productName. */ public static void unregister(OperationRegistry registry, String formatName, String productName, TileDecoderFactory tdf) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); registry.unregisterFactory(MODE_NAME, formatName, productName, tdf); } /** * Sets a preference between the given two TileDecoderFactory * objects in the given OperationRegistry under the given * formatName and productName. * * @param registry The OperationRegistry to set * preferences on. If this is * null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param formatName The formatName of the two * TileDecoderFactorys. * @param productName The productName of the two * TileDecoderFactorys. * @param preferredTDF The preferred TileDecoderFactory. * @param otherTDF The other TileDecoderFactory. * * @throws IllegalArgumentException if formatName is null. * @throws IllegalArgumentException if productName is null. * @throws IllegalArgumentException if preferredTDF is null. * @throws IllegalArgumentException if otherTDF is null. * @throws IllegalArgumentException if there is no * TileCodecDescriptor registered against the * given formatName * @throws IllegalArgumentException if either of the two tdf's was * not previously registered against the given formatName and productName. */ public static void setPreference(OperationRegistry registry, String formatName, String productName, TileDecoderFactory preferredTDF, TileDecoderFactory otherTDF) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); registry.setFactoryPreference(MODE_NAME, formatName, productName, preferredTDF, otherTDF); } /** * Unsets a preference previously set amongst the given two * TileDecoderFactory objects in the given * OperationRegistry under the given formatName * and productName. * * @param registry The OperationRegistry to unset * preferences on. If this is * null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param formatName The formatName of the two * TileDecoderFactorys. * @param productName The productName of the two * TileDecoderFactorys. * @param preferredTDF The preferred TileDecoderFactory. * @param otherTDF The other TileDecoderFactory. * * @throws IllegalArgumentException if formatName is null. * @throws IllegalArgumentException if productName is null. * @throws IllegalArgumentException if preferredTDF is null. * @throws IllegalArgumentException if otherTDF is null. * @throws IllegalArgumentException if there is no * TileCodecDescriptor registered against the * given formatName * @throws IllegalArgumentException if either of the two tdf's was * not previously registered against the given formatName and productName. */ public static void unsetPreference(OperationRegistry registry, String formatName, String productName, TileDecoderFactory preferredTDF, TileDecoderFactory otherTDF) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); registry.unsetFactoryPreference(MODE_NAME, formatName, productName, preferredTDF, otherTDF); } /** * Clears all preferences set for registered * TileDecoderFactorys under the given formatName and * productName in the given OperationRegistry. * * @param registry The OperationRegistry to clear * preferences from. If this is * null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param formatName The format name to clear preferences under. * @param productName The productName to clear preferences under. * * @throws IllegalArgumentException if formatName is null. * @throws IllegalArgumentException if productName is null. * @throws IllegalArgumentException if there is no * TileCodecDescriptor registered against the * given formatName. */ public static void clearPreferences(OperationRegistry registry, String formatName, String productName) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); registry.clearFactoryPreferences(MODE_NAME, formatName, productName); } /** * Returns a List of the TileDecoderFactorys registered * in the given OperationRegistry under the given * formatName and productName, in an ordering that satisfies * all of the pairwise preferences that have been set. Returns * null if cycles exist. * * @param registry The OperationRegistry to clear * preferences from. If this is * null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param formatName The format name to clear preferences under. * @param productName The productName to clear preferences under. * * @throws IllegalArgumentException if formatName is null. * @throws IllegalArgumentException if productName is null. * @throws IllegalArgumentException if there is no * TileCodecDescriptor registered against the * given formatName. */ public static List getOrderedList(OperationRegistry registry, String formatName, String productName) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); return registry.getOrderedFactoryList(MODE_NAME, formatName, productName); } /** * Returns an Iterator over all * TileDecoderFactory objects registered under the * given format name over all products. The order of the * TileDecoderFactory objects in the iteration will * be according to the pairwise preferences among products and * TileDecoderFactory objects within a product. The * remove() method of the Iterator * may not be implemented. * * @param registry The OperationRegistry to use. If * this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param formatName The format name. * * @return an Iterator over TileDecoderFactory * objects. * * @throws IllegalArgumentException if formatName is null * @throws IllegalArgumentException if there is no * TileCodecDescriptor registered against the * given formatName. */ public static Iterator getIterator(OperationRegistry registry, String formatName) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); return registry.getFactoryIterator(MODE_NAME, formatName); } /** * Returns the the most preferred TileDecoderFactory * object registered against the given format name. This * method will return the first TileDecoderFactory that * would be encountered by the Iterator returned by the * getIterator() method. * * @param registry The OperationRegistry to use. * If this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param formatName The format name as a String * * @return a registered TileDecoderFactory object * * @throws IllegalArgumentException if formatName is null. * @throws IllegalArgumentException if there is no * TileCodecDescriptor registered against * the formatName */ public static TileDecoderFactory get(OperationRegistry registry, String formatName) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); return (TileDecoderFactory)registry.getFactory(MODE_NAME, formatName); } /** * Creates a TileDecoder for the specified format that is * capable of handling the supplied arguments. * *

    The preferences set amongst the TileDecoderFactory * objects registered with the OperationRegistry are used * to select the most prefered TileDecoderFactory whose * createDecoder() method returns a non-null value. * *

    In order to do the decoding correctly, the caller should * retrieve the TileCodecDescriptor associated with the * returned TileDecoder from the * OperationRegistry and use it's * includesLocationInfo() method's return value to decide * which of the two versions of the decode() method on the * returned TileDecoder should be used. * *

    Since this class is a simple type-safe wrapper around * OperationRegistry's type-unsafe methods, no additional * argument validation is performed in this method. Thus errors/exceptions * may occur if incorrect values are provided for the input arguments. * *

    Exceptions thrown by the TileDecoderFactorys used to * create the TileDecoder will be caught by this method * and will not be propagated. * * @param registry The OperationRegistry to use to create * the TileDecoder. If * this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param formatName The format for which the TileDecoder is * to be created. * @param input The InputStream to read encoded data from. * @param paramList The object containing the tile decoding parameters. * * @throws IllegalArgumentException if formatName is null. * @throws IllegalArgumentException if there is no * TileCodecDescriptor registered against the * given formatName. */ public static TileDecoder create(OperationRegistry registry, String formatName, InputStream input, TileCodecParameterList paramList) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); Object args[] = {input, paramList}; return (TileDecoder)registry.invokeFactory(MODE_NAME, formatName, args); } // Decode methods /** * Decodes the data from the specified InputStream * using the given formatName and TileCodecParameterList. * The TileDecoder which performs the decoding is the * one created from the most prefered TileDecoderFactory * whose create method returns a non-null result. * *

    An IllegalArgumentException will be thrown if * the specified format's TileCodecDescriptor's * includesLocationInfo() method returns false, as no * location information is provided in this method. * *

    If the specified TileCodecParameterList is null, the * default TileCodecParameterList retrieved by the specific * TileDecoder.getDefaultParameters() method for the * "tileDecoder" registry mode will be used. * *

    For the specified format, if the associated * TileCodecDescriptor's includesSampleModelInfo() * method returns false, and either the specified * TileCodecParameterList is null or if it doesn't contain * a non-value for the "sampleModel" parameter, an * IllegalArgumentException will be thrown. * *

    If there are no TileDecoder objects that can decode * the specified InputStream according to the decoding * parameters supplied, null will be returned from this method. * *

    If multiple tiles are to be decoded from the same * InputStream in the same format using the same * TileCodecParameterList, it is advisable to create a * TileDecoder object and use the decode() method * on this decoder for each tile, thus creating and using only a single * TileDecoder object. The decode() * method on TileDecoderRegistry creates a new * TileDecoder object each time it is called. * *

    Since this class is a simple type-safe wrapper around * OperationRegistry's type-unsafe methods, no additional * argument validation is performed in this method. Thus errors/exceptions * may occur if incorrect values are provided for the input arguments. * *

    Exceptions thrown by the TileDecoderFactorys used to * create the TileDecoder will be caught by this method * and will not be propagated. * * @param registry The OperationRegistry to use to create * the TileDecoder. If * this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param formatName The format name associated with the decoder. * @param input The InputStream containing the data * to be decoded. * @param param The TileCodecParameterList to be used. * * @throws IllegalArgumentException if formatName is null. * @throws IOException if an I/O error occurs while reading from the * associated InputStream. * @throws IllegalArgumentException if there is no * TileCodecDescriptor registered against the * given formatName. * * @return The associated TileDecoder, or null. */ public static Raster decode(OperationRegistry registry, String formatName, InputStream input, TileCodecParameterList param) throws IOException { TileDecoder decoder = create(registry, formatName, input, param); if (decoder == null) { return null; } return decoder.decode(); } /** * Decodes the data from the specified InputStream * using the given formatName and TileCodecParameterList. * The TileDecoder which performs the decoding is the * one created from the most prefered TileDecoderFactory * whose create method returns a non-null result. If * there are no TileDecoder objects that can decode * the specified InputStream according to the decoding * parameters supplied, null will be returned from this method. * *

    If the specified TileCodecParameterList is null, the * default TileCodecParameterList retrieved by the specific * TileDecoder.getDefaultParameters() method will be used. * *

    If the specified location is null, and the associated * TileCodecDescriptor's includesLocationInfo() * method returns false, IllegalArgumentException will be * thrown. * *

    For the specified format, if the associated * TileCodecDescriptor's includesSampleModelInfo() * method returns false, and if the specified * TileCodecParameterList is null or if it doesn't contain * a non-value for the "sampleModel" parameter, an * IllegalArgumentException will be thrown. * *

    If multiple tiles are to be decoded from the same * InputStream in the same format using the same * TileCodecParameterList, it is advisable to create a * TileDecoder object and use the decode() method * on this decoder for each tile, thus creating and using only a single * TileDecoder object. The decode() * method on TileDecoderRegistry creates a new * TileDecoder object each time it is called. * *

    Since this class is a simple type-safe wrapper around * OperationRegistry's type-unsafe methods, no additional * argument validation is performed in this method. Thus errors/exceptions * may occur if incorrect values are provided for the input arguments. * *

    Exceptions thrown by the TileDecoderFactorys used to * create the TileDecoder will be caught by this method * and will not be propagated. * * @param registry The OperationRegistry to use to create * the TileDecoder. If * this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param formatName The format name associated with the decoder. * @param input The InputStream containing the data to * be decoded. * @param param The TileCodecParameterList to be used. * @param location The Point specifying the upper left * corner of the Raster. * * @throws IllegalArgumentException if formatName is null. * @throws IOException if an inout/output error occurs while reading from * the associated InputStream or during decoding. * @throws IllegalArgumentException if there is no * TileCodecDescriptor registered against the * given formatName. * @return The associated TileDecoder, or null. */ public static Raster decode(OperationRegistry registry, String formatName, InputStream input, TileCodecParameterList param, Point location) throws IOException { TileDecoder decoder = create(registry, formatName, input, param); if (decoder == null) { return null; } return decoder.decode(location); } } jai-core-1.1.4/src/share/classes/javax/media/jai/registry/TileEncoderRegistry.java0000644000175000017500000004277210203035544030012 0ustar mathieumathieu/* * $RCSfile: TileEncoderRegistry.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:49 $ * $State: Exp $ */package javax.media.jai.registry; import java.awt.image.Raster; import java.awt.image.SampleModel; import java.io.IOException; import java.io.OutputStream; import java.util.Iterator; import java.util.List; import javax.media.jai.JAI; import javax.media.jai.OperationRegistry; import javax.media.jai.tilecodec.TileCodecParameterList; import javax.media.jai.tilecodec.TileEncoder; import javax.media.jai.tilecodec.TileEncoderFactory; /** * Utility class to provide type-safe interaction with the * OperationRegistry for TileEncoderFactory objects. * * If the OperationRegistry specified as an argument to the * methods in this class is null, then JAI.getOperationRegistry() * will be used. * * @since JAI 1.1 */ public final class TileEncoderRegistry { private static final String MODE_NAME = TileEncoderRegistryMode.MODE_NAME; /** * Registers the given TileEncoderFactory with the given * OperationRegistry under the given formatName and * productName. * * @param registry The OperationRegistry to register the * TileEncoderFactory with. * @param formatName The formatName to register the * TileEncoderFactory under. * @param productName The productName to register the * TileEncoderFactory under. * @param tef The TileEncoderFactory to register. * * @throws IllegalArgumentException if formatName is null. * @throws IllegalArgumentException if productName is null. * @throws IllegalArgumentException if tef is null. * @throws IllegalArgumentException if there is no * TileCodecDescriptor registered against the * given formatName. */ public static void register(OperationRegistry registry, String formatName, String productName, TileEncoderFactory tef) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); registry.registerFactory(MODE_NAME, formatName, productName, tef); } /** * Unregisters the given TileEncoderFactory previously * registered under the given formatName and productName in the given * OperationRegistry. * * @param registry The OperationRegistry to unregister the * TileEncoderFactory from. * @param formatName The formatName to unregister the * TileEncoderFactory from. * @param productName The productName to unregister the * TileEncoderFactory from. * @param tef The TileEncoderFactory to unregister. * * @throws IllegalArgumentException if formatName is null. * @throws IllegalArgumentException if productName is null. * @throws IllegalArgumentException if tef is null. * @throws IllegalArgumentException if there is no * TileCodecDescriptor registered against the * given formatName. * @throws IllegalArgumentException if the tef was not previously * registered against the given formatName and productName. */ public static void unregister(OperationRegistry registry, String formatName, String productName, TileEncoderFactory tef) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); registry.unregisterFactory(MODE_NAME, formatName, productName, tef); } /** * Sets a preference between the given two TileEncoderFactory * objects in the given OperationRegistry under the given * formatName and productName. * * @param registry The OperationRegistry to set * preferences on. * @param formatName The formatName of the two * TileEncoderFactorys. * @param productName The productName of the two * TileEncoderFactorys. * @param preferredTEF The preferred TileEncoderFactory. * @param otherTEF The other TileEncoderFactory. * * @throws IllegalArgumentException if formatName is null. * @throws IllegalArgumentException if productName is null. * @throws IllegalArgumentException if preferredTEF is null. * @throws IllegalArgumentException if otherTEF is null. * @throws IllegalArgumentException if there is no * TileCodecDescriptor registered against the * given formatName. * @throws IllegalArgumentException if either of the two tef's * was not previously registered against formatName and productName. */ public static void setPreference(OperationRegistry registry, String formatName, String productName, TileEncoderFactory preferredTEF, TileEncoderFactory otherTEF) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); registry.setFactoryPreference(MODE_NAME, formatName, productName, preferredTEF, otherTEF); } /** * Unsets a preference previously set amongst the given two * TileEncoderFactory objects in the given * OperationRegistry under the given formatName and productName. * * @param registry The OperationRegistry to unset * preferences on. * @param formanName The formatName of the two * TileEncoderFactorys. * @param productName The productName of the two * TileEncoderFactorys. * @param preferredTEF The preferred TileEncoderFactory. * @param otherTEF The other TileEncoderFactory. * * @throws IllegalArgumentException if formatName is null. * @throws IllegalArgumentException if productName is null. * @throws IllegalArgumentException if preferredTEF is null. * @throws IllegalArgumentException if otherTEF is null. * @throws IllegalArgumentException if there is no * TileCodecDescriptor registered against the * given formatName. * @throws IllegalArgumentException if either of the two tef's * was not previously registered against formatName and productName. */ public static void unsetPreference(OperationRegistry registry, String formatName, String productName, TileEncoderFactory preferredTEF, TileEncoderFactory otherTEF) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); registry.unsetFactoryPreference(MODE_NAME, formatName, productName, preferredTEF, otherTEF); } /** * Clears all preferences set for registered TileEncoderFactorys * under the given formatName and productName in the given * OperationRegistry. * * @param registry The OperationRegistry to clear * preferences from. * @param formatName The format name to clear preferences under. * @param productName The productName to clear preferences under. * * @throws IllegalArgumentException if formatName is null. * @throws IllegalArgumentException if productName is null. * @throws IllegalArgumentException if there is no * TileCodecDescriptor registered against the * given formatName. */ public static void clearPreferences(OperationRegistry registry, String formatName, String productName) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); registry.clearFactoryPreferences(MODE_NAME, formatName, productName); } /** * Returns a List of the TileEncoderFactorys registered * in the given OperationRegistry under the given * formatName and productName, in an ordering that satisfies * all of the pairwise preferences that have been set. Returns * null if cycles exist. * * @param registry The OperationRegistry to clear * preferences from. * @param formatName The format name to clear preferences under. * @param productName The productName to clear preferences under. * * @throws IllegalArgumentException if formatName is null. * @throws IllegalArgumentException if productName is null. * @throws IllegalArgumentException if there is no * TileCodecDescriptor registered against the * given formatName. */ public static List getOrderedList(OperationRegistry registry, String formatName, String productName) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); return registry.getOrderedFactoryList(MODE_NAME, formatName, productName); } /** * Returns an Iterator over all * TileEncoderFactory objects registered under the * given format name over all products. The order of the * TileEncoderFactory objects in the iteration will * be according to the pairwise preferences among products and * TileEncoderFactory objects within a product. The * remove() method of the Iterator * may not be implemented. * * @param registry The OperationRegistry to use. * @param formatName The format name. * * @return an Iterator over TileEncoderFactory * objects. * * @throws IllegalArgumentException if formatName is null * @throws IllegalArgumentException if there is no * TileCodecDescriptor registered against * the formatName. */ public static Iterator getIterator(OperationRegistry registry, String formatName) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); return registry.getFactoryIterator(MODE_NAME, formatName); } /** * Returns the the most preferred TileEncoderFactory * object registered against the given format name. This * method will return the first TileEncoderFactory that * would be encountered by the Iterator returned by the * getIterator() method. * * @param registry The OperationRegistry to use. * If this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param formatName The format name as a String * * @return a registered TileEncoderFactory object * * @throws IllegalArgumentException if formatName is null. * @throws IllegalArgumentException if there is no * TileCodecDescriptor registered against the formatName */ public static TileEncoderFactory get(OperationRegistry registry, String formatName) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); return (TileEncoderFactory)registry.getFactory(MODE_NAME, formatName); } /** * Creates a TileEncoder for the specified format that is * capable of handling the supplied arguments. * *

    The preferences set amongst the TileEncoderFactory * objects registered with the OperationRegistry are used * to select the most prefered TileEncoderFactory whose * createEncoder() method returns a non-null value. * *

    Since this class is a simple type-safe wrapper around * OperationRegistry's type-unsafe methods, no additional * argument validation is performed in this method. Thus errors/exceptions * may occur if incorrect values are provided for the input arguments. * *

    Exceptions thrown by the TileEncoderFactorys used to * create the TileEncoder will be caught by this method * and will not be propagated. * * @param registry The OperationRegistry to use to create * the TileEncoder. * @param formatName The format for which the TileEncoder is * to be created. * @param output The OutputStream to write encoded data to. * @param paramList The object containing the tile encoding parameters. * @param sampleModel The SampleModel of the * Raster to be encoded. * * @throws IllegalArgumentException if formatName is null. * @throws IllegalArgumentException if there is no * TileCodecDescriptor registered against the * given formatName. */ public static TileEncoder create(OperationRegistry registry, String formatName, OutputStream output, TileCodecParameterList paramList, SampleModel sampleModel) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); Object args[] = {output, paramList, sampleModel}; return (TileEncoder)registry.invokeFactory(MODE_NAME, formatName, args); } /** * Encodes the given Raster using the given formatName and * TileCodecParameterList and writes the encoded data to the * specified OutputStream. * The TileEncoder which performs the encoding is the * one created from the most prefered TileEncoderFactory * whose create method returns a non-null result. If * there are no TileEncoder objects that can encode * the specified Raster according to the encoding * parameters supplied, nothing will be written to the specified * OutputStream. * *

    If the specified TileCodecParameterList is null, the * default TileCodecParameterList retrieved by the specific * TileEncoder.getDefaultParameters() method for the * "tileEncoder" registry mode will be used. * *

    If multiple tiles are to be encoded to the same * OutputStream in the same format using the same * TileCodecParameterList, it is advisable to create a * TileEncoder object and use the encode() method * on this encoder to encode each tile, thus creating and using only a * single TileEncoder object. The encode() * method on TileEncoderRegistry creates a new * TileEncoder object each time it is called. * *

    Since this class is a simple type-safe wrapper around * OperationRegistry's type-unsafe methods, no additional * argument validation is performed in this method. Thus errors/exceptions * may occur if incorrect values are provided for the input arguments. * *

    Exceptions thrown by the TileEncoderFactorys used to * create the TileEncoder will be caught by this method * and will not be propagated. * * @param registry The OperationRegistry to use to create * the TileEncoder. * @param formatName The name of the format to encode the data in. * @param raster The Raster to be encoded. * @param output The OutputStream to write the encoded * data to. * @param param The TileCodecParameterList to be used. * @throws IllegalArgumentException if formatName is null. * @throws IOException if an input/output error occurs during the encoding. * @throws IllegalArgumentException if there is no * TileCodecDescriptor registered against the * given formatName. * * @return The associated TileEncoder, or null. */ public static void encode(OperationRegistry registry, String formatName, Raster raster, OutputStream output, TileCodecParameterList param) throws IOException { TileEncoder encoder = create(registry, formatName, output, param, raster.getSampleModel()); if (encoder != null) encoder.encode(raster); } } jai-core-1.1.4/src/share/classes/javax/media/jai/registry/RemoteRenderableRegistryMode.java0000644000175000017500000000430110203035544031623 0ustar mathieumathieu/* * $RCSfile: RemoteRenderableRegistryMode.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:48 $ * $State: Exp $ */package javax.media.jai.registry; import java.lang.reflect.Method; import javax.media.jai.JAI; import javax.media.jai.RegistryMode; import javax.media.jai.util.ImagingListener; import javax.media.jai.remote.RemoteDescriptor; /** * A class which provides information about the "remoteRenderable" registry * mode. * * @since JAI 1.1 */ public class RemoteRenderableRegistryMode extends RegistryMode { public static final String MODE_NAME = "remoteRenderable"; // The Method used to "create" objects from this factory. private static Method factoryMethod = null; private static Method getThisFactoryMethod() { if (factoryMethod != null) return factoryMethod; // The factory Class that this registry mode represents. Class factoryClass = javax.media.jai.remote.RemoteCRIF.class; try { Class[] paramTypes = new Class[] {java.lang.String.class, java.lang.String.class, java.awt.image.renderable.RenderContext.class, java.awt.image.renderable.ParameterBlock.class}; factoryMethod = factoryClass.getMethod("create", paramTypes); } catch (NoSuchMethodException e) { ImagingListener listener = JAI.getDefaultInstance().getImagingListener(); String message = JaiI18N.getString("RegistryMode0") + " " + factoryClass.getName() + "."; listener.errorOccurred(message, e, RemoteRenderableRegistryMode.class, false); // e.printStackTrace(); } return factoryMethod; } /** * Creates a RemoteRenderableRegistryMode for describing * the "remoteRenderable" registry mode. */ public RemoteRenderableRegistryMode() { super(MODE_NAME, RemoteDescriptor.class, getThisFactoryMethod().getReturnType(), getThisFactoryMethod(), false, // arePreferencesSupported false); // arePropertiesSupported) } } jai-core-1.1.4/src/share/classes/javax/media/jai/registry/TileEncoderRegistryMode.java0000644000175000017500000000445010203035544030606 0ustar mathieumathieu/* * $RCSfile: TileEncoderRegistryMode.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:50 $ * $State: Exp $ */package javax.media.jai.registry; import java.lang.reflect.Method; import javax.media.jai.JAI; import javax.media.jai.RegistryMode; import javax.media.jai.tilecodec.TileCodecDescriptor; import javax.media.jai.tilecodec.TileCodecParameterList; import javax.media.jai.tilecodec.TileEncoderFactory; import javax.media.jai.util.ImagingListener; /** * A class which provides information about the "tileEncoder" registry * mode. * * @since JAI 1.1 */ public class TileEncoderRegistryMode extends RegistryMode { public static final String MODE_NAME = "tileEncoder"; // Method to return the factory method for the "tileDecoder" mode. // The Method used to "create" objects from this factory. private static Method factoryMethod = null; private static Method getThisFactoryMethod() { if (factoryMethod != null) return factoryMethod; // The factory Class that this registry mode represents. Class factoryClass = TileEncoderFactory.class; try { Class[] paramTypes = new Class[] {java.io.OutputStream.class, TileCodecParameterList.class, java.awt.image.SampleModel.class}; factoryMethod = factoryClass.getMethod("createEncoder", paramTypes); } catch (NoSuchMethodException e) { ImagingListener listener = JAI.getDefaultInstance().getImagingListener(); String message = JaiI18N.getString("RegistryMode0") + " " + factoryClass.getName() + "."; listener.errorOccurred(message, e, TileEncoderRegistryMode.class, false); // e.printStackTrace(); } return factoryMethod; } /** * Creates a TileEncoderRegistryMode for describing * the "tileEncoder" registry mode. */ public TileEncoderRegistryMode() { super(MODE_NAME, TileCodecDescriptor.class, getThisFactoryMethod().getReturnType(), getThisFactoryMethod(), // default factory method true, // arePreferencesSupported false); // arePropertiesSupported, } } jai-core-1.1.4/src/share/classes/javax/media/jai/registry/RIFRegistry.java0000644000175000017500000003575110203035544026234 0ustar mathieumathieu/* * $RCSfile: RIFRegistry.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:48 $ * $State: Exp $ */ package javax.media.jai.registry; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import java.util.Iterator; import java.util.List; import java.util.Vector; import javax.media.jai.JAI; import javax.media.jai.OperationNode; import javax.media.jai.OperationRegistry; import javax.media.jai.PropertySource; import javax.media.jai.RenderedOp; /** * Utility class to provide type-safe interaction with the * OperationRegistry for RenderedImageFactory * objects. * * If the OperationRegistry is null, then * JAI.getDefaultInstance().getOperationRegistry() will be used. * * @since JAI 1.1 */ public final class RIFRegistry { private static final String MODE_NAME = RenderedRegistryMode.MODE_NAME; /** * Register a RIF with a particular product and operation * against a specified mode. This is JAI 1.0.x equivalent * of registry.registerRIF(...) * * @param registry the OperationRegistry to register with. * if this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param operationName the operation name as a String * @param productName the product name as a String * @param rif the RenderedImageFactory to be registered * * @throws IllegalArgumentException if operationName, productName, * or rif is null * @throws IllegalArgumentException if there is no * OperationDescriptor registered against * the operationName */ public static void register(OperationRegistry registry, String operationName, String productName, RenderedImageFactory rif) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); registry.registerFactory(MODE_NAME, operationName, productName, rif); } /** * Unregister a RIF previously registered with a product * and operation against the specified mode. * * @param registry the OperationRegistry to unregister from. * if this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param operationName the operation name as a String * @param productName the product name as a String * @param rif the RenderedImageFactory to be unregistered * * @throws IllegalArgumentException if operationName, productName, * or rif is null * @throws IllegalArgumentException if there is no * OperationDescriptor registered against * the operationName * @throws IllegalArgumentException if the rif was not previously * registered against operationName and productName */ public static void unregister(OperationRegistry registry, String operationName, String productName, RenderedImageFactory rif) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); registry.unregisterFactory(MODE_NAME, operationName, productName, rif); } /** * Sets a preference between two rifs for a given operation under a * specified product. * * @param registry the OperationRegistry to use. * if this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param operationName the operation name as a String * @param productName the product name as a String * @param preferredRIF the preferred rif * @param otherRIF the other rif * * @throws IllegalArgumentException if operationName, productName, * preferredRIF or otherRIF is null * @throws IllegalArgumentException if there is no * OperationDescriptor registered against * the operationName * @throws IllegalArgumentException if either of the rifs * were not previously registered against * operationName and productName */ public static void setPreference(OperationRegistry registry, String operationName, String productName, RenderedImageFactory preferredRIF, RenderedImageFactory otherRIF) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); registry.setFactoryPreference(MODE_NAME, operationName, productName, preferredRIF, otherRIF); } /** * Unsets a preference between two rifs for a given operation under * a specified product. * * @param registry the OperationRegistry to use. * if this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param operationName the operation name as a String * @param productName the product name as a String * @param preferredRIF the factory object formerly preferred * @param otherRIF the other factory object * * @throws IllegalArgumentException if operationName, productName, * preferredRIF or otherRIF is null * @throws IllegalArgumentException if there is no * OperationDescriptor registered against * the operationName * @throws IllegalArgumentException if either of the rifs * were not previously registered against * operationName and productName */ public static void unsetPreference(OperationRegistry registry, String operationName, String productName, RenderedImageFactory preferredRIF, RenderedImageFactory otherRIF) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); registry.unsetFactoryPreference(MODE_NAME, operationName, productName, preferredRIF, otherRIF); } /** * Removes all preferences between RIFs within a product registered * under a particular OperationDescriptor. * * @param registry the OperationRegistry to use. * if this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param operationName the operation name as a String * @param productName the product name as a String * * @throws IllegalArgumentException if operationName or productName * is null * @throws IllegalArgumentException if there is no * OperationDescriptor registered against * the operationName */ public static void clearPreferences(OperationRegistry registry, String operationName, String productName) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); registry.clearFactoryPreferences(MODE_NAME, operationName, productName); } /** * Returns a list of the RIFs of a product registered under a * particular OperationDescriptor, in an ordering * that satisfies all of the pairwise preferences that have * been set. Returns null if cycles exist. Returns * null, if the product does not exist under this * operationName. * * @param registry the OperationRegistry to use. * if this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param operationName the operation name as a String * @param productName the product name as a String * * @return an ordered List of RIFs * * @throws IllegalArgumentException if operationName or productName * is null * @throws IllegalArgumentException if there is no * OperationDescriptor registered against * the operationName */ public static List getOrderedList(OperationRegistry registry, String operationName, String productName) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); return registry.getOrderedFactoryList(MODE_NAME, operationName, productName); } /** * Returns an Iterator over all * RenderedImageFactory objects registered under the * operation name over all products. The order of objects in * the iteration will be according to the pairwise preferences * among products and image factories within a product. The * remove() method of the Iterator * may not be implemented. * * @param registry the OperationRegistry to use. * if this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param operationName the operation name as a String * * @return an Iterator over RenderedImageFactory objects * * @throws IllegalArgumentException if operationName is null * @throws IllegalArgumentException if there is no * OperationDescriptor registered against * the operationName * * @since JAI 1.1 */ public static Iterator getIterator(OperationRegistry registry, String operationName) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); return registry.getFactoryIterator(MODE_NAME, operationName); } /** * Returns the the most preferred RenderedImageFactory * object registered against the operation name. This * method will return the first object that would be * encountered by the Iterator returned by the * getIterator() method. * * @param registry the OperationRegistry to use. * if this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param operationName the operation name as a String * * @return a registered RenderedImageFactory object * * @throws IllegalArgumentException if operationName is null * @throws IllegalArgumentException if there is no * OperationDescriptor registered against * the operationName */ public static RenderedImageFactory get(OperationRegistry registry, String operationName) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); return (RenderedImageFactory) registry.getFactory(MODE_NAME, operationName); } /** * Constructs a RenderedImage (usually a * RenderedOp) representing the results of applying * a given operation to a particular ParameterBlock and rendering * hints. The registry is used to determine the RIF to be used to * instantiate the operation. * *

    If none of the RIFs registered with this * OperationRegistry returns a non-null value, null is * returned. Exceptions thrown by the RIFs will be caught by this * method and will not be propagated. * * @param registry the OperationRegistry to use. * if this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param operationName the operation name as a String * @param paramBlock the operation's ParameterBlock. * @param renderHints a RenderingHints object * containing rendering hints. * * @throws IllegalArgumentException if operationName is null * @throws IllegalArgumentException if there is no * OperationDescriptor registered against * the operationName */ public static RenderedImage create(OperationRegistry registry, String operationName, ParameterBlock paramBlock, RenderingHints renderHints) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); Object args[] = { paramBlock, renderHints }; return (RenderedImage) registry.invokeFactory(MODE_NAME, operationName, args); } /** * Constructs and returns a PropertySource suitable for * use by a given RenderedOp. The * PropertySource includes properties copied from prior * nodes as well as those generated at the node itself. Additionally, * property suppression is taken into account. The actual * implementation of getPropertySource() may make use * of deferred execution and caching. * * @param op the RenderedOp requesting its * PropertySource. * * @throws IllegalArgumentException if op is null */ public static PropertySource getPropertySource(RenderedOp op) { if (op == null) throw new IllegalArgumentException("op - " + JaiI18N.getString("Generic0")); return op.getRegistry().getPropertySource((OperationNode)op); } } jai-core-1.1.4/src/share/classes/javax/media/jai/registry/RCIFRegistry.java0000644000175000017500000001576410203035544026341 0ustar mathieumathieu/* * $RCSfile: RCIFRegistry.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:48 $ * $State: Exp $ */ package javax.media.jai.registry; import java.awt.image.renderable.ParameterBlock; import java.util.Vector; import javax.media.jai.CollectionImage; import javax.media.jai.CollectionOp; import javax.media.jai.JAI; import javax.media.jai.OperationNode; import javax.media.jai.OperationRegistry; import javax.media.jai.PropertySource; import javax.media.jai.RenderableCollectionImageFactory; /** * Utility class to provide type-safe interaction * with the OperationRegistry for * RenderableCollectionImageFactory objects. * * If the OperationRegistry is null, then * JAI.getDefaultInstance().getOperationRegistry() will be used. * * @see CollectionImage * @since JAI 1.1 */ public final class RCIFRegistry { private static final String MODE_NAME = RenderableCollectionRegistryMode.MODE_NAME; /** * Register a RCIF with a particular operation against a specified * mode. * * @param registry the OperationRegistry to register with. * if this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param operationName the operation name as a String * @param rcif the RenderableCollectionImageFactory to be registered * * @throws IllegalArgumentException if operationName or rcif is * null * @throws IllegalArgumentException if there is no * OperationDescriptor registered against * the operationName */ public static void register(OperationRegistry registry, String operationName, RenderableCollectionImageFactory rcif) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); registry.registerFactory(MODE_NAME, operationName, null, rcif); } /** * Unregister a RCIF previously registered with a operation against * the specified mode. * * @param registry the OperationRegistry to unregister from. * if this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param operationName the operation name as a String * @param rcif the RenderableCollectionImageFactory to be unregistered * * @throws IllegalArgumentException if operationName or rcif is * null * @throws IllegalArgumentException if there is no * OperationDescriptor registered against * the operationName * @throws IllegalArgumentException if the rcif was not previously * registered against operationName */ public static void unregister(OperationRegistry registry, String operationName, RenderableCollectionImageFactory rcif) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); registry.unregisterFactory(MODE_NAME, operationName, null, rcif); } /** * Returns the RenderableCollectionImageFactory object * registered against the operation name. * * @param registry the OperationRegistry to use. * if this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param operationName the operation name as a String * * @return a registered RenderableCollectionImageFactory object * * @throws IllegalArgumentException if operationName is null * @throws IllegalArgumentException if there is no * OperationDescriptor registered against * the operationName */ public static RenderableCollectionImageFactory get( OperationRegistry registry, String operationName) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); return (RenderableCollectionImageFactory) registry.getFactory(MODE_NAME, operationName); } /** * Creates a renderable CollectionImage given * a ParameterBlock containing the operation's sources and * parameters. The registry is used to determine the RCIF to be * used to instantiate the operation. * * @param registry the OperationRegistry to use. * if this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param operationName the operation name as a String * @param paramBlock the operation's ParameterBlock. * * @throws IllegalArgumentException if operationName is null * @throws IllegalArgumentException if there is no * OperationDescriptor registered against * the operationName */ public static CollectionImage create(OperationRegistry registry, String operationName, ParameterBlock paramBlock) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); Object args[] = { paramBlock }; return (CollectionImage) registry.invokeFactory(MODE_NAME, operationName, args); } /** * Constructs and returns a PropertySource suitable for * use by a given CollectionOp. The * PropertySource includes properties copied from prior * nodes as well as those generated at the node itself. Additionally, * property suppression is taken into account. The actual implementation * of getPropertySource() may make use of deferred * execution and caching. * * @param op the CollectionOp requesting its * PropertySource. * * @throws IllegalArgumentException if op is null * @throws IllegalArgumentException if op.isRenderable() * returns false */ public static PropertySource getPropertySource(CollectionOp op) { if (op == null) throw new IllegalArgumentException("op - " + JaiI18N.getString("Generic0")); if (!op.isRenderable()) throw new IllegalArgumentException("op - " + JaiI18N.getString("CIFRegistry1")); return op.getRegistry().getPropertySource((OperationNode)op); } } jai-core-1.1.4/src/share/classes/javax/media/jai/registry/CIFRegistry.java0000644000175000017500000003637510203035544026220 0ustar mathieumathieu/* * $RCSfile: CIFRegistry.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:47 $ * $State: Exp $ */ package javax.media.jai.registry; import java.awt.RenderingHints; import java.awt.image.renderable.ParameterBlock; import java.util.Iterator; import java.util.List; import java.util.Vector; import javax.media.jai.CollectionImage; import javax.media.jai.CollectionImageFactory; import javax.media.jai.CollectionOp; import javax.media.jai.JAI; import javax.media.jai.OperationNode; import javax.media.jai.OperationRegistry; import javax.media.jai.PropertySource; /** * Utility class to provide type-safe interaction with the * OperationRegistry for CollectionImageFactory * objects. * * If the OperationRegistry is null, then * JAI.getDefaultInstance().getOperationRegistry() will be used. * * @since JAI 1.1 */ public final class CIFRegistry { private static final String MODE_NAME = CollectionRegistryMode.MODE_NAME; /** * Register a CIF with a particular product and operation * against a specified mode. This is JAI 1.0.x equivalent * of registry.registerCIF(...) * * @param registry the OperationRegistry to register with. * if this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param operationName the operation name as a String * @param productName the product name as a String * @param cif the CollectionImageFactory to be registered * * @throws IllegalArgumentException if operationName, productName, * or cif is null * @throws IllegalArgumentException if there is no * OperationDescriptor registered against * the operationName */ public static void register(OperationRegistry registry, String operationName, String productName, CollectionImageFactory cif) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); registry.registerFactory(MODE_NAME, operationName, productName, cif); } /** * Unregister a CIF previously registered with a product * and operation against the specified mode. * * @param registry the OperationRegistry to unregister from. * if this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param operationName the operation name as a String * @param productName the product name as a String * @param cif the CollectionImageFactory to be unregistered * * @throws IllegalArgumentException if operationName, productName, * or cif is null * @throws IllegalArgumentException if there is no * OperationDescriptor registered against * the operationName * @throws IllegalArgumentException if the cif was not previously * registered against operationName and productName */ public static void unregister(OperationRegistry registry, String operationName, String productName, CollectionImageFactory cif) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); registry.unregisterFactory(MODE_NAME, operationName, productName, cif); } /** * Sets a preference between two cifs for a given operation under a * specified product. * * @param registry the OperationRegistry to use. * if this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param operationName the operation name as a String * @param productName the product name as a String * @param preferredCIF the preferred cif * @param otherCIF the other cif * * @throws IllegalArgumentException if operationName, productName, * preferredCIF or otherCIF is null * @throws IllegalArgumentException if there is no * OperationDescriptor registered against * the operationName * @throws IllegalArgumentException if either of the cifs * were not previously registered against * operationName and productName */ public static void setPreference(OperationRegistry registry, String operationName, String productName, CollectionImageFactory preferredCIF, CollectionImageFactory otherCIF) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); registry.setFactoryPreference(MODE_NAME, operationName, productName, preferredCIF, otherCIF); } /** * Unsets a preference between two cifs for a given operation under * a specified product. * * @param registry the OperationRegistry to use. * if this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param operationName the operation name as a String * @param productName the product name as a String * @param preferredCIF the factory object formerly preferred * @param otherCIF the other factory object * * @throws IllegalArgumentException if operationName, productName, * preferredCIF or otherCIF is null * @throws IllegalArgumentException if there is no * OperationDescriptor registered against * the operationName * @throws IllegalArgumentException if either of the cifs * were not previously registered against * operationName and productName */ public static void unsetPreference(OperationRegistry registry, String operationName, String productName, CollectionImageFactory preferredCIF, CollectionImageFactory otherCIF) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); registry.unsetFactoryPreference(MODE_NAME, operationName, productName, preferredCIF, otherCIF); } /** * Removes all preferences between CIFs within a product registered * under a particular OperationDescriptor. * * @param registry the OperationRegistry to use. * if this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param operationName the operation name as a String * @param productName the product name as a String * * @throws IllegalArgumentException if operationName or productName * is null * @throws IllegalArgumentException if there is no * OperationDescriptor registered against * the operationName */ public static void clearPreferences(OperationRegistry registry, String operationName, String productName) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); registry.clearFactoryPreferences(MODE_NAME, operationName, productName); } /** * Returns a list of the CIFs of a product registered under a * particular OperationDescriptor, in an ordering * that satisfies all of the pairwise preferences that have * been set. Returns null if cycles exist. Returns * null, if the product does not exist under this * operationName. * * @param registry the OperationRegistry to use. * if this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param operationName the operation name as a String * @param productName the product name as a String * * @return an ordered List of CIFs * * @throws IllegalArgumentException if operationName or productName * is null * @throws IllegalArgumentException if there is no * OperationDescriptor registered against * the operationName */ public static List getOrderedList(OperationRegistry registry, String operationName, String productName) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); return registry.getOrderedFactoryList(MODE_NAME, operationName, productName); } /** * Returns an Iterator over all * CollectionImageFactory objects registered under the * operation name over all products. The order of objects in * the iteration will be according to the pairwise preferences * among products and image factories within a product. The * remove() method of the Iterator * may not be implemented. * * @param registry the OperationRegistry to use. * if this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param operationName the operation name as a String * * @return an Iterator over CollectionImageFactory objects * * @throws IllegalArgumentException if operationName is null * @throws IllegalArgumentException if there is no * OperationDescriptor registered against * the operationName * * @since JAI 1.1 */ public static Iterator getIterator(OperationRegistry registry, String operationName) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); return registry.getFactoryIterator(MODE_NAME, operationName); } /** * Returns the the most preferred CollectionImageFactory * object registered against the operation name. This * method will return the first object that would be * encountered by the Iterator returned by the * getIterator() method. * * @param registry the OperationRegistry to use. * if this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param operationName the operation name as a String * * @return a registered CollectionImageFactory object * * @throws IllegalArgumentException if operationName is null * @throws IllegalArgumentException if there is no * OperationDescriptor registered against * the operationName */ public static CollectionImageFactory get(OperationRegistry registry, String operationName) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); return (CollectionImageFactory) registry.getFactory(MODE_NAME, operationName); } /** * Constructs a CollectionImageFactory (usually a * CollectionOp) representing the results of applying * a given operation to a particular ParameterBlock and rendering * hints. The registry is used to determine the CIF to be used to * instantiate the operation. * *

    If none of the CIFs registered with this * OperationRegistry returns a non-null value, null is * returned. Exceptions thrown by the CIFs will be caught by this * method and will not be propagated. * * @param registry the OperationRegistry to use. * if this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param operationName the operation name as a String * @param paramBlock the operation's ParameterBlock. * @param renderHints a RenderingHints object * containing rendering hints. * * @throws IllegalArgumentException if operationName is null * @throws IllegalArgumentException if there is no * OperationDescriptor registered against * the operationName */ public static CollectionImage create(OperationRegistry registry, String operationName, ParameterBlock paramBlock, RenderingHints renderHints) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); Object args[] = { paramBlock, renderHints }; return (CollectionImage) registry.invokeFactory(MODE_NAME, operationName, args); } /** * Constructs and returns a PropertySource suitable for * use by a given CollectionOp. The * PropertySource includes properties copied from prior * nodes as well as those generated at the node itself. Additionally, * property suppression is taken into account. The actual implementation * of getPropertySource() may make use of deferred * execution and caching. * * @param op the CollectionOp requesting its * PropertySource. * * @throws IllegalArgumentException if op is null * @throws IllegalArgumentException if op.isRenderable() * returns true */ public static PropertySource getPropertySource(CollectionOp op) { if (op == null) throw new IllegalArgumentException("op - " + JaiI18N.getString("Generic0")); if (op.isRenderable()) throw new IllegalArgumentException("op - " + JaiI18N.getString("CIFRegistry0")); return op.getRegistry().getPropertySource((OperationNode)op); } } jai-core-1.1.4/src/share/classes/javax/media/jai/registry/RenderableCollectionRegistryMode.java0000644000175000017500000000417110203035544032470 0ustar mathieumathieu/* * $RCSfile: RenderableCollectionRegistryMode.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:49 $ * $State: Exp $ */ package javax.media.jai.registry; import java.lang.reflect.Method; import javax.media.jai.JAI; import javax.media.jai.RegistryMode; import javax.media.jai.util.ImagingListener; /** * A class that provides information about the "renderableCollection" registry * (operation) mode. * * @since JAI 1.1 */ public class RenderableCollectionRegistryMode extends RegistryMode { public static final String MODE_NAME = "renderableCollection"; // The Method used to "create" objects from this factory. private static Method factoryMethod = null; private static Method getThisFactoryMethod() { if (factoryMethod != null) return factoryMethod; // The factory Class that this registry mode represents. Class factoryClass = javax.media.jai.RenderableCollectionImageFactory.class; try { Class[] paramTypes = new Class[] {java.awt.image.renderable.ParameterBlock.class}; factoryMethod = factoryClass.getMethod("create", paramTypes); } catch (NoSuchMethodException e) { ImagingListener listener = JAI.getDefaultInstance().getImagingListener(); String message = JaiI18N.getString("RegistryMode0") + " " + factoryClass.getName() + "."; listener.errorOccurred(message, e, RenderableCollectionRegistryMode.class, false); // e.printStackTrace(); } return factoryMethod; } /** * Constructor. A RegistryMode that represents a * ContextualRenderedImageFactory keyed in a case * insensitive fashion by the string "renderable". The "renderable" * mode has no preferences but supports properties. */ public RenderableCollectionRegistryMode() { super(MODE_NAME, javax.media.jai.OperationDescriptor.class, getThisFactoryMethod().getReturnType(), getThisFactoryMethod(), false, true); } } jai-core-1.1.4/src/share/classes/javax/media/jai/registry/JaiI18N.java0000644000175000017500000000075010203035544025155 0ustar mathieumathieu/* * $RCSfile: JaiI18N.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:48 $ * $State: Exp $ */ package javax.media.jai.registry; import com.sun.media.jai.util.PropertyUtil; class JaiI18N { static String packageName = "javax.media.jai.registry"; public static String getString(String key) { return PropertyUtil.getString(packageName, key); } } jai-core-1.1.4/src/share/classes/javax/media/jai/registry/TileDecoderRegistryMode.java0000644000175000017500000000436110203035544030575 0ustar mathieumathieu/* * $RCSfile: TileDecoderRegistryMode.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:49 $ * $State: Exp $ */package javax.media.jai.registry; import java.lang.reflect.Method; import javax.media.jai.JAI; import javax.media.jai.RegistryMode; import javax.media.jai.tilecodec.TileCodecDescriptor; import javax.media.jai.tilecodec.TileCodecParameterList; import javax.media.jai.tilecodec.TileDecoderFactory; import javax.media.jai.util.ImagingListener; /** * A class which provides information about the "tileDecoder" registry * mode. * * @since JAI 1.1 */ public class TileDecoderRegistryMode extends RegistryMode { public static final String MODE_NAME = "tileDecoder"; // Method to return the factory method for the "tileDecoder" mode. // The Method used to "create" objects from this factory. private static Method factoryMethod = null; private static Method getThisFactoryMethod() { if (factoryMethod != null) return factoryMethod; // The factory Class that this registry mode represents. Class factoryClass = TileDecoderFactory.class; try { Class[] paramTypes = new Class[] {java.io.InputStream.class, TileCodecParameterList.class}; factoryMethod = factoryClass.getMethod("createDecoder", paramTypes); } catch (NoSuchMethodException e) { ImagingListener listener = JAI.getDefaultInstance().getImagingListener(); String message = JaiI18N.getString("RegistryMode0") + " " + factoryClass.getName() + "."; listener.errorOccurred(message, e, TileDecoderRegistryMode.class, false); // e.printStackTrace(); } return factoryMethod; } /** * Creates a TileDecoderRegistryMode for describing * the "tileDecoder" registry mode. */ public TileDecoderRegistryMode() { super(MODE_NAME, TileCodecDescriptor.class, getThisFactoryMethod().getReturnType(), getThisFactoryMethod(), // factoryMethod true, // arePreferencesSupported false); // arePropertiesSupported, } } jai-core-1.1.4/src/share/classes/javax/media/jai/registry/RemoteRIFRegistry.java0000644000175000017500000001602610203035544027402 0ustar mathieumathieu/* * $RCSfile: RemoteRIFRegistry.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:48 $ * $State: Exp $ */package javax.media.jai.registry; import java.awt.RenderingHints; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.OperationRegistry; import javax.media.jai.JAI; import javax.media.jai.remote.RemoteRIF; import javax.media.jai.remote.RemoteRenderedImage; /** * Utility class to provide type-safe interaction with the * OperationRegistry for RemoteRIF objects. * *

    If the OperationRegistry specified as an argument to * the methods in this class is null, then * JAI.getOperationRegistry() will be used. * * @since JAI 1.1 */ public final class RemoteRIFRegistry { private static final String MODE_NAME = RemoteRenderedRegistryMode.MODE_NAME; /** * Registers the given RemoteRIF with the given * OperationRegistry under the given protocolName. * * @param registry The OperationRegistry to register * the RemoteRIF with. If this is * null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param protocolName The protocolName to register the * RemoteRIF under. * @param rrif The RemoteRIF to register. * * @throws IllegalArgumentException if protocolName is null. * @throws IllegalArgumentException if rrif is null. * @throws IllegalArgumentException if there is no * RemoteDescriptor registered against the * given protocolName. */ public static void register(OperationRegistry registry, String protocolName, RemoteRIF rrif) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); registry.registerFactory(MODE_NAME, protocolName, null, rrif); } /** * Unregisters the given RemoteRIF previously registered * under the given protocolName in the given * OperationRegistry. * * @param registry The OperationRegistry to unregister * the RemoteRIF from. If this is * null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param protocolName The protocolName to unregister the * RemoteRIF from under. * @param rrif The RemoteRIF to unregister. * * @throws IllegalArgumentException if protocolName is null. * @throws IllegalArgumentException if rrif is null. * @throws IllegalArgumentException if there is no * RemoteDescriptor registered against the * given protocolName. * @throws IllegalArgumentException if the rrif was not previously * registered against protocolName. */ public static void unregister(OperationRegistry registry, String protocolName, RemoteRIF rrif) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); registry.unregisterFactory(MODE_NAME, protocolName, null, rrif); } /** * Returns the RemoteRIF registered under the given * protocol name in the specified OperationRegistry. * * @param registry The OperationRegistry to use. * If this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param protocolName The name of the remote imaging protocol. * * @throws IllegalArgumentException if protocolName is null. * @throws IllegalArgumentException if there is no * RemoteDescriptor registered against the given * protocolName. */ public static RemoteRIF get(OperationRegistry registry, String protocolName) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); return (RemoteRIF)registry.getFactory(MODE_NAME, protocolName); } /** * Constructs a RemoteRenderedImage representing the * results of remotely applying the given operation to the source(s), * and parameters specified in the specified ParameterBlock, * using the specified rendering hints. The registry * is used to determine the RemoteRIF to be used to * instantiate the operation. * *

    Since this class is a simple type-safe wrapper around * OperationRegistry's type-unsafe methods, no additional * argument validation is performed in this method. Thus errors/exceptions * may occur if incorrect values are provided for the input arguments. * If argument validation is desired as part of creating a rendering, * RemoteJAI.create() may be used instead. * *

    Exceptions thrown by the RemoteRIFs used to create * the rendering will be caught by this method and will not be propagated. * * @param registry The OperationRegistry to use to * create the rendering. If this is * null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param protocolName The protocol to be used for remote imaging. * @param serverName The name of the server. * @param operationName The name of the operation to be performed remotely. * @param paramBlock The ParameterBlock specifying the * sources and parameters required for the operation. * @param renderHints A RenderingHints object containing * rendering hints. * * @throws IllegalArgumentException if protocolName is null. * @throws IllegalArgumentException if there is no * RemoteDescriptor registered against the given * protocolName. */ public static RemoteRenderedImage create(OperationRegistry registry, String protocolName, String serverName, String operationName, ParameterBlock paramBlock, RenderingHints renderHints) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); Object args[] = { serverName, operationName, paramBlock, renderHints }; return (RemoteRenderedImage) registry.invokeFactory(MODE_NAME, protocolName, args); } } jai-core-1.1.4/src/share/classes/javax/media/jai/registry/CollectionRegistryMode.java0000644000175000017500000000372410203035544030507 0ustar mathieumathieu/* * $RCSfile: CollectionRegistryMode.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:47 $ * $State: Exp $ */ package javax.media.jai.registry; import java.lang.reflect.Method; import javax.media.jai.JAI; import javax.media.jai.RegistryMode; import javax.media.jai.util.ImagingListener; /** * A class that provides information about the "rendered" registry * (operation) mode. * * @since JAI 1.1 */ public class CollectionRegistryMode extends RegistryMode { public static final String MODE_NAME = "collection"; // The Method used to "create" objects from this factory. private static Method factoryMethod = null; private static Method getThisFactoryMethod() { if (factoryMethod != null) return factoryMethod; // The factory Class that this registry mode represents. Class factoryClass = javax.media.jai.CollectionImageFactory.class; try { Class[] paramTypes = new Class[] {java.awt.image.renderable.ParameterBlock.class, java.awt.RenderingHints.class}; factoryMethod = factoryClass.getMethod("create", paramTypes); } catch (NoSuchMethodException e) { ImagingListener listener = JAI.getDefaultInstance().getImagingListener(); String message = JaiI18N.getString("RegistryMode0") + " " + factoryClass.getName() + "."; listener.errorOccurred(message, e, CollectionRegistryMode.class, false); // e.printStackTrace(); } return factoryMethod; } /** * Constructor. A RegistryMode that represents * a CollectionImageFactory keyed by "collection" */ public CollectionRegistryMode() { super(MODE_NAME, javax.media.jai.OperationDescriptor.class, getThisFactoryMethod().getReturnType(), getThisFactoryMethod(), true, true); } } jai-core-1.1.4/src/share/classes/javax/media/jai/registry/RemoteCRIFRegistry.java0000644000175000017500000001630710203035544027507 0ustar mathieumathieu/* * $RCSfile: RemoteCRIFRegistry.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:48 $ * $State: Exp $ */package javax.media.jai.registry; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderContext; import javax.media.jai.OperationRegistry; import javax.media.jai.JAI; import javax.media.jai.remote.RemoteCRIF; import javax.media.jai.remote.RemoteRenderedImage; /** * Utility class to provide type-safe interaction with the * OperationRegistry for RemoteCRIF objects. * * If the OperationRegistry specified as an argument to the * methods in this class is null, then JAI.getOperationRegistry() * will be used. * * @since JAI 1.1 */ public final class RemoteCRIFRegistry { private static final String MODE_NAME = RemoteRenderableRegistryMode.MODE_NAME; /** * Registers the given RemoteCRIF with the given * OperationRegistry under the given protocolName * * @param registry The OperationRegistry to register the * RemoteCRIF with. If this is * null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param protocolName The protocolName to register the * RemoteCRIF under. * @param rcrif The RemoteCRIF to register. * * @throws IllegalArgumentException if protocolName is null. * @throws IllegalArgumentException if rcrif is null. * @throws IllegalArgumentException if there is no * RemoteDescriptor registered against the * given protocolName. */ public static void register(OperationRegistry registry, String protocolName, RemoteCRIF rcrif) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); registry.registerFactory(MODE_NAME, protocolName, null, rcrif); } /** * Unregisters the given RemoteCRIF previously registered * under the given protocolName in the given * OperationRegistry. * * @param registry The OperationRegistry to unregister the * RemoteCRIF from. If this is * null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param protocolName The protocolName to unregister the * RemoteCRIF from under. * @param rcrif The RemoteCRIF to unregister. * * @throws IllegalArgumentException if protocolName is null. * @throws IllegalArgumentException if rcrif is null. * @throws IllegalArgumentException if there is no * RemoteDescriptor registered against the * given protocolName. * @throws IllegalArgumentException if the rcrif was not previously * registered against protocolName. */ public static void unregister(OperationRegistry registry, String protocolName, RemoteCRIF rcrif) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); registry.unregisterFactory(MODE_NAME, protocolName, null, rcrif); } /** * Returns the RemoteCRIF registered under the given * protocol name in the specified OperationRegistry. * * @param registry The OperationRegistry to use. * If this is null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param protocolName The name of the remote imaging protocol. * * @throws IllegalArgumentException if protocolName is null. * @throws IllegalArgumentException if there is no * RemoteDescriptor registered against the given * protocolName. */ public static RemoteCRIF get(OperationRegistry registry, String protocolName) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); return (RemoteCRIF)registry.getFactory(MODE_NAME, protocolName); } /** * Creates a rendering remotely, given the serverName, protocolName, * name of the operation to be performed, a RenderContext and * a ParameterBlock containing the operation's sources * and parameters. The registry is used to determine the * RemoteCRIF to be used to instantiate the operation. * *

    Since this class is a simple type-safe wrapper around * OperationRegistry's type-unsafe methods, no additional * argument validation is performed in this method. Thus errors/exceptions * may occur if incorrect values are provided for the input arguments. * If argument validation is desired as part of creating a rendering, * RemoteJAI.createRenderable() may be used instead to * create a RemoteRenderableOp which can then be asked for * a rendering. * *

    Exceptions thrown by the RemoteRIFs used to create * the rendering will be caught by this method and will not be propagated. * * @param registry The OperationRegistry to use to * create the rendering. If this is * null, then * JAI.getDefaultInstance().getOperationRegistry() * will be used. * @param protocolName The protocol to be used for remote imaging. * @param serverName The name of the server. * @param operationName The name of the operation to be performed remotely. * @param renderContext A RenderContext specifying the * context in which the rendering should be requested. * @param paramBlock The ParameterBlock specifying the * sources and parameters required for the operation. * * @throws IllegalArgumentException if protocolName is null. * @throws IllegalArgumentException if there is no * RemoteDescriptor registered against the given * protocolName. */ public static RemoteRenderedImage create(OperationRegistry registry, String protocolName, String serverName, String operationName, RenderContext renderContext, ParameterBlock paramBlock) { registry = (registry != null) ? registry : JAI.getDefaultInstance().getOperationRegistry(); Object args[] = { serverName, operationName, renderContext, paramBlock }; return (RemoteRenderedImage) registry.invokeFactory(MODE_NAME, protocolName, args); } } jai-core-1.1.4/src/share/classes/javax/media/jai/PropertyGenerator.java0000644000175000017500000001435210203035544025700 0ustar mathieumathieu/* * $RCSfile: PropertyGenerator.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:16 $ * $State: Exp $ */ package javax.media.jai; import java.awt.image.renderable.ParameterBlock; import java.io.Serializable; /** * An interface through which properties may be computed dynamically * with respect to an environment of pre-existing properties. In the * interest of simplicity and consistency, a PropertyGenerator * is required to be a pure function; that is, if called multiple times * with the same environment it must produce identical results. * *

    The OperationRegistry class allows * PropertyGenerators to be associated with a particular * operation type, and will automatically insert them into imaging chains * as needed. * *

    Properties are treated in a case-insensitive manner. * * @see OperationRegistry * */ public interface PropertyGenerator extends Serializable { /** * Returns an array of Strings naming properties emitted * by this property generator. The Strings may contain * characters of any case. * * @return an array of Strings that may be passed as parameter * names to the getProperty() method. */ String[] getPropertyNames(); /** * Returns the class expected to be returned by a request for * the property with the specified name. If this information * is unavailable, null will be returned indicating * that getProperty(propertyName).getClass() should * be executed instead. A null value might * be returned for example to prevent generating the value of * a deferred property solely to obtain its class. * * @return The Class expected to be return by a * request for the value of this property or null. * @exception IllegalArgumentException if propertyName * is null. * * @since JAI 1.1 */ Class getClass(String propertyName); /** * Determines whether the specified Object will * be recognized by getProperty(String,Object). * * @exception IllegalArgumentException if opNode * is null. * * @since JAI 1.1 */ boolean canGenerateProperties(Object opNode); /** * Computes the value of a property relative to an environment * of pre-existing properties. The case of the supplied * String is ignored. * *

    In the case of an OperationNode in a chain of * operations these properties may be emitted by the sources of the * node in a chain or the parameters of that operation. The information * requisite to compute the requested property must be available via the * supplied OperationNode. It is legal to call * getProperty() on the operation's sources. * * @param name the name of the property, as a String. * @param op the Object from which properties will * be generated. * @return the value of the property, as an Object or the * value java.awt.Image.UndefinedProperty. * @exception IllegalArgumentException if name or * opNode is null. * @exception IllegalArgumentException if opNode is * not an instance of a supported class for this method, i.e., * canGenerateProperties(opNode) returns * false. * * @since JAI 1.1 */ Object getProperty(String name, Object opNode); /** * Computes the value of a property relative to an environment * of pre-existing properties emitted by the sources of * a RenderedOp, and the parameters of that operation. * *

    The operation name, sources, and ParameterBlock * of the RenderedOp being processed may be obtained by * means of the op.getOperationName, * op.getSources(), and op.getParameterBlock() * methods. It is legal to call getProperty() on the * operation's sources. * * @param name the name of the property, as a String. * @param op the RenderedOp representing the operation. * @return the value of the property, as an Object or the * value java.awt.Image.UndefinedProperty. * @exception IllegalArgumentException if name or * op is null. * * @deprecated as of JAI 1.1. Use * getProperty(String,Object) instead. */ Object getProperty(String name, RenderedOp op); /** * Computes the value of a property relative to an environment * of pre-existing properties emitted by the sources of * a RenderableOp, and the parameters of that operation. * *

    The operation name, sources, and ParameterBlock * of the RenderableOp being processed may be obtained by * means of the op.getOperationName, * op.getSources(), and op.getParameterBlock() * methods. It is legal to call getProperty() on the * operation's sources. * * @param name the name of the property, as a String. * @param op the RenderableOp representing the operation. * @return the value of the property, as an Object or the * value java.awt.Image.UndefinedProperty. * @exception IllegalArgumentException if name or * op is null. * * @deprecated as of JAI 1.1. Use * getProperty(String,Object) instead. */ Object getProperty(String name, RenderableOp op); } jai-core-1.1.4/src/share/classes/javax/media/jai/OperationRegistry.java0000644000175000017500000033252310203035544025701 0ustar mathieumathieu/* * $RCSfile: OperationRegistry.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:13 $ * $State: Exp $ */ package javax.media.jai; import com.sun.media.jai.util.PropertyUtil; import com.sun.media.jai.util.Service; import java.awt.RenderingHints; import java.awt.image.renderable.ContextualRenderedImageFactory; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import java.io.BufferedWriter; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.Externalizable; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInput; import java.io.ObjectOutput; import java.io.OutputStream; import java.io.StringWriter; import java.net.URL; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Vector; import javax.media.jai.registry.CIFRegistry; import javax.media.jai.registry.CRIFRegistry; import javax.media.jai.registry.RIFRegistry; import javax.media.jai.registry.CollectionRegistryMode; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; import javax.media.jai.util.CaselessStringKey; import javax.media.jai.util.ImagingException; import javax.media.jai.util.ImagingListener; import com.sun.media.jai.util.ImageUtil; /** * A class responsible for maintaining a registry of various types of * factory objects and preferences among them. The operation registry * hierarchy looks as follows * *

     *
     *                                                      |-object1-
     *                                           |-product1-|-object2-
     *                             |-descriptor1-|          |-object3-
     *                             |             |
     *                             |             |          |-object1-
     *                             |             |-product2-|-object2-
     *                     |-mode1-|                        |-object3-
     *                     |       |
     * |-OperationRegistry-|       |                        |-object1-
     *                     |       |             |-product1-|-object2-
     *                     |       |-descriptor2-|          |-object3-
     *                     |                     |
     *                     |                     |          |-object1-
     *                     |                     |-product2-|-object2-
     *                     |                                |-object3-
     *                     |
     *                     |-mode2-|-descriptor1-|-object1--
     *                             |
     *                             |-descriptor2-|-object1--
     * 
    * *

    The OperationRegistry class maps a descriptor name * (for example, an image operation name) into the particular kind of * factory object requested, capable of implementing the functionality * described by the descriptor. The mapping is constructed in several * stages: * *

    At the highest level all objects are registered against some mode. * A mode is specified by a String which must be one * of those returned by RegistryMode.getModeNames(). * Examples of known registry modes include "rendered", "renderable", * "collection", "renderableCollection", "tileEncoder", "tileDecoder", * "remoteRendered", "remoteRenderable", etc. * *

    Each registry mode is associated with a * RegistryElementDescriptor which describes some functionality * to be implemented by factory objects associated with this * descriptor. For example, the "rendered" registry mode is associated * with OperationDescriptor.class and "tileEncoder" * is associated with TileCodecDescriptor.class. * Different registry modes can share the same * RegistryElementDescriptor. For example "rendered", "renderable" * (and other image operation registry modes) are all associated with * OperationDescriptor.class. * *

    If a registry mode supports preferences (for example "rendered", * "tileEncoder" etc.), then the hierarchy of objects registered under * that mode looks like that of "mode1" above. Descriptors are first * registered against all modes that the specific instance supports. Each * factory object that implements the functionality specified by that * descriptor is registered against some product (name) under that * descriptor. Preferences can be set among products under a given * descriptor or among objects under a specific product/descriptor. * *

    The ordering of such factory objects is determined by the order * of the products attached to an OperationDescriptor, * and by the order of the factory objects within each product. The * orders are established by setting pairwise preferences, resulting in * a partial order which is then sorted topologically. The results of * creating a cycle are undefined. * *

    The ordering of factory objects within a product is intended to * allow vendors to create complex "fallback" chains. An example would * be installing a RenderedImageFactory that implements * separable convolution ahead of a RenderedImageFactory * that implements a more general algorithm. * *

    If a registry mode does not support preferences (for example, * "renderable", "remoteRenderable" etc.) then the hierarchy of objects * registered under that mode looks like that of "mode2" above. Only a * single factory object belonging to this mode can be associated with a * given descriptor. If multiple objects are registered under the same * descriptor, the last one registered is retained. * *

    The OperationRegistry has several methods to manage this * hierarchy, which accept a modeName and work with * Objects. The preferred manner of usage is through the type-safe * wrapper class which are specific to each mode (for example * RIFRegistry, CRIFRegistry etc.) * *

    Vendors are encouraged to use unique product names (by means * of the Java programming language convention of reversed Internet * addresses) in order to maximize the likelihood of clean installation. * See The Java Programming Language, §10.1 for a discussion * of this convention in the context of package naming. * *

    Users will, for the most part, only wish to set ordering * preferences on the product level, since the factory object level * orderings will be complex. However, it is possible for a knowledgable * user to insert a specific factory object into an existing product for * tuning purposes. * *

    The OperationRegistry also has the responsibility * of associating a set of PropertyGenerators * with each descriptor. This set will be coalesced * into a PropertySource suitable for * use by the getPropertySource() method. If several * PropertyGenerators associated with a particular * descriptor generate the same property, only the last one to be * registered will have any effect. * *

    The registry handles all names (except class names) in a * case-insensitive but retentive manner. * * *

    Initialization and automatic loading of registry objects. * *

    The user has two options for automatic loading of registry * objects. *

      *
    • For most normal situations the user can create a * "registryFile.jai" with entries for operators and preferences * specific to their packages. This registry file must be put it in * the META-INF directory of the jarfile or classpath. * *
    • For situations where more control over the operation registry is * needed, (for example remove an operator registered by JAI etc.) the * user can implement a concrete sub-class of the * OperationRegistrySpi interface * and register it as service provider (see * OperationRegistrySpi for more details). The updateRegistry * method of such registered service providers will be called * with the default OperationRegistry of JAI * once it has been initialized. *
    * *

    The initialization of the OperationRegistry * of the default instance of JAI happens as follows *

      *
    1. Load the JAI distributed registry file * "META-INF/javax.media.jai.registryFile.jai" (from jai_core.jar) * *
    2. Find and load all "META-INF/registryFile.jai" files found * in the classpath in some arbitrary order. * *
    3. Look for registered service providers of OperationRegistrySpi * listed in all "META-INF/services/javax.media.jai.OperationRegistrySpi" * files found in the classpath and call their updateRegistry * method passing in the default OperationRegistry. The order of these * calls to updateRegistry is arbitrary. *
    * * Note that the user should not make any assumption about the order * of loading WITHIN step 2 or 3. If there is a need for the * updateRegistry method to be called right after the associated * registryFile.jai is read in, the following could be done. * *

    The user could give the registry file a package qualified name * for e.g xxx.yyy.registryFile.jai and put this in the META-INF * directory of the jar file. Then in the concrete class that implements * OperationRegistrySpi * *

     *  void updateRegistry(OperationRegistry or) {
     *      String registryFile = "META-INF/xxx.yyy.registryFile.jai";
     *      InputStream is = ClassLoader.getResourceAsStream(registryFile);
     *
     *      or.updateFromStream(is);
     *
     *      // Make other changes to "or" ...
     *  }
     * 
    * * For information on the format of the registry file, see the * * serialized form of the OperationRegistry. * * @see OperationRegistrySpi * @see RegistryMode * @see RegistryElementDescriptor */ public class OperationRegistry implements Externalizable { /** The JAI packaged registry file */ static String JAI_REGISTRY_FILE = "META-INF/javax.media.jai.registryFile.jai"; /** The user defined registry files that are automatically loaded */ static String USR_REGISTRY_FILE = "META-INF/registryFile.jai"; /** * A Hashtable of DescritptorCaches * for each registry mode. */ private Hashtable descriptors; /** * A Hashtable of FactoryCaches * for each registry mode. */ private Hashtable factories; /** * Get the FactoryCache associated with a specified * mode. If it does not exist but the mode is a valid registry mode * then silently create one. */ private FactoryCache getFactoryCache(String modeName) { CaselessStringKey key = new CaselessStringKey(modeName); FactoryCache fc = (FactoryCache)factories.get(key); if (fc == null) { if (RegistryMode.getMode(modeName) != null) { factories.put(key, fc = new FactoryCache(modeName)); } else { throw new IllegalArgumentException( JaiI18N.formatMsg("OperationRegistry0", new Object[] {modeName})); } } return fc; } /** * Get the DescriptorCache associated with a specified * mode. If it does not exist but the mode is a valid registry mode * then silently create one. */ private DescriptorCache getDescriptorCache(String modeName) { CaselessStringKey key = new CaselessStringKey(modeName); DescriptorCache dc = (DescriptorCache)descriptors.get(key); if (dc == null) { if (RegistryMode.getMode(modeName) != null) { descriptors.put(key, dc = new DescriptorCache(modeName)); } else { throw new IllegalArgumentException( JaiI18N.formatMsg("OperationRegistry0", new Object[] {modeName})); } } return dc; } /** * Initialize all the internal OperationRegistry fields. * * Note that this is not synchronized. It is the caller's * reponsiblity to make sure that thread safety is maintained. */ private void initialize() { // Create a Hashtable to hold a DescriptorCache for each // known registry mode. descriptors = new Hashtable(); // Create a Hashtable to hold a FactoryCache for each // known registry mode. factories = new Hashtable(); } /** * Default Constructor. The OperationRegistry created is * not thread-safe. Note that none of the automatic loading * of registry files or services happens here. * * @see #getThreadSafeOperationRegistry */ public OperationRegistry() { initialize(); } /** * Creates and returns a new thread-safe version of the * OperationRegistry which uses reader-writer locks to * wrap every method with a read or a write lock as appropriate. * Note that none of the automatic loading of registry files or * services is done on this OperationRegistry. * * @since JAI 1.1 */ public static OperationRegistry getThreadSafeOperationRegistry() { return new ThreadSafeOperationRegistry(); } /** * Creates a new thread-safe OperationRegistry. It * is initialized by first reading in the system distributed * registry file (JAI_REGISTRY_FILE), then the user * installed registry files (USR_REGISTRY_FILE) and * then by calling the updateRegistry() method of all * registered service providers. * * @return a properly initialized thread-safe * OperationRegistry */ static OperationRegistry initializeRegistry() { try { InputStream url = PropertyUtil.getFileFromClasspath(JAI_REGISTRY_FILE); if (url == null) { throw new RuntimeException(JaiI18N.getString("OperationRegistry1")); } OperationRegistry registry = new ThreadSafeOperationRegistry(); if (url != null) RegistryFileParser.loadOperationRegistry(registry, null, url); registry.registerServices(null); return registry; } catch (IOException ioe) { ImagingListener listener = JAI.getDefaultInstance().getImagingListener(); String message = JaiI18N.getString("OperationRegistry2"); listener.errorOccurred(message, new ImagingException(message, ioe), OperationRegistry.class, false); return null; // ioe.printStackTrace(); // throw new RuntimeException( // JaiI18N.getString("OperationRegistry2")); } } /** * Returns a String representation of the registry. * * @return the string representation of this OperationRegistry. */ public String toString() { StringWriter sw = new StringWriter(); try { RegistryFileParser.writeOperationRegistry( this, new BufferedWriter(sw)); return sw.getBuffer().toString(); } catch (Exception e) { return "\n[ERROR!] " + e.getMessage(); } } /** * Writes out the contents of the OperationRegistry to * a stream as specified in the writeExternal method. * For more information see the * serialized form. * * @param out The OutputStream to which the OperationRegistry * state is written. * * @throws IllegalArgumentException if out is null. * * @see #writeExternal */ public void writeToStream(OutputStream out) throws IOException { if (out == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); RegistryFileParser.writeOperationRegistry(this, out); } /** * Initializes the OperationRegistry from an * InputStream. All non-IO exceptions encountered while * parsing the registry files are caught and their error messages are * redirected to System.err. If System.err * is null the error messages will never be seen. * *

    The InputStream passed in will not be closed by * this method, the caller should close the InputStream * when it is no longer needed. * *

    The format of the data from the InputStream is * expected to be the same as that of the writeExternal * method as specified in the * serialized form. * * @param in The InputStream from which to read the data. * * @throws IllegalArgumentException if in is null. * * @see #writeExternal */ public void initializeFromStream(InputStream in) throws IOException { if (in == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); initialize(); updateFromStream(in); } /** * Updates the current OperationRegistry with the * operations specified by the given InputStream. * All non-IO exceptions encountered while parsing the registry * files are caught and their error messages are redirected to * System.err. If System.err is null the * error messages will never be seen. * *

    The InputStream passed in will not be closed by * this method, the caller should close the InputStream * when it is no longer needed. * *

    The format of the data from the InputStream is * expected to be the same as that of the writeExternal * method as specified in the * serialized form. * * @param in The InputStream from which to read the data. * * @throws IllegalArgumentException if in is null. * * @see #writeExternal * * @since JAI 1.1 */ public void updateFromStream(InputStream in) throws IOException { if (in == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); RegistryFileParser.loadOperationRegistry(this, null, in); } /** * Restores the contents of the registry from an ObjectInput which * was previously written using the writeExternal * method. * *

    All non-IO exceptions encountered while parsing the registry * files are caught and their error messages are redirected to * System.err. If System.err is null the * error messages will never be seen. * * @param in An ObjectInput from which to read the data. * * @serialData The format of the data from the ObjectInput * is expected to be the same as that written out by the * writeExternal method. For more information see * serialized form. * The current implementation is backward compatible with the old * JAI 1.0.2 registry file streams. * * @throws IllegalArgumentException if in is null. * * @see #writeExternal */ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { if (in == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); byte barray[] = (byte[])in.readObject(); InputStream s = new ByteArrayInputStream(barray); initializeFromStream(s); } /** * Saves the contents of the registry as described in the * serialized form. * * @param out An ObjectOutput to which to write the data. * * @throws IllegalArgumentException if out is null. * * @serialData The format of the data written to the stream is as * follows. Each line in the stream can be in one of the formats * described below. Space or tab characters seperate keywords in * each line. The comment character is '#' (0x23); * on each line all characters following the first comment character * are ignored. The stream must be encoded in UTF-8. * *

      * *
    1. To register descriptors :

      * * descriptor {descriptor-class-name}

      * odesc {descriptor-class-name} {descriptor-name}

      * *

      The second version above is deprecated and is retained for backward * compatibility with JAI 1.0.2. Descriptors are always registered * against {descriptor-class}.getName(). The {descriptor-name} in the * second version is always ignored.

      * *

    2. To register factory objects under a product against a specific mode :

      * * {registry-mode-name} {factory-class-name} {product-name} {descriptor-name} {local-name}

      * {registry-mode-name} {factory-class-name} {descriptor-name}

      * *

      The first version above is used to register factory objects against * modes that support preferences. The second version is used for those * that do not support preferences. {local-name}, is an arbitrary name that * is unique for a given mode. This is (only) used later on in this file * to set preferences between factory objects. See class comments * for {@link OperationRegistry} for a discussion on product names.

      * *

    3. To set preferences between products for a descriptor under a * specific mode :

      * * prefProduct {registry-mode-name} {descriptor-name} {preferred-product-name} {other-product-name}

      * pref product {descriptor-name} {preferred-product-name} {other-product-name}

      * *

      The second version above is deprecated and is retained for backward * compatibility with JAI 1.0.2. This version is assumed to set * product preferences for the "rendered" mode.

      * *

    4. To set preferences between factory objects for descriptor under a * a specific product and registry mode :

      * * pref {registry-mode-name} {descriptor-name} {product-name} {preferred-factory-local-name} {other-factory-local-name}

      *

    * *

    For example, the stream contents for an "addconst" image operation * descriptor might look like this : * *

         *    descriptor  javax.media.jai.operator.AddConstDescriptor
         *
         *    rendered    com.sun.media.jai.opimage.AddConstCRIF   com.sun.media.jai   addconst   sunaddconstrif
         *    rendered    com.sun.media.jai.mlib.MlibAddConstRIF   com.sun.media.jai   addconst   mlibaddconstrif
         *
         *    renderable  com.sun.media.jai.opimage.AddConstCRIF   addconst
         *
         *    pref        rendered   addconst   com.sun.media.jai   mlibaddconstrif   sunaddconstrif
         * 
    * * The above does the following : *
      *
    • register a descriptor for the "addconst" operator.
    • *
    • registers two rendered image factories.
    • *
    • register one renderable image factory.
    • *
    • prefer the MlibAddConstRIF factory over the AddConstCRIF * factory for the rendered mode.
    • * *

      Note that JAI 1.0.2 will not be able to read the * new version of the registry file streams. */ public void writeExternal(ObjectOutput out) throws IOException { if (out == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); ByteArrayOutputStream bstream = new ByteArrayOutputStream(); writeToStream(bstream); out.writeObject(bstream.toByteArray()); } /********************** NEW JAI 1.1 methods *************************/ /** * Remove a registry mode (including pre-defined JAI modes) from * the OperationRegistry. When a mode is removed, all associated descriptors * are also removed unless associated with another mode. Note * that this does not unregister or remove this mode from * RegistryMode * * Also note that a registry mode need not be explicitly added to * the OperationRegistry. All modes registered under * RegistryMode are automatically recognized by the * OperationRegistry. * * @throws IllegalArgumentException if modeName is null * or if the modeName is not one of the modes returned * RegistryMode.getModes() * * @since JAI 1.1 */ public void removeRegistryMode(String modeName) { if (getDescriptorCache(modeName) != null) descriptors.remove(new CaselessStringKey(modeName)); if (getFactoryCache(modeName) != null) factories.remove(new CaselessStringKey(modeName)); } /** * Get's the list of known registry modes known to the * OperationRegistry. This might not be all * modes listed in RegistryMode.getModeNames(). * * @since JAI 1.1 */ public String[] getRegistryModes() { Enumeration e = descriptors.keys(); int size = descriptors.size(); String names[] = new String[size]; for (int i = 0; i < size; i++) { CaselessStringKey key = (CaselessStringKey)e.nextElement(); names[i] = key.getName(); } return names; } ////////////////// // // Set of methods to register/unregister descriptors // with the operation registry. /** * Register a descriptor against all the registry modes it * supports. The "descriptor" must be an instance of the * RegistryMode.getDescriptorClass() The "descriptor" is keyed on * descriptor.getName(). Only one descriptor can be registered * against a descriptor name for a given mode. * * @param descriptor an instance of a concrete sub-class of * RegistryElementDescriptor * * @throws IllegalArgumentException is descriptor is null * @throws IllegalArgumentException if any of the modes returned * by descriptor.getSupportedModes() is not one * of those returned by RegistryMode.getModes() * @throws IllegalArgumentException if another descriptor with the * same name has already been registered against any of the * modes supported by this descriptor. * * @since JAI 1.1 */ public void registerDescriptor(RegistryElementDescriptor descriptor) { if (descriptor == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); String[] supportedModes = descriptor.getSupportedModes(); String descriptorName = descriptor.getName(); // First make sure that all supported modes are legal registry // modes. for (int i = 0; i < supportedModes.length; i++) { if (RegistryMode.getMode(supportedModes[i]) == null) throw new IllegalArgumentException( JaiI18N.formatMsg("OperationRegistry3", new Object[] {descriptorName, supportedModes[i]})); } // Now register the descriptor against each supported mode. for (int i = 0; i < supportedModes.length; i++) { DescriptorCache dc = getDescriptorCache(supportedModes[i]); dc.addDescriptor(descriptor); } } /** * Unregister a descriptor against all its supported modes from the * operation registry. * * @param descriptor an instance of a concrete sub-class of * RegistryElementDescriptor * * @throws IllegalArgumentException is descriptor is null * @throws IllegalArgumentException if any of the modes returned * by descriptor.getSupportedModes() is not one * of those returned by RegistryMode.getModes() * @throws IllegalArgumentException if any of the * PropertyGenerators associated with the * RegistryElementDescriptor to be unregistered is null. * * @since JAI 1.1 */ public void unregisterDescriptor(RegistryElementDescriptor descriptor) { if (descriptor == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); String descriptorName = descriptor.getName(); String[] supportedModes = descriptor.getSupportedModes(); // First make sure that all supported modes are legal registry // modes. for (int i = 0; i < supportedModes.length; i++) { if (RegistryMode.getMode(supportedModes[i]) == null) throw new IllegalArgumentException( JaiI18N.formatMsg("OperationRegistry3", new Object[] {descriptorName, supportedModes[i]})); } // Now unregister the descriptor against each supported mode. for (int i = 0; i < supportedModes.length; i++) { DescriptorCache dc = getDescriptorCache(supportedModes[i]); dc.removeDescriptor(descriptor); } } /** * Get the RegistryElementDescriptor * corresponding to a descriptorClass * and a descriptorName. For example, * getDescriptor(OperationDescriptor.class, "add") * would get the operation descriptor for the "add" image operation. * Note that different descriptors might have been registered * against each mode associated with the descriptorClass. In this * case this methods will arbitrarily return the first descriptor * it encounters with a matching descriptorName and descriptorClass. * * @param descriptorClass the descriptor Class * @param descriptorName the descriptor name as a String * * @throws IllegalArgumentException if descriptorClass is * null or if the descriptorClass is * not associated with any of the modes returned * RegistryMode.getModes() * @throws IllegalArgumentException if descriptorName is null * * @since JAI 1.1 */ public RegistryElementDescriptor getDescriptor( Class descriptorClass, String descriptorName) { if ((descriptorClass == null) || (descriptorName == null)) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); String supportedModes[] = RegistryMode.getModeNames(descriptorClass); if (supportedModes == null) throw new IllegalArgumentException( JaiI18N.formatMsg("OperationRegistry4", new Object[] {descriptorClass.getName()})); RegistryElementDescriptor red; // Now look for the descriptor in each supported mode. for (int i = 0; i < supportedModes.length; i++) { DescriptorCache dc = getDescriptorCache(supportedModes[i]); if ((red = dc.getDescriptor(descriptorName)) != null) return red; } return null; } /** * Get a List of all RegistryElementDescriptor * corresponding to the descriptorClass. For example, * getDescriptors(OperationDescriptor.class) * would get a list of all image operation descriptors. * * @param descriptorClass the descriptor Class * * @throws IllegalArgumentException if descriptorClass is * null or if the descriptorClass is * not associated with any of the modes returned * RegistryMode.getModes() * * @since JAI 1.1 */ public List getDescriptors(Class descriptorClass) { if (descriptorClass == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); String supportedModes[] = RegistryMode.getModeNames(descriptorClass); if (supportedModes == null) throw new IllegalArgumentException( JaiI18N.formatMsg("OperationRegistry4", new Object[] {descriptorClass.getName()})); List list; HashSet set = new HashSet(); // Now unregister the descriptor against each supported mode. for (int i = 0; i < supportedModes.length; i++) { DescriptorCache dc = getDescriptorCache(supportedModes[i]); if ((list = dc.getDescriptors()) != null) set.addAll(list); } return new ArrayList(set); } /** * Get an array of all the descriptor names * corresponding to the descriptorClass. For example, * getDescriptorNames(OperationDescriptor.class) * would get an array of all image operation descriptor names. * * @param descriptorClass the descriptor Class * * @throws IllegalArgumentException if descriptorClass is * null or if the descriptorClass is * not associated with any of the modes returned * RegistryMode.getModes() * * @since JAI 1.1 */ public String[] getDescriptorNames(Class descriptorClass) { List dlist = getDescriptors(descriptorClass); if (dlist != null) { Iterator diter = dlist.iterator(); String[] names = new String[dlist.size()]; int i = 0; while (diter.hasNext()) { RegistryElementDescriptor red = (RegistryElementDescriptor)diter.next(); names[i++] = red.getName(); } return names; } return null; } /** * Get the RegistryElementDescriptor corresponding to * descriptorName which supports the specified mode. * This is done by matching up the descriptorName * against RegistryElementDescriptor.getName in a * case-insensitive manner. This returns null if there * no RegistryElementDescriptor corresponding to * descriptorName that supports the specified mode. * * @param modeName the registry mode name as a String * @param descriptorName the descriptor name as a String * * @throws IllegalArgumentException if modeName is null * or if the modeName is not one of the modes returned * RegistryMode.getModes() * @throws IllegalArgumentException if descriptorName is null * * @since JAI 1.1 */ public RegistryElementDescriptor getDescriptor(String modeName, String descriptorName) { DescriptorCache dc = getDescriptorCache(modeName); if (dc != null) return dc.getDescriptor(descriptorName); return null; } /** * Get a list of all RegistryElementDescriptors registered * under a given registry mode. * * @param modeName the registry mode name as a String * * @throws IllegalArgumentException if modeName is null * or if the modeName is not one of the modes returned * RegistryMode.getModes() * * @since JAI 1.1 */ public List getDescriptors(String modeName) { DescriptorCache dc = getDescriptorCache(modeName); if (dc != null) return dc.getDescriptors(); return null; } /** * Get an array of all descriptor-names of descriptors registered * under a given registry mode. * * @param modeName the registry mode name as a String * * @throws IllegalArgumentException if modeName is null * or if the modeName is not one of the modes returned * RegistryMode.getModes() * * @since JAI 1.1 */ public String[] getDescriptorNames(String modeName) { DescriptorCache dc = getDescriptorCache(modeName); if (dc != null) return dc.getDescriptorNames(); return null; } ////////////////// // // Set of methods to set/unset/clear product preferences /** * Set the preference between two products for a descriptor * registered under a registry mode. * * @param modeName the registry mode name as a String * @param descriptorName the descriptor name as a String * @param preferredProductName the product to be preferred. * @param otherProductName the other product. * * @throws IllegalArgumentException if any of the arguments * is null * @throws IllegalArgumentException if modeName is not one of * those returned by RegistryMode.getModes() * @throws IllegalArgumentException if the registry mode does not * support preferences * @throws IllegalArgumentException if there is no * RegistryElementDescriptor registered against * the descriptorName under modeName. * @throws IllegalArgumentException if either of the products are * not registered against descriptorName * under productName. * * @since JAI 1.1 */ public void setProductPreference(String modeName, String descriptorName, String preferredProductName, String otherProductName) { DescriptorCache dc = getDescriptorCache(modeName); if (dc != null) dc.setProductPreference(descriptorName, preferredProductName, otherProductName); } /** * Remove the preference between two products for a descriptor * registered under a registry mode. * * @param modeName the registry mode name as a String * @param descriptorName the descriptor name as a String * @param preferredProductName the product formerly preferred. * @param otherProductName the other product. * * @throws IllegalArgumentException if any of the arguments * is null * @throws IllegalArgumentException if modeName is not one of * those returned by RegistryMode.getModes() * @throws IllegalArgumentException if the registry mode does not * support preferences * @throws IllegalArgumentException if there is no * RegistryElementDescriptor registered against * the descriptorName under modeName. * @throws IllegalArgumentException if either of the products are * not registered against descriptorName * under productName. * * @since JAI 1.1 */ public void unsetProductPreference(String modeName, String descriptorName, String preferredProductName, String otherProductName) { DescriptorCache dc = getDescriptorCache(modeName); if (dc != null) dc.unsetProductPreference(descriptorName, preferredProductName, otherProductName); } /** * Remove all the preferences between products for a descriptor * registered under a registry mode. * * @param modeName the registry mode name as a String * @param descriptorName the descriptor name as a String * * @throws IllegalArgumentException if modeName is null * or if the modeName is not one of the modes returned * RegistryMode.getModes() * @throws IllegalArgumentException if descriptorName is null * @throws IllegalArgumentException if the registry mode does not * support preferences * * @since JAI 1.1 */ public void clearProductPreferences(String modeName, String descriptorName) { DescriptorCache dc = getDescriptorCache(modeName); if (dc != null) dc.clearProductPreferences(descriptorName); } /** * Returns a list of the pairwise product preferences * under a particular descriptor registered against a registry mode. * * @param modeName the registry mode name as a String * @param descriptorName the descriptor name as a String * * @return an array of 2-element arrays of Strings. * * @throws IllegalArgumentException if modeName is null * or if the modeName is not one of the modes returned * RegistryMode.getModes() * @throws IllegalArgumentException if descriptorName is null * @throws IllegalArgumentException if the registry mode does not * support preferences * * @since JAI 1.1 */ public String[][] getProductPreferences(String modeName, String descriptorName) { DescriptorCache dc = getDescriptorCache(modeName); if (dc != null) return dc.getProductPreferences(descriptorName); return null; } /** * Returns a list of the products registered under a particular * descriptor in an ordering that satisfies all of the pairwise * preferences that have been set. Returns null if * cycles exist. Returns null if no descriptor has been registered * under this descriptorName, or if no products exist for this * operation. * * @param modeName the registry mode name as a String * @param descriptorName the descriptor name as a String * * @return a Vector of Strings representing product names. * * @throws IllegalArgumentException if modeName is null * or if the modeName is not one of the modes returned * RegistryMode.getModes() * @throws IllegalArgumentException if descriptorName is null * @throws IllegalArgumentException if the registry mode does not * support preferences * * @since JAI 1.1 */ public Vector getOrderedProductList(String modeName, String descriptorName) { DescriptorCache dc = getDescriptorCache(modeName); if (dc != null) return dc.getOrderedProductList(descriptorName); return null; } ////////////////// // // Set of methods to maintain factory objects to register/unregister // & set/unset/clear preferences & get-ordered-lists of factory // objects /** * Get the local name for a factory instance used in the * DescriptorCache (to be used in writing out the registry file). */ String getLocalName(String modeName, Object factoryInstance) { FactoryCache fc = getFactoryCache(modeName); if (fc != null) return fc.getLocalName(factoryInstance); return null; } /** * Register a factory object with a particular product and descriptor * against a specified mode. For modes that do not support preferences * the productName is ignored (can be null) * * @param modeName the registry mode name as a String * @param descriptorName the descriptor name as a String * @param productName the product name as a String * @param factory the object to be registered. * * @throws IllegalArgumentException if any of the arguments * is null (productName can be null * for modes that do not support preferences). * @throws IllegalArgumentException if modeName is not one of * those returned by RegistryMode.getModes() * @throws IllegalArgumentException if there is no * RegistryElementDescriptor registered against * the descriptorName * * @since JAI 1.1 */ public void registerFactory(String modeName, String descriptorName, String productName, Object factory) { DescriptorCache dc = getDescriptorCache(modeName); FactoryCache fc = getFactoryCache(modeName); if (dc.getDescriptor(descriptorName) == null) { throw new IllegalArgumentException( JaiI18N.formatMsg("OperationRegistry5", new Object[] {descriptorName, modeName})); } if (factory == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (dc.arePreferencesSupported) { OperationGraph og = dc.addProduct(descriptorName, productName); if (og == null) { throw new IllegalArgumentException( JaiI18N.formatMsg("OperationRegistry5", new Object[] {descriptorName, modeName})); } og.addOp(new PartialOrderNode( factory, factory.getClass().getName())); } fc.addFactory(descriptorName, productName, factory); } /** * Unregister a factory object previously registered with a product * and descriptor against the specified mode. For modes that do * not support preferences the productName is ignored (can be * null) * * @param modeName the registry mode name as a String * @param descriptorName the descriptor name as a String * @param productName the product name as a String * @param factory the object to be unregistered. * * @throws IllegalArgumentException if any of the arguments * is null (productName can be null * for modes that do not support preferences). * @throws IllegalArgumentException if modeName is not one of * those returned by RegistryMode.getModes() * @throws IllegalArgumentException if there is no * RegistryElementDescriptor registered against * the descriptorName * @throws IllegalArgumentException if the factory object was not previously * registered against descriptorName and productName * * @since JAI 1.1 */ public void unregisterFactory(String modeName, String descriptorName, String productName, Object factory) { DescriptorCache dc = getDescriptorCache(modeName); FactoryCache fc = getFactoryCache(modeName); if (dc.getDescriptor(descriptorName) == null) { throw new IllegalArgumentException( JaiI18N.formatMsg("OperationRegistry5", new Object[] {descriptorName, modeName})); } if (factory == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } fc.removeFactory(descriptorName, productName, factory); if (dc.arePreferencesSupported) { OperationGraph og = dc.lookupProduct(descriptorName, productName); if (og == null) { throw new IllegalArgumentException( JaiI18N.formatMsg("OperationRegistry5", new Object[] {descriptorName, modeName})); } og.removeOp(factory); } } /** * Sets a preference between two factory instances for a given * operation under a specified product. * * @param modeName the registry mode name as a String * @param descriptorName the descriptor name as a String * @param productName the product name as a String * @param preferredOp the preferred factory object * @param otherOp the other factory object * * @throws IllegalArgumentException if any of the arguments * is null * @throws IllegalArgumentException if modeName is not one of * those returned by RegistryMode.getModes() * @throws IllegalArgumentException if there is no * RegistryElementDescriptor registered against * the descriptorName * @throws IllegalArgumentException if either of the factory objects * were not previously registered against * descriptorName and productName * @throws IllegalArgumentException if the registry mode does not * support preferences * * @since JAI 1.1 */ public void setFactoryPreference(String modeName, String descriptorName, String productName, Object preferredOp, Object otherOp) { DescriptorCache dc = getDescriptorCache(modeName); FactoryCache fc = getFactoryCache(modeName); if (dc.getDescriptor(descriptorName) == null) { throw new IllegalArgumentException( JaiI18N.formatMsg("OperationRegistry5", new Object[] {descriptorName, modeName})); } // This should throw an exception if preferences are not // supported. fc.setPreference( descriptorName, productName, preferredOp, otherOp); if (dc.arePreferencesSupported) { OperationGraph og = dc.lookupProduct(descriptorName, productName); if (og == null) { throw new IllegalArgumentException( JaiI18N.formatMsg("OperationRegistry5", new Object[] {descriptorName, modeName})); } og.setPreference(preferredOp, otherOp); } } /** * Unsets a preference between two factory instances for a given * operation under a specified product. * * @param modeName the registry mode name as a String * @param descriptorName the descriptor name as a String * @param productName the product name as a String * @param preferredOp the factory object formerly preferred * @param otherOp the other factory object * * @throws IllegalArgumentException if any of the arguments * is null * @throws IllegalArgumentException if modeName is not one of * those returned by RegistryMode.getModes() * @throws IllegalArgumentException if there is no * RegistryElementDescriptor registered against * the descriptorName * @throws IllegalArgumentException if either of the factory objects * were not previously registered against * descriptorName and productName * @throws IllegalArgumentException if the registry mode does not * support preferences * * @since JAI 1.1 */ public void unsetFactoryPreference(String modeName, String descriptorName, String productName, Object preferredOp, Object otherOp) { DescriptorCache dc = getDescriptorCache(modeName); FactoryCache fc = getFactoryCache(modeName); if (dc.getDescriptor(descriptorName) == null) { throw new IllegalArgumentException( JaiI18N.formatMsg("OperationRegistry5", new Object[] {descriptorName, modeName})); } // This should throw an exception if preferences are not // supported. fc.unsetPreference( descriptorName, productName, preferredOp, otherOp); if (dc.arePreferencesSupported) { OperationGraph og = dc.lookupProduct(descriptorName, productName); if (og == null) { throw new IllegalArgumentException( JaiI18N.formatMsg("OperationRegistry5", new Object[] {descriptorName, modeName})); } og.unsetPreference(preferredOp, otherOp); } } /** * Removes all preferences between instances of a factory * within a product registered under a particular * OperationDescriptor. * * @param modeName the registry mode name as a String * @param descriptorName the descriptor name as a String * @param productName the product name as a String * * @throws IllegalArgumentException if any of the arguments * is null * @throws IllegalArgumentException if modeName is not one of * those returned by RegistryMode.getModes() * @throws IllegalArgumentException if there is no * RegistryElementDescriptor registered against * the descriptorName * * @since JAI 1.1 */ public void clearFactoryPreferences(String modeName, String descriptorName, String productName) { DescriptorCache dc = getDescriptorCache(modeName); FactoryCache fc = getFactoryCache(modeName); if (dc.getDescriptor(descriptorName) == null) { throw new IllegalArgumentException( JaiI18N.formatMsg("OperationRegistry5", new Object[] {descriptorName, modeName})); } Object prefs[][] = fc.getPreferences(descriptorName, productName); if (prefs != null) { OperationGraph og = dc.lookupProduct(descriptorName, productName); if (og == null) { throw new IllegalArgumentException( JaiI18N.formatMsg("OperationRegistry5", new Object[] {descriptorName, modeName})); } for (int i = 0; i < prefs.length; i++) { og.unsetPreference(prefs[i][0], prefs[i][1]); } } fc.clearPreferences(descriptorName, productName); } /** * Get all pairwise preferences between instances of a factory * within a product registered under a particular * OperationDescriptor. * * @param modeName the registry mode name as a String * @param descriptorName the descriptor name as a String * @param productName the product name as a String * * @throws IllegalArgumentException if any of the arguments * is null * @throws IllegalArgumentException if modeName is not one of * those returned by RegistryMode.getModes() * @throws IllegalArgumentException if there is no * RegistryElementDescriptor registered against * the descriptorName * * @since JAI 1.1 */ public Object[][] getFactoryPreferences(String modeName, String descriptorName, String productName) { DescriptorCache dc = getDescriptorCache(modeName); FactoryCache fc = getFactoryCache(modeName); if (dc.getDescriptor(descriptorName) == null) { throw new IllegalArgumentException( JaiI18N.formatMsg("OperationRegistry5", new Object[] {descriptorName, modeName})); } return fc.getPreferences(descriptorName, productName); } /** * Returns a list of the factory instances of a product registered * under a particular OperationDescriptor, in an * ordering that satisfies all of the pairwise preferences that * have been set. Returns null if cycles exist. * Returns null, if the product does not exist under * this descriptorName. * * If the particular registry mode does not support preferences then the * returned List will contain a single factory. * * @param modeName the registry mode name as a String * @param descriptorName the descriptor name as a String * @param productName the product name as a String * * @return an ordered List of factory instances * * @throws IllegalArgumentException if any of the arguments * is null (productName can be null * for modes that do not support preferences). * @throws IllegalArgumentException if modeName is not one of * those returned by RegistryMode.getModes() * @throws IllegalArgumentException if there is no * RegistryElementDescriptor registered against * the descriptorName * * @since JAI 1.1 */ public List getOrderedFactoryList(String modeName, String descriptorName, String productName) { DescriptorCache dc = getDescriptorCache(modeName); FactoryCache fc = getFactoryCache(modeName); if (dc.getDescriptor(descriptorName) == null) { throw new IllegalArgumentException( JaiI18N.formatMsg("OperationRegistry5", new Object[] {descriptorName, modeName})); } if (dc.arePreferencesSupported) { OperationGraph og = dc.lookupProduct(descriptorName, productName); if (og == null) return null; Vector v = og.getOrderedOperationList(); if ((v == null) || (v.size() <= 0)) return null; ArrayList list = new ArrayList(v.size()); for (int i = 0; i < v.size(); i++) { list.add(((PartialOrderNode)v.elementAt(i)).getData()); } return list; } else { return fc.getFactoryList(descriptorName, productName); } } /** * Returns an Iterator over all factory objects * registered with the specified factory and operation names over * all products. The order of objects in the iteration will be * according to the pairwise preferences among products and image * factories within a product. The remove() method * of the Iterator may not be implemented. If the * particular factory does not have preferences then the returned * Iterator will traverse a collection containing the * single factory. * * @param modeName the registry mode name as a String * @param descriptorName the descriptor name as a String * * @return an Iterator over factory objects * * @throws IllegalArgumentException if any of the arguments * is null * @throws IllegalArgumentException if modeName is not one of * those returned by RegistryMode.getModes() * @throws IllegalArgumentException if there is no * RegistryElementDescriptor registered against * the descriptorName * * @since JAI 1.1 */ public Iterator getFactoryIterator(String modeName, String descriptorName) { DescriptorCache dc = getDescriptorCache(modeName); FactoryCache fc = getFactoryCache(modeName); if (dc.getDescriptor(descriptorName) == null) { throw new IllegalArgumentException( JaiI18N.formatMsg("OperationRegistry5", new Object[] {descriptorName, modeName})); } if (dc.arePreferencesSupported) { Vector v = getOrderedProductList(modeName, descriptorName); if ((v == null) || (v.size() <= 0)) return null; ArrayList list = new ArrayList(); List plist; for (int i = 0; i < v.size(); i++) { plist = getOrderedFactoryList(modeName, descriptorName, (String)v.get(i)); if (plist != null) list.addAll(plist); } return list.iterator(); } else { List list = fc.getFactoryList(descriptorName, null); if (list != null) return list.iterator(); } return null; } /** * Returns the factory of the specified type for the named * operation. This method will return the first factory that would * be encountered by the Iterator returned by the * getFactoryIterator() method. * * @param modeName the registry mode name as a String * @param descriptorName the descriptor name as a String * * @return a registered factory object * * @throws IllegalArgumentException if any of the arguments * is null * @throws IllegalArgumentException if modeName is not one of * those returned by RegistryMode.getModes() * @throws IllegalArgumentException if there is no * RegistryElementDescriptor registered against * the descriptorName * * @since JAI 1.1 */ public Object getFactory(String modeName, String descriptorName) { Iterator it = getFactoryIterator(modeName, descriptorName); if ((it != null) && it.hasNext()) return it.next(); return null; } /** * Finds the factory of the specified type for the named operation * and invokes its default factory method with the supplied * parameters. The class of the returned object is that of the * object returned by the factory's object creation method. * * @param modeName the registry mode name as a String * @param descriptorName the descriptor name as a String * * @return an object created by the factory method * * @throws IllegalArgumentException if modeName or descriptorName * is null * @throws IllegalArgumentException if modeName is not one of * those returned by RegistryMode.getModes() * @throws IllegalArgumentException if there is no * RegistryElementDescriptor registered against * the descriptorName * * @since JAI 1.1 */ public Object invokeFactory(String modeName, String descriptorName, Object[] args) { Iterator it = getFactoryIterator(modeName, descriptorName); if (it == null) return null; FactoryCache fc = getFactoryCache(modeName); ImagingListener listener = JAI.getDefaultInstance().getImagingListener(); Exception savedOne = null; while (it.hasNext()) { Object factory = it.next(); Object obj; try { if ((obj = fc.invoke(factory, args)) != null) return obj; savedOne = null; } catch (Exception e) { listener.errorOccurred(JaiI18N.getString("OperationRegistry6")+ " \""+descriptorName+"\"", e, this, false); savedOne = e; // e.printStackTrace(); } } if (savedOne != null) throw new ImagingException(JaiI18N.getString("OperationRegistry7")+ " \""+descriptorName+"\"", savedOne); return null; } ////////////////// // // Property related methods : // If a RegistryElementDescriptor supports properties // (arePropertiesSupported() return true) then a property // environment has to be managed. // // In the next four methods if the mode is null then apply to all modes // that support properties. /** * Adds a PropertyGenerator to the registry, * associating it with a particular descriptor registered against a * registry mode. * * @param modeName the registry mode name as a String * @param descriptorName the descriptor name as a String * @param generator the PropertyGenerator to be added. * * @throws IllegalArgumentException if any of the arguments * is null * @throws IllegalArgumentException if modeName is not one of * those returned by RegistryMode.getModes() * @throws IllegalArgumentException if there is no * RegistryElementDescriptor registered against * the descriptorName * @throws IllegalArgumentException if the specified mode does not * support properties. * * @since JAI 1.1 */ public void addPropertyGenerator(String modeName, String descriptorName, PropertyGenerator generator) { DescriptorCache dc = getDescriptorCache(modeName); if (dc != null) dc.addPropertyGenerator(descriptorName, generator); } /** * Removes a PropertyGenerator from its association * with a particular descriptor/registry-mode in the registry. If * the generator was not associated with the operation, nothing * happens. * * @param modeName the registry mode name as a String * @param descriptorName the descriptor name as a String * @param generator the PropertyGenerator to be removed. * * @throws IllegalArgumentException if any of the arguments * is null * @throws IllegalArgumentException if modeName is not one of * those returned by RegistryMode.getModes() * @throws IllegalArgumentException if there is no * RegistryElementDescriptor registered against * the descriptorName * @throws IllegalArgumentException if the specified mode does not * support properties. * * @since JAI 1.1 */ public void removePropertyGenerator(String modeName, String descriptorName, PropertyGenerator generator) { DescriptorCache dc = getDescriptorCache(modeName); if (dc != null) dc.removePropertyGenerator(descriptorName, generator); } /** * Forces a property to be copied from the specified source by nodes * performing a particular operation. By default, a property is * copied from the first source node that emits it. The result of * specifying an invalid source is undefined. * * @param modeName the registry mode name as a String * @param descriptorName the descriptor name as a String * @param propertyName the name of the property to be copied. * @param sourceIndex the index of the source to copy the property from. * * @throws IllegalArgumentException if any of the String * arguments is null * @throws IllegalArgumentException if modeName is not one of * those returned by RegistryMode.getModes() * @throws IllegalArgumentException if there is no * RegistryElementDescriptor registered against * the descriptorName * @throws IllegalArgumentException if the specified mode does not * support properties. * * @since JAI 1.1 */ public void copyPropertyFromSource(String modeName, String descriptorName, String propertyName, int sourceIndex) { DescriptorCache dc = getDescriptorCache(modeName); if (dc != null) dc.copyPropertyFromSource(descriptorName, propertyName, sourceIndex); } /** * Forces a particular property to be suppressed by nodes * performing a particular operation. By default, properties * are passed through operations unchanged. * * @param modeName the registry mode name as a String * @param descriptorName the descriptor name as a String * @param propertyName the name of the property to be suppressed. * * @throws IllegalArgumentException if any of the arguments * is null * @throws IllegalArgumentException if modeName is not one of * those returned by RegistryMode.getModes() * @throws IllegalArgumentException if there is no * RegistryElementDescriptor registered against * the descriptorName * @throws IllegalArgumentException if the specified mode does not * support properties. * * @since JAI 1.1 */ public void suppressProperty(String modeName, String descriptorName, String propertyName) { DescriptorCache dc = getDescriptorCache(modeName); if (dc != null) dc.suppressProperty(descriptorName, propertyName); } /** * Forces all properties to be suppressed by nodes performing a * particular operation. By default, properties are passed * through operations unchanged. * * @param modeName the registry mode name as a String * @param descriptorName the descriptor name as a String * * @throws IllegalArgumentException if any of the arguments * is null * @throws IllegalArgumentException if modeName is not one of * those returned by RegistryMode.getModes() * @throws IllegalArgumentException if there is no * RegistryElementDescriptor registered against * the descriptorName * @throws IllegalArgumentException if the specified mode does not * support properties. * * @since JAI 1.1 */ public void suppressAllProperties(String modeName, String descriptorName) { DescriptorCache dc = getDescriptorCache(modeName); if (dc != null) dc.suppressAllProperties(descriptorName); } /** * Removes all property associated information for this registry * mode from this OperationRegistry. * * @param modeName the registry mode name as a String * * @throws IllegalArgumentException if modeName is null or is not one of * those returned by RegistryMode.getModes() * @throws IllegalArgumentException if the specified mode does not * support properties. * * @since JAI 1.1 */ public void clearPropertyState(String modeName) { DescriptorCache dc = getDescriptorCache(modeName); if (dc != null) dc.clearPropertyState(); } /** * Returns a list of the properties generated by nodes * implementing the descriptor associated with a particular * descriptor Name. Returns null if no properties are * generated. * * @param modeName the registry mode name as a String * @param descriptorName the descriptor name as a String * * @throws IllegalArgumentException if any of the arguments * is null * @throws IllegalArgumentException if modeName is not one of * those returned by RegistryMode.getModes() * @throws IllegalArgumentException if there is no * RegistryElementDescriptor registered against * the descriptorName * @throws IllegalArgumentException if the specified mode does not * support properties. * * @since JAI 1.1 */ public String[] getGeneratedPropertyNames(String modeName, String descriptorName) { DescriptorCache dc = getDescriptorCache(modeName); if (dc != null) return dc.getGeneratedPropertyNames(descriptorName); return null; } /** * Merge mode-specific property environment with mode-independent * property environment of the descriptor. Array elements of * "sources" are expected to be in the same ordering as referenced * by the "sourceIndex" parameter of copyPropertyFromSource(). * * @param modeName the registry mode name as a String * @param descriptorName the descriptor name as a String * @param op the Object from which properties will * be generated. * @param sources the PropertySources corresponding to * the sources of the object representing the named descriptor * in the indicated mode. The supplied Vector may * be empty to indicate that there are no sources. * * @return A PropertySource which encapsulates * the global property environment for the object representing * the named descriptor in the indicated mode. * * @throws IllegalArgumentException if any of the arguments * is null * @throws IllegalArgumentException if modeName is not one of * those returned by RegistryMode.getModes() * @throws IllegalArgumentException if there is no * RegistryElementDescriptor registered against * the descriptorName * @throws IllegalArgumentException if the specified mode does not * support properties. * * @since JAI 1.1 */ public PropertySource getPropertySource(String modeName, String descriptorName, Object op, Vector sources) { DescriptorCache dc = getDescriptorCache(modeName); if (dc != null) return dc.getPropertySource(descriptorName, op, sources); return null; } /** * Constructs and returns a PropertySource suitable for * use by a given OperationNode. The * PropertySource includes properties copied from prior * nodes as well as those generated at the node itself. Additionally, * property suppression is taken into account. The actual * implementation of getPropertySource() may make use * of deferred execution and caching. * * @param op the OperationNode requesting its * PropertySource. * * @throws IllegalArgumentException if op is null. * * @since JAI 1.1 */ public PropertySource getPropertySource(OperationNode op) { if (op == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); // Get the source Vector from the ParameterBlock. ParameterBlock pb = op.getParameterBlock(); Vector pv = (pb == null) ? null : pb.getSources(); // If the source Vector is null, replace it by a zero-length // Vector. This tricks the DescriptorCache into accepting the // parameter and the PropertyEnvironment object created in // the DescriptorCache works with either a null or zero-length // source Vector. if(pv == null) { pv = new Vector(); } return getPropertySource(op.getRegistryModeName(), op.getOperationName(), op, pv); } /** * Load all the "META-INF/registryFile.jai" files and then * called the updateRegistry() of the registered * service provider of OperationRegistrySpi found * in the classpath corresponding to this class loader. All * non-IO exceptions encountered while parsing the registry * files are caught and their error messages are redirected to * System.err. If System.err is null the * error messages will never be seen. * *

      This is a convenience method to do automatic detection in runtime * loaded jar files * *

      Note that the JAI does not keep track of which JAR files have * their registry files loaded and/or services initialized. Hence * if registerServices is called twice with the * same ClassLoader, the loading of the registry files and/or * initialization of the services will happen twice. * * @since JAI 1.1 */ public void registerServices(ClassLoader cl) throws IOException { // First load all the REGISTRY_FILEs that are found in // the specified class loader. Enumeration en; if (cl == null) en = ClassLoader.getSystemResources(USR_REGISTRY_FILE); else en = cl.getResources(USR_REGISTRY_FILE); while (en.hasMoreElements()) { URL url = (URL)en.nextElement(); RegistryFileParser.loadOperationRegistry(this, cl, url); } // Now call the "updateRegistry" method for all OperationRegistry // service providers. Iterator spitr; if (cl == null) spitr = Service.providers(OperationRegistrySpi.class); else spitr = Service.providers(OperationRegistrySpi.class, cl); while (spitr.hasNext()) { OperationRegistrySpi ospi = (OperationRegistrySpi)spitr.next(); ospi.updateRegistry(this); } } /********************** DEPRECATED METHODS *************************/ // OperationDescriptor methods /** * Registers an OperationDescriptor with the registry. Each * operation must have an OperationDescriptor before * registerRIF() may be called to add RIFs to the operation. * * @param odesc an OperationDescriptor containing information * about the operation. * @param operationName the operation name as a String. * * @deprecated as of JAI 1.1 in favor of registerDescriptor(odesc) * * @see #registerDescriptor(RegistryElementDescriptor) * registerDescriptor - for list of exceptions thrown. */ public void registerOperationDescriptor(OperationDescriptor odesc, String operationName) { registerDescriptor(odesc); } /** * Unregisters an OperationDescriptor from the registry. * * @param operationName the operation name as a String. * * @deprecated as of JAI 1.1 in favor of unregisterDescriptor(...) * which accepts an OperationDescriptor * and not a operationName. * * @see #unregisterDescriptor(RegistryElementDescriptor) * unregisterDescriptor - for list of exceptions thrown. */ public void unregisterOperationDescriptor(String operationName) { String[] operationModes = RegistryMode.getModeNames(OperationDescriptor.class); RegistryElementDescriptor red; for (int i = 0; i < operationModes.length; i++) { if ((red = getDescriptor(operationModes[i], operationName)) != null) unregisterDescriptor(red); } } /** * Returns the OperationDescriptor that is currently * registered under the given name, or null if none exists. * Though unlikely, it is possible to have different descriptors * registered under different modes. In this case this will * arbitrarily return the first operation descriptor found. * * @param operationName the String to be queried. * @return an OperationDescriptor. * * @throws IllegalArgumentException if operationName is null. * * @deprecated as of JAI 1.1 in favor of getDescriptor(...) * where the mode name is explicitly specified. * * @see #getDescriptor(Class, String) */ public OperationDescriptor getOperationDescriptor(String operationName) { return (OperationDescriptor) getDescriptor(OperationDescriptor.class, operationName); } /** * Returns a Vector of all currently registered * OperationDescriptors. * * @return a Vector of OperationDescriptors. * * @deprecated as of JAI 1.1 in favor of getDescriptors( * OperationDescriptor.class) which returns a List and * not a Vector. This is currently equivalent to * new Vector(getDescriptors(OperationDescriptor.class)) * * @see #getDescriptors(Class) */ public Vector getOperationDescriptors() { List list = getDescriptors(OperationDescriptor.class); return list == null ? null : new Vector(list); } /** * Returns a list of names under which all the * OperationDescriptors in the registry are registered. * * @return a list of currently existing operation names. * * @deprecated as of JAI 1.1 in favor of getDescriptorNames( * OperationDescriptor.class). * * @see #getDescriptorNames(Class) */ public String[] getOperationNames() { return getDescriptorNames(OperationDescriptor.class); } // RenderedImageFactory methods /** * Registers a RIF with a particular product and operation. * * @param operationName the operation name as a String. * @param productName the product name, as a String. * @param RIF the RenderedImageFactory to be registered. * * @deprecated as of JAI 1.1 in favor of RIFRegistry.register(...) * . This is currently equivalent to * RIFRegistry.register(this, operationName, productName, RIF) * * @see #registerFactory registerFactory - for list of exceptions thrown. * @see RIFRegistry#register */ public void registerRIF(String operationName, String productName, RenderedImageFactory RIF) { registerFactory(RenderedRegistryMode.MODE_NAME, operationName, productName, RIF); } /** * Unregisters a RIF from a particular product and operation. * * @param operationName the operation name as a String. * @param productName the product name, as a String. * @param RIF the RenderedImageFactory to be unregistered. * * @deprecated as of JAI 1.1 in favor of RIFRegistry.unregister(...) * . This is currently equivalent to * RIFRegistry.unregister(this, operationName, productName, RIF) * * @see #unregisterFactory unregisterFactory - for list of exceptions thrown. * @see RIFRegistry#unregister */ public void unregisterRIF(String operationName, String productName, RenderedImageFactory RIF) { unregisterFactory(RenderedRegistryMode.MODE_NAME, operationName, productName, RIF); } /** * Registers a CRIF under a particular operation. * * @param operationName the operation name as a String. * @param CRIF the ContextualRenderedImageFactory to be * registered. * * @deprecated as of JAI 1.1 in favor of CRIFRegistry.register(...) * . This is currently equivalent to * CRIFRegistry.register(this, operationName, productName, CRIF) * * @see #registerFactory registerFactory - for list of exceptions thrown. * @see CRIFRegistry#register */ public void registerCRIF(String operationName, ContextualRenderedImageFactory CRIF) { registerFactory(RenderableRegistryMode.MODE_NAME, operationName, null, CRIF); } /** * Unregisters a CRIF from a particular operation. * * @param operationName the operation name as a String. * @param CRIF the ContextualRenderedImageFactory to be * unregistered. * * @deprecated as of JAI 1.1 in favor of CRIFRegistry.unregister(...) * . This is currently equivalent to * CRIFRegistry.unregister(this, operationName, productName, CRIF) * * @see #unregisterFactory unregisterFactory - for list of exceptions thrown. * @see CRIFRegistry#unregister */ public void unregisterCRIF(String operationName, ContextualRenderedImageFactory CRIF) { unregisterFactory(RenderableRegistryMode.MODE_NAME, operationName, null, CRIF); } /** * Registers a CIF with a particular product and operation. * * @param operationName the operation name as a String. * @param productName the product name, as a String. * @param CIF the CollectionImageFactory to be registered. * * @deprecated as of JAI 1.1 in favor of CIFRegistry.register(...) * . This is currently equivalent to * CIFRegistry.register(this, operationName, productName, CIF) * * @see #registerFactory registerFactory - for list of exceptions thrown. * @see CIFRegistry#register */ public void registerCIF(String operationName, String productName, CollectionImageFactory CIF) { registerFactory(CollectionRegistryMode.MODE_NAME, operationName, productName, CIF); } /** * Unregisters a CIF from a particular product and operation. * * @param operationName the operation name as a String. * @param productName the product name, as a String. * @param CIF the CollectionImageFactory to be unregistered. * * @deprecated as of JAI 1.1 in favor of CIFRegistry.unregister(...) * . This is currently equivalent to * CIFRegistry.unregister(this, operationName, productName, CIF) * * @see #unregisterFactory unregisterFactory - for list of exceptions thrown. * @see CIFRegistry#unregister */ public void unregisterCIF(String operationName, String productName, CollectionImageFactory CIF) { unregisterFactory(CollectionRegistryMode.MODE_NAME, operationName, productName, CIF); } // Product preferences /** * Sets a preference between two products registered under a common * OperationDescriptor. Any attempt to set a preference * between a product and itself will be ignored. * * @param operationName the operation name as a String. * @param preferredProductName the product to be preferred. * @param otherProductName the other product. * * @deprecated as of JAI 1.1 in favor of setProductPreference(...) * which specifies a modeName also. This is * currently equivalent to setProductPreference("rendered", * operationName, preferredProductName, otherProductName) * * @see #setProductPreference setProductPreference - for list of exceptions thrown. */ public void setProductPreference(String operationName, String preferredProductName, String otherProductName) { setProductPreference(RenderedRegistryMode.MODE_NAME, operationName, preferredProductName, otherProductName); } /** * Removes a preference between two products registered under * a common OperationDescriptor. * * @param operationName the operation name as a String. * @param preferredProductName the product formerly preferred. * @param otherProductName the other product. * * @deprecated as of JAI 1.1 in favor of unsetProductPreference(...) * which specifies a modeName also. This is * currently equivalent to unsetProductPreference("rendered", * operationName, preferredProductName, otherProductName) * * @see #unsetProductPreference unsetProductPreference - for list of exceptions thrown. */ public void unsetProductPreference(String operationName, String preferredProductName, String otherProductName) { unsetProductPreference(RenderedRegistryMode.MODE_NAME, operationName, preferredProductName, otherProductName); } /** * Removes all preferences between products registered under * a common OperationDescriptor. * * @param operationName the operation name as a String. * * @deprecated as of JAI 1.1 in favor of clearProductPreferences(...) * which specifies a modeName also. This is * currently equivalent to * clearProductPreferences("rendered", operationName) * * @see #clearProductPreferences clearProductPreferences - for list of exceptions thrown. */ public void clearProductPreferences(String operationName) { clearProductPreferences(RenderedRegistryMode.MODE_NAME, operationName); } /** * Returns a list of the pairwise product preferences * under a particular OperationDescriptor. If no product * preferences have been set, returns null. * * @param operationName the operation name as a String. * @return an array of 2-element arrays of Strings. * * @deprecated as of JAI 1.1 in favor of getProductPreferences(...) * which accepts a modeName also. This is * currently equivalent to * getProductPreferences("rendered", operationName) * * @see #getProductPreferences getProductPreferences - for list of exceptions thrown. */ public String [][] getProductPreferences(String operationName) { return getProductPreferences(RenderedRegistryMode.MODE_NAME, operationName); } /** * Returns a list of the products registered under a particular * OperationDescriptor, in an ordering that satisfies * all of the pairwise preferences that have been set. Returns * null if cycles exist. Returns null if * no OperationDescriptor has been registered under * this operationName, or if no products exist for this operation. * * @param operationName the operation name as a String. * @return a Vector of Strings representing product names. * * @deprecated as of JAI 1.1 in favor of getOrderedProductList(...) * which accepts a modeName also. This is * currently equivalent to * getOrderedProductList("rendered", operationName) * * @see #getOrderedProductList getOrderedProductList - for list of exceptions thrown. */ public Vector getOrderedProductList(String operationName) { return getOrderedProductList(RenderedRegistryMode.MODE_NAME, operationName); } // Operation preferences (within a product) /** * Sets a preference between two RIFs within the same product. Any * attempt to set a preference between a RIF and itself will be * ignored. * * @param operationName the operation name as a String. * @param productName the name of the product. * @param preferredRIF the preferred RenderedImageFactory. * @param otherRIF the other RenderedImageFactory. * * @deprecated as of JAI 1.1 in favor of RIFRegistry.setPreference(...) * . This is currently equivalent to * RIFRegistry.setPreference(this, operationName, productName, * preferredRIF, otherRIF) * * @see #setFactoryPreference setFactoryPreference - for list of exceptions thrown. * @see RIFRegistry#setPreference */ public void setRIFPreference(String operationName, String productName, RenderedImageFactory preferredRIF, RenderedImageFactory otherRIF) { setFactoryPreference(RenderedRegistryMode.MODE_NAME, operationName, productName, preferredRIF, otherRIF); } /** * Sets a preference between two CIFs within the same product. Any * attempt to set a preference between a CIF and itself will be * ignored. * * @param operationName the operation name as a String. * @param productName the name of the product. * @param preferredCIF the preferred CollectionRenderedImageFactory. * @param otherCIF the other CollectionRenderedImageFactory. * * @deprecated as of JAI 1.1 in favor of CIFRegistry.setPreference(...) * . This is currently equivalent to * CIFRegistry.setPreference(this, operationName, productName, * preferredCIF, otherCIF) * * @see #setFactoryPreference setFactoryPreference - for list of exceptions thrown. * @see CIFRegistry#setPreference */ public void setCIFPreference(String operationName, String productName, CollectionImageFactory preferredCIF, CollectionImageFactory otherCIF) { setFactoryPreference(CollectionRegistryMode.MODE_NAME, operationName, productName, preferredCIF, otherCIF); } /** * Removes a preference between two RIFs within the same product. * * @param operationName the operation name as a String. * @param productName the name of the product. * @param preferredRIF the formerly preferred * RenderedImageFactory. * @param otherRIF the other RenderedImageFactory. * * @deprecated as of JAI 1.1 in favor of RIFRegistry.unsetPreference(...) * . This is currently equivalent to * RIFRegistry.unsetPreference(this, operationName, productName, * preferredRIF, otherRIF) * * @see #unsetFactoryPreference unsetFactoryPreference - for list of exceptions thrown. * @see RIFRegistry#unsetPreference */ public void unsetRIFPreference(String operationName, String productName, RenderedImageFactory preferredRIF, RenderedImageFactory otherRIF) { unsetFactoryPreference(RenderedRegistryMode.MODE_NAME, operationName, productName, preferredRIF, otherRIF); } /** * Removes a preference between two CIFs within the same product. * * @param operationName the operation name as a String. * @param productName the name of the product. * @param preferredCIF the formerly preferred * CollectionImageFactory. * @param otherCIF the other CollectionImageFactory. * * @deprecated as of JAI 1.1 in favor of CIFRegistry.unsetPreference(...) * . This is currently equivalent to * CIFRegistry.unsetPreference(this, operationName, productName, * preferredCIF, otherCIF) * * @see #unsetFactoryPreference unsetFactoryPreference - for list of exceptions thrown. * @see CIFRegistry#unsetPreference */ public void unsetCIFPreference(String operationName, String productName, CollectionImageFactory preferredCIF, CollectionImageFactory otherCIF) { unsetFactoryPreference(CollectionRegistryMode.MODE_NAME, operationName, productName, preferredCIF, otherCIF); } /** * Removes all preferences between RIFs within a product * registered under a particular OperationDescriptor. * * @param operationName the operation name as a String. * @param productName the name of the product. * * @deprecated as of JAI 1.1 in favor of RIFRegistry.clearPreferences(...) * . This is currently equivalent to * RIFRegistry.clearPreferences(this, operationName, productName) * * @see #clearFactoryPreferences clearFactoryPreferences - for list of exceptions thrown. * @see RIFRegistry#clearPreferences */ public void clearRIFPreferences(String operationName, String productName) { clearFactoryPreferences( RenderedRegistryMode.MODE_NAME, operationName, productName); } /** * Removes all preferences between CIFs within a product * registered under a particular OperationDescriptor. * * @param operationName the operation name as a String. * @param productName the name of the product. * * @deprecated as of JAI 1.1 in favor of CIFRegistry.clearPreferences(...) * . This is currently equivalent to * CIFRegistry.clearPreferences(this, operationName, productName) * * @see #clearFactoryPreferences clearFactoryPreferences - for list of exceptions thrown. * @see CIFRegistry#clearPreferences */ public void clearCIFPreferences(String operationName, String productName) { clearFactoryPreferences( CollectionRegistryMode.MODE_NAME, operationName, productName); } /** * Removes all RIF and CIF preferences within a product * registered under a particular OperationDescriptor. * * @param operationName the operation name as a String. * @param productName the name of the product. * * @deprecated as of JAI 1.1 in favor of calling * *IFRegistry.clearPreferences(..) on all image operation * related modes. * * @see #clearFactoryPreferences clearFactoryPreferences - for list of exceptions thrown. * @see RIFRegistry#clearPreferences * @see CIFRegistry#clearPreferences */ public void clearOperationPreferences(String operationName, String productName) { String[] operationModes = RegistryMode.getModeNames(OperationDescriptor.class); for (int i = 0; i < operationModes.length; i++) { DescriptorCache dc = getDescriptorCache(operationModes[i]); if (!dc.arePreferencesSupported) continue; if (getDescriptor(operationModes[i], operationName) == null) continue; clearFactoryPreferences( operationModes[i], operationName, productName); } } /** * Returns a list of the RIFs of a product registered under a * particular OperationDescriptor, in an ordering * that satisfies all of the pairwise preferences that have * been set. Returns null if cycles exist. Returns * null, if the product does not exist under this * operationName. * * @param operationName the operation name as a String. * @param productName the name of the product. * @return a Vector of RIFs. * * @deprecated as of JAI 1.1 in favor of RIFRegistry.getOrderedList(...) * which returns a List and not a * Vector. This is currently equivalent to * new Vector(RIFRegistry.getOrderedList(this, operationName, * productName)) * * @see #getOrderedFactoryList getOrderedFactoryList - for list of exceptions thrown. * @see RIFRegistry#getOrderedList */ public Vector getOrderedRIFList(String operationName, String productName) { List list = getOrderedFactoryList( RenderedRegistryMode.MODE_NAME, operationName, productName); return list == null ? null : new Vector(list); } /** * Returns a list of the CIFs of a product registered under a * particular OperationDescriptor, in an ordering * that satisfies all of the pairwise preferences that have * been set. Returns null if cycles exist. Returns * null, if the product does not exist under this * operationName. * * @param operationName the operation name as a String. * @param productName the name of the product. * @return a Vector of CIFs. * * @deprecated as of JAI 1.1 in favor of CIFRegistry.getOrderedList(...) * which returns a List and not a * Vector. This is currently equivalent to * new Vector(CIFRegistry.getOrderedList(this, operationName, * productName)) * * @see #getOrderedFactoryList getOrderedFactoryList - for list of exceptions thrown. * @see CIFRegistry#getOrderedList */ public Vector getOrderedCIFList(String operationName, String productName) { List list = getOrderedFactoryList( CollectionRegistryMode.MODE_NAME, operationName, productName); return list == null ? null : new Vector(list); } // Create methods /** * Constructs a PlanarImage (usually a RenderedOp) representing * the results of applying a given operation to a particular * ParameterBlock and rendering hints. The registry is used to * determine the RIF to be used to instantiate the operation. * *

      If none of the RIFs registered with this * OperationRegistry returns a non-null value, null is * returned. Exceptions thrown by the RIFs will be caught by this * method and will not be propagated. * * @param operationName the operation name as a String. * @param paramBlock the operation's ParameterBlock. * @param renderHints a RenderingHints object containing rendering hints. * * @throws IllegalArgumentException if operationName is null. * * @deprecated as of JAI 1.1 in favor of RIFRegistry.create(...) * which returns a RenderedImage and not a * PlanarImage. This is currently equivalent to * PlanarImage.wrapRenderedImage(RIFRegistry.create(this, * operationName, paramBlock, renderHints)) * * @see RIFRegistry#create */ public PlanarImage create(String operationName, ParameterBlock paramBlock, RenderingHints renderHints) { return PlanarImage.wrapRenderedImage( RIFRegistry.create(this, operationName, paramBlock, renderHints)); } /** * Constructs the CRIF to be used to instantiate the operation. * Returns null, if no CRIF is registered with the given operation * name. * * @param operationName the operation name as a String. * @param paramBlock the operation's ParameterBlock. * * @throws IllegalArgumentException if operationName is null. * * @deprecated as of JAI 1.1 in favor of CRIFRegistry.get(...) * This is currently equivalent to CRIFRegistry.get(this, * operationName) * * @see CRIFRegistry#get */ public ContextualRenderedImageFactory createRenderable(String operationName, ParameterBlock paramBlock) { return CRIFRegistry.get(this, operationName); } /** * Constructs a CollectionImage (usually a * CollectionOp) representing the results of applying * a given operation to a particular ParameterBlock and rendering hints. * The registry is used to determine the CIF to be used to instantiate * the operation. * *

      If none of the CIFs registered with this * OperationRegistry returns a non-null value, null is * returned. Exceptions thrown by the CIFs will be caught by this * method and will not be propagated. * * @param operationName The operation name as a String. * @param args The operation's input parameters. * @param hints A RenderingHints object containing rendering hints. * * @throws IllegalArgumentException if operationName is null. * * @deprecated as of JAI 1.1 in favor of CIFRegistry.create(...) * . This is currently equivalent to * CIFRegistry.create(this, operationName, args, hints)) * * @see CIFRegistry#create */ public CollectionImage createCollection(String operationName, ParameterBlock args, RenderingHints hints) { return CIFRegistry.create(this, operationName, args, hints); } // Property management /** * Removes all property associated information from this * OperationRegistry. * * @deprecated as of JAI 1.1 in factor of the version where the modeName * is explicitly specified. This is currently equivalent to * clearPropertyState("rendered") * * @see #clearPropertyState */ public void clearPropertyState() { clearPropertyState(RenderedRegistryMode.MODE_NAME); } /** * Adds a PropertyGenerator to the registry, associating * it with a particular OperationDescriptor. * * @param operationName the operation name as a String. * @param generator the PropertyGenerator to be added. * * @deprecated as of JAI 1.1 in favor of the version where the * modeName is explicitly specified. This is currently * equivalent to addPropertyGenerator("rendered", ...) * * @see #addPropertyGenerator addPropertyGenerator - for list of exceptions thrown. */ public void addPropertyGenerator(String operationName, PropertyGenerator generator) { addPropertyGenerator(RenderedRegistryMode.MODE_NAME, operationName, generator); } /** * Removes a PropertyGenerator from its association with a * particular OperationDescriptor in the registry. If * the generator was not associated with the operation, * nothing happens. * * @param operationName the operation name as a String. * @param generator the PropertyGenerator to be removed. * * @deprecated as of JAI 1.1 in favor of the version where the * modeName is explicitly specified. This is currently * equivalent to removePropertyGenerator("rendered", ...) * * @see #removePropertyGenerator removePropertyGenerator - for list of exceptions thrown. */ public void removePropertyGenerator(String operationName, PropertyGenerator generator) { removePropertyGenerator(RenderedRegistryMode.MODE_NAME, operationName, generator); } /** * Forces a particular property to be suppressed by nodes * performing a particular operation. By default, properties * are passed through operations unchanged. * * @param operationName the operation name as a String. * @param propertyName the name of the property to be suppressed. * * @deprecated as of JAI 1.1 in favor of the version where the * modeName is explicitly specified. This is currently * equivalent to suppressProperty("rendered", ...) * * @see #suppressProperty suppressProperty - for list of exceptions thrown. */ public void suppressProperty(String operationName, String propertyName) { suppressProperty(RenderedRegistryMode.MODE_NAME, operationName, propertyName); } /** * Forces all properties to be suppressed by nodes performing a * particular operation. By default, properties are passed * through operations unchanged. * * @param operationName the operation name as a String. * * @deprecated as of JAI 1.1 in favor of the version where the * modeName is explicitly specified. This is currently * equivalent to suppressAllProperties("rendered", ...) * * @see #suppressAllProperties suppressAllProperties - for list of exceptions thrown. */ public void suppressAllProperties(String operationName) { suppressAllProperties(RenderedRegistryMode.MODE_NAME, operationName); } /** * Forces a property to be copied from the specified source image * by RenderedOp nodes performing a particular * operation. By default, a property is copied from the first * source node that emits it. The result of specifying an invalid * source is undefined. * * @param operationName the operation name as a String. * @param propertyName the name of the property to be copied. * @param sourceIndex the index of the source to copy the property from. * * @deprecated as of JAI 1.1 in favor of the version where the * modeName is explicitly specified. This is currently * equivalent to copyPropertyFromSource("rendered", ...) * * @see #copyPropertyFromSource copyPropertyFromSource - for list of exceptions thrown. */ public void copyPropertyFromSource(String operationName, String propertyName, int sourceIndex) { copyPropertyFromSource(RenderedRegistryMode.MODE_NAME, operationName, propertyName, sourceIndex); } /** * Returns a list of the properties generated by nodes * implementing the operation associated with a particular * Operation Name. Returns null if no properties are * generated. * * @param operationName the operation name as a String. * @return an array of Strings. * * @deprecated as of JAI 1.1 in favor of the version where the * modeName is explicitly specified. This is currently * equivalent to getGeneratedPropertyNames("rendered", ...) * * @see #getGeneratedPropertyNames getGeneratedPropertyNames - for list of exceptions thrown. */ public String[] getGeneratedPropertyNames(String operationName) { return getGeneratedPropertyNames(RenderedRegistryMode.MODE_NAME, operationName); } /** * Constructs and returns a PropertySource suitable for * use by a given RenderedOp. The * PropertySource includes properties copied from prior * nodes as well as those generated at the node itself. Additionally, * property suppression is taken into account. The actual * implementation of getPropertySource() may make use * of deferred execution and caching. * * @param op the RenderedOp requesting its * PropertySource. * * @deprecated as of JAI 1.1 in favor * RIFRegistry.getPropertySource(op) * * @see RIFRegistry#getPropertySource #getPropertySource - for list of exceptions thrown. */ public PropertySource getPropertySource(RenderedOp op) { return RIFRegistry.getPropertySource(op); } /** * Constructs and returns a PropertySource suitable for * use by a given RenderableOp. The * PropertySource includes properties copied from prior * nodes as well as those generated at the node itself. Additionally, * property suppression is taken into account. The actual implementation * of getPropertySource() may make use of deferred * execution and caching. * * @param op the RenderableOp requesting its * PropertySource. * * @deprecated as of JAI 1.1 in favor * CRIFRegistry.getPropertySource(op) * * @see CRIFRegistry#getPropertySource */ public PropertySource getPropertySource(RenderableOp op) { return CRIFRegistry.getPropertySource(op); } } jai-core-1.1.4/src/share/classes/javax/media/jai/RasterAccessor.java0000644000175000017500000016101510203035544025127 0ustar mathieumathieu/* * $RCSfile: RasterAccessor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:18 $ * $State: Exp $ */ package javax.media.jai; import java.awt.image.SampleModel; import java.awt.image.ComponentSampleModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferUShort; import java.awt.image.DataBufferShort; import java.awt.image.DataBufferInt; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.awt.Rectangle; import java.awt.image.RenderedImage; import java.awt.image.ColorModel; import java.awt.image.IndexColorModel; import java.awt.image.ComponentColorModel; import com.sun.media.jai.util.DataBufferUtils; import com.sun.media.jai.util.ImageUtil; /** * An adapter class for presenting non-binary image data in a * ComponentSampleModel format and binary image data in * a zero-offset byte array format even when the original data are not * so stored. RasterAccessor is meant to make the common * (ComponentSampleModel) case fast and other formats * possible without forcing the OpImage writer to cover more * than one case per non-binary data type. * *

      When constructing a RasterAccessor with a source(s) that * has an IndexColorModel and a destination that has a * non-IndexColorModel, RasterAccessor will * perform expansion of the source pixels. If the source(s) and the * destination have an IndexColorModel, then RasterAccessor * will assume that the operation can correctly process an IndexColorModel * source and will not expand the source pixels (colormap indices) into * color components. Refer to {@link JAI#KEY_REPLACE_INDEX_COLOR_MODEL} * for a mechanism by which the destination image's ColorModel * is set to a non-IndexColorModel to cause * RasterAccessor to expand the source's * IndexColorModel. * *

      Binary data are handled as a special case. In general image data * are considered to be binary when the image has a single-banded * MultiPixelPackedSampleModel with one bit per pixel. This * may be verified by invoking the isBinary() method. For this * case the methods getBinaryDataArray() and * copyBinaryDataToRaster() should be used to access and set, * respectively, the binary data in packed form. If the binary data are * to be accessed in expanded form, i.e., as bytes, then the usual byte * methods getByteDataArray(), getByteDataArrays(), * and copyDataToRaster() should be used. * */ public class RasterAccessor { /** * Value indicating how far COPY_MASK info is shifted to avoid * interfering with the data type info. */ private static final int COPY_MASK_SHIFT = 7; /* Value indicating how many bits the COPY_MASK is */ private static final int COPY_MASK_SIZE = 2; /** The bits of a FormatTag associated with how dataArrays are obtained. */ public static final int COPY_MASK = ((1 << COPY_MASK_SIZE) - 1) << COPY_MASK_SHIFT; /** Flag indicating data is raster's data. */ public static final int UNCOPIED = 0x0 << COPY_MASK_SHIFT; /** Flag indicating data is a copy of the raster's data. */ public static final int COPIED = 0x1 << COPY_MASK_SHIFT; /** * Value indicating how far EXPANSION_MASK info is shifted to avoid * interfering with the data type info. */ private static final int EXPANSION_MASK_SHIFT = COPY_MASK_SHIFT+COPY_MASK_SIZE; /** Value indicating how many bits the EXPANSION_MASK is */ private static final int EXPANSION_MASK_SIZE = 2; /** The bits of a FormatTag associated with how ColorModels are used. */ public static final int EXPANSION_MASK = ((1 << EXPANSION_MASK_SIZE) - 1) << EXPANSION_MASK_SHIFT; /** Flag indicating ColorModel data should be used only in copied case */ public static final int DEFAULTEXPANSION = 0x0 << EXPANSION_MASK_SHIFT; /** Flag indicating ColorModel data should be interpreted. */ public static final int EXPANDED = 0x1 << EXPANSION_MASK_SHIFT; /** Flag indicating ColorModel info should be ignored */ public static final int UNEXPANDED = 0x02 << EXPANSION_MASK_SHIFT; /** The bits of a FormatTagID associated with pixel datatype. */ public static final int DATATYPE_MASK = (0x1 << COPY_MASK_SHIFT) - 1; /** FormatTagID indicating data in byte arrays and uncopied. */ public static final int TAG_BYTE_UNCOPIED = DataBuffer.TYPE_BYTE | UNCOPIED; /** FormatTagID indicating data in unsigned short arrays and uncopied. */ public static final int TAG_USHORT_UNCOPIED = DataBuffer.TYPE_USHORT | UNCOPIED; /** FormatTagID indicating data in short arrays and uncopied. */ public static final int TAG_SHORT_UNCOPIED = DataBuffer.TYPE_SHORT | UNCOPIED; /** FormatTagID indicating data in int arrays and uncopied. */ public static final int TAG_INT_UNCOPIED = DataBuffer.TYPE_INT | UNCOPIED; /** FormatTagID indicating data in float arrays and uncopied. */ public static final int TAG_FLOAT_UNCOPIED = DataBuffer.TYPE_FLOAT | UNCOPIED; /** FormatTagID indicating data in double arrays and uncopied. */ public static final int TAG_DOUBLE_UNCOPIED = DataBuffer.TYPE_DOUBLE | UNCOPIED; /** FormatTagID indicating data in int arrays and copied. */ public static final int TAG_INT_COPIED = DataBuffer.TYPE_INT | COPIED; /** FormatTagID indicating data in float arrays and copied. */ public static final int TAG_FLOAT_COPIED = DataBuffer.TYPE_FLOAT | COPIED; /** FormatTagID indicating data in double arrays and copied. */ public static final int TAG_DOUBLE_COPIED = DataBuffer.TYPE_DOUBLE | COPIED; /** FormatTagID indicating data in byte arrays and expanded. */ public static final int TAG_BYTE_EXPANDED = DataBuffer.TYPE_BYTE | EXPANDED; /** * FormatTagID corresponding to the binary case. This occurs when * the image has a MultiPixelPackedSampleModel with a * single band and one bit per pixel. */ private static final int TAG_BINARY = DataBuffer.TYPE_BYTE | COPIED | UNEXPANDED; /** The raster that is the source of pixel data. */ protected Raster raster; /** The width of the rectangle this RasterAccessor addresses. */ protected int rectWidth; /** The height of the rectangle this RasterAccessor addresses. */ protected int rectHeight; /** The x coordinate of upper-left corner of the rectangle this * RasterAccessor addresses. */ protected int rectX; /** The y coordinate of upper-left corner of the rectangle this * RasterAccessor addresses. */ protected int rectY; /** Tag indicating the data type of the data and whether it's copied */ protected int formatTagID; /** * The image data for the binary case. The data will be packed as * eight bits per byte with no bit offset, i.e., the first bit in each * image line will be the left-most bit of the first byte of the line. * The line stride in bytes will be (int)((rectWidth+7)/8). * The length of the array will be rectHeight multiplied * by the line stride. * * @since JAI 1.1 */ protected byte binaryDataArray[] = null; /** * The image data in a two-dimensional byte array. This * value will be non-null only if getDataType() returns * DataBuffer.TYPE_BYTE. byteDataArrays.length will equal * numBands. Note that often the numBands subArrays will all * point to the same place in memory. * *

      For the case of binary data this variable will not be initialized * until getByteDataArrays() or * getByteDataArray(int b) is invoked. */ protected byte byteDataArrays[][] = null; /** * The image data in a two-dimensional short array. This * value will be non-null only if getDataType() returns * DataBuffer.TYPE_USHORT or DataBuffer.TYPE_SHORT. * shortDataArrays.length will equal * numBands. Note that often the numBands subArrays will all * point to the same place in memory. */ protected short shortDataArrays[][] = null; /** * The image data in a two-dimensional int array. This * value will be non-null only if getDataType() returns * DataBuffer.TYPE_INT. intDataArrays.length will equal * numBands. Note that often the numBands subArrays will all * point to the same place in memory. */ protected int intDataArrays[][] = null; /** * The image data in a two-dimensional float array. This * value will be non-null only if getDataType() returns * DataBuffer.TYPE_FLOAT. floatDataArrays.length will equal * numBands. Note that often the numBand subArrays will all * point to the same place in memory. */ protected float floatDataArrays[][] = null; /** * The image data in a two-dimensional double array. This * value will be non-null only if getDataType() returns * DataBuffer.TYPE_DOUBLE. doubleDataArrays.length will equal * numBands. Note that often the numBand subArrays will all * point to the same place in memory. */ protected double doubleDataArrays[][] = null; /** * The bandOffset + subRasterOffset + DataBufferOffset into each of * the numBand data arrays */ protected int bandDataOffsets[]; /** Offset from a pixel's offset to a band of that pixel */ protected int bandOffsets[]; /** The number of bands per pixel in the data array. */ protected int numBands; /** The scanline stride of the image data in each data array */ protected int scanlineStride; /** The pixel stride of the image data in each data array */ protected int pixelStride; /** * Finds the appropriate tags for the constructor, based on the * SampleModel and ColorModel of all the source and destination. * * @param srcs The operations sources; may be null which * is taken to be equivalent to zero sources. * @param dst The operation destination. * @return An array containing RasterFormatTags for the * sources in the first src.length elements and a * RasterFormatTag for the destination in the last element. * @throws NullPointerException if dst is null. */ public static RasterFormatTag[] findCompatibleTags(RenderedImage srcs[], RenderedImage dst) { int tagIDs[]; if (srcs != null) { tagIDs = new int[srcs.length + 1]; } else { tagIDs = new int[1]; } SampleModel dstSampleModel = dst.getSampleModel(); int dstDataType = dstSampleModel.getTransferType(); int defaultDataType = dstDataType; boolean binaryDst = ImageUtil.isBinary(dstSampleModel); if (binaryDst) { defaultDataType = DataBuffer.TYPE_BYTE; } else if((dstDataType == DataBuffer.TYPE_BYTE) || (dstDataType == DataBuffer.TYPE_USHORT) || (dstDataType == DataBuffer.TYPE_SHORT)) { defaultDataType = DataBuffer.TYPE_INT; } // use highest precision datatype of all srcs & dst if (srcs != null) { int numSources = srcs.length; int i; for (i = 0; i < numSources; i++) { SampleModel srcSampleModel = srcs[i].getSampleModel(); int srcDataType = srcSampleModel.getTransferType(); if (!(binaryDst && ImageUtil.isBinary(srcSampleModel)) && srcDataType > defaultDataType) { defaultDataType = srcDataType; } } } // Set the tag. For binary data at this point this should // equal DataBuffer.TYPE_BYTE | COPIED. int tagID = defaultDataType | COPIED; if (dstSampleModel instanceof ComponentSampleModel) { if (srcs != null) { int numSources = srcs.length; int i; for (i = 0; i < numSources; i++) { SampleModel srcSampleModel = srcs[i].getSampleModel(); int srcDataType = srcSampleModel.getTransferType(); if (!(srcSampleModel instanceof ComponentSampleModel) || (srcDataType != dstDataType)) { break; } } if (i == numSources) { tagID = dstDataType | UNCOPIED; } } else { tagID = dstDataType | UNCOPIED; } } // If the source has an IndexColorModel but the dest does not, // perform expansion of the source pixels. If both have an // IndexColorModel, assume the operation knows what it is doing. RasterFormatTag rft[] = new RasterFormatTag[tagIDs.length]; if (srcs != null) { for (int i = 0; i < srcs.length; i++) { // dst can't be EXPANDED if ((srcs[i].getColorModel() instanceof IndexColorModel)) { if (dst.getColorModel() instanceof IndexColorModel) { tagIDs[i] = tagID | UNEXPANDED; } else { tagIDs[i] = tagID | EXPANDED; } } else if (srcs[i].getColorModel() instanceof ComponentColorModel || (binaryDst && ImageUtil.isBinary(srcs[i].getSampleModel()))){ tagIDs[i] = tagID | UNEXPANDED; } else { tagIDs[i] = tagID | DEFAULTEXPANSION; } } tagIDs[srcs.length] = tagID | UNEXPANDED; for (int i = 0; i < srcs.length; i++) { rft[i] = new RasterFormatTag(srcs[i].getSampleModel(), tagIDs[i]); } // get the dest rft[srcs.length] = new RasterFormatTag(dstSampleModel,tagIDs[srcs.length]); } else { // no sources, dest only rft[0] = new RasterFormatTag(dstSampleModel, tagID | UNEXPANDED); } return rft; } /** * Returns the most efficient FormatTagID that is compatible with * the destination SampleModel and all source SampleModels. * Since there is no ColorModel associated with * a SampleModel, this method does not expand the data * buffer as it has no access to the Raster's ColorModel. */ public static int findCompatibleTag(SampleModel[] srcSampleModels, SampleModel dstSampleModel) { int dstDataType = dstSampleModel.getTransferType(); int tag = dstDataType | COPIED; if (ImageUtil.isBinary(dstSampleModel)) { tag = DataBuffer.TYPE_BYTE | COPIED; } else if (dstDataType == DataBuffer.TYPE_BYTE || dstDataType == DataBuffer.TYPE_USHORT || dstDataType == DataBuffer.TYPE_SHORT) { tag = TAG_INT_COPIED; } if (dstSampleModel instanceof ComponentSampleModel) { if (srcSampleModels != null) { int numSources = srcSampleModels.length; int i; for (i = 0; i < numSources; i++) { int srcDataType = srcSampleModels[i].getTransferType(); if (!(srcSampleModels[i] instanceof ComponentSampleModel) || srcDataType != dstDataType) { break; } } if (i == numSources) { tag = dstDataType | UNCOPIED; } } else { tag = dstDataType | UNCOPIED; } } return tag | UNEXPANDED; // only called when colormodel not around // so never expand } /** * Constructs a RasterAccessor object out of a Raster, Rectangle * and formatTagID returned from RasterFormat.findCompatibleTag(). * *

      The RasterFormatTag must agree with the raster's * SampleModel and ColorModel. It is best * to obtain the correct tag using the findCompatibleTags * static method. * * @param raster The raster to be accessed * @param rect A Rectangle from the raster to be accessed * @param rft The RasterFormatTag associated with the Raster * @param theColorModel The ColorModel for color components * * @throws ClassCastException if the data type of * RasterFormatTag does not agree with the actual * data type of the Raster. * @throws IllegalArgumentException if raster, * rect, or rft is null. * @throws IllegalArgumentException if the Rectangle * is not contained within Raster's bounds. */ public RasterAccessor(Raster raster, Rectangle rect, RasterFormatTag rft, ColorModel theColorModel) { if(raster == null || rect == null || rft == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } // If requesting a region that lies outside the bounds, // throw an exception. if (!raster.getBounds().contains(rect)) { throw new IllegalArgumentException( JaiI18N.getString("RasterAccessor2")); } this.raster = raster; this.rectX = rect.x; this.rectY = rect.y; this.rectWidth = rect.width; this.rectHeight = rect.height; this.formatTagID = rft.getFormatTagID(); if ((formatTagID & COPY_MASK) == UNCOPIED) { this.numBands = rft.getNumBands(); this.pixelStride = rft.getPixelStride(); ComponentSampleModel csm = (ComponentSampleModel)raster.getSampleModel(); this.scanlineStride = csm.getScanlineStride(); int bankIndices[] = null; // if the rft isPixelSequential we can rely on it's // version of bandOffsets and bankIndicies. If it's // not the SampleModel passed in might not completely // match the one that was passed to the the // RasterFormatTag constructor so we have to get them // from the passed in Raster/SampleModel if (rft.isPixelSequential()) { this.bandOffsets = rft.getBandOffsets(); bankIndices = rft.getBankIndices(); } else { this.bandOffsets = csm.getBandOffsets(); bankIndices = csm.getBankIndices(); } this.bandDataOffsets = new int[numBands]; int dataBufferOffsets[] = raster.getDataBuffer().getOffsets(); int subRasterOffset = (rectY-raster.getSampleModelTranslateY())*scanlineStride+ (rectX-raster.getSampleModelTranslateX())*pixelStride; if (dataBufferOffsets.length == 1) { int theDataBufferOffset = dataBufferOffsets[0]; for (int i = 0; i < numBands; i++) { bandDataOffsets[i] = bandOffsets[i] + theDataBufferOffset + subRasterOffset; } } else if (dataBufferOffsets.length == bandDataOffsets.length) { for (int i = 0; i < numBands; i++) { bandDataOffsets[i] = bandOffsets[i] + dataBufferOffsets[i] + subRasterOffset; } } else { throw new RuntimeException(JaiI18N.getString("RasterAccessor0")); } switch (formatTagID & DATATYPE_MASK) { case DataBuffer.TYPE_BYTE: DataBufferByte dbb = (DataBufferByte)raster.getDataBuffer(); byteDataArrays = new byte[numBands][]; for (int i = 0; i < numBands; i++) { byteDataArrays[i] = dbb.getData(bankIndices[i]); } break; case DataBuffer.TYPE_USHORT: DataBufferUShort dbus = (DataBufferUShort)raster.getDataBuffer(); shortDataArrays = new short[numBands][]; for (int i = 0; i < numBands; i++) { shortDataArrays[i] = dbus.getData(bankIndices[i]); } break; case DataBuffer.TYPE_SHORT: DataBufferShort dbs = (DataBufferShort)raster.getDataBuffer(); shortDataArrays = new short[numBands][]; for (int i = 0; i < numBands; i++) { shortDataArrays[i] = dbs.getData(bankIndices[i]); } break; case DataBuffer.TYPE_INT: DataBufferInt dbi = (DataBufferInt)raster.getDataBuffer(); intDataArrays = new int[numBands][]; for (int i = 0; i < numBands; i++) { intDataArrays[i] = dbi.getData(bankIndices[i]); } break; case DataBuffer.TYPE_FLOAT: DataBuffer dbf = (DataBuffer)raster.getDataBuffer(); floatDataArrays = new float[numBands][]; for (int i = 0; i < numBands; i++) { floatDataArrays[i] = DataBufferUtils.getDataFloat(dbf, bankIndices[i]); } break; case DataBuffer.TYPE_DOUBLE: DataBuffer dbd = (DataBuffer)raster.getDataBuffer(); doubleDataArrays = new double[numBands][]; for (int i = 0; i < numBands; i++) { doubleDataArrays[i] = DataBufferUtils.getDataDouble(dbd, bankIndices[i]); } break; } // only do this if not copied and expanded if ((formatTagID & EXPANSION_MASK) == EXPANDED && theColorModel instanceof IndexColorModel) { IndexColorModel icm = (IndexColorModel)theColorModel; int newNumBands = icm.getNumComponents(); int mapSize = icm.getMapSize(); int newBandDataOffsets[] = new int[newNumBands]; int newScanlineStride = rectWidth*newNumBands; int newPixelStride = newNumBands; byte ctable[][] = new byte[newNumBands][mapSize]; icm.getReds(ctable[0]); icm.getGreens(ctable[1]); icm.getBlues(ctable[2]); byte rtable[] = ctable[0]; byte gtable[] = ctable[1]; byte btable[] = ctable[2]; byte atable[] = null; if (newNumBands == 4) { icm.getAlphas(ctable[3]); atable = ctable[3]; } for (int i = 0; i < newNumBands; i++) { newBandDataOffsets[i] = i; } switch (formatTagID & DATATYPE_MASK) { case DataBuffer.TYPE_BYTE: { byte newBArray[] = new byte[rectWidth*rectHeight*newNumBands]; byte byteDataArray[] = byteDataArrays[0]; int scanlineOffset = bandDataOffsets[0]; int newScanlineOffset = 0; for (int j = 0; j < rectHeight; j++) { int pixelOffset = scanlineOffset; int newPixelOffset = newScanlineOffset; for (int i = 0; i < rectWidth; i++) { int index = byteDataArray[pixelOffset] & 0xff; for (int k = 0; k < newNumBands; k++) { newBArray[newPixelOffset+k] = ctable[k][index]; } pixelOffset += pixelStride; newPixelOffset += newPixelStride; } scanlineOffset += scanlineStride; newScanlineOffset += newScanlineStride; } byteDataArrays = new byte[newNumBands][]; for (int i = 0; i < newNumBands; i++) { byteDataArrays[i] = newBArray; } } break; case DataBuffer.TYPE_USHORT: { short newIArray[] = new short[rectWidth*rectHeight*newNumBands]; short shortDataArray[] = shortDataArrays[0]; int scanlineOffset = bandDataOffsets[0]; int newScanlineOffset = 0; for (int j = 0; j < rectHeight; j++) { int pixelOffset = scanlineOffset; int newPixelOffset = newScanlineOffset; for (int i = 0; i < rectWidth; i++) { int index = (shortDataArray[pixelOffset] & 0xffff); for (int k = 0; k < newNumBands; k++) { newIArray[newPixelOffset+k] = (short)(ctable[k][index] & 0xff); } pixelOffset += pixelStride; newPixelOffset += newPixelStride; } scanlineOffset += scanlineStride; newScanlineOffset += newScanlineStride; } shortDataArrays = new short[newNumBands][]; for (int i = 0; i < newNumBands; i++) { shortDataArrays[i] = newIArray; } } break; case DataBuffer.TYPE_SHORT: { short newIArray[] = new short[rectWidth*rectHeight*newNumBands]; short shortDataArray[] = shortDataArrays[0]; int scanlineOffset = bandDataOffsets[0]; int newScanlineOffset = 0; for (int j = 0; j < rectHeight; j++) { int pixelOffset = scanlineOffset; int newPixelOffset = newScanlineOffset; for (int i = 0; i < rectWidth; i++) { int index = shortDataArray[pixelOffset]; for (int k = 0; k < newNumBands; k++) { newIArray[newPixelOffset+k] = (short)(ctable[k][index] & 0xff); } pixelOffset += pixelStride; newPixelOffset += newPixelStride; } scanlineOffset += scanlineStride; newScanlineOffset += newScanlineStride; } shortDataArrays = new short[newNumBands][]; for (int i = 0; i < newNumBands; i++) { shortDataArrays[i] = newIArray; } } break; case DataBuffer.TYPE_INT: { int newIArray[] = new int[rectWidth*rectHeight*newNumBands]; int intDataArray[] = intDataArrays[0]; int scanlineOffset = bandDataOffsets[0]; int newScanlineOffset = 0; for (int j = 0; j < rectHeight; j++) { int pixelOffset = scanlineOffset; int newPixelOffset = newScanlineOffset; for (int i = 0; i < rectWidth; i++) { int index = intDataArray[pixelOffset]; for (int k = 0; k < newNumBands; k++) { newIArray[newPixelOffset+k] = (ctable[k][index] & 0xff); } pixelOffset += pixelStride; newPixelOffset += newPixelStride; } scanlineOffset += scanlineStride; newScanlineOffset += newScanlineStride; } intDataArrays = new int[newNumBands][]; for (int i = 0; i < newNumBands; i++) { intDataArrays[i] = newIArray; } } break; case DataBuffer.TYPE_FLOAT: { float newFArray[] = new float[rectWidth*rectHeight*newNumBands]; float floatDataArray[] = floatDataArrays[0]; int scanlineOffset = bandDataOffsets[0]; int newScanlineOffset = 0; for (int j = 0; j < rectHeight; j++) { int pixelOffset = scanlineOffset; int newPixelOffset = newScanlineOffset; for (int i = 0; i < rectWidth; i++) { int index = (int)floatDataArray[pixelOffset]; for (int k = 0; k < newNumBands; k++) { newFArray[newPixelOffset+k] = (ctable[k][index] & 0xff); } pixelOffset += pixelStride; newPixelOffset += newPixelStride; } scanlineOffset += scanlineStride; newScanlineOffset += newScanlineStride; } floatDataArrays = new float[newNumBands][]; for (int i = 0; i < newNumBands; i++) { floatDataArrays[i] = newFArray; } } break; case DataBuffer.TYPE_DOUBLE: { double newDArray[] = new double[rectWidth*rectHeight*newNumBands]; double doubleDataArray[] = doubleDataArrays[0]; int scanlineOffset = bandDataOffsets[0]; int newScanlineOffset = 0; for (int j = 0; j < rectHeight; j++) { int pixelOffset = scanlineOffset; int newPixelOffset = newScanlineOffset; for (int i = 0; i < rectWidth; i++) { int index = (int)doubleDataArray[pixelOffset]; for (int k = 0; k < newNumBands; k++) { newDArray[newPixelOffset+k] = (ctable[k][index] & 0xff); } pixelOffset += pixelStride; newPixelOffset += newPixelStride; } scanlineOffset += scanlineStride; newScanlineOffset += newScanlineStride; } doubleDataArrays = new double[newNumBands][]; for (int i = 0; i < newNumBands; i++) { doubleDataArrays[i] = newDArray; } } break; } this.numBands = newNumBands; this.pixelStride = newPixelStride; this.scanlineStride = newScanlineStride; this.bandDataOffsets = newBandDataOffsets; this.bandOffsets = newBandDataOffsets; } } else if ((formatTagID & COPY_MASK) == COPIED && (formatTagID & EXPANSION_MASK) != UNEXPANDED && theColorModel != null) { this.numBands = theColorModel instanceof IndexColorModel ? theColorModel.getNumComponents() : raster.getSampleModel().getNumBands(); this.pixelStride = this.numBands; this.scanlineStride = rectWidth*numBands; this.bandOffsets = new int[numBands]; for (int i = 0; i < numBands; i++) { bandOffsets[i] = i; } this.bandDataOffsets = bandOffsets; Object odata = null; int offset = 0; int[] components = new int[theColorModel.getNumComponents()]; switch (formatTagID & DATATYPE_MASK) { case DataBuffer.TYPE_INT: int idata[] = new int[rectWidth*rectHeight*numBands]; intDataArrays = new int[numBands][]; for (int i = 0; i < numBands; i++) { intDataArrays[i] = idata; } odata = raster.getDataElements(rectX, rectY, null); offset = 0; // Workaround for bug in BytePackedRaster byte[] bdata = null; if (raster instanceof sun.awt.image.BytePackedRaster) { bdata = (byte[])odata; } for (int j = rectY; j < rectY+rectHeight; j++) { for (int i = rectX; i < rectX+rectWidth; i++) { if (bdata != null) { bdata[0] = (byte)raster.getSample(i, j, 0); } else { raster.getDataElements(i,j,odata); } theColorModel.getComponents(odata, components, 0); idata[offset] = components[0]; idata[offset+1] = components[1]; idata[offset+2] = components[2]; if (numBands > 3) { idata[offset+3] = components[3]; } offset += pixelStride; } } break; case DataBuffer.TYPE_FLOAT: float fdata[] = new float[rectWidth*rectHeight*numBands]; floatDataArrays = new float[numBands][]; for (int i = 0; i < numBands; i++) { floatDataArrays[i] = fdata; } odata = null; offset = 0; for (int j = rectY; j < rectY+rectHeight; j++) { for (int i = rectX; i < rectX+rectWidth; i++) { odata = raster.getDataElements(i,j,odata); theColorModel.getComponents(odata, components, 0); fdata[offset] = components[0]; fdata[offset+1] = components[1]; fdata[offset+2] = components[2]; if (numBands > 3) { fdata[offset+3] = components[3]; } offset += pixelStride; } } break; case DataBuffer.TYPE_DOUBLE: double ddata[] = new double[rectWidth*rectHeight*numBands]; doubleDataArrays = new double[numBands][]; for (int i = 0; i < numBands; i++) { doubleDataArrays[i] = ddata; } odata = null; offset = 0; for (int j = rectY; j < rectY+rectHeight; j++) { for (int i = rectX; i < rectX+rectWidth; i++) { odata = raster.getDataElements(i,j,odata); theColorModel.getComponents(odata, components, 0); ddata[offset] = components[0]; ddata[offset+1] = components[1]; ddata[offset+2] = components[2]; if (numBands > 3) { ddata[offset+3] = components[3]; } offset += pixelStride; } } break; } } else { // if ((formatTagID & COPY_MASK) == COPIED && // (formatTagID & EXPANSION_MASK) == UNEXPANDED) { // this has become a catchall case. Specifically for // Rasters with null colormodels. So we take out the // if as the boolean clause will get way complicated // otherwise. this.numBands = rft.getNumBands(); this.pixelStride = this.numBands; this.scanlineStride = rectWidth*numBands; this.bandDataOffsets = rft.getBandOffsets(); this.bandOffsets = this.bandDataOffsets; switch (formatTagID & DATATYPE_MASK) { case DataBuffer.TYPE_INT: int idata[] = raster.getPixels(rectX,rectY, rectWidth,rectHeight, (int[])null); intDataArrays = new int[numBands][]; for (int i = 0; i < numBands; i++) { intDataArrays[i] = idata; } break; case DataBuffer.TYPE_FLOAT: float fdata[] = raster.getPixels(rectX,rectY, rectWidth,rectHeight, (float[])null); floatDataArrays = new float[numBands][]; for (int i = 0; i < numBands; i++) { floatDataArrays[i] = fdata; } break; case DataBuffer.TYPE_DOUBLE: double ddata[] = raster.getPixels(rectX,rectY, rectWidth,rectHeight, (double[])null); doubleDataArrays = new double[numBands][]; for (int i = 0; i < numBands; i++) { doubleDataArrays[i] = ddata; } break; } } } /** * Returns the x coordinate of the upper-left corner of the * RasterAccessor's accessible area. */ public int getX() { return rectX; } /** * Returns the y coordinate of the upper-left corner of the * RasterAccessor's accessible area. */ public int getY() { return rectY; } /** Returns the width of the * RasterAccessor's accessible area. */ public int getWidth() { return rectWidth; } /** Returns the height of the * RasterAccessor's accessible area. */ public int getHeight() { return rectHeight; } /** Returns the numBands of the presented area. */ public int getNumBands() { return numBands; } /** * Whether the RasterAccessor represents binary data. * This occurs when the Raster has a * MultiPixelPackedSampleModel with a single band and * one bit per pixel. * * @since JAI 1.1 */ public boolean isBinary() { return (formatTagID & TAG_BINARY) == TAG_BINARY && ImageUtil.isBinary(raster.getSampleModel()); } /** * For the case of binary data (isBinary() returns * true), return the binary data as a packed byte array. * The data will be packed as eight bits per byte with no bit offset, * i.e., the first bit in each image line will be the left-most of the * first byte of the line. The line stride in bytes will be * (int)((getWidth()+7)/8). The length of the returned * array will be the line stride multiplied by getHeight() * * @return the binary data as a packed array of bytes with zero offset * of null if the data are not binary. * * @since JAI 1.1 */ public byte[] getBinaryDataArray() { if(binaryDataArray == null && isBinary()) { binaryDataArray = ImageUtil.getPackedBinaryData(raster, new Rectangle(rectX, rectY, rectWidth, rectHeight)); } return binaryDataArray; } /** * Returns the image data as a byte array. Non-null only if * getDataType = DataBuffer.TYPE_BYTE. * *

      For the case of binary data the corresponding instance variable * byteDataArrays will not be initialized until this * method or getByteDataArray(int b) is invoked. The * binary data will be returned as bytes with value 0 or 1. */ public byte[][] getByteDataArrays() { if(byteDataArrays == null && isBinary()) { byte[] bdata = ImageUtil.getUnpackedBinaryData(raster, new Rectangle(rectX, rectY, rectWidth, rectHeight)); byteDataArrays = new byte[][] {bdata}; } return byteDataArrays; } /** * Returns the image data as a byte array for a specific band. * Non-null only if getDataType = DataBuffer.TYPE_BYTE. */ public byte[] getByteDataArray(int b) { byte[][] bda = getByteDataArrays(); return (bda == null ? null : bda[b]); } /** * Returns the image data as a short array. Non-null only if * getDataType = DataBuffer.TYPE_USHORT or DataBuffer.TYPE_SHORT. */ public short[][] getShortDataArrays() { return shortDataArrays; } /** * Returns the image data as a short array for a specific band. * Non-null only if getDataType = DataBuffer.TYPE_USHORT or * DataBuffer.TYPE_SHORT. */ public short[] getShortDataArray(int b) { return (shortDataArrays == null ? null : shortDataArrays[b]); } /** * Returns the image data as an int array. Non-null only if * getDataType = DataBuffer.TYPE_INT. */ public int[][] getIntDataArrays() { return intDataArrays; } /** * Returns the image data as an int array for a specific band. * Non-null only if getDataType = DataBuffer.TYPE_INT. */ public int[] getIntDataArray(int b) { return (intDataArrays == null ? null : intDataArrays[b]); } /** * Returns the image data as a float array. Non-null only if * getDataType = DataBuffer.TYPE_FLOAT. */ public float[][] getFloatDataArrays() { return floatDataArrays; } /** * Returns the image data as a float array for a specific band. * Non-null only if getDataType = DataBuffer.TYPE_FLOAT. */ public float[] getFloatDataArray(int b) { return (floatDataArrays == null ? null : floatDataArrays[b]); } /** * Returns the image data as a double array. Non-null only if * getDataType = DataBuffer.TYPE_DOUBLE */ public double[][] getDoubleDataArrays() { return doubleDataArrays; } /** * Returns the image data as a double array for a specific band. * Non-null only if getDataType = DataBuffer.TYPE_DOUBLE */ public double[] getDoubleDataArray(int b) { return (doubleDataArrays == null ? null : doubleDataArrays[b]); } /** * Returns the image data as an Object for a specific band. * * @param b The index of the image band of interest. */ public Object getDataArray(int b) { Object dataArray = null; switch(getDataType()) { case DataBuffer.TYPE_BYTE: dataArray = getByteDataArray(b); break; case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_USHORT: dataArray = getShortDataArray(b); break; case DataBuffer.TYPE_INT: dataArray = getIntDataArray(b); break; case DataBuffer.TYPE_FLOAT: dataArray = getFloatDataArray(b); break; case DataBuffer.TYPE_DOUBLE: dataArray = getDoubleDataArray(b); break; default: dataArray = null; } return dataArray; } /** Returns the bandDataOffsets into the dataArrays. */ public int[] getBandOffsets() { return bandDataOffsets; } /** * Returns the offset of all band's samples from any * pixel offset. */ public int[] getOffsetsForBands() { return bandOffsets; } /** * Returns the offset of a specific band's first sample into the * DataBuffer including the DataBuffer's offset. */ public int getBandOffset(int b) { return bandDataOffsets[b]; } /** * Returns the offset of a specified band's sample from any * pixel offset. */ public int getOffsetForBand(int b) { return bandOffsets[b]; } /** * Returns the scanlineStride for the image data. * *

      For binary data this stride is applies to the arrays returned by * getByteDataArray() and getByteDataArrays() * if the data are accessed as bytes; it does not apply to the array * returned by getBinaryDataArray() when the data are * accessed as bits packed into bytes. */ public int getScanlineStride() { return scanlineStride; } /** Returns the pixelStride for the image data. */ public int getPixelStride() { return pixelStride; } /** * Returns the data type of the RasterAccessor object. Note that * this datatype is not necessarily the same data type as the * underlying raster. */ public int getDataType() { return formatTagID & DATATYPE_MASK; } /** * Returns true if the RasterAccessors's data is copied from it's * raster. */ public boolean isDataCopy() { return ((formatTagID & COPY_MASK) == COPIED); } /** * For the case of binary data (isBinary() returns * true), copy the binary data back into the * Raster of the RasterAccessor. If * this method is invoked in the non-binary case it does nothing. * Any bit offset in the original SampleModel will be * accounted for. * * @since JAI 1.1 */ // Note: ALL branches of this method have been tested. (bpb 10 May 2000) public void copyBinaryDataToRaster() { if(binaryDataArray == null || !isBinary()) { return; } ImageUtil.setPackedBinaryData(binaryDataArray, (WritableRaster)raster, new Rectangle(rectX, rectY, rectWidth, rectHeight)); } /** * Copies data back into the RasterAccessor's raster. Note that * the data is cast from the intermediate data format to * the raster's format. If clamping is needed, the call * clampDataArrays() method needs to be called before * calling the copyDataToRaster() method. * Note: the raster is expected to be writable - typically a * destination raster - otherwise, a run-time exception will occur. * *

      If the data are binary, then the target bit will be set if * and only if the corresponding byte is non-zero. */ public void copyDataToRaster() { if (isDataCopy()) { // Writeback should only be necessary on destRasters which // should be writable so this cast should succeed. WritableRaster wr = (WritableRaster)raster; switch (getDataType()) { case DataBuffer.TYPE_BYTE: // Note: ALL branches of this case have been tested. // (bpb 10 May 2000) if(!isBinary()) { // If this exception occurs then there is a logic // error within this accessor since the only case // wherein byte data should be COPIED is when the // data set is binary. throw new RuntimeException(JaiI18N.getString("RasterAccessor1")); } // This case only occurs for binary src and dst. ImageUtil.setUnpackedBinaryData(byteDataArrays[0], wr, new Rectangle(rectX, rectY, rectWidth, rectHeight)); break; case DataBuffer.TYPE_INT: wr.setPixels(rectX,rectY, rectWidth,rectHeight, intDataArrays[0]); break; case DataBuffer.TYPE_FLOAT: wr.setPixels(rectX,rectY, rectWidth,rectHeight, floatDataArrays[0]); break; case DataBuffer.TYPE_DOUBLE: wr.setPixels(rectX,rectY, rectWidth,rectHeight, doubleDataArrays[0]); break; } } } /** * Indicates if the RasterAccessor has a larger dynamic range than * the underlying Raster. Except in special cases, where the op * knows something special, this call will determine whether or * not clampDataArrays() needs to be called. */ public boolean needsClamping() { int bits[] = raster.getSampleModel().getSampleSize(); // Do we even need a clamp? We do if there's any band // of the source image stored in that's less than 32 bits // and is stored in a byte, short or int format. (The automatic // casts between floats/doubles and 32-bit ints in setPixel() // do what we want.) for (int i = 0; i < bits.length; i++) { if (bits[i] < 32) { return true; } } return false; } /** * Clamps data array values to a range that the underlying raster * can deal with. For example, if the underlying raster stores * data as bytes, but the samples are unpacked into integer arrays by * the RasterAccessor for an operation, the operation will * need to call clampDataArrays() so that the data in the int * arrays is restricted to the range 0..255 before a setPixels() * call is made on the underlying raster. Note that some * operations (for example, lookup) can guarantee that their * results don't need clamping so they can call * RasterAccessor.copyDataToRaster() without first calling this * function. */ public void clampDataArrays () { int bits[] = raster.getSampleModel().getSampleSize(); // Do we even need a clamp? We do if there's any band // of the source image stored in that's less than 32 bits // and is stored in a byte, short or int format. (The automatic // casts between floats/doubles and 32-bit ints in setPixel() // do what we want.) boolean needClamp = false; boolean uniformBitSize = true; int bitSize = bits[0]; for (int i = 0; i < bits.length; i++) { if (bits[i] < 32) { needClamp = true; } if (bits[i] != bitSize) { uniformBitSize = false; } } if (!needClamp) { return; } int dataType = raster.getDataBuffer().getDataType(); double hiVals[] = new double[bits.length]; double loVals[] = new double[bits.length]; if (dataType == DataBuffer.TYPE_USHORT && uniformBitSize && bits[0] == 16) { for (int i = 0; i < bits.length; i++) { hiVals[i] = (double)0xFFFF; loVals[i] = (double)0; } } else if (dataType == DataBuffer.TYPE_SHORT && uniformBitSize && bits[0] == 16) { for (int i = 0; i < bits.length; i++) { hiVals[i] = (double)Short.MAX_VALUE; loVals[i] = (double)Short.MIN_VALUE; } } else if (dataType == DataBuffer.TYPE_INT && uniformBitSize && bits[0] == 32) { for (int i = 0; i < bits.length; i++) { hiVals[i] = (double)Integer.MAX_VALUE; loVals[i] = (double)Integer.MIN_VALUE; } } else { for (int i = 0; i < bits.length; i++) { hiVals[i] = (double)((1 << bits[i]) - 1); loVals[i] = (double)0; } } clampDataArray(hiVals,loVals); } private void clampDataArray(double hiVals[], double loVals[]) { switch (getDataType()) { case DataBuffer.TYPE_INT: clampIntArrays(toIntArray(hiVals),toIntArray(loVals)); break; case DataBuffer.TYPE_FLOAT: clampFloatArrays(toFloatArray(hiVals),toFloatArray(loVals)); break; case DataBuffer.TYPE_DOUBLE: clampDoubleArrays(hiVals,loVals); break; } } private int[] toIntArray(double vals[]) { int returnVals[] = new int[vals.length]; for (int i = 0; i < vals.length; i++) { returnVals[i] = (int)vals[i]; } return returnVals; } private float[] toFloatArray(double vals[]) { float returnVals[] = new float[vals.length]; for (int i = 0; i < vals.length; i++) { returnVals[i] = (float)vals[i]; } return returnVals; } private void clampIntArrays(int hiVals[], int loVals[]) { int width = rectWidth; int height = rectHeight; for (int k = 0; k < numBands; k++) { int data[] = intDataArrays[k]; int scanlineOffset = bandDataOffsets[k]; int hiVal = hiVals[k]; int loVal = loVals[k]; for (int j = 0; j < height; j++) { int pixelOffset = scanlineOffset; for (int i = 0; i < width; i++) { int tmp = data[pixelOffset]; if (tmp < loVal) { data[pixelOffset] = loVal; } else if (tmp > hiVal) { data[pixelOffset] = hiVal; } pixelOffset += pixelStride; } scanlineOffset += scanlineStride; } } } private void clampFloatArrays(float hiVals[], float loVals[]) { int width = rectWidth; int height = rectHeight; for (int k = 0; k < numBands; k++) { float data[] = floatDataArrays[k]; int scanlineOffset = bandDataOffsets[k]; float hiVal = hiVals[k]; float loVal = loVals[k]; for (int j = 0; j < height; j++) { int pixelOffset = scanlineOffset; for (int i = 0; i < width; i++) { float tmp = data[pixelOffset]; if (tmp < loVal) { data[pixelOffset] = loVal; } else if (tmp > hiVal) { data[pixelOffset] = hiVal; } pixelOffset += pixelStride; } scanlineOffset += scanlineStride; } } } private void clampDoubleArrays(double hiVals[], double loVals[]) { int width = rectWidth; int height = rectHeight; for (int k = 0; k < numBands; k++) { double data[] = doubleDataArrays[k]; int scanlineOffset = bandDataOffsets[k]; double hiVal = hiVals[k]; double loVal = loVals[k]; for (int j = 0; j < height; j++) { int pixelOffset = scanlineOffset; for (int i = 0; i < width; i++) { double tmp = data[pixelOffset]; if (tmp < loVal) { data[pixelOffset] = loVal; } else if (tmp > hiVal) { data[pixelOffset] = hiVal; } pixelOffset += pixelStride; } scanlineOffset += scanlineStride; } } } } jai-core-1.1.4/src/share/classes/javax/media/jai/PlanarImage.java0000644000175000017500000040261510444606314024376 0ustar mathieumathieu/* * $RCSfile: PlanarImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.3 $ * $Date: 2006-06-16 19:55:56 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Graphics; import java.awt.Image; import java.awt.Point; import java.awt.Rectangle; import java.awt.Transparency; import java.awt.color.ColorSpace; import java.awt.image.BufferedImage; import java.awt.image.ComponentSampleModel; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferShort; import java.awt.image.DataBufferUShort; import java.awt.image.DataBufferInt; import java.awt.image.DirectColorModel; import java.awt.image.IndexColorModel; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.SinglePixelPackedSampleModel; import java.awt.image.WritableRaster; import java.awt.image.WritableRenderedImage; import java.beans.PropertyChangeListener; import java.lang.ref.WeakReference; import java.util.Collections; import java.util.Enumeration; import java.util.Hashtable; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Vector; import javax.media.jai.RasterFactory; import com.sun.media.jai.util.DataBufferUtils; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.util.JDKWorkarounds; import com.sun.media.jai.util.PropertyUtil; import javax.media.jai.util.CaselessStringKey; /** * A RenderedImage is expressed as a collection of pixels. * A pixel is defined as a 1-by-1 square; its origin is the top-left * corner of the square (0, 0), and its energy center is located at the * center of the square (0.5, 0.5). * *

      This is the fundamental base class of Java Advanced Imaging (JAI) * that represents a two-dimensional RenderedImage. * *

      This class provides a home for the information and functionalities * common to all the JAI classes that implement the * RenderedImage interface, such as the image's layout, * sources, properties, etc. The image layout, sources, and properties * may be set either at construction or subsequently using one of the * mutator methods supplied for the respective attribute. In general * this class does not perform sanity checking on the state of its * variables so it is very important that subclasses set them correctly. * This is of particular importance with respect to the image layout. * *

      The layout of a PlanarImage is specified by variables * minX, minY, width, * height, tileGridXOffset, * tileGridYOffset, tileWidth, * tileHeight, sampleModel, and * colorModel. These variables do not have any default settings * so subclasses must set the appropriate ones at construction via the * ImageLayout argument or subsequently using * setImageLayout(). Otherwise, unexpected errors may occur. * Although these variables have protected access, it is * strongly recommended that subclasses not set the values of these variables * directly but rather via setImageLayout() which performs a * certain few initializations based on the layout values. The variables * are defined to have protected access for convenience. * *

      A PlanarImage may have any number of * RenderedImage sources or no source at all. * *

      All non-JAI RenderedImage instances must be * converted into PlanarImages by means of the * RenderedImageAdapter and * WritableRenderedImageAdapter classes. The * wrapRenderedImage method provides a convenient interface * to both add a wrapper and take a snapshot if the image is writable. * All of the PlanarImage constructors perform this wrapping * automatically. Images that already extend PlanarImage * will be returned unchanged by wrapRenderedImage; that * is, it is idempotent. * *

      Going in the other direction, existing code that makes use of * the RenderedImage interface will be able to use * PlanarImages directly, without any changes or * recompilation. Therefore, within JAI, two-dimensional images are * returned from methods as PlanarImages, even though * incoming RenderedImages are accepted as arguments directly. * *

      A PlanarImage may also have any number of properties * of any type. If or how a property is used depends on the individual * subclass. This class only stores the property information. If any * PropertyChangeListeners are registered they will receive * a PropertySourceChangeEvent for each change in an image * property. * *

      In general, methods in this class are implemented such that they * use any class variables directly instead of through their accessors for * performance reasons. Subclasses need to be careful when overriding this * class' variable accessors that other appropriate methods are overriden * as well. * *

      PlanarImage implements a createSnapshot * method that produces a new, immutable image with a copy of this * image's current contents. In practice, this snapshot is only a * virtual copy; it is managed by the SnapshotImage class * in such a way as to minimize copying and memory footprint generally. * Multiple calls to createSnapshot make use of a single * SnapshotImage per PlanarImage in order to * centralize version management. These mechanisms are transparent to * the API user and are discussed here only for edification. * *

      The source and sink lists have the effect of creating a graph * structure between a set of PlanarImages. Note that * the practice of making such bidirectional connections between * images means that the garbage collector will not inform us when all * user references to a node are lost, since there will still be * internal references up until the point where the entire graph is * detached from user space. A solution is available in the form of * Reference Objects; see * http://java.sun.com/j2se/1.5.0/docs/guide/refobs/ for * more information. These classes include weak references * that allow the Garbage Collector (GC) to collect objects they * reference, setting the reference to null in the process. * *

      The reference problem requires us to be careful about how we * define the reachability of directed acyclic graph (DAG) nodes. * If we were to allow nodes to be reached by arbitrary graph traversal, * we would be unable to garbage collect any subgraphs of an active * graph at all since any node may be reached from any other. Instead, * we define the set of reachable nodes as those that may be accessed * directly from a reference in user code, or that are the source (not * sink) of a reachable node. Reachable nodes are always accessible, * whether they are reached by traversing upwards or downwards in the DAG. * *

      A DAG may also contain nodes that are not reachable, that is, * they require a downward traversal at some point. For example, * assume a node A is reachable, and a call to * A.getSinks() yields a Vector containing a * reference to a previously unreachable node B. The * node B naturally becomes reachable by virtue of the * new user reference pointing to it. However, if the user were to * relinquish that reference, the node might be garbage collected, and * a future call to A.getSinks() might no longer include * B in its return value. * *

      Because the set of sinks of a node is inherently unstable, only * the getSinks method is provided for external access to * the sink vector at a node. A hypothetical method such as * getSink or getNumSinks would produce * confusing results should a sink be garbage collected between that * call and a subsequent call to getSinks. * * @see java.awt.image.RenderedImage * @see java.lang.ref.Reference * @see java.lang.ref.WeakReference * @see ImageJAI * @see OpImage * @see RenderedImageAdapter * @see SnapshotImage * @see TiledImage */ public abstract class PlanarImage implements ImageJAI, RenderedImage { /** The UID for this image. */ private Object UID; /** * The X coordinate of the image's top-left pixel. */ protected int minX; /** * The Y coordinate of the image's top-left pixel. */ protected int minY; /** * The image's width in number of pixels. */ protected int width; /** * The image's height in number of pixels. */ protected int height; /** The image's bounds. */ // Initialize to an empty Rectangle so this object may always // be used as a mutual exclusion lock in getBounds(). private Rectangle bounds = new Rectangle(); /** * The X coordinate of the top-left pixel of tile (0, 0). */ protected int tileGridXOffset; /** * The Y coordinate of the top-left pixel of tile (0, 0). */ protected int tileGridYOffset; /** * The width of a tile in number of pixels. */ protected int tileWidth; /** * The height of a tile in number of pixels. */ protected int tileHeight; /** * The image's SampleModel. */ protected SampleModel sampleModel = null; /** * The image's ColorModel. */ protected ColorModel colorModel = null; /** * A TileFactory for use in * {@link #createWritableRaster(SampleModel,Point)}. * This field will be null unless initialized via the * configuration properties passed to * {@link #PlanarImage(ImageLayout,Vector,Map)}. * * @since JAI 1.1.2 */ protected TileFactory tileFactory = null; /** The PlanarImage sources of the image. */ private Vector sources = null; /** A set of WeakReferences to the sinks of the image. */ private Vector sinks = null; /** * A helper object to manage firing events. * * @since JAI 1.1 */ protected PropertyChangeSupportJAI eventManager = null; /** * A helper object to manage the image properties. * * @since JAI 1.1 */ protected WritablePropertySourceImpl properties = null; /** * A SnapshotImage that will centralize tile * versioning for this image. */ private SnapshotImage snapshot = null; /** A WeakReference to this image. */ private WeakReference weakThis; /** Cache of registered TileComputationListeners. */ private Set tileListeners = null; private boolean disposed = false; /** Array copy size, used by "cobble" methods. */ private static final int MIN_ARRAYCOPY_SIZE = 64; /** * The default constructor. * *

      The eventManager and properties * helper fields are initialized by this constructor; no other * non-private fields are set. */ public PlanarImage() { this.weakThis = new WeakReference(this); // Create an event manager. eventManager = new PropertyChangeSupportJAI(this); // Copy the properties by reference. this.properties = new WritablePropertySourceImpl(null, null, eventManager); this.UID = ImageUtil.generateID(this); } /** * Constructor. * *

      The image's layout is encapsulated in the layout * argument. Note that no verification is performed to determine whether * the image layout has been set either at construction or subsequently. * *

      This constructor does not provide any default settings for * the layout variables so all of those that will be used later must * be set in the layout argument or subsequently via * setImageLayout() before the values are used. * Otherwise, unexpected errors may occur. *

      If the SampleModel is non-null and the * supplied tile dimensions are positive, then if the dimensions of the * supplied SampleModel differ from the tile dimensions, a * new SampleModel will be created for the image from the * supplied SampleModel but with dimensions equal to those * of a tile. * *

      If both the SampleModel and the ColorModel * in the supplied ImageLayout are non-null * they will be tested for compatibility. If the test fails an * exception will be thrown. The test is that * *

        *
      • ColorModel.isCompatibleSampleModel() invoked on * the SampleModel must return true, and *
      • if the ColorModel is a * ComponentColorModel then: *
          *
        • the number of bands of the SampleModel must equal * the number of components of the ColorModel, and *
        • SampleModel.getSampleSize(b) >= ColorModel.getComponentSize(b) * for all bands b. *
        *
      * *

      The sources parameter contains a list of immediate * sources of this image none of which may be null. All * RenderedImages in the list are automatically converted * into PlanarImages when necessary. If this image has * no source, this argument should be null. * *

      The properties parameter contains a mapping of image * properties. All map entries which have a key which is either a * String or a CaselessStringKey are interpreted * as image properties and will be copied to the property database of * this image. This parameter may be null. * *

      If a {@link TileFactory}-valued mapping of the key * {@link JAI#KEY_TILE_FACTORY} is present in * properties, then set the instance variable * tileFactory to the specified TileFactory. * This TileFactory will be used by * {@link #createWritableRaster(SampleModel,Point)} to create * Rasters, notably in {@link #getData(Rectangle)}, * {@link #copyData(WritableRaster)}, and * {@link #getExtendedData(Rectangle,BorderExtender)}.

      * *

      The event and property helper fields are initialized by this * constructor. * * @param layout The layout of this image or null. * @param sources The immediate sources of this image or * null. * @param properties A Map containing the properties of * this image or null. * * @throws IllegalArgumentException if a * ColorModel is specified in the layout and it is * incompatible with the SampleModel * @throws IllegalArgumentException If sources * is non-null and any object in * sources is null. * * @since JAI 1.1 */ public PlanarImage(ImageLayout layout, Vector sources, Map properties) { this(); // Set the image layout. if(layout != null) { setImageLayout(layout); } // Set the image sources. All source Vector elements must be non-null. // If any source is a RenderedImage it is converted to a PlanarImage // before being set. if (sources != null) { setSources(sources); } if(properties != null) { // Add properties from parameter. this.properties.addProperties(properties); // Set tileFactory if key present. if(properties.containsKey(JAI.KEY_TILE_FACTORY)) { Object factoryValue = properties.get(JAI.KEY_TILE_FACTORY); // Check the class type in case 'properties' is not // an instance of RenderingHints. if(factoryValue instanceof TileFactory) { this.tileFactory = (TileFactory)factoryValue; } } } } /** * Sets the image bounds, tile grid layout, * SampleModel and ColorModel using * values from an ImageLayout object. * *

      If either of the tile dimensions is not set in the passed in * ImageLayout object, then the tile dimension in question * will be set to the corresponding image dimension. * *

      If either of the tile grid offsets is not set in the passed in * ImageLayout object, then the tile grid offset in * question will be set to 0. The same is true for the minX * , minY, width and height * fields, if no value is set in the passed in ImageLayout * object, they will be set to 0. * *

      If the SampleModel is non-null and the * supplied tile dimensions are positive, then if the dimensions of the * supplied SampleModel differ from the tile dimensions, a * new SampleModel will be created for the image from the * supplied SampleModel but with dimensions equal to those * of a tile. * *

      If both the SampleModel and the ColorModel * in the supplied ImageLayout are non-null * they will be tested for compatibility. If the test fails an * exception will be thrown. The test is that * *

        *
      • ColorModel.isCompatibleSampleModel() invoked on * the SampleModel must return true, and *
      • if the ColorModel is a * ComponentColorModel then: *
          *
        • the number of bands of the SampleModel must equal * the number of components of the ColorModel, and *
        • SampleModel.getSampleSize(b) >= ColorModel.getComponentSize(b) * for all bands b. *
        *
      * * @param layout an ImageLayout that is used to selectively * override the image's layout, SampleModel, * and ColorModel. Only valid fields, i.e., * those for which ImageLayout.isValid() returns * true for the appropriate mask, are used. * * @throws IllegalArgumentException if layout * is null. * @throws IllegalArgumentException if a * ColorModel is specified in the layout and it is * incompatible with the SampleModel * * @since JAI 1.1 */ protected void setImageLayout(ImageLayout layout) { if(layout == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } else { // Set image bounds. if(layout.isValid(ImageLayout.MIN_X_MASK)) { minX = layout.getMinX(null); } if(layout.isValid(ImageLayout.MIN_Y_MASK)) { minY = layout.getMinY(null); } if(layout.isValid(ImageLayout.WIDTH_MASK)) { width = layout.getWidth(null); } if(layout.isValid(ImageLayout.HEIGHT_MASK)) { height = layout.getHeight(null); } // Set tile grid parameters. if(layout.isValid(ImageLayout.TILE_GRID_X_OFFSET_MASK)) { tileGridXOffset = layout.getTileGridXOffset(null); } if(layout.isValid(ImageLayout.TILE_GRID_Y_OFFSET_MASK)) { tileGridYOffset = layout.getTileGridYOffset(null); } if(layout.isValid(ImageLayout.TILE_WIDTH_MASK)) { tileWidth = layout.getTileWidth(null); } else { tileWidth = width; } if(layout.isValid(ImageLayout.TILE_HEIGHT_MASK)) { tileHeight = layout.getTileHeight(null); } else { tileHeight = height; } // Set SampleModel. if(layout.isValid(ImageLayout.SAMPLE_MODEL_MASK)) { sampleModel = layout.getSampleModel(null); } // Make the SampleModel dimensions equal to those of a tile. if(sampleModel != null && tileWidth > 0 && tileHeight > 0 && (sampleModel.getWidth() != tileWidth || sampleModel.getHeight() != tileHeight)) { sampleModel = sampleModel.createCompatibleSampleModel(tileWidth, tileHeight); } // Set ColorModel. if(layout.isValid(ImageLayout.COLOR_MODEL_MASK)) { colorModel = layout.getColorModel(null); } if(colorModel != null && sampleModel != null) { if(!JDKWorkarounds.areCompatibleDataModels(sampleModel, colorModel)) { throw new IllegalArgumentException(JaiI18N.getString("PlanarImage5")); /* XXX Begin debugging statements: to be deleted System.err.println("\n----- ERROR: "+ JaiI18N.getString("PlanarImage5")); System.err.println(getClass().getName()); System.err.println(sampleModel.getClass().getName()+": "+ sampleModel); System.err.println("Transfer type = "+ sampleModel.getTransferType()); System.err.println(colorModel.getClass().getName()+": "+ colorModel); System.err.println(""); XXX End debugging statements */ } } } } /** * Wraps an arbitrary RenderedImage to produce a * PlanarImage. PlanarImage adds * various properties to an image, such as source and sink vectors * and the ability to produce snapshots, that are necessary for * JAI. * *

      If the image is already a PlanarImage, it is * simply returned unchanged. Otherwise, the image is wrapped in * a RenderedImageAdapter or * WritableRenderedImageAdapter as appropriate. * * @param image The RenderedImage to be converted into * a PlanarImage. * * @return A PlanarImage containing image's * pixel data. * * @throws IllegalArgumentException If image is * null. */ public static PlanarImage wrapRenderedImage(RenderedImage image) { if (image == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (image instanceof PlanarImage) { return (PlanarImage)image; } else if (image instanceof WritableRenderedImage) { return new WritableRenderedImageAdapter( (WritableRenderedImage)image); } else { return new RenderedImageAdapter(image); } } /** * Creates a snapshot, that is, a virtual copy of the image's * current contents. If the image is not a * WritableRenderedImage, it is returned unchanged. * Otherwise, a SnapshotImage is created and the * result of calling its createSnapshot() is * returned. * * @return A PlanarImage with immutable contents. */ public PlanarImage createSnapshot() { if (this instanceof WritableRenderedImage) { if (snapshot == null) { synchronized (this) { snapshot = new SnapshotImage(this); } } return snapshot.createSnapshot(); } else { return this; } } /** * Returns the X coordinate of the left-most column of the image. * The default implementation returns the corresponding instance variable. */ public int getMinX() { return minX; } /** * Returns the X coordinate of the column immediately to the right * of the right-most column of the image. * *

      This method is implemented in terms of getMinX() * and getWidth() so that subclasses which override * those methods do not need to override this one. */ public int getMaxX() { return getMinX() + getWidth(); } /** * Returns the Y coordinate of the top-most row of the image. * The default implementation returns the corresponding instance variable. */ public int getMinY() { return minY; } /** * Returns the Y coordinate of the row immediately below the * bottom-most row of the image. * *

      This method is implemented in terms of getMinY() * and getHeight() so that subclasses which override * those methods do not need to override this one. */ public int getMaxY() { return getMinY() + getHeight(); } /** * Returns the width of the image in number of pixels. * The default implementation returns the corresponding instance variable. */ public int getWidth() { return width; } /** * Returns the height of the image in number of pixels. * The default implementation returns the corresponding instance variable. */ public int getHeight() { return height; } /** * Retrieve the number of image bands. Note that this will not equal * the number of color components if the image has an * IndexColorModel. This is equivalent to calling * getSampleModel().getNumBands(). * * @since JAI 1.1 */ public int getNumBands() { return getSampleModel().getNumBands(); } /** * Returns the image's bounds as a Rectangle. * *

      The image's bounds are defined by the values returned by * getMinX(), getMinY(), * getWidth(), and getHeight(). * A Rectangle is created based on these four methods and * cached in this class. Each time that this method is invoked, the * bounds of this Rectangle are updated with the values * returned by the four aforementioned accessors. * *

      Because this method returns the bounds variable * by reference, the caller should not change the settings of the * Rectangle. Otherwise, unexpected errors may occur. * Likewise, if the caller expects this variable to be immutable it * should clone the returned Rectangle if there is any * possibility that it might be changed by the PlanarImage. * This may generally occur only for instances of RenderedOp. */ public Rectangle getBounds() { synchronized(bounds) { bounds.setBounds(getMinX(), getMinY(), getWidth(), getHeight()); } return bounds; } /** * Returns the X coordinate of the top-left pixel of tile (0, 0). * The default implementation returns the corresponding instance variable. */ public int getTileGridXOffset() { return tileGridXOffset; } /** * Returns the Y coordinate of the top-left pixel of tile (0, 0). * The default implementation returns the corresponding instance variable. */ public int getTileGridYOffset() { return tileGridYOffset; } /** * Returns the width of a tile of this image in number of pixels. * The default implementation returns the corresponding instance variable. */ public int getTileWidth() { return tileWidth; } /** * Returns the height of a tile of this image in number of pixels. * The default implementation returns the corresponding instance variable. */ public int getTileHeight() { return tileHeight; } /** * Returns the horizontal index of the left-most column of tiles. * *

      This method is implemented in terms of the static method * XToTileX() applied to the values returned by primitive * layout accessors and so does not need to be implemented by subclasses. */ public int getMinTileX() { return XToTileX(getMinX(), getTileGridXOffset(), getTileWidth()); } /** * Returns the horizontal index of the right-most column of tiles. * *

      This method is implemented in terms of the static method * XToTileX() applied to the values returned by primitive * layout accessors and so does not need to be implemented by subclasses. */ public int getMaxTileX() { return XToTileX(getMinX() + getWidth() - 1, getTileGridXOffset(), getTileWidth()); } /** * Returns the number of tiles along the tile grid in the * horizontal direction. * *

      This method is implemented in terms of the static method * XToTileX() applied to the values returned by primitive * layout accessors and so does not need to be implemented by subclasses. */ public int getNumXTiles() { int x = getMinX(); int tx = getTileGridXOffset(); int tw = getTileWidth(); return XToTileX(x + getWidth() - 1, tx, tw) - XToTileX(x, tx, tw) + 1; } /** * Returns the vertical index of the top-most row of tiles. * *

      This method is implemented in terms of the static method * YToTileY() applied to the values returned by primitive * layout accessors and so does not need to be implemented by subclasses. */ public int getMinTileY() { return YToTileY(getMinY(), getTileGridYOffset(), getTileHeight()); } /** * Returns the vertical index of the bottom-most row of tiles. * *

      This method is implemented in terms of the static method * YToTileY() applied to the values returned by primitive * layout accessors and so does not need to be implemented by subclasses. */ public int getMaxTileY() { return YToTileY(getMinY() + getHeight() - 1, getTileGridYOffset(), getTileHeight()); } /** * Returns the number of tiles along the tile grid in the vertical * direction. * *

      This method is implemented in terms of the static method * YToTileY() applied to the values returned by primitive * layout accessors and so does not need to be implemented by subclasses. */ public int getNumYTiles() { int y = getMinY(); int ty = getTileGridYOffset(); int th = getTileHeight(); return YToTileY(y + getHeight() - 1, ty, th) - YToTileY(y, ty, th) + 1; } /** * Converts a pixel's X coordinate into a horizontal tile index * relative to a given tile grid layout specified by its X offset * and tile width. * *

      If tileWidth < 0, the results of this method * are undefined. If tileWidth == 0, an * ArithmeticException will be thrown. * * @throws ArithmeticException If tileWidth == 0. */ public static int XToTileX(int x, int tileGridXOffset, int tileWidth) { x -= tileGridXOffset; if (x < 0) { x += 1 - tileWidth; // force round to -infinity (ceiling) } return x/tileWidth; } /** * Converts a pixel's Y coordinate into a vertical tile index * relative to a given tile grid layout specified by its Y offset * and tile height. * *

      If tileHeight < 0, the results of this method * are undefined. If tileHeight == 0, an * ArithmeticException will be thrown. * * @throws ArithmeticException If tileHeight == 0. */ public static int YToTileY(int y, int tileGridYOffset, int tileHeight) { y -= tileGridYOffset; if (y < 0) { y += 1 - tileHeight; // force round to -infinity (ceiling) } return y/tileHeight; } /** * Converts a pixel's X coordinate into a horizontal tile index. * No attempt is made to detect out-of-range coordinates. * *

      This method is implemented in terms of the static method * XToTileX() applied to the values returned by primitive * layout accessors and so does not need to be implemented by subclasses. * * @param x the X coordinate of a pixel. * * @return the X index of the tile containing the pixel. * * @throws ArithmeticException If the tile width of this image is 0. */ public int XToTileX(int x) { return XToTileX(x, getTileGridXOffset(), getTileWidth()); } /** * Converts a pixel's Y coordinate into a vertical tile index. No * attempt is made to detect out-of-range coordinates. * *

      This method is implemented in terms of the static method * YToTileY() applied to the values returned by primitive * layout accessors and so does not need to be implemented by subclasses. * * @param y the Y coordinate of a pixel. * * @return the Y index of the tile containing the pixel. * * @throws ArithmeticException If the tile height of this image is 0. */ public int YToTileY(int y) { return YToTileY(y, getTileGridYOffset(), getTileHeight()); } /** * Converts a horizontal tile index into the X coordinate of its * upper left pixel relative to a given tile grid layout specified * by its X offset and tile width. */ public static int tileXToX(int tx, int tileGridXOffset, int tileWidth) { return tx * tileWidth + tileGridXOffset; } /** * Converts a vertical tile index into the Y coordinate of * its upper left pixel relative to a given tile grid layout * specified by its Y offset and tile height. */ public static int tileYToY(int ty, int tileGridYOffset, int tileHeight) { return ty * tileHeight + tileGridYOffset; } /** * Converts a horizontal tile index into the X coordinate of its * upper left pixel. No attempt is made to detect out-of-range * indices. * *

      This method is implemented in terms of the static method * tileXToX() applied to the values returned by primitive * layout accessors and so does not need to be implemented by subclasses. * * @param tx the horizontal index of a tile. * @return the X coordinate of the tile's upper left pixel. */ public int tileXToX(int tx) { return tileXToX(tx, getTileGridXOffset(), getTileWidth()); } /** * Converts a vertical tile index into the Y coordinate of its * upper left pixel. No attempt is made to detect out-of-range * indices. * *

      This method is implemented in terms of the static method * tileYToY() applied to the values returned by primitive * layout accessors and so does not need to be implemented by subclasses. * * @param ty the vertical index of a tile. * @return the Y coordinate of the tile's upper left pixel. */ public int tileYToY(int ty) { return tileYToY(ty, getTileGridYOffset(), getTileHeight()); } /** * Returns a Rectangle indicating the active area of * a given tile. The Rectangle is defined as the * intersection of the tile area and the image bounds. No attempt * is made to detect out-of-range indices; tile indices lying * completely outside of the image will result in returning an * empty Rectangle (width and/or height less than or * equal to 0). * *

      This method is implemented in terms of the primitive layout * accessors and so does not need to be implemented by subclasses. * * @param tileX The X index of the tile. * @param tileY The Y index of the tile. * * @return A Rectangle */ public Rectangle getTileRect(int tileX, int tileY) { return getBounds().intersection(new Rectangle( tileXToX(tileX), tileYToY(tileY), getTileWidth(), getTileHeight())); } /** * Returns the SampleModel of the image. * The default implementation returns the corresponding instance variable. */ public SampleModel getSampleModel() { return sampleModel; } /** * Returns the ColorModel of the image. * The default implementation returns the corresponding instance variable. */ public ColorModel getColorModel() { return colorModel; } /** * Returns a ComponentColorModel created based on * the indicated dataType and numBands. * *

      The dataType must be one of DataBuffer's * TYPE_BYTE, TYPE_USHORT, * TYPE_INT, TYPE_FLOAT, or * TYPE_DOUBLE. * *

      The numBands may range from 1 to 4, with the * following ColorSpace and alpha settings: *

        *
      • numBands = 1: CS_GRAY without alpha; *
      • numBands = 2: CS_GRAY with alpha; *
      • numBands = 3: CS_sRGB without alpha; *
      • numBands = 4: CS_sRGB with alpha. *
      * The transparency is set to Transparency.TRANSLUCENT if * alpha is used and to Transparency.OPAQUE otherwise. * *

      All other inputs result in a null return value. * * @param dataType The data type of the ColorModel. * @param numBands The number of bands of the pixels the created * ColorModel is going to work with. * * @since JAI 1.1 */ public static ColorModel getDefaultColorModel(int dataType, int numBands) { if (dataType < DataBuffer.TYPE_BYTE || dataType == DataBuffer.TYPE_SHORT || dataType > DataBuffer.TYPE_DOUBLE || numBands < 1 || numBands > 4) { return null; } ColorSpace cs = numBands <= 2 ? ColorSpace.getInstance(ColorSpace.CS_GRAY) : ColorSpace.getInstance(ColorSpace.CS_sRGB); boolean useAlpha = (numBands == 2) || (numBands == 4); int transparency = useAlpha ? Transparency.TRANSLUCENT : Transparency.OPAQUE; return RasterFactory.createComponentColorModel(dataType, cs, useAlpha, false, transparency); } /** * Creates a ColorModel that may be used with the * specified SampleModel. If a suitable * ColorModel cannot be found, this method returns * null. * *

      Suitable ColorModels are guaranteed to exist * for all instances of ComponentSampleModel whose * dataType is not DataBuffer.TYPE_SHORT * and with no more than 4 bands. A ComponentColorModel * of either type CS_GRAY or CS_sRGB is returned. * *

      For 1- and 3- banded SampleModels, the returned * ColorModel will be opaque. For 2- and 4-banded * SampleModels, the output will use alpha transparency. * *

      Additionally, an instance of DirectColorModel * will be created for instances of * SinglePixelPackedSampleModel with no more than 4 bands. * *

      Finally, an instance of IndexColorModel * will be created for instances of * MultiPixelPackedSampleModel with a single band and a * pixel bit stride of unity. This represents the case of binary data. * *

      This method is intended as an useful utility for the creation * of simple ColorModels for some common cases. * In more complex situations, it may be necessary to instantiate * the appropriate ColorModels directly. * * @return An instance of ColorModel that is suitable for * the supplied SampleModel, or null. * * @throws IllegalArgumentException If sm is * null. */ public static ColorModel createColorModel(SampleModel sm) { if(sm == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } int bands = sm.getNumBands(); if (bands < 1 || bands > 4) { return null; } if (sm instanceof ComponentSampleModel) { return getDefaultColorModel(sm.getDataType(), bands); } else if (sm instanceof SinglePixelPackedSampleModel) { SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel)sm; int[] bitMasks = sppsm.getBitMasks(); int rmask = 0; int gmask = 0; int bmask = 0; int amask = 0; int numBands = bitMasks.length; if (numBands <= 2) { rmask = gmask = bmask = bitMasks[0]; if (numBands == 2) { amask = bitMasks[1]; } } else { rmask = bitMasks[0]; gmask = bitMasks[1]; bmask = bitMasks[2]; if (numBands == 4) { amask = bitMasks[3]; } } int[] sampleSize = sppsm.getSampleSize(); int bits = 0; for (int i = 0; i < sampleSize.length; i++) { bits += sampleSize[i]; } return new DirectColorModel(bits, rmask, gmask, bmask, amask); } else if (ImageUtil.isBinary(sm)) { byte[] comp = new byte[] { (byte)0x00, (byte)0xFF }; return new IndexColorModel(1, 2, comp, comp, comp); } else { // unable to create an suitable ColorModel return null; } } /** * Returns the value of the instance variable tileFactory. * * @since JAI 1.1.2 */ public TileFactory getTileFactory() { return tileFactory; } /** * Returns the number of immediate PlanarImage sources * this image has. If this image has no source, this method returns * 0. */ public int getNumSources() { return sources == null ? 0 : sources.size(); } /** * Returns this image's immediate source(s) in a Vector. * If this image has no source, this method returns null. */ public Vector getSources() { if (getNumSources() == 0) { return null; } else { synchronized (sources) { return (Vector)sources.clone(); } } } /** * Returns the immediate source indicated by the index. If there * is no source corresponding to the specified index, this method * throws an exception. * * @param index The index of the desired source. * * @return A PlanarImage source. * * @throws ArrayIndexOutOfBoundsException If this image has no * immediate source, or if the index is negative or greater * than the maximum source index. * * @deprecated as of JAI 1.1. Use getSourceImage(). * @see PlanarImage#getSourceImage(int) */ public PlanarImage getSource(int index) { if (sources == null) { throw new ArrayIndexOutOfBoundsException( JaiI18N.getString("PlanarImage0")); } synchronized (sources) { return (PlanarImage)sources.get(index); } } /** * Sets the list of sources from a given List of * PlanarImages. All of the existing sources are * discarded. Any RenderedImage sources in the supplied * list are wrapped using wrapRenderedImage(). The list * of sinks of each prior PlanarImage source and of each * current unwrapped PlanarImage source is adjusted as * necessary such that this image is a sink of all such current sources * but is removed as a sink of all such prior sources which are not * also current. * * @param sourceList a List of PlanarImages. * * @throws IllegalArgumentException If sourceList is * null or contains any null elements. */ protected void setSources(List sourceList) { if(sourceList == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } int size = sourceList.size(); synchronized (this) { if(sources != null) { // Remove this image as a sink of prior PlanarImage sources. Iterator it = sources.iterator(); while(it.hasNext()) { Object src = it.next(); if(src instanceof PlanarImage) { ((PlanarImage)src).removeSink(this); } } } sources = new Vector(size); } synchronized (sources) { for (int i = 0; i < size; i++) { Object sourceElement = sourceList.get(i); if(sourceElement == null) { throw new IllegalArgumentException(JaiI18N.getString("PlanarImage7")); } sources.add(sourceElement instanceof RenderedImage ? wrapRenderedImage((RenderedImage)sourceElement) : sourceElement); // Add as a sink of any PlanarImage source. if(sourceElement instanceof PlanarImage) { ((PlanarImage)sourceElement).addSink(this); } } } } /** * Removes all the sources of this image. This image is removed from * the list of sinks of any prior PlanarImages sources. */ protected void removeSources() { if (sources != null) { synchronized (this) { if(sources != null) { // Remove this image as a sink of prior PlanarImage sources. Iterator it = sources.iterator(); while(it.hasNext()) { Object src = it.next(); if(src instanceof PlanarImage) { ((PlanarImage)src).removeSink(this); } } } sources = null; } } } /** * Returns the immediate source indicated by the index. If there * is no source corresponding to the specified index, this method * throws an exception. * * @param index The index of the desired source. * * @return A PlanarImage source. * * @throws ArrayIndexOutOfBoundsException If this image has no * immediate source, or if the index is negative or greater * than the maximum source index. * * @since JAI 1.1 */ public PlanarImage getSourceImage(int index) { if (sources == null) { throw new ArrayIndexOutOfBoundsException( JaiI18N.getString("PlanarImage0")); } synchronized (sources) { return (PlanarImage)sources.get(index); } } /** * Returns the immediate source indicated by the index. If there * is no source corresponding to the specified index, this method * throws an exception. * * @param index The index of the desired source. * * @return An Object source. * * @throws ArrayIndexOutOfBoundsException If this image has no * immediate source, or if the index is negative or greater * than the maximum source index. * * @since JAI 1.1 */ public Object getSourceObject(int index) { if (sources == null) { throw new ArrayIndexOutOfBoundsException( JaiI18N.getString("PlanarImage0")); } synchronized (sources) { return sources.get(index); } } /** * Adds an Object source to the list of sources. * If the source is a RenderedImage it is wrapped using * wrapRenderedImage(). If the unwrapped source is a * PlanarImage then this image is added to its list of sinks. * * @param source An Object to be added as an * immediate source of this image. * * @throws IllegalArgumentException If source is * null. * * @since JAI 1.1 */ protected void addSource(Object source) { if(source == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sources == null) { synchronized (this) { sources = new Vector(); } } synchronized (sources) { // Add the source wrapping it if necessary. sources.add(source instanceof RenderedImage ? wrapRenderedImage((RenderedImage)source) : source); } if(source instanceof PlanarImage) { ((PlanarImage)source).addSink(this); } } /** * Sets an immediate source of this image. The source to be replaced * with the new input Object is referred to by its * index. This image must already have a source corresponding to the * specified index. If the source is a RenderedImage it is * wrapped using wrapRenderedImage(). If the unwrapped * source is a PlanarImage then this image is added to its * list of sinks. If a PlanarImage source previously * existed at this index, this image is removed from its list of sinks. * * @param source A Object source to be set. * @param index The index of the source to be set. * * @throws ArrayIndexOutOfBoundsException If this image has no * immediate source, or if there is no source corresponding * to the index value. * @throws IllegalArgumentException If source is * null. * * @since JAI 1.1 */ protected void setSource(Object source, int index) { if(source == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sources == null) { throw new ArrayIndexOutOfBoundsException( JaiI18N.getString("PlanarImage0")); } synchronized (sources) { if(index < sources.size() && sources.get(index) instanceof PlanarImage) { getSourceImage(index).removeSink(this); } sources.set(index, source instanceof RenderedImage ? wrapRenderedImage((RenderedImage)source) : source); } if(source instanceof PlanarImage) { ((PlanarImage)source).addSink(this); } } /** * Removes an Object source from the list of sources. * If the source is a PlanarImage then this image * is removed from its list of sinks. * * @param source The Object source to be removed. * * @return true if the element was present, * false otherwise. * * @throws IllegalArgumentException If source is * null. * * @since JAI 1.1 */ protected boolean removeSource(Object source) { if(source == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sources == null) { return false; } synchronized (sources) { if(source instanceof PlanarImage) { ((PlanarImage)source).removeSink(this); } return sources.remove(source); } } /** * Returns a Vector containing the currently available * PlanarImage sinks of this image (images for which * this image is a source), or null if no sinks are * present. * *

      Sinks are stored using weak references. This means that * the set of sinks may change between calls to * getSinks() if the garbage collector happens to * identify a sink as not otherwise reachable (reachability is * discussed in the class comments for this class). * *

      Since the pool of sinks may change as garbage collection * occurs, PlanarImage does not implement either a * getSink(int index) or a getNumSinks() * method. Instead, the caller must call getSinks(), * which returns a Vector of normal references. As long as the * returned Vector is referenced from user code, the * images it references are reachable and may be reliably * accessed. */ public Vector getSinks() { Vector v = null; if (sinks != null) { synchronized (sinks) { int size = sinks.size(); v = new Vector(size); for (int i = 0; i < size; i++) { Object o = ((WeakReference)sinks.get(i)).get(); if (o != null) { v.add(o); } } } if (v.size() == 0) { v = null; } } return v; } /** * Adds an Object sink to the list of sinks. * * @return true if the element was added, * false otherwise. * * @throws IllegalArgumentException if * sink is null. * * @since JAI 1.1 */ public synchronized boolean addSink(Object sink) { if (sink == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sinks == null) { sinks = new Vector(); } boolean result = false; if(sink instanceof PlanarImage) { result = sinks.add(((PlanarImage)sink).weakThis); } else { result = sinks.add(new WeakReference(sink)); } return result; } /** * Removes an Object sink from the list of sinks. * * @return true if the element was present, * false otherwise. * * @throws IllegalArgumentException if * sink is null. * * @since JAI 1.1 */ public synchronized boolean removeSink(Object sink) { if (sink == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sinks == null) { return false; } boolean result = false; if(sink instanceof PlanarImage) { result = sinks.remove(((PlanarImage)sink).weakThis); } else { Iterator it = sinks.iterator(); while(it.hasNext()) { Object referent = ((WeakReference)it.next()).get(); if(referent == sink) { // Remove the sink. it.remove(); result = true; // Do not break: could be more than one. } else if(referent == null) { // A cleared reference: might as well remove it. it.remove(); // ignore return value here. } } } return result; } /** * Adds a PlanarImage sink to the list of sinks. * * @param sink A PlanarImage to be added as a sink. * * @throws IllegalArgumentException If sink is * null. * * @deprecated as of JAI 1.1. Use addSink(Object) instead. */ protected void addSink(PlanarImage sink) { if(sink == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sinks == null) { synchronized (this) { sinks = new Vector(); } } synchronized (sinks) { sinks.add(sink.weakThis); } } /** * Removes a PlanarImage sink from the list of sinks. * * @param sink A PlanarImage sink to be removed. * * @return true if the element was present, * false otherwise. * * @throws IllegalArgumentException If sink is * null. * @throws IndexOutOfBoundsException If sink is not * in the sink list. * * @deprecated as of JAI 1.1. Use removeSink(Object) instead. */ protected boolean removeSink(PlanarImage sink) { if(sink == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sinks == null) { return false; } synchronized (sinks) { return sinks.remove(sink.weakThis); } } /** Removes all the sinks of this image. */ public void removeSinks() { if (sinks != null) { synchronized (this) { sinks = null; } } } /** * Returns the internal Hashtable containing the * image properties by reference. */ protected Hashtable getProperties() { return (Hashtable)properties.getProperties(); } /** * Sets the Hashtable containing the image properties * to a given Hashtable. The Hashtable * is incorporated by reference and must not be altered by other * classes after this method is called. */ protected void setProperties(Hashtable properties) { this.properties.addProperties(properties); } /** * 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. * * @exception IllegalArgumentException if propertyName * is null. */ public Object getProperty(String name) { return properties.getProperty(name); } /** * Returns the class expected to be returned by a request for * the property with the specified name. If this information * is unavailable, null will be returned. * * @exception IllegalArgumentException if name * is null. * * @return The Class expected to be return by a * request for the value of this property or null. * * @since JAI 1.1 */ public Class getPropertyClass(String name) { return properties.getPropertyClass(name); } /** * Sets a property on a PlanarImage. Some * PlanarImage subclasses may ignore attempts to set * properties. * * @param name a String containing the property's name. * @param value the property, as a general Object. * * @throws IllegalArgumentException If name or * value is null. */ public void setProperty(String name, Object value) { properties.setProperty(name, value); } /** * Removes the named property from the PlanarImage. * Some PlanarImage subclasses may ignore attempts to * remove properties. * * @exception IllegalArgumentException if name * is null. * * @since JAI 1.1 */ public void removeProperty(String name) { properties.removeProperty(name); } /** * Returns a list of property names that are recognized by this image * or null if none are recognized. * * @return an array of Strings containing valid * property names or null. */ public String[] getPropertyNames() { return properties.getPropertyNames(); } /** * Returns an array of Strings recognized as names by * this property source that begin with the supplied prefix. If * no property names match, null will be returned. * The comparison is done in a case-independent manner. * *

      The default implementation calls * getPropertyNames() and searches the list of names * for matches. * * @return an array of Strings giving the valid * property names. * * @throws IllegalArgumentException If prefix is * null. */ public String[] getPropertyNames(String prefix) { return PropertyUtil.getPropertyNames(getPropertyNames(), prefix); } /** * Add a PropertyChangeListener to the listener list. The * listener is registered for all properties. * * @since JAI 1.1 */ public void addPropertyChangeListener(PropertyChangeListener listener) { eventManager.addPropertyChangeListener(listener); } /** * Add a PropertyChangeListener for a specific property. The * listener will be invoked only when a call on * firePropertyChange names that specific property. The case of * the name is ignored. * * @since JAI 1.1 */ public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { eventManager.addPropertyChangeListener(propertyName.toLowerCase(), listener); } /** * Remove a PropertyChangeListener from the listener list. This * removes a PropertyChangeListener that was registered for all * properties. * * @since JAI 1.1 */ public void removePropertyChangeListener(PropertyChangeListener listener) { eventManager.removePropertyChangeListener(listener); } /** * Remove a PropertyChangeListener for a specific property. The case * of the name is ignored. * * @since JAI 1.1 */ public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { eventManager.removePropertyChangeListener(propertyName.toLowerCase(), listener); } private synchronized Set getTileComputationListeners(boolean createIfNull) { if(createIfNull && tileListeners == null) { tileListeners = Collections.synchronizedSet(new HashSet()); } return tileListeners; } /** * Adds a TileComputationListener to the list of * registered TileComputationListeners. This listener * will be notified when tiles requested via queueTiles() * have been computed. * * @param listener The TileComputationListener to register. * @throws IllegalArgumentException if listener is * null. * * @since JAI 1.1 */ public synchronized void addTileComputationListener(TileComputationListener listener) { if(listener == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } Set listeners = getTileComputationListeners(true); listeners.add(listener); } /** * Removes a TileComputationListener from the list of * registered TileComputationListeners. * * @param listener The TileComputationListener to unregister. * @throws IllegalArgumentException if listener is * null. * * @since JAI 1.1 */ public synchronized void removeTileComputationListener(TileComputationListener listener) { if(listener == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } Set listeners = getTileComputationListeners(false); if(listeners != null) { listeners.remove(listener); } } /** * Retrieves a snapshot of the set of all registered * TileComputationListeners as of the moment this * method is invoked. * * @return All TileComputationListeners or * null if there are none. * * @since JAI 1.1 */ public TileComputationListener[] getTileComputationListeners() { Set listeners = getTileComputationListeners(false); if(listeners == null) { return null; } return (TileComputationListener[])listeners.toArray(new TileComputationListener[listeners.size()]); } /** * Within a given rectangle, store the list of tile seams of both * X and Y directions into the corresponding split sequence. * * @param xSplits An IntegerSequence to which the * tile seams in the X direction are to be added. * @param ySplits An IntegerSequence to which the * tile seams in the Y direction are to be added. * @param rect The rectangular region of interest. * * @throws IllegalArgumentException If xSplits, * ySplits, or rect * is null. */ public void getSplits(IntegerSequence xSplits, IntegerSequence ySplits, Rectangle rect) { if(xSplits == null || ySplits == null || rect == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } int minTileX = XToTileX(rect.x); int maxTileX = XToTileX(rect.x + rect.width - 1); int xTilePos = tileXToX(minTileX); for (int i = minTileX; i <= maxTileX; i++) { xSplits.insert(xTilePos); xTilePos += tileWidth; } int minTileY = YToTileY(rect.y); int maxTileY = YToTileY(rect.y + rect.height - 1); int yTilePos = tileYToY(minTileY); for (int i = minTileY; i <= maxTileY; i++) { ySplits.insert(yTilePos); yTilePos += tileHeight; } } /** * Returns an array containing the indices of all tiles which overlap * the specified Rectangle. If the Rectangle * does not intersect the image bounds then null will be * returned. If an array is returned, it will be ordered in terms of * the row major ordering of its contained tile indices. If the * specified Rectangle is null, the tile * indicies for the entire image will be returned. * * @param region The Rectangle of interest. * @return An array of the indices of overlapping tiles or * null if region does not intersect * the image bounds. * * @since JAI 1.1 */ public Point[] getTileIndices(Rectangle region) { if(region == null) { region = (Rectangle)getBounds().clone(); } else if(!region.intersects(getBounds())) { return null; } else { region = region.intersection(getBounds()); if(region.isEmpty()) { return null; } } if(region == null) { region = getBounds(); } else { Rectangle r = new Rectangle(getMinX(), getMinY(), getWidth() + 1, getHeight() + 1); if(!region.intersects(r)) { return null; } else { region = region.intersection(r); } } int minTileX = XToTileX(region.x); int maxTileX = XToTileX(region.x + region.width - 1); int minTileY = YToTileY(region.y); int maxTileY = YToTileY(region.y + region.height - 1); Point[] tileIndices = new Point[(maxTileY-minTileY+1)*(maxTileX-minTileX+1)]; int tileIndexOffset = 0; for (int ty = minTileY; ty <= maxTileY; ty++) { for (int tx = minTileX; tx <= maxTileX; tx++) { tileIndices[tileIndexOffset++] = new Point(tx, ty); } } return tileIndices; } /** * Returns true if and only if the intersection of * the specified Rectangle with the image bounds * overlaps more than one tile. * * @throws IllegalArgumentException if rect is * null. */ public boolean overlapsMultipleTiles(Rectangle rect) { if(rect == null) { throw new IllegalArgumentException("rect == null!"); } Rectangle xsect = rect.intersection(getBounds()); // 'true' if and only if non-empty and more than one tile in // either horizontal or vertical direction. return !xsect.isEmpty() && (XToTileX(xsect.x) != XToTileX(xsect.x + xsect.width - 1) || YToTileY(xsect.y) != YToTileY(xsect.y + xsect.height - 1)); } /** * Creates a WritableRaster with the specified * SampleModel and location. If tileFactory * is non-null, it will be used to create the * WritableRaster; otherwise * {@link RasterFactory#createWritableRaster(SampleModel,Point)} * will be used. * * @param sampleModel The SampleModel to use. * @param location The origin of the WritableRaster; if * null, (0, 0) will be used. * * @throws IllegalArgumentException if sampleModel is * null. * * @since JAI 1.1.2 */ protected final WritableRaster createWritableRaster(SampleModel sampleModel, Point location) { if(sampleModel == null) { throw new IllegalArgumentException("sampleModel == null!"); } return tileFactory != null ? tileFactory.createTile(sampleModel, location) : RasterFactory.createWritableRaster(sampleModel, location); } /** * Returns the entire image in a single Raster. For * images with multiple tiles this will require creating a new * Raster and copying data from multiple tiles into * it ("cobbling"). * *

      The returned Raster is semantically a copy. * This means that subsequent updates to this image will not be * reflected in the returned Raster. For non-writable * (immutable) images, the returned value may be a reference to the * image's internal data. The returned Raster should * be considered non-writable; any attempt to alter its pixel data * (such as by casting it to a WritableRaster or obtaining * and modifying its DataBuffer) may result in undefined * behavior. The copyData method should be used if the * returned Raster is to be modified. * *

      For a very large image, more than * Integer.MAX_VALUE entries could be required in the * returned Raster's underlying data array. Since * the Java language does not permit such an array, an * IllegalArgumentException will be thrown. * * @return A Raster containing the entire image data. * * @throws IllegalArgumentException If the size of the returned data * is too large to be stored in a single Raster. */ public Raster getData() { return getData(null); } /** * Returns a specified region of this image in a Raster. * *

      The returned Raster is semantically a copy. * This means that subsequent updates to this image will not be * reflected in the returned Raster. For non-writable * (immutable) images, the returned value may be a reference to the * image's internal data. The returned Raster should * be considered non-writable; any attempt to alter its pixel data * (such as by casting it to a WritableRaster or obtaining * and modifying its DataBuffer) may result in undefined * behavior. The copyData method should be used if the * returned Raster is to be modified. * *

      The region of the image to be returned is specified by a * Rectangle. This region may go beyond this image's * boundary. If so, the pixels in the areas outside this image's * boundary are left unset. Use getExtendedData if * a specific extension policy is required. * *

      The region parameter may also be * null, in which case the entire image data is * returned in the Raster. * *

      If region is non-null but does * not intersect the image bounds at all, an * IllegalArgumentException will be thrown. * *

      It is possible to request a region of an image that would * require more than Integer.MAX_VALUE entries * in the returned Raster's underlying data array. * Since the Java language does not permit such an array, * an IllegalArgumentException will be thrown. * * @param region The rectangular region of this image to be * returned, or null. * * @return A Raster containing the specified image data. * * @throws IllegalArgumentException If the region does not * intersect the image bounds. * @throws IllegalArgumentException If the size of the returned data * is too large to be stored in a single Raster. */ public Raster getData(Rectangle region) { Rectangle b = getBounds(); // image's bounds if (region == null) { region = b; } else if (!region.intersects(b)) { throw new IllegalArgumentException( JaiI18N.getString("PlanarImage4")); } // Get the intersection of the region and the image bounds. Rectangle xsect = region == b ? region : region.intersection(b); // Compute tile indices over the intersection. int startTileX = XToTileX(xsect.x); int startTileY = YToTileY(xsect.y); int endTileX = XToTileX(xsect.x + xsect.width - 1); int endTileY = YToTileY(xsect.y + xsect.height - 1); if (startTileX == endTileX && startTileY == endTileY && getTileRect(startTileX, startTileY).contains(region)) { // Requested region is within a single tile. Raster tile = getTile(startTileX, startTileY); if(this instanceof WritableRenderedImage) { // Returned Raster must not change if the corresponding // image data are modified so if this image is mutable // a copy must be created. SampleModel sm = tile.getSampleModel(); if(sm.getWidth() != region.width || sm.getHeight() != region.height) { sm = sm.createCompatibleSampleModel(region.width, region.height); } WritableRaster destinationRaster = createWritableRaster(sm, region.getLocation()); Raster sourceRaster = tile.getBounds().equals(region) ? tile : tile.createChild(region.x, region.y, region.width, region.height, region.x, region.y, null); JDKWorkarounds.setRect(destinationRaster, sourceRaster); return destinationRaster; } else { // Image is immutable so returning the tile or a child // thereof is acceptable. return tile.getBounds().equals(region) ? tile : tile.createChild(region.x, region.y, region.width, region.height, region.x, region.y, null); } } else { // Extract a region crossing tiles into a new WritableRaster WritableRaster dstRaster; SampleModel srcSM = getSampleModel(); int dataType = srcSM.getDataType(); int nbands = srcSM.getNumBands(); boolean isBandChild = false; ComponentSampleModel csm = null; int[] bandOffs = null; boolean fastCobblePossible = false; if (srcSM instanceof ComponentSampleModel) { csm = (ComponentSampleModel)srcSM; int ps = csm.getPixelStride(); boolean isBandInt = (ps == 1 && nbands > 1); isBandChild = (ps > 1 && nbands != ps); if ( (!isBandChild) && (!isBandInt)) { bandOffs = csm.getBandOffsets(); int i; for (i=0; i= nbands) { break; } } if (i == nbands) { fastCobblePossible = true; } } } if (fastCobblePossible) { // For acceptable cases of ComponentSampleModel, // use an optimized cobbler which directly accesses the // tile DataBuffers, using arraycopy whenever possible. try { SampleModel interleavedSM = RasterFactory.createPixelInterleavedSampleModel( dataType, region.width, region.height, nbands, region.width*nbands, bandOffs); dstRaster = createWritableRaster(interleavedSM, region.getLocation()); } catch (IllegalArgumentException e) { throw new IllegalArgumentException( JaiI18N.getString("PlanarImage2")); } switch (dataType) { case DataBuffer.TYPE_BYTE: cobbleByte(region, dstRaster); break; case DataBuffer.TYPE_SHORT: cobbleShort(region, dstRaster); break; case DataBuffer.TYPE_USHORT: cobbleUShort(region, dstRaster); break; case DataBuffer.TYPE_INT: cobbleInt(region, dstRaster); break; case DataBuffer.TYPE_FLOAT: cobbleFloat(region, dstRaster); break; case DataBuffer.TYPE_DOUBLE: cobbleDouble(region, dstRaster); break; default: break; } } else { SampleModel sm = sampleModel; if(sm.getWidth() != region.width || sm.getHeight() != region.height) { sm = sm.createCompatibleSampleModel(region.width, region.height); } try { dstRaster = createWritableRaster(sm, region.getLocation()); } catch (IllegalArgumentException e) { throw new IllegalArgumentException( JaiI18N.getString("PlanarImage2")); } for (int j = startTileY; j <= endTileY; j++) { for (int i = startTileX; i <= endTileX; i++) { Raster tile = getTile(i, j); Rectangle subRegion = region.intersection( tile.getBounds()); Raster subRaster = tile.createChild(subRegion.x, subRegion.y, subRegion.width, subRegion.height, subRegion.x, subRegion.y, null); if (sm instanceof ComponentSampleModel && isBandChild) { // Need to handle this case specially, since // setDataElements will not copy band child images switch (sm.getDataType()) { case DataBuffer.TYPE_FLOAT: dstRaster.setPixels( subRegion.x, subRegion.y, subRegion.width, subRegion.height, subRaster.getPixels( subRegion.x, subRegion.y, subRegion.width, subRegion.height, new float[nbands*subRegion.width*subRegion.height])); break; case DataBuffer.TYPE_DOUBLE: dstRaster.setPixels( subRegion.x, subRegion.y, subRegion.width, subRegion.height, subRaster.getPixels( subRegion.x, subRegion.y, subRegion.width, subRegion.height, new double[nbands*subRegion.width*subRegion.height])); break; default: dstRaster.setPixels( subRegion.x, subRegion.y, subRegion.width, subRegion.height, subRaster.getPixels( subRegion.x, subRegion.y, subRegion.width, subRegion.height, new int[nbands*subRegion.width*subRegion.height])); break; } } else { JDKWorkarounds.setRect(dstRaster, subRaster); } } } } return dstRaster; } } /** Copies the entire image into a single raster. */ public WritableRaster copyData() { return copyData(null); } /** * Copies an arbitrary rectangular region of this image's pixel * data into a caller-supplied WritableRaster. * The region to be copied is defined as the boundary of the * WritableRaster, which can be obtained by calling * WritableRaster.getBounds(). * *

      The supplied WritableRaster may have a region * that extends beyond this image's boundary, in which case only * pixels in the part of the region that intersects this image * are copied. The areas outside of this image's boundary are left * untouched. * *

      The supplied WritableRaster may also be * null, in which case the entire image is copied * into a newly-created WritableRaster with a * SampleModel that is compatible with that of * this image. * * @param raster A WritableRaster to hold the copied * pixel data of this image. * * @return A reference to the supplied WritableRaster, * or to a new WritableRaster if the supplied * one was null. */ public WritableRaster copyData(WritableRaster raster) { Rectangle region; // the region to be copied if (raster == null) { // copy the entire image region = getBounds(); SampleModel sm = getSampleModel(); if(sm.getWidth() != region.width || sm.getHeight() != region.height) { sm = sm.createCompatibleSampleModel(region.width, region.height); } raster = createWritableRaster(sm, region.getLocation()); } else { region = raster.getBounds().intersection(getBounds()); if (region.isEmpty()) { // Raster is outside of image's boundary return raster; } } int startTileX = XToTileX(region.x); int startTileY = YToTileY(region.y); int endTileX = XToTileX(region.x + region.width - 1); int endTileY = YToTileY(region.y + region.height - 1); SampleModel[] sampleModels = { getSampleModel() }; int tagID = RasterAccessor.findCompatibleTag(sampleModels, raster.getSampleModel()); RasterFormatTag srcTag = new RasterFormatTag(getSampleModel(),tagID); RasterFormatTag dstTag = new RasterFormatTag(raster.getSampleModel(),tagID); for (int ty = startTileY; ty <= endTileY; ty++) { for (int tx = startTileX; tx <= endTileX; tx++) { Raster tile = getTile(tx, ty); Rectangle subRegion = region.intersection(tile.getBounds()); RasterAccessor s = new RasterAccessor(tile, subRegion, srcTag, getColorModel()); RasterAccessor d = new RasterAccessor(raster, subRegion, dstTag, null); ImageUtil.copyRaster(s, d); } } return raster; } /** * Copies an arbitrary rectangular region of the * RenderedImage into a caller-supplied * WritableRaster. The portion of the supplied * WritableRaster that lies outside of the bounds of * the image is computed by calling the given * BorderExtender. The supplied * WritableRaster must have a * SampleModel that is compatible with that of the * image. * * @param dest a WritableRaster to hold the returned * portion of the image. * @param extender an instance of BorderExtender. * * @throws IllegalArgumentException If dest or * extender is null. */ public void copyExtendedData(WritableRaster dest, BorderExtender extender) { if(dest == null || extender == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } // If the Raster is within the image just copy directly. Rectangle destBounds = dest.getBounds(); Rectangle imageBounds = getBounds(); if(imageBounds.contains(destBounds)) { copyData(dest); return; } // Get the intersection of the Raster and image bounds. Rectangle isect = imageBounds.intersection(destBounds); if(!isect.isEmpty()) { // Copy image data into the dest Raster. WritableRaster isectRaster = dest.createWritableChild( isect.x, isect.y, isect.width, isect.height, isect.x, isect.y, null); copyData(isectRaster); } // Extend the Raster. extender.extend(dest, this); } /** * Returns a copy of an arbitrary rectangular region of this image * in a Raster. The portion of the rectangle of * interest ouside the bounds of the image will be computed by * calling the given BorderExtender. If the region * falls entirely within the image, extender will not * be used in any way. Thus it is possible to use a * null value for extender when it is * known that no actual extension will be required. * *

      The returned Raster should be considered * non-writable; any attempt to alter its pixel data (such as by * casting it to a WritableRaster or obtaining and * modifying its DataBuffer) may result in undefined * behavior. The copyExtendedData method should be * used if the returned Raster is to be modified. * * @param region the region of the image to be returned. * @param extender an instance of BorderExtender, * used only if the region exceeds the image bounds, * or null. * @return a Raster containing the extended data. * * @throws IllegalArgumentException If region is * null. * @throws IllegalArgumentException If the region exceeds the image * bounds and extender is null. */ public Raster getExtendedData(Rectangle region, BorderExtender extender) { if(region == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (getBounds().contains(region)) { return getData(region); } if(extender == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } // Create a WritableRaster of the desired size SampleModel destSM = getSampleModel(); if(destSM.getWidth() != region.width || destSM.getHeight() != region.height) { destSM = destSM.createCompatibleSampleModel(region.width, region.height); } // Translate it WritableRaster dest = createWritableRaster(destSM, region.getLocation()); copyExtendedData(dest, extender); return dest; } /** * Returns a copy of this image as a BufferedImage. * A subarea of the image may be copied by supplying a * Rectangle parameter; if it is set to * null, the entire image is copied. The supplied * Rectangle will be clipped to the image bounds. The image's * ColorModel may be overridden by supplying a * non-null second argument. The resulting * ColorModel must be non-null and * appropriate for the image's SampleModel. * *

      The resulting BufferedImage will contain the * full requested area, but will always have its top-left corner * translated (0, 0) as required by the BufferedImage * interface. * * @param rect The Rectangle of the image to be * copied, or null to indicate that the * entire image is to be copied. * * @param cm A ColorModel used to override * this image's ColorModel, or null. * The caller is responsible for supplying a * ColorModel that is compatible with the image's * SampleModel. * * @throws IllegalArgumentException If an incompatible, non-null * ColorModel is supplied. * @throws IllegalArgumentException If no ColorModel is * supplied, and the image ColorModel is * null. */ public BufferedImage getAsBufferedImage(Rectangle rect, ColorModel cm) { if (cm == null) { cm = getColorModel(); if(cm == null) { throw new IllegalArgumentException( JaiI18N.getString("PlanarImage6")); } } if (!JDKWorkarounds.areCompatibleDataModels(sampleModel, cm)) { throw new IllegalArgumentException( JaiI18N.getString("PlanarImage3")); } if (rect == null) { rect = getBounds(); } else { rect = getBounds().intersection(rect); } SampleModel sm = sampleModel.getWidth() != rect.width || sampleModel.getHeight() != rect.height ? sampleModel.createCompatibleSampleModel(rect.width, rect.height) : sampleModel; WritableRaster ras = createWritableRaster(sm, rect.getLocation()); copyData(ras); if (rect.x != 0 || rect.y != 0) { // Move Raster to (0, 0) ras = RasterFactory.createWritableChild(ras, rect.x, rect.y, rect.width, rect.height, 0, 0, null); } return new BufferedImage(cm, ras, cm.isAlphaPremultiplied(), null); } /** * Returns a copy of the entire image as a * BufferedImage. The image's * ColorModel must be non-null, and * appropriate for the image's SampleModel. * * @see java.awt.image.BufferedImage */ public BufferedImage getAsBufferedImage() { return getAsBufferedImage(null, null); } /** * Returns a Graphics object that may be used to draw * into this image. By default, an * IllegalAccessError is thrown. Subclasses that * support such drawing, such as TiledImage, may * override this method to return a suitable Graphics * object. */ public Graphics getGraphics() { throw new IllegalAccessError(JaiI18N.getString("PlanarImage1")); } /** * Returns tile (tileX, tileY) as a * Raster. Note that tileX and * tileY are indices into the tile array, not pixel * locations. * *

      Subclasses must override this method to return a * non-null value for all tile indices between * getMinTile{X,Y} and getMaxTile{X,Y}, * inclusive. Tile indices outside of this region should result * in a return value of null. * * @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. */ public abstract Raster getTile(int tileX, int tileY); /** * Returns the Rasters indicated by the * tileIndices array. This call allows certain * PlanarImage subclasses such as * OpImage to take advantage of the knowledge that * multiple tiles are requested at once. * * @param tileIndices An array of Points representing tile indices. * * @return An array of Raster containing the tiles * corresponding to the given tile indices. * * @throws IllegalArgumentException If tileIndices is * null. */ public Raster[] getTiles(Point[] tileIndices) { if(tileIndices == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } int size = tileIndices.length; Raster tiles[] = new Raster[size]; for (int i = 0; i < tileIndices.length; i++) { Point p = tileIndices[i]; tiles[i] = getTile(p.x,p.y); } return tiles; } /** * Computes and returns all tiles in the image. The tiles are returned * in a sequence corresponding to the row-major order of their respective * tile indices. The returned array may of course be ignored, e.g., in * the case of a subclass which caches the tiles and the intent is to * force their computation. */ public Raster[] getTiles() { return getTiles(getTileIndices(getBounds())); } /** * Queues a list of tiles for computation. Registered listeners * will be notified after each tile has been computed. * *

      The TileScheduler of the default instance of the * JAI class is used to process the tiles. If this * TileScheduler has a positive parallelism this * method will be non-blocking. The event source parameter passed to * such listeners will be the TileScheduler and the image * parameter will be this image. * * @param tileIndices A list of tile indices indicating which tiles * to schedule for computation. * @throws IllegalArgumentException If tileIndices is * null. * * @since JAI 1.1 */ public TileRequest queueTiles(Point[] tileIndices) { if(tileIndices == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } TileComputationListener[] listeners = getTileComputationListeners(); return JAI.getDefaultInstance().getTileScheduler().scheduleTiles(this, tileIndices, listeners); } /** * Issue an advisory cancellation request to nullify processing of * the indicated tiles. It is legal to implement this method as a no-op. * *

      The cancellation request is forwarded to the * TileScheduler of the default instance of the * JAI class. * * @param request The request for which tiles are to be cancelled. * @param tileIndices The tiles to be cancelled; may be null. * Any tiles not actually in the TileRequest will be * ignored. * @throws IllegalArgumentException If request is * null. * * @since JAI 1.1 */ public void cancelTiles(TileRequest request, Point[] tileIndices) { if (request == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic4")); } JAI.getDefaultInstance().getTileScheduler().cancelTiles(request, tileIndices); } /** * Hints that the given tiles might be needed in the near future. * Some implementations may spawn a thread or threads * to compute the tiles while others may ignore the hint. * *

      The TileScheduler of the default instance of the * JAI class is used to prefetch the tiles. If this * TileScheduler has a positive prefetch parallelism * this method will be non-blocking. * * @param tileIndices A list of tile indices indicating which tiles * to prefetch. * * @throws IllegalArgumentException If tileIndices is * null. */ public void prefetchTiles(Point[] tileIndices) { if(tileIndices == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } JAI.getDefaultInstance().getTileScheduler().prefetchTiles(this, tileIndices); } /** * Provides a hint that an image will no longer be accessed from a * reference in user space. The results are equivalent to those * that occur when the program loses its last reference to this * image, the garbage collector discovers this, and finalize is * called. This can be used as a hint in situations where waiting * for garbage collection would be overly conservative. * *

      PlanarImage defines this method to remove the * image being disposed from the list of sinks in all of its * source images. Subclasses should call * super.dispose() in their dispose * methods, if any. * *

      The results of referencing an image after a call to * dispose() are undefined. */ public synchronized void dispose() { // Do nothing if dispose() has been called previously if (disposed) { return; } disposed = true; // Retrieve the sources as a Vector rather than using getSource() // to enable compatibility with subclasses which may have sources // which are not PlanarImages, e.g., as in RenderedOp. Vector srcs = getSources(); if(srcs != null) { int numSources = srcs.size(); for (int i = 0; i < numSources; i++) { Object src = srcs.get(i); if(src instanceof PlanarImage) { ((PlanarImage)src).removeSink(this); } } } } /** * Performs cleanup prior to garbage collection. * *

      PlanarImage defines this method to invoke * the dispose() method.

      * * @exception Throwable if an error occurs in the * garbage collector. */ protected void finalize() throws Throwable { dispose(); } /** For debugging. */ private void printBounds() { System.out.println("Bounds: [x=" + getMinX() + ", y=" + getMinY() + ", width=" + getWidth() + ", height=" + getHeight() + "]"); } /** For debugging. */ private void printTile(int i, int j) { int xmin = i*getTileWidth() + getTileGridXOffset(); int ymin = j*getTileHeight() + getTileGridYOffset(); Rectangle imageBounds = getBounds(); Rectangle tileBounds = new Rectangle(xmin, ymin, getTileWidth(), getTileHeight()); tileBounds = tileBounds.intersection(imageBounds); Raster tile = getTile(i, j); Rectangle realTileBounds = new Rectangle(tile.getMinX(), tile.getMinY(), tile.getWidth(), tile.getHeight()); System.out.println("Tile bounds (actual) = " + realTileBounds); System.out.println("Tile bounds (computed) = " + tileBounds); xmin = tileBounds.x; ymin = tileBounds.y; int xmax = tileBounds.x + tileBounds.width - 1; int ymax = tileBounds.y + tileBounds.height - 1; int numBands = getSampleModel().getNumBands(); int[] val = new int[numBands]; int pi, pj; for (pj = ymin; pj <= ymax; pj++) { for (pi = xmin; pi <= xmax; pi++) { tile.getPixel(pi, pj, val); if (numBands == 1) { System.out.print("(" + val[0] + ") "); } else if (numBands == 3) { System.out.print("(" + val[0] + "," + val[1] + "," + val[2] + ") "); } } System.out.println(); } } /** * Returns a String which includes the basic information * of this image. * * @since JAI 1.1 */ public String toString() { return "PlanarImage[" + "minX=" + minX + " minY=" + minY + " width=" + width + " height=" + height + " tileGridXOffset=" + tileGridXOffset + " tileGridYOffset=" + tileGridYOffset + " tileWidth=" + tileWidth + " tileHeight=" + tileHeight + " sampleModel=" + sampleModel + " colorModel=" + colorModel + "]"; } private void cobbleByte(Rectangle bounds, Raster dstRaster) { ComponentSampleModel dstSM = (ComponentSampleModel)dstRaster.getSampleModel(); int startX = XToTileX(bounds.x); int startY = YToTileY(bounds.y); int rectXend = bounds.x + bounds.width - 1; int rectYend = bounds.y + bounds.height - 1; int endX = XToTileX(rectXend); int endY = YToTileY(rectYend); // // Get parameters of destination raster // DataBufferByte dstDB = (DataBufferByte)dstRaster.getDataBuffer(); byte[] dst = dstDB.getData(); int dstPS = dstSM.getPixelStride(); int dstSS = dstSM.getScanlineStride(); boolean tileParamsSet = false; ComponentSampleModel srcSM = null; int srcPS=0, srcSS=0; int xOrg, yOrg; int srcX1, srcY1, srcX2, srcY2, srcW, srcH; for (int y = startY; y <= endY; y++) { for (int x = startX; x <= endX; x++) { Raster tile = getTile(x, y); if (tile == null) { // // Out-of-bounds tile. Zero fill will be supplied // since dstRaster is initialized to zero // continue; } if (! tileParamsSet) { // // These are constant for all tiles, // so only set them once. // srcSM = (ComponentSampleModel)tile.getSampleModel(); srcPS = srcSM.getPixelStride(); srcSS = srcSM.getScanlineStride(); tileParamsSet = true; } // // Intersect the tile and the rectangle // Avoid use of Math.min/max // yOrg = y*tileHeight + tileGridYOffset; srcY1 = yOrg; srcY2 = srcY1 + tileHeight - 1; if (bounds.y > srcY1) srcY1 = bounds.y; if (rectYend < srcY2) srcY2 = rectYend; srcH = srcY2 - srcY1 + 1; xOrg = x*tileWidth + tileGridXOffset; srcX1 = xOrg; srcX2 = srcX1 + tileWidth - 1; if (bounds.x > srcX1) srcX1 = bounds.x; if (rectXend < srcX2) srcX2 = rectXend; srcW = srcX2 - srcX1 + 1; int dstX = srcX1 - bounds.x; int dstY = srcY1 - bounds.y; // Get the actual data array DataBufferByte srcDB = (DataBufferByte)tile.getDataBuffer(); byte[] src = srcDB.getData(); int nsamps = srcW * srcPS; boolean useArrayCopy = (nsamps >= MIN_ARRAYCOPY_SIZE); int ySrcIdx = (srcY1 - yOrg)*srcSS + (srcX1 - xOrg)*srcPS; int yDstIdx = dstY*dstSS + dstX*dstPS; if (useArrayCopy) { for (int row = 0; row < srcH; row++) { System.arraycopy(src, ySrcIdx, dst, yDstIdx, nsamps); ySrcIdx += srcSS; yDstIdx += dstSS; } } else { for (int row = 0; row < srcH; row++) { int xSrcIdx = ySrcIdx; int xDstIdx = yDstIdx; int xEnd = xDstIdx + nsamps; while (xDstIdx < xEnd) { dst[xDstIdx++] = src[xSrcIdx++]; } ySrcIdx += srcSS; yDstIdx += dstSS; } } } } } private void cobbleShort(Rectangle bounds, Raster dstRaster) { ComponentSampleModel dstSM = (ComponentSampleModel)dstRaster.getSampleModel(); int startX = XToTileX(bounds.x); int startY = YToTileY(bounds.y); int rectXend = bounds.x + bounds.width - 1; int rectYend = bounds.y + bounds.height - 1; int endX = XToTileX(rectXend); int endY = YToTileY(rectYend); // // Get parameters of destination raster // DataBufferShort dstDB = (DataBufferShort)dstRaster.getDataBuffer(); short[] dst = dstDB.getData(); int dstPS = dstSM.getPixelStride(); int dstSS = dstSM.getScanlineStride(); boolean tileParamsSet = false; ComponentSampleModel srcSM = null; int srcPS=0, srcSS=0; int xOrg, yOrg; int srcX1, srcY1, srcX2, srcY2, srcW, srcH; for (int y = startY; y <= endY; y++) { for (int x = startX; x <= endX; x++) { Raster tile = getTile(x, y); if (tile == null) { // // Out-of-bounds tile. Zero fill will be supplied // since dstRaster is initialized to zero // continue; } if (! tileParamsSet) { // // These are constant for all tiles, // so only set them once. // srcSM = (ComponentSampleModel)tile.getSampleModel(); srcPS = srcSM.getPixelStride(); srcSS = srcSM.getScanlineStride(); tileParamsSet = true; } // // Intersect the tile and the rectangle // Avoid use of Math.min/max // yOrg = y*tileHeight + tileGridYOffset; srcY1 = yOrg; srcY2 = srcY1 + tileHeight - 1; if (bounds.y > srcY1) srcY1 = bounds.y; if (rectYend < srcY2) srcY2 = rectYend; srcH = srcY2 - srcY1 + 1; xOrg = x*tileWidth + tileGridXOffset; srcX1 = xOrg; srcX2 = srcX1 + tileWidth - 1; if (bounds.x > srcX1) srcX1 = bounds.x; if (rectXend < srcX2) srcX2 = rectXend; srcW = srcX2 - srcX1 + 1; int dstX = srcX1 - bounds.x; int dstY = srcY1 - bounds.y; // Get the actual data array DataBufferShort srcDB = (DataBufferShort)tile.getDataBuffer(); short[] src = srcDB.getData(); int nsamps = srcW * srcPS; boolean useArrayCopy = (nsamps >= MIN_ARRAYCOPY_SIZE); int ySrcIdx = (srcY1 - yOrg)*srcSS + (srcX1 - xOrg)*srcPS; int yDstIdx = dstY*dstSS + dstX*dstPS; if (useArrayCopy) { for (int row = 0; row < srcH; row++) { System.arraycopy(src, ySrcIdx, dst, yDstIdx, nsamps); ySrcIdx += srcSS; yDstIdx += dstSS; } } else { for (int row = 0; row < srcH; row++) { int xSrcIdx = ySrcIdx; int xDstIdx = yDstIdx; int xEnd = xDstIdx + nsamps; while (xDstIdx < xEnd) { dst[xDstIdx++] = src[xSrcIdx++]; } ySrcIdx += srcSS; yDstIdx += dstSS; } } } } } private void cobbleUShort(Rectangle bounds, Raster dstRaster) { ComponentSampleModel dstSM = (ComponentSampleModel)dstRaster.getSampleModel(); int startX = XToTileX(bounds.x); int startY = YToTileY(bounds.y); int rectXend = bounds.x + bounds.width - 1; int rectYend = bounds.y + bounds.height - 1; int endX = XToTileX(rectXend); int endY = YToTileY(rectYend); // // Get parameters of destination raster // DataBufferUShort dstDB = (DataBufferUShort)dstRaster.getDataBuffer(); short[] dst = dstDB.getData(); int dstPS = dstSM.getPixelStride(); int dstSS = dstSM.getScanlineStride(); boolean tileParamsSet = false; ComponentSampleModel srcSM = null; int srcPS=0, srcSS=0; int xOrg, yOrg; int srcX1, srcY1, srcX2, srcY2, srcW, srcH; for (int y = startY; y <= endY; y++) { for (int x = startX; x <= endX; x++) { Raster tile = getTile(x, y); if (tile == null) { // // Out-of-bounds tile. Zero fill will be supplied // since dstRaster is initialized to zero // continue; } if (! tileParamsSet) { // // These are constant for all tiles, // so only set them once. // srcSM = (ComponentSampleModel)tile.getSampleModel(); srcPS = srcSM.getPixelStride(); srcSS = srcSM.getScanlineStride(); tileParamsSet = true; } // // Intersect the tile and the rectangle // Avoid use of Math.min/max // yOrg = y*tileHeight + tileGridYOffset; srcY1 = yOrg; srcY2 = srcY1 + tileHeight - 1; if (bounds.y > srcY1) srcY1 = bounds.y; if (rectYend < srcY2) srcY2 = rectYend; srcH = srcY2 - srcY1 + 1; xOrg = x*tileWidth + tileGridXOffset; srcX1 = xOrg; srcX2 = srcX1 + tileWidth - 1; if (bounds.x > srcX1) srcX1 = bounds.x; if (rectXend < srcX2) srcX2 = rectXend; srcW = srcX2 - srcX1 + 1; int dstX = srcX1 - bounds.x; int dstY = srcY1 - bounds.y; // Get the actual data array DataBufferUShort srcDB = (DataBufferUShort)tile.getDataBuffer(); short[] src = srcDB.getData(); int nsamps = srcW * srcPS; boolean useArrayCopy = (nsamps >= MIN_ARRAYCOPY_SIZE); int ySrcIdx = (srcY1 - yOrg)*srcSS + (srcX1 - xOrg)*srcPS; int yDstIdx = dstY*dstSS + dstX*dstPS; if (useArrayCopy) { for (int row = 0; row < srcH; row++) { System.arraycopy(src, ySrcIdx, dst, yDstIdx, nsamps); ySrcIdx += srcSS; yDstIdx += dstSS; } } else { for (int row = 0; row < srcH; row++) { int xSrcIdx = ySrcIdx; int xDstIdx = yDstIdx; int xEnd = xDstIdx + nsamps; while (xDstIdx < xEnd) { dst[xDstIdx++] = src[xSrcIdx++]; } ySrcIdx += srcSS; yDstIdx += dstSS; } } } } } private void cobbleInt(Rectangle bounds, Raster dstRaster) { ComponentSampleModel dstSM = (ComponentSampleModel)dstRaster.getSampleModel(); int startX = XToTileX(bounds.x); int startY = YToTileY(bounds.y); int rectXend = bounds.x + bounds.width - 1; int rectYend = bounds.y + bounds.height - 1; int endX = XToTileX(rectXend); int endY = YToTileY(rectYend); // // Get parameters of destination raster // DataBufferInt dstDB = (DataBufferInt)dstRaster.getDataBuffer(); int[] dst = dstDB.getData(); int dstPS = dstSM.getPixelStride(); int dstSS = dstSM.getScanlineStride(); boolean tileParamsSet = false; ComponentSampleModel srcSM = null; int srcPS=0, srcSS=0; int xOrg, yOrg; int srcX1, srcY1, srcX2, srcY2, srcW, srcH; for (int y = startY; y <= endY; y++) { for (int x = startX; x <= endX; x++) { Raster tile = getTile(x, y); if (tile == null) { // // Out-of-bounds tile. Zero fill will be supplied // since dstRaster is initialized to zero // continue; } if (! tileParamsSet) { // // These are constant for all tiles, // so only set them once. // srcSM = (ComponentSampleModel)tile.getSampleModel(); srcPS = srcSM.getPixelStride(); srcSS = srcSM.getScanlineStride(); tileParamsSet = true; } // // Intersect the tile and the rectangle // Avoid use of Math.min/max // yOrg = y*tileHeight + tileGridYOffset; srcY1 = yOrg; srcY2 = srcY1 + tileHeight - 1; if (bounds.y > srcY1) srcY1 = bounds.y; if (rectYend < srcY2) srcY2 = rectYend; srcH = srcY2 - srcY1 + 1; xOrg = x*tileWidth + tileGridXOffset; srcX1 = xOrg; srcX2 = srcX1 + tileWidth - 1; if (bounds.x > srcX1) srcX1 = bounds.x; if (rectXend < srcX2) srcX2 = rectXend; srcW = srcX2 - srcX1 + 1; int dstX = srcX1 - bounds.x; int dstY = srcY1 - bounds.y; // Get the actual data array DataBufferInt srcDB = (DataBufferInt)tile.getDataBuffer(); int[] src = srcDB.getData(); int nsamps = srcW * srcPS; boolean useArrayCopy = (nsamps >= MIN_ARRAYCOPY_SIZE); int ySrcIdx = (srcY1 - yOrg)*srcSS + (srcX1 - xOrg)*srcPS; int yDstIdx = dstY*dstSS + dstX*dstPS; if (useArrayCopy) { for (int row = 0; row < srcH; row++) { System.arraycopy(src, ySrcIdx, dst, yDstIdx, nsamps); ySrcIdx += srcSS; yDstIdx += dstSS; } } else { for (int row = 0; row < srcH; row++) { int xSrcIdx = ySrcIdx; int xDstIdx = yDstIdx; int xEnd = xDstIdx + nsamps; while (xDstIdx < xEnd) { dst[xDstIdx++] = src[xSrcIdx++]; } ySrcIdx += srcSS; yDstIdx += dstSS; } } } } } private void cobbleFloat(Rectangle bounds, Raster dstRaster) { ComponentSampleModel dstSM = (ComponentSampleModel)dstRaster.getSampleModel(); int startX = XToTileX(bounds.x); int startY = YToTileY(bounds.y); int rectXend = bounds.x + bounds.width - 1; int rectYend = bounds.y + bounds.height - 1; int endX = XToTileX(rectXend); int endY = YToTileY(rectYend); // // Get parameters of destination raster // DataBuffer dstDB = dstRaster.getDataBuffer(); float[] dst = DataBufferUtils.getDataFloat(dstDB); int dstPS = dstSM.getPixelStride(); int dstSS = dstSM.getScanlineStride(); boolean tileParamsSet = false; ComponentSampleModel srcSM = null; int srcPS=0, srcSS=0; int xOrg, yOrg; int srcX1, srcY1, srcX2, srcY2, srcW, srcH; for (int y = startY; y <= endY; y++) { for (int x = startX; x <= endX; x++) { Raster tile = getTile(x, y); if (tile == null) { // // Out-of-bounds tile. Zero fill will be supplied // since dstRaster is initialized to zero // continue; } if (! tileParamsSet) { // // These are constant for all tiles, // so only set them once. // srcSM = (ComponentSampleModel)tile.getSampleModel(); srcPS = srcSM.getPixelStride(); srcSS = srcSM.getScanlineStride(); tileParamsSet = true; } // // Intersect the tile and the rectangle // Avoid use of Math.min/max // yOrg = y*tileHeight + tileGridYOffset; srcY1 = yOrg; srcY2 = srcY1 + tileHeight - 1; if (bounds.y > srcY1) srcY1 = bounds.y; if (rectYend < srcY2) srcY2 = rectYend; srcH = srcY2 - srcY1 + 1; xOrg = x*tileWidth + tileGridXOffset; srcX1 = xOrg; srcX2 = srcX1 + tileWidth - 1; if (bounds.x > srcX1) srcX1 = bounds.x; if (rectXend < srcX2) srcX2 = rectXend; srcW = srcX2 - srcX1 + 1; int dstX = srcX1 - bounds.x; int dstY = srcY1 - bounds.y; // Get the actual data array DataBuffer srcDB = tile.getDataBuffer(); float[] src = DataBufferUtils.getDataFloat(srcDB); int nsamps = srcW * srcPS; boolean useArrayCopy = (nsamps >= MIN_ARRAYCOPY_SIZE); int ySrcIdx = (srcY1 - yOrg)*srcSS + (srcX1 - xOrg)*srcPS; int yDstIdx = dstY*dstSS + dstX*dstPS; if (useArrayCopy) { for (int row = 0; row < srcH; row++) { System.arraycopy(src, ySrcIdx, dst, yDstIdx, nsamps); ySrcIdx += srcSS; yDstIdx += dstSS; } } else { for (int row = 0; row < srcH; row++) { int xSrcIdx = ySrcIdx; int xDstIdx = yDstIdx; int xEnd = xDstIdx + nsamps; while (xDstIdx < xEnd) { dst[xDstIdx++] = src[xSrcIdx++]; } ySrcIdx += srcSS; yDstIdx += dstSS; } } } } } private void cobbleDouble(Rectangle bounds, Raster dstRaster) { ComponentSampleModel dstSM = (ComponentSampleModel)dstRaster.getSampleModel(); int startX = XToTileX(bounds.x); int startY = YToTileY(bounds.y); int rectXend = bounds.x + bounds.width - 1; int rectYend = bounds.y + bounds.height - 1; int endX = XToTileX(rectXend); int endY = YToTileY(rectYend); // // Get parameters of destination raster // DataBuffer dstDB = dstRaster.getDataBuffer(); double[] dst = DataBufferUtils.getDataDouble(dstDB); int dstPS = dstSM.getPixelStride(); int dstSS = dstSM.getScanlineStride(); boolean tileParamsSet = false; ComponentSampleModel srcSM = null; int srcPS=0, srcSS=0; int xOrg, yOrg; int srcX1, srcY1, srcX2, srcY2, srcW, srcH; for (int y = startY; y <= endY; y++) { for (int x = startX; x <= endX; x++) { Raster tile = getTile(x, y); if (tile == null) { // // Out-of-bounds tile. Zero fill will be supplied // since dstRaster is initialized to zero // continue; } if (! tileParamsSet) { // // These are constant for all tiles, // so only set them once. // srcSM = (ComponentSampleModel)tile.getSampleModel(); srcPS = srcSM.getPixelStride(); srcSS = srcSM.getScanlineStride(); tileParamsSet = true; } // // Intersect the tile and the rectangle // Avoid use of Math.min/max // yOrg = y*tileHeight + tileGridYOffset; srcY1 = yOrg; srcY2 = srcY1 + tileHeight - 1; if (bounds.y > srcY1) srcY1 = bounds.y; if (rectYend < srcY2) srcY2 = rectYend; srcH = srcY2 - srcY1 + 1; xOrg = x*tileWidth + tileGridXOffset; srcX1 = xOrg; srcX2 = srcX1 + tileWidth - 1; if (bounds.x > srcX1) srcX1 = bounds.x; if (rectXend < srcX2) srcX2 = rectXend; srcW = srcX2 - srcX1 + 1; int dstX = srcX1 - bounds.x; int dstY = srcY1 - bounds.y; // Get the actual data array DataBuffer srcDB = tile.getDataBuffer(); double[] src = DataBufferUtils.getDataDouble(srcDB); int nsamps = srcW * srcPS; boolean useArrayCopy = (nsamps >= MIN_ARRAYCOPY_SIZE); int ySrcIdx = (srcY1 - yOrg)*srcSS + (srcX1 - xOrg)*srcPS; int yDstIdx = dstY*dstSS + dstX*dstPS; if (useArrayCopy) { for (int row = 0; row < srcH; row++) { System.arraycopy(src, ySrcIdx, dst, yDstIdx, nsamps); ySrcIdx += srcSS; yDstIdx += dstSS; } } else { for (int row = 0; row < srcH; row++) { int xSrcIdx = ySrcIdx; int xDstIdx = yDstIdx; int xEnd = xDstIdx + nsamps; while (xDstIdx < xEnd) { dst[xDstIdx++] = src[xSrcIdx++]; } ySrcIdx += srcSS; yDstIdx += dstSS; } } } } } /** * Returns a unique identifier (UID) for this PlanarImage. * This UID may be used when the potential redundancy of the value * returned by the hashCode() method is unacceptable. * An example of this is in generating a key for storing image tiles * in a cache. */ public Object getImageID() { return UID; } } jai-core-1.1.4/src/share/classes/javax/media/jai/widget/0000755000175000017500000000000011633360402022622 5ustar mathieumathieujai-core-1.1.4/src/share/classes/javax/media/jai/widget/javax.media.jai.widget.properties0000644000175000017500000000056310203035544031155 0ustar mathieumathieu# # $RCSfile: javax.media.jai.widget.properties,v $ # # Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. # # Use is subject to license terms. # # $Revision: 1.1 $ # $Date: 2005-02-11 04:57:59 $ # $State: Exp $ # ImageCanvas0=Image Canvas is unable to display supplied RenderedImage. ImageCanvas1=Graphics object passed in is not an instance of Graphics2D. jai-core-1.1.4/src/share/classes/javax/media/jai/widget/ScrollingImagePanel.java0000644000175000017500000002221110203035544027340 0ustar mathieumathieu/* * $RCSfile: ScrollingImagePanel.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:59 $ * $State: Exp $ */ package javax.media.jai.widget; import java.awt.BorderLayout; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Panel; import java.awt.Point; import java.awt.Scrollbar; import java.awt.event.AdjustmentEvent; import java.awt.event.AdjustmentListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.image.RenderedImage; import java.util.Vector; import java.awt.ScrollPane; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.Container; import java.awt.peer.ScrollPanePeer; /** * An extension of java.awt.Panel that contains an ImageCanvas and * vertical and horizontal scrollbars. The origin of the ImageCanvas * is set according to the value of the scrollbars. Additionally, the * origin may be changed by dragging the mouse. The window cursor * will be changed to Cursor.MOVE_CURSOR for the duration of the * drag. * *

      Due to the limitations of BufferedImage, only TYPE_BYTE of band * 1, 2, 3, 4, and TYPE_USHORT of band 1, 2, 3 images can be displayed * using this widget. * * *

      * This class has been deprecated. The source * code has been moved to the samples/widget * directory. These widgets are no longer * supported. * * @deprecated as of JAI 1.1 */ public class ScrollingImagePanel extends ScrollPane implements AdjustmentListener, ComponentListener, MouseListener, MouseMotionListener { /** The ImageCanvas we are controlling. */ protected ImageCanvas ic; /** The RenderedImage displayed by the ImageCanvas. */ protected RenderedImage im; /** The width of the panel. */ protected int panelWidth; /** The height of the panel. */ protected int panelHeight; /** Vector of ViewportListeners. */ protected Vector viewportListeners = new Vector(); /** * Constructs a ScrollingImagePanel of a given size for a * given RenderedImage. */ public ScrollingImagePanel(RenderedImage im, int width, int height) { super(); this.im = im; this.panelWidth = width; this.panelHeight = height; ic = new ImageCanvas(im); getHAdjustable().addAdjustmentListener(this); getVAdjustable().addAdjustmentListener(this); super.setSize(width, height); addComponentListener(this); add("Center",ic); } /** Adds the specified ViewportListener to the panel */ public void addViewportListener(ViewportListener l) { viewportListeners.addElement(l); l.setViewport(getXOrigin(), getYOrigin(), panelWidth, panelHeight); } /** Removes the specified ViewportListener */ public void removeViewportListener(ViewportListener l) { viewportListeners.removeElement(l); } private void notifyViewportListeners(int x, int y, int w, int h) { int i; int numListeners = viewportListeners.size(); for (i = 0; i < numListeners; i++) { ViewportListener l = (ViewportListener)(viewportListeners.elementAt(i)); l.setViewport(x, y, w, h); } } /** * Returns the image canvas. Allows mouse listeners * to be used on the image canvas. * * @since JAI 1.1 */ public ImageCanvas getImageCanvas() { return ic; } /** Returns the XOrigin of the image */ public int getXOrigin() { return ic.getXOrigin(); } /** Returns the YOrigin of the image */ public int getYOrigin() { return ic.getYOrigin(); } /** * Sets the image origin to a given (x, y) position. * The scrollbars are updated appropriately. */ public void setOrigin(int x, int y) { ic.setOrigin(x, y); notifyViewportListeners(x, y, panelWidth, panelHeight); } /** * Set the center of the image to the given coordinates * of the scroll window. */ public synchronized void setCenter(int x, int y) { // scrollbar position int sx = 0; int sy = 0; // bounds int iw = im.getWidth(); int ih = im.getHeight(); int vw = getViewportSize().width; int vh = getViewportSize().height; // scrollbar lengths (proportional indicator) int fx = getHAdjustable().getBlockIncrement(); int fy = getVAdjustable().getBlockIncrement(); if ( x < (vw - iw/2) ) { sx = 0; } else if ( x > iw/2 ) { sx = iw - vw; } else { sx = x + (iw-vw-fx)/2; } if ( y < (vh - ih/2) ) { sy = 0; } else if ( y > ih/2 ) { sy = ih - vh; } else { sy = y + (ih-vh-fy)/2; } getHAdjustable().setValue(sx); getVAdjustable().setValue(sy); notifyViewportListeners(getXOrigin(), getYOrigin(), panelWidth, panelHeight); } /** Sets the panel to display the specified image */ public void set(RenderedImage im) { this.im = im; ic.set(im); } /** Returns the X co-ordinate of the image center. */ public int getXCenter() { return getXOrigin() + panelWidth/2; } /** Returns the Y co-ordinate of the image center. */ public int getYCenter() { return getYOrigin() + panelHeight/2; } /** Called by the AWT when instantiating the component. */ public Dimension getPreferredSize() { return new Dimension(panelWidth, panelHeight); } /** Called by the AWT during instantiation and * when events such as resize occur. */ public void setBounds(int x, int y, int width, int height) { // must set this first super.setBounds(x, y, width, height); int vpw = getViewportSize().width; int vph = getViewportSize().height; int imw = im.getWidth(); int imh = im.getHeight(); if ( vpw >= imw && vph >= imh ) { ic.setBounds(x, y, width, height); } else { // BUG // This fixes bad image positions during resize // but breaks tiles (causes them all to load) // Somehow the graphics context clip area gets // changed in the ImageCanvas update(g) call. // This occurs when the image size is greater // than the viewport when the display first // comes up. Removing the repaint in the // ImageCanvas fixes this, but breaks ImageCanvas. // Should have a new ImageCanvas call that sets // the origin without a repaint. /* causes all tiles to be loaded, breaks scrolling, fixes resize */ //setOrigin(0, 0); /* causes image to shift incorrectly on resize event */ ic.setBounds(x, y, vpw, vph); } this.panelWidth = width; this.panelHeight = height; } /** Called by the AWT when either scrollbar changes. */ public void adjustmentValueChanged(AdjustmentEvent e) { } /// ComponentListener Interface /** Called when the ImagePanel is resized */ public void componentResized(ComponentEvent e) { notifyViewportListeners(getXOrigin(), getYOrigin(), panelWidth, panelHeight); } /** Ignored */ public void componentHidden(ComponentEvent e) {} /** Ignored */ public void componentMoved(ComponentEvent e) {} /** Ignored */ public void componentShown(ComponentEvent e) {} // // Mouse drag interface // /** The initial Point of a mouse drag. */ protected Point moveSource; /** True if we are in the middle of a mouse drag. */ protected boolean beingDragged = false; /** A place to save the cursor. */ protected Cursor defaultCursor = null; /** Called at the beginning of a mouse drag. */ private synchronized void startDrag(Point p) { this.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); beingDragged = true; moveSource = p; } /** Called for each point of a mouse drag. */ protected synchronized void updateDrag(Point moveTarget) { if (beingDragged) { int dx = moveSource.x - moveTarget.x; int dy = moveSource.y - moveTarget.y; moveSource = moveTarget; int x = getHAdjustable().getValue() + dx; int y = getVAdjustable().getValue() + dy; setOrigin(x, y); } } /** Called at the end of a mouse drag. */ private synchronized void endDrag() { this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); beingDragged = false; } /** Called by the AWT when the mouse button is pressed. */ public void mousePressed(MouseEvent me) { startDrag(me.getPoint()); } /** Called by the AWT as the mouse is dragged. */ public void mouseDragged(MouseEvent me) { updateDrag(me.getPoint()); } /** Called by the AWT when the mouse button is released. */ public void mouseReleased(MouseEvent me) { endDrag(); } /** Called by the AWT when the mouse leaves the component. */ public void mouseExited(MouseEvent me) { endDrag(); } /** Ignored. */ public void mouseClicked(MouseEvent me) {} /** Ignored. */ public void mouseMoved(MouseEvent me) {} /** Ignored. */ public void mouseEntered(MouseEvent me) {} } jai-core-1.1.4/src/share/classes/javax/media/jai/widget/ImageCanvas.java0000644000175000017500000003435110203035544025647 0ustar mathieumathieu/* * $RCSfile: ImageCanvas.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:58 $ * $State: Exp $ */ package javax.media.jai.widget; import java.awt.Canvas; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.Graphics; import java.awt.Image; import java.awt.Point; import java.awt.Rectangle; import java.awt.color.ColorSpace; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.ComponentColorModel; import java.awt.image.ComponentSampleModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.MemoryImageSource; import java.awt.image.PixelInterleavedSampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.peer.ComponentPeer; import java.util.HashSet; import java.util.Iterator; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationNearest; import javax.media.jai.JAI; import javax.media.jai.PlanarImage; /** * A simple output widget for a RenderedImage. ImageCanvas subclasses * java.awt.Canvas, and can be used in any context that calls for a * Canvas. It monitors resize and update events and automatically * requests tiles from its source on demand. Any displayed area outside * the image is displayed in grey. * *

      There is currently no policy regarding what sorts of widgets, * if any, will be part of JAI. * *

      Due to the limitations of BufferedImage, only TYPE_BYTE of band * 1, 2, 3, 4, and TYPE_USHORT of band 1, 2, 3 images can be displayed * using this widget. * * *

      * This class has been deprecated. The source * code has been moved to the samples/widget * directory. These widgets are no longer * supported. * * @deprecated as of JAI 1.1 */ public class ImageCanvas extends Canvas { /** The source RenderedImage. */ protected RenderedImage im; /** The image's SampleModel. */ protected SampleModel sampleModel; /** The image's ColorModel or one we supply. */ protected ColorModel colorModel; /** The image's min X tile. */ protected int minTileX; /** The image's max X tile. */ protected int maxTileX; /** The image's min Y tile. */ protected int minTileY; /** The image's max Y tile. */ protected int maxTileY; /** The image's tile width. */ protected int tileWidth; /** The image's tile height. */ protected int tileHeight; /** The image's tile grid X offset. */ protected int tileGridXOffset; /** The image's tile grid Y offset. */ protected int tileGridYOffset; protected int imWidth; protected int imHeight; /** used to center image in it's container */ protected int padX; protected int padY; protected boolean drawBorder = false; /** The pixel to display in the upper left corner or the canvas. */ protected int originX; /** The pixel to display in the upper left corner or the canvas. */ protected int originY; /** The width of the canvas. */ protected int canvasWidth = 0; /** The height of the canvas. */ protected int canvasHeight = 0; private Color grayColor = new Color(192, 192, 192); private Color backgroundColor = null; /** Initializes the ImageCanvas. */ private synchronized void initialize() { int mx = im.getMinX(); int my = im.getMinY(); if ((mx < 0) || (my < 0)) { ParameterBlock pb = new ParameterBlock(); pb.addSource(im); pb.add((float)Math.max(-mx, 0)); pb.add((float)Math.max(-my, 0)); pb.add(new InterpolationNearest()); im = JAI.create("translate", pb, null); } this.sampleModel = im.getSampleModel(); // First check whether the opimage has already set a suitable ColorModel this.colorModel = im.getColorModel(); if (this.colorModel == null) { // If not, then create one. this.colorModel = PlanarImage.createColorModel(im.getSampleModel()); if (this.colorModel == null) { throw new IllegalArgumentException(JaiI18N.getString("ImageCanvas0")); } } Object col = im.getProperty("background_color"); if (col != Image.UndefinedProperty) { backgroundColor = (Color)col; } minTileX = im.getMinTileX(); maxTileX = im.getMinTileX() + im.getNumXTiles() - 1; minTileY = im.getMinTileY(); maxTileY = im.getMinTileY() + im.getNumYTiles() - 1; tileWidth = im.getTileWidth(); tileHeight = im.getTileHeight(); tileGridXOffset = im.getTileGridXOffset(); tileGridYOffset = im.getTileGridYOffset(); imWidth = im.getMinX() + im.getWidth(); imHeight = im.getMinY() + im.getHeight(); originX = originY = 0; } /** * Constructs an ImageCanvas to display a RenderedImage. * * @param im a RenderedImage to be displayed. * @param drawBorder true if a raised border is desired. */ public ImageCanvas(RenderedImage im, boolean drawBorder) { this.im = im; this.drawBorder = drawBorder; initialize(); } /** * Constructs an ImageCanvas to display a RenderedImage. * * @param im a RenderedImage to be displayed. */ public ImageCanvas(RenderedImage im) { this(im, false); } public void addNotify() { super.addNotify(); initialize(); } /** Changes the source image to a new RenderedImage. */ public synchronized void set(RenderedImage im) { this.im = im; initialize(); repaint(); } /** Changes the pixel to set Origin at x,y */ public void setOrigin(int x, int y) { padX = 0; padY = 0; originX = x; originY = y; repaint(); } public int getXOrigin() { return originX; } public int getYOrigin() { return originY; } public int getXPad() { return padX; } public int getYPad() { return padY; } public Dimension getMinimumSize() { return new Dimension(im.getMinX() + im.getWidth() + (drawBorder ? 4 : 0), im.getMinY() + im.getHeight() + (drawBorder ? 4 : 0)); } public Dimension getPreferredSize() { return getMinimumSize(); } public Dimension getMaximumSize() { return getMinimumSize(); } /** Records a new size. Called by the AWT. */ public void setBounds(int x, int y, int width, int height) { super.setBounds(x, y, width, height); canvasWidth = width; canvasHeight = height; padX = Math.max((canvasWidth - imWidth - (drawBorder ? 4 : 0))/2, 0); padY = Math.max((canvasHeight - imHeight - (drawBorder ? 4 : 0))/2, 0); } private int XtoTileX(int x) { return (int) Math.floor((double) (x - tileGridXOffset)/tileWidth); } private int YtoTileY(int y) { return (int) Math.floor((double) (y - tileGridYOffset)/tileHeight); } private int TileXtoX(int tx) { return tx*tileWidth + tileGridXOffset; } private int TileYtoY(int ty) { return ty*tileHeight + tileGridYOffset; } /** * There is no need to erase prior to drawing, so we override the * default update method to simply call paint(). */ public void update(Graphics g) { paint(g); } /** * Paint the image onto a Graphics object. The painting is * performed tile-by-tile, and includes a grey region covering the * unused portion of image tiles as well as the general * background. */ public synchronized void paint(Graphics g) { if (im == null) { return; } Graphics2D g2D = null; if (g instanceof Graphics2D) { g2D = (Graphics2D)g; } else { System.err.println(JaiI18N.getString("ImageCanvas1")); return; } Color saveColor = g2D.getColor(); if (drawBorder) { g.setColor(new Color(171, 171, 171)); g.draw3DRect(padX, padY, imWidth + 3, imHeight + 3, true); g.draw3DRect(padX + 1, padY + 1, imWidth + 1, imHeight + 1, true); } // Get the clipping rectangle and translate it into image coordinates. Rectangle clipBounds = g.getClipBounds(); if (clipBounds == null) { clipBounds = new Rectangle(0, 0, canvasWidth, canvasHeight); } int border = drawBorder ? 2 : 0; int transX = padX + border - originX; int transY = padY + border - originY; clipBounds.translate(-transX, -transY); // Determine the extent of the clipping region in tile coordinates. int txmin, txmax, tymin, tymax; txmin = XtoTileX(clipBounds.x); txmin = Math.max(txmin, minTileX); txmin = Math.min(txmin, maxTileX); txmax = XtoTileX(clipBounds.x + clipBounds.width - 1); txmax = Math.max(txmax, minTileX); txmax = Math.min(txmax, maxTileX); tymin = YtoTileY(clipBounds.y); tymin = Math.max(tymin, minTileY); tymin = Math.min(tymin, maxTileY); tymax = YtoTileY(clipBounds.y + clipBounds.height - 1); tymax = Math.max(tymax, minTileY); tymax = Math.min(tymax, maxTileY); if (backgroundColor != null) { g2D.setColor(backgroundColor); } else { // Draw grey over unused area g2D.setColor(grayColor); } int xmin = im.getMinX(); int xmax = im.getMinX()+im.getWidth(); int ymin = im.getMinY(); int ymax = im.getMinY()+im.getHeight(); int screenX = clipBounds.x + clipBounds.width; int screenY = clipBounds.y + clipBounds.height; // Left if (xmin > clipBounds.x) { g2D.fillRect(clipBounds.x + transX, clipBounds.y + transY, xmin - clipBounds.x, clipBounds.height); } // Right if (xmax < screenX) { g2D.fillRect(xmax + transX, clipBounds.y + transY, screenX - xmax, clipBounds.height); } // Top if (ymin > clipBounds.y) { g2D.fillRect(xmin + transX, clipBounds.y + transY, xmax - xmin, ymin - clipBounds.y); } // Bottom if (ymax < screenY) { g2D.fillRect(xmin + transX, ymax + transY, xmax - xmin, screenY - ymax); } // needed for clipping (crop op) g2D.setClip(new Rectangle(transX + im.getMinX(), transY + im.getMinY(), im.getWidth(), im.getHeight())); // Get all tiles which overlap the clipping region. Point[] tileIndices = new Point[(txmax-txmin+1)*(tymax-tymin+1)]; int index = 0; for(int tj = tymin; tj <= tymax; tj++) { for(int ti = txmin; ti <= txmax; ti++) { tileIndices[index++] = new Point(ti, tj); } } Raster[] tiles = PlanarImage.wrapRenderedImage(im).getTiles(tileIndices); // Loop over tiles within the clipping region int numTiles = tiles.length; for (int tileNum = 0; tileNum < numTiles; tileNum++) { Raster tile = tiles[tileNum]; int tx = tile.getMinX(); int ty = tile.getMinY(); if ( tile != null ) { WritableRaster wr = tile instanceof WritableRaster ? ((WritableRaster)tile).createWritableTranslatedChild(0, 0) : tile.createWritableRaster(sampleModel, tile.getDataBuffer(), new Point(0, 0)); BufferedImage bi = new BufferedImage(colorModel, wr, colorModel.isAlphaPremultiplied(), null); AffineTransform transform = AffineTransform.getTranslateInstance(tx + transX, ty + transY); if (backgroundColor != null) { g2D.fillRect(tx + transX, ty + transY, tileWidth, tileHeight); } g2D.drawRenderedImage(bi, transform); } } // Restore color g2D.setColor(saveColor); notifyPaintListeners(g2D); } /** * An interface used to notify listeners during a paint * just after the image has been painted on the image canvas. This * allows registered listeners to draw additional graphics on top * of the image. * * @since JAI 1.1 */ public interface PaintListener { /** * Called from ImageCanvas.paint just after * the image has been drawn on the canvas. */ public void paint(ImageCanvas ic, Graphics g); } private HashSet paintListeners = new HashSet(); /** * Adds the specified PaintListener to the canvas. * * @since JAI 1.1 */ public void addPaintListener(PaintListener pl) { paintListeners.add(pl); } /** * Removes the specified PaintListener from the canvas. * * @since JAI 1.1 */ public void removePaintListener(PaintListener pl) { paintListeners.remove(pl); } private void notifyPaintListeners(Graphics g) { Iterator it = paintListeners.iterator(); while (it.hasNext()) { ((PaintListener)it.next()).paint(this, g); } } } jai-core-1.1.4/src/share/classes/javax/media/jai/widget/ViewportListener.java0000644000175000017500000000225310203035544027012 0ustar mathieumathieu/* * $RCSfile: ViewportListener.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:59 $ * $State: Exp $ */ package javax.media.jai.widget; /** * An interface used by the ScrollingImagePanel * class to inform listeners of the current viewable area of the image. * * @see ScrollingImagePanel * *

      * This class has been deprecated. The source * code has been moved to the samples/widget * directory. These widgets are no longer * supported. * * @deprecated as of JAI 1.1 */ public interface ViewportListener { /** * Called to inform the listener of the currently viewable area od * the source image. * * @param x The X coordinate of the upper-left corner of the current * viewable area. * @param y The Y coordinate of the upper-left corner of the current * viewable area. * @param width The width of the current viewable area in pixels. * @param height The height of the current viewable area in pixels. */ void setViewport(int x, int y, int width, int height); } jai-core-1.1.4/src/share/classes/javax/media/jai/widget/JaiI18N.java0000644000175000017500000000125010203035544024564 0ustar mathieumathieu/* * $RCSfile: JaiI18N.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:58 $ * $State: Exp $ */ /** *

      * This class has been deprecated. The source * code has been moved to the samples/widget * directory. These widgets are no longer * supported. * * @deprecated as of JAI 1.1 */ package javax.media.jai.widget; import com.sun.media.jai.util.PropertyUtil; class JaiI18N { static String packageName = "javax.media.jai.widget"; public static String getString(String key) { return PropertyUtil.getString(packageName, key); } } jai-core-1.1.4/src/share/classes/javax/media/jai/WarpPerspective.java0000644000175000017500000002464010203035544025331 0ustar mathieumathieu/* * $RCSfile: WarpPerspective.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:24 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Rectangle; import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.Point2D; /** * A description of a perspective (projective) warp. * *

      The transform is specified as a mapping from destination * space to source space. This is a backward mapping, as opposed * to the forward mapping used in the "Affine" operation. * */ public final class WarpPerspective extends Warp { private PerspectiveTransform transform; private PerspectiveTransform invTransform; /** * Constructs a WarpPerspective with a given * transform mapping destination pixels into source space. Note * that this is a backward mapping as opposed to the forward * mapping used in AffineOpImage. * * @param transform The destination to source transform. * @throws IllegalArgumentException if transform is null */ public WarpPerspective(PerspectiveTransform transform) { if (transform == null) { throw new IllegalArgumentException(JaiI18N.getString("WarpPerspective0")); } this.transform = transform; // Transform could be non-invertible. // If so the transform is set to null. try { invTransform = transform.createInverse(); } catch (NoninvertibleTransformException e) { invTransform = null; } catch (CloneNotSupportedException e) { invTransform = null; } } /** * Returns a clone of the PerspectiveTransform * associated with this WarpPerspective object. * * @return An instance of PerspectiveTransform. */ public PerspectiveTransform getTransform() { return (PerspectiveTransform)transform.clone(); } /** * Computes the source subpixel positions for a given rectangular * destination region, subsampled with an integral period. The * destination region is specified using normal integral (full * pixel) coordinates. The source positions returned by the * method are specified in floating point. * * @param x The minimum X coordinate of the destination region. * @param y The minimum Y coordinate of the destination region. * @param width The width of the destination region. * @param height The height of the destination region. * @param periodX The horizontal sampling period. * @param periodY The horizontal sampling period. * * @param destRect A float array containing at least * 2*((width+periodX-1)/periodX)* * ((height+periodY-1)/periodY) * elements, or null. If null, a * new array will be constructed. * * @return A reference to the destRect parameter if * it is non-null, or a new * float array otherwise. * @throw ArrayBoundsException if destRect is too small. */ public float[] warpSparseRect(int x, int y, int width, int height, int periodX, int periodY, float[] destRect) { if (destRect == null) { destRect = new float[2 * ((width + periodX - 1) / periodX) * ((height + periodY - 1) / periodY)]; } double[][] matrix = new double[3][3]; matrix = transform.getMatrix(matrix); float m00 = (float)matrix[0][0]; float m01 = (float)matrix[0][1]; float m02 = (float)matrix[0][2]; float m10 = (float)matrix[1][0]; float m11 = (float)matrix[1][1]; float m12 = (float)matrix[1][2]; float m20 = (float)matrix[2][0]; float m21 = (float)matrix[2][1]; float m22 = (float)matrix[2][2]; // // x' = (m00x + m01y + m02) / (m20x + m21y + m22) // y' = (m10x + m11y + m12) / (m20x + m21y + m22) // float dx = m00 * periodX; float dy = m10 * periodX; float dw = m20 * periodX; float sx = x + 0.5F; // shift coordinate by 0.5 width += x; height += y; int index = 0; // destRect index for (int j = y; j < height; j += periodY) { float sy = j + 0.5F; float wx = m00 * sx + m01 * sy + m02; float wy = m10 * sx + m11 * sy + m12; float w = m20 * sx + m21 * sy + m22; for (int i = x; i < width; i += periodX) { float tx, ty; try { tx = wx / w; ty = wy / w; } catch (java.lang.ArithmeticException e) { // w is 0, do not warp tx = i + 0.5F; // to be subtracted below ty = j + 0.5F; } destRect[index++] = tx - 0.5F; destRect[index++] = ty - 0.5F; wx += dx; wy += dy; w += dw; } } return destRect; } /** * Computes a Rectangle that is guaranteed to enclose the region * of the source that is required in order to produce a given * rectangular output region. * * @param destRect The Rectangle in destination coordinates. * @throws IllegalArgumentException if destRect is null. * @return A Rectangle in the source coordinate * system that is guaranteed to contain all pixels * referenced by the output of warpRect() on * the destination region. */ public Rectangle mapDestRect(Rectangle destRect) { if ( destRect == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } int x0 = destRect.x; int x1 = destRect.x + destRect.width; int y0 = destRect.y; int y1 = destRect.y + destRect.height; Point2D[] pts = new Point2D[4]; pts[0] = new Point2D.Float(x0, y0); pts[1] = new Point2D.Float(x1, y0); pts[2] = new Point2D.Float(x0, y1); pts[3] = new Point2D.Float(x1, y1); transform.transform(pts, 0, pts, 0, 4); int minX = Integer.MAX_VALUE; int maxX = Integer.MIN_VALUE; int minY = Integer.MAX_VALUE; int maxY = Integer.MIN_VALUE; for (int i = 0; i < 4; i++) { int px = (int)pts[i].getX(); int py = (int)pts[i].getY(); minX = Math.min(minX, px); maxX = Math.max(maxX, px); minY = Math.min(minY, py); maxY = Math.max(maxY, py); } return new Rectangle(minX, minY, maxX - minX, maxY - minY); } /** * Computes a Rectangle that is guaranteed to enclose the region * of the source that is required in order to produce a given * rectangular output region. * * @param srcRect The Rectangle in source coordinates. * @throws IllegalArgumentException is srcRect is null. * @return A Rectangle in the destination coordinate * system that is guaranteed to contain all pixels * within the forward mapping of the source rectangle. * * @since JAI 1.1 */ public Rectangle mapSourceRect(Rectangle srcRect) { if ( srcRect == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } // Return null if no forward mapping could be derived if (invTransform == null) { return null; } int x0 = srcRect.x; int x1 = srcRect.x + srcRect.width; int y0 = srcRect.y; int y1 = srcRect.y + srcRect.height; Point2D[] pts = new Point2D[4]; pts[0] = new Point2D.Float(x0, y0); pts[1] = new Point2D.Float(x1, y0); pts[2] = new Point2D.Float(x0, y1); pts[3] = new Point2D.Float(x1, y1); invTransform.transform(pts, 0, pts, 0, 4); int minX = Integer.MAX_VALUE; int maxX = Integer.MIN_VALUE; int minY = Integer.MAX_VALUE; int maxY = Integer.MIN_VALUE; for (int i = 0; i < 4; i++) { int px = (int)pts[i].getX(); int py = (int)pts[i].getY(); minX = Math.min(minX, px); maxX = Math.max(maxX, px); minY = Math.min(minY, py); maxY = Math.max(maxY, py); } return new Rectangle(minX, minY, maxX - minX, maxY - minY); } /** * Computes the source point corresponding to the supplied point. * *

      This method returns the return value of * transform.transform(destPt, null).

      * * @param destPt the position in destination image coordinates * to map to source image coordinates. * * @return a Point2D of the same class as * destPt. * * @throws IllegalArgumentException if destPt is * null. * * @since JAI 1.1.2 */ public Point2D mapDestPoint(Point2D destPt) { if (destPt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return transform.transform(destPt, null); } /** * Computes the destination point corresponding to the supplied point. * *

      If the transform is invertible, this method returns the return * value of transform.inverseTransform(destPt, null). If * the transform is not invertible, null is returned.

      * * @param sourcePt the position in source image coordinates * to map to destination image coordinates. * * @return a Point2D of the same class as * sourcePt or null> if the transform is * not invertible. * * @throws IllegalArgumentException if sourcePt is * null. * * @since JAI 1.1.2 */ public Point2D mapSourcePoint(Point2D sourcePt) { if (sourcePt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return invTransform != null ? invTransform.transform(sourcePt, null) : null; } } jai-core-1.1.4/src/share/classes/javax/media/jai/RenderableImageAdapter.java0000644000175000017500000002710010203035544026507 0ustar mathieumathieu/* * $RCSfile: RenderableImageAdapter.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:20 $ * $State: Exp $ */ package javax.media.jai; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderableImage; import java.awt.image.renderable.RenderContext; import java.beans.PropertyChangeListener; import java.util.Hashtable; import java.util.Vector; import com.sun.media.jai.util.PropertyUtil; /** * An adapter class for externally-generated RenderableImages. All * methods are simply forwarded to the image being adapted. The * purpose of this class is simply to ensure that the PropertySource * interface is available for all JAI RenderableImages. * *

      The set of properties available on the image will be a combination of * those defined locally via setProperty() and those defined * on the source image with the local properties taking precedence. No * PropertySourceChangeEvent will be generated as a result of * changes to the property set of the source image. */ public final class RenderableImageAdapter implements RenderableImage, WritablePropertySource { /** A reference to the external RenderableImage. */ private RenderableImage im; /** A helper object to manage firing events. */ private PropertyChangeSupportJAI eventManager = null; /** A helper object to manage the image properties. */ private WritablePropertySourceImpl properties = null; /** * Adapts a RenderableImage into a RenderableImageAdapter. * If the image is already an instance of RenderableImageAdapter, * it is returned unchanged. * * @param im a RenderableImage. * * @return a RenderableImageAdapter. * * @throws IllegalArgumentException if im is null. */ public static RenderableImageAdapter wrapRenderableImage(RenderableImage im) { if (im == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } else if (im instanceof RenderableImageAdapter) { return (RenderableImageAdapter)im; } else { return new RenderableImageAdapter(im); } } /** * Constructs a RenderableImageAdapter from a RenderableImage. * * @throws IllegalArgumentException if im is null. */ public RenderableImageAdapter(RenderableImage im) { if ( im == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.im = im; eventManager = new PropertyChangeSupportJAI(this); properties = new WritablePropertySourceImpl(null, null, eventManager); } /** * Returns the reference to the external RenderableImage * originally supplied to the constructor. * * @since JAI 1.1.2 */ public final RenderableImage getWrappedImage() { return im; } /* * 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 final Vector getSources() { return im.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. * @throws IllegalArgumentException if name is * null. * @return a reference to the property Object, or the value * java.awt.Image.UndefinedProperty. */ public final Object getProperty(String name) { // Retrieve the property from the local cache. Object property = properties.getProperty(name); // If it is still undefined, forward the call. if(property == java.awt.Image.UndefinedProperty) { property = im.getProperty(name); } return property; } /** * Returns the class expected to be returned by a request for * the property with the specified name. If this information * is unavailable, null will be returned. * * @return The Class expected to be return by a * request for the value of this property or null. * @throws IllegalArgumentException if name is * null. * * @since JAI 1.1 */ public Class getPropertyClass(String name) { // Get the class if the property is local. Class propClass = properties.getPropertyClass(name); // If not local ... if(propClass == null) { // Get the property value. Object propValue = getProperty(name); if(propValue != java.awt.Image.UndefinedProperty) { // If the property is defined, get the class. propClass = propValue.getClass(); } } return propClass; } /** * Returns a list of the properties recognized by this image. If * no properties are available, null will be * returned. * * @return an array of Strings representing valid * property names. */ public final String[] getPropertyNames() { return RenderedImageAdapter.mergePropertyNames( properties.getPropertyNames(), im.getPropertyNames()); } /** * Returns an array of Strings recognized as names by * this property source that begin with the supplied prefix. If * no property names match, null will be returned. * The comparison is done in a case-independent manner. * * @throws IllegalArgumentException if prefix is * null. * @return an array of Strings giving the valid * property names. */ public String[] getPropertyNames(String prefix) { return PropertyUtil.getPropertyNames(getPropertyNames(), prefix); } /** * Sets a property on a RenderableImageAdapter. * * @param name a String containing the property's name. * @param value the property, as a general Object. * * @throws IllegalArgumentException If name or * value is null. * * @since JAI 1.1 */ public void setProperty(String name, Object value) { properties.setProperty(name, value); } /** * Removes the named property from the RenderableImageAdapter. * * @throws IllegalArgumentException if name is * null. * * @since JAI 1.1 */ public void removeProperty(String name) { properties.removeProperty(name); } /** * Add a PropertyChangeListener to the listener list. The * listener is registered for all properties. * * @since JAI 1.1 */ public void addPropertyChangeListener(PropertyChangeListener listener) { eventManager.addPropertyChangeListener(listener); } /** * Add a PropertyChangeListener for a specific property. The case of * the name is ignored. * * @since JAI 1.1 */ public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { eventManager.addPropertyChangeListener(propertyName, listener); } /** * Remove a PropertyChangeListener from the listener list. This * removes a PropertyChangeListener that was registered for all * properties. * * @since JAI 1.1 */ public void removePropertyChangeListener(PropertyChangeListener listener) { eventManager.removePropertyChangeListener(listener); } /** * Remove a PropertyChangeListener for a specific property. The case * of the name is ignored. * * @since JAI 1.1 */ public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { eventManager.removePropertyChangeListener(propertyName, listener); } /** * 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 final float getWidth() { return im.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 final float getHeight() { return im.getHeight(); } /** * Gets the minimum X coordinate of the rendering-independent image. */ public final float getMinX() { return im.getMinX(); } /** * Gets the minimum Y coordinate of the rendering-independent image. */ public final float getMinY() { return im.getMinY(); } /** * 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. */ public final boolean isDynamic() { return im.isDynamic(); } /** * Gets 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. * * @param w the width of rendered image in pixels. * @param h the height of rendered image in pixels. * @param hints a RenderingHints object containing rendering hints. * @return a RenderedImage containing the rendered data. */ public final RenderedImage createScaledRendering(int w, int h, RenderingHints hints) { return im.createScaledRendering(w, h, hints); } /** * 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 final RenderedImage createDefaultRendering() { return im.createDefaultRendering(); } /** * Gets a RenderedImage instance of this image from a * RenderContext. This is the most general way to obtain a * rendering of a RenderableImage. * * @param renderContext the RenderContext to use to produce the rendering. * @return a RenderedImage containing the rendered data. */ public final RenderedImage createRendering(RenderContext renderContext) { return im.createRendering(renderContext); } } jai-core-1.1.4/src/share/classes/javax/media/jai/WarpGeneralPolynomial.java0000644000175000017500000001450610203035544026461 0ustar mathieumathieu/* * $RCSfile: WarpGeneralPolynomial.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:24 $ * $State: Exp $ */ package javax.media.jai; /** * A general polynomial-based description of an image warp. * *

      The mapping is defined by two bivariate polynomial functions * X(x, y) and Y(x, y) that define the source X and Y positions * that map to a given destination (x, y) pixel coordinate. * *

      The functions X(x, y) and Y(x, y) have the form: *

       * SUM{i = 0 to n} {SUM{j = 0 to i}{a_ij*x^(i - j)*y^j}}
       * 
      * * @see WarpPolynomial * */ public final class WarpGeneralPolynomial extends WarpPolynomial { /** * Constructs a WarpGeneralPolynomial with a given transform mapping * destination pixels into source space. Note that this is * a backward mapping as opposed to the forward mapping used in * AffineOpImage. * *

      The xCoeffs and yCoeffs parameters * must contain the same number of coefficients, * (n + 1)(n + 2)/2, for some n, where * n is the non-negative degree power of the polynomial. * The coefficients, in order, are associated with the terms: * *

           * 1, x, y, x^2, x*y, y^2, ..., x^n, x^(n - 1)*y, ..., x*y^(n - 1), y^n
           * 
      * * and coefficients of value 0 cannot be omitted. * *

      The destination pixel coordinates (the arguments to the X() * and Y() functions) are given in normal integral pixel * coordinates, while the output of the functions is given in * fixed-point, subpixel coordinates with a number of fractional * bits specified by the subsampleBitsH and subsampleBitsV * parameters. * * @param xCoeffs The destination to source transform coefficients for * the X coordinate. * @param yCoeffs The destination to source transform coefficients for * the Y coordinate. * @param preScaleX The scale factor to apply to input (dst) X positions. * @param preScaleY The scale factor to apply to input (dst) Y positions. * @param postScaleX The scale factor to apply to output (src) X positions. * @param postScaleY The scale factor to apply to output (src) Y positions. * @throws IllegalArgumentException if arrays xCoeffs and yCoeffs do not * have the correct number of entries. */ public WarpGeneralPolynomial(float[] xCoeffs, float[] yCoeffs, float preScaleX, float preScaleY, float postScaleX, float postScaleY) { super(xCoeffs, yCoeffs, preScaleX, preScaleY, postScaleX, postScaleY); } /** * Constructs a WarpGeneralPolynomial with pre- and post-scale * factors of 1. * * @param xCoeffs The destination to source transform coefficients for * the X coordinate. * @param yCoeffs The destination to source transform coefficients for * the Y coordinate. * @throws IllegalArgumentException if arrays xCoeffs and yCoeffs do not * have the correct number of entries. */ public WarpGeneralPolynomial(float[] xCoeffs, float[] yCoeffs) { this(xCoeffs, yCoeffs, 1.0F, 1.0F, 1.0F, 1.0F); } /** * Computes the source subpixel positions for a given rectangular * destination region, subsampled with an integral period. * * @param x The minimum X coordinate of the destination region. * @param y The minimum Y coordinate of the destination region. * @param width The width of the destination region. * @param height The height of the destination region. * @param periodX The horizontal sampling period. * @param periodY The vertical sampling period. * @param destRect An int array containing at least * 2*((width+periodX-1)/periodX)*((height+periodY-1)/periodY) * elements, or null. If null, a * new array will be constructed. * * @return a reference to the destRect parameter if it is * non-null, or a new int array of length * 2*width*height otherwise. * @throws ArrayBoundsException if destRect array is too small */ public float[] warpSparseRect(int x, int y, int width, int height, int periodX, int periodY, float[] destRect) { // XXX: Calculations should be performed in doubles // XXX: Calculations should use Horner's rule if (destRect == null) { destRect = new float[2 * ((width + periodX - 1) / periodX) * ((height + periodY - 1) / periodY)]; } // Power tables for x and y float[] xPows = new float[degree + 1]; float[] yPows = new float[degree + 1]; xPows[0] = 1.0F; yPows[0] = 1.0F; width += x; height += y; int index = 0; // destRect index for (int j = y; j < height; j += periodY) { // Initialize power table for the current y position (j) float y1 = (j + 0.5F) * preScaleY; for (int n = 1; n <= degree; n++) { yPows[n] = yPows[n - 1] * y1; } for (int i = x; i < width; i += periodX) { // Initialize power table for current x position (i) float x1 = (i + 0.5F) * preScaleX; for (int n = 1; n <= degree; n++) { xPows[n] = xPows[n - 1] * x1; } float wx = 0.0F; // warped x float wy = 0.0F; // warped y int c = 0; // coefficient index for (int nx = 0; nx <= degree; nx++) { for (int ny = 0; ny <= nx; ny++) { float t = xPows[nx - ny] * yPows[ny]; wx += xCoeffs[c] * t; wy += yCoeffs[c] * t; c++; } } destRect[index++] = wx * postScaleX - 0.5F; destRect[index++] = wy * postScaleY - 0.5F; } } return destRect; } } jai-core-1.1.4/src/share/classes/javax/media/jai/Histogram.java0000644000175000017500000017730510203035544024152 0ustar mathieumathieu/* * $RCSfile: Histogram.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:08 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.SampleModel; import java.io.Serializable; import java.util.LinkedList; import java.util.ListIterator; /** * This class represents a histogram accumulated from a * RenderedImage. * *

      A "bin" is a container, where each element stores the total number * of pixel samples of an image whose * values lie within a given range. A histogram of an image consists of * a list of such bins whose range does not overlap with each other * (mutually exclusive). For an image that has multiple samples per * pixel (multi-banded images), a separate list of bins represents each * individual band. * *

      A "low-value" specifies the lowest inclusive pixel value to be * checked, and a "high-value" specifies the highest exclusive pixel * value to be checked. Therefore, the width of a bin * (binWidth) is determined by * (highValue - lowValue) / numberOfBins. The range * of bin i is defined as from * lowValue + i * binWidth inclusive to * lowValue + (i + 1) * binWidth exclusive. * *

      The image may have any data type. Its histogram may be accumulated * over the entire image, or over a specific region-of-interest (ROI) * within the image's bounds. Furthermore, the horizontal and vertical * subsampling factors specify the rate of sampling in the two directions, * so that only every nth pixel will be counted. This allows the * accuracy of the histogram to be traded for the speed of the computation. * Of course a subsampling rate of 1 means every pixel will be counted. * *

      The "Histogram" operator generates the histogram data of an image * and uses this object to store the final pixel counts. The operator * returns an instance of this class when a request is made via the * getProperty method for the "histogram" property. The actual * bins may be obtained by calling the getBins method. * * @see ROI * @see javax.media.jai.operator.HistogramDescriptor * */ public class Histogram implements Serializable { /** The number of bins used for each band of the image. */ private int[] numBins; /** * The lowest inclusive pixel value of the image checked for each band. */ private double[] lowValue; /** * The highest exclusive pixel value of the image checked for each band. */ private double[] highValue; /** * The number of bands of the image from which the histogram is * accumulated. This is the same as the number of bands of the * bins (bins.length). */ private int numBands; /** The width of a bin for each band. */ private double[] binWidth; /** The bins for each band, used to hold the pixel counts. */ private int[][] bins = null; /** The total bin count over all bins for each band. */ private int[] totals = null; /** The mean value over all bins for each band. */ private double[] mean = null; /** * Copy an int array into a new int array of a given length padding * with zeroth element if needed. * * @throws IllegalArgumentException If array * is null or its length is 0. */ private static final int[] fill(int[] array, int newLength) { int[] newArray = null; if (array == null || array.length == 0) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } else if (newLength > 0) { newArray = new int[newLength]; int oldLength = array.length; for(int i = 0; i < newLength; i++) { if(i < oldLength) { newArray[i] = array[i]; } else { newArray[i] = array[0]; } } } return newArray; } /** * Copy an double array into a new double array of a given length padding * with zeroth element if needed. * * @throws IllegalArgumentException If array * is null or its length is 0. */ private static final double[] fill(double[] array, int newLength) { double[] newArray = null; if (array == null || array.length == 0) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } else if (newLength > 0) { newArray = new double[newLength]; int oldLength = array.length; for(int i = 0; i < newLength; i++) { if(i < oldLength) { newArray[i] = array[i]; } else { newArray[i] = array[0]; } } } return newArray; } /** * Constructor. * *

      This constructor should be used when numBins, * lowValue, and/or highValues are * different for each band of the image. The length of the arrays * indicates the number of bands the image has, and the three arrays * must have the same length. * *

      Since this constructor has no way of knowing the actual number * of bands of the image, the length of the arrays is not checked * against anything. Therefore, it is very important that the caller * supplies the correct arrays of correct lengths, or errors will occur. * * @param numBins The number of bins for each band of the image. * The length of this array indicates the number of bands for * this histogram. * @param lowValue The lowest inclusive pixel value checked for * each band. * @param highValue The highest exclusive pixel value checked for * each band. * * @throws IllegalArgumentException If numBins, * lowValue, or highValue is * null. * @throws IllegalArgumentException If the array length of the three * arguments are not the same, or any array length is 0. * @throws IllegalArgumentException If the number of bins for any band * is less than or equal to 0. * @throws IllegalArgumentException If the low-value of any band is * greater than or equal to its corresponding high-value. */ public Histogram(int[] numBins, double[] lowValue, double[] highValue) { if ( numBins == null || lowValue == null || highValue == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } numBands = numBins.length; if (lowValue.length != numBands || highValue.length != numBands) { throw new IllegalArgumentException( JaiI18N.getString("Histogram0")); } if (numBands == 0) { throw new IllegalArgumentException( JaiI18N.getString("Histogram1")); } for (int i = 0; i < numBands; i++) { if (numBins[i] <= 0) { throw new IllegalArgumentException( JaiI18N.getString("Histogram2")); } if (lowValue[i] >= highValue[i]) { throw new IllegalArgumentException( JaiI18N.getString("Histogram3")); } } this.numBins = (int[])numBins.clone(); this.lowValue = (double[])lowValue.clone(); this.highValue = (double[])highValue.clone(); binWidth = new double[numBands]; // Compute binWidth for all bands. for (int i = 0; i < numBands; i++) { binWidth[i] = (highValue[i] - lowValue[i]) / numBins[i]; } } /** * Constructor. * *

      This constructor should be used when numBins, * lowValue, and/or highValues may be * different for each band of the image. The number of bands in the * image is provided explicitly. If any of the arrays provided * has a length which is less than the number of bands, the first * element in that array is used to fill out the array to a length * of numBands. * * * @param numBins The number of bins for each band of the image. * @param lowValue The lowest inclusive pixel value checked for * each band. * @param highValue The highest exclusive pixel value checked for * each band. * @param numBands The number of bands in the image. * * @throws IllegalArgumentException If numBins, * lowValue, or highValue is * null. * @throws IllegalArgumentException If any array length is 0. * @throws IllegalArgumentException If the number of bins for any band * is less than or equal to 0. * @throws IllegalArgumentException If the low-value of any band is * greater than or equal to its corresponding high-value. * @throws IllegalArgumentException If numBands is less * than or equal to 0. * * @since JAI 1.1 */ public Histogram(int[] numBins, double[] lowValue, double[] highValue, int numBands) { this(fill(numBins, numBands), fill(lowValue, numBands), fill(highValue, numBands)); } /** * Constructor. * *

      The same numBins, lowValue, and * highValue is applied to every band of the image. * * @param numBins The number of bins for all bands of the image. * @param lowValue The lowest inclusive pixel value checked for * all bands. * @param highValue The highest exclusive pixel value checked for * all bands. * @param numBands The number of bands of the image. * * @throws IllegalArgumentException If numBins or * numBands is less than or equal to 0. * @throws IllegalArgumentException If lowValue * is less than or equal to highValue. * * @since JAI 1.1 */ public Histogram(int numBins, double lowValue, double highValue, int numBands) { if (numBands <= 0) { throw new IllegalArgumentException( JaiI18N.getString("Histogram1")); } if (numBins <= 0) { throw new IllegalArgumentException( JaiI18N.getString("Histogram2")); } if (lowValue >= highValue) { throw new IllegalArgumentException( JaiI18N.getString("Histogram3")); } this.numBands = numBands; this.numBins = new int[numBands]; this.lowValue = new double[numBands]; this.highValue = new double[numBands]; this.binWidth = new double[numBands]; double bw = (highValue - lowValue) / numBins; // binWidth for (int i = 0; i < numBands; i++) { this.numBins[i] = numBins; this.lowValue[i] = lowValue; this.highValue[i] = highValue; this.binWidth[i] = bw; } } /** Returns the number of bins of the histogram for all bands. */ public int[] getNumBins() { return (int[])numBins.clone(); } /** * Returns the number of bins of the histogram for a specific band. * * @param band The index of the band whose numBins * is to be returned. * * @throws ArrayIndexOutOfBoundsException If an invalid band index * is specified. */ public int getNumBins(int band) { return numBins[band]; } /** Returns the lowest inclusive pixel value checked for all bands. */ public double[] getLowValue() { return (double[])lowValue.clone(); } /** * Returns the lowest inclusive pixel value checked for a specific band. * * @param band The index of the band whose lowValue * is to be returned. * * @throws ArrayIndexOutOfBoundsException If an invalid band index * is specified. */ public double getLowValue(int band) { return lowValue[band]; } /** Returns the highest exclusive pixel value checked for all bands. */ public double[] getHighValue() { return (double[])highValue.clone(); } /** * Returns the highest exclusive pixel value checked for a specific band. * * @param band The index of the band whose highValue * is to be returned. * * @throws ArrayIndexOutOfBoundsException If an invalid band index * is specified. */ public double getHighValue(int band) { return highValue[band]; } /** * Returns the number of bands of the histogram. * This value is the same as the number of bands of the * bins, bins.length. */ public int getNumBands() { return numBands; } /** * Returns the array of bands of bins, each bin is the histogram for a * band, i.e., the format of the returned array is * int[bands][bins]. */ public synchronized int[][] getBins() { if (bins == null) { bins = new int[numBands][]; for (int i = 0; i < numBands; i++) { bins[i] = new int[numBins[i]]; } } return bins; } /** * Returns the bins of the histogram for a specific band by reference. * * @param band The index of the band whose bins * are to be returned. * * @throws ArrayIndexOutOfBoundsException If an invalid band index * is specified. */ public int[] getBins(int band) { getBins(); // make sure bins is initialized return bins[band]; } /** * Returns the number of pixel samples found in a given bin for a * specific band. * * @param band The index of the band-of-interest. * @param bin The index of the bin whose value is to be returned. * * @throws ArrayIndexOutOfBoundsException If an invalid band or * bin index is specified. */ public int getBinSize(int band, int bin) { getBins(); // make sure bins is initialized return bins[band][bin]; } /** * Returns the lowest inclusive pixel value of a given bin * for a specific band. * * @param band The index of the band-of-interest. * @param bin The index of the bin whose lowValue * is to be returned. * * @throws ArrayIndexOutOfBoundsException If an invalid band * index is specified. */ public double getBinLowValue(int band, int bin) { return lowValue[band] + bin * binWidth[band]; } /** * Resets the values of all bins to zero. If bins has * not been initialized (null), this method does nothing. */ public void clearHistogram() { if (bins != null) { synchronized (bins) { for (int i = 0; i < numBands; i++) { int[] b = bins[i]; int length = b.length; for (int j = 0; j < length; j++) { b[j] = 0; } } } } } /** * Returns the total bin count over all bins for all bands. * *

      An array which stores the total bin count is kept in this class * and a reference to this array is returned by this method for * performance reasons. The elements of the returned array should * not be modified or undefined errors may occur. The array format is * int[numBands]. * * @since JAI 1.1 */ public int[] getTotals() { if (totals == null) { getBins(); // make sure bins is initialized synchronized (this) { totals = new int[numBands]; for (int i = 0; i < numBands; i++) { int[] b = bins[i]; int length = b.length; int t = 0; for (int j = 0; j < length; j++) { t += b[j]; } totals[i] = t; } } } return totals; } /** * Returns the total bin count for the specified sub-range (via "min" * and "max" bin) of the indicated band. The sub-ragne must fall * within the actual range of the bins. * * @param band The index of the band-of-interest. * @param minBin The minimum bin index to be counted. * @param maxBin The maximum bin index to be counted. * * @throws ArrayIndexOutOfBoundsException If an invalid band index * is specified. * @throws IllegalArgumentException If minBin is greater than * maxBin. * * @since JAI 1.1 */ public int getSubTotal(int band, int minBin, int maxBin) { if (minBin < 0 || maxBin >= numBins[band]) { throw new ArrayIndexOutOfBoundsException( JaiI18N.getString("Histogram5")); } if (minBin > maxBin) { throw new IllegalArgumentException( JaiI18N.getString("Histogram10")); } int[] b = getBins(band); int total = 0; for (int i = minBin; i <= maxBin; i++) { total += b[i]; } return total; } /** * Returns the mean values for all bands of the histogram. * * @since JAI 1.1 */ public double[] getMean() { if (mean == null) { getTotals(); // make sure totals is computed synchronized (this) { mean = new double[numBands]; for (int i = 0; i < numBands; i++) { int[] counts = getBins(i); int nBins = numBins[i]; double level = getLowValue(i); double bw = binWidth[i]; double mu = 0.0; double total = totals[i]; for(int b = 0; b < nBins; b++) { mu += (counts[b] / total) * level; level += bw; } mean[i] = mu; } } } return mean; } /** * Accumulates the histogram of the pixels within a specific * region-of-interest (ROI) by counting them based on the * indicated horizontal and vertical sampling period. The result * is stored in the bins array and may be obtained * by calling the getBins method. * *

      The ROI specifies the region within which * the pixels are counted. If it is null, the * entire Raster is counted. If it is not * null and does not intersect with the * Raster, then this method returns without changing * the bins. * *

      The set of pixels to be counted may be obtained by * intersecting the grid (xStart + i * xPeriod, * yStart + j * yPeriod); i, j >= 0 with the ROI * and the bounding rectangle of the Raster. * * @param raster The Raster that contains the pixels to be counted. * @param roi The region-of-interest within which the pixels are counted. * @param xStart The initial X sample coordinate. * @param yStart The initial Y sample coordinate. * @param xPeriod The X sampling period. * @param yPeriod The Y sampling period. * * @throws IllegalArgumentException If raster is * null. * @throws IllegalArgumentException If the pixels stored in the * raster do not have the same number of bands * (samples per pixel) as this histogram's bins. * @thows RuntimeException if the data type is not supported * (not in DataBuffer.TYPE_BYTE,..., DataBuff.TYPE_DOUBLE. */ public void countPixels(Raster raster, ROI roi, int xStart, int yStart, int xPeriod, int yPeriod) { if ( raster == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } SampleModel sampleModel = raster.getSampleModel(); if (sampleModel.getNumBands() != numBands) { throw new IllegalArgumentException( JaiI18N.getString("Histogram4")); } Rectangle bounds = raster.getBounds(); LinkedList rectList; if (roi == null) { // ROI is the whole Raster rectList = new LinkedList(); rectList.addLast(bounds); } else { rectList = roi.getAsRectangleList(bounds.x, bounds.y, bounds.width, bounds.height); if (rectList == null) { return; // ROI does not intersect with Raster boundary. } } PixelAccessor accessor = new PixelAccessor(sampleModel, null); ListIterator iterator = rectList.listIterator(0); while (iterator.hasNext()) { Rectangle r = (Rectangle)iterator.next(); int tx = r.x; int ty = r.y; // Find the actual ROI based on start and period. r.x = startPosition(tx, xStart, xPeriod); r.y = startPosition(ty, yStart, yPeriod); r.width = tx + r.width - r.x; r.height = ty + r.height - r.y; if (r.width <= 0 || r.height <= 0) { continue; // no pixel to count in this rectangle } switch (accessor.sampleType) { case PixelAccessor.TYPE_BIT: case DataBuffer.TYPE_BYTE: countPixelsByte(accessor, raster, r, xPeriod, yPeriod); break; case DataBuffer.TYPE_USHORT: countPixelsUShort(accessor, raster, r, xPeriod, yPeriod); break; case DataBuffer.TYPE_SHORT: countPixelsShort(accessor, raster, r, xPeriod, yPeriod); break; case DataBuffer.TYPE_INT: countPixelsInt(accessor, raster, r, xPeriod, yPeriod); break; case DataBuffer.TYPE_FLOAT: countPixelsFloat(accessor, raster, r, xPeriod, yPeriod); break; case DataBuffer.TYPE_DOUBLE: countPixelsDouble(accessor, raster, r, xPeriod, yPeriod); break; default: throw new RuntimeException(JaiI18N.getString("Histogram11")); } } } private void countPixelsByte(PixelAccessor accessor, Raster raster, Rectangle rect, int xPeriod, int yPeriod) { UnpackedImageData uid = accessor.getPixels( raster, rect, DataBuffer.TYPE_BYTE, false); byte[][] byteData = uid.getByteData(); int pixelStride = uid.pixelStride * xPeriod; int lineStride = uid.lineStride * yPeriod; int[] offsets = uid.bandOffsets; for (int b = 0; b < numBands; b++) { byte[] data = byteData[b]; int lineOffset = offsets[b]; // line offset int[] bin = new int[numBins[b]]; double low = lowValue[b]; double high = highValue[b]; double bwidth = binWidth[b]; for (int h = 0; h < rect.height; h += yPeriod) { int pixelOffset = lineOffset; // pixel offset lineOffset += lineStride; for (int w = 0; w < rect.width; w += xPeriod) { int d = data[pixelOffset] & 0xff; pixelOffset += pixelStride; if (d >= low && d < high) { int i = (int)((d - low) / bwidth); bin[i]++; } } } mergeBins(b, bin); // merge this band to the whole bins } } private void countPixelsUShort(PixelAccessor accessor, Raster raster, Rectangle rect, int xPeriod, int yPeriod) { UnpackedImageData uid = accessor.getPixels( raster, rect, DataBuffer.TYPE_USHORT, false); short[][] shortData = uid.getShortData(); int pixelStride = uid.pixelStride * xPeriod; int lineStride = uid.lineStride * yPeriod; int[] offsets = uid.bandOffsets; for (int b = 0; b < numBands; b++) { short[] data = shortData[b]; int lineOffset = offsets[b]; // line offset int[] bin = new int[numBins[b]]; double low = lowValue[b]; double high = highValue[b]; double bwidth = binWidth[b]; for (int h = 0; h < rect.height; h += yPeriod) { int pixelOffset = lineOffset; // pixel offset lineOffset += lineStride; for (int w = 0; w < rect.width; w += xPeriod) { int d = data[pixelOffset] & 0xffff; pixelOffset += pixelStride; if (d >= low && d < high) { int i = (int)((d - low) / bwidth); bin[i]++; } } } mergeBins(b, bin); // merge this band to the whole bins } } private void countPixelsShort(PixelAccessor accessor, Raster raster, Rectangle rect, int xPeriod, int yPeriod) { UnpackedImageData uid = accessor.getPixels( raster, rect, DataBuffer.TYPE_SHORT, false); short[][] shortData = uid.getShortData(); int pixelStride = uid.pixelStride * xPeriod; int lineStride = uid.lineStride * yPeriod; int[] offsets = uid.bandOffsets; for (int b = 0; b < numBands; b++) { short[] data = shortData[b]; int lineOffset = offsets[b]; // line offset int[] bin = new int[numBins[b]]; double low = lowValue[b]; double high = highValue[b]; double bwidth = binWidth[b]; for (int h = 0; h < rect.height; h += yPeriod) { int pixelOffset = lineOffset; // pixel offset lineOffset += lineStride; for (int w = 0; w < rect.width; w += xPeriod) { int d = data[pixelOffset]; pixelOffset += pixelStride; if (d >= low && d < high) { int i = (int)((d - low) / bwidth); bin[i]++; } } } mergeBins(b, bin); // merge this band to the whole bins } } private void countPixelsInt(PixelAccessor accessor, Raster raster, Rectangle rect, int xPeriod, int yPeriod) { UnpackedImageData uid = accessor.getPixels( raster, rect, DataBuffer.TYPE_INT, false); int[][] intData = uid.getIntData(); int pixelStride = uid.pixelStride * xPeriod; int lineStride = uid.lineStride * yPeriod; int[] offsets = uid.bandOffsets; for (int b = 0; b < numBands; b++) { int[] data = intData[b]; int lineOffset = offsets[b]; // line offset int[] bin = new int[numBins[b]]; double low = lowValue[b]; double high = highValue[b]; double bwidth = binWidth[b]; for (int h = 0; h < rect.height; h += yPeriod) { int pixelOffset = lineOffset; // pixel offset lineOffset += lineStride; for (int w = 0; w < rect.width; w += xPeriod) { int d = data[pixelOffset]; pixelOffset += pixelStride; if (d >= low && d < high) { int i = (int)((d - low) / bwidth); bin[i]++; } } } mergeBins(b, bin); // merge this band to the whole bins } } private void countPixelsFloat(PixelAccessor accessor, Raster raster, Rectangle rect, int xPeriod, int yPeriod) { UnpackedImageData uid = accessor.getPixels( raster, rect, DataBuffer.TYPE_FLOAT, false); float[][] floatData = uid.getFloatData(); int pixelStride = uid.pixelStride * xPeriod; int lineStride = uid.lineStride * yPeriod; int[] offsets = uid.bandOffsets; for (int b = 0; b < numBands; b++) { float[] data = floatData[b]; int lineOffset = offsets[b]; // line offset int[] bin = new int[numBins[b]]; double low = lowValue[b]; double high = highValue[b]; double bwidth = binWidth[b]; for (int h = 0; h < rect.height; h += yPeriod) { int pixelOffset = lineOffset; // pixel offset lineOffset += lineStride; for (int w = 0; w < rect.width; w += xPeriod) { float d = data[pixelOffset]; pixelOffset += pixelStride; if (d >= low && d < high) { int i = (int)((d - low) / bwidth); bin[i]++; } } } mergeBins(b, bin); // merge this band to the whole bins } } private void countPixelsDouble(PixelAccessor accessor, Raster raster, Rectangle rect, int xPeriod, int yPeriod) { UnpackedImageData uid = accessor.getPixels( raster, rect, DataBuffer.TYPE_DOUBLE, false); double[][] doubleData = uid.getDoubleData(); int pixelStride = uid.pixelStride * xPeriod; int lineStride = uid.lineStride * yPeriod; int[] offsets = uid.bandOffsets; for (int b = 0; b < numBands; b++) { double[] data = doubleData[b]; int lineOffset = offsets[b]; // line offset int[] bin = new int[numBins[b]]; double low = lowValue[b]; double high = highValue[b]; double bwidth = binWidth[b]; for (int h = 0; h < rect.height; h += yPeriod) { int pixelOffset = lineOffset; // pixel offset lineOffset += lineStride; for (int w = 0; w < rect.width; w += xPeriod) { double d = data[pixelOffset]; pixelOffset += pixelStride; if (d >= low && d < high) { int i = (int)((d - low) / bwidth); bin[i]++; } } } mergeBins(b, bin); // merge this band to the whole bins } } /** Finds the first pixel at or after pos to be counted. */ private int startPosition(int pos, int start, int Period) { int t = (pos - start) % Period; return t == 0 ? pos : pos + (Period - t); } /** Merges bin count for a band. Synchronized on bins for MT-safe. */ private void mergeBins(int band, int[] bin) { getBins(); synchronized (bins) { int[] b = bins[band]; int length = b.length; for (int i = 0; i < length; i++) { b[i] += bin[i]; } } } /** * Returns a specified (absolute) (central) moment of the histogram. * *

      The ith moment in each band is defined to be the mean * of the image pixel values raised to the ith power in that * band. For central moments the average of the ith power * of the deviation from the mean is used. For absolute moments the * absolute value of the exponentiated term is used. * *

      Note that the mean is the first moment, the average energy the * second central moment, etc. * * @param moment The moment number or index which must be positive or * an IllegalArgumentException will be thrown. * @param isAbsolute Whether to calculate the absolute moment. * @param isCentral Whether to calculate the central moment. * @return The requested (absolute) (central) moment of the histogram. * * @since JAI 1.1 */ public double[] getMoment(int moment, boolean isAbsolute, boolean isCentral) { // Check for non-positive moment number. if(moment < 1) { throw new IllegalArgumentException(JaiI18N.getString("Histogram6")); } // If the mean is required but has not yet been calculated // then calculate it first. if((moment == 1 || isCentral) && mean == null) { getMean(); } // If it's the first non-absolute, non-central moment return the mean. if((moment == 1) && !isAbsolute && !isCentral) { return mean; } double[] moments = new double[numBands]; // For the trivial case of first central moment return zeros. if(moment == 1 && isCentral) { for(int band = 0; band < numBands; band++) { moments[band] = 0.0; } } else { // Get the total counts for all bands. getTotals(); for(int band = 0; band < numBands; band++) { // Cache some band-dependent quantities. int[] counts = getBins(band); int nBins = numBins[band]; double level = getLowValue(band); double bw = binWidth[band]; double total = totals[band]; // Clear the moment value for this band. double mmt = 0.0; if(isCentral) { // Cache the mean value for this band. double mu = mean[band]; if(isAbsolute && moment %2 == 0) { // Even moment so absolute value has no effect. for(int b = 0; b < nBins; b++) { mmt += Math.pow(level - mu, moment)* counts[b]/total; level += bw; } } else { // Odd moment so need to use absolute value. for(int b = 0; b < nBins; b++) { mmt += Math.abs(Math.pow(level - mu, moment))* counts[b]/total; level += bw; } } } else if(isAbsolute && moment %2 != 0) { // Odd moment so need to use absolute value. for(int b = 0; b < nBins; b++) { mmt += Math.abs(Math.pow(level, moment))* counts[b]/total; level += bw; } } else { // Even or non-absolute non-central moment. for(int b = 0; b < nBins; b++) { mmt += Math.pow(level, moment)*counts[b]/total; level += bw; } } // Save the result. moments[band] = mmt; } } return moments; } /** * Returns the standard deviation for all bands of the histogram. * This is a convenience method as the returned values * could be calculated using the first and second moments which * are available via the moment generation function. * * @return The standard deviation values for all bands. * * @since JAI 1.1 */ public double[] getStandardDeviation() { getMean(); double[] variance = getMoment(2, false, false); double[] stdev = new double[numBands]; for(int i = 0; i < variance.length; i++) { stdev[i] = Math.sqrt(variance[i] - mean[i]*mean[i]); } return stdev; } /** * Returns the entropy of the histogram. * *

      The histogram entropy is defined to be the negation of the sum * of the products of the probability associated with each bin with * the base-2 log of the probability. * * @return The entropy of the histogram. * * @since JAI 1.1 */ public double[] getEntropy() { // Get the total counts for all bands. getTotals(); double log2 = Math.log(2.0); double[] entropy = new double[numBands]; for(int band = 0; band < numBands; band++) { int[] counts = getBins(band); int nBins = numBins[band]; double total = totals[band]; double H = 0.0; for(int b = 0; b < nBins; b++) { double p = counts[b]/total; if(p != 0.0) { H -= p*(Math.log(p)/log2); } } entropy[band] = H; } return entropy; } /** * Computes a smoothed version of the histogram. * *

      Each band of the histogram is smoothed by averaging over a * moving window of a size specified by the method parameter: if * the value of the parameter is k then the width of the window * is 2*k + 1. If the window runs off the end of the histogram * only those values which intersect the histogram are taken into * consideration. The smoothing may optionally be weighted to favor * the central value using a "triangular" weighting. For example, * for a value of k equal to 2 the central bin would have weight * 1/3, the adjacent bins 2/9, and the next adjacent bins 1/9. * * @param isWeighted Whether bins will be weighted using a triangular * weighting scheme favoring bins near the central bin. * @param k The smoothing parameter which must be non-negative or an * IllegalArgumentException will be thrown. If zero, the * histogram object will be returned with no smoothing applied. * @return A smoothed version of the histogram. * * @since JAI 1.1 */ public Histogram getSmoothed(boolean isWeighted, int k) { if(k < 0) { throw new IllegalArgumentException(JaiI18N.getString("Histogram7")); } else if(k == 0) { return this; } // Create a new, identical but empty Histogram. Histogram smoothedHistogram = new Histogram(getNumBins(), getLowValue(), getHighValue()); // Get a reference to the bins of the new Histogram. int[][] smoothedBins = smoothedHistogram.getBins(); // Get the total counts for all bands. getTotals(); // Initialize the smoothing weights if needed. double[] weights = null; if(isWeighted) { int numWeights = 2*k + 1; double denom = numWeights*numWeights; weights = new double[numWeights]; for(int i = 0; i <= k; i++) { weights[i] = (i + 1)/denom; } for(int i = k + 1; i < numWeights; i++) { weights[i] = weights[numWeights - 1 - i]; } } for(int band = 0; band < numBands; band++) { // Cache bin-dependent values and references. int[] counts = getBins(band); int[] smoothedCounts = smoothedBins[band]; int nBins = smoothedHistogram.getNumBins(band); // Clear the band total count for the smoothed histogram. int sum = 0; if(isWeighted) { for(int b = 0; b < nBins; b++) { // Determine clipped range. int min = Math.max(b - k, 0); int max = Math.min(b + k, nBins); // Calculate the offset into the weight array. int offset = k > b ? k - b : 0; // Accumulate the total for the range. double acc = 0; double weightTotal = 0; for(int i = min; i < max; i++) { double w = weights[offset++]; acc += counts[i]*w; weightTotal += w; } // Round the accumulated value. smoothedCounts[b] = (int)(acc/weightTotal + 0.5); // Accumulate total for band. sum += smoothedCounts[b]; } } else { for(int b = 0; b < nBins; b++) { // Determine clipped range. int min = Math.max(b - k, 0); int max = Math.min(b + k, nBins); // Accumulate the total for the range. int acc = 0; for(int i = min; i < max; i++) { acc += counts[i]; } // Calculate the average for the range. smoothedCounts[b] = (int)(acc / (double)(max - min + 1) + 0.5); // Accumulate total for band. sum += smoothedCounts[b]; } } // Rescale the counts such that the band total is approximately // the same as for the same band of the original histogram. double factor = (double)totals[band]/(double)sum; for(int b = 0; b < nBins; b++) { smoothedCounts[b] = (int)(smoothedCounts[b]*factor + 0.5); } } return smoothedHistogram; } /** * Computes a Gaussian smoothed version of the histogram. * *

      Each band of the histogram is smoothed by discrete convolution * with a kernel approximating a Gaussian impulse response with the * specified standard deviation. * * @param standardDeviation The standard deviation of the Gaussian * smoothing kernel which must be non-negative or an * IllegalArgumentException will be thrown. If zero, the * histogram object will be returned with no smoothing applied. * @return A Gaussian smoothed version of the histogram. * * @since JAI 1.1 */ public Histogram getGaussianSmoothed(double standardDeviation) { if(standardDeviation < 0.0) { throw new IllegalArgumentException(JaiI18N.getString("Histogram8")); } else if(standardDeviation == 0.0) { return this; } // Create a new, identical but empty Histogram. Histogram smoothedHistogram = new Histogram(getNumBins(), getLowValue(), getHighValue()); // Get a reference to the bins of the new Histogram. int[][] smoothedBins = smoothedHistogram.getBins(); // Get the total counts for all bands. getTotals(); // Determine the number of weights (must be odd). int numWeights = (int)(2*2.58*standardDeviation + 0.5); if(numWeights % 2 == 0) { numWeights++; } // Initialize the smoothing weights. double[] weights = new double[numWeights]; int m = numWeights/2; double var = standardDeviation*standardDeviation; double gain = 1.0/Math.sqrt(2.0*Math.PI*var); double exp = -1.0/(2.0*var); for(int i = m; i < numWeights; i++) { double del = i - m; weights[i] = weights[numWeights-1-i] = gain*Math.exp(exp*del*del); } for(int band = 0; band < numBands; band++) { // Cache bin-dependent values and references. int[] counts = getBins(band); int[] smoothedCounts = smoothedBins[band]; int nBins = smoothedHistogram.getNumBins(band); // Clear the band total count for the smoothed histogram. int sum = 0; for(int b = 0; b < nBins; b++) { // Determine clipped range. int min = Math.max(b - m, 0); int max = Math.min(b + m, nBins); // Calculate the offset into the weight array. int offset = m > b ? m - b : 0; // Accumulate the total for the range. double acc = 0; double weightTotal = 0; for(int i = min; i < max; i++) { double w = weights[offset++]; acc += counts[i]*w; weightTotal += w; } // Round the accumulated value. smoothedCounts[b] = (int)(acc/weightTotal + 0.5); // Accumulate total for band. sum += smoothedCounts[b]; } // Rescale the counts such that the band total is approximately // the same as for the same band of the original histogram. double factor = (double)totals[band]/(double)sum; for(int b = 0; b < nBins; b++) { smoothedCounts[b] = (int)(smoothedCounts[b]*factor + 0.5); } } return smoothedHistogram; } /** * Calculates the p-tile threshold. * *

      Computes thresholds such that a specified proportion of the sample * values in each band are below the threshold. * * @param p The proportion of samples in each band which should be below * the threshold in the band. If p is not in the range * (0.0, 1.0) an IllegalArgumentException will be thrown. * @return The requested p-tile thresholds. * * @since JAI 1.1 */ public double[] getPTileThreshold(double p) { if(p <= 0.0 || p >= 1.0) { throw new IllegalArgumentException(JaiI18N.getString("Histogram9")); } double[] thresholds = new double[numBands]; getTotals(); for(int band = 0; band < numBands; band++) { // Cache some band-dependent values. int nBins = numBins[band]; int[] counts = getBins(band); // Calculate the total count for this band. int totalCount = totals[band]; // Determine the number of binWidths to add to the lowValue // to get the desired threshold. int numBinWidths = 0; int count = counts[0]; int idx = 0; while((double)count/(double)totalCount < p) { numBinWidths++; count += counts[++idx]; } // Calculate the threshold. thresholds[band] = getLowValue(band) + numBinWidths*binWidth[band]; } return thresholds; } /** * Calculates the threshold using the mode method. * *

      The threshold is defined to be the minimum between two peaks. * The first peak is the highest peak in the histogram. The second * peak is the highest peak in the histogram weighted by a specified * power of the distance from the first peak. * * @param power The exponent of the distance weighting from the * first peak. * @return The requested thresholds. * * @since JAI 1.1 */ public double[] getModeThreshold(double power) { double[] thresholds = new double[numBands]; getTotals(); for(int band = 0; band < numBands; band++) { // Cache some band-dependent values. int nBins = numBins[band]; int[] counts = getBins(band); // Find the primary mode (highest peak). int mode1 = 0; int mode1Count = counts[0]; for(int b = 1; b < nBins; b++) { if(counts[b] > mode1Count) { mode1 = b; mode1Count = counts[b]; } } // Find the secondary mode (highest weighted peak). int mode2 = -1; double mode2count = 0.0; for(int b = 0; b < nBins; b++) { double d = counts[b]*Math.pow(Math.abs(b - mode1), power); if(d > mode2count) { mode2 = b; mode2count = d; } } // Find the minimum value between the two peaks. int min = mode1; int minCount = counts[mode1]; for(int b = mode1 + 1; b <= mode2; b++) { if(counts[b] < minCount) { min = b; minCount = counts[b]; } } thresholds[band] = (int)((mode1 + mode2)/2.0 + 0.5); } return thresholds; } /** * Calculates the threshold using iterative bisection. * *

      For each band an initial threshold is defined to be the midpoint * of the range of data represented by the histogram. The mean value is * calculated for each sub-histogram and a new threshold is defined as the * arithmetic mean of the two sub-histogram means. This process is * repeated until the threshold value no longer changes. * * @return The requested thresholds. * * @since JAI 1.1 */ public double[] getIterativeThreshold() { double[] thresholds = new double[numBands]; getTotals(); for(int band = 0; band < numBands; band++) { // Cache some band-dependent values. int nBins = numBins[band]; int[] counts = getBins(band); double bw = binWidth[band]; // Set intial threshold to midpoint of data range for this band. double threshold = 0.5 * (getLowValue(band) + getHighValue(band)); double mid1 = 0.5 * (getLowValue(band) + threshold); double mid2 = 0.5 * (threshold + getHighValue(band)); // Iterate only if total is nonzero. if (totals[band] != 0) { // Loop until threshold estimate no longer changes. int countDown = 1000; do { // Save band threshold for this iteration. thresholds[band] = threshold; // Cache the total count for this band. double total = totals[band]; // Initialize the level corresponding to a bin. double level = getLowValue(band); // Clear mean values for sub-ranges. double mean1 = 0.0; double mean2 = 0.0; // Clear sub-range 1 count. int count1 = 0; // Calculate the mean values for the two sub-ranges. for(int b = 0; b < nBins; b++) { // Update the mean value for the appropriate sub-range. if(level <= threshold) { int c = counts[b]; mean1 += c*level; count1 += c; } else { mean2 += counts[b]*level; } // Augment the level for the current bin by the bin width. level += bw; } // Rescale values using sub-range totals. if (count1 != 0) { mean1 /= count1; } else { mean1 = mid1; } if (total != count1) { mean2 /= (total - count1); } else { mean2 = mid2; } // Update the threshold estimate. threshold = 0.5 * (mean1 + mean2); } while(Math.abs(threshold - thresholds[band]) > 1e-6 && --countDown > 0); } else { thresholds[band] = threshold; } } return thresholds; } /** * Calculates the threshold which maximizes the ratio of the between-class * variance to the within-class variance for each band. * * @return The requested thresholds. * * @since JAI 1.1 */ public double[] getMaxVarianceThreshold() { double[] thresholds = new double[numBands]; getTotals(); getMean(); double[] variance = getMoment(2, false, false); for(int band = 0; band < numBands; band++) { // Cache some band-dependent values. int nBins = numBins[band]; int[] counts = getBins(band); double total = totals[band]; double mBand = mean[band]; double bw = binWidth[band]; double prob0 = 0.0; double mean0 = 0.0; double lv = getLowValue(band); double level = lv; double maxRatio = -Double.MAX_VALUE; int maxIndex = 0; int runLength = 0; for(int t = 0; t < nBins; t++, level += bw) { double p = counts[t]/total; prob0 += p; if(prob0 == 0.0) { continue; } double m0 = (mean0 += p*level)/prob0; double prob1 = 1.0 - prob0; if(prob1 == 0.0) { continue; } double m1 = (mBand - mean0)/prob1; double var0 = 0.0; double g = lv; for(int b = 0; b <= t; b++, g += bw) { double del = g - m0; var0 += del*del*counts[b]; } var0 /= total; double var1 = 0.0; for(int b = t + 1; b < nBins; b++, g += bw) { double del = g - m1; var1 += del*del*counts[b]; } var1 /= total; if(var0 == 0.0 && var1 == 0.0 && m1 != 0.0) { maxIndex = (int)(((m0 + m1)/2.0 - getLowValue(band))/bw + 0.5); runLength = 0; break; } if(var0/prob0 < 0.5 || var1/prob1 < 0.5) { continue; } double mdel = m0 - m1; double ratio = prob0*prob1*mdel*mdel/(var0 + var1); if(ratio > maxRatio) { maxRatio = ratio; maxIndex = t; runLength = 0; } else if(ratio == maxRatio) { runLength++; } } thresholds[band] = getLowValue(band) + (maxIndex + runLength/2.0 + 0.5)*bw; } return thresholds; } /** * Calculates the threshold which maximizes the entropy. * *

      The entropy of a range of gray levels is defined to * be the negation of the sum of products of the probability and * the logarithm thereof over all gray levels in the range. The * maximum entropy threshold is defined to be that value which maximizes * the sum of the entropy of the two ranges which are above and below the * threshold, respectively. This computation is effected for each band. * * @return The requested thresholds. * * @since JAI 1.1 */ public double[] getMaxEntropyThreshold() { double[] thresholds = new double[numBands]; getTotals(); double[] entropy = getEntropy(); double log2 = Math.log(2.0); for(int band = 0; band < numBands; band++) { // Cache some band-dependent values. int nBins = numBins[band]; int[] counts = getBins(band); double total = totals[band]; double H = entropy[band]; double P1 = 0.0; double H1 = 0.0; double maxCriterion = -Double.MAX_VALUE; int maxIndex = 0; int runLength = 0; for(int t = 0; t < nBins; t++) { double p = counts[t]/total; if(p == 0.0) { continue; } P1 += p; H1 -= p*Math.log(p)/log2; double max1 = 0.0; for(int b = 0; b <= t; b++) { if(counts[b] > max1) { max1 = counts[b]; } } if(max1 == 0.0) { continue; } double max2 = 0.0; for(int b = t + 1; b < nBins; b++) { if(counts[b] > max2) { max2 = counts[b]; } } if(max2 == 0.0) { continue; } double ratio = H1/H; double criterion = ratio*Math.log(P1)/Math.log(max1/total) + (1.0 - ratio)*Math.log(1.0 - P1)/Math.log(max2/total); if(criterion > maxCriterion) { maxCriterion = criterion; maxIndex = t; runLength = 0; } else if(criterion == maxCriterion) { runLength++; } } thresholds[band] = getLowValue(band) + (maxIndex + runLength/2.0 + 0.5)*binWidth[band]; } return thresholds; } /** * Calculates the threshold which minimizes the probability of error. * *

      For each band the histogram is modeled as the sum of two Gaussian * distributions and the threshold which minimizes the misclassification * error is computed. If the underlying histogram is unimodal the mean * value of each band will be returned as the threshold. * The bimodality of the histogram for that band will be identically zero. * * @return The requested thresholds. * * @since JAI 1.1 */ public double[] getMinErrorThreshold() { double[] thresholds = new double[numBands]; getTotals(); getMean(); for(int band = 0; band < numBands; band++) { // Cache some band-dependent values. int nBins = numBins[band]; int[] counts = getBins(band); double total = totals[band]; double lv = getLowValue(band); double bw = binWidth[band]; int total1 = 0; int total2 = totals[band]; double sum1 = 0.0; double sum2 = mean[band]*total; double level = lv; double minCriterion = Double.MAX_VALUE; int minIndex = 0; int runLength = 0; double J0 = Double.MAX_VALUE; double J1 = Double.MAX_VALUE; double J2 = Double.MAX_VALUE; int Jcount = 0; for(int t = 0; t < nBins; t++, level += bw) { int c = counts[t]; total1 += c; total2 -= c; double incr = level*c; sum1 += incr; sum2 -= incr; if(total1 == 0 || sum1 == 0.0) { continue; } else if(total2 == 0 || sum2 == 0.0) { break; } double m1 = sum1/total1; double m2 = sum2/total2; double s1 = 0.0; double g = lv; for(int b = 0; b <= t; b++, g += bw) { double v = g - m1; s1 += counts[b]*v*v; } s1 /= total1; if(s1 < 0.5) { continue; } double s2 = 0.0; //g = lv; for(int b = t + 1; b < nBins; b++, g += bw) { double v = g - m2; s2 += counts[b]*v*v; } s2 /= total2; if(s2 < 0.5) { continue; } double P1 = total1/total; double P2 = total2/total; double J = 1.0 + P1*Math.log(s1) + P2*Math.log(s2) - 2.0*(P1*Math.log(P1) + P2*Math.log(P2)); Jcount++; J0 = J1; J1 = J2; J2 = J; if(Jcount >= 3) { if(J1 <= J0 && J1 <= J2) { if(J1 < minCriterion) { minCriterion = J1; minIndex = t - 1; runLength = 0; } else if(J1 == minCriterion) { runLength++; } } } } thresholds[band] = minIndex == 0 ? mean[band] : getLowValue(band) + (minIndex + runLength/2.0 + 0.5)*bw; } return thresholds; } /** * Calculates the threshold which minimizes the fuzziness. * * @since JAI 1.1 */ public double[] getMinFuzzinessThreshold() { double[] thresholds = new double[numBands]; getTotals(); getMean(); for(int band = 0; band < numBands; band++) { // Cache some band-dependent values. int nBins = numBins[band]; int[] counts = getBins(band); double total = totals[band]; double bw = binWidth[band]; int total1 = 0; int total2 = totals[band]; double sum1 = 0.0; double sum2 = mean[band]*total; double lv = getLowValue(band); double level = lv; double C = getHighValue(band) - lv; double minCriterion = Double.MAX_VALUE; int minIndex = 0; int runLength = 0; for(int t = 0; t < nBins; t++, level += bw) { int c = counts[t]; total1 += c; total2 -= c; double incr = level*c; sum1 += incr; sum2 -= incr; if(total1 == 0 || total2 == 0) { continue; } double m1 = sum1/total1; double m2 = sum2/total2; double g = lv; double E = 0.0; for(int b = 0; b < nBins; b++, g += bw) { double u = b <= t ? 1.0/(1.0 + Math.abs(g - m1)/C) : 1.0/(1.0 + Math.abs(g - m2)/C); double v = 1 - u; E += (-u*Math.log(u) - v*Math.log(v))*(counts[b]/total); } if(E < minCriterion) { minCriterion = E; minIndex = t; runLength = 0; } else if(E == minCriterion) { runLength++; } } thresholds[band] = lv + (minIndex + runLength/2.0 + 0.5)*bw; } return thresholds; } } jai-core-1.1.4/src/share/classes/javax/media/jai/RenderedImageAdapter.java0000644000175000017500000001744510203035544026207 0ustar mathieumathieu/* * $RCSfile: RenderedImageAdapter.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:20 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Rectangle; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.util.Hashtable; import java.util.HashSet; import java.util.Set; import javax.media.jai.util.CaselessStringKey; /** * A PlanarImage wrapper for a non-writable * RenderedImage. The tile layout, sample model, and so forth * are preserved. Calls to getTile(), getData(), * and copyData() are forwarded to the image being adapted. * *

      The set of properties available on the image will be a combination of * those defined locally via setProperty() and those defined * on the source image with the local properties taking precedence. No * PropertySourceChangeEvent will be generated as a result of * changes to the property set of the source image. * *

      From JAI's point of view, this image is a PlanarImage of * unknown type, with no sources. The source image is assumed to be * immutable. If the RenderedImage source implements * WritableRenderedImage, a * WritableRenderedImageAdapter should be used. * *

      The methods are marked 'final' in order to allow dynamic inlining to * take place. This should eliminate any performance penalty associated with * the use of an adapter class. * *

      Since the methods of this class all derive from * PlanarImage, they are not commented in detail. * * @see PlanarImage * @see WritableRenderedImageAdapter * @see java.awt.image.RenderedImage * @see java.awt.image.WritableRenderedImage */ public class RenderedImageAdapter extends PlanarImage { /** The RenderedImage being adapted. */ protected RenderedImage theImage; /** Tile index bounds. */ private Rectangle tileIndexBounds; /** * Merge the Strings in the two arrays with the local names * taking precedence. Comparison is performed independent of case. Both * parameters may be null. * * @return The merged name arrays or null. */ static String[] mergePropertyNames(String[] localNames, String[] sourceNames) { // Set the output names to the other array if one array is null // or zero-length. String[] names = null; if(localNames == null || localNames.length == 0) { names = sourceNames; } else if(sourceNames == null || sourceNames.length == 0) { names = localNames; } else { // Merge the name arrays. // Allocate a Set. Set nameSet = new HashSet((localNames.length+sourceNames.length)/2); // Add source names first as they have lower priority. int numSourceNames = sourceNames.length; for(int i = 0; i < numSourceNames; i++) { nameSet.add(new CaselessStringKey(sourceNames[i])); } // Add local names which will "bump" duplicate source names. int numLocalNames = localNames.length; for(int i = 0; i < numLocalNames; i++) { nameSet.add(new CaselessStringKey(localNames[i])); } // Convert result to an array of Strings. int numNames = nameSet.size(); CaselessStringKey[] caselessNames = new CaselessStringKey[numNames]; nameSet.toArray(caselessNames); names = new String[numNames]; for(int i = 0; i < numNames; i++) { names[i] = caselessNames[i].getName(); } } // Set return value to null if zero-length. if(names != null && names.length == 0) { names = null; } return names; } /** * Constructs a RenderedImageAdapter. * * @param im a RenderedImage to be `wrapped' as a PlanarImage. * @throws IllegalArgumentException if im is * null. */ public RenderedImageAdapter(RenderedImage im) { super(im != null ? new ImageLayout(im) : null, null, null); if (im == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); theImage = im; tileIndexBounds = new Rectangle(theImage.getMinTileX(), theImage.getMinTileY(), theImage.getNumXTiles(), theImage.getNumYTiles()); } /** * Returns the reference to the external RenderedImage * originally supplied to the constructor. * * @since JAI 1.1.2 */ public final RenderedImage getWrappedImage() { return theImage; } /** * Forwards call to the true source unless the specified tile indices * refer to a tile which does not overlap the image bounds in which * case null is returned. */ public final Raster getTile(int x, int y) { return tileIndexBounds.contains(x, y) ? theImage.getTile(x, y) : null; } /** Forwards call to the true source. */ public final Raster getData() { return theImage.getData(); } /** Forwards call to the true source. */ public final Raster getData(Rectangle rect) { return theImage.getData(rect); } /** Forwards call to the true source. */ public final WritableRaster copyData(WritableRaster raster) { return theImage.copyData(raster); } /** * Retrieves a list of property names recognized by this image. * The locally defined property names are combined with those derived * from the true source. */ public final String[] getPropertyNames() { return mergePropertyNames(super.getPropertyNames(), theImage.getPropertyNames()); } /** * Retrieves the property from those set locally on the image or, * if the property is not available locally, the call is forwarded to * the true source. * @exception IllegalArgumentException if name * is null. */ public final Object getProperty(String name) { // Retrieve the property from the local cache. Object property = super.getProperty(name); // If it is still undefined, forward the call. if(property == java.awt.Image.UndefinedProperty) { property = theImage.getProperty(name); } return property; } /** * Returns the class expected to be returned by a request for * the property with the specified name. If this information * is unavailable, null will be returned. * * @exception IllegalArgumentException if name * is null. * * @return The Class expected to be return by a * request for the value of this property or null. * * @since JAI 1.1 */ public final Class getPropertyClass(String name) { // Get the class if the property is local. Class propClass = super.getPropertyClass(name); // If not local ... if(propClass == null) { // Get the property value. Object propValue = getProperty(name); if(propValue != java.awt.Image.UndefinedProperty) { // If the property is defined, get the class. propClass = propValue.getClass(); } } return propClass; } } jai-core-1.1.4/src/share/classes/javax/media/jai/TileRequest.java0000644000175000017500000000745310203035544024457 0ustar mathieumathieu/* * $RCSfile: TileRequest.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:22 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Point; /** * Interface representing a TileScheduler request to compute * a specific set of tiles for a given image with optional monitoring by * TileComputationListeners. * * @see TileScheduler * @see TileComputationListener * @see RenderedOp * @see OpImage * * @since JAI 1.1 */ public interface TileRequest { /** * Status value indicating that the tile has yet to be processed. */ public static final int TILE_STATUS_PENDING = 0; /** * Status value indicating that the tile has is being processed. */ public static final int TILE_STATUS_PROCESSING = 1; /** * Status value indicating that the tile has been computed successfully. */ public static final int TILE_STATUS_COMPUTED = 2; /** * Status value indicating that the tile computation has been cancelled. */ public static final int TILE_STATUS_CANCELLED = 3; /** * Status value indicating that the tile computation failed. */ public static final int TILE_STATUS_FAILED = 4; /** * Returns the image associated with the request. This is the image * which is actually specified to the TileScheduler. For * most PlanarImages (including OpImages) * this will be the image on which queueTiles() was * invoked; for RenderedOp nodes this will be the rendering * of the node. */ PlanarImage getImage(); /** * Returns the tile indices of all tiles associated with the request. */ Point[] getTileIndices(); /** * Returns the array of TileComputationListeners specified as * monitoring the request. The returned value should be null * if there are no such listeners. */ TileComputationListener[] getTileListeners(); /** * Whether this TileRequest implementation supports the * getTileStatus() method. */ boolean isStatusAvailable(); /** * Returns one of the TILE_STATUS_* constants defined in * this interface to indicate the status of the specified tile (optional * operation). Implementations for which status is available, i.e., * for which isStatusAvailable() returns true, * may but are not required to support all status levels defined by * this interface. The status levels must however be a subset of those * herein defined. * * @param tileX The X index of the tile in the tile array. * @param tileY The Y index of the tile in the tile array. * * @exception UnsupportedOperationException if * isStatusAvailable() returns false. * @exception IllegalArgumentException if the specified tile is not * associated with this request. */ int getTileStatus(int tileX, int tileY); /** * Issues a request to the TileScheduler which generated * this TileRequest to cancel all tiles in the supplied * parameter array which are associated with this request. Any tiles * in the array which are not associated with this request will be * ignored. If the parameter is null a request to cancel * all tiles in the request will be issued. This method should merely * be a convenience wrapper around the cancelTiles() * method of the TileScheduler which created the * TileRequest. */ void cancelTiles(Point[] tileIndices); } jai-core-1.1.4/src/share/classes/javax/media/jai/SequentialImage.java0000644000175000017500000000426210203035544025261 0ustar mathieumathieu/* * $RCSfile: SequentialImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:21 $ * $State: Exp $ */ package javax.media.jai; /** * A class representing an image that is associated with a time stamp * and a camera position. This class is used with ImageSequence. * *

      This class is equivalent to an AttributedImage with an * attribute defined as: * *

       * public class SequentialAttribute {
       *     protected Object position;
       *     protected Float timeStamp;
       *
       *     public SequentialAttribute(Object position, float timeStamp);
       *
       *     public Object getPosition();
       *     public float getTimeStamp();
       *
       *     public boolean equals(Object o) {
       *         if(o instanceof SequentialAttribute) {
       *	       SequentialAttribute sa = (SequentialAttribute)o;
       *	       return sa.getPosition().equals(position) &&
       *	              sa.getTimeStamp().equals(timeStamp);
       *	   }
       *         return false;
       *     }
       * }
       * 
      * * @see ImageSequence * * @deprecated as of JAI 1.1. Use * AttributedImage instead. */ public class SequentialImage { /** The image. */ public PlanarImage image; /** The time stamp associated with the image. */ public float timeStamp; /** * The camera position associated with the image. The type of this * parameter is Object so that the application may choose * any class to represent a camera position based on the individual's * needs. */ public Object cameraPosition; /** * Constructor. * * @param pi The specified planar image. * @param ts The time stamp, as a float. * @param cp The camera position object. * @throws IllegalArgumentException if pi is null. */ public SequentialImage(PlanarImage pi, float ts, Object cp) { if (pi == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } image = pi; timeStamp = ts; cameraPosition = cp; } } jai-core-1.1.4/src/share/classes/javax/media/jai/TileComputationListener.java0000644000175000017500000001162310203035544027031 0ustar mathieumathieu/* * $RCSfile: TileComputationListener.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:22 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Point; import java.awt.image.Raster; import java.util.EventListener; /** * Interface to monitor tiles which have been submitted to the * TileScheduler for non-prefetch background processing. * The request parameter of each method corresponds to the value * returned by the method used to queue the tiles, i.e., * TileScheduler.scheduleTiles() or more commonly * PlanarImage.queueTiles(). The eventSource * parameter provides the identity of the emitter of the event. If the * event is emitted by the TileScheduler itself this will * be a reference to the TileScheduler object; if it is * emitted by a RenderedOp this will be the * RenderedOp node. The image parameter will * in all cases be the image actually specified to the * TileScheduler. * *

      With respect to standard rendered imaging chains consisting of * RenderedOp nodes, any TileComputationListeners * registered with the RenderedOp itself will receive events * from the RenderedOp so that the event source will be the * RenderedOp. If the listener is registered with the * rendering of the node, then the event source will likely be the * TileScheduler itself. This is definitely the case if * the rendering is either an OpImage or is a * PlanarImage which has not overridden queueTiles(). * *

      The image parameter passed to any registered listener of * a RenderedOp will contain a reference to the rendering of the * node rather than to the node itself. * *

      For a given TileComputationListener exactly one of the * tile status callbacks should be invoked during the life cycle of a given * tile in the TileScheduler. * * @see TileScheduler * @see TileRequest * @see RenderedOp * @see OpImage * * @since JAI 1.1 */ public interface TileComputationListener extends EventListener { /** * To be invoked after each tile is computed. * * @param eventSource The actual emitter of the tile scheduling event, i.e., * the caller of this method. * @param requests The relevant tile computation requests as returned * by the method used to queue the tile. * @param image The image for which tiles are being computed as * specified to the TileScheduler. * @param tileX The X index of the tile in the tile array. * @param tileY The Y index of the tile in the tile array. * @param tile The computed tile. */ void tileComputed(Object eventSource, TileRequest[] requests, PlanarImage image, int tileX, int tileY, Raster tile); /** * To be invoked after a tile is cancelled. * * @param eventSource The actual emitter of the tile scheduling event, i.e., * the caller of this method. * @param requests The relevant tile computation requests as returned * by the method used to queue the tile. * @param image The image for which tiles are being computed as * specified to the TileScheduler. * @param tileX The X index of the tile in the tile array. * @param tileY The Y index of the tile in the tile array. */ void tileCancelled(Object eventSource, TileRequest[] requests, PlanarImage image, int tileX, int tileY); /** * To be invoked when an exceptional situation prevents computation of * a tile. * * @param eventSource The actual emitter of the tile scheduling event, i.e., * the caller of this method. * @param requests The relevant tile computation requests as returned * by the method used to queue the tile. * @param image The image for which tiles are being computed as * specified to the TileScheduler. * @param tileX The X index of the tile in the tile array. * @param tileY The Y index of the tile in the tile array. * @param situation An object describing the error or exception which * prevented computation of the tile. */ void tileComputationFailure(Object eventSource, TileRequest[] requests, PlanarImage image, int tileX, int tileY, Throwable situation); } jai-core-1.1.4/src/share/classes/javax/media/jai/ComponentSampleModelJAI.java0000644000175000017500000011163710203035544026622 0ustar mathieumathieu/* * $RCSfile: ComponentSampleModelJAI.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:07 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.ComponentSampleModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferShort; import java.awt.image.DataBufferInt; import java.awt.image.DataBufferUShort; import java.awt.image.Raster; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import com.sun.media.jai.util.DataBufferUtils; /** * 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 the dataTypes enumerated in java.awt.image.DataBuffer (all * samples of a given ComponentSampleModel are stored with the same precision) * . This class adds support for Double and Float data types in addition * to those supported by the ComponentSampleModel class in Java 2D. * All strides and offsets must be non-negative. * @see java.awt.image.ComponentSampleModel */ public class ComponentSampleModelJAI extends ComponentSampleModel { /** * 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. */ public ComponentSampleModelJAI(int dataType, int w, int h, int pixelStride, int scanlineStride, int bandOffsets[]) { super(dataType, w, h, pixelStride, scanlineStride, bandOffsets); } /** * Constructs a ComponentSampleModel with the specified * parameters. The number of bands will be given by the length of * the bandOffsets array. Different bands may be stored in * different banks 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 bankIndices The bank indices of all bands. * @param bandOffsets The band offsets of all bands. */ public ComponentSampleModelJAI(int dataType, int w, int h, int pixelStride, int scanlineStride, int bankIndices[], int bandOffsets[]) { super(dataType, w, h, pixelStride, scanlineStride, bankIndices, bandOffsets); } /** * 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... */ private int[] JAIorderBands(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 in pixels. * @param h The height in pixels */ 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 = JAIorderBands(bandOffsets,lStride*h); pStride = bands*lStride*h; } } else { // band > pix > line pStride = lStride*h; bandOff = JAIorderBands(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 = JAIorderBands(bandOffsets,pStride*w); lStride = bands*pStride*w; } else { // band > line > pix lStride = pStride*w; bandOff = JAIorderBands(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 with a subset of the bands * of this ComponentSampleModel. The new ComponentSampleModel can be * used with any DataBuffer that the existing ComponentSampleModel * can be used with. The new ComponentSampleModel/DataBuffer * combination will represent an image with a subset of the bands * of the original ComponentSampleModel/DataBuffer combination. * * @param bands subset of bands of this ComponentSampleModel */ public SampleModel createSubsetSampleModel(int 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's data type, number of banks, and size * will be 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_INT: dataBuffer = new DataBufferInt(size, numBanks); break; case DataBuffer.TYPE_SHORT: dataBuffer = new DataBufferShort(size, numBanks); break; case DataBuffer.TYPE_FLOAT: dataBuffer = DataBufferUtils.createDataBufferFloat(size, numBanks); break; case DataBuffer.TYPE_DOUBLE: dataBuffer = DataBufferUtils.createDataBufferDouble(size, numBanks); break; default: throw new RuntimeException(JaiI18N.getString("RasterFactory3")); } return dataBuffer; } /** * Returns data for a single pixel in a primitive array of type * TransferType. For a ComponentSampleModel, 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 * ComponentSampleModel csm1, to DataBuffer db2, * whose storage layout is described by * ComponentSampleModel csm2. * The transfer will generally be more efficient than using * getPixel/setPixel. *

           * 	     ComponentSampleModel csm1, csm2;
           *	     DataBufferInt db1, db2;
           * 	     csm2.setDataElements(x, y,
           *                            csm1.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. *

      * @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. * @throws ClassCastException if obj is non-null and is not * a primitive array of type TransferType. * @throws ArrayIndexOutOfBoundsException if the coordinates * are not in bounds, or if obj is non-null and is not large * enough to hold the pixel data. */ public Object getDataElements(int x, int y, Object obj, DataBuffer data) { 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; iObject 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. *

      * @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. * @see #getNumDataElements * @see #getTransferType * @see java.awt.image.DataBuffer * @throws ClassCastException if obj is non-null and is not * a primitive array of type TransferType. * @throws ArrayIndexOutOfBoundsException if the coordinates * are not in bounds, or if obj is non-null and is not large * enough to hold the pixel data. */ 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; iDataBuffer from a * primitive array of type TransferType. For a ComponentSampleModel, * 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 * ComponentSampleModel csm1, to DataBuffer db2, * whose storage layout is described by * ComponentSampleModel csm2. * The transfer will generally be more efficient than using * getPixel/setPixel. *

           * 	     ComponentSampleModel csm1, csm2;
           *	     DataBufferInt db1, db2;
           * 	     csm2.setDataElements(x, y, csm1.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. *

      * @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. * @throws ClassCastException if obj is non-null and is not * a primitive array of type TransferType. * @throws ArrayIndexOutOfBoundsException if the coordinates * are not in bounds, or if obj is non-null and is not large * enough to hold the pixel data. */ public void setDataElements(int x, int y, Object obj, DataBuffer data) { 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 * from a primitive array of type TransferType. For image data supported * by the Java 2D API, this will be one of the dataTypes supported by * java.awt.image.DataBuffer. 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. *

      * @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. * @throws ClassCastException if obj is non-null and is not * a primitive array of type TransferType. * @throws ArrayIndexOutOfBoundsException if the coordinates * are not in bounds, or if obj is non-null and is not large * enough to hold the pixel data. * @see #getNumDataElements * @see #getTransferType * @see java.awt.image.DataBuffer */ 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; iDataBuffer 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. * * @throws ArrayIndexOutOfBoundsException if coordinates are not in bounds */ public void setSample(int x, int y, int b, float s, DataBuffer data) { data.setElemFloat(bankIndices[b], y*scanlineStride + x*pixelStride + bandOffsets[b], s); } /** * 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 sample The floating point sample value * @throws ArrayIndexOutOfBoundsException if coordinates are not in bounds */ public float getSampleFloat(int x, int y, int b, DataBuffer data) { float sample = data.getElemFloat(bankIndices[b], y*scanlineStride + x*pixelStride + bandOffsets[b]); return sample; } /** * 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. * * @throws ArrayIndexOutOfBoundsException if coordinates are not in bounds */ public void setSample(int x, int y, int b, double s, DataBuffer data) { data.setElemDouble(bankIndices[b], y*scanlineStride + x*pixelStride + bandOffsets[b], s); } /** * 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 sample The double sample value * @throws ArrayIndexOutOfBoundsException if coordinates are not in bounds */ public double getSampleDouble(int x, int y, int b, DataBuffer data) { double sample = data.getElemDouble(bankIndices[b], y*scanlineStride + x*pixelStride + bandOffsets[b]); return sample; } /** * Returns all samples for a rectangle of pixels in a double * 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 dArray If non-null, returns the samples in this array. * @param data The DataBuffer containing the image data. * @throws ArrayIndexOutOfBoundsException if coordinates are not in bounds */ public double[] getPixels(int x, int y, int w, int h, double dArray[], DataBuffer data) { double pixels[]; int Offset = 0; if (dArray != null) pixels = dArray; else pixels = new double[numBands * w * h]; for (int i=y; i<(h+y); i++) { for (int j=x; j<(w+x); j++) { for (int k=0; kString containing the values of all valid fields. */ public String toString() { String ret = "ComponentSampleModelJAI: " + " dataType=" + this.getDataType() + " numBands=" + this.getNumBands() + " width=" +this.getWidth() + " height=" +this.getHeight() + " bandOffsets=[ "; for (int i = 0; i < numBands; i++) { ret += this.getBandOffsets()[i] + " "; } ret += "]"; return ret; } } jai-core-1.1.4/src/share/classes/javax/media/jai/WritablePropertySourceImpl.java0000644000175000017500000004761110203035544027532 0ustar mathieumathieu/* * $RCSfile: WritablePropertySourceImpl.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:25 $ * $State: Exp $ */ package javax.media.jai; import java.beans.PropertyChangeListener; import java.util.Collections; import java.util.Hashtable; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import javax.media.jai.util.CaselessStringKey; /** * A utility implementation of the WritablePropertySource * interface. The same internal superclass data structures are used and * are supplemented by a PropertyChangeSupportJAI to handle * property event firing. All events fired by an instance of this class * will be PropertySourceChangeEvents with an event source * equal to the object used to create the PropertyChangeSupportJAI * helper object. This object is user-specifiable at construction time or * automatically created when a PropertyChangeListener is * added or removed and the PropertyChangeSupportJAI specified * at construction was null. * * @see CaselessStringKey * @see PropertySource * @see PropertySourceChangeEvent * @see PropertySourceImpl * @see WritablePropertySource * @see PropertyChangeEmitter * @see PropertyChangeSupportJAI * * @since JAI 1.1 */ public class WritablePropertySourceImpl extends PropertySourceImpl implements WritablePropertySource { /** * Helper object for bean-style property events. Its default * value is null which indicates that no events * are to be fired. */ protected PropertyChangeSupportJAI manager = null; /** * Constructs a WritablePropertySourceImpl instance with * no properties set. */ public WritablePropertySourceImpl() { super(); } /** * Constructs a WritablePropertySourceImpl instance which * will derive properties from one or both of the supplied parameters. * The propertyMap and propertySource parameters * will be used to initialize the name-value and * name-PropertySource mappings, respectively. * Entries in the propertyMap object will be assumed * to be properties if the key is a String or a * CaselessStringKey. The propertySource * object will be queried for the names of properties that it emits * but requests for associated values will not be made at this time * so as to to defer any calculation that such requests might provoke. * The case of property names will be retained but will be ignored * insofar as the name is used as a key to the property value. * * @param propertyMap A Map from which to copy properties * which have keys which are either Strings or * CaselessStringKeys. * @param propertySource A PropertySource from which to * derive properties. * @param manager The object which will actually fire the events. * May be null in which case a default * event manager will be created as needed with this * object as its event source. */ public WritablePropertySourceImpl(Map propertyMap, PropertySource source, PropertyChangeSupportJAI manager) { super(propertyMap, source); this.manager = manager; } /** * Returns the value of a property. If the property name is not * recognized, java.awt.Image.UndefinedProperty will * be returned. * *

      If the requested name is found in the name-value mapping, * the corresponding value will be returned. Otherwise the * name-PropertySource mapping will be queried and the * value will be derived from the found PropertySource, * if any. If the value is derived from a PropertySource, * a record will be kept of this and this property will be referred to * as a "cached property". Derivation of the value from a * PropertySource rather than from the name-value * mapping may cause a PropertySourceChangeEvent to be fired. * * @param propertyName the name of the property, as a String. * * @return the value of the property, as an * Object, or the value * java.awt.Image.UndefinedProperty. * * @exception IllegalArgumentException if propertyName * is null. */ public Object getProperty(String propertyName) { if(propertyName == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } synchronized(properties) { // Check whether a value mapping exists. boolean isMapped = properties.containsKey(new CaselessStringKey(propertyName)); // Retrieve the value. Object value = super.getProperty(propertyName); // Fire an event if necessary, i.e., if there is an event manager // and the property value was just derived from a PropertySource in // the name-PropertySource mapping. if(manager != null && !isMapped && value != java.awt.Image.UndefinedProperty) { // Value was derived from a PropertySource -> event. Object eventSource = manager.getPropertyChangeEventSource(); PropertySourceChangeEvent evt = new PropertySourceChangeEvent(eventSource, propertyName, java.awt.Image.UndefinedProperty, value); manager.firePropertyChange(evt); } return value; } } /** * Adds the property value associated with the supplied name to * the PropertySource. Values set by this means will * supersede any previously defined value of the property. * *

      PropertySourceChangeEvents may be fired * with a name set to that of the property (retaining case) and old and * new values set to the previous and current values of the property, * respectively. The value returned by the getSource() * method of the event is determined by the value passed to the * constructor of the PropertyChangeSupportJAI parameter. * Neither the old nor the new value may be null: undefined * properties must as usual be indicated by the constant value * java.awt.Image.UndefinedProperty. It is however * legal for either but not both of the old and new property values * to equal java.awt.Image.UndefinedProperty. * * @param propertyName the name of the property, as a String. * @param propertyValue the property, as a general Object. * * @exception IllegalArgumentException if propertyName * or propertyValue * is null. */ public void setProperty(String propertyName, Object propertyValue) { if(propertyName == null || propertyValue == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } synchronized(properties) { CaselessStringKey key = new CaselessStringKey(propertyName); // Set the entry in the name-value mapping. Object oldValue = properties.put(key, propertyValue); if(oldValue == null) { oldValue = java.awt.Image.UndefinedProperty; } // Suppress the name if present in the cached properties listing. cachedPropertyNames.remove(key); if(manager != null && !oldValue.equals(propertyValue)) { Object eventSource = manager.getPropertyChangeEventSource(); PropertySourceChangeEvent evt = new PropertySourceChangeEvent(eventSource, propertyName, oldValue, propertyValue); manager.firePropertyChange(evt); } } } /** * Removes the named property from the PropertySource. * PropertySourceChangeEvents may be fired if the property * values is actually removed from the name-value mapping. In this case * the new value of the property event will be * java.awt.Image.UndefinedProperty. * * @param propertyName the name of the property, as a String. * @param propertyValue the property, as a general Object. * * @exception IllegalArgumentException if propertyName * is null. */ public void removeProperty(String propertyName) { if(propertyName == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } synchronized(properties) { CaselessStringKey key = new CaselessStringKey(propertyName); // Remove the entry from the name-value mapping and save its value. Object oldValue = properties.remove(key); // Remove the entry from the name-PropertySource mapping and from // the list of cached properties. propertySources.remove(key); cachedPropertyNames.remove(key); if(manager != null && oldValue != null) { Object eventSource = manager.getPropertyChangeEventSource(); PropertySourceChangeEvent evt = new PropertySourceChangeEvent(eventSource, propertyName, oldValue, java.awt.Image.UndefinedProperty); manager.firePropertyChange(evt); } } } /** * Copies from the supplied Map to the property set all * entries wherein the key is an instance of String * or CaselessStringKey. Values set by this means will * supersede any previously defined values of the respective properties. * All property values are copied by reference. * PropertySourceChangeEvents may be fired for each * property added. If the property was not previously defined the * old value of the property event will be * java.awt.Image.UndefinedProperty. * * @param propertyMap A Map from which to copy properties * which have keys which are either Strings or * CaselessStringKeys. If null no * properties will be added. */ public void addProperties(Map propertyMap) { if(propertyMap != null) { synchronized(properties) { Iterator keys = propertyMap.keySet().iterator(); while(keys.hasNext()) { Object key = keys.next(); if(key instanceof String) { setProperty((String)key, propertyMap.get(key)); } else if(key instanceof CaselessStringKey) { setProperty(((CaselessStringKey)key).getName(), propertyMap.get(key)); } } } } } /** * Adds a PropertySource to the * name-PropertySource mapping. The * actual property values are not requested at this time but instead * an entry for the name of each property emitted by the * PropertySource is added to the * name-PropertySource mapping. Properties defined by * this PropertySource supersede those of all other * previously added PropertySources including that * specified at construction, if any. Note that this method will not * provoke any events as no properties will actually have changed. * * @param propertySource A PropertySource from which to * derive properties. If null nothing is done. */ public void addProperties(PropertySource propertySource) { if(propertySource != null) { synchronized(properties) { String[] names = propertySource.getPropertyNames(); if(names != null) { int length = names.length; for(int i = 0; i < length; i++) { propertySources.put(new CaselessStringKey(names[i]), propertySource); } } } } } /** * Clears all properties from the PropertySource by * invoking removeProperty() with all known names. * PropertySourceChangeEvents may be fired * for each stored property removed. In this case the new value * of the property event will be * java.awt.Image.UndefinedProperty. */ public void clearProperties() { synchronized(properties) { String[] names = getPropertyNames(); if(names != null) { int length = names.length; for(int i = 0; i < length; i++) { removeProperty(names[i]); } } } } /** * Clears the name-value mapping of all properties. * PropertySourceChangeEvents may be fired * for each stored property removed. In this case the new value * of the property event will be * java.awt.Image.UndefinedProperty. */ public void clearPropertyMap() { synchronized(properties) { Iterator keys = properties.keySet().iterator(); while(keys.hasNext()) { CaselessStringKey key = (CaselessStringKey)keys.next(); Object oldValue = properties.get(key); keys.remove(); if(manager != null) { Object eventSource = manager.getPropertyChangeEventSource(); PropertySourceChangeEvent evt = new PropertySourceChangeEvent(eventSource, key.getName(), oldValue, java.awt.Image.UndefinedProperty); manager.firePropertyChange(evt); } } // Also clear cached names. cachedPropertyNames.clear(); } } /** * Clears the name-PropertySource mapping. * No events will be fired. */ public void clearPropertySourceMap() { synchronized(properties) { propertySources.clear(); } } /** * Clears from the name-value mapping all properties which were * derived from a source in the name-PropertySource mapping. * PropertySourceChangeEvents may be fired * for each stored property removed. In this case the new value * of the property event will be * java.awt.Image.UndefinedProperty. */ public void clearCachedProperties() { synchronized(properties) { Iterator names = cachedPropertyNames.iterator(); while(names.hasNext()) { CaselessStringKey name = (CaselessStringKey)names.next(); Object oldValue = properties.remove(name); names.remove(); // remove name from cachedPropertyNames. if(manager != null) { Object eventSource = manager.getPropertyChangeEventSource(); PropertySourceChangeEvent evt = new PropertySourceChangeEvent(eventSource, name.getName(), oldValue, java.awt.Image.UndefinedProperty); manager.firePropertyChange(evt); } } } } /** * Removes from the name-PropertySource mapping all entries * which refer to the supplied PropertySource. */ public void removePropertySource(PropertySource propertySource) { synchronized(properties) { Iterator keys = propertySources.keySet().iterator(); while(keys.hasNext()) { Object ps = propertySources.get(keys.next()); if(ps.equals(propertySource)) { keys.remove(); // remove this entry from propertySources. } } } } /** * Add a PropertyChangeListener to the listener list. The * listener is registered for all properties. * *

      If the property event utility object was not set at construction, * then it will be initialized to a PropertyChangeSupportJAI * whose property event source is this object. */ public void addPropertyChangeListener(PropertyChangeListener listener) { getEventManager().addPropertyChangeListener(listener); } /** * Add a PropertyChangeListener for a specific property. The * listener will be invoked only when a call on * firePropertyChange names that specific property. * *

      If the property event utility object was not set at construction, * then it will be initialized to a PropertyChangeSupportJAI * whose property event source is this object. */ public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { getEventManager().addPropertyChangeListener(propertyName, listener); } /** * Remove a PropertyChangeListener from the listener list. * This removes a PropertyChangeListener that was registered * for all properties. * *

      If the property event utility object was not set at construction, * then it will be initialized to a PropertyChangeSupportJAI * whose property event source is this object. */ public void removePropertyChangeListener(PropertyChangeListener listener) { getEventManager().removePropertyChangeListener(listener); } /** * Remove a PropertyChangeListener for a specific property. * *

      If the property event utility object was not set at construction, * then it will be initialized to a PropertyChangeSupportJAI * whose property event source is this object. */ public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { getEventManager().removePropertyChangeListener(propertyName, listener); } /** * Returns the utility property event manager. If none has been set, * initialize it to one whose source is this object. */ private PropertyChangeSupportJAI getEventManager() { if(manager == null) { synchronized(this) { manager = new PropertyChangeSupportJAI(this); } } return manager; } } jai-core-1.1.4/src/share/classes/javax/media/jai/Warp.java0000644000175000017500000004770710203035544023130 0ustar mathieumathieu/* * $RCSfile: Warp.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:23 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Rectangle; import java.awt.geom.Point2D; import java.io.Serializable; /** * A description of an image warp. * *

      The central method of a Warp is * warpSparseRect(), which returns the source pixel positions * for a specified (subdivided) rectangular region of the output. * *

      As in the Interpolation class, pixel positions are * represented using scaled integer coordinates, yielding subpixel * accuracy but still allowing the use of integer arithmetic. The * degree of precision is set by means of the * getSubSampleBitsH() and getSubSampleBitsV * parameters to the warpRect() method. * * @see Interpolation * @see WarpAffine * @see WarpGrid * @see WarpPerspective * @see WarpPolynomial * @see WarpQuadratic * @see WarpCubic * @see WarpGeneralPolynomial * @see WarpOpImage */ public abstract class Warp extends Object implements Serializable { /** Default constructor. */ protected Warp() {} /** * Computes the source subpixel positions for a given rectangular * destination region. The destination region is specified using * normal integral (full pixel) coordinates. The source positions * returned by the method are specified in fixed point, subpixel * coordinates using the subsampleBitsH and * subsampleBitsV parameters. * *

      The integral destination rectangle coordinates should be * considered pixel indices. The continuous plane of pixels * locates each pixel index at a half-pixel location. For example, * destination pixel (0,0) is located at the real location (0.5, 0.5). * Thus pixels are considered to have a dimension of (1.0 x 1.0) with * their "energy" concentrated in a "delta function" at relative * coordinates (0.5, 0.5). * *

      Destination to source mappings must keep this (0.5, 0.5) pixel * center in mind when formulating transformation functions. Given * integral destination pixel indices as an input, the fractional * source location, as calculated by functions X(xDst,yDst), Y(xDst,yDst) * is given by: *

           *
           *     Xsrc = X(xDst+0.5, yDst+0.5) - 0.5
           *     Ysrc = Y(xDst+0.5, yDst+0.5) - 0.5
           *
           * 
      * *

      The subtraction of 0.5 in the above formula produces the * source pixel indices (in fractional form) needed to implement * the various types of interpolation algorithms. * *

      All of the Sun-supplied warp mapping functions perform the * above final subtraction, since they have no knowledge of what * interpolation algorithm will be used by a WarpOpImage implementation. * *

      As a convenience, an implementation is provided for this * method that calls warpSparseRect(). Subclasses * may wish to provide their own implementations for better * performance. * * @param x The minimum X coordinate of the destination region. * @param y The minimum Y coordinate of the destination region. * @param width The width of the destination region. Must be positive. * @param height The height of the destination region. Must be positive. * @param subsampleBitsH The desired fixed-point precision of the * output X coordinates. Must be positive. * @param subsampleBitsV The desired fixed-point precision of the * output Y coordinates. Must be positive. * @param destRect An int array containing at least * 2*width*height elements, or * null. If null, a new array * will be constructed. * * @return A reference to the destRect parameter if it is * non-null, or a new int array * of length 2*width*height otherwise. */ public int[] warpRect(int x, int y, int width, int height, int subsampleBitsH, int subsampleBitsV, int[] destRect) { if (destRect != null && destRect.length < (width * height * 2)) { throw new IllegalArgumentException(JaiI18N.getString("Warp0")); } return warpSparseRect(x, y, width, height, 1, 1, subsampleBitsH, subsampleBitsV, destRect); } /** * Computes the source subpixel positions for a given rectangular * destination region. The destination region is specified using * normal integral (full pixel) coordinates. The source positions * returned by the method are specified in floating point. * *

      As a convenience, an implementation is provided for this * method that calls warpSparseRect(). Subclasses * may wish to provide their own implementations for better * performance. * * @param x The minimum X coordinate of the destination region. * @param y The minimum Y coordinate of the destination region. * @param width The width of the destination region. * @param height The height of the destination region. * @param destRect A float array containing at least * 2*width*height elements, or * null. If null, a new array * will be constructed. * * @return A reference to the destRect parameter if * it is non-null, or a new float * array of length 2*width*height otherwise. * @throws IllegalArgumentException if destRect is too small. */ public float[] warpRect(int x, int y, int width, int height, float[] destRect) { if (destRect != null && destRect.length < (width * height * 2)) { throw new IllegalArgumentException(JaiI18N.getString("Warp0")); } return warpSparseRect(x, y, width, height, 1, 1, destRect); } /** * Computes the source subpixel position for a given destination * pixel. The destination pixel is specified using normal * integral (full pixel) coordinates. The source position * returned by the method is specified in fixed point, subpixel * coordinates using the subsampleBitsH and * subsampleBitsV parameters. * *

      As a convenience, an implementation is provided for this * method that calls warpSparseRect(). Subclasses * may wish to provide their own implementations for better * performance. * * @param x The minimum X coordinate of the destination region. * @param y The minimum Y coordinate of the destination region. * @param subsampleBitsH The desired fixed-point precision of the * output X coordinates. * @param subsampleBitsV The desired fixed-point precision of the * output Y coordinates. * @param destRect An int array containing at least 2 * elements, or null. If null, a * new array will be constructed. * * @return A reference to the destRect parameter if it is * non-null, or a new int array * of length 2 otherwise. * @throws IllegalArgumentException if destRect is too small. */ public int[] warpPoint(int x, int y, int subsampleBitsH, int subsampleBitsV, int[] destRect) { if (destRect != null && destRect.length < 2) { throw new IllegalArgumentException(JaiI18N.getString("Warp0")); } return warpSparseRect(x, y, 1, 1, 1, 1, subsampleBitsH, subsampleBitsV, destRect); } /** * Computes the source subpixel position for a given destination * pixel. The destination pixel is specified using normal * integral (full pixel) coordinates. The source position * returned by the method is specified in floating point. * *

      As a convenience, an implementation is provided for this * method that calls warpSparseRect(). Subclasses * may wish to provide their own implementations for better * performance. * * @param x The minimum X coordinate of the destination region. * @param y The minimum Y coordinate of the destination region. * @param destRect A float array containing at least * 2 elements, or null. If null, * a new array will be constructed. * * @return A reference to the destRect parameter if * it is non-null, or a new * float array of length 2 otherwise. * @throws IllegalArgumentException if destRect is too small. */ public float[] warpPoint(int x, int y, float[] destRect) { if (destRect != null && destRect.length < 2) { throw new IllegalArgumentException(JaiI18N.getString("Warp0")); } return warpSparseRect(x, y, 1, 1, 1, 1, destRect); } /** * Computes the source subpixel positions for a given rectangular * destination region, subsampled with an integral period. The * destination region is specified using normal integral (full * pixel) coordinates. The source positions returned by the * method are specified in fixed point, subpixel coordinates using * the subsampleBitsH and subsampleBitsV * parameters. * *

      As a convenience, an implementation is provided for this * method that calls warpSparseRect() with a * float destRect parameter. Subclasses * may wish to provide their own implementations for better * performance. * * @param x the minimum X coordinate of the destination region. * @param y the minimum Y coordinate of the destination region. * @param width the width of the destination region. * @param height the height of the destination region. * @param periodX the horizontal sampling period. * @param periodY the horizontal sampling period. * @param subsampleBitsH The desired fixed-point precision of the * output X coordinates. * @param subsampleBitsV The desired fixed-point precision of the * output Y coordinates. * @param destRect An int array containing at least * 2*((width+periodX-1)/periodX)*((height+periodY-1)/periodY) * elements, or null. If null, a * new array will be constructed. * * @return A reference to the destRect parameter if * it is non-null, or a new int * array otherwise. * @throws IllegalArgumentException if destRect is too small. */ public int[] warpSparseRect(int x, int y, int width, int height, int periodX, int periodY, int subsampleBitsH, int subsampleBitsV, int[] destRect) { int nVals = 2*((width+periodX-1)/periodX)*((height+periodY-1)/periodY); if (destRect != null && destRect.length < nVals) { throw new IllegalArgumentException(JaiI18N.getString("Warp0")); } float[] fdestRect = warpSparseRect(x, y, width, height, periodX, periodY, (float[])null); int size = fdestRect.length; if (destRect == null) { destRect = new int[size]; } int precH = 1 << subsampleBitsH; int precV = 1 << subsampleBitsV; for (int i = 0; i < size; i += 2) { destRect[i] = (int)Math.floor(fdestRect[i]*precH); destRect[i+1] = (int)Math.floor(fdestRect[i + 1]*precV); } return destRect; } /** *

      This method is must be implemented in all concrete subclasses. * * @param x The minimum X coordinate of the destination region. * @param y The minimum Y coordinate of the destination region. * @param width The width of the destination region. * @param height The height of the destination region. * @param periodX The horizontal sampling period. * @param periodY The vertical sampling period. * * @param destRect A float array containing at least * 2*((width+periodX-1)/periodX)* * ((height+periodY-1)/periodY) * elements, or null. If null, a * new array will be constructed. * * @return a reference to the destRect parameter if * it is non-null, or a new * float array otherwise. */ public abstract float[] warpSparseRect(int x, int y, int width, int height, int periodX, int periodY, float[] destRect); /** * Computes a rectangle that is guaranteed to enclose the region * of the destination that can potentially be affected by the * pixels of a rectangle of a given source. * Unlike the corresponding WarpOpImage method, * this routine may return null * if it is infeasible to compute such a bounding box. * *

      The default implementation in this class returns null. * * @param sourceRect The Rectangle in source coordinates. * * @return A Rectangle in the destination coordinate * system that enclose the region that can potentially be * affected by the pixels of a rectangle of a given source, * or null. */ public Rectangle mapSourceRect(Rectangle sourceRect) { return null; } /** * Computes a Rectangle that is guaranteed to enclose the region * of the source that is required in order to produce a given * rectangular output region. * * @param destRect The Rectangle in destination coordinates. * * @return A Rectangle in the source coordinate * system that is guaranteed to contain all pixels * referenced by the output of warpRect() on * the destination region, or null. * * @throws IllegalArgumentException if destRect is * null. */ public Rectangle mapDestRect(Rectangle destRect) { if ( destRect == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } int x = destRect.x; int y = destRect.y; int w = destRect.width; // the column immediately to the right int h = destRect.height; // and bottom of the last column // Alloc an array large enough for the largest destRect side float[] warpPoints = new float[Math.max(w * 2, (h - 2) * 2)];; // Map the pixels along the edges and find their min and max. // Map Top edge. int length = w * 2; // length for top edge warpSparseRect(x, y, w, 1, 1, 1, warpPoints); // initialize min/maxX/Y to first point float minX = warpPoints[0]; float maxX = warpPoints[0]; float minY = warpPoints[1]; float maxY = warpPoints[1]; float thisX, thisY; for (int i = 2; i < length; i += 2) { thisX = warpPoints[i]; thisY = warpPoints[i+1]; if (thisX < minX) { minX = thisX; } else if (thisX > maxX) { maxX = thisX; } if (thisY < minY) { minY = thisY; } else if (thisY > maxY) { maxY = thisY; } } // Map bottom edge. warpSparseRect(x, y + h - 1, w, 1, 1, 1, warpPoints); for (int i = 0; i < length; i += 2) { thisX = warpPoints[i]; thisY = warpPoints[i+1]; if (thisX < minX) { minX = thisX; } else if (thisX > maxX) { maxX = thisX; } if (thisY < minY) { minY = thisY; } else if (thisY > maxY) { maxY = thisY; } } // Map left edge. length = (h - 2) * 2; warpSparseRect(x, y + 1, 1, h - 2, 1, 1, warpPoints); for (int i = 0; i < length; i += 2) { thisX = warpPoints[i]; thisY = warpPoints[i+1]; if (thisX < minX) { minX = thisX; } else if (thisX > maxX) { maxX = thisX; } if (thisY < minY) { minY = thisY; } else if (thisY > maxY) { maxY = thisY; } } // Map right edge. warpSparseRect(x + w - 1, y + 1, 1, h - 2, 1, 1, warpPoints); for (int i = 0; i < length; i += 2) { thisX = warpPoints[i]; thisY = warpPoints[i+1]; if (thisX < minX) { minX = thisX; } else if (thisX > maxX) { maxX = thisX; } if (thisY < minY) { minY = thisY; } else if (thisY > maxY) { maxY = thisY; } } x = (int)Math.floor(minX); y = (int)Math.floor(minY); w = (int)Math.ceil(maxX - x) + 1; h = (int)Math.ceil(maxY - y) + 1; return new Rectangle(x, y, w, h); } /** * Computes the source point corresponding to the supplied point. * *

      This method returns the value of pt in the following * code snippet: * *

           * float[] sourceXY = warpSparseRect((int)destPt.getX(),
           *                                   (int)destPt.getY(),
           *                                   1, 1, 1, 1, null);
           * Point2D pt = (Point2D)destPt.clone();
           * pt.setLocation(sourceXY[0], sourceXY[1]);
           * 
      * * Subclasses requiring different behavior should override this * method. This would be the case for those which desire a more * precise mapping.

      * * @param destPt the position in destination image coordinates * to map to source image coordinates. * * @return a Point2D of the same class as * destPt. * * @throws IllegalArgumentException if destPt is * null. * * @since JAI 1.1.2 */ public Point2D mapDestPoint(Point2D destPt) { if (destPt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } float[] sourceXY = warpSparseRect((int)destPt.getX(), (int)destPt.getY(), 1, 1, 1, 1, null); Point2D pt = (Point2D)destPt.clone(); pt.setLocation(sourceXY[0], sourceXY[1]); return pt; } /** * Computes the destination point corresponding to the supplied point. * *

      This method returns null. Subclasses requiring * different behavior should override this method.

      * * @param sourcePt the position in source image coordinates * to map to destination image coordinates. * * @return null. * * @throws IllegalArgumentException if sourcePt is * null. * * @since JAI 1.1.2 */ public Point2D mapSourcePoint(Point2D sourcePt) { if (sourcePt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return null; } } jai-core-1.1.4/src/share/classes/javax/media/jai/ParameterListDescriptorImpl.java0000644000175000017500000004270110203035544027641 0ustar mathieumathieu/* * $RCSfile: ParameterListDescriptorImpl.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:14 $ * $State: Exp $ */ package javax.media.jai; import com.sun.media.jai.util.CaselessStringArrayTable; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.Vector; import javax.media.jai.util.Range; /** * A concrete implementation of the ParameterListDescriptor * interface. * * @see ParameterListDescriptor * * @since JAI 1.1 */ public class ParameterListDescriptorImpl implements ParameterListDescriptor, java.io.Serializable { /** The number of parameters in the list described by this parameter. */ private int numParams; /** The names of each parameter. */ private String[] paramNames; /** The Class type of each parameter. There is one-to-one * mapping between this and paramNames. */ private Class[] paramClasses; /** * The default values for of each parameter. There is one-to-one * mapping between this and paramNames. If there is * no default value for a given parameter, it is initialized with * ParameterListDescriptor.NO_PARAMETER_DEFAULT */ private Object[] paramDefaults; /** * Defines the valid parameter values for each parameter. */ private Object[] validParamValues; /** * A CaselessStringArrayTable mapping the parameter * names to their indices in the above arrays in a case-insensitive * manner. */ private CaselessStringArrayTable paramIndices; /** The Object to reflect upon for enumerated parameters. */ private Object descriptor; /** * Indicates if the validParamValues field has been * initialized. */ private boolean validParamsInitialized = false; /** * Uses reflection to examine "descriptor" for public, * static final Fields * that are instances of "paramClass". * * @param descriptor the object to be reflected upon. * @param paramClass the parameter class * * @return a Set of enumerated values. * * @throws IllegalArgumentException if descriptor is null * or paramClass is null * @throws IllegalArgumentException if "paramClass" is not an instance of * EnumeratedParameter */ public static Set getEnumeratedValues( Object descriptor, Class paramClass) { if ((descriptor == null) || (paramClass == null)) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); // If not an enumerated parameter, return null if (!EnumeratedParameter.class.isAssignableFrom(paramClass)) throw new IllegalArgumentException( JaiI18N.formatMsg("ParameterListDescriptorImpl10", new Object[] { paramClass.getName() })); Field[] fields = descriptor.getClass().getDeclaredFields(); if (fields == null) return null; // Look for all parameters which are instance of EnumeratedParameter. int numFields = fields.length; Set valueSet = null; // Look for all fields which are static, final // instances of the class of this parameter. for(int j = 0; j < numFields; j++) { Field field = fields[j]; int modifiers = field.getModifiers(); if(Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)) { Object fieldValue = null; try { fieldValue = field.get(null); } catch(Exception e) { // Ignore exception } if(paramClass.isInstance(fieldValue)) { if(valueSet == null) { valueSet = new HashSet(); } if(valueSet.contains(fieldValue)) { // This error is a coding error // which should be caught by the // developer the first time the // bogus descriptor is loaded. throw new UnsupportedOperationException( JaiI18N.getString("ParameterListDescriptorImpl0")); } // Save parameter value in Set. valueSet.add(fieldValue); } } } return valueSet; } /** * A wrapper method to get the valid parameter values for * the specified parameter index. This makes sure that the * field has been initialized if it wasnt done before. */ private Object getValidParamValue(int index) { if (validParamsInitialized) return validParamValues[index]; synchronized (this) { if (validParamValues == null) { validParamValues = new Object[numParams]; } Class enumeratedClass = EnumeratedParameter.class; for (int i = 0; i < numParams; i++) { if (validParamValues[i] != null) continue; if (enumeratedClass.isAssignableFrom(paramClasses[i])) { validParamValues[i] = getEnumeratedValues(descriptor, paramClasses[i]); } } } validParamsInitialized = true; return validParamValues[index]; } /** * Constructor for descriptors that dont have any parameters. */ public ParameterListDescriptorImpl() { this.numParams = 0; this.paramNames = null; this.paramClasses = null; this.paramDefaults = null; this.paramIndices = new CaselessStringArrayTable(); this.validParamValues = null; } /** * Constructor. * * @param descriptor the object to be reflected upon for enumerated values * @param paramNames the names of each parameter. can be null * if there are no parameters. * @param paramClasses the Class type of each parameter. * can be null if there are no parameters. * @param paramDefaults the default values for each parameter. can be * null if there are no parameters or if * there are no default values, in which case the parameter * defaults are assumed to be * ParameterListDescriptor.NO_PARAMETER_DEFAULT * @param validParamValues defines the valid values for each parameter. * *

      Each element of this array can be null (if the parameter * can take on any value of its class or if it is an enumerated parameter * whose values are to be auto-detected - see getEnumeratedValues * ), or a Set (for user specified enumerated * values) or a Range (for parameters that are * Comparable.) * *

      The valid set of values for an object which is neither an * EnumeratedParameter nor Comparable * should just be the set of all possible instances of the associated * class, i.e., the parameter has to be an instance of the specified * class. * *

      If this array itself is null then it is treated * as an array full of nulls as described above. * * @throws IllegalArgumentException if paramNames is non-null * and the number of paramClasses or a non-null * paramDefaults does not match the length of * paramNames * @throws IllegalArgumentException if null is passed in for * validParamValues for a parameter whose * class is of EnumeratedParameter type. */ public ParameterListDescriptorImpl(Object descriptor, String[] paramNames, Class [] paramClasses, Object[] paramDefaults, Object[] validParamValues) { int numParams = (paramNames == null) ? 0 : paramNames.length; if ((paramDefaults != null) && (paramDefaults.length != numParams)) throw new IllegalArgumentException("paramDefaults" + JaiI18N.getString("ParameterListDescriptorImpl1")); if ((validParamValues != null) && (validParamValues.length != numParams)) throw new IllegalArgumentException("validParamValues" + JaiI18N.getString("ParameterListDescriptorImpl2")); this.descriptor = descriptor; if (numParams == 0) { if ((paramClasses != null) && (paramClasses.length != 0)) throw new IllegalArgumentException("paramClasses" + JaiI18N.getString("ParameterListDescriptorImpl3")); this.numParams = 0; this.paramNames = null; this.paramClasses = null; this.paramDefaults = null; this.paramIndices = new CaselessStringArrayTable(); this.validParamValues = null; } else { if ((paramClasses == null) || (paramClasses.length != numParams)) throw new IllegalArgumentException("paramClasses" + JaiI18N.getString("ParameterListDescriptorImpl3")); this.numParams = numParams; this.paramNames = paramNames; this.paramClasses = paramClasses; this.validParamValues = validParamValues; // // If the defaults are null, fill in NO_PARAMETER_DEFAULT // Else, make sure they belong to the right class. // if (paramDefaults == null) { this.paramDefaults = new Object[numParams]; for (int i = 0; i < numParams; i++) this.paramDefaults[i] = ParameterListDescriptor.NO_PARAMETER_DEFAULT; } else { this.paramDefaults = paramDefaults; for (int i = 0; i < numParams; i++) { if ((paramDefaults[i] == null) || (paramDefaults[i] == ParameterListDescriptor.NO_PARAMETER_DEFAULT)) continue; if (!paramClasses[i].isInstance(paramDefaults[i])) { throw new IllegalArgumentException( JaiI18N.formatMsg("ParameterListDescriptorImpl4", new Object[] { paramDefaults[i].getClass().getName(), paramClasses [i].getName(), paramNames[i] })); } } } // // Make sure that validParamValues belongs to the right class. // if (validParamValues != null) { Class enumeratedClass = EnumeratedParameter.class; for (int i = 0; i < numParams; i++) { if (validParamValues[i] == null) continue; if (enumeratedClass.isAssignableFrom(paramClasses[i])) { // If paramClass[i] is an enumerated parameter, then // the validParamValues[i] has to be a Set if (!(validParamValues[i] instanceof Set)) throw new IllegalArgumentException( JaiI18N.formatMsg("ParameterListDescriptorImpl5", new Object[] { paramNames[i] })); } else if (validParamValues[i] instanceof Range) { Range range = (Range)validParamValues[i]; // If the validParamValues[i] is a Range, then // the Range's class must match with paramClass[i] if (!paramClasses[i].isAssignableFrom( range.getElementClass())) throw new IllegalArgumentException( JaiI18N.formatMsg("ParameterListDescriptorImpl6", new Object[] { range.getElementClass().getName(), paramClasses[i].getName(), paramNames[i] })); } else { // Otherwise, the validParamValues[i] has to be // an instance of the paramClasses[i] if (!paramClasses[i].isInstance(validParamValues[i])) throw new IllegalArgumentException( JaiI18N.formatMsg("ParameterListDescriptorImpl7", new Object[] { validParamValues[i].getClass().getName(), paramClasses[i].getName(), paramNames[i] })); } } } paramIndices = new CaselessStringArrayTable(paramNames); } } /** * Returns the total number of parameters. */ public int getNumParameters() { return numParams; } /** * Returns an array of Classes that describe the types * of parameters. If there are no parameters, this method returns * null. */ public Class[] getParamClasses() { return paramClasses; } /** * Returns an array of Strings that are the * names of the parameters associated with this descriptor. If there * are no parameters, this method returns null. */ public String[] getParamNames() { return paramNames; } /** * Returns an array of Objects that define the default * values of the parameters. Default values may be null. * The NO_PARAMETER_DEFAULT static Object * indicates that a parameter has no default value. If there are no * parameters, this method returns null. */ public Object[] getParamDefaults() { return paramDefaults; } /** * Returns the default value of a specified parameter. The default * value may be null. If a parameter has no default * value, this method returns NO_PARAMETER_DEFAULT. * * @param parameterName The name of the parameter whose default * value is queried. * * @throws IllegalArgumentException if parameterName is null * or if the parameter does not exist. */ public Object getParamDefaultValue(String parameterName) { return paramDefaults[paramIndices.indexOf(parameterName)]; } /** * Returns the Range that represents the range of valid * values for the specified parameter. Returns null if * the parameter can take on any value or if the valid values are * not representable as a Range. * * @param parameterName The name of the parameter whose valid range * of values is to be determined. * * @throws IllegalArgumentException if parameterName is null * or if the parameter does not exist. */ public Range getParamValueRange(String parameterName) { Object values = getValidParamValue(paramIndices.indexOf(parameterName)); if ((values == null) || (values instanceof Range)) return (Range)values; return null; } /** * Return an array of the names of all parameters the type of which is * EnumeratedParameter. * * @return The requested array of names or null if there * are no parameters with EnumeratedParameter type. */ public String[] getEnumeratedParameterNames() { Vector v = new Vector(); for (int i = 0; i < numParams; i++) { if (EnumeratedParameter.class.isAssignableFrom(paramClasses[i])) v.add(paramNames[i]); } if (v.size() <= 0) return null; return (String[])v.toArray(new String[0]); } /** * Return an array of EnumeratedParameter objects * corresponding to the parameter with the specified name. * * @param parameterName The name of the parameter for which the * EnumeratedParameter array is to be returned. * * @throws IllegalArgumentException if parameterName is null * or if the parameter does not exist. * @throws UnsupportedOperationException if there are no enumerated * parameters associated with the descriptor. * @throws IllegalArgumentException if parameterName is * a parameter the class of which is not a subclass of * EnumeratedParameter. * * @return An array of EnumeratedParameter objects * representing the range of values for the named parameter. */ public EnumeratedParameter[] getEnumeratedParameterValues(String parameterName) { int i = paramIndices.indexOf(parameterName); if (!EnumeratedParameter.class.isAssignableFrom(paramClasses[i])) throw new IllegalArgumentException(parameterName + ":" + JaiI18N.getString("ParameterListDescriptorImpl8")); Set enumSet = (Set)getValidParamValue(i); if (enumSet == null) return null; return (EnumeratedParameter[]) enumSet.toArray(new EnumeratedParameter[0]); } /** * Checks to see if the specified parameter can take on the specified * value. * * @param parameterName The name of the parameter for which the * validity check is to be performed. * * @throws IllegalArgumentException if parameterName is null * or if the parameter does not exist. * @throws IllegalArgumentException if the class of the object "value" * is not an instance of the class type of parameter * pointed to by the parameterName * * @return true, if it is valid to pass this value in for this * parameter, false otherwise. */ public boolean isParameterValueValid(String parameterName, Object value) { int index = paramIndices.indexOf(parameterName); if ((value == null) && (paramDefaults[index] == null)) { return true; } // Make sure the object belongs to the right class if ((value != null) && !paramClasses[index].isInstance(value)) { throw new IllegalArgumentException( JaiI18N.formatMsg("ParameterListDescriptorImpl9", new Object[] { value.getClass().getName(), paramClasses[index].getName(), parameterName })); } Object validValues = getValidParamValue(index); // If validValues is null then any value is acceptable. if (validValues == null) return true; // If validValues is a Range, make sure "value" lies within it. if (validValues instanceof Range) return ((Range)validValues).contains((Comparable)value); // If validValues is a Set, then make sure that "value" is contained // in the Set. if (validValues instanceof Set) return ((Set)validValues).contains(value); // Otherwise the value must be the same as validValues return value == validValues; } } jai-core-1.1.4/src/share/classes/javax/media/jai/ColormapOpImage.java0000644000175000017500000002220110203035544025213 0ustar mathieumathieu/* * $RCSfile: ColormapOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:06 $ * $State: Exp $ */ package javax.media.jai; import java.awt.image.ColorModel; import java.awt.image.IndexColorModel; import java.awt.image.RenderedImage; import java.util.Map; import javax.media.jai.ImageLayout; import javax.media.jai.PointOpImage; import javax.media.jai.TileCache; import javax.media.jai.util.CaselessStringKey; /** * A class to be used to implement an operation which may conditionally * be accelerated by transforming the colormap of the source image instead * of its data. An instance of this class will represent the destination * of a single-source point operation which may be effected by a simple * transformation of the source image colormap if the ColorModels * of the source and destination images are both instances of * IndexColorModel. A subclass may take advantage of this * capability merely by implementing transformColormap() and * invoking initializeColormapOperation() as the last * statement of its constructor. If the ColorModels are not * IndexColorModels, the behavior is the same as if the * superclass PointOpImage had been extended directly. * *

      The default behavior for a ColormapOpImage is to * do the transform only on the color map in order to accelerate processing * when the source and destination images are all color-indexed. * However, in some situations it may be desirable to transform the * pixel (index) data directly instead of transforming the colormap. * To suppress the acceleration, a mapping of the key * JAI.KEY_TRANSFORM_ON_COLORMAP with value * Boolean.FALSE should be added to the configuration * map (or the RenderingHints provided to the * create methods in the class JAI) supplied to * the corresponding operation when it is created. * *

      Transforming on the pixel (index) data is only meaningful * when the transform maps all the possible index values of the source image * into the index value set of the destination image. Otherwise, * it may generate pixel (index) values without color definitions, and * cause problems when computing the color components from pixel values. * In addition, the colormaps should be ordered in a useful way * for the transform in question, e.g. a smooth grayscale.

      * * @see java.awt.image.IndexColorModel * @see PointOpImage * * @since JAI 1.1 */ public abstract class ColormapOpImage extends PointOpImage { /** Whether the colormap acceleration flag has been initialized. */ private boolean isInitialized = false; /** Whether the operation is effected via colormap transformation. */ private boolean isColormapAccelerated; /** * Constructs a ColormapOpImage with one source image. * The parameters are forwarded unmodified to the equivalent * superclass constructor. * * @param layout The layout parameters of the destination image. * @param source The source image. * @param configuration Configurable attributes of the image including * configuration variables indexed by * RenderingHints.Keys and image properties indexed * by Strings or CaselessStringKeys. * This is simply forwarded to the superclass constructor. * @param cobbleSources Indicates whether computeRect() * expects contiguous sources. * * @throws IllegalArgumentException if source * is null. */ public ColormapOpImage(RenderedImage source, ImageLayout layout, Map configuration, boolean cobbleSources) { super(source, // tested for null in superclass layout, configuration, cobbleSources); // If the source has an IndexColorModel, override the default setting // in OpImage. The dest shall have exactly the same SampleModel and // ColorModel as the source. // Note, in this case, the source should have an integral data type. // Fix 4706651: ColormapOpImage should not force destination to // have an IndexColorModel /* ColorModel srcColorModel = source.getColorModel(); if (srcColorModel instanceof IndexColorModel) { sampleModel = source.getSampleModel().createCompatibleSampleModel( tileWidth, tileHeight); colorModel = srcColorModel; } */ isColormapAccelerated = true; Boolean value = configuration == null ? Boolean.TRUE : (Boolean)configuration.get(JAI.KEY_TRANSFORM_ON_COLORMAP); if (value != null) isColormapAccelerated = value.booleanValue(); } /** * Whether the operation is performed on the colormap directly. */ protected final boolean isColormapOperation() { return isColormapAccelerated; } /** * Method to be invoked as the last statement of a subclass constructor. * The colormap acceleration flag is set appropriately and if * true a new ColorModel is calculated by * transforming the source colormap. This method must be invoked in * the subclass constructor as it calls transformColormap() * which is abstract and therefore requires that the implementing class be * constructed before it may be invoked. */ protected final void initializeColormapOperation() { // Retrieve the ColorModels ColorModel srcCM = getSource(0).getColorModel(); ColorModel dstCM = super.getColorModel(); // Set the acceleration flag. isColormapAccelerated &= srcCM != null && dstCM != null && srcCM instanceof IndexColorModel && dstCM instanceof IndexColorModel; // Set the initialization flag. isInitialized = true; // Transform the colormap if the operation is accelerated. if(isColormapAccelerated) { // Cast the target ColorModel. IndexColorModel icm = (IndexColorModel)dstCM; // Get the size and allocate the array. int mapSize = icm.getMapSize(); byte[][] colormap = new byte[3][mapSize]; // Load the colormap. icm.getReds(colormap[0]); icm.getGreens(colormap[1]); icm.getBlues(colormap[2]); // Transform the colormap. transformColormap(colormap); // Clamp the colormap if necessary. In the Sun implementation // of IndexColorModel, getComponentSize() always returns 8 so // no clamping will be performed. for(int b = 0; b < 3; b++) { int maxComponent = 0xFF >> (8 - icm.getComponentSize(b)); if(maxComponent < 255) { byte[] map = colormap[b]; for(int i = 0; i < mapSize; i++) { if((map[i] & 0xFF) > maxComponent) { map[i] = (byte)maxComponent; } } } } // Cache references to individual color component arrays. byte[] reds = colormap[0]; byte[] greens = colormap[1]; byte[] blues = colormap[2]; // Create the RGB array. int[] rgb = new int[mapSize]; // Copy the colormap into the RGB array. if(icm.hasAlpha()) { byte[] alphas = new byte[mapSize]; icm.getAlphas(alphas); for(int i = 0; i < mapSize; i++) { rgb[i] = ((alphas[i] & 0xFF) << 24) | ((reds[i] & 0xFF) << 16) | ((greens[i] & 0xFF) << 8) | (blues[i] & 0xFF); } } else { for(int i = 0; i < mapSize; i++) { rgb[i] = ((reds[i] & 0xFF) << 16) | ((greens[i] & 0xFF) << 8) | (blues[i] & 0xFF); } } // Create the new ColorModel. colorModel= new IndexColorModel(icm.getPixelSize(), mapSize, rgb, 0, icm.hasAlpha(), icm.getTransparentPixel(), sampleModel.getTransferType()); } } /** * Transform the colormap according to the specific operation. * The modification is done in place so that the parameter array * will be modified. The format of the parameter array is * byte[3][] wherein colormap[0], * colormap[1], and colormap[2] * represent the red, green, and blue colormaps, respectively. */ protected abstract void transformColormap(byte[][] colormap); } jai-core-1.1.4/src/share/classes/javax/media/jai/OperationDescriptorImpl.java0000644000175000017500000024417710203035544027040 0ustar mathieumathieu/* * $RCSfile: OperationDescriptorImpl.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:12 $ * $State: Exp $ */ package javax.media.jai; import com.sun.media.jai.util.CaselessStringArrayTable; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.util.Collection; import java.util.Hashtable; import java.util.Iterator; import java.util.ListResourceBundle; import java.util.Locale; import java.util.ResourceBundle; import javax.media.jai.util.Range; /** * This class provides an abstract implementation of the * OperationDescriptor interface that is suitable for * subclassing. * * @see OperationDescriptor * @see RegistryElementDescriptor * */ public abstract class OperationDescriptorImpl implements OperationDescriptor, java.io.Serializable { private boolean deprecated = false; /** * The resource tags and their corresponding data, stored as an * two-dimensional String array. * * @since JAI 1.1 */ protected final String[][] resources; /** * An array of operation modes supported by this operator. * Must be a non-empty subset of "rendered", "renderable", * "collection" and "renderableCollection" or other image * operator modes to be defined later. * * @since JAI 1.1 */ protected final String[] supportedModes; /** * A CaselessStringArrayTable mapping the mode names to * their indices in the above arrays in a case-insensitive manner. */ private CaselessStringArrayTable modeIndices; /** * An array of Strings that are the names of the * sources of this operation. The names must be listed in the * order corresponding to the source Classes. * * @since JAI 1.1 */ protected final String[] sourceNames; /** * A 2D array of source classes for each source for each mode. * sourceClasses[m][i] specifies the Class for * supportedModes[m] and sourceNames[i]. */ private Class[][] sourceClasses; /** * A CaselessStringArrayTable mapping the source names to * their indices in the above arrays in a case-insensitive manner. */ private CaselessStringArrayTable sourceIndices; /** * An array of ParameterListDescriptor for each mode. */ private ParameterListDescriptor[] paramListDescriptors; /** * The array of parameter names. We need this because * ParameterListDescriptor works only with parameter names and not * parameter indices. But many of the deprecated methods and * the validation of ParameterBlock has to happen through * parameter indices. */ String[] paramNames; /** The global name of this operation. */ private String name = null; // Constructors... /** * Do some of the initialization common to all constructors. */ private String[] checkSources(String[][] resources, String[] supportedModes, String[] sourceNames, Class [][] sourceClasses) { if ((resources == null) || (resources.length == 0)) throw new IllegalArgumentException( "resources: " + JaiI18N.getString("Generic2")); if ((supportedModes == null) || (supportedModes.length == 0)) throw new IllegalArgumentException( "supportedModes: " + JaiI18N.getString("Generic2")); // Validate source related arguments. int numModes = supportedModes.length; if (sourceClasses != null) { if (sourceClasses.length != numModes) throw new IllegalArgumentException( JaiI18N.formatMsg("OperationDescriptorImpl0", new Object[] {"sourceClasses", new Integer(numModes)})); int numSources = (sourceClasses[0] == null) ? 0 : sourceClasses[0].length; if (sourceNames == null) { sourceNames = getDefaultSourceNames(numSources); } else if (sourceNames.length != numSources) { throw new IllegalArgumentException( JaiI18N.formatMsg("OperationDescriptorImpl1", new Object[] { new Integer(sourceNames.length), new Integer(numSources) })); } for (int i = 0; i < sourceClasses.length; i++) { int ns = (sourceClasses[i] == null) ? 0 : sourceClasses[i].length; if (numSources != ns) { throw new IllegalArgumentException( JaiI18N.formatMsg("OperationDescriptorImpl2", new Object[] { new Integer(ns), new Integer(numSources), supportedModes[i] })); } } } else if ((sourceNames != null) && (sourceNames.length != 0)) { throw new IllegalArgumentException( JaiI18N.formatMsg("OperationDescriptorImpl1", new Object[] { new Integer(sourceNames.length), new Integer(0) })); } return sourceNames; } /** * Constructor. Note that sourceClasses[m][i] * corresponds to the mode supportedModes[m] * and the source sourceNames[i]. Similarly * paramClasses[m][i] corresponds to the * mode supportedModes[m] and the parameter * paramNames[i]. The same holds true for * paramDefaults and validParamValues * * @param resources The resource tags and their corresponding data. * @param supportedModes The modes that this operator supports. * maybe one or more of "rendered", "renderable", "collection", * and "renderableCollection" (or other image operation related * modes that maybe defined later). Must support at least one mode. * @param sourceNames The source names. It may be null * if this operation has no sources or if the default source * naming convention ("source0", "source1", etc.) is to be used. * @param sourceClasses The source types required by this operation * for each of the above supported modes. can be null * if this operation has no sources. The number of * sources for each mode must be the same. * @param paramNames The localized parameter names. It may be * null if this operation has no parameters. * @param paramClasses The parameter types required by this operation. * for each mode. It may be null if this operation * has no parameters. The number of parameters for each mode * must be the same. * @param paramDefaults The parameter default values for each parameter * for each mode. It may be null if this * operation has no parameters, or none of the parameters has * a default value for any mode. The parameter defaults for an * individual mode may be null, if there are no defaults for * that mode. * @param validParamValues defines the valid values for each parameter * for each mode. this can be null if the operation * has no parameters. Otherwise each element can be filled in as * defined in {@link * ParameterListDescriptorImpl#ParameterListDescriptorImpl( * Object, String[], Class[], Object[], Object[])} * * @throws IllegalArgumentException if resources is * null. * @throws IllegalArgumentException if supportedModes is null * @throws IllegalArgumentException if the number of sourceClasses * for each mode is not the same or is not equal to the number * of sourceNames (if non-null). * @throws IllegalArgumentException if this operation has parameters and * paramClasses or paramNames is * null. * @throws IllegalArgumentException if sourceNames * is non-null and its length does not equal * the number of sources of this operation. * @throws IllegalArgumentException if this operation has parameters * and paramClasses, paramNames, * and paramDefaults (if all are not * null) do not all have the same number of elements. * * @since JAI 1.1 */ public OperationDescriptorImpl(String[][] resources, String[] supportedModes, String[] sourceNames, Class [][] sourceClasses, String[] paramNames, Class [][] paramClasses, Object[][] paramDefaults, Object[][] validParamValues) { sourceNames = checkSources(resources, supportedModes, sourceNames, sourceClasses); this.resources = resources; this.supportedModes = supportedModes; this.sourceNames = sourceNames; this.sourceClasses = sourceClasses; this.paramNames = paramNames; this.modeIndices = new CaselessStringArrayTable(supportedModes); this.sourceIndices = new CaselessStringArrayTable(sourceNames); // Validate parameter related arguments. int numParams = (paramNames == null) ? 0 : paramNames.length; int numModes = supportedModes.length; if (numParams == 0) { if ((paramClasses != null) && (paramClasses.length != numModes)) throw new IllegalArgumentException( JaiI18N.formatMsg("OperationDescriptorImpl0", new Object[] {"paramClasses", new Integer(numModes)})); } else { if ((paramClasses == null) || (paramClasses.length != numModes)) throw new IllegalArgumentException( JaiI18N.formatMsg("OperationDescriptorImpl0", new Object[] {"paramClasses", new Integer(numModes)})); } if ((paramDefaults != null) && (paramDefaults.length != numModes)) throw new IllegalArgumentException( JaiI18N.formatMsg("OperationDescriptorImpl0", new Object[] {"paramDefaults", new Integer(numModes)})); if ((validParamValues != null) && (validParamValues.length != numModes)) throw new IllegalArgumentException( JaiI18N.formatMsg("OperationDescriptorImpl0", new Object[] {"validParamValues", new Integer(numModes)})); // Create the ParameterListDescriptor-s for each mode. paramListDescriptors = new ParameterListDescriptor[numModes]; for (int i = 0; i < numModes; i++) { paramListDescriptors[i] = new ParameterListDescriptorImpl(this, paramNames, paramClasses[i], paramDefaults == null ? null : paramDefaults[i], validParamValues == null ? null : validParamValues[i]); } } /** * Constructor. This assumes that all modes have the same * set of parameter classes, defaults and valid values. Note * that sourceClasses[m][i] corresponds to * the mode supportedModes[m] and the source * sourceNames[i]. * * @param resources The resource tags and their corresponding data. * @param supportedModes The modes that this operator supports. * maybe one or more of "rendered", "renderable", "collection", * and "renderableCollection". Must support at least one mode. * @param sourceNames The source names. It may be null * if this operation has no sources or if the default source * naming convention ("source0", "source1", etc.) is to be used. * @param sourceClasses The source types required by this operation * for each of the above supported modes. can be null * if this operation has no sources. The number of * sources for each mode must be the same. * @param paramNames The localized parameter names. It may be * null if this operation has no parameters. * @param paramClasses The parameter types required by this operation. * It may be null if this operation has no parameters. * @param paramDefaults The parameter default values for each parameter * It may be null if this operation has no * parameters, or none of the parameters has a default value. * @param validParamValues defines the valid values for each parameter * for all modes. this can be null if the operation * has no parameters. Otherwise it can be filled in as * defined in {@link * ParameterListDescriptorImpl#ParameterListDescriptorImpl( * Object, String[], Class[], Object[], Object[])} * * @throws IllegalArgumentException if resources is * null. * @throws IllegalArgumentException if supportedModes is null * @throws IllegalArgumentException if the number of sourceClasses * for each mode is not the same or is not equal to the number * of sourceNames (if non-null). * @throws IllegalArgumentException if this operation has parameters and * paramClasses or paramNames is * null. * @throws IllegalArgumentException if sourceNames * is non-null and its length does not equal * the number of sources of this operation. * @throws IllegalArgumentException if this operation has parameters * and paramClasses, paramNames, * and paramDefaults (if all are not * null) do not all have the same number of elements. * * @since JAI 1.1 */ public OperationDescriptorImpl(String[][] resources, String[] supportedModes, String[] sourceNames, Class [][] sourceClasses, String[] paramNames, Class [] paramClasses, Object[] paramDefaults, Object[] validParamValues) { sourceNames = checkSources(resources, supportedModes, sourceNames, sourceClasses); this.resources = resources; this.supportedModes = supportedModes; this.sourceNames = sourceNames; this.sourceClasses = sourceClasses; this.paramNames = paramNames; this.modeIndices = new CaselessStringArrayTable(supportedModes); this.sourceIndices = new CaselessStringArrayTable(sourceNames); // Create one ParameterListDescriptor and use the same for each mode. ParameterListDescriptor pld = new ParameterListDescriptorImpl( this, paramNames, paramClasses, paramDefaults, validParamValues); paramListDescriptors = new ParameterListDescriptor[supportedModes.length]; for (int i = 0; i < supportedModes.length; i++) { paramListDescriptors[i] = pld; } } /** * Constructor. This assumes that all modes have the same * set of parameter classes, defaults and valid values. The * source names are automatically generated using the default * source naming convertion ("source0", "source1", etc.). The * source class list is automatically generated using * makeDefaultSourceClassList() from numSources * and supportedModes * * @param resources The resource tags and their corresponding data. * @param supportedModes The modes that this operator supports. * maybe one or more of "rendered", "renderable", "collection", * and "renderableCollection". Must support at least one mode. * @param numSources The number of sources. * @param paramNames The localized parameter names. It may be * null if this operation has no parameters. * @param paramClasses The parameter types required by this operation. * It may be null if this operation has no parameters. * @param paramDefaults The parameter default values for each parameter * It may be null if this operation has no * parameters, or none of the parameters has a default value. * @param validParamValues defines the valid values for each parameter * for all modes. this can be null if the operation * has no parameters. Otherwise it can be filled in as * defined in {@link * ParameterListDescriptorImpl#ParameterListDescriptorImpl( * Object, String[], Class[], Object[], Object[])} * * @throws IllegalArgumentException if resources is * null. * @throws IllegalArgumentException if supportedModes is null * @throws IllegalArgumentException if this operation has parameters and * paramClasses or paramNames is * null. * @throws IllegalArgumentException if this operation has parameters * and paramClasses, paramNames, * and paramDefaults (if all are not * null) do not all have the same number of elements. * * @since JAI 1.1 */ public OperationDescriptorImpl(String[][] resources, String[] supportedModes, int numSources, String[] paramNames, Class [] paramClasses, Object[] paramDefaults, Object[] validParamValues) { Class[][] sourceClasses = makeDefaultSourceClassList(supportedModes, numSources); String[] sourceNames = checkSources(resources, supportedModes, null, sourceClasses); this.resources = resources; this.supportedModes = supportedModes; this.sourceNames = sourceNames; this.sourceClasses = sourceClasses; this.paramNames = paramNames; this.modeIndices = new CaselessStringArrayTable(supportedModes); this.sourceIndices = new CaselessStringArrayTable(sourceNames); // Create one ParameterListDescriptor and use the same for each mode. ParameterListDescriptor pld = new ParameterListDescriptorImpl( this, paramNames, paramClasses, paramDefaults, validParamValues); paramListDescriptors = new ParameterListDescriptor[supportedModes.length]; for (int i = 0; i < supportedModes.length; i++) { paramListDescriptors[i] = pld; } } /** * Constructor which accepts a ParameterListDescriptor * to describe the parameters for each mode. Note * that sourceClasses[m][i] corresponds to * the mode supportedModes[m] and the source * sourceNames[i]. * * @param resources The resource tags and their corresponding data. * @param supportedModes The modes that this operator supports. * maybe one or more of "rendered", "renderable", "collection", * and "renderableCollection". Must support at least one mode. * @param sourceNames The source names. It may be null * if this operation has no sources or if the default source * naming convention ("source0", "source1", etc.) is to be used. * @param sourceClasses The source types required by this operation * for each of the above supported modes. can be null * if this operation has no sources. The number of * sources for each mode must be the same. * @param pld the parameter list descriptor for each mode. * Can be null if there are no parameters. * * @throws IllegalArgumentException if resources is * null. * @throws IllegalArgumentException if supportedModes is null * @throws IllegalArgumentException if the number of sourceClasses * for each mode is not the same or is not equal to the number * of sourceNames (if non-null). * @throws IllegalArgumentException if sourceNames * is non-null and its length does not equal * the number of sources of this operation. * * @since JAI 1.1 */ public OperationDescriptorImpl(String[][] resources, String[] supportedModes, String[] sourceNames, Class [][] sourceClasses, ParameterListDescriptor[] pld) { sourceNames = checkSources(resources, supportedModes, sourceNames, sourceClasses); this.resources = resources; this.supportedModes = supportedModes; this.sourceNames = sourceNames; this.sourceClasses = sourceClasses; this.modeIndices = new CaselessStringArrayTable(supportedModes); this.sourceIndices = new CaselessStringArrayTable(sourceNames); if ((pld != null) && (pld.length != supportedModes.length)) { throw new IllegalArgumentException( JaiI18N.formatMsg("OperationDescriptorImpl0", new Object[] { "ParameterListDescriptor's", new Integer(supportedModes.length) })); } if (pld == null) { ParameterListDescriptor tpld = new ParameterListDescriptorImpl(); paramListDescriptors = new ParameterListDescriptor[supportedModes.length]; for (int i = 0; i < supportedModes.length; i++) paramListDescriptors[i] = tpld; this.paramNames = null; } else { paramListDescriptors = pld; this.paramNames = paramListDescriptors[0].getParamNames(); } } /** * Constructor. This assumes that all modes use the same * ParameterListDescriptor. Note * that sourceClasses[m][i] corresponds to * the mode supportedModes[m] and the source * sourceNames[i]. * * @param resources The resource tags and their corresponding data. * @param supportedModes The modes that this operator supports. * maybe one or more of "rendered", "renderable", "collection", * and "renderableCollection". Must support at least one mode. * @param sourceNames The source names. It may be null * if this operation has no sources or if the default source * naming convention ("source0", "source1", etc.) is to be used. * @param sourceClasses The source types required by this operation * for each of the above supported modes. can be null * if this operation has no sources. The number of * sources for each mode must be the same. * @param pld the parameter list descriptor for all modes. * Can be null if there are no parameters. * * @throws IllegalArgumentException if resources is * null. * @throws IllegalArgumentException if supportedModes is null * @throws IllegalArgumentException if the number of sourceClasses * for each mode is not the same or is not equal to the number * of sourceNames (if non-null). * @throws IllegalArgumentException if sourceNames * is non-null and its length does not equal * the number of sources of this operation. * * @since JAI 1.1 */ public OperationDescriptorImpl(String[][] resources, String[] supportedModes, String[] sourceNames, Class [][] sourceClasses, ParameterListDescriptor pld) { sourceNames = checkSources(resources, supportedModes, sourceNames, sourceClasses); this.resources = resources; this.supportedModes = supportedModes; this.sourceNames = sourceNames; this.sourceClasses = sourceClasses; this.modeIndices = new CaselessStringArrayTable(supportedModes); this.sourceIndices = new CaselessStringArrayTable(sourceNames); if (pld == null) pld = new ParameterListDescriptorImpl(); this.paramNames = pld.getParamNames(); paramListDescriptors = new ParameterListDescriptor[supportedModes.length]; for (int i = 0; i < supportedModes.length; i++) { paramListDescriptors[i] = pld; } } /** Gets the default source names. */ private String[] getDefaultSourceNames(int numSources) { String[] defaultSourceNames = new String[numSources]; for(int i = 0; i < numSources; i++) { defaultSourceNames[i] = "source"+i; } return defaultSourceNames; } // BEGIN : RegistryElementDescriptor methods /** * Returns the name of this operation; this is the same as the * GlobalName value in the resources and is visible * to all. This is also descriptor name under which it is registered * in the OperationRegistry. * * @return A String representing the operation's * global name. * * @throws MissingResourceException if the GlobalName * resource value is not supplied in the resources. */ public String getName() { if (name == null) { name = (String)getResourceBundle( Locale.getDefault()).getObject("GlobalName"); } return name; } /** * The registry modes supported by this descriptor. Known modes * include those returned by RegistryMode.getModes() * * @return an array of Strings specifying the supported modes. * * @see RegistryMode * @see RegistryElementDescriptor * * @since JAI 1.1 */ public String[] getSupportedModes() { return supportedModes; } /** * Does this descriptor support the specified registry mode ?. * The modeNames are treated in a case-insensitive * (but retentive) manner. * * @param modeName the registry mode name * * @return true, if the implementation of this descriptor supports * the specified mode. false otherwise. * * @throws IllegalArgumentException if modeName is null * * @see RegistryElementDescriptor * * @since JAI 1.1 */ public boolean isModeSupported(String modeName) { if (modeName == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); return modeIndices.contains(modeName); } /** * Does this descriptor support properties ? * * @return The default implementation in this class returns * true so that properties are always supported. * Operations that do not wish to support properties must * override this implementation. * * @see PropertyGenerator * @see RegistryElementDescriptor * * @since JAI 1.1 */ public boolean arePropertiesSupported() { return true; } /** * Returns an array of PropertyGenerators implementing * the property inheritance for this operator that may be used as * a basis for the operation's property management. The default * implementation returns null, indicating that source * properties are simply copied. Subclasses should override this * method if they wish to produce inherited properties. * *

      For the sake of backward compatibilty, if a * deprecated constructor was used to create this object, * then this method simply calls its deprecated equivalent, * getPropertyGenerators(), if the modeName is either * "rendered" or "renderable". * * @param modeName the registry mode name * * @return An array of PropertyGenerators, or * null if this operation does not have any of * its own PropertyGenerators. * * @throws IllegalArgumentException if modeName is null * or if it is not one of the supported modes. * @throws UnsupportedOperationException if arePropertiesSupported * returns false * * @see RegistryElementDescriptor * * @since JAI 1.1 */ public PropertyGenerator[] getPropertyGenerators(String modeName) { if (modeName == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); if (deprecated) { if (modeName.equalsIgnoreCase("rendered") || modeName.equalsIgnoreCase("renderable")) return getPropertyGenerators(); } if (!arePropertiesSupported()) { throw new UnsupportedOperationException( JaiI18N.formatMsg("OperationDescriptorImpl3", new Object[] { modeName })); } return null; } /** * Returns the ParameterListDescriptor that describes * the associated parameters (NOT sources). This method returns * null if there are no parameters for the specified modeName. * If the specified modeName supports parameters but the * implementing class does not have parameters, then this method * returns a non-null ParameterListDescriptor whose * getNumParameters() returns 0. * * @param modeName the registry mode name. * * @throws IllegalArgumentException if modeName is null * or if it is not one of the supported modes. * * @see RegistryElementDescriptor * * @since JAI 1.1 */ public ParameterListDescriptor getParameterListDescriptor(String modeName) { return paramListDescriptors[modeIndices.indexOf(modeName)]; } // END : RegistryElementDescriptor methods /** * Returns the resource data for this operation. It must contain * String data for the following tags: "GlobalName", * "LocalName", "Vendor", "Description", "DocURL", and "Version". * Additional resources should be supplied when appropriate. * *

      The default implementation simply returns a reference to * the local "resources" variable, which should be supplied by * each subclass by way of the superclass constructor. It also * ignores the Locale argument, and always returns * the Strings in the default Locale. * * @param locale The Locale in which to localize the * resource data. * * @return A two-dimensional array of Strings containing * the mandatory and optional resource tags and their * corresponding resource data. (String[i][0] is * the tag for the i-th resource and String[i][1] is the * corresponding data) */ public String[][] getResources(Locale locale) { return resources; } /** * Returns the resource data for this operation in a * ResourceBundle. The resource data are taken from the * getResources() method. * *

      The default implementation ignores the Locale * argument, and always returns the resources in the default * Locale. * * @param locale The Locale in which to localize the * resource data. * * @return A ResourceBundle containing mandatory and * optional resource information. */ public ResourceBundle getResourceBundle(Locale locale) { final Locale l = locale; return new ListResourceBundle() { public Object[][] getContents() { return getResources(l); } }; // from return statement } /** * Returns the number of sources required by this operation. * All modes have the same number of sources. */ public int getNumSources() { return sourceNames.length; } /** * Returns an array of Classes that describe the types * of sources required by this operation for the specified mode. * If this operation has no sources, this method returns null. * * @param modeName the operation mode name * * @throws IllegalArgumentException if modeName is null * or if it is not one of the supported modes. * * @since JAI 1.1 */ public Class[] getSourceClasses(String modeName) { checkModeName(modeName); Class[] sc = sourceClasses[modeIndices.indexOf(modeName)]; if ((sc != null) && (sc.length <= 0)) return null; return sc; } /** * Returns an array of Strings that are the names * of the sources of this operation. If this operation has no * sources, this method returns null. If this * operation has sources but their names were not provided at * construction time, then the returned names will be of the * form "source0", "source1", etc. * * @since JAI 1.1 */ public String[] getSourceNames() { if ((sourceNames == null) || (sourceNames.length <= 0)) return null; return sourceNames; } /** * Returns a Class that describes the type of * destination this operation produces for the specified mode. * *

      For the sake of backward compatibilty, if a * deprecated constructor was used to create this object, then this * method simply calls its deprecated equivalent, if the modeName is * either "rendered" or "renderable". * * @param modeName the operation mode name * * @throws IllegalArgumentException if modeName is null * or if it is not one of the supported modes. * * @since JAI 1.1 */ public Class getDestClass(String modeName) { checkModeName(modeName); if (deprecated) { if (modeName.equalsIgnoreCase("rendered")) return getDestClass(); if (modeName.equalsIgnoreCase("renderable")) return getRenderableDestClass(); } return RegistryMode.getMode(modeName).getProductClass(); } /** * Returns true if this operation supports the * specified mode, and is capable of handling the given input * source(s) for the specified mode. The default implementation * ensures that the ParameterBlock has at least * the required number of sources. It also verifies the class type * of the first getNumSources() and makes sure * that none of them are null. Any extra sources in * the ParameterBlock are ignored. Subclasses should * override this implementation if their requirement on the * sources are different from the default. This method is used by * validateArguments to validate the sources. * *

      For the sake of backward compatibilty, if a * deprecated constructor was used to create this object, then this * method simply calls its deprecated equivalent, if the modeName is * either "rendered" or "renderable". * * @param modeName the operation mode name * @param args a ParameterBlock that has the sources * @param msg A string that may contain error messages. * * @throws IllegalArgumentException if any of the input parameters are null. * * @since JAI 1.1 * * @see #validateArguments */ protected boolean validateSources(String modeName, ParameterBlock args, StringBuffer msg) { if (modeName == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); if (deprecated) { if (modeName.equalsIgnoreCase("rendered")) return validateSources(args, msg); if (modeName.equalsIgnoreCase("renderable")) return validateRenderableSources(args, msg); } return validateSources(getSourceClasses(modeName), args, msg); } /** * Returns true if this operation is capable of * handling the input parameters for the specified mode. The default * implementation validates the number of parameters, the class type * of each parameter, and null parameter. For non-null parameters, * it also checks to see if the parameter value is valid. Subclasses * should override this implementation if their requirement on the * parameter objects are different from the default. This is used by * validateArguments to validate the parameters. * *

      JAI allows unspecified tailing parameters if these parameters * have default values. This method automatically sets these unspecified * parameters to their default values. However, if a parameter, which * has a default value, is followed by one or more parameters that * have no default values, this parameter must be specified in the * ParameterBlock; else this method returns * false. * *

      This method sets all the undefined parameters in the * ParameterBlock to their default values, if the default * values are specified. * *

      Note that DeferredData parameters will not be * recognized as valid unless the parameter is defined to have class * DeferredData.class. * *

      For the sake of backward compatibilty, if a * deprecated constructor was used to create this object, then this * method simply calls its deprecated equivalent, if the modeName is * either "rendered" or "renderable". * * @throws IllegalArgumentException if any of the input parameters are null. * * @since JAI 1.1 * * @see #validateArguments * @see ParameterListDescriptorImpl#isParameterValueValid */ protected boolean validateParameters(String modeName, ParameterBlock args, StringBuffer msg) { if (modeName == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); if (deprecated) { if (modeName.equalsIgnoreCase("rendered") || modeName.equalsIgnoreCase("renderable")) return validateParameters(args, msg); } return validateParameters( getParameterListDescriptor(modeName), args, msg); } /** * Returns true if this operation/mode is capable of * handling the input source(s) and/or parameter(s) * specified in the ParameterBlock, or * false otherwise, in which case an explanatory * message may be appended to the StringBuffer. * *

      This method is the standard place where input arguments are * validated against this operation's specification for the specified * mode. It is called by JAI.create() as a part of its * validation process. Thus it is strongly recommended that the * application programs use the JAI.create() methods to * instantiate all the rendered operations. * *

      The default implementation of this method makes sure that * this operator supports the specified mode and then calls * validateSources and validateParameters. * *

      This method sets all the undefined parameters in the * ParameterBlock to their default values, if the default * values are specified. * *

      Note that DeferredData parameters will not be * recognized as valid unless the parameter is defined to have class * DeferredData.class. * * @param modeName the operation mode name * @param args Input arguments, including source(s) and/or parameter(s). * @param msg A string that may contain error messages. * * @throws IllegalArgumentException if modeName is null * * @since JAI 1.1 * * @see #validateSources * @see #validateParameters */ public boolean validateArguments(String modeName, ParameterBlock args, StringBuffer msg) { return (isModeSupported(modeName) && validateSources(modeName, args, msg) && validateParameters(modeName, args, msg)); } /** * Returns true if the operation should be computed * immediately for all supported modes of this operation during * the call to JAI.create(); that is, the operation * is placed in immediate mode. If true, and * the computation fails, null will be returned * from JAI.create(). If false, * JAI.create() will return an instance of the * appropriate destination class that may be asked to compute itself * at a later time; this computation may fail at that time. * *

      Operations that rely on an external resource, such as * a source file, or that produce externally-visible side * effects, such as writing to an output file, should return * true from this method. Operations that rely * only on their sources and parameters usually wish to return * false in order to defer rendering as long as * possible. * *

      The default implementation in this class returns * false so that deferred execution is invoked. * Operations that wish to be placed in the immediate mode must * override this implementation. */ public boolean isImmediate() { return false; } /** * Calculates the region over which two distinct renderings * of an operation may be expected to differ. * *

      The class of the returned object will vary as a function of * the mode of the operation. For rendered and renderable two- * dimensional images this will be an instance of a class which * implements java.awt.Shape. * * @param modeName The name of the mode. * @param oldParamBlock The previous sources and parameters. * @param oldHints The previous hints. * @param newParamBlock The current sources and parameters. * @param newHints The current hints. * @param node The affected node in the processing chain. * * @return null to indicate that there is no * common region of validity. * * @throws IllegalArgumentException if modeName * is null or if the operation requires either * sources or parameters and either oldParamBlock * or newParamBlock is null. * @throws IllegalArgumentException if oldParamBlock or * newParamBlock do not contain sufficient sources * or parameters for the operation in question. * * @since JAI 1.1 */ public Object getInvalidRegion(String modeName, ParameterBlock oldParamBlock, RenderingHints oldHints, ParameterBlock newParamBlock, RenderingHints newHints, OperationNode node) { if (modeName == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); return null; } /** * Get the default source class for the supported mode. * * "rendered" - java.awt.image.RenderedImage.class * "renderable" - java.awt.image.renderable.RenderableImage.class * "collection" - java.util.Collection.class * "renderableCollection" - java.util.Collection.class * * @since JAI 1.1 */ protected static Class getDefaultSourceClass(String modeName) { if ("rendered".equalsIgnoreCase(modeName)) return java.awt.image.RenderedImage.class; if ("renderable".equalsIgnoreCase(modeName)) return java.awt.image.renderable.RenderableImage.class; if ("collection".equalsIgnoreCase(modeName)) return java.util.Collection.class; if ("renderableCollection".equalsIgnoreCase(modeName)) return java.util.Collection.class; return null; } /** * Create a list of per mode source classes for each supported mode * which can then be passed on to the constructor. Uses * getDefaultSourceClass(modeName) to construct this * list. * * @since JAI 1.1 */ protected static Class[][] makeDefaultSourceClassList( String[] supportedModes, int numSources) { if ((supportedModes == null) || (supportedModes.length == 0)) return null; int count = supportedModes.length; Class classes[][] = new Class[count][numSources]; for (int i = 0; i < count; i++) { Class sourceClass = getDefaultSourceClass(supportedModes[i]); for (int j = 0; j < numSources; j++) classes[i][j] = sourceClass; } return classes; } /********************** DEPRECATED Constructors ********************/ /** * Create a list of supported modes using the deprecated is*Supported * methods. */ private String[] makeSupportedModeList() { int count = 0; if (isRenderedSupported()) count++; if (isRenderableSupported()) count++; String modes[] = new String[count]; count = 0; if (isRenderedSupported()) modes[count++] = "rendered"; if (isRenderableSupported()) modes[count++] = "renderable"; return modes; } /** * Create a list of per mode source classes for use by deprecated * constructors. */ private Class[][] makeSourceClassList(Class[] sourceClasses, Class[] renderableSourceClasses) { int count = 0; if (isRenderedSupported()) count++; if (isRenderableSupported()) count++; Class classes[][] = new Class[count][]; count = 0; if (isRenderedSupported()) classes[count++] = sourceClasses; if (isRenderableSupported()) classes[count++] = renderableSourceClasses; return classes; } /** * Create a list of valid parameter values, one for each param. */ private Object[] makeValidParamValueList(Class[] paramClasses) { if (paramClasses == null) return null; int numParams = paramClasses.length; Object validValues[] = null; for (int i = 0; i < numParams; i++) { Number min = getParamMinValue(i); Number max = getParamMaxValue(i); if ((min == null) && (max == null)) continue; if (validValues == null) validValues = new Object[numParams]; validValues[i] = new Range( min.getClass(), (Comparable)min, (Comparable)max); } return validValues; } /** * Constructor. * * @param resources The resource tags and their corresponding data. * @param sourceClasses The source types required by this operation * in the rendered mode. It may be null * if this operation does not support the rendered mode, or * if it has no sources. * @param renderableSourceClasses The source types required by this * operation in the renderable mode. It may be * null if this operation does not support the * renderable mode, or if it has no sources. * @param paramClasses The parameter types required by this operation. * It may be null if this operation has no * parameters. * @param paramNames The localized parameter names. It may be * null if this operation has no parameters. * @param paramDefaults The parameter default values. It may be * null if this operation has no parameters, * or none of the parameters has a default value. * * @throws IllegalArgumentException if resources is * null. * @throws IllegalArgumentException if this operation supports the * rendered mode, and it has sources, and * sourceClasses is null. * @throws IllegalArgumentException if this operation supports the * renderable mode, and it has sources, and * renderableSourceClasses is null. * @throws IllegalArgumentException if sourceClasses * and renderableSourceClasses (if both are not * null) do not have the same number of elements. * @throws IllegalArgumentException if this operation has parameters and * paramClasses or paramNames is * null. * @throws IllegalArgumentException if this operation has parameters * and paramClasses, paramNames, * and paramDefaults (if all are not * null) do not all have the same number of elements. * * @deprecated as of JAI 1.1 in favor of * constructors where supported modes are explicitly specified. * Uses isRenderedSupported() and * isRenderableSupported() to figure out the supported modes. * * @see OperationDescriptorImpl#OperationDescriptorImpl(String[][], * String[], String[], Class [][], String[], Class [], Object[], Object[]) */ public OperationDescriptorImpl(String[][] resources, Class[] sourceClasses, Class[] renderableSourceClasses, Class[] paramClasses, String[] paramNames, Object[] paramDefaults) { this.deprecated = true; String[] supportedModes = makeSupportedModeList(); Class[][] sourceClassList = makeSourceClassList(sourceClasses, renderableSourceClasses); String[] sourceNames = checkSources(resources, supportedModes, null, sourceClassList); Object[] validParamValues = makeValidParamValueList(paramClasses); this.resources = resources; this.supportedModes = supportedModes; this.sourceNames = sourceNames; this.sourceClasses = sourceClassList; this.paramNames = paramNames; this.modeIndices = new CaselessStringArrayTable(supportedModes); this.sourceIndices = new CaselessStringArrayTable(sourceNames); // Create one ParameterListDescriptor and use the same for each mode. ParameterListDescriptor pld = new ParameterListDescriptorImpl( this, paramNames, paramClasses, paramDefaults, validParamValues); paramListDescriptors = new ParameterListDescriptor[supportedModes.length]; for (int i = 0; i < supportedModes.length; i++) { paramListDescriptors[i] = pld; } } /** * Constructor for operations that support either the rendered * or the renderable or both modes. The class type for all the * source(s) of the rendered mode (if supported) is set to * java.awt.image.RenderedImage.class. * The class type for all the source(s) of the renderable mode (if * supported) is set to * java.awt.image.renderable.RenderableImage. * * @param resources The resource tags and their corresponding data. * @param numSources The number of sources required by this operation. * It should not be negative. A negative value indicates this * operation has no sources. * @param paramClasses The parameter types required by this operation. * It may be null if this operation has no * parameters. * @param paramNames The localized parameter names. It may be * null if this operation has no parameters. * @param paramDefaults The parameter default values. It may be * null if this operation has no parameters, * or none of the parameters has a default value. * * @throws IllegalArgumentException if resources is * null. * @throws IllegalArgumentException if this operation has parameters and * paramClasses or paramNames is * null. * @throws IllegalArgumentException if this operation has parameters * and paramClasses, paramNames, * and paramDefaults (if not null) * do not all have the same number of elements. * * @deprecated as of JAI 1.1 in favor of * constructors where supported modes are explicitly specified. * Uses isRenderedSupported() and * isRenderableSupported() to figure out the supported modes. * * @see OperationDescriptorImpl#OperationDescriptorImpl(String[][], * String[], int, String[], Class [], Object[], Object[]) */ public OperationDescriptorImpl(String[][] resources, int numSources, Class[] paramClasses, String[] paramNames, Object[] paramDefaults) { this.deprecated = true; String[] supportedModes = makeSupportedModeList(); Class[][] sourceClassList = makeDefaultSourceClassList(supportedModes, numSources); String[] sourceNames = checkSources(resources, supportedModes, null, sourceClassList); Object[] validParamValues = makeValidParamValueList(paramClasses); this.resources = resources; this.supportedModes = supportedModes; this.sourceNames = sourceNames; this.sourceClasses = sourceClassList; this.paramNames = paramNames; this.modeIndices = new CaselessStringArrayTable(supportedModes); this.sourceIndices = new CaselessStringArrayTable(sourceNames); // Create one ParameterListDescriptor and use the same for each mode. ParameterListDescriptor pld = new ParameterListDescriptorImpl( this, paramNames, paramClasses, paramDefaults, validParamValues); paramListDescriptors = new ParameterListDescriptor[supportedModes.length]; for (int i = 0; i < supportedModes.length; i++) { paramListDescriptors[i] = pld; } } /** * Constructor for operations that supports only the rendered mode * and requires no parameters. * * @param resources The resource tags and their corresponding data. * @param sourceClasses The source types required by this operation * in the rendered mode. It may be null * if this operation has no sources. * * @throws IllegalArgumentException if resources is * null. * * @deprecated as of JAI 1.1 in favor of constructors where the mode * specfic information is explicitly specified. * * @see OperationDescriptorImpl#OperationDescriptorImpl(String[][], * String[], String[], Class [][], String[], Class [], Object[], Object[]) */ public OperationDescriptorImpl(String[][] resources, Class[] sourceClasses) { this(resources, sourceClasses, null, null, null, null); } /** * Constructor for operations that supports either the rendered * or the renderable or both modes and requires no parameters. * * @param resources The resource tags and their corresponding data. * @param sourceClasses The source types required by this operation * in the rendered mode. It may be null * if this operation does not support the rendered mode, or * if it has no sources. * @param renderableSourceClasses The source types required by this * operation in the renderable mode. It may be * null if this operation does not support the * renderable mode, or if it has no sources. * * @throws IllegalArgumentException if resources is * null. * @throws IllegalArgumentException if this operation supports the * rendered mode, and it has sources, and * sourceClasses is null. * @throws IllegalArgumentException if this operation supports the * renderable mode, and it has sources, and * renderableSourceClasses is null. * @throws IllegalArgumentException if sourceClasses * and renderableSourceClasses (if both are not * null) do not have the same number of elements. * * @deprecated as of JAI 1.1 in favor of constructors where the mode * specfic information is explicitly specified. * * @see OperationDescriptorImpl#OperationDescriptorImpl(String[][], * String[], String[], Class [][], String[], Class [], Object[], Object[]) */ public OperationDescriptorImpl(String[][] resources, Class[] sourceClasses, Class[] renderableSourceClasses) { this(resources, sourceClasses, renderableSourceClasses, null, null, null); } /** * Constructor for operations that supports either the rendered * or the renderable or both modes and requires no sources. * * @throws IllegalArgumentException if resources is * null. * @throws IllegalArgumentException if this operation has parameters and * paramClasses or paramNames is * null. * @throws IllegalArgumentException if this operation has parameters * and paramClasses, paramNames, * and paramDefaults (if not null) * do not all have the same number of elements. * * @deprecated as of JAI 1.1 in favor of constructors where the mode * specfic information is explicitly specified. * * @see OperationDescriptorImpl#OperationDescriptorImpl(String[][], * String[], int, String[], Class [], Object[], Object[]) */ public OperationDescriptorImpl(String[][] resources, Class[] paramClasses, String[] paramNames, Object[] paramDefaults) { this(resources, null, null, paramClasses, paramNames, paramDefaults); } /** * Constructor for operations that support the rendered mode and * possibly the renderable mode and require no parameters. The * class type for all the source(s) of the rendered mode is set to * java.awt.image.RenderedImage.class. * The class type for all the source(s) of the renderable mode (if * supported) is set to * java.awt.image.renderable.RenderableImage. * * @param resources The resource tags and their corresponding data. * @param numSources The number of sources required by this operation. * It should not be negative. A negative value indicates this * operation has no sources. * * @throws IllegalArgumentException if resources is * null. * * @deprecated as of JAI 1.1 in favor of constructors where the mode * specfic information is explicitly specified. * * @see OperationDescriptorImpl#OperationDescriptorImpl(String[][], * String[], int, String[], Class [], Object[], Object[]) */ public OperationDescriptorImpl(String[][] resources, int numSources) { this(resources, numSources, null, null, null); } /********************** DEPRECATED METHODS *************************/ /** * Returns an array of PropertyGenerators implementing * the property inheritance for this operation. The default * implementation returns null, indicating that source * properties are simply copied. Subclasses should override * this method if they wish to produce inherited properties. * * @deprecated as of JAI 1.1 in favor of the equivalent method * that specifies the mode name. * * @see #getPropertyGenerators */ public PropertyGenerator[] getPropertyGenerators() { return deprecated ? null : getPropertyGenerators("rendered"); } /** * Returns true if this operation supports the * rendered mode. The default implementation in this * class returns true. * * @deprecated as of JAI 1.1 in favor of isModeSupported("rendered") * * @see #isModeSupported */ public boolean isRenderedSupported() { return deprecated ? true : isModeSupported("rendered"); } /** * Returns the source class types of this operation for the rendered * mode. If this operation has no sources, this method returns null. * * @deprecated as of JAI 1.1 in favor of getSourceClasses("rendered") * * @throws IllegalArgumentException if the rendered mode * is not supported. * * @see #getSourceClasses */ public Class[] getSourceClasses() { return getSourceClasses("rendered"); } /** * Returns the destination class type of this operation for the * rendered mode. The default implementation in this class returns * java.awt.image.RenderedImage.class if this operation * supports the rendered mode, or null otherwise. * * @deprecated as of JAI 1.1 in favor of getDestClass("rendered") * * @see #getDestClass */ public Class getDestClass() { if (deprecated) { return (isRenderedSupported() ? java.awt.image.RenderedImage.class : null); } else { return getDestClass("rendered"); } } /** * Returns true if this operation supports the rendered * mode, and is capable of handling the input arguments for the * rendered mode. The default implementation validates both the * source(s) and the parameter(s). * *

      Note that DeferredData parameters will not be * recognized as valid unless the parameter is defined to have class * DeferredData.class. * *

      Additional validations should be added by each individual * operation based on its specification. * * @throws IllegalArgumentException if args is null. * @throws IllegalArgumentException if msg is null * and the validation fails. * * @deprecated as of JAI 1.1 in favor of validateArguments("rendered", ...) * * @see #validateArguments */ public boolean validateArguments(ParameterBlock args, StringBuffer msg) { if (deprecated) { return (validateSources(args, msg) && validateParameters(args, msg)); } else { return validateArguments("rendered", args, msg); } } /********************* Renderable Mode Methods (deprecated) ********/ /** * Returns true if this operation supports the * renderable mode. The default implementation in this * class returns false. Operations that support * the renderable mode must override this implementation. * * @deprecated as of JAI 1.1 in favor of isModeSupported("renderable") * * @see #isModeSupported */ public boolean isRenderableSupported() { return deprecated ? false : isModeSupported("renderable"); } /** * Returns the source class types of this operation for the * renderable mode. If this operation has no sources this method * returns null. * * @deprecated as of JAI 1.1 in favor of getSourceClasses("renderable") * * @throws IllegalArgumentException if the renderable mode * is not supported. * * @see #getSourceClasses */ public Class[] getRenderableSourceClasses() { return getSourceClasses("renderable"); } /** * Returns the destination class type of this operation for the * renderable mode. The default implementation in this class returns * java.awt.image.renderable.RenderableImage.class if * this operation supports the renderable mode, or null * otherwise. * * @deprecated as of JAI 1.1 in favor of getDestClass("renderable") * * @see #getDestClass */ public Class getRenderableDestClass() { if (deprecated) { return (isRenderableSupported() ? java.awt.image.renderable.RenderableImage.class : null); } else { return getDestClass("renderable"); } } /** * Returns true if this operation supports the * renderable mode, and is capable of handling the input * arguments for the renderable mode. The default implementation * validates both the source(s) and the parameter(s). * *

      If this operation does not support the renderable mode, * this method returns false regardless of the input * arguments. * *

      Note that DeferredData parameters will not be * recognized as valid unless the parameter is defined to have class * DeferredData.class. * *

      Additional validations should be added by each individual * operation based on its specification. * * @throws IllegalArgumentException if args is null. * @throws IllegalArgumentException if msg is null * and the validation fails. * * @deprecated as of JAI 1.1 in favor of validateArguments("renderable", ...) * * @see #validateArguments */ public boolean validateRenderableArguments(ParameterBlock args, StringBuffer msg) { if (deprecated) { return (validateRenderableSources(args, msg) && validateParameters(args, msg)); } else { return validateArguments("renderable", args, msg); } } /************************ Parameter Methods (deprecated) ***********/ /** * The ParameterListDescriptor for the first supported mode. Used * by deprecated methods where modeName is not specified. */ private ParameterListDescriptor getDefaultPLD() { return getParameterListDescriptor(getSupportedModes()[0]); } /** * Returns the number of parameters (not including sources) * required by this operation. * * @deprecated as of JAI 1.1 in favor of * getParameterListDescriptor(modeName).getNumParameters() * This will for the time being return the above value for * modeName = getSupportedModes()[0] * * @see ParameterListDescriptor#getNumParameters */ public int getNumParameters() { return getDefaultPLD().getNumParameters(); } /** * Returns the parameter class types of this operation. * If this operation has no parameters, this method returns * null. * * @deprecated as of JAI 1.1 in favor of * getParameterListDescriptor(modeName).getParamClasses() * This will for the time being return the above value for * modeName = getSupportedModes()[0] * * @see ParameterListDescriptor#getParamClasses */ public Class[] getParamClasses() { return getDefaultPLD().getParamClasses(); } /** * Returns the localized parameter names of this operation. * If this operation has no parameters, this method returns * null. * * @deprecated as of JAI 1.1 in favor of * getParameterListDescriptor(modeName).getParamNames() * This will for the time being return the above value for * modeName = getSupportedModes()[0] * * @see ParameterListDescriptor#getParamNames */ public String[] getParamNames() { return getDefaultPLD().getParamNames(); } /** * Returns the default values of the parameters for this operation. * If this operation has no parameters, this method returns * null. If a parameter does not have a default value, * the constant * OperationDescriptor.NO_PARAMETER_DEFAULT will be * returned. The validateArguments() and * validateRenderableArguments method will return * false if an input parameter without a default value * is supplied as null, or if an unspecified tailing * parameter does not have a default value. * * @deprecated as of JAI 1.1 in favor of * getParameterListDescriptor(modeName).getParamDefaults() * This will for the time being return the above value for * modeName = getSupportedModes()[0] * * @see ParameterListDescriptor#getParamDefaults */ public Object[] getParamDefaults() { return getDefaultPLD().getParamDefaults(); } /** * Returns the default value of specified parameter. The default * value may be null. If a parameter has no default * value, this method returns * OperationDescriptor.NO_PARAMETER_DEFAULT. * * @param index The index of the parameter whose default * value is queried. * * @throws IllegalArgumentException if this operation has no parameters. * @throws ArrayIndexOutOfBoundsException if there is no parameter * corresponding to the specified index. * * @deprecated as of JAI 1.1 in favor of * getParameterListDescriptor(modeName).getParamDefaultValue(...) * This will for the time being return the above value for * modeName = getSupportedModes()[0] * * @see ParameterListDescriptor#getParamDefaultValue */ public Object getParamDefaultValue(int index) { return getDefaultPLD().getParamDefaultValue(paramNames[index]); } /** * Returns the minimum legal value of a specified numeric parameter * for this operation. If the specified parameter is non-numeric, * this method returns null. * *

      The return value should be of the class types corresponding to * the parameter's primitive type, that is, Byte for a * byte parameter, Integer for an * int parameter, and so forth. * *

      The default implementation returns the minimum value * in the parameter data type's full range. * * @param index The index of the parameter to be queried. * * @return A Number representing the minimum legal value, * or null if the specified parameter is not * numeric. * * @throws IllegalArgumentException if this operation has no parameters. * @throws ArrayIndexOutOfBoundsException if there is no parameter * corresponding to the specified index. * * @deprecated as of JAI 1.1 in favor of * getParameterListDescriptor(modeName).getParamValueRange() * This will for the time being use getSupportedModes()[0] * for modeName. * *

      If the parameter is not a sub-class of the Number * class then this method returns null. * *

      Else if the above getParamValueRange() returns a non-null * Range then it returns the getMinValue() * of that Range. * *

      Else for Float and Double parameters * it returns the corresponding -MAX_VALUE and * MIN_VALUE for other Number classes. * * @see ParameterListDescriptor#getParamValueRange * @see ParameterListDescriptor#getEnumeratedParameterValues * @see ParameterListDescriptor#isParameterValueValid */ public Number getParamMinValue(int index) { return null; } /** * Returns the maximum legal value of a specified numeric parameter * for this operation. If the specified parameter is non-numeric, * this method returns null. * *

      The return value should be of the class type corresponding to * the parameter's primitive type, that is, Byte for a * byte parameter, Integer for an * int parameter, and so forth. * *

      The default implementation returns the maximum value * in the parameter data type's full range. * * @param index The index of the parameter to be queried. * * @return A Number representing the maximum legal value, * or null if the specified parameter is not * numeric. * * @throws IllegalArgumentException if this operation has no parameters. * @throws ArrayIndexOutOfBoundsException if there is no parameter * corresponding to the specified index. * * @deprecated as of JAI 1.1 in favor of * getParameterListDescriptor(modeName).getParamValueRange() * This will for the time being use getSupportedModes()[0] * for modeName. * *

      If the parameter is not a sub-class of the Number * class then this method returns null. * *

      Else if the above getParamValueRange() returns a non-null * Range then it returns the getMaxValue() * of that Range. * *

      Else returns the MAX_VALUE of the corresponding * Number class. * * @see ParameterListDescriptor#getParamValueRange * @see ParameterListDescriptor#getEnumeratedParameterValues * @see ParameterListDescriptor#isParameterValueValid */ public Number getParamMaxValue(int index) { return null; } /** * Returns true if this operation supports the rendered * mode, and is capable of handling the given input source(s) for the * rendered mode. The default implementation validates the number of * sources, the class type of each source, and null sources. Subclasses * should override this implementation if their requirement on the * sources are different from the default. * * @throws IllegalArgumentException if args is null. * @throws IllegalArgumentException if msg is null * and the validation fails. * * @deprecated as of JAI 1.1 in favor of * validateSources("rendered", ...) * * @see #validateSources */ protected boolean validateSources(ParameterBlock args, StringBuffer msg) { if (deprecated) { return (isRenderedSupported() && validateSources(getSourceClasses(), args, msg)); } else { return validateSources("rendered", args, msg); } } /** * Returns true if this operation supports the * renderable mode, and is capable of handling the given input * source(s) for the renderable mode. The default * implementation validates the number of sources, the class type of * each source, and null sources. Subclasses should override this * implementation if their requirement on the sources are * different from the default. * * @throws IllegalArgumentException if args is null. * @throws IllegalArgumentException if msg is null * and the validation fails. * * @deprecated as of JAI 1.1 in favor of * validateSources("renderable", ...) * * @see #validateSources */ protected boolean validateRenderableSources(ParameterBlock args, StringBuffer msg) { if (deprecated) { return (isRenderableSupported() && validateSources(getRenderableSourceClasses(), args, msg)); } else { return validateSources("renderable", args, msg); } } /** * Returns true if this operation is capable of handling * the given input parameters. The default implementation validates the * number of parameters, the class type of each parameter, and null * parameters. For those non-null numeric parameters, it also checks to * see if the parameter value is within the minimum and maximum range. * Subclasses should override this implementation if their requirements * for the parameter objects are different from the default. * *

      JAI allows unspecified tailing parameters if these parameters * have default values. This method automatically sets these unspecified * parameters to their default values. However, if a parameter, which * has a default value, is followed by one or more parameters that * have no default values, this parameter must be specified in the * ParameterBlock; else this method returns * false. * *

      Note that DeferredData parameters will not be * recognized as valid unless the parameter is defined to have class * DeferredData.class. * * @throws IllegalArgumentException if args is null. * @throws IllegalArgumentException if msg is null * and the validation fails. * * @deprecated as of JAI 1.1 in favor of * validateParameters(getSupportedModes()[0], ...) * * @see #validateParameters */ protected boolean validateParameters(ParameterBlock args, StringBuffer msg) { return validateParameters(getDefaultPLD(), args, msg); } /** * Returns the minimum number of parameters must be supplied in * the ParameterBlock. */ private int getMinNumParameters(ParameterListDescriptor pld) { // The number of parameters this operation should have. int numParams = pld.getNumParameters(); Object paramDefaults[] = pld.getParamDefaults(); for (int i = numParams - 1; i >= 0; i--) { if (paramDefaults[i] == ParameterListDescriptor.NO_PARAMETER_DEFAULT) { break; } else { numParams--; } } return numParams; } private boolean validateSources(Class[] sources, ParameterBlock args, StringBuffer msg) { if ((args == null) || (msg == null)) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); // The number of sources this operation requires. int numSources = getNumSources(); // Check for the correct number of sources. if (args.getNumSources() < numSources) { msg.append(JaiI18N.formatMsg("OperationDescriptorImpl6", new Object[] { getName(), new Integer(numSources) })); return false; } for (int i = 0; i < numSources; i++) { Object s = args.getSource(i); // Check for null source. if (s == null) { msg.append(JaiI18N.formatMsg("OperationDescriptorImpl7", new Object[] { getName()})); return false; } // Check for the correct class of each supplied source. Class c = sources[i]; if (!c.isInstance(s)) { msg.append(JaiI18N.formatMsg("OperationDescriptorImpl8", new Object[] { getName(), new Integer(i), new String(c.toString()), new String(s.getClass().toString()) })); return false; } } return true; } private boolean validateParameters(ParameterListDescriptor pld, ParameterBlock args, StringBuffer msg) { if ((args == null) || (msg == null)) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); // The number of parameters this operation should have. int numParams = pld.getNumParameters(); // The number of parameters supplied. int argNumParams = args.getNumParameters(); Object paramDefaults[] = pld.getParamDefaults(); // Check for the correct number of parameters. if (argNumParams < numParams) { // The minimum number of parameters this operation must have. if (argNumParams < getMinNumParameters(pld)) { msg.append(JaiI18N.formatMsg("OperationDescriptorImpl9", new Object[] { getName(), new Integer(numParams)})); return false; } else { // use default values for (int i = argNumParams; i < numParams; i++) { args.add(paramDefaults[i]); } } } for (int i = 0; i < numParams; i++) { Object p = args.getObjectParameter(i); /* Check for null parameter. */ if (p == null) { p = paramDefaults[i]; // get the default parameter value if (p == OperationDescriptor.NO_PARAMETER_DEFAULT) { msg.append( JaiI18N.formatMsg("OperationDescriptorImpl11", new Object[] { getName(), new Integer(i) })); return false; } else { args.set(p, i); // replace null parameter with default } } // Now check if the parameter value is valid try { if (!pld.isParameterValueValid(paramNames[i], p)) { msg.append( JaiI18N.formatMsg("OperationDescriptorImpl10", new Object[] { getName(), pld.getParamNames()[i] })); return false; } } catch (IllegalArgumentException e) { msg.append(getName() + " - " + e.getLocalizedMessage()); return false; } } return true; } /** * Make sure that modeName is not null and * is one of the supported modes. */ private void checkModeName(String modeName) { if (modeName == null) throw new IllegalArgumentException( JaiI18N.getString("OperationDescriptorImpl12")); if (modeIndices.contains(modeName) == false) { throw new IllegalArgumentException( JaiI18N.formatMsg("OperationDescriptorImpl13", new Object[] { getName(), modeName })); } } } jai-core-1.1.4/src/share/classes/javax/media/jai/tilecodec/0000755000175000017500000000000011633360403023273 5ustar mathieumathieujai-core-1.1.4/src/share/classes/javax/media/jai/tilecodec/TileCodecDescriptorImpl.java0000644000175000017500000001316110203035544030651 0ustar mathieumathieu/* * $RCSfile: TileCodecDescriptorImpl.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:55 $ * $State: Exp $ */package javax.media.jai.tilecodec; import javax.media.jai.PropertyGenerator; /** * An abstract class that implements the TileCodecDescriptor * interface and is suitable for subclassing. This class provides default * implementations for some of the methods from * TileCodecDescriptor. Subclasses should override these methods * if they do not wish to retain the default implementation. * * All Strings are treated in a case-retentive and * case-insensitive manner. * * @since JAI 1.1 */ public abstract class TileCodecDescriptorImpl implements TileCodecDescriptor { private String formatName; private boolean includesSMInfo, includesLocationInfo; /** * Creates a TileCodecDescriptorImpl with the given * format name and booleans to specify whether layout * information is included in the encoded stream. * * @param formatName The name of the format. This is also * the name under which this descriptor * will be registered under in the * OperationRegistry. * @param includesSampleModelInfo Whether the format encodes the tile's * SampleModel or equivalent * information into the encoded stream. * @param includesLocationInfo Whether the format encodes the tile's * upper left corner position or equivalent * information into the encoded stream. * @throws IllegalArgumentException if formatName is null. */ public TileCodecDescriptorImpl(String formatName, boolean includesSampleModelInfo, boolean includesLocationInfo) { // Cause IllegalArgumentException if formatName is null if (formatName == null) { throw new IllegalArgumentException( JaiI18N.getString("TileCodecDescriptorImpl0")); } this.formatName = formatName; this.includesSMInfo = includesSampleModelInfo; this.includesLocationInfo = includesLocationInfo; } /** * Returns the name of the format. */ public String getName() { return formatName; } /** * Returns the registry modes supported by this descriptor. The * default implementation of this method in this class returns a * String array containing the "tileDecoder" and * "tileEncoder" strings. If the subclass does not support any of * these modes, it should override this method to return the names of * those modes that it supports. * * @see javax.media.jai.RegistryMode */ public String[] getSupportedModes() { return new String[] {"tileDecoder", "tileEncoder"}; } /** * This method is implemented to return true if the specified * registryModeName is either "tileDecoder" or "tileEncoder". If * the subclass doesn't support any one of these modes, it should * override this method to return true only for the supported mode(s). * * @param registryModeName The name of the registry mode to check * support for. * @throws IllegalArgumentException if registryModeName is null. */ public boolean isModeSupported(String registryModeName) { if (registryModeName == null) { throw new IllegalArgumentException( JaiI18N.getString("TileCodecDescriptorImpl1")); } if (registryModeName.equalsIgnoreCase("tileDecoder") == true || registryModeName.equalsIgnoreCase("tileEncoder") == true) { return true; } return false; } /** * Whether this descriptor supports properties. * * @return true, if the implementation of this descriptor supports * properties. false otherwise. Since tile codecs do not support * properties, so this default implementation returns false. * * @see PropertyGenerator */ public boolean arePropertiesSupported() { return false ; } /** * Returns an array of PropertyGenerators implementing * the property inheritance for this operation. Since neither * TileEncoder or TileDecoder supports * properties, the default implementation throws an * UnsupportedOperationException. Subclasses should * override this method if they wish to produce inherited properties. * * @throws IllegalArgumentException if modeName is null. * @throws UnsupportedOperationException if * arePropertiesSupported() returns false */ public PropertyGenerator[] getPropertyGenerators(String modeName) { if (modeName == null) { throw new IllegalArgumentException( JaiI18N.getString("TileCodecDescriptorImpl1")); } throw new UnsupportedOperationException( JaiI18N.getString("TileCodecDescriptorImpl2")); } /** * Returns true if the format encodes layout information generally * specified via the SampleModel in the encoded data stream. */ public boolean includesSampleModelInfo() { return includesSMInfo; } /** * Returns true if the format encodes in the data stream the location of * the Raster with respect to its enclosing image. */ public boolean includesLocationInfo() { return includesLocationInfo; } } jai-core-1.1.4/src/share/classes/javax/media/jai/tilecodec/TileDecoderImpl.java0000644000175000017500000001542510203035544027147 0ustar mathieumathieu/* * $RCSfile: TileDecoderImpl.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:56 $ * $State: Exp $ */package javax.media.jai.tilecodec; import java.awt.Point; import java.awt.image.Raster; import java.awt.image.SampleModel; import java.io.InputStream; import java.io.IOException; import javax.media.jai.JAI; import javax.media.jai.ParameterListDescriptor; import javax.media.jai.tilecodec.TileCodecDescriptor; import com.sun.media.jai.tilecodec.TileCodecUtils; /** * A partial implementation of the TileDecoder interface * useful for subclassing. * * @since JAI 1.1 */ public abstract class TileDecoderImpl implements TileDecoder { /** * The name of the format. */ protected String formatName; /** * The InputStream containing the encoded data to decode. */ protected InputStream inputStream; /** * The TileCodecParameterList object containing the * decoding parameters. */ protected TileCodecParameterList paramList; /** * Constructs a TileDecoderImpl. An * IllegalArgumentException will be thrown if * param's getParameterListDescriptor() * method does not return the same descriptor as that from * the associated TileCodecDescriptor's * getParameterListDescriptor method for the "tileDecoder" * registry mode. * *

      If param is null, then the default parameter list for decoding * as defined by the associated TileCodecDescriptor's * getDefaultParameters() method will be used for decoding. * If this too is null, an IllegalArgumentException will * be thrown if the ParameterListDescriptor associated * with the associated TileCodecDescriptor for the * "tileDecoder" registry mode, reports that the number of parameters * for this format is non-zero. * * @param formatName The name of the format. * @param input The InputStream to decode data from. * @param param The object containing the tile decoding parameters. * * @throws IllegalArgumentException if formatName is null. * @throws IllegalArgumentException if input is null. * @throws IllegalArgumentException if param's getFormatName() method does * not return the same formatName as the one specified to this method. * @throws IllegalArgumentException if the ParameterListDescriptor * associated with the param and the associated TileCodecDescriptor are * not equal. * @throws IllegalArgumentException if param does not have "tileDecoder" * as one of the valid modes that it supports. * @throws IllegalArgumentException if the associated TileCodecDescriptor's * includesSampleModelInfo() returns false and a non-null value for the * "sampleModel" parameter is not supplied in the supplied parameter list. */ public TileDecoderImpl(String formatName, InputStream input, TileCodecParameterList param) { // Cause IllegalArgumentException to be thrown if formatName, // input is null if (formatName == null) { throw new IllegalArgumentException( JaiI18N.getString("TileCodecDescriptorImpl0")); } if (input == null) { throw new IllegalArgumentException( JaiI18N.getString("TileDecoderImpl0")); } TileCodecDescriptor tcd = TileCodecUtils.getTileCodecDescriptor("tileDecoder", formatName); // If param is null, get the default parameter list. if (param == null) param = tcd.getDefaultParameters("tileDecoder"); if (param != null) { // Check whether the formatName from the param is the same as the // one supplied to this method. if (param.getFormatName().equalsIgnoreCase(formatName) == false) { throw new IllegalArgumentException( JaiI18N.getString("TileDecoderImpl1")); } // Check whether the supplied parameterList supports the // "tileDecoder" mode. if (param.isValidForMode("tileDecoder") == false) { throw new IllegalArgumentException( JaiI18N.getString("TileDecoderImpl2")); } // Check whether the ParameterListDescriptors are the same. if (param.getParameterListDescriptor().equals( tcd.getParameterListDescriptor("tileDecoder")) == false) throw new IllegalArgumentException(JaiI18N.getString("TileCodec0")); SampleModel sm = null; // Check whether a non-null samplemodel value is needed if (tcd.includesSampleModelInfo() == false) { try { sm = (SampleModel)param.getObjectParameter("sampleModel"); } catch (IllegalArgumentException iae) { // There is no parameter named sampleModel defined on the // supplied parameter list throw new IllegalArgumentException( JaiI18N.getString("TileDecoderImpl3")); } if (sm == null || sm == ParameterListDescriptor.NO_PARAMETER_DEFAULT) { if (tcd.getParameterListDescriptor("tileDecoder"). getParamDefaultValue("sampleModel") == null) { // If a non-null value was not set on the parameter list // and wasn't available thru the descriptor either throw new IllegalArgumentException( JaiI18N.getString("TileDecoderImpl4")); } } } } else { // If the supplied parameterList is null and the default one is // null too, then check whether this format supports no parameters ParameterListDescriptor pld = tcd.getParameterListDescriptor("tileDecoder"); // Check whether a non-null samplemodel value is needed if (tcd.includesSampleModelInfo() == false) { // SampleModel must be specified via the parameter list throw new IllegalArgumentException( JaiI18N.getString("TileDecoderImpl5")); } // If the PLD is not null and says that there are supposed to // be some parameters (numParameters returns non-zero value) // throw an IllegalArgumentException if (pld != null && pld.getNumParameters() != 0) { throw new IllegalArgumentException( JaiI18N.getString("TileDecoderImpl6")); } } this.formatName = formatName; this.inputStream = input; this.paramList = param; } /** * Returns the format name. */ public String getFormatName() { return formatName; } /** * Returns the current parameters as an instance of the * TileCodecParameterList interface. */ public TileCodecParameterList getDecodeParameterList() { return paramList; } /** * Returns the InputStream associated with this * TileDecoder. */ public InputStream getInputStream() { return inputStream; } } jai-core-1.1.4/src/share/classes/javax/media/jai/tilecodec/javax.media.jai.tilecodec.properties0000644000175000017500000000326510203035544032277 0ustar mathieumathieu# # $RCSfile: javax.media.jai.tilecodec.properties,v $ # # Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. # # Use is subject to license terms. # # $Revision: 1.1 $ # $Date: 2005-02-11 04:57:56 $ # $State: Exp $ # TileCodec0=ParameterListDescriptor for the supplied parameter list must be the same as the one associated with the TileCodecDescriptor for this format. TileCodec1=The supplied modeName is not valid. TileCodec2=The supplied format name of the parameter list is not the same as the descriptor. TileCodecDescriptorImpl0=formatName is null. TileCodecDescriptorImpl1=modeName is null. TileCodecDescriptorImpl2=Properties are not supported by tile codecs. TileCodecDescriptorImpl3=otherParamList is null. TileCodecParameterList0=validModes cannot be null. TileCodecParameterList1=descriptor cannot be null. TileDecoderImpl0=The supplied InputStream cannot be null. TileDecoderImpl1=The supplied parameter list must be for the same formatName as that of this TileDecoder. TileDecoderImpl2=The supplied parameter list does not support the "tileDecoder" mode. TileDecoderImpl3=There is no parameter named "sampleModel" defined on the parameter list. TileDecoderImpl4=A non-null value for the "sampleModel" parameter must be specified in the parameter list. TileDecoderImpl5=The parameter list should not be null, as samplemodel needs to be specified through it. TileDecoderImpl6=A non-null parameter list must be specified for this format. TileEncoderImpl0=The supplied OutputStream cannot be null. TileEncoderImpl1=The supplied parameter list must be for the same formatName as that of this TileEnDecoder. TileEncoderImpl2=The supplied parameter list does not support the "tileEncoder" mode. jai-core-1.1.4/src/share/classes/javax/media/jai/tilecodec/TileEncoderImpl.java0000644000175000017500000001271610203035544027161 0ustar mathieumathieu/* * $RCSfile: TileEncoderImpl.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:56 $ * $State: Exp $ */package javax.media.jai.tilecodec; import java.awt.image.Raster; import java.awt.image.SampleModel; import java.io.IOException; import java.io.OutputStream; import javax.media.jai.JAI ; import javax.media.jai.ParameterListDescriptor; import javax.media.jai.tilecodec.TileCodecDescriptor; import com.sun.media.jai.tilecodec.TileCodecUtils; /** * A partial implementation of the TileEncoder interface * useful for subclassing. * * @since JAI 1.1 */ public abstract class TileEncoderImpl implements TileEncoder { /** * The name of the format. */ protected String formatName; /** * The OutputStream to write the encoded data to. */ protected OutputStream outputStream; /** * The TileCodecParameterList object containing the * encoding parameters. */ protected TileCodecParameterList paramList; /** * Constructs an TileEncoderImpl. An * IllegalArgumentException will be thrown if * param's getParameterListDescriptor() method * does not return the same descriptor as that from the associated * TileCodecDescriptor's * getParameterListDescriptor method for the "tileEncoder" * registry mode. * *

      If param is null, then the default parameter list for encoding * as defined by the associated TileCodecDescriptor's * getDefaultParameters() method will be used for encoding. * If this too is null, an IllegalArgumentException will * be thrown if the ParameterListDescriptor associated * with the associated TileCodecDescriptor for the * "tileEncoder" registry mode, reports that the number of parameters * for this format is non-zero. * * @param formatName The name of the format. * @param output The OutputStream to write encoded data to. * @param param The object containing the tile encoding parameters. * * @throws IllegalArgumentException if formatName is null. * @throws IllegalArgumentException if output is null. * @throws IllegalArgumentException if param's getFormatName() method does * not return the same formatName as the one specified to this method. * @throws IllegalArgumentException if the ParameterListDescriptors * associated with the param and the associated TileCodecDescriptor are * not equal. * @throws IllegalArgumentException if param does not have "tileEncoder" * as one of the valid modes that it supports. */ public TileEncoderImpl(String formatName, OutputStream output, TileCodecParameterList param) { // Cause a IllegalArgumentException to be thrown if formatName, output // is null if (formatName == null) { throw new IllegalArgumentException( JaiI18N.getString("TileCodecDescriptorImpl0")); } if (output == null) { throw new IllegalArgumentException( JaiI18N.getString("TileEncoderImpl0")); } TileCodecDescriptor tcd = TileCodecUtils.getTileCodecDescriptor("tileEncoder", formatName); // If param is null, get the default parameter list. if (param == null) param = tcd.getDefaultParameters("tileEncoder"); if (param != null) { // Check whether the formatName from the param is the same as the // one supplied to this method. if (param.getFormatName().equalsIgnoreCase(formatName) == false) { throw new IllegalArgumentException( JaiI18N.getString("TileEncoderImpl1")); } // Check whether the supplied parameterList supports the // "tileDecoder" mode. if (param.isValidForMode("tileEncoder") == false) { throw new IllegalArgumentException( JaiI18N.getString("TileEncoderImpl2")); } // Check whether the ParameterListDescriptors are the same. if (param.getParameterListDescriptor().equals( tcd.getParameterListDescriptor("tileEncoder")) == false) throw new IllegalArgumentException(JaiI18N.getString("TileCodec0")); } else { // If the supplied parameterList is null and the default one is // null too, then check whether this format supports no parameters ParameterListDescriptor pld = tcd.getParameterListDescriptor("tileEncoder"); // If the PLD is not null and says that there are supposed to // be some parameters (numParameters returns non-zero value) // throw an IllegalArgumentException if (pld != null && pld.getNumParameters() != 0) { throw new IllegalArgumentException( JaiI18N.getString("TileDecoderImpl6")); } } this.formatName = formatName; this.outputStream = output; this.paramList = param; } /** * Returns the format name of the encoding scheme. */ public String getFormatName() { return formatName; } /** * Returns the current parameters as an instance of the * TileCodecParameterList interface. */ public TileCodecParameterList getEncodeParameterList() { return paramList; } /** * Returns the OutputStream to which the encoded data will * be written. */ public OutputStream getOutputStream() { return outputStream; } } jai-core-1.1.4/src/share/classes/javax/media/jai/tilecodec/TileCodecParameterList.java0000644000175000017500000001114310203035544030463 0ustar mathieumathieu/* * $RCSfile: TileCodecParameterList.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:55 $ * $State: Exp $ */package javax.media.jai.tilecodec; import javax.media.jai.ParameterListDescriptor; import javax.media.jai.ParameterListImpl; /** * A subclass of ParameterListImpl that is specific to * tile codecs. This class functions in either one or both of the two * registry modes supported by the TileCodecDescriptor * - "tileEncoder" and "tileDecoder". * *

      This class is not intended to be subclassed for each individual * TileEncoder or TileDecoder. This is a generic * class which can be used as is for representing a parameter list for * any tile encoding/decoding format. The ParameterListDescriptor * provided as argument to the constructor should be the one returned from * the getParameterListDescriptor() method of the * TileCodecDescriptor for the given format name. * *

      If the associated TileCodecDescriptor's * includesSampleModelInfo() method returns false, then for the * "tileDecoder" mode, this class will be expected to contain a parameter * named "sampleModel" with a non-null SampleModel as its value. * * @since JAI 1.1 */ public class TileCodecParameterList extends ParameterListImpl { // The name of the format private String formatName; // The modes valid for this class private String validModes[]; /** * Creates a TileCodecParameterList. The * validModes argument specifies the registry modes valid * for this TileCodecParameterList. This should contain * the "tileEncoder" registry mode or the "tileDecoder" registry * mode or both. The supplied descriptor object specifies the names * and number of the valid parameters, their Class types, * as well as the Range of valid values for each parameter. * * @param formatName The name of the format, parameters for which are * specified through this parameter list. * @param validModes An array of String objects specifying * which registry modes are valid for this parameter list. * @param descriptor The ParameterListDescriptor object that * describes all valid parameters for this format. This * must be the the same descriptor that is returned from * the getParameterListDescriptor() method of * the TileCodecDescriptor for the given * formatName. * * @throws IllegalArgumentException if formatName is null. * @throws IllegalArgumentException if validModes is null. * @throws IllegalArgumentException if descriptor is null. */ public TileCodecParameterList(String formatName, String validModes[], ParameterListDescriptor descriptor) { super(descriptor); // Cause IllegalArgumentException to be thrown if any of the // arguments is null. if (formatName == null) { throw new IllegalArgumentException( JaiI18N.getString("TileCodecDescriptorImpl0")); } if (validModes == null) { throw new IllegalArgumentException( JaiI18N.getString("TileCodecParameterList0")); } if (descriptor == null) { throw new IllegalArgumentException( JaiI18N.getString("TileCodecParameterList1")); } this.formatName = formatName; this.validModes = validModes; } /** * Returns the name of the format which this parameter list describes. */ public String getFormatName() { return formatName; } /** * Returns true if the parameters in this * TileCodecParameterList are valid for the specified * registry mode name, false otherwise. The valid modes for * this class are the "tileEncoder" registry mode, and the * "tileDecoder" registry mode. */ public boolean isValidForMode(String registryModeName) { for (int i=0; iTileCodecParameterList * is valid for, as a String array. */ public String[] getValidModes() { return (String[])validModes.clone(); } } jai-core-1.1.4/src/share/classes/javax/media/jai/tilecodec/TileDecoderFactory.java0000644000175000017500000000633110203035544027651 0ustar mathieumathieu/* * $RCSfile: TileDecoderFactory.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:56 $ * $State: Exp $ */package javax.media.jai.tilecodec; import java.io.InputStream; import javax.media.jai.remote.NegotiableCapability; /** * A factory for creating TileDecoders. * *

      This class stipulates that the capabilities of the * TileDecoder be specified by implementing the * getDecodingCapability() method. * * @see javax.media.jai.remote.NegotiableCapability * * @since JAI 1.1 */ public interface TileDecoderFactory { /** * Creates a TileDecoder capable of decoding the encoded * data from the given InputStream using the specified * TileCodecParameterList containing the decoding * parameters to be used. * *

      This method can return null if the TileDecoder is * not capable of producing output for the given set of parameters. * For example, if a TileDecoder is only capable of dealing * with a jpeg quality factor of 0.5, and the associated * TileCodecParameterList specifies a quality factor of 0.75, * null should be returned. * *

      It is recommended that the data in the supplied * InputStream not be used as a factor in determining * whether this InputStream can be successfully decoded, * unless the supplied InputStream is known to be rewindable * (i.e. its markSupported() method returns true or it has * additional functionality that allows backward seeking). It is required * that the InputStream contain the same data on * returning from this method as before this method was called. * In other words, the InputStream should only be used as a * discriminator if it can be rewound to its starting position * before returning from this method. Note that wrapping the * incoming InputStream in a PushbackInputStream * and then rewinding the PushbackInputStream before returning * does not rewind the wrapped InputStream. * *

      If the supplied TileCodecParameterList is null, * a default TileCodecParameterList from the * TileCodecDescriptor will be used to create the decoder. * *

      Exceptions thrown by the TileDecoder will be * caught by this method and will not be propagated. * * @param input The InputStream containing the encoded data * to decode. * @param param The parameters to be be used in the decoding process. * @throws IllegalArgumentException if input is null. */ TileDecoder createDecoder(InputStream input, TileCodecParameterList param); /** * Returns the capabilities of this TileDecoder as a * NegotiableCapability. * * @see javax.media.jai.remote.NegotiableCapability */ NegotiableCapability getDecodeCapability(); } jai-core-1.1.4/src/share/classes/javax/media/jai/tilecodec/TileCodecDescriptor.java0000644000175000017500000001441510203035544030032 0ustar mathieumathieu/* * $RCSfile: TileCodecDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:55 $ * $State: Exp $ */package javax.media.jai.tilecodec; import java.awt.image.SampleModel; import javax.media.jai.RegistryElementDescriptor; import javax.media.jai.ParameterListDescriptor; /** * A class to describe a particular tile codec format. The getName * method of RegistryElementDescriptor should be implemented * to return the name of the format in an implementation of this interface. * This name is also the String with which this * TileCodecDescriptor is associated in the * OperationRegistry. There are two * complemetary modes that TileCodecs are expected to function in, the * decoding mode specified by the "tileDecoder" String and the encoding * mode specified by the "tileEncoder" String. It is not recommended that * separate classes be used to implement the different modes, but if this * is done, then includesSampleModelInfo() and * includesLocationInfo() methods must return the same values * from both the implementing classes. * *

      In order to successfully decode an encoded tile data stream into a * decoded Raster, at the very least, a Point * specifying the top left corner of the Raster, a * SampleModel specifying the data layout described minimally * by the dataType, number of bands, width and height and a * DataBuffer with the decoded pixel data are needed. The * DataBuffer can be created from the information from the * SampleModel and the decoded data. Therefore the absolute * minimum information that is required in order to create a * Raster upon decoding (aside from the decoded data itself) * is the Point specifying the top left corner of the * Raster, the SampleModel specifying the data * layout. Some formats include this information about the layout of the * tile while others don't. The formats that do include this information * needed to create a SampleModel and a Point * should return true from the includesSampleModelInfo() and * includesLocationInfo() methods respectively. The formats * that do not include this information in the encoded stream should return * false. For decoding, the TileCodecParameterList providing the * decoding parameters will in this case be expected to contain a parameter * named "sampleModel" with a non-null SampleModel as its value. * This SampleModel will be used to create the decoded * Raster and is expected to be the same as the * SampleModel of the tiles to be encoded. * *

      All Strings are treated in a case-retentive and * case-insensitive manner. * * @see TileDecoder * @see TileEncoder * @since JAI 1.1 */ public interface TileCodecDescriptor extends RegistryElementDescriptor { /** * Returns true if the format encodes layout information generally * specified via the SampleModel in the encoded data stream. */ boolean includesSampleModelInfo(); /** * Returns true if the format encodes in the data stream the location of * the Raster with respect to its enclosing image. */ boolean includesLocationInfo(); /** * Returns the default parameters for the specified modeName as an * instance of the TileCodecParameterList. * * @throws IllegalArgumentException if modeName is null. * @throws IllegalArgumentException if modeName is not * one of the modes valid for this descriptor, i.e those returned * from the getSupportedNames() method. */ TileCodecParameterList getDefaultParameters(String modeName); /** * Returns the default parameters for the specified modeName as an * instance of the TileCodecParameterList, adding a * "sampleModel" parameter with the specified value to the parameter * list. * *

      This method should be used when includesSampleModelInfo() * returns false. If includesSampleModelInfo() returns true, the * supplied SampleModel is ignored. * *

      If a parameter named "sampleModel" exists in the default * parameter list, the supplied SampleModel will override * the value associated with this default parameter. * * @param sm The SampleModel used to create the * default decoding parameter list. * * @throws IllegalArgumentException if modeName is null. * @throws IllegalArgumentException if modeName is not * one of the modes valid for this descriptor, i.e those returned * from the getSupportedNames() method. */ TileCodecParameterList getDefaultParameters(String modeName, SampleModel sm); /** * Returns a TileCodecParameterList valid for the * specified modeName and compatible with the supplied * TileCodecParameterList. * For example, given a TileCodecParameterList used to * encode a tile with the modeName being specified as "tileDecoder", this * method will return a TileCodecParameterList sufficient * to decode that same encoded tile. * * @param modeName The registry mode to return a valid parameter * list for. * @param otherParamList The parameter list for which a compatible * parameter list for the specified modeName is * to be returned. * * @throws IllegalArgumentException if modeName is null. * @throws IllegalArgumentException if modeName is not * one of the modes valid for this descriptor, i.e those returned * from the getSupportedNames() method. */ TileCodecParameterList getCompatibleParameters(String modeName, TileCodecParameterList otherParamList); } jai-core-1.1.4/src/share/classes/javax/media/jai/tilecodec/TileEncoderFactory.java0000644000175000017500000000547610203035544027674 0ustar mathieumathieu/* * $RCSfile: TileEncoderFactory.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:56 $ * $State: Exp $ */package javax.media.jai.tilecodec; import java.awt.image.SampleModel; import java.io.OutputStream; import javax.media.jai.remote.NegotiableCapability; /** * A factory for creating TileEncoders. * *

      This class stipulates that the capabilities of the * TileEncoder be specified by implementing the * getEncodingCapability() method. * * @see javax.media.jai.remote.NegotiableCapability * * @since JAI 1.1 */ public interface TileEncoderFactory { /** * Creates a TileEncoder capable of encoding a * Raster with the specified SampleModel * using the encoding parameters specified via the * TileCodecParameterList, to the given * OutputStream. * *

      This method can return null if the TileEncoder is not * capable of producing output for the given set of parameters. * For example, if a TileEncoder is only capable of dealing * with a PixelInterleavedSampleModel, and the supplied * SampleModel is not an instance of * PixelInterleavedSampleModel, null should be * returned. The supplied SampleModel should be used to * decide whether it can be encoded by this class, and is not needed * to actually construct a TileEncoder. * *

      If the supplied TileCodecParameterList is null, * a default TileCodecParameterList from the * TileCodecDescriptor will be used to create the encoder. * *

      Exceptions thrown by the TileEncoder * will be caught by this method and will not be propagated. * * @param output The OutputStream to write the encoded * data to. * @param paramList The TileCodecParameterList containing * the encoding parameters. * @param sampleModel The SampleModel of the * Raster to be encoded. * * @throws IllegalArgumentException if output is null. * @throws IllegalArgumentException if sampleModel is null. */ TileEncoder createEncoder(OutputStream output, TileCodecParameterList paramList, SampleModel sampleModel); /** * Returns the capabilities of this TileEncoder as a * NegotiableCapability. * * @see javax.media.jai.remote.NegotiableCapability */ NegotiableCapability getEncodeCapability(); } jai-core-1.1.4/src/share/classes/javax/media/jai/tilecodec/GZIPTileCodecDescriptor.java0000644000175000017500000001777310203035544030536 0ustar mathieumathieu/* * $RCSfile: GZIPTileCodecDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:54 $ * $State: Exp $ */package javax.media.jai.tilecodec ; import java.awt.image.SampleModel ; import javax.media.jai.ParameterListDescriptor; import javax.media.jai.ParameterListDescriptorImpl; import javax.media.jai.PropertyGenerator; /** *

      This class is the descriptor for the "GZIP" tile codec. This codec * scheme uses "gzip" as the method of compressing tile data. This is a * lossless tile codec. The format name for the gzip tile codec is "gzip". * The encoded stream contains the SampleModel and the tile's * upper left corner position, thus the includesSampleModelInfo() * and includesLocationInfo() methods in this descriptor return * true. * *

      The "gzip" codec scheme does not support any parameters. * *

      * * * * * * *
      Resource List
      Name Value
      Vendor com.sun.media.jai
      Description A descriptor to describe the lossless "gzip" * codec scheme.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/tilecodec/GZIPTileCodecDescriptor.html
      Version 1.2

      * *

      * * * *
      Parameter List
      Name Class TypeDefault Value

      * * @since JAI 1.1 */ public class GZIPTileCodecDescriptor extends TileCodecDescriptorImpl { private static ParameterListDescriptorImpl pld = new ParameterListDescriptorImpl(); /** * Creates a GZIPTileCodecDescriptor */ public GZIPTileCodecDescriptor() { super("gzip", true, true) ; } /** * Returns a TileCodecParameterList valid for the * specified modeName and compatible with the supplied * TileCodecParameterList. For example, given a * TileCodecParameterList used to encode a tile with * the modeName being specified as "tileDecoder", this method will * return a TileCodecParameterList * sufficient to decode that same tile. For the gzip tile codec, * no parameters are used. So null will be returned for any valid * modeName specified. * * @param modeName The registry mode to return a valid parameter * list for. * @param otherParamList The parameter list for which a compatible * parameter list for the complementary modeName is * to be found. * * @throws IllegalArgumentException if modeName is null. * @throws IllegalArgumentException if modeName is not * one of the modes valid for this descriptor, i.e those returned * from the getSupportedNames() method. */ public TileCodecParameterList getCompatibleParameters( String modeName, TileCodecParameterList otherParamList) { if (modeName == null) throw new IllegalArgumentException( JaiI18N.getString("TileCodecDescriptorImpl1")); String validNames[] = getSupportedModes(); boolean valid = false; for (int i=0; iTileCodecParameterList
      . For the * gzip tile codec, no parameters are used. So null will be * returned for any valid modeName specified. * * @param modeName The registry mode to return a valid parameter * list for. * * @throws IllegalArgumentException if modeName is null. * @throws IllegalArgumentException if modeName is not * one of the modes valid for this descriptor, i.e those returned * from the getSupportedNames() method. */ public TileCodecParameterList getDefaultParameters(String modeName){ if (modeName == null) throw new IllegalArgumentException( JaiI18N.getString("TileCodecDescriptorImpl1")); String validNames[] = getSupportedModes(); boolean valid = false; for (int i=0; iTileCodecParameterList, adding a * "sampleModel" parameter with the specified value to the parameter * list. For the gzip tile codec, no parameters are used. So null will be * returned for any valid modeName specified. * *

      This method should be used when includesSampleModelInfo() * returns false. If includesSampleModelInfo() returns true, the * supplied SampleModel is ignored. * *

      If a parameter named "sampleModel" exists in the default * parameter list, the supplied SampleModel will override the value * associated with this default parameter. * * @param modeName The registry mode to return a valid parameter list for. * @param sm The SampleModel used to create the * default decoding parameter list. * * @throws IllegalArgumentException if modeName is null. * @throws IllegalArgumentException if modeName is not * one of the modes valid for this descriptor, i.e those returned * from the getSupportedNames() method. */ public TileCodecParameterList getDefaultParameters(String modeName, SampleModel sm) { if (modeName == null) throw new IllegalArgumentException( JaiI18N.getString("TileCodecDescriptorImpl1")); String validNames[] = getSupportedModes(); boolean valid = false; for (int i=0; iParameterListDescriptor that describes * the associated parameters (NOT sources). This method returns * null if there are no parameters for the specified modeName. * If the specified modeName supports parameters but the * implementing class does not have parameters, then this method * returns a non-null ParameterListDescriptor whose * getNumParameters() returns 0. * * @param modeName The mode to get the ParameterListDescriptor for. * * @throws IllegalArgumentException if modeName is null. * * @throws IllegalArgumentException if modeName is null. * @throws IllegalArgumentException if modeName is not * one of the modes valid for this descriptor, i.e those returned * from the getSupportedNames() method. */ public ParameterListDescriptor getParameterListDescriptor(String modeName){ if (modeName == null) throw new IllegalArgumentException( JaiI18N.getString("TileCodecDescriptorImpl1")); String validNames[] = getSupportedModes(); boolean valid = false; for (int i=0; iInputStream * into a Raster. * *

      This interface is designed to allow decoding of formats that * include information about the format of the encoded tile as well as ones * that don't. In order to create a Raster, at the very least, * a Point specifying the top left corner of the * Raster, a SampleModel specifying the data layout * and a DataBuffer with the decoded pixel data are * needed. The DataBuffer can be created from the * information from the SampleModel and the decoded data. * Therefore the absolute minimum information that is required in order to * create a Raster on decoding (aside from the decoded data * itself) is a Point specifying the top left corner of the * Raster and a SampleModel specifying the data * layout. The formats that do include this information should return true * from the includesSampleModelInfo() and * includesLocationInfo() from the associated * TileCodecDescriptor if they include information needed to * create a SampleModel and information needed to * create the Point respectively. The formats that do not * include this information in the encoded stream should return false. The * TileCodecParameterList providing the decoding parameters * will in this case be expected to contain a parameter named "sampleModel" * with a non-null SampleModel as its value. This * SampleModel will be used to create the decoded * Raster. * *

      The formats that return true from includesSampleModelInfo() * should use the decode() method to cause the decoding to take * place, the ones that return false should specify the Point * location to the decoding process by using the decode(Point) * method. Similarly the SampleModel must be specified as a * parameter with a non-null value on the TileCodecParameterList * passed to this TileDecoder if * includesSampleModelInfo() returns false. It is expected that * the SampleModel specified in the parameter list is the * SampleModel of the encoded tiles, in order to get a * decoded Raster that is equivalent to the one encoded. If the * SampleModel specified through the parameter list is different * from those of the encoded tiles, the result of decoding is undefined. * *

      If includesSampleModelInfo() returns true, the * SampleModel (if present) on the * TileCodecParameterList is ignored. * * @see TileCodecDescriptor * @see TileEncoder * * @since JAI 1.1 */ public interface TileDecoder { /** * Returns the name of the format. */ String getFormatName(); /** * Returns the current parameters as an instance of the * TileCodecParameterList interface. */ TileCodecParameterList getDecodeParameterList(); /** * Returns the InputStream containing the encoded data. */ InputStream getInputStream(); /** * Returns a Raster that contains the decoded contents * of the InputStream associated with this * TileDecoder. * *

      This method can perform the decoding correctly only when * includesLocationInfo() returns true. * * @throws IOException if an I/O error occurs while reading from the * associated InputStream. * @throws IllegalArgumentException if the associated * TileCodecDescriptor's includesLocationInfo() returns false. */ Raster decode() throws IOException; /** * Returns a Raster that contains the decoded contents * of the InputStream associated with this * TileDecoder. * *

      This method should be used when includesLocationInfo() * returns false. If includesLocationInfo() returns true, then * the supplied Point is ignored. * * @param location The Point specifying the upper * left corner of the Raster. * @throws IOException if an I/O error occurs while reading from the * associated InputStream. * @throws IllegalArgumentException if includesLocationInfo() returns false * and location is null. */ Raster decode(Point location) throws IOException; } jai-core-1.1.4/src/share/classes/javax/media/jai/tilecodec/TileEncoder.java0000644000175000017500000000461310203035544026334 0ustar mathieumathieu/* * $RCSfile: TileEncoder.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:56 $ * $State: Exp $ */package javax.media.jai.tilecodec; import java.awt.image.Raster; import java.io.IOException; import java.io.OutputStream; /** * An interface describing objects that transform a Raster * into an OutputStream. * * If the TileCodecDescriptor for this format returns true from * its includesSampleModelInfo() and * includesLocationInfo() methods, then the encoder must encode * this information in the encoded tiles. If these methods return false, then * this information (needed to create a Raster on decoding) must * be provided to the TileDecoder by setting the * SampleModel on the TileCodecParameterList supplied * to the TileDecoder and supplying the tile upper left corner * location to the TileDecoder via the TileDecoder's * decode(Point location) method. The SampleModel * value set on the TileCodecParameterList for the * TileDecoder should be the same as that of the * Raster to be encoded, in order to get a Raster * on decoding that is equivalent to the one being encoded. * * @see TileCodecDescriptor * @see TileDecoder * * @since JAI 1.1 */ public interface TileEncoder { /** * Returns the format name of the encoding scheme. */ String getFormatName(); /** * Returns the current parameters as an instance of the * TileCodecParameterList interface. */ TileCodecParameterList getEncodeParameterList(); /** * Returns the OutputStream to which the encoded data * will be written. */ public OutputStream getOutputStream(); /** * Encodes a Raster and writes the output * to the OutputStream associated with this * TileEncoder. * * @param ras the Raster to encode. * @throws IOException if an I/O error occurs while writing to the * OutputStream. * @throws IllegalArgumentException if ras is null. */ public void encode(Raster ras) throws IOException; } jai-core-1.1.4/src/share/classes/javax/media/jai/tilecodec/RawTileCodecDescriptor.java0000644000175000017500000002021710203035544030501 0ustar mathieumathieu/* * $RCSfile: RawTileCodecDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:55 $ * $State: Exp $ */package javax.media.jai.tilecodec ; import java.awt.image.SampleModel ; import javax.media.jai.PropertyGenerator; import javax.media.jai.ParameterListDescriptor; import javax.media.jai.ParameterListDescriptorImpl; import javax.media.jai.tilecodec.TileCodecDescriptorImpl ; import javax.media.jai.tilecodec.TileCodecParameterList ; /** * This class is the descriptor for the "Raw" tile codec. The "Raw" tile * codec scheme involves simply writing out the raw pixel data to the * encoded stream. The format name for the raw tile codec is "raw". * Since the encoded stream contains the Raster, it * automatically contains the SampleModel and the tile's * upper left corner position. Therefore the * includesSampleModelInfo() and * includesLocationInfo() methods in this descriptor return * true. * *

      The "Raw" codec scheme does not support any parameters. * *

      * * * * * * *
      Resource List
      Name Value
      Vendor com.sun.media.jai
      Description A descriptor to describe the lossless * "raw" codec scheme.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/tilecodec/RawTileCodecDescriptor.html
      Version 1.2

      * *

      * * * *
      Parameter List
      Name Class TypeDefault Value

      * * @since JAI 1.1 */ public class RawTileCodecDescriptor extends TileCodecDescriptorImpl { private static ParameterListDescriptorImpl pld = new ParameterListDescriptorImpl(); /** * Creates a RawTileCodecDescriptor. */ public RawTileCodecDescriptor( ) { super("raw", true, true) ; } /** * Returns a TileCodecParameterList valid for the * specified modeName and compatible with the supplied * TileCodecParameterList. For example, given a * TileCodecParameterList used to encode a tile with * the modeName being specified as "tileDecoder", this method will return * a TileCodecParameterList sufficient to decode that * same tile. For the raw tile codec, no parameters are used. So null * will be returned for any valid modeName specified. * * @param modeName The registry mode to return a valid parameter * list for. * @param otherParamList The parameter list for which a compatible * parameter list for the complementary modeName is * to be found. * * @throws IllegalArgumentException if modeName is null. * @throws IllegalArgumentException if modeName is not * one of the modes valid for this descriptor, i.e those returned * from the getSupportedNames() method. */ public TileCodecParameterList getCompatibleParameters( String modeName, TileCodecParameterList otherParamList) { if (modeName == null) throw new IllegalArgumentException( JaiI18N.getString("TileCodecDescriptorImpl1")); String validNames[] = getSupportedModes(); boolean valid = false; for (int i=0; iTileCodecParameterList. For the * raw tile codec, no parameters are used. So null will be * returned for any valid modeName specified. * * @param modeName The registry mode to return a valid parameter * list for. * * @throws IllegalArgumentException if modeName is null. * @throws IllegalArgumentException if modeName is not * one of the modes valid for this descriptor, i.e those returned * from the getSupportedNames() method. */ public TileCodecParameterList getDefaultParameters(String modeName) { if (modeName == null) throw new IllegalArgumentException( JaiI18N.getString("TileCodecDescriptorImpl1")); String validNames[] = getSupportedModes(); boolean valid = false; for (int i=0; iTileCodecParameterList, adding a * "sampleModel" parameter with the specified value to the parameter * list. For the raw tile codec, no parameters is used. So null will be * returned for any valid modeName specified. * *

      This method should be used when includesSampleModelInfo() * returns false. If includesSampleModelInfo() returns true, the * supplied SampleModel is ignored. * *

      If a parameter named "sampleModel" exists in the default * parameter list, the supplied SampleModel will override the value * associated with this default parameter. * * @param modeName The registry mode to return a valid parameter list for. * @param sm The SampleModel used to create the * default decoding parameter list. * * @throws IllegalArgumentException if modeName is null. * @throws IllegalArgumentException if modeName is not * one of the modes valid for this descriptor, i.e those returned * from the getSupportedNames() method. */ public TileCodecParameterList getDefaultParameters(String modeName, SampleModel sm){ if (modeName == null) throw new IllegalArgumentException( JaiI18N.getString("TileCodecDescriptorImpl1")); String validNames[] = getSupportedModes(); boolean valid = false; for (int i=0; iParameterListDescriptor that describes * the associated parameters (NOT sources). This method returns * null if there are no parameters for the specified modeName. * If the specified modeName supports parameters but the * implementing class does not have parameters, then this method * returns a non-null ParameterListDescriptor whose * getNumParameters() returns 0. * * @param modeName The mode to return a ParameterListDescriptor for. * * @throws IllegalArgumentException if modeName is null. * * @throws IllegalArgumentException if modeName is null. * @throws IllegalArgumentException if modeName is not * one of the modes valid for this descriptor, i.e those returned * from the getSupportedNames() method. */ public ParameterListDescriptor getParameterListDescriptor(String modeName){ if (modeName == null) throw new IllegalArgumentException( JaiI18N.getString("TileCodecDescriptorImpl1")); String validNames[] = getSupportedModes(); boolean valid = false; for (int i=0; iSampleModel and the * tile's upper left corner position, thus the * includesSampleModelInfo() and * includesLocationInfo() methods in this descriptor return * true. * *

      This JPEG tile codec works well only on byte type images with 1, * 3 or 4 bands. * *

      While both the "tileDecoder" and "tileEncoder" registry modes for * the "jpeg" tile codec scheme have the same set of parameters, the * parameters for the "tileDecoder" mode are read-only and will be * ignored if values are set for them in the * TileCodecParameterList passed to the TileDecoder. * The reason for this is that the parameter values needed to control * the decoding process are included in the encoded stream, therefore * parameter values specified externally are ignored. Once the decoding * is completed, the parameter values in the * TileCodecParameterList are updated to reflect the values * specified in the encoded stream. * *

      * * * * * * * * * * * * * * * * * * * *
      Resource List
      Name Value
      Vendor com.sun.media.jai
      Description A descriptor to describe the lossy "jpeg" codec * scheme.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/tilecodec/JPEGTileCodecDescriptor.html
      Version 1.2
      quality *

      A factor that relates to the desired tradeoff * between image quality and the image data * compression ratio. The range of this parameter * is from 0.0 to 1.0. A setting of 1.0 produces the * highest quality image at a lower compression * rate. A setting of 0.0 produces the highest * compression ratio, with a sacrifice to image * quality. The default value is 0.75.

      In JPEG, * compression, there are two ways to control the * quantization quality: one is define quantization * table for each image component; the other is * define the quality. The later overrides the * former. If neither the quality nor quantization * tables are set, the default setting is used. *

      When the quality is set, a group of * quantization tables are generated by rescaling * the default quantization tables. For more * infomation, please refer to the links below. *

      qualitySet A boolean used to indicate that the * parameter quality is set or * not.
      horizontalSubsampling The subsampling rate in the * horizontal direction applied to each image * component to reduce their resolution * prior to encoding.
      verticalSubsampling The subsampling rate in the * vertical direction applied to each image * component to reduce their resolution * prior to encoding.
      quantizationTableMapping In JPEG compression, several * image components may share one quantization * table. This is the mapping between the * image component and the quantization * tables.
      quantizationTable0 A quantization table for JPEG codec * is an array of 64 (8x8) expressed in zig-zag * order (see the JPEG spec section K.1). Since * this descriptor defines a JPEG scheme where the * tile has at most 4 components, so at most 4 * quantization tables will * be used. This parameter is the first * quantization table.
      quantizationTable1 The second quantization table.
      quantizationTable2 The third quantization table.
      quantizationTable3 The fourth quantization table.
      restartInterval JPEG images use restart markers to define * multiple strips or tiles. The restart markers * are inserted periodically into the image data * to delineate image segments known as restart * intervals. To limit the effect of bitstream * errors to a single restart interval, JAI * provides methods to set the restart interval * in JPEG Minimum Coded Units (MCUs). * The default is zero (no restart interval * markers).
      writeImageInfo A boolean instructs the encoder to * write the image data to the output * stream.
      writeTableInfo A boolean instructs the encoder to * write the table data to the output * stream.
      writeJFIFHeader The JPEG File Interchange Format (JFIF) * is a minimal file format that enables JPEG * bitstreams to be exchanged between a wide * variety of platforms and applications. * The parameter instructs the encoder to write * the output stream in the JFIF format.

      * *

      * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      quality java.lang.Float0.75
      qualitySet java.lang.Booleantrue
      horizontalSubsampling integer array{1,1,1}
      verticalSubsampling integer array{1,1,1}
      quantizationTableMapping integer array{0,1,1}
      quantizationTable0 integer arrayThe default Luminance table as defined in * section K.1 of the JPEG specification.
      quantizationTable1 integer arrayThe default Chrominance table as defined in * section K.1 of the JPEG specification.
      quantizationTable2 integer arrayThe default Chrominance table as defined in * section K.1 of the JPEG specification.
      quantizationTable3 integer arrayThe default Chrominance table as defined in * section K.1 of the JPEG specification.
      restartInterval java.lang.Integer0
      writeImageInfo java.lang.Booleantrue
      writeTableInfo java.lang.Booleantrue
      writeJFIFHeader java.lang.Booleanfalse

      * * @see com.sun.image.codec.jpeg.JPEGQTable * @see com.sun.image.codec.jpeg.JPEGDecodeParam * @see com.sun.image.codec.jpeg.JPEGEncodeParam * * @since JAI 1.1 */ public class JPEGTileCodecDescriptor extends TileCodecDescriptorImpl { private static final int[] lumQuantizationTable = { 16, 11, 12, 14, 12, 10, 16, 14, 13, 14, 18, 17, 16, 19, 24, 40, 26, 24, 22, 22, 24, 49, 35, 37, 29, 40, 58, 51, 61, 60, 57, 51, 56, 55, 64, 72, 92, 78, 64, 68, 87, 69, 55, 56, 80, 109, 81, 87, 95, 98, 103, 104, 103, 62, 77, 113, 121, 112, 100, 120, 92, 101, 103, 99 }; private static final int[] chromQuantizationTable = { 17, 18, 18, 24, 21, 24, 47, 26, 26, 47, 99, 66, 56, 66, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99 }; // Parameter names private static final String[] paramNames = { "quality", "qualitySet", "horizontalSubsampling", "verticalSubsampling", "quantizationTableMapping", "quantizationTable0", "quantizationTable1", "quantizationTable2", "quantizationTable3", "restartInterval", "writeImageInfo", "writeTableInfo", "writeJFIFHeader"} ; // Parameter class names private static final Class[] paramClasses = { java.lang.Float.class, java.lang.Boolean.class, int[].class, int[].class, int[].class, int[].class, int[].class, int[].class, int[].class, java.lang.Integer.class, java.lang.Boolean.class, java.lang.Boolean.class, java.lang.Boolean.class} ; // Parameter default values. private static final Object[] paramDefaults = { new Float(0.75f) , new Boolean(true) , new int[] {1, 1, 1}, new int[] {1, 1, 1}, new int[] {0, 1, 1}, lumQuantizationTable, chromQuantizationTable, chromQuantizationTable, chromQuantizationTable, new Integer(0), new Boolean(true), new Boolean(true), new Boolean(false) }; /* XXX: If Set can be specified for non EnumeratedParameter types. // Valid values for the boolean valued parameters private static java.util.Set set = Collections.synchronizedSet(new HashSet()); static { set.add(Boolean.TRUE); set.add(Boolean.FALSE); } */ // Parameters' valid value ranges private static final Object[] validParamValues = { new Range(java.lang.Float.class, new Float(0.0f), new Float(1.0f)) , null, // set, null, null, null, null, null, null, null, new Range(java.lang.Integer.class, new Integer(0), null), null, // set, null, // set, null // set }; private static ParameterListDescriptor paramListDescriptor = new ParameterListDescriptorImpl(null, paramNames, paramClasses, paramDefaults, validParamValues); /** * Creates a JPEGTileCodecDescriptor */ public JPEGTileCodecDescriptor() { super("jpeg", true, true) ; } /** * Returns a TileCodecParameterList valid for the * specified modeName and compatible with the supplied * TileCodecParameterList. For example, given a * TileCodecParameterList used to encode a tile with * the modeName being specified as "tileDecoder", this method will return * a TileCodecParameterList sufficient to decode that * same tile. * *

      If the supplied modeName is one of the valid mode names as * ascertained from the getSupportedNames() method, * this method returns a TileCodecParameterList that * contains values that are compatible for the supplied mode name. * * @param modeName The registry mode to return a valid parameter * list for. * @param otherParamList The parameter list for which a compatible * parameter list for the complementary modeName is * to be found. * * @throws IllegalArgumentException if modeName is null. * @throws IllegalArgumentException if modeName is not * one of the modes valid for this descriptor, i.e those returned * from the getSupportedNames() method. * @throws IllegalArgumentException if otherParamList is null. */ public TileCodecParameterList getCompatibleParameters( String modeName, TileCodecParameterList otherParamList) { if (modeName == null) { throw new IllegalArgumentException( JaiI18N.getString("TileCodecDescriptorImpl1")); } if (otherParamList == null) { throw new IllegalArgumentException( JaiI18N.getString("TileCodecDescriptorImpl3")); } String name = getName(); if (!otherParamList.getFormatName().equals(name)) { throw new IllegalArgumentException( JaiI18N.getString("TileCodec2")); } if (otherParamList.isValidForMode(modeName)) return otherParamList; if (modeName.equalsIgnoreCase("tileDecoder")) { return new TileCodecParameterList( name, new String[]{"tileDecoder"}, otherParamList.getParameterListDescriptor()); } else if (modeName.equalsIgnoreCase("tileEncoder")) { return new TileCodecParameterList( name, new String[]{"tileEncoder"}, otherParamList.getParameterListDescriptor()); } else { throw new IllegalArgumentException(JaiI18N.getString("TileCodec1")); } } /** * Returns the default parameters for the specified modeName as an * instance of the TileCodecParameterList. If the supplied * modeName is one of the valid mode names as ascertained from the * getSupportedNames() method, this method returns the * default parameters for that mode. * * @param modeName The mode to return the default parameters for. * * @throws IllegalArgumentException if modeName is null. * @throws IllegalArgumentException if modeName is not * one of the modes valid for this descriptor, i.e those returned * from the getSupportedNames() method. */ public TileCodecParameterList getDefaultParameters(String modeName){ if (modeName == null) throw new IllegalArgumentException( JaiI18N.getString("TileCodecDescriptorImpl1")) ; String validNames[] = getSupportedModes(); boolean valid = false; for (int i=0; iTileCodecParameterList, adding a * "sampleModel" parameter with the specified value to the parameter * list. If the supplied modeName is one of the valid mode names as * ascertained from the getSupportedNames() method, this * method returns the default parameters for that mode. * *

      This method should be used when includesSampleModelInfo() * returns false. If includesSampleModelInfo() returns true, the * supplied SampleModel is ignored. * *

      For the JPEG codec, includesSampleModelInfo() returns true, so * the supplied SampleModel is ignored. * * @param modeName The mode to return the default parameters for. * @param sm The SampleModel used to create the * default decoding parameter list. * * @throws IllegalArgumentException if modeName is null. * @throws IllegalArgumentException if modeName is not * one of the modes valid for this descriptor, i.e those returned * from the getSupportedNames() method. */ public TileCodecParameterList getDefaultParameters(String modeName, SampleModel sm){ return getDefaultParameters(modeName); } /** * Returns the ParameterListDescriptor that describes * the associated parameters (NOT sources). If the supplied modeName * is one of the valid mode names as ascertained from the * getSupportedNames() method, this method returns a * non-null ParameterListDescriptor with the appropriate * parameters. * * @param modeName The mode to return the ParameterListDescriptor for. * * @throws IllegalArgumentException if modeName is null. * @throws IllegalArgumentException if modeName is not * one of the modes valid for this descriptor, i.e those returned * from the getSupportedNames() method. */ public ParameterListDescriptor getParameterListDescriptor(String modeName){ if(modeName == null) throw new IllegalArgumentException( JaiI18N.getString("TileCodecDescriptorImpl1")) ; String validNames[] = getSupportedModes(); boolean valid = false; for (int i=0; iOperationDescriptorImpl while * retaining the ability to perform introspection on the allowable range of * values of the enumeration. An example of an enumerated parameter is the * type parameter of the "Transpose" operation which is defined in * TransposeDescriptor to accept only the values defined by the * FLIP_* and ROTATE_* fields of the descriptor. * *

      This class may be used to create enumerated parameters in an * OperationDescriptor as follows: * *

        * *
      • For each constrained-value parameter create a final class extending * EnumeratedParameter. This class should consist of only a * package private constructor with a single String parameter * called name and a single statement invoking the superclass * constructor with name as the parameter.
      • * *
      • Define the class of the parameter in question to be the * subclass of EnumeratedParameter which was created for it in * in the previous step.
      • * *
      • For each possible value of the parameter, define in the * OperationDescriptor of the operator a public static final * field of type equal to the class defined in the first step. Each field * should be assigned an instance of the subclass defined in the first step. * The name and value used for each of these * EnumeratedParameter subclass instances should be distinct * for each distinct field or an error will occur.
      • * *
      * * With respect to TransposeDescriptor, the three steps above * would be to 1) create a final class TransposeType in the * javax.media.jai.operator package; 2) define the * type of the "type" parameter as TransposeType.class; and * 3) define a static final field of class TransposeType for * each of the enumerated values with each field being initialized to an * instance of TransposeType with name equal to the name * of the field and value to its integral (enumerated) value. * * @see OperationDescriptorImpl * @see javax.media.jai.operator.TransposeDescriptor * * @since JAI 1.1 */ public class EnumeratedParameter implements Serializable { private String name; private int value; /** * Constructs an EnumeratedParameter with the indicated name * and value. */ public EnumeratedParameter(String name, int value) { this.name = name; this.value = value; } /** * Returns the name assigned to this EnumeratedParameter * when it was constructed. */ public String getName() { return name; } /** * Returns the value assigned to this EnumeratedParameter * when it was constructed. */ public int getValue() { return value; } /** * Returns a hash code value for the object. */ public int hashCode() { return (getClass().getName() + (new Integer(value))).hashCode(); } /** * Returns true if and only if the parameter is an instance * of the class on which this method is invoked and has either the same * name or the same value. */ public boolean equals(Object o) { return o != null && this.getClass().equals(o.getClass()) && (name.equals(((EnumeratedParameter)o).getName()) || value == ((EnumeratedParameter)o).getValue()); } /** * Returns a String representation of this * EnumeratedParameter as a concatentation of the form *
           * [class name]:[parameter name]=[parameter value]
           * 
      * For example, for an instance of a subclass * org.foobar.jai.EnumParam with name "SomeValue" and * value "2" the returned String would be *
           * org.foobar.jai.EnumParam:SomeValue=2
           * 
      */ public String toString() { return getClass().getName()+":"+name+"="+String.valueOf(value); } } jai-core-1.1.4/src/share/classes/javax/media/jai/JAI.java0000644000175000017500000035776710341171325022635 0ustar mathieumathieu/* * $RCSfile: JAI.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.3 $ * $Date: 2005-11-23 22:53:09 $ * $State: Exp $ */ package javax.media.jai; import com.sun.media.jai.util.SunTileCache; import com.sun.media.jai.util.SunTileScheduler; import java.awt.Dimension; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.StringWriter; import java.lang.reflect.Method; import java.util.Collection; import java.util.Iterator; import java.util.Map; import java.util.Vector; import javax.media.jai.remote.NegotiableCapabilitySet; import javax.media.jai.tilecodec.TileCodecParameterList; import javax.media.jai.util.ImagingListener; import com.sun.media.jai.util.ImagingListenerImpl; import com.sun.media.jai.util.PropertyUtil; /** * A convenience class for instantiating operations. * *

      This class allows programmers to use the syntax: * *

       * import javax.media.jai.JAI;
       * RenderedOp im = JAI.create("convolve", paramBlock, renderHints);
       * 
      * * to create new images or collections by applying operators. * The create() method returns a RenderedOp * encapsulating the operation name, parameter block, and rendering * hints. Additionally, it performs validity checking on the operation * parameters. Programmers may also refer to * JAI.createCollection("opname", paramBlock, renderHints), * JAI.createRenderable("opname", paramBlock, renderHints), and * JAI.createRenderableCollection("opname", paramBlock, * renderHints). * *

      If the OperationDescriptor associated with the * named operation returns true from its * isImmediate() method, the JAI.createNS() * method will ask the RenderedOp it constructs to render * itself immediately. If this rendering is null, * createNS() will itself return null * rather than returning an instance of RenderedOp as it * normally does. * *

      It is possible to create new instances of theJAIclass in * order to control each instance's registry, tile cache, and tile scheduler * individually. Most users will want to use only the static methods * of this class, which perform all operations on a default instance, * which in turn makes use of a default registry. To create a new * image or collection on a non-default JAI instance, * the createNS() and createCollectionNS * (NS being short for "non-static") methods are used. * *

      The JAI class contains convenience methods for a * number of common argument list formats. These methods perform the * work of constructing a ParameterBlock automatically. * The convenience methods are available only in static * form and make use of the default instance. When operating with a * specific instance, the general, non-static functions * createNS() and createCollectionNS() * should be used. All of the convenience methods operate by calling * createNS() on the default JAI instance, * and thus inherit the semantics of that method with regard to immediate * rendering. * *

      The registry being used by a particular instance may be * retrieved or set using the getOperationRegistry() and * setOperationRegistry() methods, respectively. Only * advanced users should attempt to set the registry. The tile cache and * tile scheduler being used by a particular instance may likewise be set * or retrieved using the methods setTileCache(), * setTileScheduler(), getTileCache(), or * getTileScheduler(). * *

      Each instance of JAI contains a set of rendering * hints which will be used for all image or collection creations. * These hints are merged with any hints supplied to the * create method; directly supplied hints take precedence * over the common hints. When a new JAI instance is * constructed, its hints are initialized to a copy of the hints * associated with the default instance. When the default instance is * constructed, hints for the default registry, tile cache, and tile * scheduler are added to the set of common rendering hints. Similarly, * invoking setOperationRegistry(), setTileCache(), * or setTileScheduler() on a JAI instance will * cause the respective entity to be added to the common rendering hints. * The hints associated with any instance, including the default instance, * may be manipulated using the getRenderingHints(), * setRenderingHints(), and clearRenderingHints() * methods. * *

      An ImagingListener will reside in each instance of * JAI. It can be used to listen to (and * process) the exceptional situations that occur in the * operations and JAI. A default ImagingListener is * initially registered which re-throws RuntimeExceptions * and prints the error message and * the stack trace of other types to System.err. To override this * default behavior an instance of an alternate ImagingListener * implementation should be registered using * {@link JAI#setImagingListener}. * *

      An ImagingListener also can be attached to a node as * a rendering hint, which maps the key KEY_IMAGING_LISTENER. * The Throwables which arise in the creation * and the rendering of this node will be sent to this * ImagingListener (note that those thrown at the top levels * such as node creation failure will be handled by the listener registered * to the JAI instead.) The default value for this hint will * be the one registered to the instance of JAI.

      * * @see OperationRegistry * @see RenderingHints * @see TileScheduler * @see TileCache * @see ImagingListener */ public final class JAI { // // Private hint keys. Each of these keys must be assigned a unique value. // // JAI Core private static final int HINT_IMAGE_LAYOUT = 101; private static final int HINT_INTERPOLATION = 102; private static final int HINT_OPERATION_REGISTRY = 103; private static final int HINT_OPERATION_BOUND = 104; private static final int HINT_BORDER_EXTENDER = 105; private static final int HINT_TILE_CACHE = 106; private static final int HINT_TILE_SCHEDULER = 107; private static final int HINT_DEFAULT_COLOR_MODEL_ENABLED = 108; private static final int HINT_DEFAULT_COLOR_MODEL_METHOD = 109; private static final int HINT_TILE_CACHE_METRIC = 110; private static final int HINT_SERIALIZE_DEEP_COPY = 111; private static final int HINT_TILE_CODEC_FORMAT = 112; private static final int HINT_TILE_ENCODING_PARAM = 113; private static final int HINT_TILE_DECODING_PARAM = 114; private static final int HINT_RETRY_INTERVAL = 115; private static final int HINT_NUM_RETRIES = 116; private static final int HINT_NEGOTIATION_PREFERENCES = 117; private static final int HINT_DEFAULT_RENDERING_SIZE = 118; private static final int HINT_COLOR_MODEL_FACTORY = 119; private static final int HINT_REPLACE_INDEX_COLOR_MODEL = 120; private static final int HINT_TILE_FACTORY = 121; private static final int HINT_TILE_RECYCLER = 122; private static final int HINT_CACHED_TILE_RECYCLING_ENABLED = 123; private static final int HINT_TRANSFORM_ON_COLORMAP = 124; private static final int HINT_IMAGING_LISTENER = 125; // // Public keys // /** * Key for {@link ImageLayout} object values. * The common RenderingHints do not contain a default * hint corresponding to this key. */ public static RenderingHints.Key KEY_IMAGE_LAYOUT = new RenderingKey(HINT_IMAGE_LAYOUT, ImageLayout.class); /** * Key for {@link Interpolation} object values. * The common RenderingHints do not contain a default * hint corresponding to this key. * * @see MultiResolutionRenderableImage#createScaledRendering * @see MultiResolutionRenderableImage#createRendering */ public static RenderingHints.Key KEY_INTERPOLATION = new RenderingKey(HINT_INTERPOLATION, Interpolation.class); /** * Key for {@link OperationRegistry} object values. * The common RenderingHints by default contain a hint * corresponding to this key the value of which is equal to the value * returned by getOperationRegistry(). The hint is * automatically set by setOperationRegistry(). */ public static RenderingHints.Key KEY_OPERATION_REGISTRY = new RenderingKey(HINT_OPERATION_REGISTRY, OperationRegistry.class); /** * Key for Integer object values representing whether * the operation is compute, network, or I/O bound. The values * come from the constants OpImage.OP_COMPUTE_BOUND, * OpImage.OP_IO_BOUND, and * OpImage.OP_NETWORK_BOUND. * The common RenderingHints do not contain a default * hint corresponding to this key. */ public static RenderingHints.Key KEY_OPERATION_BOUND = new RenderingKey(HINT_OPERATION_BOUND, Integer.class); /** * Key for {@link BorderExtender} object values. * The common RenderingHints do not contain a default * hint corresponding to this key. */ public static RenderingHints.Key KEY_BORDER_EXTENDER = new RenderingKey(HINT_BORDER_EXTENDER, BorderExtender.class); /** * Key for {@link TileCache} object values. * The common RenderingHints by default contain a hint * corresponding to this key the value of which is equal to the value * returned by getTileCache(). The hint is * automatically set by setTileCache(). * * @see #createTileCache(long) * @see #createTileCache() * * @see OpImage#OpImage */ public static RenderingHints.Key KEY_TILE_CACHE = new RenderingKey(HINT_TILE_CACHE, TileCache.class); /** * Key for Tile ordering metric. * The common RenderingHints do not contain a default * hint corresponding to this key. * * @see OpImage#OpImage * @see TileCache * * @since JAI 1.1 */ public static RenderingHints.Key KEY_TILE_CACHE_METRIC = new RenderingKey(HINT_TILE_CACHE_METRIC, Object.class); /** * Key for {@link TileScheduler} object values. * The common RenderingHints by default contain a hint * corresponding to this key the value of which is equal to the value * returned by getTileScheduler(). The hint is * automatically set by setTileScheduler(). * * @see OpImage#OpImage * * @since JAI 1.1 */ public static RenderingHints.Key KEY_TILE_SCHEDULER = new RenderingKey(HINT_TILE_SCHEDULER, TileScheduler.class); /** * Key for enabling default ColorModel initialization * when a valid ColorModel may not be derived by * inheritance. The corresponding object must be a Boolean. * The common RenderingHints do not contain a default * hint corresponding to this key which is equivalent to its being * set to TRUE. * * @see OpImage#OpImage * * @since JAI 1.1 */ public static RenderingHints.Key KEY_DEFAULT_COLOR_MODEL_ENABLED = new RenderingKey(HINT_DEFAULT_COLOR_MODEL_ENABLED, Boolean.class); /** * Key for specifying a method to be used as for default * ColorModel initialization. The corresponding object * must be a java.lang.reflect.Method which is static * and accepts a single SampleModel parameter and returns a * ColorModel or null. * The common RenderingHints do not contain a default * hint corresponding to this key which is equivalent to its being * set to the Method corresponding to * {@link PlanarImage#createColorModel(SampleModel)}. * * @see OpImage#OpImage * * @since JAI 1.1 */ public static RenderingHints.Key KEY_DEFAULT_COLOR_MODEL_METHOD = new RenderingKey(HINT_DEFAULT_COLOR_MODEL_METHOD, Method.class); /** * Key for the dimensions of a RenderedImage created by * invoking createDefaultRendering() on a node of type * RenderableOp in a renderable processing chain. The * type of the associated value is java.awt.Dimension. * * @see RenderableOp#createDefaultRendering() * * @since JAI 1.1 */ public static final RenderingHints.Key KEY_DEFAULT_RENDERING_SIZE = new RenderingKey(HINT_DEFAULT_RENDERING_SIZE, Dimension.class); /** * Key for {@link ColorModelFactory} object values. * The common RenderingHints do not contain a default * hint corresponding to this key. * * @see OpImage#OpImage * * @since JAI 1.1.2 */ public static RenderingHints.Key KEY_COLOR_MODEL_FACTORY = new RenderingKey(HINT_COLOR_MODEL_FACTORY, ColorModelFactory.class); /** * Key for enabling changing of destination image's ColorModel * to a ComponentColorModel, when the source image has * an IndexColorModel. The corresponding object must be * a Boolean. If the source image has an * IndexColorModel, and the user or the operation itself * does not set the ColorModel of the destination image, * the destination image's ColorModel would be copied * from the source and would therefore also be an * IndexColorModel. A Boolean.TRUE value * set for this key causes the destination image's * ColorModel to be changed to a * ComponentColorModel. The advantage of changing the * destination's ColorModel comes in the usage of * RasterAccessor. When a RasterAccessor * is created using this source and destination pair, the source * IndexColorModel will be automatically expanded, * allowing operations that depend on the pixel value (as opposed * to the index into the ColorModel) to function correctly. * * Note that the JAI provided dithering operations * (errordiffusion, ordereddither) along with * the color quantization operator, colorquantizer can be * used for the inverse operation, i.e. converting from an RGB image * to an indexed image. * * The common RenderingHints do not contain a default * hint corresponding to this key which is equivalent to its being * set to FALSE. Certain operators, however, change the * default for themselves to Boolean.TRUE. * * @see javax.media.jai.OpImage#OpImage * @see javax.media.jai.RasterAccessor * @see javax.media.jai.operator.ColorQuantizerDescriptor * * @since JAI 1.1.2 */ public static RenderingHints.Key KEY_REPLACE_INDEX_COLOR_MODEL = new RenderingKey(HINT_REPLACE_INDEX_COLOR_MODEL, Boolean.class); /** * Key for TileFactory object values. * The common RenderingHints contain a * {@link RecyclingTileFactory}-valued hint corresponding * to this key. The value is the same as that to which * {@link #KEY_TILE_RECYCLER} is initially mapped. * * @see PlanarImage#PlanarImage(ImageLayout,Vector,Map) * @see OpImage#OpImage(Vector,ImageLayout,Map,boolean) * * @since JAI 1.1.2 */ public static RenderingHints.Key KEY_TILE_FACTORY = new RenderingKey(HINT_TILE_FACTORY, TileFactory.class); /** * Key for TileRecycler object values. * The common RenderingHints contain a * {@link RecyclingTileFactory}-valued hint corresponding * to this key. The value is the same as that to which * {@link #KEY_TILE_FACTORY} is initially mapped. * * @see OpImage#OpImage(Vector,ImageLayout,Map,boolean) * * @since JAI 1.1.2 */ public static RenderingHints.Key KEY_TILE_RECYCLER = new RenderingKey(HINT_TILE_RECYCLER, TileRecycler.class); /** * Key for Boolean object values which specify * whether automatic recycling of application-visible tiles * should occur. The common RenderingHints contain * a FALSE-valued hint corresponding to this key. * * @see OpImage#OpImage(Vector,ImageLayout,Map,boolean) * * @since JAI 1.1.2 */ public static RenderingHints.Key KEY_CACHED_TILE_RECYCLING_ENABLED = new RenderingKey(HINT_CACHED_TILE_RECYCLING_ENABLED, Boolean.class); /** * Key for specifying whether a deep copy of the image data should * be used when serializing images. The corresponding * object must be a Boolean. * The common RenderingHints do not contain a default * hint corresponding to this key. * * @since JAI 1.1 */ public static RenderingHints.Key KEY_SERIALIZE_DEEP_COPY = new RenderingKey(HINT_SERIALIZE_DEEP_COPY, Boolean.class); /** * Key for specifying the default format to be used for tile * serialization via TileCodecs. The corresponding * object must be a String. * The common RenderingHints do not contain a default * hint corresponding to this key. * * @since JAI 1.1 */ public static RenderingHints.Key KEY_TILE_CODEC_FORMAT = new RenderingKey(HINT_TILE_CODEC_FORMAT, String.class); /** * Key for specifying the default encoding parameters to be used for * tile serialization via TileCodecs. The corresponding * object must be a TileCodecParameterList. * The common RenderingHints do not contain a default * hint corresponding to this key. * * @since JAI 1.1 */ public static RenderingHints.Key KEY_TILE_ENCODING_PARAM = new RenderingKey(HINT_TILE_ENCODING_PARAM, TileCodecParameterList.class); /** * Key for specifying the default decoding parameters to be used for * tile serialization via TileCodecs. The corresponding * object must be a TileCodecParameterList. * The common RenderingHints do not contain a default * hint corresponding to this key. * * @since JAI 1.1 */ public static RenderingHints.Key KEY_TILE_DECODING_PARAM = new RenderingKey(HINT_TILE_DECODING_PARAM, TileCodecParameterList.class); /** * Key for the retry interval value to be used for dealing with * network errors during remote imaging. The corresponding * object must be an Integer. * The common RenderingHints do not contain a default * hint corresponding to this key. * * @see javax.media.jai.remote.RemoteJAI * * @since JAI 1.1 */ public static RenderingHints.Key KEY_RETRY_INTERVAL = new RenderingKey(HINT_RETRY_INTERVAL, Integer.class); /** * Key for the number of retries to be used for dealing with * network errors during remote imaging. The corresponding * object must be an Integer. * The common RenderingHints do not contain a default * hint corresponding to this key. * * @see javax.media.jai.remote.RemoteJAI * * @since JAI 1.1 */ public static RenderingHints.Key KEY_NUM_RETRIES = new RenderingKey(HINT_NUM_RETRIES, Integer.class); /** * Key for the negotiation preferences to be used to negotiate * capabilities to be used in the remote communication. The * corresponding object must be a NegotiableCapabilitySet. * The common RenderingHints do not contain a default * hint corresponding to this key. * * @see javax.media.jai.remote.RemoteJAI * * @since JAI 1.1 */ public static RenderingHints.Key KEY_NEGOTIATION_PREFERENCES = new RenderingKey(HINT_NEGOTIATION_PREFERENCES, NegotiableCapabilitySet.class); /** * Key that indicates whether the {@link ColormapOpImage}s do * the transform on the color map or on the pixels when the source * image and destination images are all color-indexed. The * corresponding object must be a Boolean. The * common RenderingHints do not contain a default * hint corresponding to this key. The default behavior is * equivalent to setting a hint with a value of * Boolean.TRUE. * * @since JAI 1.1.2 */ public static RenderingHints.Key KEY_TRANSFORM_ON_COLORMAP = new RenderingKey(HINT_TRANSFORM_ON_COLORMAP, Boolean.class); /** * Key for the {@link ImagingListener} registered to a rendering node. * The default mapping of this key in each JAI instance rethrows * RuntimeExceptions and prints the error message and * stack trace of other exceptions to System.err. * * @since JAI 1.1.2 */ public static RenderingHints.Key KEY_IMAGING_LISTENER = new RenderingKey(HINT_IMAGING_LISTENER, ImagingListener.class); /** * Initial default tile size. Applies to both dimensions. */ private static final int DEFAULT_TILE_SIZE = 512; /** * Default tile size. Null signifies no default. */ private static Dimension defaultTileSize = new Dimension(DEFAULT_TILE_SIZE, DEFAULT_TILE_SIZE); /** * Default RenderableOp rendering size. * Null signifies no default. */ private static Dimension defaultRenderingSize = new Dimension(0, 512); private OperationRegistry operationRegistry; private TileScheduler tileScheduler; private TileCache tileCache; private RenderingHints renderingHints; /** * A ImagingListener to listen and/or process the special * situations in the operations registered in this * JAI. * * @since JAI 1.1.2 */ private ImagingListener imagingListener = ImagingListenerImpl.getInstance(); private static JAI defaultInstance = new JAI(OperationRegistry.initializeRegistry(), new SunTileScheduler(), new SunTileCache(), new RenderingHints(null)); /** Returns a new instance of the JAI class. */ private JAI(OperationRegistry operationRegistry, TileScheduler tileScheduler, TileCache tileCache, RenderingHints renderingHints) { this.operationRegistry = operationRegistry; this.tileScheduler = tileScheduler; this.tileCache = tileCache; this.renderingHints = renderingHints; this.renderingHints.put(KEY_OPERATION_REGISTRY, operationRegistry); this.renderingHints.put(KEY_TILE_CACHE, tileCache); this.renderingHints.put(KEY_TILE_SCHEDULER, tileScheduler); TileFactory rtf = new RecyclingTileFactory(); this.renderingHints.put(KEY_TILE_FACTORY, rtf); this.renderingHints.put(KEY_TILE_RECYCLER, rtf); this.renderingHints.put(KEY_CACHED_TILE_RECYCLING_ENABLED, Boolean.FALSE); this.renderingHints.put(KEY_IMAGING_LISTENER, imagingListener); } /** * Returns JAI version information as a String * * @since JAI 1.1 */ public static final String getBuildVersion() { try { InputStream is = JAI.class.getResourceAsStream("buildVersion"); if (is == null) is = PropertyUtil.getFileFromClasspath("javax/media/jai/buildVersion"); BufferedReader reader = new BufferedReader(new InputStreamReader(is)); StringWriter sw = new StringWriter(); BufferedWriter writer = new BufferedWriter(sw); String str; boolean append = false; while ((str = reader.readLine()) != null) { if (append) writer.newLine(); writer.write(str); append = true; } writer.close(); return sw.getBuffer().toString(); } catch (Exception e) { return JaiI18N.getString("JAI13"); } } /** * Disable use of default tile cache. Tiles are not stored. * * @since JAI 1.1 */ public static final void disableDefaultTileCache() { TileCache tmp = defaultInstance.getTileCache(); if ( tmp != null ) { tmp.flush(); } defaultInstance.renderingHints.remove(KEY_TILE_CACHE); } /** * Enable use of default tile cache. Tiles are stored. * * @since JAI 1.1 */ public static final void enableDefaultTileCache() { defaultInstance.renderingHints.put(KEY_TILE_CACHE, defaultInstance.getTileCache()); } /** * Sets the default tile dimensions to the clone of the provided parameter. * If null there are no default dimensions. * * @param tileDimensions The default tile dimensions or null. * * @throws IllegalArgumentException if * tileDimensions is non-null and * has non-positive width or height. * * @since JAI 1.1 */ public static final void setDefaultTileSize(Dimension tileDimensions) { if(tileDimensions != null && (tileDimensions.width <= 0 || tileDimensions.height <= 0)) { throw new IllegalArgumentException(); } defaultTileSize = tileDimensions != null ? (Dimension)tileDimensions.clone() : null; } /** * Retrieves the clone of the default tile dimensions. * If null there are no default dimensions set. * * @return The default tile dimensions or null. * * @since JAI 1.1 */ public static final Dimension getDefaultTileSize() { return defaultTileSize != null ? (Dimension)defaultTileSize.clone() : null; } /** * Sets the default size of the rendering created by invoking * createDefaultRendering() on a RenderableOp. * This default size may be overruled by setting a hint with key * KEY_DEFAULT_RENDERING_SIZE on the node. * If null there are no default dimensions. * Either dimension may be non-positive in which case the other * dimension and the renderable aspect ratio will be used to compute * the rendered image size. The intial value of this setting is *
           * new Dimension(0, 512)
           * 
      * which produces a default rendering of height 512 and width * 512*aspect_ratio. * * @param defaultSize The default rendering size or null. * * @throws IllegalArgumentException if * defaultSize is non-null and * both the width and height are non-positive. * * @since JAI 1.1 */ public static final void setDefaultRenderingSize(Dimension defaultSize) { if(defaultSize != null && defaultSize.width <= 0 && defaultSize.height <= 0) { throw new IllegalArgumentException(JaiI18N.getString("JAI8")); } defaultRenderingSize = defaultSize == null ? null : new Dimension(defaultSize); } /** * Retrieves a copy of the default rendering size. * If null there is no default size set. * * @return The default rendering size or null. * * @since JAI 1.1 */ public static final Dimension getDefaultRenderingSize(){ return defaultRenderingSize == null ? null : new Dimension(defaultRenderingSize); } /** * Returns the defaultJAIinstance. This instance is used * by all of the static methods of this class. It uses the default * OperationRegistry and, in the Sun Microsystems, Inc. * implementation, the Sun implementations of TileCache and * TileScheduler. The RenderingHints will * contain hints only for these three entities. * *

      Unless otherwise changed through a setOperationRegistry * the OperationRegistry used by the default * instance is thread-safe. */ public static JAI getDefaultInstance() { return defaultInstance; } /** * Merge one RenderingHints into another. * * @param defaultHints The default RenderingHints. * @param hints The superseding RenderingHints; hints in * this mapping take precedence over any in * defaultHints. */ static RenderingHints mergeRenderingHints(RenderingHints defaultHints, RenderingHints hints) { RenderingHints mergedHints; if (hints == null || hints.isEmpty()) { mergedHints = defaultHints; } else if(defaultHints == null || defaultHints.isEmpty()) { mergedHints = hints; } else { // Both parameters are non-null and non-empty. mergedHints = new RenderingHints((Map)defaultHints); mergedHints.add(hints); } return mergedHints; } /** * Returns a new instance of theJAIclass. The * OperationRegistry, TileScheduler, and * TileCache will initially be references to those of * the default instance. The rendering hints will be set to a * clone of those of the default instance. */ public JAI() { this.operationRegistry = defaultInstance.operationRegistry; this.tileScheduler = defaultInstance.tileScheduler; this.tileCache = defaultInstance.tileCache; this.renderingHints = (RenderingHints)defaultInstance.renderingHints.clone(); } /** * Returns theOperationRegistry being used by * thisJAIinstance. * *

      Unless otherwise changed through a setOperationRegistry * the OperationRegistry returned by * getDefaultInstance().getOperationRegistry() is thread-safe. */ public OperationRegistry getOperationRegistry() { return operationRegistry; } /** * Sets theOperationRegistry to be used by thisJAIinstance. * *@throws IllegalArgumentException if operationRegistry is null. */ public void setOperationRegistry(OperationRegistry operationRegistry) { if (operationRegistry == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.operationRegistry = operationRegistry; this.renderingHints.put(KEY_OPERATION_REGISTRY, operationRegistry); } /** Returns the TileScheduler being used by thisJAIinstance. */ public TileScheduler getTileScheduler() { return tileScheduler; } /** * Sets the TileScheduler to be used by thisJAI * instance. The * tileScheduler parameter will be added to the * RenderingHints of this JAI instance. * @throws IllegalArgumentException if tileScheduler is null. */ public void setTileScheduler(TileScheduler tileScheduler) { if (tileScheduler == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.tileScheduler = tileScheduler; renderingHints.put(KEY_TILE_SCHEDULER, tileScheduler); } /** Returns the TileCache being used by thisJAIinstance. */ public TileCache getTileCache() { return tileCache; } /** * Sets the TileCache to be used by thisJAI * instance. The * tileCache parameter will be added to the * RenderingHints of this JAI instance. * * @throws IllegalArgumentException if tileCache is null. */ public void setTileCache(TileCache tileCache) { if (tileCache == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.tileCache = tileCache; renderingHints.put(KEY_TILE_CACHE, tileCache); } /** * The default implementation constructs a TileCache * with the given memory capacity in bytes. Users may supply an * instance of TileCache to an operation by * supplying a RenderingHint with a * JAI.KEY_TILE_CACHE key and the desired TileCache * instance as its value. Note that the absence of a tile cache * hint will result in the use of the TileCache * belonging to the defaultJAIinstance. To force an operation * not to perform caching, a TileCache instance with * a tile capacity of 0 may be used. * An exception will be thrown if memCapacity is negative. * Attempting to set either value larger than the JVM size may result in an * OutOfMemory exception. * * @deprecated as of JAI 1.1 Refer to createTileCache(long memCapacity). */ public static TileCache createTileCache(int tileCapacity, long memCapacity) { if (memCapacity < 0) { throw new IllegalArgumentException(JaiI18N.getString("JAI10")); } return new SunTileCache(memCapacity); } /** * Constructs a TileCache with the given memory capacity * in bytes. Users may supply an instance of TileCache * to an operation by supplying a RenderingHint with a * JAI.KEY_TILE_CACHE key and the desired TileCache * instance as its value. Note that the absence of a tile cache * hint will result in the use of the TileCache * belonging to the defaultJAIinstance. To force an operation * not to perform caching, a TileCache instance with * a tile capacity of 0 may be used. * An exception will be thrown if memCapacity is negative. * Attempting to set either value larger than the JVM size may result in an * OutOfMemory exception. * * @since JAI 1.1 */ public static TileCache createTileCache(long memCapacity) { if (memCapacity < 0) { throw new IllegalArgumentException(JaiI18N.getString("JAI10")); } return new SunTileCache(memCapacity); } /** * Constructs a TileCache with the default memory * capacity in bytes. Users may supply an instance of * TileCache to an operation by * supplying a RenderingHint with a * JAI.KEY_TILE_CACHE key and the desired TileCache * instance as its value. Note that the absence of a tile cache * hint will result in the use of the TileCache * belonging to the defaultJAIinstance. To force an operation * not to perform caching, a TileCache instance with * a tile capacity of 0 may be used. */ public static TileCache createTileCache() { return new SunTileCache(); } /** * Constructs a TileScheduler with the default parallelism * and priorities. * *

      In the Sun Microsystems reference implementation of TileScheduler * the default parallelism is 2, default priority is * THREAD.NORM_PRIORITY, default prefetch parallelism is 1, * and default prefetch priority is THREAD.MIN_PRIORITY. * * @since JAI 1.1 */ public static TileScheduler createTileScheduler() { return new SunTileScheduler(); } // Create methods for Rendered mode. /** * Creates a RenderedOp which represents the named * operation, using the source(s) and/or parameter(s) specified in * the ParameterBlock, and applying the specified * hints to the destination. This method should only be used when * the final result returned is a single * RenderedImage. * *

      The defaultJAIinstance is used as the source of the * registry and tile scheduler; that is, this method is equivalent * to getDefaultInstance().createNS(opName, args, hints). * The functionality of this method is the same as its corresponding * non-static method createNS(). * * @param opName The name of the operation. * @param args The source(s) and/or parameter(s) for the operation. * @param hints The hints for the operation. * * @throws IllegalArgumentException if opName is null. * @throws IllegalArgumentException if args is null. * @throws IllegalArgumentException if no * OperationDescriptor is registered under the * specified operation name in the default operation registry. * @throws IllegalArgumentException if the * OperationDescriptor registered under the specified * operation name in the default operation registry does not * support rendered image mode. * @throws IllegalArgumentException if the specified operation does * not produce a * java.awt.image.RenderedImage. * @throws IllegalArgumentException if the specified operation is * unable to handle the sources and parameters specified in * args. * * @return A RenderedOp that represents the named * operation, or null if the specified operation * is in the "immediate" mode and the rendering of the * PlanarImage failed. */ public static RenderedOp create(String opName, ParameterBlock args, RenderingHints hints) { return defaultInstance.createNS(opName, args, hints); } /** * Creates a RenderedOp which represents the named * operation, using the source(s) and/or parameter(s) specified in * the ParameterBlock, and applying the specified * hints to the destination. This method should only be used when * the final result returned is a single * RenderedImage. However, the source(s) supplied * may be a collection of rendered images or a collection of * collections that at the very basic level include rendered * images. * *

      The supplied operation name is validated against the * operation registry. The source(s) and/or parameter(s) in the * ParameterBlock are validated against the named * operation's descriptor, both in their numbers and types. * Additional restrictions placed on the sources and parameters * by an individual operation are also validated by calling its * OperationDescriptor.validateArguments() method. * *

      JAIallows a parameter to have a null input * value, if that particular parameter has a default value specified * in its operation's descriptor. In this case, the default value * will replace the null input. * *

      JAIalso allows unspecified tailing parameters, if these * parameters have default values specified in the operation's * descriptor. However, if a parameter, which has a default value, * is followed by one or more parameters that * have no default values, this parameter must be specified in the * ParameterBlock, even if it only has a value of * code>null. * *

      The rendering hints associated with this instance of * JAI are overlaid with the hints passed to this * method. That is, the set of keys will be the union of the * keys from the instance's hints and the hints parameter. * If the same key exists in both places, the value from the * hints parameter will be used. * *

      This version of create is non-static; it may * be used with a specific instance of theJAIclass. * All of the static create() methods ultimately call this * method, thus inheriting this method's error handling. * *

      Since this method performs parameter checking, it may not * be suitable for creating RenderedOp nodes meant to * be passed to another host using the RemoteImage * interface. For example, it might be necessary to refer to a * file that is present only on the remote host. In such cases, * it is possible to instantiate a RenderedOp * directly, avoiding all checks. * * @param opName The name of the operation. * @param args The source(s) and/or parameter(s) for the operation. * @param hints The hints for the operation. * * @throws IllegalArgumentException if opName is null. * @throws IllegalArgumentException if args is null. * @throws IllegalArgumentException if no * OperationDescriptor is registered under the * specified operation name in the current operation registry. * @throws IllegalArgumentException if the * OperationDescriptor registered under the specified * operation name in the current operation registry does not * support rendered image mode. * @throws IllegalArgumentException if the specified operation does * not produce a * java.awt.image.RenderedImage. * @throws IllegalArgumentException if the specified operation is * unable to handle the sources and parameters specified in * args. * * @return A RenderedOp that represents the named * operation, or null if the specified operation * is in the "immediate" mode and the rendering of the * PlanarImage failed. */ public RenderedOp createNS(String opName, ParameterBlock args, RenderingHints hints) { if(opName == null) { throw new IllegalArgumentException(JaiI18N.getString("JAI14")); } else if (args == null) { throw new IllegalArgumentException(JaiI18N.getString("JAI15")); } String modeName = "rendered"; // Get the OperationDescriptor registered under the specified name. OperationDescriptor odesc = (OperationDescriptor) operationRegistry.getDescriptor(modeName, opName); if (odesc == null) { throw new IllegalArgumentException(opName + ": " + JaiI18N.getString("JAI0")); } if (!RenderedImage.class.isAssignableFrom(odesc.getDestClass(modeName))) { throw new IllegalArgumentException(opName + ": " + JaiI18N.getString("JAI2")); } // Validate input arguments. The ParameterBlock is cloned here // because OperationDescriptor.validateArguments() may change // its content. StringBuffer msg = new StringBuffer(); args = (ParameterBlock)args.clone(); if (!odesc.validateArguments(modeName, args, msg)) { throw new IllegalArgumentException(msg.toString()); } // Merge rendering hints. Hints passed in take precedence. RenderingHints mergedHints = mergeRenderingHints(renderingHints, hints); RenderedOp op = new RenderedOp(operationRegistry, opName, args, mergedHints); // If the operation requests immediate rendering, do so. if (odesc.isImmediate()) { PlanarImage im = null; im = op.getRendering(); if (im == null) { // Op could not be rendered, return null. return null; } } // Return the RenderedOp associated with this operation. return op; } /** * Creates a Collection which represents the named * operation, using the source(s) and/or parameter(s) specified in * the ParameterBlock, and applying the specified * hints to the destination. This method should only be used when * the final result returned is a Collection. (This * includes javax.media.jai.CollectionOps.) * *

      The defaultJAIinstance is used as the source of the * registry and tile scheduler; that is, this method is equivalent * to getDefaultInstance().createCollectionNS(opName, args, * hints). The functionality of this method is the same as * its corresponding non-static method createCollectionNS(). * * @param opName The name of the operation. * @param args The source(s) and/or parameter(s) for the operation. * @param hints The hints for the operation. * * @throws IllegalArgumentException if opName is null. * @throws IllegalArgumentException if args is null. * @throws IllegalArgumentException if no * OperationDescriptor is registered under the * specified operation name in the default operation registry. * @throws IllegalArgumentException if the * OperationDescriptor registered under the specified * operation name in the default operation registry does not * support rendered image mode. * @throws IllegalArgumentException if the specified operation does * not produce a * java.awt.image.RenderedImage or a * javax.media.jai.CollectionImage. * @throws IllegalArgumentException if the specified operation is * unable to handle the sources and parameters specified in * args. * * @return A Collection that represents the named * operation, or null if the specified operation * is in the "immediate" mode and the rendering of the * PlanarImage failed. */ public static Collection createCollection(String opName, ParameterBlock args, RenderingHints hints) { return defaultInstance.createCollectionNS(opName, args, hints); } /** * Creates a Collection which represents the named * operation, using the source(s) and/or parameter(s) specified in * the ParameterBlock, and applying the specified * hints to the destination. This method should only be used when * the final result returned is a Collection. (This * includes javax.media.jai.CollectionOps.) The * source(s) supplied may be a collection of rendered images or a * collection of collections that at the very basic level include * rendered images. * The source(s) supplied are unwrapped to create a single collection * that contains RenderedOps and collections as many as the size of * the smallest collection supplied in the sources. The nth collection * is created using all supplied rendered images and the nth element of * each of the collections supplied in the source. * *

      The supplied operation name is validated against the * operation registry. The source(s) and/or parameter(s) in the * ParameterBlock are val>idated against the named * operation's descriptor, both in their numbers and types. * Additional restrictions placed on the sources and parameters * by an individual operation are also validated by calling its * OperationDescriptor.validateArguments() method. * *

      JAIallows a parameter to have a null input * value, if that particular parameter has a default value specified * in its operation's descriptor. In this case, the default value * will replace the null input. * *

      JAIalso allows unspecified tailing parameters, if these * parameters have default values specified in the operation's * descriptor. However, if a parameter, which * has a default value, is followed by one or more parameters that * have no default values, this parameter must be specified in the * ParameterBlock, even if it only has a value of * code>null. * *

      The rendering hints associated with this instance of * JAI are overlaid with the hints passed to this * method. That is, the set of keys will be the union of the * keys from the instance's hints and the hints parameter. * If the same key exists in both places, the value from the * hints parameter will be used. * *

      This version of createCollection is * non-static; it may be used with a specific instance of the JAI * class. * * @param opName The name of the operation. * @param args The source(s) and/or parameter(s) for the operation. * @param hints The hints for the operation. * * @throws IllegalArgumentException if opName is null. * @throws IllegalArgumentException if args is null. * @throws IllegalArgumentException if no * OperationDescriptor is registered under the * specified operation name in the current operation registry. * @throws IllegalArgumentException if the * OperationDescriptor registered under the specified * operation name in the current operation registry does not * support rendered image mode. * @throws IllegalArgumentException if the specified operation does * not produce a * java.awt.image.RenderedImage or a * javax.media.jai.CollectionImage. * @throws IllegalArgumentException if the specified operation is * unable to handle the sources and parameters specified in * args. * * @return A Collection that represents the named * operation, or null if the specified operation * is in the "immediate" mode and the rendering of the * PlanarImage failed. */ public Collection createCollectionNS(String opName, ParameterBlock args, RenderingHints hints) { if(opName == null) { throw new IllegalArgumentException(JaiI18N.getString("JAI14")); } else if (args == null) { throw new IllegalArgumentException(JaiI18N.getString("JAI15")); } String modeName = "collection"; // Get the OperationDescriptor registered under the specified name. OperationDescriptor odesc = (OperationDescriptor) operationRegistry.getDescriptor(modeName, opName); if (odesc == null) { throw new IllegalArgumentException(opName + ": " + JaiI18N.getString("JAI0")); } Class destClass = odesc.getDestClass(modeName); if (!RenderedImage.class.isAssignableFrom(destClass) && !CollectionImage.class.isAssignableFrom(destClass)) { throw new IllegalArgumentException(opName + ": " + JaiI18N.getString("JAI5")); } // Merge rendering hints. Hints passed in take precedence. RenderingHints mergedHints = mergeRenderingHints(renderingHints, hints); // Validate input arguments. The ParameterBlock is cloned here // because OperationDescriptor.validateArguments() may change // its content. StringBuffer msg = new StringBuffer(); args = (ParameterBlock)args.clone(); if (odesc.validateArguments(modeName, args, msg)) { if (RenderedImage.class.isAssignableFrom(destClass)) { Vector v = new Vector(1); v.add(new RenderedOp(operationRegistry, opName, args, mergedHints)); return v; } else { CollectionOp cOp = new CollectionOp(operationRegistry, opName, args, mergedHints); // If the operation requests immediate rendering, do so. if (odesc.isImmediate()) { Collection coll = null; coll = cOp.getCollection(); if(coll == null) { return null; } } return cOp; } } else { int numSources = odesc.getNumSources(); Vector sources = args.getSources(); // Get the iterator of all the sources that are collection. // Get the iterator for the collection that has the least elements. Iterator[] iters = new Iterator[numSources]; Iterator iter = null; int size = Integer.MAX_VALUE; for (int i = 0; i < numSources; i++) { Object s = sources.elementAt(i); if (s instanceof Collection) { iters[i] = ((Collection)s).iterator(); if (iter == null || ((Collection)s).size() < size) { iter = iters[i]; size = ((Collection)s).size(); } } } if (iter == null) { // None of the sources is a collection. The error is // with the input arguments. throw new IllegalArgumentException(msg.toString()); } // Some sources are of type collection. Need to unwrap them. Collection col = null; for (int i = 0; i < numSources; i++) { Object s = sources.elementAt(i); if (s instanceof Collection) { try { col = (Collection)s.getClass().newInstance(); break; } catch (Exception e) { // Unable to create this collection type, try next. sendExceptionToListener( JaiI18N.getString("JAI16") + s.getClass().getName(), e); } } } if (col == null) { col = new Vector(); } // Get the source types. Class[] sourceClasses = odesc.getSourceClasses(modeName); while (iter.hasNext()) { ParameterBlock pb = new ParameterBlock(); pb.setParameters(args.getParameters()); for (int i = 0; i < numSources; i++) { // Get the next source. Object nextSource = null; if (iters[i] == null) { nextSource = sources.elementAt(i); } else { nextSource = iters[i].next(); } // If the source is not of a compatible type and // is not a Collection then the 'false' value // returned by validateArguments() above must indicate // a real error. if(!sourceClasses[i].isAssignableFrom(nextSource.getClass()) && !(nextSource instanceof Collection)) { throw new IllegalArgumentException(msg.toString()); } pb.addSource(nextSource); } Collection c = createCollectionNS(opName, pb, mergedHints); if (c instanceof Vector && c.size() == 1 && ((Vector)c).elementAt(0) instanceof RenderedOp) { col.add(((Vector)c).elementAt(0)); } else { col.add(c); } } return col; } } // Convenience create methods for rendered mode. /** * Creates a RenderedOp with null * rendering hints. * * @param opName The name of the operation. * @param args The source(s) and/or parameter(s) for the operation. */ public static RenderedOp create(String opName, ParameterBlock args) { return create(opName, args, null); } /** * Creates a RenderedOp that takes 1 Object parameter. * * @param opName The name of the operation. * @param param The Object parameter. */ public static RenderedOp create(String opName, Object param) { ParameterBlock args = new ParameterBlock(); args.add(param); return create(opName, args, null); } /** * Creates a RenderedOp that takes 2 Object parameters. * * @param opName The name of the operation. * @param param1 The first Object parameter. * @param param2 The second Object parameter. */ public static RenderedOp create(String opName, Object param1, Object param2) { ParameterBlock args = new ParameterBlock(); args.add(param1); args.add(param2); return create(opName, args, null); } /** * Creates a RenderedOp that takes 1 Object parameter and * 1 int parameter * * @param opName The name of the operation. * @param param1 The Object parameter. * @param param2 The int parameter. * @deprecated as of JAI 1.1. Instead use * create(String,ParameterBlock). */ public static RenderedOp create(String opName, Object param1, int param2) { ParameterBlock args = new ParameterBlock(); args.add(param1); args.add(param2); return create(opName, args, null); } /** * Creates a RenderedOp that takes 3 Object parameters. * * @param opName The name of the operation. * @param param1 The first Object parameter. * @param param2 The second Object parameter. * @param param3 The third Object parameter. * @deprecated as of JAI 1.1. Instead use * create(String,ParameterBlock). */ public static RenderedOp create(String opName, Object param1, Object param2, Object param3) { ParameterBlock args = new ParameterBlock(); args.add(param1); args.add(param2); args.add(param3); return create(opName, args, null); } /** * Creates a RenderedOp that takes 2 int parameters * and one Object parameter * * @param opName The name of the operation. * @param param1 The first int parameter. * @param param2 The second int parameter. * @param param3 The Object parameter. * @deprecated as of JAI 1.1. Instead use * create(String,ParameterBlock). */ public static RenderedOp create(String opName, int param1, int param2, Object param3) { ParameterBlock args = new ParameterBlock(); args.add(param1); args.add(param2); args.add(param3); return create(opName, args, null); } /** * Creates a RenderedOp that takes 4 Object parameters. * * @param opName The name of the operation. * @param param1 The first Object parameter. * @param param2 The second Object parameter. * @param param3 The third Object parameter. * @param param4 The fourth Object parameter. * @deprecated as of JAI 1.1. Instead use * create(String,ParameterBlock). */ public static RenderedOp create(String opName, Object param1, Object param2, Object param3, Object param4) { ParameterBlock args = new ParameterBlock(); args.add(param1); args.add(param2); args.add(param3); args.add(param4); return create(opName, args, null); } /** * Creates a RenderedOp that takes 2 Object and 2 int parameters. * * @param opName The name of the operation. * @param param1 The first Object parameter. * @param param2 The first int parameter. * @param param3 The second Object parameter. * @param param4 The second int parameter. * @deprecated as of JAI 1.1. Instead use * create(String,ParameterBlock). */ public static RenderedOp create(String opName, Object param1, int param2, Object param3, int param4) { ParameterBlock args = new ParameterBlock(); args.add(param1); args.add(param2); args.add(param3); args.add(param4); return create(opName, args, null); } /** * Creates a RenderedOp that takes 1 RenderedImage source. * * @param opName The name of the operation. * @param src The RenderedImage src parameter. */ public static RenderedOp create(String opName, RenderedImage src) { ParameterBlock args = new ParameterBlock(); args.addSource(src); return create(opName, args, null); } /** * Creates a RenderedOp that takes 1 Collection source. * * @param opName The name of the operation. * @param srcCol The Collection src parameter. * @deprecated as of JAI 1.1. Instead use * create(String,ParameterBlock). */ public static RenderedOp create(String opName, Collection srcCol) { ParameterBlock args = new ParameterBlock(); args.addSource(srcCol); return create(opName, args, null); } /** * Creates a RenderedOp that takes 1 * RenderedImage source and * 1 Object parameter. * * @param opName The name of the operation. * @param src The RenderedImage src parameter. * @param param The Object parameter. */ public static RenderedOp create(String opName, RenderedImage src, Object param) { ParameterBlock args = new ParameterBlock(); args.addSource(src); args.add(param); return create(opName, args, null); } /** * Creates a RenderedOp that takes 1 * RenderedImage source and * 1 int parameter. * * @param opName The name of the operation. * @param src The RenderedImage src parameter. * @param param The int parameter. * @deprecated as of JAI 1.1. Instead use * create(String,ParameterBlock). */ public static RenderedOp create(String opName, RenderedImage src, int param) { ParameterBlock args = new ParameterBlock(); args.addSource(src); args.add(param); return create(opName, args, null); } /** * Creates a RenderedOp that takes 1 * RenderedImage source * and 2 Object parameters. * * @param opName The name of the operation. * @param src The RenderedImage src parameter. * @param param1 The first object parameter. * @param param2 The second Object parameter. */ public static RenderedOp create(String opName, RenderedImage src, Object param1, Object param2) { ParameterBlock args = new ParameterBlock(); args.addSource(src); args.add(param1); args.add(param2); return create(opName, args, null); } /** * Creates a RenderedOp that takes 1 * RenderedImage source, * 1 Object and 1 float parameter. * * @param opName The name of the operation. * @param src The RenderedImage src parameter. * @param param1 The Object parameter. * @param param2 The float parameter. * @deprecated as of JAI 1.1. Instead use * create(String,ParameterBlock). */ public static RenderedOp create(String opName, RenderedImage src, Object param1, float param2) { ParameterBlock args = new ParameterBlock(); args.addSource(src); args.add(param1); args.add(param2); return create(opName, args, null); } /** * Creates a RenderedOp that takes 1 * RenderedImage source * and 3 Object parameters. * * @param opName The name of the operation. * @param src The RenderedImage src parameter. * @param param1 The first Object parameter. * @param param2 The second Object parameter. * @param param3 The third Object parameter. */ public static RenderedOp create(String opName, RenderedImage src, Object param1, Object param2, Object param3) { ParameterBlock args = new ParameterBlock(); args.addSource(src); args.add(param1); args.add(param2); args.add(param3); return create(opName, args, null); } /** * Creates a RenderedOp that takes 1 * RenderedImage source, * 1 Object and 2 int parameters. * * @param opName The name of the operation. * @param src The RenderedImage src parameter. * @param param1 The Object parameter. * @param param2 The first int parameter. * @param param3 The second int parameter. * @deprecated as of JAI 1.1. Instead use * create(String,ParameterBlock). */ public static RenderedOp create(String opName, RenderedImage src, Object param1, int param2, int param3) { ParameterBlock args = new ParameterBlock(); args.addSource(src); args.add(param1); args.add(param2); args.add(param3); return create(opName, args, null); } /** * Creates a RenderedOp that takes 1 * RenderedImage source, * 2 float and 1 Object parameters. * * @param opName The name of the operation. * @param src The RenderedImage src parameter. * @param param1 The first float parameter. * @param param2 The second float parameter. * @param param3 The Object parameter. * @deprecated as of JAI 1.1. Instead use * create(String,ParameterBlock). */ public static RenderedOp create(String opName, RenderedImage src, float param1, float param2, Object param3) { ParameterBlock args = new ParameterBlock(); args.addSource(src); args.add(param1); args.add(param2); args.add(param3); return create(opName, args, null); } /** * Creates a RenderedOp that takes 1 * RenderedImage source and * 4 Object parameters. * * @param opName The name of the operation. * @param src The RenderedImage src parameter. * @param param1 The first Object parameter. * @param param2 The second Object parameter. * @param param3 The third Object parameter. * @param param4 The fourth Object parameter. * @deprecated as of JAI 1.1. Instead use * create(String,ParameterBlock). */ public static RenderedOp create(String opName, RenderedImage src, Object param1, Object param2, Object param3, Object param4) { ParameterBlock args = new ParameterBlock(); args.addSource(src); args.add(param1); args.add(param2); args.add(param3); args.add(param4); return create(opName, args, null); } /** * Creates a RenderedOp that takes 1 * RenderedImage source and * 2 Object parameters and 2 int parameters * * @param opName The name of the operation. * @param src The RenderedImage src parameter. * @param param1 The first Object parameter. * @param param2 The second Object parameter. * @param param3 The first int parameter. * @param param4 The second int parameter. * @deprecated as of JAI 1.1. Instead use * create(String,ParameterBlock). */ public static RenderedOp create(String opName, RenderedImage src, Object param1, Object param2, int param3, int param4) { ParameterBlock args = new ParameterBlock(); args.addSource(src); args.add(param1); args.add(param2); args.add(param3); args.add(param4); return create(opName, args, null); } /** * Creates a RenderedOp that takes 1 * RenderedImage source and * 4 int parameters. * * @param opName The name of the operation. * @param src The RenderedImage src parameter. * @param param1 The first int parameter. * @param param2 The second int parameter. * @param param3 The third int parameter. * @param param4 The fourth int parameter. * @deprecated as of JAI 1.1. Instead use * create(String,ParameterBlock). */ public static RenderedOp create(String opName, RenderedImage src, int param1, int param2, int param3, int param4) { ParameterBlock args = new ParameterBlock(); args.addSource(src); args.add(param1); args.add(param2); args.add(param3); args.add(param4); return create(opName, args, null); } /** * Creates a RenderedOp that takes 1 * RenderedImage source, * 3 float and 1 Object parameters. * * @param opName The name of the operation. * @param src The RenderedImage src parameter. * @param param1 The first float parameter. * @param param2 The second float parameter. * @param param3 The third float parameter. * @param param4 The Object parameter. * @deprecated as of JAI 1.1. Instead use * create(String,ParameterBlock). */ public static RenderedOp create(String opName, RenderedImage src, float param1, float param2, float param3, Object param4) { ParameterBlock args = new ParameterBlock(); args.addSource(src); args.add(param1); args.add(param2); args.add(param3); args.add(param4); return create(opName, args, null); } /** * Creates a RenderedOp that takes 1 * RenderedImage source and * 5 Object parameters. * * @param opName The name of the operation. * @param src The RenderedImage src parameter. * @param param1 The first Object parameter. * @param param2 The second Object parameter. * @param param3 The third Object parameter. * @param param4 The fourth Object parameter. * @param param5 The fifth Object parameter. * @deprecated as of JAI 1.1. Instead use * create(String,ParameterBlock). */ public static RenderedOp create(String opName, RenderedImage src, Object param1, Object param2, Object param3, Object param4, Object param5) { ParameterBlock args = new ParameterBlock(); args.addSource(src); args.add(param1); args.add(param2); args.add(param3); args.add(param4); args.add(param5); return create(opName, args, null); } /** * Creates a RenderedOp that takes 1 RenderedImage source, * 4 float parameters and one Object parameter. * * @param opName The name of the operation. * @param src The RenderedImage src parameter. * @param param1 The first float parameter. * @param param2 The second float parameter. * @param param3 The third float parameter. * @param param4 The fourth float parameter. * @param param5 The Object parameter. * @deprecated as of JAI 1.1. Instead use * create(String,ParameterBlock). */ public static RenderedOp create(String opName, RenderedImage src, float param1, float param2, float param3, float param4, Object param5) { ParameterBlock args = new ParameterBlock(); args.addSource(src); args.add(param1); args.add(param2); args.add(param3); args.add(param4); args.add(param5); return create(opName, args, null); } /** * Creates a RenderedOp that takes 1 * RenderedImage source, * 3 float parameters, 1 int parameter and 1 Object parameter. * * @param opName The name of the operation. * @param src The RenderedImage src parameter. * @param param1 The first float parameter. * @param param2 The int parameter. * @param param3 The second float parameter. * @param param4 The third float parameter. * @param param5 The Object parameter. * @deprecated as of JAI 1.1. Instead use * create(String,ParameterBlock). */ public static RenderedOp create(String opName, RenderedImage src, float param1, int param2, float param3, float param4, Object param5) { ParameterBlock args = new ParameterBlock(); args.addSource(src); args.add(param1); args.add(param2); args.add(param3); args.add(param4); args.add(param5); return create(opName, args, null); } /** * Creates a RenderedOp that takes 1 * RenderedImage source and * 6 Object parameters. * * @param opName The name of the operation. * @param src The RenderedImage src parameter. * @param param1 The first Object parameter. * @param param2 The second Object parameter. * @param param3 The third Object parameter. * @param param4 The fourth Object parameter. * @param param5 The fifth Object parameter. * @param param6 The sixth Object parameter. * @deprecated as of JAI 1.1. Instead use * create(String,ParameterBlock). */ public static RenderedOp create(String opName, RenderedImage src, Object param1, Object param2, Object param3, Object param4, Object param5, Object param6) { ParameterBlock args = new ParameterBlock(); args.addSource(src); args.add(param1); args.add(param2); args.add(param3); args.add(param4); args.add(param5); args.add(param6); return create(opName, args, null); } /** * Creates a RenderedOp that takes 1 RenderedImage source, * 5 int parameters and 1 Object parameter. * * @param opName The name of the operation. * @param src The RenderedImage src parameter. * @param param1 The first int parameter. * @param param2 The second int parameter. * @param param3 The third int parameter. * @param param4 The fourth int parameter. * @param param5 The fifth int parameter. * @param param6 The Object parameter. * @deprecated as of JAI 1.1. Instead use * create(String,ParameterBlock). */ public static RenderedOp create(String opName, RenderedImage src, int param1, int param2, int param3, int param4, int param5, Object param6) { ParameterBlock args = new ParameterBlock(); args.addSource(src); args.add(param1); args.add(param2); args.add(param3); args.add(param4); args.add(param5); args.add(param6); return create(opName, args, null); } /** * Creates a RenderedOp that takes 2 * RenderedImage sources. * * @param opName The name of the operation. * @param src1 The first RenderedImage src. * @param src2 The second RenderedImage src. */ public static RenderedOp create(String opName, RenderedImage src1, RenderedImage src2) { ParameterBlock args = new ParameterBlock(); args.addSource(src1); args.addSource(src2); return create(opName, args, null); } /** * Creates a RenderedOp that takes 2 * RenderedImage sources and * 4 Object parameters. * * @param opName The name of the operation. * @param src1 The first RenderedImage src. * @param src2 The second RenderedImage src. * @param param1 The first Object parameter. * @param param2 The second Object parameter. * @param param3 The third Object parameter. * @param param4 The fourth Object parameter. * @deprecated as of JAI 1.1. Instead use * create(String,ParameterBlock). */ public static RenderedOp create(String opName, RenderedImage src1, RenderedImage src2, Object param1, Object param2, Object param3, Object param4) { ParameterBlock args = new ParameterBlock(); args.addSource(src1); args.addSource(src2); args.add(param1); args.add(param2); args.add(param3); args.add(param4); return create(opName, args, null); } /** * Creates a Collection with null * rendering hints. * * @param opName The name of the operation. * @param args The source(s) and/or parameter(s) for the operation. */ public static Collection createCollection(String opName, ParameterBlock args) { return createCollection(opName, args, null); } // Create methods for Renderable mode. /** * Creates a RenderableOp that represents the named * operation, using the source(s) and/or parameter(s) specified * in the ParameterBlock. This method should only * be used when the final result returned is a single * RenderdableImage. * *

      The defaultJAIinstance is used as the source of the * registry and tile scheduler; that is, this method is equivalent to * getDefaultInstance().createRenderableNS(opName, args, hints). * The functionality of this method is the same as its corresponding * non-static method createRenderableNS(). * * @param opName The name of the operation. * @param args The source(s) and/or parameter(s) for the operation. * @param hints The hints for the operation. * * @throws IllegalArgumentException if opName is null. * @throws IllegalArgumentException if args is null. * @throws IllegalArgumentException if no * OperationDescriptor is registered under the * specified operation name in the default operation registry. * @throws IllegalArgumentException if the * OperationDescriptor registered under the specified * operation name in the default operation registry does not * support renderable image mode. * @throws IllegalArgumentException if the specified operation does * not produce a * java.awt.image.renderable.RenderableImage. * @throws IllegalArgumentException if the specified operation is * unable to handle the sources and parameters specified in * args. * * @return A RenderableOp that represents the named * operation. * * @since JAI 1.1 */ public static RenderableOp createRenderable(String opName, ParameterBlock args, RenderingHints hints) { return defaultInstance.createRenderableNS(opName, args, hints); } /** * Creates a RenderableOp that represents the named * operation, using the source(s) and/or parameter(s) specified * in the ParameterBlock. This method should only * be used when the final result returned is a single * RenderdableImage. * *

      The defaultJAIinstance is used as the source of the * registry and tile scheduler; that is, this method is equivalent to * getDefaultInstance().createRenderableNS(opName, args, null). * The functionality of this method is the same as its corresponding * non-static method createRenderableNS(). * * @param opName The name of the operation. * @param args The source(s) and/or parameter(s) for the operation. * * @throws IllegalArgumentException if opName is null. * @throws IllegalArgumentException if args is null. * @throws IllegalArgumentException if no * OperationDescriptor is registered under the * specified operation name in the default operation registry. * @throws IllegalArgumentException if the * OperationDescriptor registered under the specified * operation name in the default operation registry does not * support renderable image mode. * @throws IllegalArgumentException if the specified operation does * not produce a * java.awt.image.renderable.RenderableImage. * @throws IllegalArgumentException if the specified operation is * unable to handle the sources and parameters specified in * args. * * @return A RenderableOp that represents the named * operation. */ public static RenderableOp createRenderable(String opName, ParameterBlock args) { return defaultInstance.createRenderableNS(opName, args, null); } /** * Creates a RenderableOp that represents the named * operation, using the source(s) and/or parameter(s) specified * in the ParameterBlock. This method should only * be used when the final result returned is a single * RenderableImage. However, the source(s) supplied * may be a collection of renderable images or a collection of * collections that at the very basic level include renderable * images. * *

      The supplied operation name is validated against the * operation registry. The source(s) and/or parameter(s) in the * ParameterBlock are validated against the named * operation's descriptor, both in their numbers and types. * Additional restrictions placed on the sources and parameters * by an individual operation are also validated by calling its * OperationDescriptor.validateRenderableArguments() * method. * *

      JAIallows a parameter to have a null input * value, if that particular parameter has a default value specified * in its operation's descriptor. In this case, the default value * will replace the null input. * *

      JAIalso allows unspecified tailing parameters, if these * parameters have default values specified in the operation's * descriptor. However, if a parameter, which * has a default value, is followed by one or more parameters that * have no default values, this parameter must be specified in the * ParameterBlock, even if it only has a value of * code>null. * *

      The rendering hints associated with this instance of * JAI are overlaid with the hints passed to this * method. That is, the set of keys will be the union of the * keys from the instance's hints and the hints parameter. * If the same key exists in both places, the value from the * hints parameter will be used. * *

      This version of the "createRenderable" is non-static; it * may be used with a specific instance of theJAIclass. * * @param opName The name of the operation. * @param args The source(s) and/or parameter(s) for the operation. * @param hints The hints for the operation. * * @throws IllegalArgumentException if opName is null. * @throws IllegalArgumentException if args is null. * @throws IllegalArgumentException if no * OperationDescriptor is registered under the * specified operation name in the current operation registry. * @throws IllegalArgumentException if the * OperationDescriptor registered under the specified * operation name in the current operation registry does not * support renderable image mode. * @throws IllegalArgumentException if the specified operation does * not produce a * java.awt.image.renderable.RenderableImage. * @throws IllegalArgumentException if the specified operation is * unable to handle the sources and parameters specified in * args. * * @return A RenderableOp that represents the named * operation. * * @since JAI 1.1 */ public RenderableOp createRenderableNS(String opName, ParameterBlock args, RenderingHints hints) { if(opName == null) { throw new IllegalArgumentException(JaiI18N.getString("JAI14")); } else if (args == null) { throw new IllegalArgumentException(JaiI18N.getString("JAI15")); } String modeName = "renderable"; // Get the OperationDescriptor registered under the specified name. OperationDescriptor odesc = (OperationDescriptor) operationRegistry.getDescriptor(modeName, opName); if (odesc == null) { throw new IllegalArgumentException(opName + ": " + JaiI18N.getString("JAI0")); } if (!RenderableImage.class.isAssignableFrom(odesc.getDestClass(modeName))) { throw new IllegalArgumentException(opName + ": " + JaiI18N.getString("JAI4")); } // Validate input arguments. The ParameterBlock is cloned here // because OperationDescriptor.validateRenderableArguments() // may change its content. StringBuffer msg = new StringBuffer(); args = (ParameterBlock)args.clone(); if (!odesc.validateArguments(modeName, args, msg)) { throw new IllegalArgumentException(msg.toString()); } // Create a RenderableOp. RenderableOp op = new RenderableOp(operationRegistry, opName, args, mergeRenderingHints(renderingHints, hints)); // Return the RenderableOp. return op; } /** * Creates a RenderableOp that represents the named * operation, using the source(s) and/or parameter(s) specified * in the ParameterBlock. This method should only * be used when the final result returned is a single * RenderableImage. However, the source(s) supplied * may be a collection of renderable images or a collection of * collections that at the very basic level include renderable * images. * *

      The supplied operation name is validated against the * operation registry. The source(s) and/or parameter(s) in the * ParameterBlock are validated against the named * operation's descriptor, both in their numbers and types. * Additional restrictions placed on the sources and parameters * by an individual operation are also validated by calling its * OperationDescriptor.validateRenderableArguments() * method. * *

      JAIallows a parameter to have a null input * value, if that particular parameter has a default value specified * in its operation's descriptor. In this case, the default value * will replace the null input. * *

      JAIalso allows unspecified tailing parameters, if these * parameters have default values specified in the operation's * descriptor. However, if a parameter, which * has a default value, is followed by one or more parameters that * have no default values, this parameter must be specified in the * ParameterBlock, even if it only has a value of * code>null. * *

      The rendering hints associated with this instance of * JAI are overlaid with the hints passed to this * method. That is, the set of keys will be the union of the * keys from the instance's hints and the hints parameter. * If the same key exists in both places, the value from the * hints parameter will be used. * *

      This version of the "createRenderable" is non-static; it * may be used with a specific instance of theJAIclass. * * @param opName The name of the operation. * @param args The source(s) and/or parameter(s) for the operation. * * @throws IllegalArgumentException if opName is null. * @throws IllegalArgumentException if args is null. * @throws IllegalArgumentException if no * OperationDescriptor is registered under the * specified operation name in the current operation registry. * @throws IllegalArgumentException if the * OperationDescriptor registered under the specified * operation name in the current operation registry does not * support renderable image mode. * @throws IllegalArgumentException if the specified operation does * not produce a * java.awt.image.renderable.RenderableImage. * @throws IllegalArgumentException if the specified operation is * unable to handle the sources and parameters specified in * args. * * @return A RenderableOp that represents the named * operation. * * @deprecated as of JAI 1.1 in favor of * createRenderableNS(String,ParameterBlock,RenderingHints). * @see JAI#createRenderableNS(String,ParameterBlock,RenderingHints) */ public RenderableOp createRenderableNS(String opName, ParameterBlock args) { return createRenderableNS(opName, args, null); } /** * Creates a Collection which represents the named * operation, using the source(s) and/or parameter(s) specified in * the ParameterBlock. This method should only be used * when the final result returned is a Collection. * (This includes javax.media.jai.CollectionOps.) * *

      The defaultJAIinstance is used as the source of the * registry and tile scheduler; that is, this method is equivalent * to getDefaultInstance().createRenderableCollectionNS(opName, * args,hints). The functionality of this method is the same as * its corresponding non-static method * createRenderableCollectionNS(). * * @param opName The name of the operation. * @param args The source(s) and/or parameter(s) for the operation. * @param hints The hints for the operation. * * @throws IllegalArgumentException if opName is null. * @throws IllegalArgumentException if args is null. * @throws IllegalArgumentException if no * OperationDescriptor is registered under the * specified operation name in the default operation registry. * @throws IllegalArgumentException if the * OperationDescriptor registered under the specified * operation name in the default operation registry does not * support renderable image mode. * @throws IllegalArgumentException if the specified operation does * not produce a * java.awt.image.renderable.RenderableImage or a * javax.media.jai.CollectionImage. * @throws IllegalArgumentException if the specified operation is * unable to handle the sources and parameters specified in * args. * * @return A Collection that represents the named * operation. * * @since JAI 1.1 */ public static Collection createRenderableCollection(String opName, ParameterBlock args, RenderingHints hints) { return defaultInstance.createRenderableCollectionNS(opName, args, hints); } /** * Creates a Collection which represents the named * operation, using the source(s) and/or parameter(s) specified in * the ParameterBlock. This method should only be used * when the final result returned is a Collection. * (This includes javax.media.jai.CollectionOps.) * *

      The defaultJAIinstance is used as the source of the * registry and tile scheduler; that is, this method is equivalent * to getDefaultInstance().createRenderableCollectionNS(opName, * args,null). The functionality of this method is the same as * its corresponding non-static method * createRenderableCollectionNS(). * * @param opName The name of the operation. * @param args The source(s) and/or parameter(s) for the operation. * * @throws IllegalArgumentException if opName is null. * @throws IllegalArgumentException if args is null. * @throws IllegalArgumentException if no * OperationDescriptor is registered under the * specified operation name in the default operation registry. * @throws IllegalArgumentException if the * OperationDescriptor registered under the specified * operation name in the default operation registry does not * support renderable image mode. * @throws IllegalArgumentException if the specified operation does * not produce a * java.awt.image.renderable.RenderableImage or a * javax.media.jai.CollectionImage. * @throws IllegalArgumentException if the specified operation is * unable to handle the sources and parameters specified in * args. * * @return A Collection that represents the named * operation. */ public static Collection createRenderableCollection(String opName, ParameterBlock args) { return defaultInstance.createRenderableCollectionNS(opName, args, null); } /** * Creates a Collection which represents the named * operation, using the source(s) and/or parameter(s) specified in * the ParameterBlock. This method should only be used * when the final result returned is a Collection. * (This includes javax.media.jai.CollectionOps.) The * source(s) supplied may be a collection of renderable images or a * collection of collections that at the very basic level include * renderable images. * The source(s) supplied are unwrapped to create a single collection * that contains RenderableOps and collections as many as the size of * the smallest collection supplied in the sources. The nth collection * is created using all supplied renderable images and the nth element of * each of the collections supplied in the source. * *

      This method should be used to create a Collection * in the renderable image mode. * *

      The supplied operation name is validated against the * operation registry. The source(s) and/or parameter(s) in the * ParameterBlock are validated against the named * operation's descriptor, both in their numbers and types. * Additional restrictions placed on the sources and parameters * by an individual operation are also validated by calling its * OperationDescriptor.validateRenderableArguments() * method. * *

      JAIallows a parameter to have a null input * value, if that particular parameter has a default value specified * in its operation's descriptor. In this case, the default value * will replace the null input. * *

      JAIalso allows unspecified tailing parameters, if these * parameters have default values specified in the operation's * descriptor. However, if a parameter, which * has a default value, is followed by one or more parameters that * have no default values, this parameter must be specified in the * ParameterBlock, even if it only has a value of * code>null. * *

      This version of createRenderableCollection is * non-static; it may be used with a specific instance of the JAI * class. * * @param opName The name of the operation. * @param args The source(s) and/or parameter(s) for the operation. * * @throws IllegalArgumentException if opName is null. * @throws IllegalArgumentException if args is null. * @throws IllegalArgumentException if no * OperationDescriptor is registered under the * specified operation name in the current operation registry. * @throws IllegalArgumentException if the * OperationDescriptor registered under the specified * operation name in the current operation registry does not * support renderable image mode. * @throws IllegalArgumentException if the specified operation does * not produce a * java.awt.image.renderable.RenderableImage or a * javax.media.jai.CollectionImage. * @throws IllegalArgumentException if the specified operation is * unable to handle the sources and parameters specified in * args. * * @return A Collection that represents the named * operation. * * @deprecated as of JAI 1.1 in favor of * createRenderableCollectionNS(String,ParameterBlock,RenderingHints). * @see JAI#createRenderableCollectionNS(String,ParameterBlock,RenderingHints) */ public Collection createRenderableCollectionNS(String opName, ParameterBlock args) { return createRenderableCollectionNS(opName, args, null); } /** * Creates a Collection which represents the named * operation, using the source(s) and/or parameter(s) specified in * the ParameterBlock. This method should only be used * when the final result returned is a Collection. * (This includes javax.media.jai.CollectionOps.) The * source(s) supplied may be a collection of renderable images or a * collection of collections that at the very basic level include * renderable images. * The source(s) supplied are unwrapped to create a single collection * that contains RenderableOps and collections as many as the size of * the smallest collection supplied in the sources. The nth collection * is created using all supplied renderable images and the nth element of * each of the collections supplied in the source. * *

      This method should be used to create a Collection * in the renderable image mode. * *

      The supplied operation name is validated against the * operation registry. The source(s) and/or parameter(s) in the * ParameterBlock are validated against the named * operation's descriptor, both in their numbers and types. * Additional restrictions placed on the sources and parameters * by an individual operation are also validated by calling its * OperationDescriptor.validateRenderableArguments() * method. * *

      JAIallows a parameter to have a null input * value, if that particular parameter has a default value specified * in its operation's descriptor. In this case, the default value * will replace the null input. * *

      JAIalso allows unspecified tailing parameters, if these * parameters have default values specified in the operation's * descriptor. However, if a parameter, which * has a default value, is followed by one or more parameters that * have no default values, this parameter must be specified in the * ParameterBlock, even if it only has a value of * code>null. * *

      The rendering hints associated with this instance of * JAI are overlaid with the hints passed to this * method. That is, the set of keys will be the union of the * keys from the instance's hints and the hints parameter. * If the same key exists in both places, the value from the * hints parameter will be used. * *

      This version of createRenderableCollection is * non-static; it may be used with a specific instance of the JAI * class. * * @param opName The name of the operation. * @param args The source(s) and/or parameter(s) for the operation. * @param hints The hints for the operation. * * @throws IllegalArgumentException if opName is null. * @throws IllegalArgumentException if args is null. * @throws IllegalArgumentException if no * OperationDescriptor is registered under the * specified operation name in the current operation registry. * @throws IllegalArgumentException if the * OperationDescriptor registered under the specified * operation name in the current operation registry does not * support renderable image mode. * @throws IllegalArgumentException if the specified operation does * not produce a * java.awt.image.renderable.RenderableImage or a * javax.media.jai.CollectionImage. * @throws IllegalArgumentException if the specified operation is * unable to handle the sources and parameters specified in * args. * * @return A Collection that represents the named * operation. * * @since JAI 1.1 */ public Collection createRenderableCollectionNS(String opName, ParameterBlock args, RenderingHints hints) { if(opName == null) { throw new IllegalArgumentException(JaiI18N.getString("JAI14")); } else if (args == null) { throw new IllegalArgumentException(JaiI18N.getString("JAI15")); } String modeName = "renderableCollection"; // Get the OperationDescriptor registered under the specified name. OperationDescriptor odesc = (OperationDescriptor) operationRegistry.getDescriptor(modeName, opName); if (odesc == null) { throw new IllegalArgumentException(opName + ": " + JaiI18N.getString("JAI0")); } Class destClass = odesc.getDestClass(modeName); if (!RenderableImage.class.isAssignableFrom(destClass) && !CollectionImage.class.isAssignableFrom(destClass)) { throw new IllegalArgumentException(opName + ": " + JaiI18N.getString("JAI6")); } // Validate input arguments. The ParameterBlock is cloned here // because OperationDescriptor.validateRenderableArguments() // may change its content. StringBuffer msg = new StringBuffer(); args = (ParameterBlock)args.clone(); RenderingHints mergedHints = mergeRenderingHints(renderingHints, hints); if (odesc.validateArguments(modeName, args, msg)) { if (RenderableImage.class.isAssignableFrom(destClass)) { Vector v = new Vector(1); RenderableOp op = new RenderableOp(operationRegistry, opName, args, mergedHints); v.add(op); return v; } else { CollectionOp cOp = new CollectionOp(operationRegistry, opName, args, mergedHints, true); // If the operation requests immediate rendering, do so. if (odesc.isImmediate()) { Collection coll = null; coll = cOp.getCollection(); if(coll == null) { return null; } } return cOp; } } else { int numSources = odesc.getNumSources(); Vector sources = args.getSources(); // Get the iterator of all the sources that are collection. // Get the iterator for the collection that has the least elements. Iterator[] iters = new Iterator[numSources]; Iterator iter = null; int size = Integer.MAX_VALUE; for (int i = 0; i < numSources; i++) { Object s = sources.elementAt(i); if (s instanceof Collection) { iters[i] = ((Collection)s).iterator(); if (iter == null || ((Collection)s).size() < size) { iter = iters[i]; size = ((Collection)s).size(); } } } if (iter == null) { // None of the sources is a collection. The error is // with the input arguments. throw new IllegalArgumentException(msg.toString()); } // Some sources are of type collection. Need to unwrap them. Collection col = null; for (int i = 0; i < numSources; i++) { Object s = sources.elementAt(i); if (s instanceof Collection) { try { col = (Collection)s.getClass().newInstance(); break; } catch (Exception e) { // Unable to create this collection type, try next. sendExceptionToListener( JaiI18N.getString("JAI16") + s.getClass().getName(), e); } } } if (col == null) { col = new Vector(); } // Get the source types. Class[] sourceClasses = odesc.getSourceClasses(modeName); while (iter.hasNext()) { ParameterBlock pb = new ParameterBlock(); pb.setParameters(args.getParameters()); for (int i = 0; i < numSources; i++) { // Get the next source. Object nextSource = null; if (iters[i] == null) { nextSource = sources.elementAt(i); } else { nextSource = iters[i].next(); } // If the source is not of a compatible type and // is not a Collection then the 'false' value // returned by validateArguments() above must indicate // a real error. if(!sourceClasses[i].isAssignableFrom(nextSource.getClass()) && !(nextSource instanceof Collection)) { throw new IllegalArgumentException(msg.toString()); } pb.addSource(nextSource); } Collection c = createRenderableCollectionNS(opName, pb, mergedHints); if (c instanceof Vector && c.size() == 1 && ((Vector)c).elementAt(0) instanceof RenderableOp) { col.add(((Vector)c).elementAt(0)); } else { col.add(c); } } return col; } } // Rendering hints. /** An inner class defining rendering hint keys. */ static class RenderingKey extends RenderingHints.Key { //cache the class of JAI to keep JAI.class in memory unless //the class RenderingKey is GC'ed. In this case, the // WeakReferences in the map of RenderingHints.Key will release // the instances of RenderingKey. So when JAI is loaded next // time, the keys can be recreated without any exception. // Fix bug: 4754807 private static Class JAIclass = JAI.class; private Class objectClass; RenderingKey(int privateKey, Class objectClass) { super(privateKey); this.objectClass = objectClass; } public boolean isCompatibleValue(Object val) { return objectClass.isInstance(val); } } /** * Returns the RenderingHints associated with this * JAI instance. These rendering hints will be * merged with any hints supplied as an argument to the * createNS(), createRenderableNS(), * or createCollectionNS() methods. */ public RenderingHints getRenderingHints() { return renderingHints; } /** * Sets the RenderingHints associated with this * JAI instance. These rendering hints will be * merged with any hints supplied as an argument to the * createNS(), createRenderableNS(), * or createCollectionNS() methods. * *

      The hints argument must be non-null, otherwise * a IllegalArgumentException will be thrown. */ public void setRenderingHints(RenderingHints hints) { if (hints == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.renderingHints = hints; } /** * Clears the RenderingHints associated with this * JAI instance. */ public void clearRenderingHints() { this.renderingHints = new RenderingHints(null); } /** * Returns the hint value associated with a given key * in this JAI instance, or null * if no value is associated with the given key. * * @throws IllegalArgumentException if key is * null. */ public Object getRenderingHint(RenderingHints.Key key) { if (key == null) { throw new IllegalArgumentException(JaiI18N.getString("JAI7")); } return renderingHints.get(key); } /** * Sets the hint value associated with a given key * in this JAI instance. * * @throws IllegalArgumentException if key is * null. * @throws IllegalArgumentException if value is * null. * @throws IllegalArgumentException if value is * not of the correct type for the given hint. */ public void setRenderingHint(RenderingHints.Key key, Object value) { if (key == null) { throw new IllegalArgumentException(JaiI18N.getString("JAI7")); } if (value == null) { throw new IllegalArgumentException(JaiI18N.getString("JAI9")); } try { renderingHints.put(key, value); } catch (Exception e) { throw new IllegalArgumentException(e.toString()); } } /** * Removes the hint value associated with a given key * in this JAI instance. */ public void removeRenderingHint(RenderingHints.Key key) { renderingHints.remove(key); } /** * Sets an ImagingListener object on this * JAI. * * @param imagingListener The ImagingListener to be used. If * the provided ImagingListener is * null, the default * ImagingListener, which rethrows the * RuntimeExceptions and prints * the stack trace of the other types to the stream * System.err, will be set. */ public void setImagingListener(ImagingListener listener) { if (listener == null) listener = ImagingListenerImpl.getInstance(); this.renderingHints.put(KEY_IMAGING_LISTENER, listener); this.imagingListener = listener; } /** * Gets the ImagingListener object from this * JAI. * * @return The ImagingListener object that currently * resides in this JAI. */ public ImagingListener getImagingListener() { return imagingListener; } private void sendExceptionToListener(String message, Exception e) { ImagingListener listener = getImagingListener(); listener.errorOccurred(message, e, this, false); } } jai-core-1.1.4/src/share/classes/javax/media/jai/ScaleOpImage.java0000644000175000017500000014410710203035544024500 0ustar mathieumathieu/* * $RCSfile: ScaleOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:21 $ * $State: Exp $ */ package javax.media.jai; import com.sun.media.jai.util.Rational; import com.sun.media.jai.util.ImageUtil; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.Point2D; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.awt.Point; import java.util.Map; import java.util.LinkedList; import javax.media.jai.util.CaselessStringKey; /** * A class extending WarpOpImage for use by further * extension classes that perform image scaling. Image scaling operations * require rectilinear backwards mapping and padding by the resampling * filter dimensions. * *

      When applying scale factors of scaleX, scaleY to a source image * with the upper left pixel at (srcMinX, srcMinY) and width of srcWidth * and height of srcHeight, the resulting image is defined to have the * following bounds: * * * dstMinX = ceil(A), where A = srcMinX * scaleX - 0.5 + transX, * dstMinY = ceil(B), where B = srcMinY * scaleY - 0.5 + transY, * dstMaxX = ceil(C), where C = (srcMaxX + 1) * scaleX - 1.5 + transX * and srcMaxX = srcMinX + srcWidth - 1 * dstMaxY = ceil(D), where D = (srcMaxY + 1) * scaleY - 1.5 + transY * and srcMaxY = srcMinY + srcHeight - 1 * dstWidth = dstMaxX - dstMinX + 1 * dstHeight = dstMaxY - dstMinY + 1 * * *

      In the case where source's upper left pixel is located is (0, 0), * the formulae simplify to * * * dstMinX = 0 * dstMinY = 0 * dstWidth = ceil (srcWidth * scaleX - 0.5 + transX) * dstHeight = ceil (srcHeight * scaleY - 0.5 + transY) * * *

      In the case where the source's upper left pixel is located at (0, 0) * and the scaling factors are integers, the formulae further simplify to * * * dstMinX = 0 * dstMinY = 0 * dstWidth = ceil (srcWidth * scaleX + transX) * dstWidth = ceil (srcHeight * scaleY + transY) * * *

      When interpolations which require padding the source such as Bilinear * or Bicubic interpolation are specified, the source needs to be extended * such that it has the extra pixels needed to compute all the destination * pixels. This extension is performed via the BorderExtender * class. The type of border extension can be specified as a * RenderingHint to the JAI.create method. * *

      If no BorderExtender is specified, the source will * not be extended. The scaled image size is still calculated * according to the formula specified above. However since there is not * enough source to compute all the destination pixels, only that * subset of the destination image's pixels which can be computed, * will be written in the destination. The rest of the destination * will be set to zeros. * *

      It may be noted that the minX, minY, width and height hints as * specified through the JAI.KEY_IMAGE_LAYOUT hint in the * RenderingHints object are not honored, as this operator * calculates the destination image bounds itself. The other * ImageLayout hints, like tileWidth and tileHeight, * however are honored. * * It should be noted that the superclass GeometricOpImage * automatically adds a value of Boolean.TRUE for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL to the given * configuration and passes it up to its superclass constructor * so that geometric operations are performed on the pixel values instead * of being performed on the indices into the color map for those * operations whose source(s) have an IndexColorModel. * This addition will take place only if a value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL has not already been * provided by the user. Note that the configuration Map * is cloned before the new hint is added to it. Regarding the value for * the JAI.KEY_REPLACE_INDEX_COLOR_MODEL * RenderingHints, the operator itself can be smart * based on the parameters, i.e. while the default value for * the JAI.KEY_REPLACE_INDEX_COLOR_MODEL is * Boolean.TRUE for operations that extend this class, * in some cases the operator could set the default. * * @see WarpOpImage * @see OpImage * */ public abstract class ScaleOpImage extends GeometricOpImage { /** The horizontal scale factor. */ protected float scaleX; /** The vertical scale factor. */ protected float scaleY; /** Thee horizontal translation factor */ protected float transX; /** The vertical translation factor */ protected float transY; /*** Rational representations */ protected Rational scaleXRational, scaleYRational; protected long scaleXRationalNum, scaleXRationalDenom; protected long scaleYRationalNum, scaleYRationalDenom; protected Rational invScaleXRational, invScaleYRational; protected long invScaleXRationalNum, invScaleXRationalDenom; protected long invScaleYRationalNum, invScaleYRationalDenom; protected Rational transXRational, transYRational; protected long transXRationalNum, transXRationalDenom; protected long transYRationalNum, transYRationalDenom; protected static float rationalTolerance = 0.000001F; // Padding private int lpad, rpad, tpad, bpad; // FORMULAE FOR FORWARD MAP are derived as follows // Nearest // Minimum: // srcMin = floor ((dstMin + 0.5 - trans) / scale) // srcMin <= (dstMin + 0.5 - trans) / scale < srcMin + 1 // srcMin*scale <= dstMin + 0.5 - trans < (srcMin + 1)*scale // srcMin*scale - 0.5 + trans // <= dstMin < (srcMin + 1)*scale - 0.5 + trans // Let A = srcMin*scale - 0.5 + trans, // Let B = (srcMin + 1)*scale - 0.5 + trans // // dstMin = ceil(A) // // Maximum: // Note that srcMax is defined to be srcMin + dimension - 1 // srcMax = floor ((dstMax + 0.5 - trans) / scale) // srcMax <= (dstMax + 0.5 - trans) / scale < srcMax + 1 // srcMax*scale <= dstMax + 0.5 - trans < (srcMax + 1)*scale // srcMax*scale - 0.5 + trans // <= dstMax < (srcMax+1) * scale - 0.5 + trans // Let float A = (srcMax + 1) * scale - 0.5 + trans // // dstMax = floor(A), if floor(A) < A, else // dstMax = floor(A) - 1 // OR dstMax = ceil(A - 1) // // Other interpolations // // First the source should be shrunk by the padding that is // required for the particular interpolation. Then the // shrunk source should be forward mapped as follows: // // Minimum: // srcMin = floor (((dstMin + 0.5 - trans)/scale) - 0.5) // srcMin <= ((dstMin + 0.5 - trans)/scale) - 0.5 < srcMin+1 // (srcMin+0.5)*scale <= dstMin+0.5-trans < // (srcMin+1.5)*scale // (srcMin+0.5)*scale - 0.5 + trans // <= dstMin < (srcMin+1.5)*scale - 0.5 + trans // Let A = (srcMin+0.5)*scale - 0.5 + trans, // Let B = (srcMin+1.5)*scale - 0.5 + trans // // dstMin = ceil(A) // // Maximum: // srcMax is defined as srcMin + dimension - 1 // srcMax = floor (((dstMax + 0.5 - trans) / scale) - 0.5) // srcMax <= ((dstMax + 0.5 - trans)/scale) - 0.5 < srcMax+1 // (srcMax+0.5)*scale <= dstMax + 0.5 - trans < // (srcMax+1.5)*scale // (srcMax+0.5)*scale - 0.5 + trans // <= dstMax < (srcMax+1.5)*scale - 0.5 + trans // Let float A = (srcMax+1.5)*scale - 0.5 + trans // // dstMax = floor(A), if floor(A) < A, else // dstMax = floor(A) - 1 // OR dstMax = ceil(A - 1) // private static ImageLayout layoutHelper(RenderedImage source, float scaleX, float scaleY, float transX, float transY, Interpolation interp, ImageLayout il) { // Represent the scale factors as Rational numbers. // Since a value of 1.2 is represented as 1.200001 which // throws the forward/backward mapping in certain situations. // Convert the scale and translation factors to Rational numbers Rational scaleXRational = Rational.approximate(scaleX, rationalTolerance); Rational scaleYRational = Rational.approximate(scaleY, rationalTolerance); long scaleXRationalNum = (long)scaleXRational.num; long scaleXRationalDenom = (long)scaleXRational.denom; long scaleYRationalNum = (long)scaleYRational.num; long scaleYRationalDenom = (long)scaleYRational.denom; Rational transXRational = Rational.approximate(transX, rationalTolerance); Rational transYRational = Rational.approximate(transY, rationalTolerance); long transXRationalNum = (long)transXRational.num; long transXRationalDenom = (long)transXRational.denom; long transYRationalNum = (long)transYRational.num; long transYRationalDenom = (long)transYRational.denom; ImageLayout layout = (il == null) ? new ImageLayout() : (ImageLayout)il.clone(); int x0 = source.getMinX(); int y0 = source.getMinY(); int w = source.getWidth(); int h = source.getHeight(); // Variables to store the calculated destination upper left coordinate long dx0Num, dx0Denom, dy0Num, dy0Denom; // Variables to store the calculated destination bottom right // coordinate long dx1Num, dx1Denom, dy1Num, dy1Denom; // Start calculations for destination dx0Num = x0; dx0Denom = 1; dy0Num = y0; dy0Denom = 1; // Formula requires srcMaxX + 1 = (x0 + w - 1) + 1 = x0 + w dx1Num = x0 + w; dx1Denom = 1; // Formula requires srcMaxY + 1 = (y0 + h - 1) + 1 = y0 + h dy1Num = y0 + h; dy1Denom = 1; dx0Num *= scaleXRationalNum; dx0Denom *= scaleXRationalDenom; dy0Num *= scaleYRationalNum; dy0Denom *= scaleYRationalDenom; dx1Num *= scaleXRationalNum; dx1Denom *= scaleXRationalDenom; dy1Num *= scaleYRationalNum; dy1Denom *= scaleYRationalDenom; // Equivalent to subtracting 0.5 dx0Num = 2 * dx0Num - dx0Denom; dx0Denom *= 2; dy0Num = 2 * dy0Num - dy0Denom; dy0Denom *= 2; // Equivalent to subtracting 1.5 dx1Num = 2 * dx1Num - 3 * dx1Denom; dx1Denom *= 2; dy1Num = 2 * dy1Num - 3 * dy1Denom; dy1Denom *= 2; // Adding translation factors // Equivalent to float dx0 += transX dx0Num = dx0Num * transXRationalDenom + transXRationalNum * dx0Denom; dx0Denom *= transXRationalDenom; // Equivalent to float dy0 += transY dy0Num = dy0Num * transYRationalDenom + transYRationalNum * dy0Denom; dy0Denom *= transYRationalDenom; // Equivalent to float dx1 += transX dx1Num = dx1Num * transXRationalDenom + transXRationalNum * dx1Denom; dx1Denom *= transXRationalDenom; // Equivalent to float dy1 += transY dy1Num = dy1Num * transYRationalDenom + transYRationalNum * dy1Denom; dy1Denom *= transYRationalDenom; // Get the integral coordinates int l_x0, l_y0, l_x1, l_y1; l_x0 = Rational.ceil(dx0Num, dx0Denom); l_y0 = Rational.ceil(dy0Num, dy0Denom); l_x1 = Rational.ceil(dx1Num, dx1Denom); l_y1 = Rational.ceil(dy1Num, dy1Denom); // Set the top left coordinate of the destination layout.setMinX(l_x0); layout.setMinY(l_y0); // Width and height layout.setWidth(l_x1 - l_x0 + 1); layout.setHeight(l_y1 - l_y0 + 1); return layout; } private static Map configHelper(RenderedImage source, Map configuration, Interpolation interp) { Map config = configuration; // If source image is binary and the interpolation is either nearest // or bilinear, do not expand if (ImageUtil.isBinary(source.getSampleModel()) && (interp == null || interp instanceof InterpolationNearest || interp instanceof InterpolationBilinear)) { // Set to false if (configuration == null) { config = new RenderingHints(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.FALSE); } else { // If the user specified a value for this hint, we don't // want to change that if (!config.containsKey(JAI.KEY_REPLACE_INDEX_COLOR_MODEL)) { RenderingHints hints = new RenderingHints(null); // This is effectively a clone of configuration hints.putAll(configuration); config = hints; config.put(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.TRUE); } } } return config; } /** * Constructs a ScaleOpImage from a RenderedImage * source, an optional BorderExtender, x and y scale * and translation factors, and an Interpolation * object. The image dimensions are determined by forward-mapping * the source bounds, and are passed to the superclass constructor * by means of the layout parameter. Other fields of * the layout are passed through unchanged. If * layout is null, a new * ImageLayout will be constructor to hold the bounds * information. * * Note that the scale factors are represented internally as Rational * numbers in order to workaround inexact device specific representation * of floating point numbers. For instance the floating point number 1.2 * is internally represented as 1.200001, which can throw the * calculations off during a forward/backward map. * *

      The Rational approximation is valid upto the sixth decimal place. * * @param layout an ImageLayout optionally containing * the tile grid layout, SampleModel, and * ColorModel, or null. * @param source a RenderedImage. * @param configuration Configurable attributes of the image including * configuration variables indexed by * RenderingHints.Keys and image properties indexed * by Strings or CaselessStringKeys. * This is simply forwarded to the superclass constructor. * @param cobbleSources a boolean indicating whether * computeRect expects contiguous sources. * @param extender a BorderExtender, or null. * @param interp an Interpolation object to use for * resampling. * @param scaleX scale factor along x axis. * @param scaleY scale factor along y axis. * @param transX translation factor along x axis. * @param transY translation factor along y axis. * * @throws IllegalArgumentException if source * is null. * @throws IllegalArgumentException if combining the * source bounds with the layout parameter results in negative * output width or height. * * @since JAI 1.1 */ public ScaleOpImage(RenderedImage source, ImageLayout layout, Map configuration, boolean cobbleSources, BorderExtender extender, Interpolation interp, float scaleX, float scaleY, float transX, float transY) { super(vectorize(source), // vectorize() checks for null source. layoutHelper(source, scaleX, scaleY, transX, transY, interp, layout), configHelper(source, configuration, interp), cobbleSources, extender, interp, null); this.scaleX = scaleX; this.scaleY = scaleY; this.transX = transX; this.transY = transY; // Represent the scale factors as Rational numbers. // Since a value of 1.2 is represented as 1.200001 which // throws the forward/backward mapping in certain situations. // Convert the scale and translation factors to Rational numbers this.scaleXRational = Rational.approximate(scaleX, rationalTolerance); this.scaleYRational = Rational.approximate(scaleY, rationalTolerance); this.scaleXRationalNum = (long)this.scaleXRational.num; this.scaleXRationalDenom = (long)this.scaleXRational.denom; this.scaleYRationalNum = (long)this.scaleYRational.num; this.scaleYRationalDenom = (long)this.scaleYRational.denom; this.transXRational = Rational.approximate(transX, rationalTolerance); this.transYRational = Rational.approximate(transY, rationalTolerance); this.transXRationalNum = (long)this.transXRational.num; this.transXRationalDenom = (long)this.transXRational.denom; this.transYRationalNum = (long)this.transYRational.num; this.transYRationalDenom = (long)this.transYRational.denom; // Inverse scale factors as Rationals invScaleXRational = new Rational(scaleXRational); invScaleXRational.invert(); invScaleYRational = new Rational(scaleYRational); invScaleYRational.invert(); invScaleXRationalNum = invScaleXRational.num; invScaleXRationalDenom = invScaleXRational.denom; invScaleYRationalNum = invScaleYRational.num; invScaleYRationalDenom = invScaleYRational.denom; lpad = interp.getLeftPadding(); rpad = interp.getRightPadding(); tpad = interp.getTopPadding(); bpad = interp.getBottomPadding(); if (extender == null) { // Get the source dimensions int x0 = source.getMinX(); int y0 = source.getMinY(); int w = source.getWidth(); int h = source.getHeight(); // The first source pixel (x0, y0) long dx0Num, dx0Denom, dy0Num, dy0Denom; // The first pixel (x1, y1) that is just outside the source long dx1Num, dx1Denom, dy1Num, dy1Denom; if (interp instanceof InterpolationNearest) { // First point inside the source dx0Num = x0; dx0Denom = 1; dy0Num = y0; dy0Denom = 1; // First point outside source // for nearest, x1 = x0 + w, y1 = y0 + h // since anything >= a but < (a+1) maps to a for nearest // Equivalent to float d_x1 = x0 + w dx1Num = x0 + w; dx1Denom = 1; // Equivalent to float d_y1 = y0 + h dy1Num = y0 + h; dy1Denom = 1; } else { // First point inside the source dx0Num = 2 * x0 + 1; dx0Denom = 2; dy0Num = 2 * y0 + 1; dy0Denom = 2; // for other interpolations, x1 = x0+w+0.5, y1 = y0+h+0.5 // as derived in the formulae derivation above. dx1Num = 2 * x0 + 2 * w + 1; dx1Denom = 2; dy1Num = 2 * y0 + 2 * h + 1; dy1Denom = 2; // Equivalent to x0 += lpad; dx0Num += dx0Denom * lpad; // Equivalent to y0 += tpad; dy0Num += dy0Denom * tpad; // Equivalent to x1 -= rpad; dx1Num -= dx1Denom * rpad; // Equivalent to y1 += bpad; dy1Num -= dy1Denom * bpad; } // Forward map the first and last source points // Equivalent to float d_x0 = x0 * scaleX; dx0Num *= scaleXRationalNum; dx0Denom *= scaleXRationalDenom; // Add the X translation factor d_x0 += transX dx0Num = dx0Num * transXRationalDenom + transXRationalNum*dx0Denom; dx0Denom *= transXRationalDenom; // Equivalent to float d_y0 = y0 * scaleY; dy0Num *= scaleYRationalNum; dy0Denom *= scaleYRationalDenom; // Add the Y translation factor, float d_y0 += transY dy0Num = dy0Num * transYRationalDenom + transYRationalNum*dy0Denom; dy0Denom *= transYRationalDenom; // Equivalent to float d_x1 = x1 * scaleX; dx1Num *= scaleXRationalNum; dx1Denom *= scaleXRationalDenom; // Add the X translation factor d_x1 += transX dx1Num = dx1Num * transXRationalDenom + transXRationalNum*dx1Denom; dx1Denom *= transXRationalDenom; // Equivalent to float d_y1 = y1 * scaleY; dy1Num *= scaleYRationalNum; dy1Denom *= scaleYRationalDenom; // Add the Y translation factor, float d_y1 += transY dy1Num = dy1Num * transYRationalDenom + transYRationalNum*dy1Denom; dy1Denom *= transYRationalDenom; // Get the integral coordinates int l_x0, l_y0, l_x1, l_y1; // Subtract 0.5 from dx0, dy0 dx0Num = 2 * dx0Num - dx0Denom; dx0Denom *= 2; dy0Num = 2 * dy0Num - dy0Denom; dy0Denom *= 2; l_x0 = Rational.ceil(dx0Num, dx0Denom); l_y0 = Rational.ceil(dy0Num, dy0Denom); // Subtract 0.5 from dx1, dy1 dx1Num = 2 * dx1Num - dx1Denom; dx1Denom *= 2; dy1Num = 2 * dy1Num - dy1Denom; dy1Denom *= 2; l_x1 = (int)Rational.floor(dx1Num, dx1Denom); // l_x1 must be less than but not equal to (dx1Num/dx1Denom) if ((l_x1 * dx1Denom) == dx1Num) { l_x1 -= 1; } l_y1 = (int)Rational.floor(dy1Num, dy1Denom); if (l_y1 * dy1Denom == dy1Num) { l_y1 -= 1; } computableBounds = new Rectangle(l_x0, l_y0, (l_x1 - l_x0 + 1), (l_y1 - l_y0 + 1)); } else { // If extender is present we can write the entire destination. computableBounds = getBounds(); } } /** * Returns the number of samples required to the left of the center. * * @return The left padding factor. * * @deprecated as of JAI 1.1. */ public int getLeftPadding() { return interp == null ? 0 : interp.getLeftPadding(); } /** * Returns the number of samples required to the right of the center. * * @return The right padding factor. * * @deprecated as of JAI 1.1. */ public int getRightPadding() { return interp == null ? 0 : interp.getRightPadding(); } /** * Returns the number of samples required above the center. * * @return The top padding factor. * * @deprecated as of JAI 1.1. */ public int getTopPadding() { return interp == null ? 0 : interp.getTopPadding(); } /** * Returns the number of samples required below the center. * * @return The bottom padding factor. * * @deprecated as of JAI 1.1. */ public int getBottomPadding() { return interp == null ? 0 : interp.getBottomPadding(); } /** * Computes the position in the specified source that best * matches the supplied destination image position. * *

      The implementation in this class returns the value of * pt in the following code snippet: * *

           * Point2D pt = (Point2D)destPt.clone();
           * pt.setLocation((destPt.getX() - transX + 0.5)/scaleX - 0.5,
           *                (destPt.getY() - transY + 0.5)/scaleY - 0.5);
           * 
      * * Subclasses requiring different behavior should override this * method.

      * * @param destPt the position in destination image coordinates * to map to source image coordinates. * @param sourceIndex the index of the source image. * * @return a Point2D of the same class as * destPt. * * @throws IllegalArgumentException if destPt is * null. * @throws IndexOutOfBoundsException if sourceIndex is * non-zero. * * @since JAI 1.1.2 */ public Point2D mapDestPoint(Point2D destPt, int sourceIndex) { if (destPt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } else if (sourceIndex != 0) { throw new IndexOutOfBoundsException(JaiI18N.getString("Generic1")); } Point2D pt = (Point2D)destPt.clone(); pt.setLocation((destPt.getX() - transX + 0.5)/scaleX - 0.5, (destPt.getY() - transY + 0.5)/scaleY - 0.5); return pt; } /** * Computes the position in the destination that best * matches the supplied source image position. * *

      The implementation in this class returns the value of * pt in the following code snippet: * *

           * Point2D pt = (Point2D)sourcePt.clone();
           * pt.setLocation(scaleX*(sourcePt.getX() + 0.5) + transX - 0.5,
           *                scaleY*(sourcePt.getY() + 0.5) + transY - 0.5);
           * 
      * * Subclasses requiring different behavior should override this * method.

      * * @param sourcePt the position in source image coordinates * to map to destination image coordinates. * @param sourceIndex the index of the source image. * * @return a Point2D of the same class as * sourcePt. * * @throws IllegalArgumentException if sourcePt is * null. * @throws IndexOutOfBoundsException if sourceIndex is * non-zero. * * @since JAI 1.1.2 */ public Point2D mapSourcePoint(Point2D sourcePt, int sourceIndex) { if (sourcePt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } else if (sourceIndex != 0) { throw new IndexOutOfBoundsException(JaiI18N.getString("Generic1")); } Point2D pt = (Point2D)sourcePt.clone(); pt.setLocation(scaleX*(sourcePt.getX() + 0.5) + transX - 0.5, scaleY*(sourcePt.getY() + 0.5) + transY - 0.5); return pt; } /** * Returns the minimum bounding box of the region of the destination * to which a particular Rectangle of the specified source * will be mapped. * * @param sourceRect the Rectangle in source coordinates. * @param sourceIndex the index of the source image. * * @return a Rectangle indicating the destination * bounding box, or null if the bounding box * is unknown. * * @throws IllegalArgumentException if sourceIndex is * negative or greater than the index of the last source. * @throws IllegalArgumentException if sourceRect is * null. * * @since JAI 1.1 */ protected Rectangle forwardMapRect(Rectangle sourceRect, int sourceIndex) { if (sourceRect == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sourceIndex != 0) { throw new IllegalArgumentException(JaiI18N.getString("Generic1")); } // Get the source dimensions int x0 = sourceRect.x; int y0 = sourceRect.y; int w = sourceRect.width; int h = sourceRect.height; // Variables to represent the first pixel inside the destination. long dx0Num, dx0Denom, dy0Num, dy0Denom; // Variables to represent the last destination pixel. long dx1Num, dx1Denom, dy1Num, dy1Denom; if (interp instanceof InterpolationNearest) { // First point inside the source dx0Num = x0; dx0Denom = 1; dy0Num = y0; dy0Denom = 1; // First point outside source // for nearest, x1 = x0 + w, y1 = y0 + h // since anything >= a and < a+1 maps to a for nearest, since // we use floor to calculate the integral source position // Equivalent to float d_x1 = x0 + w dx1Num = x0 + w; dx1Denom = 1; // Equivalent to float d_y1 = y0 + h dy1Num = y0 + h; dy1Denom = 1; } else { // First point inside the source (x0 + 0.5, y0 + 0.5) dx0Num = 2 * x0 + 1; dx0Denom = 2; dy0Num = 2 * y0 + 1; dy0Denom = 2; // for other interpolations, x1 = x0 + w + 0.5, y1 = y0 + h + 0.5 // as derived in the formulae derivation above. dx1Num = 2 * x0 + 2 * w + 1; dx1Denom = 2; dy1Num = 2 * y0 + 2 * h + 1; dy1Denom = 2; } // Forward map first and last source positions // Equivalent to float d_x0 = x0 * scaleX; dx0Num = dx0Num * scaleXRationalNum; dx0Denom *= scaleXRationalDenom; // Equivalent to float d_y0 = y0 * scaleY; dy0Num = dy0Num * scaleYRationalNum; dy0Denom *= scaleYRationalDenom; // Equivalent to float d_x1 = x1 * scaleX; dx1Num = dx1Num * scaleXRationalNum; dx1Denom *= scaleXRationalDenom; // Equivalent to float d_y1 = y1 * scaleY; dy1Num = dy1Num * scaleYRationalNum; dy1Denom *= scaleYRationalDenom; // Add the translation factors. // Equivalent to float d_x0 += transX dx0Num = dx0Num * transXRationalDenom + transXRationalNum * dx0Denom; dx0Denom *= transXRationalDenom; // Equivalent to float d_y0 += transY dy0Num = dy0Num * transYRationalDenom + transYRationalNum * dy0Denom; dy0Denom *= transYRationalDenom; // Equivalent to float d_x1 += transX dx1Num = dx1Num * transXRationalDenom + transXRationalNum * dx1Denom; dx1Denom *= transXRationalDenom; // Equivalent to float d_y1 += transY dy1Num = dy1Num * transYRationalDenom + transYRationalNum * dy1Denom; dy1Denom *= transYRationalDenom; // Get the integral coordinates int l_x0, l_y0, l_x1, l_y1; // Subtract 0.5 from dx0, dy0 dx0Num = 2 * dx0Num - dx0Denom; dx0Denom *= 2; dy0Num = 2 * dy0Num - dy0Denom; dy0Denom *= 2; l_x0 = Rational.ceil(dx0Num, dx0Denom); l_y0 = Rational.ceil(dy0Num, dy0Denom); // Subtract 0.5 from dx1, dy1 dx1Num = 2 * dx1Num - dx1Denom; dx1Denom *= 2; dy1Num = 2 * dy1Num - dy1Denom; dy1Denom *= 2; l_x1 = (int)Rational.floor(dx1Num, dx1Denom); if ((l_x1 * dx1Denom) == dx1Num) { l_x1 -= 1; } l_y1 = (int)Rational.floor(dy1Num, dy1Denom); if ((l_y1 * dy1Denom) == dy1Num) { l_y1 -= 1; } return new Rectangle(l_x0, l_y0, (l_x1 - l_x0 + 1), (l_y1 - l_y0 + 1)); } /** * Returns the minimum bounding box of the region of the specified * source to which a particular Rectangle of the * destination will be mapped. * * @param destRect the Rectangle in destination coordinates. * @param sourceIndex the index of the source image. * * @return a Rectangle indicating the source bounding box, * or null if the bounding box is unknown. * * @throws IllegalArgumentException if sourceIndex is * negative or greater than the index of the last source. * @throws IllegalArgumentException if destRect is * null. * * @since JAI 1.1 */ protected Rectangle backwardMapRect(Rectangle destRect, int sourceIndex) { if (destRect == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sourceIndex != 0) { throw new IllegalArgumentException(JaiI18N.getString("Generic1")); } // Get the destination rectangle coordinates and dimensions int x0 = destRect.x; int y0 = destRect.y; int w = destRect.width; int h = destRect.height; // Variables that will eventually hold the source pixel // positions which are the result of the backward map long sx0Num, sx0Denom, sy0Num, sy0Denom; // First destination point that will be backward mapped // will be dx0 + 0.5, dy0 + 0.5 sx0Num = (x0 * 2 + 1); sx0Denom = 2; sy0Num = (y0 * 2 + 1); sy0Denom = 2; // The last destination pixel to be backward mapped will be // dx0 + w - 1 + 0.5, dy0 + h - 1 + 0.5 i.e. // dx0 + w - 0.5, dy0 + h - 0.5 long sx1Num, sx1Denom, sy1Num, sy1Denom; // Equivalent to float sx1 = dx0 + dw - 0.5; sx1Num = 2 * x0 + 2 * w - 1; sx1Denom = 2; // Equivalent to float sy1 = dy0 + dh - 0.5; sy1Num = 2 * y0 + 2 * h - 1; sy1Denom = 2; // Subtract the translation factors. sx0Num = sx0Num * transXRationalDenom - transXRationalNum * sx0Denom; sx0Denom *= transXRationalDenom; sy0Num = sy0Num * transYRationalDenom - transYRationalNum * sy0Denom; sy0Denom *= transYRationalDenom; sx1Num = sx1Num * transXRationalDenom - transXRationalNum * sx1Denom; sx1Denom *= transXRationalDenom; sy1Num = sy1Num * transYRationalDenom - transYRationalNum * sy1Denom; sy1Denom *= transYRationalDenom; // Backward map both the destination positions // Equivalent to float sx0 = x0 / scaleX; sx0Num *= invScaleXRationalNum; sx0Denom *= invScaleXRationalDenom; sy0Num *= invScaleYRationalNum; sy0Denom *= invScaleYRationalDenom; sx1Num *= invScaleXRationalNum; sx1Denom *= invScaleXRationalDenom; sy1Num *= invScaleYRationalNum; sy1Denom *= invScaleYRationalDenom; int s_x0 = 0, s_y0 = 0, s_x1 = 0, s_y1 = 0; if (interp instanceof InterpolationNearest) { // Floor sx0, sy0 s_x0 = Rational.floor(sx0Num, sx0Denom); s_y0 = Rational.floor(sy0Num, sy0Denom); // Equivalent to (int)Math.floor(sx1) s_x1 = Rational.floor(sx1Num , sx1Denom); // Equivalent to (int)Math.floor(sy1) s_y1 = Rational.floor(sy1Num , sy1Denom); } else { // For all other interpolations // Equivalent to (int) Math.floor(sx0 - 0.5) s_x0 = Rational.floor(2 * sx0Num - sx0Denom, 2 * sx0Denom); // Equivalent to (int) Math.floor(sy0 - 0.5) s_y0 = Rational.floor(2 * sy0Num - sy0Denom , 2 * sy0Denom); // Calculate the last source point s_x1 = Rational.floor(2 * sx1Num - sx1Denom , 2 * sx1Denom); // Equivalent to (int)Math.ceil(sy1 - 0.5) s_y1 = Rational.floor(2 * sy1Num - sy1Denom , 2 * sy1Denom); } return new Rectangle(s_x0, s_y0, (s_x1 - s_x0 + 1), (s_y1 - s_y0 + 1)); } /** * Computes a tile. If source cobbling was requested at * construction time, the source tile boundaries are overlayed * onto the destination, cobbling is performed for areas that * intersect multiple source tiles, and * computeRect(Raster[], WritableRaster, Rectangle) * is called for each of the resulting regions. Otherwise, * computeRect(PlanarImage[], WritableRaster, * Rectangle) is called once to compute the entire active * area of the tile. * *

      The image bounds may be larger than the bounds of the * source image. In this case, samples for which there are no * corresponding sources are set to zero. * *

      The following steps are performed in order to compute the tile: *

        *
      • The destination tile is backward mapped to compute the needed * source. *
      • This source is then split on tile boundaries to produce rectangles * that do not cross tile boundaries. *
      • These source rectangles are then forward mapped to produce * destination rectangles, and the computeRect method is called for * each corresponding pair of source and destination rectangles. *
      • For higher order interpolations, some source cobbling across tile * boundaries does occur. *
      * * @param tileX The X index of the tile. * @param tileY The Y index of the tile. * * @return The tile as a Raster. */ public Raster computeTile(int tileX, int tileY) { if (!cobbleSources) { return super.computeTile(tileX, tileY); } // X and Y coordinate of the pixel pixel of the tile. int orgX = tileXToX(tileX); int orgY = tileYToY(tileY); // Create a new WritableRaster to represent this tile. WritableRaster dest = createWritableRaster(sampleModel, new Point(orgX, orgY)); Rectangle rect = new Rectangle(orgX, orgY, tileWidth, tileHeight); // Clip dest rectangle against the part of the destination // rectangle that can be written. Rectangle destRect = rect.intersection(computableBounds); if ((destRect.width <= 0) || (destRect.height <= 0)) { // If empty rectangle, return empty tile. return dest; } // Get the source rectangle required to compute the destRect Rectangle srcRect = mapDestRect(destRect, 0); Raster[] sources = new Raster[1]; // Split the source on tile boundaries. // Get the new pairs of src & dest Rectangles // The tileWidth and tileHeight of the source image // may differ from this tileWidth and tileHeight. PlanarImage source0 = getSource(0); IntegerSequence srcXSplits = new IntegerSequence(); IntegerSequence srcYSplits = new IntegerSequence(); source0.getSplits(srcXSplits, srcYSplits, srcRect); if (srcXSplits.getNumElements() == 1 && srcYSplits.getNumElements() == 1) { // If the source is fully contained within // a tile there is no need to split it any further. if (extender == null) { sources[0] = source0.getData(srcRect); } else { sources[0] = source0.getExtendedData(srcRect, extender); } // Compute the destination tile. computeRect(sources, dest, destRect); } else { // Source Rect straddles 2 or more tiles // Get Source Tilewidth & height int srcTileWidth = source0.getTileWidth(); int srcTileHeight = source0.getTileHeight(); srcYSplits.startEnumeration(); while (srcYSplits.hasMoreElements()) { // Along Y TileBoundaries int ysplit = srcYSplits.nextElement(); srcXSplits.startEnumeration(); while (srcXSplits.hasMoreElements()) { // Along X TileBoundaries int xsplit = srcXSplits.nextElement(); // Construct a pseudo tile for intersection purposes Rectangle srcTile = new Rectangle(xsplit, ysplit, srcTileWidth, srcTileHeight); // Intersect the tile with the source Rectangle Rectangle newSrcRect = srcRect.intersection(srcTile); // // The new source rect could be of size less than or equal // to the interpolation kernel dimensions. In which case // the forward map produces null destination rectangles. // Hence we need to deal with these cases in the manner // implemented below (grow the source before the map. This // would result in source cobbling). Not an issue for // Nearest-Neighbour. // if (!(interp instanceof InterpolationNearest)) { if (newSrcRect.width <= interp.getWidth()) { // // Need to forward map this source rectangle. // Since we need a minimum of 2 * (lpad + rpad + 1) // in order to process, we contsruct a source // rectangle of that size, forward map and then // process the resulting destination rectangle. // Rectangle wSrcRect = new Rectangle(); Rectangle wDestRect; wSrcRect.x = newSrcRect.x; wSrcRect.y = newSrcRect.y - tpad - 1; wSrcRect.width = 2 * (lpad + rpad + 1); wSrcRect.height = newSrcRect.height + bpad + tpad + 2; wSrcRect = wSrcRect.intersection(source0.getBounds()); wDestRect = mapSourceRect(wSrcRect, 0); // // Make sure this destination rectangle is // within the bounds of our original writable // destination rectangle // wDestRect = wDestRect.intersection(destRect); if ((wDestRect.width > 0) && (wDestRect.height > 0)) { // Do the operations with these new rectangles if (extender == null) { sources[0] = source0.getData(wSrcRect); } else { sources[0] = source0.getExtendedData(wSrcRect, extender); } // Compute the destination tile. computeRect(sources, dest, wDestRect); } } if (newSrcRect.height <= interp.getHeight()) { // // Need to forward map this source rectangle. // Since we need a minimum of 2 * (tpad + bpad + 1) // in order to process, we create a source // rectangle of that size, forward map and then // process the resulting destinaltion rectangle // Rectangle hSrcRect = new Rectangle(); Rectangle hDestRect; hSrcRect.x = newSrcRect.x - lpad - 1; hSrcRect.y = newSrcRect.y; hSrcRect.width = newSrcRect.width + lpad + rpad + 2; hSrcRect.height = 2 * (tpad + bpad + 1); hSrcRect = hSrcRect.intersection(source0.getBounds()); hDestRect = mapSourceRect(hSrcRect, 0); // // Make sure this destination rectangle is // within the bounds of our original writable // destination rectangle // hDestRect = hDestRect.intersection(destRect); if ((hDestRect.width > 0) && (hDestRect.height > 0)) { // Do the operations with these new rectangles if (extender == null) { sources[0] = source0.getData(hSrcRect); } else { sources[0] = source0.getExtendedData(hSrcRect, extender); } // Compute the destination tile. computeRect(sources, dest, hDestRect); } } } // Process source rectangle if ((newSrcRect.width > 0) && (newSrcRect.height > 0)) { // Forward map this source rectangle // to get the destination rectangle Rectangle newDestRect = mapSourceRect(newSrcRect, 0); // Make sure this destination rectangle is // within the bounds of our original writable // destination rectangle newDestRect = newDestRect.intersection(destRect); if ((newDestRect.width > 0) && (newDestRect.height > 0)) { // Do the operations with these new rectangles if (extender == null) { sources[0] = source0.getData(newSrcRect); } else { sources[0] = source0.getExtendedData(newSrcRect, extender); } // Compute the destination tile. computeRect(sources, dest, newDestRect); } // // Since mapSourceRect (forward map) shrinks the // source rectangle before the map, there are areas // of this rectangle which never get mapped. // // These occur at the tile boundaries between // rectangles. The following algorithm handles // these edge conditions. // // The cases : // Right edge // Bottom edge // Lower Right Corner // if (!(interp instanceof InterpolationNearest)) { Rectangle RTSrcRect = new Rectangle(); Rectangle RTDestRect; // Right Edge RTSrcRect.x = newSrcRect.x + newSrcRect.width - 1 - rpad - lpad; RTSrcRect.y = newSrcRect.y; // // The amount of src not used from the end of // the first tile is rpad + 0.5. The amount // not used from the beginning of the next tile // is lpad + 0.5. Since we cannot start mapping // at 0.5, we need to get the area of the half // pixel on both sides. So we get another 0,5 // from both sides. In total (rpad + 0.5 + // 0.5) + (lpad + 0.5 + 0.5) // Since mapSourceRect subtracts rpad + 0.5 and // lpad + 0.5 from the source before the // forward map, we need to add that in. // RTSrcRect.width = 2 * (lpad + rpad + 1); RTSrcRect.height = newSrcRect.height; RTDestRect = mapSourceRect(RTSrcRect, 0); // Clip this against the whole destrect RTDestRect = RTDestRect.intersection(destRect); // RTSrcRect may be out of image bounds; // map one more time RTSrcRect = mapDestRect(RTDestRect, 0); if (RTDestRect.width > 0 && RTDestRect.height > 0) { // Do the operations with these new rectangles if (extender == null) { sources[0] = source0.getData(RTSrcRect); } else { sources[0] = source0.getExtendedData(RTSrcRect, extender); } computeRect(sources, dest, RTDestRect); } // Bottom Edge Rectangle BTSrcRect = new Rectangle(); Rectangle BTDestRect; BTSrcRect.x = newSrcRect.x; BTSrcRect.y = newSrcRect.y + newSrcRect.height - 1 - bpad - tpad; // // The amount of src not used from the end of // the first tile is tpad + 0.5. The amount // not used from the beginning of the next tile // is bpad + 0.5. Since we cannot start mapping // at 0.5, we need to get the area of the half // pixel on both sides. So we get another 0,5 // from both sides. In total (tpad + 0.5 + // 0.5) + (bpad + 0.5 + 0.5) // Since mapSourceRect subtracts tpad + 0.5 and // bpad + 0.5 from the source before the // forward map, we need to add that in. // BTSrcRect.width = newSrcRect.width; BTSrcRect.height = 2 * (tpad + bpad + 1); BTDestRect = mapSourceRect(BTSrcRect, 0); // Clip this against the whole destrect BTDestRect = BTDestRect.intersection(destRect); //BTSrcRect maybe out of bounds //map one more time BTSrcRect = mapDestRect(BTDestRect, 0); //end if (BTDestRect.width > 0 && BTDestRect.height > 0) { // Do the operations with these new rectangles if (extender == null) { sources[0] = source0.getData(BTSrcRect); } else { sources[0] = source0.getExtendedData(BTSrcRect, extender); } computeRect(sources, dest, BTDestRect); } // Lower Right Area Rectangle LRTSrcRect = new Rectangle(); Rectangle LRTDestRect; LRTSrcRect.x = newSrcRect.x + newSrcRect.width - 1 - rpad - lpad; LRTSrcRect.y = newSrcRect.y + newSrcRect.height - 1 - bpad - tpad; // Comment forthcoming LRTSrcRect.width = 2 * (rpad + lpad + 1); LRTSrcRect.height = 2 * (tpad + bpad + 1); LRTDestRect = mapSourceRect(LRTSrcRect, 0); // Clip this against the whole destrect LRTDestRect = LRTDestRect.intersection(destRect); // LRTSrcRect may still be out of bounds LRTSrcRect = mapDestRect(LRTDestRect, 0); if (LRTDestRect.width > 0 && LRTDestRect.height > 0) { // Do the operations with these new rectangles if (extender == null) { sources[0] = source0.getData(LRTSrcRect); } else { sources[0] = source0.getExtendedData(LRTSrcRect, extender); } computeRect(sources, dest, LRTDestRect); } } } } } } // Return the written destination raster return dest; } } jai-core-1.1.4/src/share/classes/javax/media/jai/RenderedImageList.java0000644000175000017500000004333210203035544025534 0ustar mathieumathieu/* * $RCSfile: RenderedImageList.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:20 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.io.Serializable; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Vector; /** * A CollectionImage which is also a * RenderedImage. The underlying Collection * in this case is required to be a List containing only * RenderedImages. * *

      Instances of this class may be returned from either a * RenderedImageFactory or from a * CollectionImageFactory. This class would be * particularly useful for implementing operations the result of which * includes a single primary image and one or more secondary or * dependant images the data of which are less likely to be requested. * *

      Invocations of RenderedImage methods on an instance * of this class will be forwarded to the first RenderedImage * in the underlying List, i.e., the image at index zero. * This should be the index assigned to the primary image in the case * alluded to above. If there are no images in the List * when a RenderedImage method is invoked an * IllegalStateException will be thrown. * *

      One example of the use of this class is in generating a classmap * image using a classification algorithm. A by-product image of such an * operation is often an error image wherein the value of each pixel is * some measure of the classification error at that pixel. In this case * the classmap image would be stored at index zero in the internal list * and the error image at the unity index. The error image would be * an OpImage that has the classmap image as its source. * The result is that a reference to the error image is always available * with the classmap image but the computation of the error image pixel * values may be deferred until such time as the data are needed, if ever. * *

      Methods defined in the RenderedImage and List * interfaces are not all commented in detail. The List methods * merely forward the call in most cases directly to the underlying * List; as previously stated, RenderedImage method * invocations are forwarded to the RenderedImage at position * zero in the List. * * @since JAI 1.1 * @see CollectionImage * @see java.awt.image.RenderedImage * @see java.util.List */ public class RenderedImageList extends CollectionImage implements List, RenderedImage, Serializable { /** * Creates an empty RenderedImageList. */ protected RenderedImageList() { super(); } /** * Creates a RenderedImageList from the supplied * List. * * @throws IllegalArgumentException if any objects in the * List are not RenderedImages. * @throws IllegalArgumentException if the List * is empty. * @throws IllegalArgumentException if the List * parameter is null. */ public RenderedImageList(List renderedImageList) { super(); // separate throws, for better error reporting if ( renderedImageList == null ) { throw new IllegalArgumentException(JaiI18N.getString("RenderedImageList0")); } if ( renderedImageList.isEmpty() ) { throw new IllegalArgumentException(JaiI18N.getString("RenderedImageList1")); } Iterator iter = renderedImageList.iterator(); imageCollection = new Vector(); while( iter.hasNext() ) { Object item = iter.next(); if ( item instanceof RenderedImage ) { imageCollection.add(item); } else { throw new IllegalArgumentException(JaiI18N.getString("RenderedImageList2")); } } } /// --- RenderedImage methods. --- /** * Returns the image Collection as a List. */ private List getList() { return (List)imageCollection; } /** * Returns the first image in the underlying list of * RenderedImages. * The call is forwarded to the first image in the List. */ public RenderedImage getPrimaryImage() { return (RenderedImage)getList().get(0); } /** * Returns the X coordinate of the leftmost column of the * primary image. * The call is forwarded to the first image in the List. */ public int getMinX() { return ((RenderedImage) getList().get(0)).getMinX(); } /** * Returns the X coordinate of the uppermost row of the primary image. * The call is forwarded to the first image in the List. */ public int getMinY() { return ((RenderedImage) getList().get(0)).getMinY(); } /** * Returns the width of the primary image. * The call is forwarded to the first image in the List. */ public int getWidth() { return ((RenderedImage) getList().get(0)).getWidth(); } /** * Returns the height of the primary image. * The call is forwarded to the first image in the List. */ public int getHeight() { return ((RenderedImage) getList().get(0)).getHeight(); } /** * Returns the width of a tile of the primary image. * The call is forwarded to the first image in the List. */ public int getTileWidth() { return ((RenderedImage) getList().get(0)).getTileWidth(); } /** * Returns the height of a tile of the primary image. * The call is forwarded to the first image in the List. */ public int getTileHeight() { return ((RenderedImage) getList().get(0)).getTileHeight(); } /** * Returns the X coordinate of the upper-left pixel of tile (0, 0) * of the primary image. * The call is forwarded to the first image in the List. */ public int getTileGridXOffset() { return ((RenderedImage) getList().get(0)).getTileGridXOffset(); } /** * Returns the Y coordinate of the upper-left pixel of tile (0, 0) * of the primary image. * The call is forwarded to the first image in the List. */ public int getTileGridYOffset() { return ((RenderedImage) getList().get(0)).getTileGridYOffset(); } /** * Returns the horizontal index of the leftmost column of tiles * of the primary image. * The call is forwarded to the first image in the List. */ public int getMinTileX() { return ((RenderedImage) getList().get(0)).getMinTileX(); } /** * Returns the number of tiles of the primary image along the * tile grid in the horizontal direction. * The call is forwarded to the first image in the List. */ public int getNumXTiles() { return ((RenderedImage) getList().get(0)).getNumXTiles(); } /** * Returns the vertical index of the uppermost row of tiles * of the primary image. * The call is forwarded to the first image in the List. */ public int getMinTileY() { return ((RenderedImage) getList().get(0)).getMinTileY(); } /** * Returns the number of tiles of the primary image along the * tile grid in the vertical direction. * The call is forwarded to the first image in the List. */ public int getNumYTiles() { return ((RenderedImage) getList().get(0)).getNumYTiles(); } /** * Returns the SampleModel of the primary image. * The call is forwarded to the first image in the List. */ public SampleModel getSampleModel() { return ((RenderedImage) getList().get(0)).getSampleModel(); } /** * Returns the ColorModel of the primary image. * The call is forwarded to the first image in the List. */ public ColorModel getColorModel() { return ((RenderedImage) getList().get(0)).getColorModel(); } /** * Gets a property from the property set of this image. If the * property name is not recognized, * java.awt.Image.UndefinedProperty will be returned. * The call is forwarded to the first image in the List. * * @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. * @throws IllegalArgumentException if name is * null. */ public Object getProperty(String name) { if ( name == null ) { throw new IllegalArgumentException(JaiI18N.getString("RenderedImageList0")); } return ((RenderedImage) getList().get(0)).getProperty(name); } /** * Returns a list of the properties recognized by this image. If * no properties are available, null will be * returned. * The call is forwarded to the first image in the List. * * @return an array of Strings representing valid * property names. */ public String[] getPropertyNames() { return ((RenderedImage) getList().get(0)).getPropertyNames(); } /** * Returns a Vector containing the image sources. * The call is forwarded to the first image in the List. */ public Vector getSources() { return ((RenderedImage) getList().get(0)).getSources(); } /** * Returns tile (tileX, tileY) of the * primary image as a Raster. Note that tileX * and tileY are indices into the tile array, not pixel * locations. * The call is forwarded to the first image in the List. * * @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. */ public Raster getTile(int tileX, int tileY) { return ((RenderedImage) getList().get(0)).getTile(tileX, tileY); } /** * Returns the entire primary image in a single Raster. * For images with multiple tiles this will require making a copy. * The returned Raster is semantically a copy. * The call is forwarded to the first image in the List. * * @return a Raster containing a copy of this image's data. */ public Raster getData() { return ((RenderedImage) getList().get(0)).getData(); } /** * Returns an arbitrary rectangular region of the primary image * in a Raster. The returned Raster is * semantically a copy. * The call is forwarded to the first image in the List. * * @param bounds the region of the RenderedImage to be * returned. */ public Raster getData(Rectangle bounds) { return ((RenderedImage) getList().get(0)).getData(bounds); } /** * Copies an arbitrary rectangular region of the primary image * into a caller-supplied WritableRaster. The region to be * computed is determined by clipping the bounds of the supplied * WritableRaster against the bounds of the image. The supplied * WritableRaster must have a SampleModel that is compatible with * that of the image. * *

      If the raster argument is null, the entire image will * be copied into a newly-created WritableRaster with a SampleModel * that is compatible with that of the image. * The call is forwarded to the first image in the List. * * @param dest a WritableRaster to hold the returned portion of * the image. * @return a reference to the supplied WritableRaster, or to a * new WritableRaster if the supplied one was null. */ public WritableRaster copyData(WritableRaster dest) { return ((RenderedImage) getList().get(0)).copyData(dest); } /// --- List methods. --- /** * Inserts the specified element at the specified position in this list. * * @throws IllegalArgumentException if the specified element * is not a RenderedImage. * @throws IndexOutOfBoundsException if the index is out of range * (index < 0 || index > size()). */ public void add(int index, Object element) { if ( element instanceof RenderedImage ) { if ( index >=0 && index <= imageCollection.size() ) { ((List)imageCollection).add(index, element); } else { throw new IndexOutOfBoundsException(JaiI18N.getString("RenderedImageList3")); } } else { throw new IllegalArgumentException(JaiI18N.getString("RenderedImageList2")); } } /** * Inserts into this List at the indicated position * all elements in the specified Collection which are * RenderedImages. * * @return true if this List changed * as a result of the call. * @throws IndexOutOfBoundsException if the index is out of range * (index < 0 || index > size()). */ public boolean addAll(int index, Collection c) { // Add only elements of c which are RenderedImages. if ( index < 0 || index > imageCollection.size() ) { throw new IndexOutOfBoundsException(JaiI18N.getString("RenderedImageList3")); } // Only allow RenderedImages Vector temp = null; Iterator iter = c.iterator(); while( iter.hasNext() ) { Object o = iter.next(); if ( o instanceof RenderedImage ) { if ( temp == null ) { temp = new Vector(); } temp.add( o ); } } return ((List)imageCollection).addAll(index, temp); } /** * @return the RenderedImage object at the * specified index. * * @param index index of element to return. * @return the element at the specified position in this list. * * @throws IndexOutOfBoundsException if the index is out of range (index * < 0 || index >= size()). */ public Object get(int index) { if ( index < 0 || index >= imageCollection.size() ) { throw new IndexOutOfBoundsException(JaiI18N.getString("RenderedImageList3")); } return ((List)imageCollection).get(index); } public int indexOf(Object o) { // Do not throw an IllegalArgumentException even // if o is not a RenderedImage. return ((List)imageCollection).indexOf(o); } public int lastIndexOf(Object o) { // Do not throw an IllegalArgumentException even // if o is not a RenderedImage. return ((List)imageCollection).lastIndexOf(o); } public ListIterator listIterator() { return ((List)imageCollection).listIterator(); } public ListIterator listIterator(int index) { return ((List)imageCollection).listIterator(index); } public Object remove(int index) { return ((List)imageCollection).remove(index); } public Object set(int index, Object element) { if ( element instanceof RenderedImage ) { return ((List)imageCollection).set(index, element); } throw new IllegalArgumentException(JaiI18N.getString("RenderedImageList2")); } public List subList(int fromIndex, int toIndex) { return ((List)imageCollection).subList(fromIndex, toIndex); } // --- Collection methods: overridden to require RenderedImages. --- /** * Adds the specified object to this List. * * @throws IllegalArgumentException if o is null * or is not an RenderedImage. * * @return true if and only if the parameter is added to the * List. */ public boolean add(Object o) { if ( o == null ) { throw new IllegalArgumentException(JaiI18N.getString("RenderedImageList0")); } if ( o instanceof RenderedImage ) { imageCollection.add(o); return true; } else { throw new IllegalArgumentException(JaiI18N.getString("RenderedImageList2")); } } /** * Adds to this List all elements in the specified * Collection which are RenderedImages. * * @return true if this List changed * as a result of the call. */ public boolean addAll(Collection c) { Iterator iter = c.iterator(); boolean status = false; while( iter.hasNext() ) { Object o = iter.next(); if ( o instanceof RenderedImage ) { imageCollection.add(o); status = true; } } return status; } } jai-core-1.1.4/src/share/classes/javax/media/jai/PropertySource.java0000644000175000017500000001274210203035544025213 0ustar mathieumathieu/* * $RCSfile: PropertySource.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:17 $ * $State: Exp $ */ package javax.media.jai; /** * An interface encapsulating the set of operations involved in * identifying and reading properties. * *

      The interface includes the getProperty() and * getPropertyNames() methods familiar from the * RenderedImage and RenderableImage interfaces. * Classes which implement this interface should do so in a manner which * treats the property names as case-retentive. That is to say that as * concerns operations effected using the property name as a key, the * operation should ignore the case of the property name while on the * other hand the property name retrieval methods should return the names * with their case preserved. * *

      PropertySource is implemented by the ImageJAI * interface and, among other classes, by PlanarImage, * RenderableOp and CollectionImage. * Since all RenderedImages used with JAI are "wrapped" * by a RenderedImageAdapter, all JAI RenderedImages * may be assumed to implement PropertySource. * *

      If a PropertySource is also a * PropertyChangeEmitter * then it should fire PropertySourceChangeEvents to all * registered listeners whenever this is reasonable to do so in the context * of the PropertySource in question. * *

      Property name space collisions may be prevented by adhering to * an hierarchical naming convention. This could for example be based * on the name of the package in question, e.g., * com.sun.media.jai.MyProperty. * The names of properties generated by JAI itself will not adhere to the * aforementioned naming convention, but this should not pose a problem if * users adopt this convention for their own property names. *

      Another approach to handling multiple property name spaces would * be to define a separate PropertySource for each name space of properties. * These PropertySources could themselves be attached to images as properties. * Inheritance of these properties would occur by the default mechanism. * Modification of these properties within an operation chain could be * managed by PropertyGenerators which are capable of recognizing these * properties. Note that a potential problem with this approach exists * when a PropertySourceChangeEvent is fired: it might be * necessary to clone the entire tree of properties in order to obtain * the old value of event object. * * @see ImageJAI * @see PlanarImage * @see PropertyChangeEmitter * @see WritablePropertySource * @see java.awt.image.RenderedImage * @see java.awt.image.renderable.RenderableImage */ public interface PropertySource { /** * Returns an array of Strings recognized as names by * this property source. If no properties are available, * null will be returned. * * @return an array of Strings giving the valid * property names or null. */ String[] getPropertyNames(); /** * Returns an array of Strings recognized as names by * this property source that begin with the supplied prefix. If * no property names match, null will be returned. * The comparison is done in a case-independent manner. * * @return an array of Strings giving the valid * property names. * * @exception IllegalArgumentException if prefix * is null. */ String[] getPropertyNames(String prefix); /** * Returns the class expected to be returned by a request for * the property with the specified name. If this information * is unavailable, null will be returned indicating * that getProperty(propertyName).getClass() should * be executed instead. A null value might * be returned for example to prevent generating the value of * a deferred property solely to obtain its class. null * will also be returned if the requested property is not emitted * by this property source. * * @param propertyName the name of the property, as a String. * * @return The Class expected to be return by a * request for the value of this property or null. * * @exception IllegalArgumentException if propertyName * is null. * * @since JAI 1.1 */ Class getPropertyClass(String propertyName); /** * Returns the value of a property. If the property name is not * recognized, java.awt.Image.UndefinedProperty will * be returned. * * @param propertyName the name of the property, as a String. * * @return the value of the property, as an * Object, or the value * java.awt.Image.UndefinedProperty. * * @exception IllegalArgumentException if propertyName * is null. */ Object getProperty(String propertyName); } jai-core-1.1.4/src/share/classes/javax/media/jai/AreaOpImage.java0000644000175000017500000004621010203035544024315 0ustar mathieumathieu/* * $RCSfile: AreaOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:03 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.awt.Point; import java.util.Map; import java.util.Vector; import javax.media.jai.util.CaselessStringKey; /** * An abstract base class for image operators that require only a * fixed rectangular source region around a source pixel in order to * compute each destination pixel. * *

      The source and the destination images will occupy the same * region of the plane. A given destination pixel (x, y) may be * computed from the neighborhood of source pixels beginning at (x - * leftPadding, y - topPadding) and extending to (x + rightPadding, y * + bottomPadding) inclusive. * *

      Since this operator needs a region around the source pixel in * order to compute the destination pixel, the border destination pixels * cannot be computed without any source extension. The source extension * can be specified by supplying a BorderExtender that will define the * pixel values of the source outside the actual source area. * *

      If no extension is specified, the destination samples that * cannot be computed will be written in the destination as zero. If * the source image begins at pixel (minX, minY) and has width w and * height h, the result of performing an area operation will be an * image beginning at minX, minY, and having a width of w and a height * of h, with the area being computed and written starting at (minX + * leftPadding, minY + topPadding) and having width Math.max(w - * leftPadding - rightPadding, 0) and height Math.max(h - topPadding * - bottomPadding, 0). * *

      A RenderingHints for * JAI.KEY_REPLACE_INDEX_COLOR_MODEL with the value of * Boolean.TRUE will automatically be added to the given * configuration and passed up to the superclass constructor * so that area operations are performed on the pixel values instead * of being performed on the indices into the color map for those * operations whose source(s) have an IndexColorModel. * This addition will only take place if a value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL has not already been * provided by the user. Note that the configuration Map * is cloned before the new hint is added to it. Regarding the value * for the JAI.KEY_REPLACE_INDEX_COLOR_MODEL * RenderingHints, the operator itself can be smart * based on the parameters, i.e. while the default value for * the JAI.KEY_REPLACE_INDEX_COLOR_MODEL is * Boolean.TRUE for operations that extend this class, * in some cases the operator could set the default. * * @see BorderExtender */ public abstract class AreaOpImage extends OpImage { /** * The number of source pixels needed to the left of the central pixel. */ protected int leftPadding; /** * The number of source pixels needed to the right of the central pixel. */ protected int rightPadding; /** The number of source pixels needed above the central pixel. */ protected int topPadding; /** The number of source pixels needed below the central pixel. */ protected int bottomPadding; /** The BorderExtender, may be null. */ protected BorderExtender extender = null; private Rectangle theDest; /** Verify that a specified bounds overlaps that of the source image. */ private static ImageLayout layoutHelper(ImageLayout layout, RenderedImage source) { // If at least one of the bounds variables is set then // check the overlap with the source image. if(layout != null && source != null && (layout.getValidMask() & (ImageLayout.MIN_X_MASK | ImageLayout.MIN_Y_MASK | ImageLayout.WIDTH_MASK | ImageLayout.HEIGHT_MASK)) != 0) { // Get the source bounds. Rectangle sourceRect = new Rectangle(source.getMinX(), source.getMinY(), source.getWidth(), source.getHeight()); // Create destination bounds defaulting un-set variables to // their respective source values as is done in the superclass // OpImage constructor. Rectangle dstRect = new Rectangle(layout.getMinX(source), layout.getMinY(source), layout.getWidth(source), layout.getHeight(source)); // Check for empty intersection. if(dstRect.intersection(sourceRect).isEmpty()) { throw new IllegalArgumentException( JaiI18N.getString("AreaOpImage0")); } } return layout; } private static Map configHelper(Map configuration) { Map config; if (configuration == null) { config = new RenderingHints(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.TRUE); } else { config = configuration; // If the user specified a value for this hint, we don't // want to change that if (!config.containsKey(JAI.KEY_REPLACE_INDEX_COLOR_MODEL)) { RenderingHints hints = new RenderingHints(null); // This is effectively a clone of configuration hints.putAll(configuration); config = hints; config.put(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.TRUE); } } return config; } /** * Constructs an AreaOpImage. The layout variables * are set in the standard way by the OpImage constructor. * *

      Additional control over the image bounds, tile grid layout, * SampleModel, and ColorModel may be * obtained by specifying an ImageLayout parameter. * If the image bounds are specified but do not overlap the source * bounds then an IllegalArgumentException will be thrown. * This parameter will be passed to the superclass constructor * unchanged. * *

      A RenderingHints for * JAI.KEY_REPLACE_INDEX_COLOR_MODEL with the value of * Boolean.TRUE will automatically be added to the given * configuration and passed up to the superclass constructor * so that area operations are performed on the pixel values instead * of being performed on the indices into the color map for those * operations whose source(s) have an IndexColorModel. * This addition will only take place if a value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL has not already been * provided by the user. Note that the configuration Map * is cloned before the new hint is added to it. * * @param source A RenderedImage. * @param layout An ImageLayout containing the source * dimensions before padding, and optionally containing the * tile grid layout, SampleModel, and * ColorModel. * @param configuration Configurable attributes of the image including * configuration variables indexed by * RenderingHints.Keys and image properties indexed * by Strings or CaselessStringKeys. * This is simply forwarded to the superclass constructor. * @param cobbleSources A boolean indicating whether * computeRect() expects contiguous sources. * @param extender A BorderExtender, or null. * @param leftPadding The desired left padding. * @param rightPadding The desired right padding. * @param topPadding The desired top padding. * @param bottomPadding The desired bottom padding. * * @throws IllegalArgumentException if source * is null. * @throws IllegalArgumentException if the user-specified bounds do * intersect the source bounds. * * @since JAI 1.1 */ public AreaOpImage(RenderedImage source, ImageLayout layout, Map configuration, boolean cobbleSources, BorderExtender extender, int leftPadding, int rightPadding, int topPadding, int bottomPadding) { super(vectorize(source), // vectorize() checks for null source. layoutHelper(layout, source), configHelper(configuration), cobbleSources); this.extender = extender; this.leftPadding = leftPadding; this.rightPadding = rightPadding; this.topPadding = topPadding; this.bottomPadding = bottomPadding; if (extender == null) { int d_x0 = getMinX() + leftPadding; int d_y0 = getMinY() + topPadding; int d_w = getWidth() - leftPadding - rightPadding; d_w = Math.max(d_w, 0); int d_h = getHeight() - topPadding - bottomPadding; d_h = Math.max(d_h, 0); theDest = new Rectangle(d_x0, d_y0, d_w, d_h); } else { theDest = getBounds(); } } /** * Returns the number of pixels needed to the left of the central pixel. * * @return The left padding factor. */ public int getLeftPadding() { return leftPadding; } /** * Returns the number of pixels needed to the right of the central pixel. * * @return The right padding factor. */ public int getRightPadding() { return rightPadding; } /** Returns the number of pixels needed above the central pixel. * * @return The top padding factor. */ public int getTopPadding() { return topPadding; } /** Returns the number of pixels needed below the central pixel. * * @return The bottom padding factor. */ public int getBottomPadding() { return bottomPadding; } /** * Retrieve the BorderExtender object associated with * this class instance. The object is returned by reference. * * @return The associated BorderExtender object * or null. * * @since JAI 1.1 */ public BorderExtender getBorderExtender() { return extender; } /** * Returns a conservative estimate of the destination region that * can potentially be affected by the pixels of a rectangle of a * given source. The resulting Rectangle is not * clipped to the destination image bounds. * * @param sourceRect the Rectangle in source coordinates. * @param sourceIndex the index of the source image. * @return a Rectangle indicating the potentially affected * destination region, or null if the region is * unknown. * * @throws IllegalArgumentException if sourceIndex is * negative or greater than the index of the last source. * @throws IllegalArgumentException if sourceRect is * null. */ public Rectangle mapSourceRect(Rectangle sourceRect, int sourceIndex) { if ( sourceRect == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sourceIndex < 0 || sourceIndex >= getNumSources()) { throw new IllegalArgumentException( JaiI18N.getString("Generic1")); } int lpad = getLeftPadding(); int rpad = getRightPadding(); int tpad = getTopPadding(); int bpad = getBottomPadding(); return new Rectangle(sourceRect.x + lpad, sourceRect.y + tpad, sourceRect.width - lpad - rpad, sourceRect.height - tpad - bpad); } /** * Returns a conservative estimate of the region of a specified * source that is required in order to compute the pixels of a * given destination rectangle. The resulting Rectangle * is not clipped to the source image bounds. * * @param destRect the Rectangle in destination coordinates. * @param sourceIndex the index of the source image. * * @return a Rectangle indicating the required source region. * * @throws IllegalArgumentException if sourceIndex is * negative or greater than the index of the last source. * @throws IllegalArgumentException if destRect is * null. */ public Rectangle mapDestRect(Rectangle destRect, int sourceIndex) { if ( destRect == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sourceIndex < 0 || sourceIndex >= getNumSources()) { throw new IllegalArgumentException( JaiI18N.getString("Generic1")); } int lpad = getLeftPadding(); int rpad = getRightPadding(); int tpad = getTopPadding(); int bpad = getBottomPadding(); return new Rectangle(destRect.x - lpad, destRect.y - tpad, destRect.width + lpad + rpad, destRect.height + tpad + bpad); } /** * Computes a tile. If source cobbling was requested at * construction time, the source tile boundaries are overlayed * onto the destination, cobbling is performed for areas that * intersect multiple source tiles, and * computeRect(Raster[], WritableRaster, Rectangle) * is called for each of the resulting regions. Otherwise, * computeRect(PlanarImage[], WritableRaster, * Rectangle) is called once to compute the entire active * area of the tile. * *

      The image bounds may be larger than the bounds of the * source image. In this case, samples for which there are no * no corresponding sources are set to zero. * * @param tileX The X index of the tile. * @param tileY The Y index of the tile. * * @return The tile as a Raster. */ public Raster computeTile(int tileX, int tileY) { if (!cobbleSources) { return super.computeTile(tileX, tileY); } /* Create a new WritableRaster to represent this tile. */ Point org = new Point(tileXToX(tileX), tileYToY(tileY)); WritableRaster dest = createWritableRaster(sampleModel, org); /* Clip output rectangle to image bounds. */ Rectangle rect = new Rectangle(org.x, org.y, sampleModel.getWidth(), sampleModel.getHeight()); Rectangle destRect = rect.intersection(theDest); if ((destRect.width <= 0) || (destRect.height <= 0)) { return dest; } /* account for padding in srcRectangle */ PlanarImage s = getSource(0); // Fix 4639755: Area operations throw exception for // destination extending beyond source bounds // The default dest image area is the same as the source // image area. However, when an ImageLayout hint is set, // this might be not true. So the destRect should be the // intersection of the provided rectangle, the destination // bounds and the source bounds. destRect = destRect.intersection(s.getBounds()); Rectangle srcRect = new Rectangle(destRect); srcRect.x -= getLeftPadding(); srcRect.width += getLeftPadding() + getRightPadding(); srcRect.y -= getTopPadding(); srcRect.height += getTopPadding() + getBottomPadding(); /* * The tileWidth and tileHeight of the source image * may differ from this tileWidth and tileHeight. */ IntegerSequence srcXSplits = new IntegerSequence(); IntegerSequence srcYSplits = new IntegerSequence(); // there is only one source for an AreaOpImage s.getSplits(srcXSplits, srcYSplits, srcRect); // Initialize new sequences of X splits. IntegerSequence xSplits = new IntegerSequence(destRect.x, destRect.x + destRect.width); xSplits.insert(destRect.x); xSplits.insert(destRect.x + destRect.width); srcXSplits.startEnumeration(); while (srcXSplits.hasMoreElements()) { int xsplit = srcXSplits.nextElement(); int lsplit = xsplit - getLeftPadding(); int rsplit = xsplit + getRightPadding(); xSplits.insert(lsplit); xSplits.insert(rsplit); } // Initialize new sequences of Y splits. IntegerSequence ySplits = new IntegerSequence(destRect.y, destRect.y + destRect.height); ySplits.insert(destRect.y); ySplits.insert(destRect.y + destRect.height); srcYSplits.startEnumeration(); while (srcYSplits.hasMoreElements()) { int ysplit = srcYSplits.nextElement(); int tsplit = ysplit - getBottomPadding(); int bsplit = ysplit + getTopPadding(); ySplits.insert(tsplit); ySplits.insert(bsplit); } /* * Divide destRect into sub rectangles based on the source splits, * and compute each sub rectangle separately. */ int x1, x2, y1, y2; Raster[] sources = new Raster[1]; ySplits.startEnumeration(); for (y1 = ySplits.nextElement(); ySplits.hasMoreElements(); y1 = y2) { y2 = ySplits.nextElement(); int h = y2 - y1; int py1 = y1 - getTopPadding(); int py2 = y2 + getBottomPadding(); int ph = py2 - py1; xSplits.startEnumeration(); for (x1 = xSplits.nextElement(); xSplits.hasMoreElements(); x1 = x2) { x2 = xSplits.nextElement(); int w = x2 - x1; int px1 = x1 - getLeftPadding(); int px2 = x2 + getRightPadding(); int pw = px2 - px1; // Fetch the padded src rectangle Rectangle srcSubRect = new Rectangle(px1, py1, pw, ph); sources[0] = (extender != null) ? s.getExtendedData(srcSubRect, extender) : s.getData(srcSubRect); // Make a destRectangle Rectangle dstSubRect = new Rectangle(x1,y1,w,h); computeRect(sources, dest, dstSubRect); // Recycle the source tile if(s.overlapsMultipleTiles(srcSubRect)) { recycleTile(sources[0]); } } } return dest; } } jai-core-1.1.4/src/share/classes/javax/media/jai/WritableRenderedImageAdapter.java0000644000175000017500000001365110203035544027674 0ustar mathieumathieu/* * $RCSfile: WritableRenderedImageAdapter.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:25 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Point; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.awt.image.WritableRenderedImage; import java.awt.image.TileObserver; /** * A PlanarImage wrapper for a * WritableRenderedImage. The tile layout, sample model, * and so forth are preserved. Calls to getTile() and so * forth are forwarded. * *

      From JAI's point of view, this image is a * PlanarImage of unknown type, with no sources, and * additionally an implementer of the * WritableRenderedImage interface. The image's pixel * data appear to be variable. * *

      The class and all its methods are marked final in * order to allow dynamic inlining to take place. This should * eliminate any performance penalty associated with the use of an * adapter class. * * @see PlanarImage * @see RenderedImageAdapter * @see java.awt.image.RenderedImage * @see java.awt.image.WritableRenderedImage * */ public final class WritableRenderedImageAdapter extends RenderedImageAdapter implements WritableRenderedImage { /** The WritableRenderedImage being adapted. */ private WritableRenderedImage theWritableImage; /** * Constructs a WritableRenderedImageAdapter. * * @param im A WritableRenderedImage to be `wrapped' * as a PlanarImage. * @throws IllegalArgumentException if im is * null. */ public WritableRenderedImageAdapter(WritableRenderedImage im) { super(im); theWritableImage = im; } /** * Adds an observer. If the observer is already present, * it will receive multiple notifications. * * @param tileObserver The TileObserver to be added. * @throws IllegalArgumentException if * tileObserver is null. */ public final void addTileObserver(TileObserver tileObserver) { if (tileObserver == null) { throw new IllegalArgumentException(JaiI18N.getString("WritableRenderedImageAdapter0")); } theWritableImage.addTileObserver(tileObserver); } /** * 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 tileObserver The TileObserver to be removed. * @throws IllegalArgumentException if * tileObserver is null. */ public final void removeTileObserver(TileObserver tileObserver) { if (tileObserver == null) { throw new IllegalArgumentException(JaiI18N.getString("WritableRenderedImageAdapter0")); } theWritableImage.removeTileObserver(tileObserver); } /** * 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 The tile as a WritableRaster. */ public final WritableRaster getWritableTile(int tileX, int tileY) { return theWritableImage.getWritableTile(tileX, 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 final void releaseWritableTile(int tileX, int tileY) { theWritableImage.releaseWritableTile(tileX, 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 the tile currently has writers. */ public final boolean isTileWritable(int tileX, int tileY) { return theWritableImage.isTileWritable(tileX, tileY); } /** * Returns an array of Point objects indicating which tiles * are checked out for writing. * * @return an array of Points or null if no * tiles are checked out for writing. */ public final Point[] getWritableTileIndices() { return theWritableImage.getWritableTileIndices(); } /** * Returns whether any tile is checked out for writing. * Semantically equivalent to (getWritableTiles().size() != 0). * * @return true if any tile currently has writers. */ public final boolean hasTileWriters() { return theWritableImage.hasTileWriters(); } /** * Sets a rectangular region of the image to the contents of * raster. * * @param raster A Raster. * @throws IllegalArgumentException if raster is * null. */ public final void setData(Raster raster) { if (raster == null) { throw new IllegalArgumentException(JaiI18N.getString("WritableRenderedImageAdapter1")); } theWritableImage.setData(raster); } } jai-core-1.1.4/src/share/classes/javax/media/jai/ROI.java0000644000175000017500000011063410203035544022636 0ustar mathieumathieu/* * $RCSfile: ROI.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:18 $ * $State: Exp $ */ package javax.media.jai; import com.sun.media.jai.util.ImageUtil; import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.Area; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.image.DataBuffer; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderedImageFactory; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Arrays; import java.util.Hashtable; import java.util.LinkedList; import java.util.ListIterator; import java.util.Vector; import javax.media.jai.iterator.RandomIter; import javax.media.jai.iterator.RandomIterFactory; import javax.media.jai.iterator.RectIter; import javax.media.jai.iterator.RectIterFactory; import javax.media.jai.remote.SerializableState; import javax.media.jai.remote.SerializerFactory; /** * The parent class for representations of a region of interest of an * image (currently only single band images with integral data types * are supported). * This class represents region information in image form, and * can thus be used as a fallback where a Shape * representation is unavailable. Where possible, subclasses such as * ROIShape are used since they provide a more compact means of * storage for large regions. * *

      The getAsShape() method may be called optimistically on any * instance of ROI; however, it may return null to indicate that a * Shape representation of the ROI is not available. In * this case, getAsImage() should be called as a fallback. * *

      Inclusion and exclusion of pixels is defined by a threshold value. * Pixel values greater than or equal to the threshold indicate inclusion. * */ public class ROI implements Serializable { /** A RandomIter used to grab pixels from the ROI. */ private transient RandomIter iter = null; /** The PlanarImage representation of the ROI. */ transient PlanarImage theImage = null; /** The inclusion/exclusion threshold of the ROI. */ int threshold = 127; /** * Merge a LinkedList of Rectangles * representing run lengths of pixels in the ROI into a minimal * list wherein vertically abutting Rectangles are * merged. The operation is effected in place. * * @param rectList The list of run length Rectangles. * @throws IllegalArgumentException if rectList is null. * @return The merged list. */ protected static LinkedList mergeRunLengthList(LinkedList rectList) { if ( rectList == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } // Merge the run length rectangles if more than one was detected. if (rectList.size() > 1) { // Traverse the list sequentially merging all subsequent // vertically abutting Rectangles with the same abscissa // origin and width with the current starting Rectangle. for (int mergeIndex = 0; mergeIndex < rectList.size() - 1; mergeIndex++) { ListIterator rectIter = rectList.listIterator(mergeIndex); Rectangle mergeRect = (Rectangle)rectIter.next(); while (rectIter.hasNext()) { Rectangle runRect = (Rectangle)rectIter.next(); // Calculate ordinate value of abutting rectangle. int abuttingY = mergeRect.y + mergeRect.height; if (runRect.y == abuttingY && runRect.x == mergeRect.x && runRect.width == mergeRect.width) { mergeRect = new Rectangle(mergeRect.x, mergeRect.y, mergeRect.width, mergeRect.height + runRect.height); // Remove "runRect" from the list. rectIter.remove(); // Replace "mergeRect" with updated version. rectList.set(mergeIndex, (Object)mergeRect); } else if (runRect.y > abuttingY) { // All Rectangles in the list with index greater than // mergeIndex are runlength Rectangles and are sorted // in non-decreasing ordinate order. Therefore there // are no more Rectangles which could possibly be // merged with mergeRect. break; } } } } return rectList; } /** * The default constructor. * * Using this constructor means that the subclass must override * all methods that reference theImage. */ protected ROI() {} /** * Constructs an ROI from a RenderedImage. The inclusion * threshold is taken to be halfway between the minimum and maximum * sample values specified by the image's SampleModel. * * @param im A single-banded RenderedImage. * * @throws IllegalArgumentException if im is null. * @throws IllegalArgumentException if im does not have exactly one band */ public ROI(RenderedImage im) { this(im, 127); } /** * Constructs an ROI from a RenderedImage. The inclusion * threshold is specified explicitly. * * @param im A single-banded RenderedImage. * @param threshold The desired inclusion threshold. * * @throws IllegalArgumentException if im is null. * @throws IllegalArgumentException if im does not have exactly one band */ public ROI(RenderedImage im, int threshold) { if (im == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } SampleModel sm = im.getSampleModel(); if (sm.getNumBands() != 1) { throw new IllegalArgumentException(JaiI18N.getString("ROI0")); } this.threshold = threshold; // If the image is already binary and the threshold is >1 // then there is no work to do. if ((threshold >= 1) && ImageUtil.isBinary(sm)) { theImage = PlanarImage.wrapRenderedImage(im); // Otherwise binarize the image for efficiency. } else { ParameterBlockJAI pbj = new ParameterBlockJAI("binarize"); pbj.setSource("source0", im); pbj.setParameter("threshold", (double)threshold); theImage = JAI.create("binarize", pbj, null); } } /** Get the iterator, construct it if need be. */ private RandomIter getIter() { if (iter == null) { iter = RandomIterFactory.create(theImage, null); } return iter; } /** Returns the inclusion/exclusion threshold value. */ public int getThreshold() { return threshold; } /** Sets the inclusion/exclusion threshold value. */ public void setThreshold(int threshold) { this.threshold = threshold; ((RenderedOp)theImage).setParameter((double)threshold, 0); iter = null; getIter(); } /** Returns the bounds of the ROI as a Rectangle. */ public Rectangle getBounds() { return new Rectangle(theImage.getMinX(), theImage.getMinY(), theImage.getWidth(), theImage.getHeight()); } /** Returns the bounds of the ROI as a Rectangle2D. */ public Rectangle2D getBounds2D() { return new Rectangle2D.Float((float) theImage.getMinX(), (float) theImage.getMinY(), (float) theImage.getWidth(), (float) theImage.getHeight()); } /** * Returns true if the ROI contains a given Point. * * @param p A Point identifying the pixel to be queried. * @throws IllegalArgumentException if p is null. * @return true if the pixel lies within the ROI. */ public boolean contains(Point p) { if ( p == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return contains(p.x, p.y); } /** * Returns true if the ROI contains a given Point2D. * * @param p A Point2D identifying the pixel to be queried. * @throws IllegalArgumentException if p is null. * @return true if the pixel lies within the ROI. */ public boolean contains(Point2D p) { if ( p == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return contains((int) p.getX(), (int) p.getY()); } /** * Returns true if the ROI contains the point (x, y). * * @param x An int specifying the X coordinate of the pixel to be queried. * @param y An int specifying the Y coordinate of the pixel to be queried. * @return true if the pixel lies within the ROI. */ public boolean contains(int x, int y) { int minX = theImage.getMinX(); int minY = theImage.getMinY(); return (x >= minX && x < minX + theImage.getWidth()) && (y >= minY && y < minY + theImage.getHeight()) && (getIter().getSample(x, y, 0) >= 1); } /** * Returns true if the ROI contain the point (x, y). * * @param x A double specifying the X coordinate of the pixel * to be queried. * @param y A double specifying the Y coordinate of the pixel * to be queried. * @return true if the pixel lies within the ROI. */ public boolean contains(double x, double y) { return contains((int) x, (int) y); } /** * Returns true if a given Rectangle is * entirely included within the ROI. * * @param rect A Rectangle specifying the region to be tested * for inclusion. * @throws IllegalArgumentException if rect is null. * @return true if the rectangle is entirely * contained within the ROI. */ public boolean contains(Rectangle rect) { if ( rect == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (!rect.equals(rect.intersection(getBounds()))) { return false; } byte[] packedData = ImageUtil.getPackedBinaryData(theImage.getData(), rect); // ImageUtil.getPackedBinaryData does not zero out the extra // bits used to pad to the nearest byte - therefore ignore // these bits. int leftover = rect.width % 8; if (leftover == 0) { for (int i = 0; i < packedData.length; i++) if ((packedData[i] & 0xff) != 0xff) return false; } else { int mask = ((1 << leftover) - 1) << (8 - leftover); for (int y = 0, k = 0; y < rect.height; y++) { for (int x = 0 ; x < rect.width-leftover; x += 8, k++) { if ((packedData[k] & 0xff) != 0xff) return false; } if ((packedData[k] & mask) != mask) return false; k++; } } return true; } /** * Returns true if a given Rectangle2D is * entirely included within the ROI. * * @param rect A Rectangle2D specifying the region to be * tested for inclusion. * @throws IllegalArgumentException if rect is null. * @return true if the rectangle is entirely contained * within the ROI. */ public boolean contains(Rectangle2D rect) { if ( rect == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } Rectangle r = new Rectangle((int) rect.getX(), (int) rect.getY(), (int) rect.getWidth(), (int) rect.getHeight()); return contains(r); } /** * Returns true if a given rectangle (x, y, w, h) is entirely * included within the ROI. * * @param x The int X coordinate of the upper left corner of the region. * @param y The int Y coordinate of the upper left corner of the region. * @param w The int width of the region. * @param h The int height of the region. * @return true if the rectangle is entirely contained * within the ROI. */ public boolean contains(int x, int y, int w, int h) { Rectangle r = new Rectangle(x, y, w, h); return contains(r); } /** * Returns true if a given rectangle (x, y, w, h) is entirely * included within the ROI. * * @param x The double X coordinate of the upper left corner of the region. * @param y The double Y coordinate of the upper left corner of the region. * @param w The double width of the region. * @param h The double height of the region. * * @return true if the rectangle is entirely * contained within the ROI. */ public boolean contains(double x, double y, double w, double h) { Rectangle rect = new Rectangle((int) x, (int) y, (int) w, (int) h); return contains(rect); } /** * Returns true if a given Rectangle * intersects the ROI. * * @param rect A Rectangle specifying the region to be tested * for inclusion. * @throws IllegalArgumentException if rect is null. * @return true if the rectangle intersects the ROI. */ public boolean intersects(Rectangle rect) { if ( rect == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } Rectangle r = rect.intersection(getBounds()); if (r.isEmpty()) { return false; } byte[] packedData = ImageUtil.getPackedBinaryData(theImage.getData(), r); // ImageUtil.getPackedBinaryData does not zero out the extra // bits used to pad to the nearest byte - therefore ignore // these bits. int leftover = r.width % 8; if (leftover == 0) { for (int i = 0; i < packedData.length; i++) if ((packedData[i] & 0xff) != 0) return true; } else { int mask = ((1 << leftover) - 1) << (8 - leftover); for (int y = 0, k = 0; y < r.height; y++) { for (int x = 0 ; x < r.width-leftover; x += 8, k++) { if ((packedData[k] & 0xff) != 0) return true; } if ((packedData[k] & mask) != 0) return true; k++; } } return false; } /** * Returns true if a given Rectangle2D * intersects the ROI. * * @param r A Rectangle2D specifying the region to be tested * for inclusion. * @throws IllegalArgumentException if r is null. * @return true if the rectangle intersects the ROI. */ public boolean intersects(Rectangle2D r) { if ( r == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } Rectangle rect = new Rectangle((int) r.getX(), (int) r.getY(), (int) r.getWidth(), (int) r.getHeight()); return intersects(rect); } /** * Returns true if a given rectangular region * intersects the ROI. * * @param x The int X coordinate of the upper left corner of the region. * @param y The int Y coordinate of the upper left corner of the region. * @param w The int width of the region. * @param h The int height of the region. * @return true if the rectangle intersects the ROI. */ public boolean intersects(int x, int y, int w, int h) { Rectangle rect = new Rectangle(x, y, w, h); return intersects(rect); } /** * Returns true if a given rectangular region * intersects the ROI. * * @param x The double X coordinate of the upper left corner of the region. * @param y The double Y coordinate of the upper left corner of the region. * @param w The double width of the region. * @param h The double height of the region. * @return true if the rectangle intersects the ROI. */ public boolean intersects(double x, double y, double w, double h) { Rectangle rect = new Rectangle((int) x, (int) y, (int) w, (int) h); return intersects(rect); } /** * Create a binary PlanarImage of the size/bounds specified by * the rectangle. */ private static PlanarImage createBinaryImage(Rectangle r) { if ((r.x == 0) && (r.y == 0)) { BufferedImage bi = new BufferedImage(r.width, r.height, BufferedImage.TYPE_BYTE_BINARY); return PlanarImage.wrapRenderedImage(bi); } else { SampleModel sm = new MultiPixelPackedSampleModel( DataBuffer.TYPE_BYTE, r.width, r.height, 1); // Create a TiledImage into which to write. return new TiledImage(r.x, r.y, r.width, r.height, r.x, r.y, sm, PlanarImage.createColorModel(sm)); } } /** * Creates a merged ROI by performing the specified image operation * on this image and the image of the specified ROI. * * @param ROI the ROI to merge with this * @param op the JAI operator to use for merge. * * @return the merged ROI */ private ROI createOpROI(ROI roi, String op) { if (roi == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } PlanarImage imThis = this.getAsImage(); PlanarImage imROI = roi.getAsImage(); PlanarImage imDest; Rectangle boundsThis = imThis.getBounds(); Rectangle boundsROI = imROI.getBounds(); // If the bounds of the two images do not match, then // expand as necessary to the union of the two bounds // using the "overlay" operator and then perform the JAI // operation. if (op.equals("and") || boundsThis.equals(boundsROI)) { imDest = JAI.create(op, imThis, imROI); } else if (op.equals("subtract") || boundsThis.contains(boundsROI)) { PlanarImage imBounds = createBinaryImage(boundsThis); imBounds = JAI.create("overlay", imBounds, imROI); imDest = JAI.create(op , imThis, imBounds); } else if (boundsROI.contains(boundsThis)) { PlanarImage imBounds = createBinaryImage(boundsROI); imBounds = JAI.create("overlay", imBounds, imThis); imDest = JAI.create(op , imBounds, imROI); } else { Rectangle merged = boundsThis.union(boundsROI); PlanarImage imBoundsThis = createBinaryImage(merged); PlanarImage imBoundsROI = createBinaryImage(merged); imBoundsThis = JAI.create("overlay", imBoundsThis, imThis); imBoundsROI = JAI.create("overlay", imBoundsROI , imROI ); imDest = JAI.create(op , imBoundsThis, imBoundsROI); } return new ROI(imDest, threshold); } /** * Adds another ROI to this one and returns the result * as a new ROI. The supplied ROI will * be converted to a rendered form if necessary. The bounds of the * resultant ROI will be the union of the bounds of the * two ROIs being merged. * * @param roi An ROI. * @throws IllegalArgumentException if roi is null. * @return A new ROI containing the new ROI data. */ public ROI add(ROI roi) { return createOpROI(roi, "add"); } /** * Subtracts another ROI from this one and returns the * result as a new ROI. The supplied ROI * will be converted to a rendered form if necessary. The * bounds of the resultant ROI will be the same as * this ROI. * * @param roi An ROI. * @throws IllegalArgumentException if roi is null. * @return A new ROI containing the new ROI data. */ public ROI subtract(ROI roi) { return createOpROI(roi, "subtract"); } /** * Intersects the ROI with another ROI and returns the result as * a new ROI. The supplied ROI will be converted to a rendered * form if necessary. The bounds of the resultant ROI will be the * intersection of the bounds of the two ROIs being merged. * * @param roi An ROI. * @throws IllegalArgumentException if roi is null. * @return A new ROI containing the new ROI data. */ public ROI intersect(ROI roi) { return createOpROI(roi, "and"); } /** * Exclusive-ors the ROI with another ROI * and returns the result as a new ROI. The supplied * ROI will be converted to a rendered form if * necessary. The bounds of the resultant ROI will * be the union of the bounds of the two ROIs being * merged. * * @param roi An ROI. * @throws IllegalArgumentException if roi is null. * @return A new ROI containing the new ROI data. */ public ROI exclusiveOr(ROI roi) { return createOpROI(roi, "xor"); } /** * Performs an affine transformation and returns the result as a new * ROI. The transformation is performed by an "Affine" RIF using the * indicated interpolation method. * * @param at an AffineTransform specifying the transformation. * @param interp the Interpolation to be used. * @throws IllegalArgumentException if at is null. * @throws IllegalArgumentException if interp is null. * @return a new ROI containing the transformed ROI data. */ public ROI transform(AffineTransform at, Interpolation interp) { if (at == null) { throw new IllegalArgumentException(JaiI18N.getString("ROI5")); } if (interp == null) { throw new IllegalArgumentException(JaiI18N.getString("ROI6")); } ParameterBlock paramBlock = new ParameterBlock(); paramBlock.add(at); paramBlock.add(interp); return performImageOp("Affine", paramBlock, 0, null); } /** * Performs an affine transformation and returns the result as a new * ROI. The transformation is performed by an "Affine" RIF using * nearest neighbor interpolation. * * @param at an AffineTransform specifying the transformation. * @throws IllegalArgumentException if at is null. * @return a new ROI containing the transformed ROI data. */ public ROI transform(AffineTransform at) { if ( at == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return transform(at, Interpolation.getInstance(Interpolation.INTERP_NEAREST)); } /** * Transforms an ROI using an imaging operation. The operation is * specified by a RenderedImageFactory. The * operation's ParameterBlock, minus the image source * itself is supplied, along with an index indicating where to * insert the ROI image. The renderHints argument * allows rendering hints to be passed in. * * @param RIF A RenderedImageFactory that will be used * to create the op. * @param paramBlock A ParameterBlock containing all * sources and parameters for the op except for the ROI itself. * @param sourceIndex The index of the ParameterBlock's * sources where the ROI is to be inserted. * @param renderHints A RenderingHints object containing * rendering hints, or null. * @throws IllegalArgumentException if RIF is null. * @throws IllegalArgumentException if paramBlock is null. */ public ROI performImageOp(RenderedImageFactory RIF, ParameterBlock paramBlock, int sourceIndex, RenderingHints renderHints) { if ( RIF == null || paramBlock == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } // Clone the ParameterBlock and insert a source ParameterBlock pb = (ParameterBlock) paramBlock.clone(); Vector sources = pb.getSources(); sources.insertElementAt(this.getAsImage(), sourceIndex); // Create a new RenderedImage based on the RIF // and ParameterBlock. RenderedImage im = RIF.create(pb, renderHints); return new ROI(im, threshold); } /** * Transforms an ROI using an imaging operation. The * operation is specified by name; the default JAI registry is * used to resolve this into a RIF. The operation's * ParameterBlock, minus the image source itself is supplied, * along with an index indicating where to insert the ROI image. * The renderHints argument allows rendering hints to * be passed in. * * @param name The name of the operation to perform. * @param paramBlock A ParameterBlock containing all * sources and parameters for the op except for the ROI itself. * @param sourceIndex The index of the ParameterBlock's * sources where the ROI is to be inserted. * @param renderHints A RenderingHints object containing * rendering hints, or null. * @throws IllegalArgumentException if name is null. * @throws IllegalArgumentException if paramBlock is null. */ public ROI performImageOp(String name, ParameterBlock paramBlock, int sourceIndex, RenderingHints renderHints) { if ( name == null || paramBlock == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } // Clone the ParameterBlock and insert a source ParameterBlock pb = (ParameterBlock) paramBlock.clone(); Vector sources = pb.getSources(); sources.insertElementAt(this.getAsImage(), sourceIndex); // Create a new RenderedImage based on the operation name // and ParameterBlock using the default registry. RenderedImage im = JAI.create(name, pb, renderHints); return new ROI(im, threshold); } /** * Returns a Shape representation of the * ROI, if possible. If none is available, null is * returned. A proper instance of ROI (one that is not * an instance of any subclass of ROI) will always * return null. * * @return The ROI as a Shape. */ public Shape getAsShape() { return null; } /** * Returns a PlanarImage representation of the * ROI. This method will always succeed. This method * returns a (bilevel) image whose SampleModel is an * instance of MultiPixelPackedSampleModel. * * @return The ROI as a PlanarImage. */ public PlanarImage getAsImage() { return theImage; } /** * Returns a bitmask for a given rectangular region of the ROI * indicating whether the pixel is included in the region of * interest. The results are packed into 32-bit integers, with * the MSB considered to lie on the left. The last entry in each * row of the result may have bits that lie outside of the * requested rectangle. These bits are guaranteed to be zeroed. * *

      The mask array, if supplied, must be of length * equal to or greater than height and each of its * subarrays must have length equal to or greater than (width + * 31)/32. If null is passed in, a suitable array * will be constructed. If the mask is non-null but has * insufficient size, an exception will be thrown. * * @param x The X coordinate of the upper left corner of the rectangle. * @param y The Y coordinate of the upper left corner of the rectangle. * @param width The width of the rectangle. * @param height The height of the rectangle. * @param mask A two-dimensional array of ints at least * (width + 31)/32 entries wide and (height) entries tall, * or null. * @return A reference to the mask parameter, or * to a newly constructed array if mask is * null. If the specified rectangle does * intersect with the image bounds then a null * is returned. */ public int[][] getAsBitmask(int x, int y, int width, int height, int[][] mask) { Rectangle rect = getBounds().intersection(new Rectangle(x, y, width, height)); // Verify that the requested area actually intersects the ROI image. if (rect.isEmpty()) { return null; } // Determine the minimum required width of the bitmask in integers. int bitmaskIntWidth = (width + 31)/32; // Construct bitmask array if argument is null. if (mask == null) { mask = new int[height][bitmaskIntWidth]; } else if (mask.length < height || mask[0].length < bitmaskIntWidth) { throw new RuntimeException(JaiI18N.getString("ROI3")); } byte[] data = ImageUtil.getPackedBinaryData( theImage.getData(), rect); // ImageUtil.getPackedBinaryData does not zero out the extra // bits used to pad to the nearest byte - so zero these // bits out. int leftover = rect.width % 8; if (leftover != 0) { int datamask = ((1 << leftover) - 1) << (8 - leftover); int linestride = (width + 7)/8; for (int i = linestride-1; i < data.length; i += linestride) { data[i] = (byte)(data[i] & datamask); } } int lineStride = (rect.width + 7)/8; int leftOver = lineStride % 4; int row, col, k; int ncols = (lineStride - leftOver) / 4; for (row = 0, k = 0; row < rect.height; row++) { int[] maskRow = mask[row]; for (col = 0; col < ncols; col++) { maskRow[col] = ((data[k ] & 0xff) << 24) | ((data[k+1] & 0xff) << 16) | ((data[k+2] & 0xff) << 8) | ((data[k+3] & 0xff) << 0); k += 4; } switch(leftOver) { case 0: break; case 1: maskRow[col++] = ((data[k ] & 0xff) << 24); break; case 2: maskRow[col++] = ((data[k ] & 0xff) << 24) | ((data[k+1] & 0xff) << 16); break; case 3: maskRow[col++] = ((data[k ] & 0xff) << 24) | ((data[k+1] & 0xff) << 16) | ((data[k+2] & 0xff) << 8); break; } k += leftOver; Arrays.fill(maskRow, col, bitmaskIntWidth, 0); } // Clear any trailing rows. for (row = rect.height; row < height; row++) { Arrays.fill(mask[row], 0); } return mask; } /** * Returns a LinkedList of Rectangles * for a given rectangular region of the ROI. The * Rectangles in the list are merged into a minimal * set. * * @param x The X coordinate of the upper left corner of the rectangle. * @param y The Y coordinate of the upper left corner of the rectangle. * @param width The width of the rectangle. * @param height The height of the rectangle. * @return A LinkedList of Rectangles. * If the specified rectangle does intersect with the image * bounds then a null is returned. */ public LinkedList getAsRectangleList(int x, int y, int width, int height) { return getAsRectangleList(x, y, width, height, true); } /** * Returns a LinkedList of Rectangles for * a given rectangular region of the ROI. * * @param x The X coordinate of the upper left corner of the rectangle. * @param y The Y coordinate of the upper left corner of the rectangle. * @param width The width of the rectangle. * @param height The height of the rectangle. * @param mergeRectangles true if the Rectangles * are to be merged into a minimal set. * @return A LinkedList of Rectangles. * If the specified rectangle does intersect with the image * bounds then a null is returned. */ protected LinkedList getAsRectangleList(int x, int y, int width, int height, boolean mergeRectangles) { // Verify that the requested area actually intersects the ROI image. Rectangle bounds = getBounds(); Rectangle rect = new Rectangle(x, y, width, height); if (!bounds.intersects(rect)) { return null; } // Clip the requested area to the ROI image if necessary. if (!bounds.contains(rect)) { rect = bounds.intersection(rect); x = rect.x; y = rect.y; width = rect.width; height = rect.height; } byte[] data = ImageUtil.getPackedBinaryData( theImage.getData(), rect); // ImageUtil.getPackedBinaryData does not zero out the extra // bits used to pad to the nearest byte - therefore ignore // these bits. int lineStride = (width + 7)/8; int leftover = width % 8; int mask = (leftover == 0) ? 0xff : ((1 << leftover) - 1) << (8 - leftover); LinkedList rectList = new LinkedList(); // Calculate the initial list of rectangles as a list of run // lengths which are in fact rectangles of unit height. int row, col, k, start, val, cnt; for (row = 0, k = 0; row < height; row++) { start = -1; for (col = 0, cnt = 0; col < lineStride; col++, k++) { val = data[k] & ((col == lineStride-1) ? mask : 0xff); if (val == 0) { if (start >= 0) { rectList.addLast( new Rectangle(x+start, y+row, col*8 - start, 1)); start = -1; } } else if (val == 0xff) { if (start < 0) { start = col*8; } } else { for (int bit = 7; bit >= 0; bit--) { if ((val & (1 << bit)) == 0x00) { if (start >= 0) { rectList.addLast(new Rectangle( x+start, y+row, col*8 + (7 - bit) - start, 1)); start = -1; } } else { if (start < 0) { start = col*8 + (7 - bit); } } } } } if (start >= 0) { rectList.addLast( new Rectangle(x+start, y+row, col*8 - start, 1)); } } // Return the list of Rectangles possibly merged into a minimal set. return mergeRectangles ? mergeRunLengthList(rectList) : rectList; } /** * Serialize the ROI. * * @param out The ObjectOutputStream. */ private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); if (theImage != null) { out.writeBoolean(true); RenderingHints hints = new RenderingHints(null); hints.put(JAI.KEY_SERIALIZE_DEEP_COPY, new Boolean(true)); out.writeObject(SerializerFactory.getState(theImage, hints)); } else { out.writeBoolean(false); } } /** * Deserialize the ROI. * * @param in The ObjectInputStream. */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); if ((boolean)in.readBoolean()) { SerializableState ss = (SerializableState)in.readObject(); RenderedImage ri =(RenderedImage)(ss.getObject()); theImage = PlanarImage.wrapRenderedImage(ri); } else { theImage = null; } iter = null; } } jai-core-1.1.4/src/share/classes/javax/media/jai/WarpAffine.java0000644000175000017500000004345410203035544024234 0ustar mathieumathieu/* * $RCSfile: WarpAffine.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:23 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; /** * A description of an Affine warp. * *

      The transform is specified as a mapping from destination * space to source space, a backward mapping, as opposed to the * forward mapping used in AffineOpImage. * *

      The source position (x', y') of a point (x, y) is given by the * first order (affine) bivariate polynomials: * *

       * x' = p(x, y) = c1 + c2*x + c3*y
       * y' = q(x, y) = c4 + c5*x + c6*y
       * 
      * *

      WarpAffine is marked final so that it may be more * easily inlined. * */ public final class WarpAffine extends WarpPolynomial { private float c1, c2, c3; // coefficients for X private float c4, c5, c6; // coefficients for Y private float invc1, invc2, invc3; // inverse xform coefficients for X private float invc4, invc5, invc6; // inverse xform coefficients for Y private AffineTransform transform; private AffineTransform invTransform; /** * @param transform * @return An array of floats. */ private static final float[] xCoeffsHelper(AffineTransform transform) { float[] coeffs = new float[3]; coeffs[0] = (float)transform.getTranslateX(); coeffs[1] = (float)transform.getScaleX(); coeffs[2] = (float)transform.getShearX(); return coeffs; } private static final float[] yCoeffsHelper(AffineTransform transform) { float[] coeffs = new float[3]; coeffs[0] = (float)transform.getTranslateY(); coeffs[1] = (float)transform.getShearY(); coeffs[2] = (float)transform.getScaleY(); return coeffs; } /** * Constructs a WarpAffine with a given transform mapping * destination pixels into source space. The transform is * given by: * *

           * x' = xCoeffs[0] + xCoeffs[1]*x + xCoeffs[2]*y;
           * y' = yCoeffs[0] + yCoeffs[1]*x + yCoeffs[2]*y;
           * 
      * * where x', y' are the source image coordinates * and x, y are the destination image coordinates. * * @param xCoeffs The 3 destination to source transform coefficients for * the X coordinate. * @param yCoeffs The 3 destination to source transform coefficients for * the Y coordinate. * @param preScaleX The scale factor to apply to input (dest) X positions. * @param preScaleY The scale factor to apply to input (dest) Y positions. * @param postScaleX The scale factor to apply to the evaluated x transform * @param postScaleY The scale factor to apply to the evaluated y transform * * @throws IllegalArgumentException if array xCoeffs or * yCoeffs does not have length of 3. */ public WarpAffine(float[] xCoeffs, float[] yCoeffs, float preScaleX, float preScaleY, float postScaleX, float postScaleY) { super(xCoeffs, yCoeffs, preScaleX, preScaleY, postScaleX, postScaleY); if (xCoeffs.length != 3 || yCoeffs.length != 3) { throw new IllegalArgumentException( JaiI18N.getString("WarpAffine0")); } c1 = xCoeffs[0]; c2 = xCoeffs[1]; c3 = xCoeffs[2]; c4 = yCoeffs[0]; c5 = yCoeffs[1]; c6 = yCoeffs[2]; transform = getTransform(); // Transform inversion may throw NoninvertibleTransformException try { invTransform = transform.createInverse(); invc1 = (float)invTransform.getTranslateX(); invc2 = (float)invTransform.getScaleX(); invc3 = (float)invTransform.getShearX(); invc4 = (float)invTransform.getTranslateY(); invc5 = (float)invTransform.getShearY(); invc6 = (float)invTransform.getScaleY(); } catch (java.awt.geom.NoninvertibleTransformException e) { // Transform can't be inverted, so set inverse to null invTransform = null; } } /** * Constructs a WarpAffine with pre- and post-scale * factors of 1. * * @param xCoeffs The 3 destination to source transform coefficients for * the X coordinate. * @param yCoeffs The 3 destination to source transform coefficients for * the Y coordinate. */ public WarpAffine(float[] xCoeffs, float[] yCoeffs) { this(xCoeffs, yCoeffs, 1.0F, 1.0F, 1.0F, 1.0F); } /** * Constructs a WarpAffine with a given transform mapping * destination pixels into source space. Note that this is * a backward mapping as opposed to the forward mapping used in * AffineOpImage. * * @param transform The destination to source transform. * @param preScaleX The scale factor to apply to source X positions. * @param preScaleY The scale factor to apply to source Y positions. * @param postScaleX The scale factor to apply to destination X positions. * @param postScaleY The scale factor to apply to destination Y positions. */ public WarpAffine(AffineTransform transform, float preScaleX, float preScaleY, float postScaleX, float postScaleY) { this(xCoeffsHelper(transform), yCoeffsHelper(transform), preScaleX, preScaleY, postScaleX, postScaleY); } /** * Constructs a WarpAffine with pre- and post-scale * factors of 1. * * @param transform An AffineTransform mapping dest to source * coordinates. */ public WarpAffine(AffineTransform transform) { this(transform, 1.0F, 1.0F, 1.0F, 1.0F); } /** * Returns a clone of the AffineTransform associated * with this WarpAffine object. * * @return An AffineTransform. */ public AffineTransform getTransform() { return new AffineTransform(c2, c5, c3, c6, c1, c4); } /** * Computes the source subpixel positions for a given rectangular * destination region, subsampled with an integral period. The * destination region is specified using normal integral (full * pixel) coordinates. The source positions returned by the * method are specified in floating point. * * @param x The minimum X coordinate of the destination region. * @param y The minimum Y coordinate of the destination region. * @param width The width of the destination region. * @param height The height of the destination region. * @param periodX The horizontal sampling period. * @param periodY The vertical sampling period. * * @param destRect A float array containing at least * 2*((width+periodX-1)/periodX)* * ((height+periodY-1)/periodY) * elements, or null. If null, a * new array will be constructed. * * @return A reference to the destRect parameter if * it is non-null, or a new * float array otherwise. */ public float[] warpSparseRect(int x, int y, int width, int height, int periodX, int periodY, float[] destRect) { //XXX: This method should do its calculations in doubles if (destRect == null) { destRect = new float[((width + periodX - 1) / periodX) * ((height + periodY - 1) / periodY) * 2]; } // // Original formula // x' = c1 + c2*x + c3*y // y' = c4 + c5*x + c6*y // // Take in preScale, postScale, and 0.5 shift // x' = (c1 + c2*(x+0.5)*preScaleX + // c3*(y+0.5)*preScaleY) * postScaleX - 0.5 // y' = (c4 + c5*(x+0.5)*preScaleX + // c6*(y+0.5)*preScaleY) * postScaleY - 0.5 // // The next point, increment by periodX // x' = (c1 + c2*(x+periodX+0.5)*preScaleX + // c3*(y+0.5)*preScaleY) * postScaleX - 0.5 // y' = (c4 + c5*(x+periodX+0.5)*preScaleX + // c6*(y+0.5)*preScaleY) * postScaleY - 0.5 // // The difference between the 2 points // dx = c2 * periodX * preScaleX * postScaleX // dy = c5 * periodX * preScaleX * postScaleY // float px1 = periodX * preScaleX; // power for period X float dx = c2 * px1 * postScaleX; // delta x for x poly float dy = c5 * px1 * postScaleY; // delta x for y poly float x1 = (x + 0.5F) * preScaleX; // power for x width += x; height += y; int index = 0; // destRect index for (int j = y; j < height; j += periodY) { float y1 = (j + 0.5F) * preScaleY; // power for current y // The warped position for the first point of the current line. float wx = (c1 + c2 * x1 + c3 * y1) * postScaleX - 0.5F; float wy = (c4 + c5 * x1 + c6 * y1) * postScaleY - 0.5F; for (int i = x; i < width; i += periodX) { destRect[index++] = wx; destRect[index++] = wy; wx += dx; wy += dy; } } return destRect; } /** * Computes a Rectangle that is guaranteed to enclose the region * of the source that is required in order to produce a given * rectangular output region. * * @param destRect The Rectangle in destination coordinates. * * @return A Rectangle in the source coordinate * system that is guaranteed to contain all pixels * referenced by the output of warpRect() on * the destination region, or null. * * @throws IllegalArgumentException if destRect is * null. */ public Rectangle mapDestRect(Rectangle destRect) { if ( destRect == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } int dx0 = destRect.x; int dx1 = destRect.x + destRect.width; int dy0 = destRect.y; int dy1 = destRect.y + destRect.height; float[] pt; float sx0, sx1, sy0, sy1; pt = mapDestPoint(dx0, dy0); sx0 = pt[0]; sx1 = pt[0]; sy0 = pt[1]; sy1 = pt[1]; pt = mapDestPoint(dx1, dy0); sx0 = Math.min(sx0, pt[0]); sx1 = Math.max(sx1, pt[0]); sy0 = Math.min(sy0, pt[1]); sy1 = Math.max(sy1, pt[1]); pt = mapDestPoint(dx0, dy1); sx0 = Math.min(sx0, pt[0]); sx1 = Math.max(sx1, pt[0]); sy0 = Math.min(sy0, pt[1]); sy1 = Math.max(sy1, pt[1]); pt = mapDestPoint(dx1, dy1); sx0 = Math.min(sx0, pt[0]); sx1 = Math.max(sx1, pt[0]); sy0 = Math.min(sy0, pt[1]); sy1 = Math.max(sy1, pt[1]); int x = (int)Math.floor(sx0); int y = (int)Math.floor(sy0); int w = (int)Math.ceil(sx1 - x); int h = (int)Math.ceil(sy1 - y); return new Rectangle(x, y, w, h); } /** * Computes a Rectangle that is guaranteed to enclose the region * of the destination to which the source rectangle maps. * * @param srcRect The Rectangle in source coordinates. * * @return A Rectangle in the destination coordinate * system that is guaranteed to contain all pixels * within the forward mapping of the source rectangle. * * @throws IllegalArgumentException if srctRect is * null. * * @since JAI 1.1 */ public Rectangle mapSourceRect(Rectangle srcRect) { if ( srcRect == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } // // According to spec, we return null if no forward // mapping can be derived. // if (invTransform == null) { return null; } int sx0 = srcRect.x; int sx1 = srcRect.x + srcRect.width; int sy0 = srcRect.y; int sy1 = srcRect.y + srcRect.height; float[] pt; float dx0, dx1, dy0, dy1; pt = mapSrcPoint(sx0, sy0); dx0 = pt[0]; dx1 = pt[0]; dy0 = pt[1]; dy1 = pt[1]; pt = mapSrcPoint(sx1, sy0); dx0 = Math.min(dx0, pt[0]); dx1 = Math.max(dx1, pt[0]); dy0 = Math.min(dy0, pt[1]); dy1 = Math.max(dy1, pt[1]); pt = mapSrcPoint(sx0, sy1); dx0 = Math.min(dx0, pt[0]); dx1 = Math.max(dx1, pt[0]); dy0 = Math.min(dy0, pt[1]); dy1 = Math.max(dy1, pt[1]); pt = mapSrcPoint(sx1, sy1); dx0 = Math.min(dx0, pt[0]); dx1 = Math.max(dx1, pt[0]); dy0 = Math.min(dy0, pt[1]); dy1 = Math.max(dy1, pt[1]); int x = (int)Math.floor(dx0); int y = (int)Math.floor(dy0); int w = (int)Math.ceil(dx1 - x); int h = (int)Math.ceil(dy1 - y); return new Rectangle(x, y, w, h); } // Maps a dest point to the source. private float[] mapDestPoint(int x, int y) { //XXX: This method should do its calculations in doubles // // x' = (c1 + c2*(x+0.5)*preScaleX + // c3*(y+0.5)*preScaleY) * postScaleX - 0.5 // y' = (c4 + c5*(x+0.5)*preScaleX + // c6*(y+0.5)*preScaleY) * postScaleY - 0.5 // float fx = (x + 0.5F) * preScaleX; // pixel energy is at center float fy = (y + 0.5F) * preScaleY; float[] p = new float[2]; p[0] = (c1 + c2 * fx + c3 * fy) * postScaleX - 0.5F; p[1] = (c4 + c5 * fx + c6 * fy) * postScaleY - 0.5F; return p; } // Maps a source point to the dest. private float[] mapSrcPoint(int x, int y) { //XXX: This method should do its calculations in doubles // // x' = (invc1 + invc2*(x+0.5)*preScaleX + // invc3*(y+0.5)*preScaleY) * postScaleX - 0.5 // y' = (invc4 + invc5*(x+0.5)*preScaleX + // invc6*(y+0.5)*preScaleY) * postScaleY - 0.5 // float fx = (x + 0.5F) * preScaleX; // pixel energy is at center float fy = (y + 0.5F) * preScaleY; float[] p = new float[2]; p[0] = (invc1 + invc2 * fx + invc3 * fy) * postScaleX - 0.5F; p[1] = (invc4 + invc5 * fx + invc6 * fy) * postScaleY - 0.5F; return p; } /** * Computes the source point corresponding to the supplied point. * *

      This method returns the value of pt in the following * code snippet: * *

           * double dx = (destPt.getX() + 0.5)*preScaleX;
           * double dy = (destPt.getY() + 0.5)*preScaleY;
           * Point2D pt = (Point2D)destPt.clone();
           * pt.setLocation((c1 + c2*dx + c3*dy)*postScaleX - 0.5F,
           *                (c4 + c5*dx + c6*dy)*postScaleY - 0.5F);
           * 
      *

      * * @param destPt the position in destination image coordinates * to map to source image coordinates. * * @return a Point2D of the same class as * destPt. * * @throws IllegalArgumentException if destPt is * null. * * @since JAI 1.1.2 */ public Point2D mapDestPoint(Point2D destPt) { if (destPt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } double dx = (destPt.getX() + 0.5)*preScaleX; double dy = (destPt.getY() + 0.5)*preScaleY; Point2D pt = (Point2D)destPt.clone(); pt.setLocation((c1 + c2*dx + c3*dy)*postScaleX - 0.5F, (c4 + c5*dx + c6*dy)*postScaleY - 0.5F); return pt; } /** * Computes the destination point corresponding to the supplied point. * *

      If the transform is invertible, this method returns the value of * pt in the following code snippet: * *

           * double sx = (sourcePt.getX() + 0.5F)/postScaleX;
           * double sy = (sourcePt.getY() + 0.5F)/postScaleY;
           * Point2D pt = (Point2D)sourcePt.clone();
           * pt.setLocation((invc1 + invc2*sx + invc3*sy)/preScaleX - 0.5F,
           *                (invc4 + invc5*sx + invc6*sy)/preScaleY - 0.5F);
           * 
      * * where invc* are the inverse transform coefficients. If * the transform is not invertible, null is returned.

      * * @param sourcePt the position in source image coordinates * to map to destination image coordinates. * * @return a Point2D of the same class as * sourcePt or null> if the transform is * not invertible. * * @throws IllegalArgumentException if destPt is * null. * * @since JAI 1.1.2 */ public Point2D mapSourcePoint(Point2D sourcePt) { if (sourcePt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if(invTransform == null) { return null; } double sx = (sourcePt.getX() + 0.5F)/postScaleX; double sy = (sourcePt.getY() + 0.5F)/postScaleY; Point2D pt = (Point2D)sourcePt.clone(); pt.setLocation((invc1 + invc2*sx + invc3*sy)/preScaleX - 0.5F, (invc4 + invc5*sx + invc6*sy)/preScaleY - 0.5F); return pt; } } jai-core-1.1.4/src/share/classes/javax/media/jai/CRIFImpl.java0000644000175000017500000002620310203035544023550 0ustar mathieumathieu/* * $RCSfile: CRIFImpl.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:05 $ * $State: Exp $ */ package javax.media.jai; import java.awt.RenderingHints; import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D.Float; import java.awt.image.RenderedImage; import java.awt.image.renderable.ContextualRenderedImageFactory; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import java.awt.image.renderable.RenderContext; import javax.media.jai.ImageLayout; import javax.media.jai.JAI; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; import javax.media.jai.util.ImagingException; import javax.media.jai.util.ImagingListener; import com.sun.media.jai.util.ImageUtil; /** * A utility class to minimize in most cases the effort required to implement * the ContextualRenderedImageFactory (CRIF) of an operation. * An extender of this class is required to implement only the method * RenderedImage create(ParameterBlock, RenderingHints) * defined in the RenderedImageFactory interface. The remaining * methods may be overridden insofar as this is necessary to obtain behavior * different from that provided by default. * * @see java.awt.image.renderable.ContextualRenderedImageFactory * @see java.awt.image.renderable.RenderedImageFactory * @since JAI 1.1 */ // This class was actually added in JAI EA2 but was then in // com.sun.media.jai.opimage. It was moved to the public API in JAI 1.1. public abstract class CRIFImpl implements ContextualRenderedImageFactory { /** * If non-null, this name will be used as a parameter to * JAI.create() in * create(RenderContext,ParameterBlock); otherwise the RIF * create(ParameterBlock,RenderingHints) method implemented * in the extending class will be invoked. */ protected String operationName = null; /** * Default constructor. The operation name is set to null. */ public CRIFImpl() { this.operationName = null; } /** * Constructor. The operation name is set to the specified value * which may be null. */ public CRIFImpl(String operationName) { this.operationName = operationName; } /** * The RenderedImageFactory create() method * which must be implemented by concrete subclasses. */ public abstract RenderedImage create(ParameterBlock paramBlock, RenderingHints renderHints); /** * Creates a RenderedImage from the renderable layer. * *

      If operationName is non-null, * JAI.create() will be invoked using the supplied * ParameterBlock and the RenderingHints * contained in the RenderContext. If * operationName is null, or * JAI.create() returns null, the * create(ParameterBlock,RenderingHints) method defined * in the extending class will be invoked. * * @param renderContext The rendering information associated with * this rendering. * @param paramBlock The parameters used to create the image. * @return A RenderedImage. */ public RenderedImage create(RenderContext renderContext, ParameterBlock paramBlock) { RenderingHints renderHints = renderContext.getRenderingHints(); if (operationName != null) { OperationRegistry registry = renderHints == null ? null : (OperationRegistry)renderHints.get(JAI.KEY_OPERATION_REGISTRY); RenderedImage rendering; if(registry == null) { // Call the JAI.create method to get the best implementation rendering = JAI.create(operationName, paramBlock, renderHints); } else { // registry != null // NB: This section is lifted pretty much verbatim from // JAI.createNS(). // Get the OperationDescriptor registered under the // specified name. OperationDescriptor odesc = (OperationDescriptor) registry.getDescriptor(OperationDescriptor.class, operationName); if (odesc == null) { throw new IllegalArgumentException(operationName + ": " + JaiI18N.getString("JAI0")); } // Does this operation support rendered mode? if (!odesc.isModeSupported(RenderedRegistryMode.MODE_NAME)) { throw new IllegalArgumentException(operationName + ": " + JaiI18N.getString("JAI1")); } // Check the destination image type. if (!RenderedImage.class.isAssignableFrom(odesc.getDestClass(RenderedRegistryMode.MODE_NAME))) { throw new IllegalArgumentException(operationName + ": " + JaiI18N.getString("JAI2")); } // Validate input arguments. The ParameterBlock is cloned here // because OperationDescriptor.validateArguments() may change // its content. StringBuffer msg = new StringBuffer(); paramBlock = (ParameterBlock)paramBlock.clone(); if (!odesc.validateArguments(RenderedRegistryMode.MODE_NAME, paramBlock, msg)) { throw new IllegalArgumentException(msg.toString()); } // Create the rendered operation node. rendering = new RenderedOp(registry, operationName, paramBlock, renderHints); } // Return the rendering if possible. if(rendering != null) { // If the rendering is a rendered chain, replace it by its // rendering which will likely be an OpImage chain. if(rendering instanceof RenderedOp) { try { rendering = ((RenderedOp)rendering).getRendering(); } catch (Exception e) { ImagingListener listener = ImageUtil.getImagingListener(renderHints); String message = JaiI18N.getString("CRIFImpl0") + operationName; listener.errorOccurred(message, e, this, false); // e.printStackTrace(); } } return rendering; } } // Call the RIF create method of the extending class return create(paramBlock, renderHints); } /** * Maps the destination RenderContext into a * RenderContext for each source. The * implementation in this class simply returns the * RenderContext passed in by the caller. * * @param i The index of the source image. * @param renderContext The RenderContext being applied to * the operation. * @param paramBlock A ParameterBlock containing the * sources and parameters of the operation. * @param image The RenderableImage being rendered. * * @return The RenderContext to be used to render the * given source. */ public RenderContext mapRenderContext(int i, RenderContext renderContext, ParameterBlock paramBlock, RenderableImage image) { return renderContext; } /** * Returns the bounding box for the output of the operation. The * implementation in this class computes the bounding box as the * intersection the bounding boxes of all the (renderable sources). * * @param paramBlock A ParameterBlock containing the * sources and parameters of the operation. * @return A Rectangle2D specifying the bounding box. */ public Rectangle2D getBounds2D(ParameterBlock paramBlock) { int numSources = paramBlock.getNumSources(); if (numSources == 0) { return null; } RenderableImage src = paramBlock.getRenderableSource(0); Rectangle2D.Float box1 = new Rectangle2D.Float(src.getMinX(), src.getMinY(), src.getWidth(), src.getHeight()); for (int i = 1; i < numSources; i++) { src = paramBlock.getRenderableSource(i); Rectangle2D.Float box2 = new Rectangle2D.Float(src.getMinX(), src.getMinY(), src.getWidth(), src.getHeight()); box1 = (Rectangle2D.Float)box1.createIntersection(box2); if(box1.isEmpty()) { break; } } return box1; } /** * Returns the appropriate instance of the property with the indicated * name. * *

      The implementation in this class always returns * java.awt.Image.UndefinedProperty since * no properties are defined by default. * * @param paramBlock A ParameterBlock containing the * sources and parameters of the operation. * @param name A String containing the desired property name. * * @return the value java.awt.Image.UndefinedProperty * indicating that the property is undefined. */ public Object getProperty(ParameterBlock paramBlock, String name) { return java.awt.Image.UndefinedProperty; } /** * Returns the valid property names for the operation. The * implementation in this class always returns null * since no properties are associated with the operation by * default. * * @return null indicating that no properties are defined. */ public String[] getPropertyNames() { return null; } /** * Returns true if successive renderings with the same * arguments may produce different results. The implementation in this * class always returns false so as to enable caching * of renderings by default. CRIFs that do implement dynamic * rendering behavior must override this method. * * @return false indicating that the rendering is static. */ public boolean isDynamic() { return false; } } jai-core-1.1.4/src/share/classes/javax/media/jai/ThreadSafeOperationRegistry.java0000644000175000017500000003416110203035544027625 0ustar mathieumathieu/* * $RCSfile: ThreadSafeOperationRegistry.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:21 $ * $State: Exp $ */ package javax.media.jai; import com.sun.media.jai.util.RWLock; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInput; import java.io.ObjectOutput; import java.io.OutputStream; import java.util.Iterator; import java.util.List; import java.util.Vector; /** * A wrapper class on OperationRegistry which is * thread safe. Every method is wrapped with an appropriate read * or a write lock. Exceptions are caught and the lock is released * before the exception is re-thrown. * * @since JAI 1.1 */ final class ThreadSafeOperationRegistry extends OperationRegistry { /** The reader/writer lock for this class. */ private RWLock lock; public ThreadSafeOperationRegistry() { super(); // Create an upgradable reader/writer lock. lock = new RWLock(true); } public String toString() { try { lock.forReading(); String t = super.toString(); lock.release(); return t; } catch (RuntimeException e) { lock.release(); throw e; } } public void writeToStream(OutputStream out) throws IOException { try { lock.forReading(); super.writeToStream(out); lock.release(); } catch (IOException ioe) { lock.release(); throw ioe; } catch (RuntimeException e) { lock.release(); throw e; } } public void initializeFromStream(InputStream in) throws IOException { try { lock.forWriting(); super.initializeFromStream(in); lock.release(); } catch (IOException ioe) { lock.release(); throw ioe; } catch (RuntimeException e) { lock.release(); throw e; } } public void updateFromStream(InputStream in) throws IOException { try { lock.forWriting(); super.updateFromStream(in); lock.release(); } catch (IOException ioe) { lock.release(); throw ioe; } catch (RuntimeException e) { lock.release(); throw e; } } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { try { lock.forWriting(); super.readExternal(in); lock.release(); } catch (IOException ioe) { lock.release(); throw ioe; } catch (RuntimeException e) { lock.release(); throw e; } } public void writeExternal(ObjectOutput out) throws IOException { try { lock.forReading(); super.writeExternal(out); lock.release(); } catch (IOException ioe) { lock.release(); throw ioe; } catch (RuntimeException e) { lock.release(); throw e; } } /********************** NEW JAI 1.1 methods *************************/ public void removeRegistryMode(String modeName) { try { lock.forWriting(); super.removeRegistryMode(modeName); lock.release(); } catch (RuntimeException e) { lock.release(); throw e; } } public String[] getRegistryModes() { try { lock.forReading(); String[] t = super.getRegistryModes(); lock.release(); return t; } catch (RuntimeException e) { lock.release(); throw e; } } public void registerDescriptor(RegistryElementDescriptor descriptor) { try { lock.forWriting(); super.registerDescriptor(descriptor); lock.release(); } catch (RuntimeException e) { lock.release(); throw e; } } public void unregisterDescriptor(RegistryElementDescriptor descriptor) { try { lock.forWriting(); super.unregisterDescriptor(descriptor); lock.release(); } catch (RuntimeException e) { lock.release(); throw e; } } public RegistryElementDescriptor getDescriptor( Class descriptorClass, String descriptorName) { try { lock.forReading(); RegistryElementDescriptor t = super.getDescriptor(descriptorClass, descriptorName); lock.release(); return t; } catch (RuntimeException e) { lock.release(); throw e; } } public List getDescriptors(Class descriptorClass) { try { lock.forReading(); List t = super.getDescriptors(descriptorClass); lock.release(); return t; } catch (RuntimeException e) { lock.release(); throw e; } } public String[] getDescriptorNames(Class descriptorClass) { try { lock.forReading(); String[] t = super.getDescriptorNames(descriptorClass); lock.release(); return t; } catch (RuntimeException e) { lock.release(); throw e; } } public RegistryElementDescriptor getDescriptor(String modeName, String descriptorName) { try { lock.forReading(); RegistryElementDescriptor t = super.getDescriptor(modeName, descriptorName); lock.release(); return t; } catch (RuntimeException e) { lock.release(); throw e; } } public List getDescriptors(String modeName) { try { lock.forReading(); List t = super.getDescriptors(modeName); lock.release(); return t; } catch (RuntimeException e) { lock.release(); throw e; } } public String[] getDescriptorNames(String modeName) { try { lock.forReading(); String[] t = super.getDescriptorNames(modeName); lock.release(); return t; } catch (RuntimeException e) { lock.release(); throw e; } } public void setProductPreference(String modeName, String descriptorName, String preferredProductName, String otherProductName) { try { lock.forWriting(); super.setProductPreference(modeName, descriptorName, preferredProductName, otherProductName); lock.release(); } catch (RuntimeException e) { lock.release(); throw e; } } public void unsetProductPreference(String modeName, String descriptorName, String preferredProductName, String otherProductName) { try { lock.forWriting(); super.unsetProductPreference(modeName, descriptorName, preferredProductName, otherProductName); lock.release(); } catch (RuntimeException e) { lock.release(); throw e; } } public void clearProductPreferences(String modeName, String descriptorName) { try { lock.forWriting(); super.clearProductPreferences(modeName, descriptorName); lock.release(); } catch (RuntimeException e) { lock.release(); throw e; } } public String[][] getProductPreferences(String modeName, String descriptorName) { try { lock.forReading(); String[][] t = super.getProductPreferences(modeName, descriptorName); lock.release(); return t; } catch (RuntimeException e) { lock.release(); throw e; } } public Vector getOrderedProductList(String modeName, String descriptorName) { try { lock.forReading(); Vector t = super.getOrderedProductList(modeName, descriptorName); lock.release(); return t; } catch (RuntimeException e) { lock.release(); throw e; } } public void registerFactory(String modeName, String descriptorName, String productName, Object factory) { try { lock.forWriting(); super.registerFactory(modeName, descriptorName, productName, factory); lock.release(); } catch (RuntimeException e) { lock.release(); throw e; } } public void unregisterFactory(String modeName, String descriptorName, String productName, Object factory) { try { lock.forWriting(); super.unregisterFactory(modeName, descriptorName, productName, factory); lock.release(); } catch (RuntimeException e) { lock.release(); throw e; } } public void setFactoryPreference(String modeName, String descriptorName, String productName, Object preferredOp, Object otherOp) { try { lock.forWriting(); super.setFactoryPreference(modeName, descriptorName, productName, preferredOp, otherOp); lock.release(); } catch (RuntimeException e) { lock.release(); throw e; } } public void unsetFactoryPreference(String modeName, String descriptorName, String productName, Object preferredOp, Object otherOp) { try { lock.forWriting(); super.unsetFactoryPreference(modeName, descriptorName, productName, preferredOp, otherOp); lock.release(); } catch (RuntimeException e) { lock.release(); throw e; } } public void clearFactoryPreferences(String modeName, String descriptorName, String productName) { try { lock.forWriting(); super.clearFactoryPreferences(modeName, descriptorName, productName); lock.release(); } catch (RuntimeException e) { lock.release(); throw e; } } public Object[][] getFactoryPreferences(String modeName, String descriptorName, String productName) { try { lock.forReading(); Object[][] t = super.getFactoryPreferences( modeName, descriptorName, productName); lock.release(); return t; } catch (RuntimeException e) { lock.release(); throw e; } } public List getOrderedFactoryList(String modeName, String descriptorName, String productName) { try { lock.forReading(); List t = super.getOrderedFactoryList(modeName, descriptorName, productName); lock.release(); return t; } catch (RuntimeException e) { lock.release(); throw e; } } public Iterator getFactoryIterator(String modeName, String descriptorName) { try { lock.forReading(); Iterator t = super.getFactoryIterator(modeName, descriptorName); lock.release(); return t; } catch (RuntimeException e) { lock.release(); throw e; } } public Object getFactory(String modeName, String descriptorName) { try { lock.forReading(); Object t = super.getFactory(modeName, descriptorName); lock.release(); return t; } catch (RuntimeException e) { lock.release(); throw e; } } public Object invokeFactory(String modeName, String descriptorName, Object[] args) { try { lock.forReading(); Object t = super.invokeFactory(modeName, descriptorName, args); lock.release(); return t; } catch (RuntimeException e) { lock.release(); throw e; } } public void addPropertyGenerator(String modeName, String descriptorName, PropertyGenerator generator) { try { lock.forWriting(); super.addPropertyGenerator(modeName, descriptorName, generator); lock.release(); } catch (RuntimeException e) { lock.release(); throw e; } } public void removePropertyGenerator(String modeName, String descriptorName, PropertyGenerator generator) { try { lock.forWriting(); super.removePropertyGenerator(modeName, descriptorName, generator); lock.release(); } catch (RuntimeException e) { lock.release(); throw e; } } public void copyPropertyFromSource(String modeName, String descriptorName, String propertyName, int sourceIndex) { try { lock.forWriting(); super.copyPropertyFromSource(modeName, descriptorName, propertyName, sourceIndex); lock.release(); } catch (RuntimeException e) { lock.release(); throw e; } } public void suppressProperty(String modeName, String descriptorName, String propertyName) { try { lock.forWriting(); super.suppressProperty(modeName, descriptorName, propertyName); lock.release(); } catch (RuntimeException e) { lock.release(); throw e; } } public void suppressAllProperties(String modeName, String descriptorName) { try { lock.forWriting(); super.suppressAllProperties(modeName, descriptorName); lock.release(); } catch (RuntimeException e) { lock.release(); throw e; } } public void clearPropertyState(String modeName) { try { lock.forWriting(); super.clearPropertyState(modeName); lock.release(); } catch (RuntimeException e) { lock.release(); throw e; } } public String[] getGeneratedPropertyNames(String modeName, String descriptorName) { try { lock.forReading(); String[] t = super.getGeneratedPropertyNames(modeName, descriptorName); lock.release(); return t; } catch (RuntimeException e) { lock.release(); throw e; } } public PropertySource getPropertySource(String modeName, String descriptorName, Object op, Vector sources) { try { lock.forReading(); PropertySource t = super.getPropertySource( modeName, descriptorName, op, sources); lock.release(); return t; } catch (RuntimeException e) { lock.release(); throw e; } } public PropertySource getPropertySource(OperationNode op) { try { lock.forReading(); PropertySource t = super.getPropertySource(op); lock.release(); return t; } catch (RuntimeException e) { lock.release(); throw e; } } public void registerServices(ClassLoader cl) throws IOException { try { lock.forWriting(); super.registerServices(cl); lock.release(); } catch (RuntimeException e) { lock.release(); throw e; } } /********************** DEPRECATED METHODS *************************/ public void unregisterOperationDescriptor(String operationName) { try { lock.forWriting(); super.unregisterOperationDescriptor(operationName); lock.release(); } catch (RuntimeException e) { lock.release(); throw e; } } public void clearOperationPreferences(String operationName, String productName) { try { lock.forWriting(); super.clearOperationPreferences(operationName, productName); lock.release(); } catch (RuntimeException e) { lock.release(); throw e; } } } jai-core-1.1.4/src/share/classes/javax/media/jai/TileFactory.java0000644000175000017500000000560710203035544024435 0ustar mathieumathieu/* * $RCSfile: TileFactory.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:22 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Point; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; /** * An interface defining a mechanism which may be used to create tiles * for an image. Implementations of this interface might be based for * example on managing a pool of memory, recycling previously allocated * memory, or using an image as a backing store. * * @since JAI 1.1.2 */ public interface TileFactory { /** * Returns a value indicating whether a tile returned by * createTile() might be created without allocating * new memory for the requisite data array. */ boolean canReclaimMemory(); /** * Returns true if this object can cache in main memory * any data arrays which might be made available for future use. */ boolean isMemoryCache(); /** * Returns the amount of memory currently being consumed by data * arrays cached within this object. If this object does not cache * data arrays then this method will always return zero. * * @return The amount of memory used by internally cached data arrays, * measured in bytes. */ long getMemoryUsed(); /** * Removes references to all internally cached data arrays, if any. * If such arrays are objects potentially subject to finalization, * then this should make them available for garbage collection * provided no references to them are held elsewhere. */ void flush(); // XXX Comment about optionally not clearing the data arrays? /** * Create a tile with the specified SampleModel and * location, possibly using a DataBuffer constructed * using a reclaimed data bank (array). If it is not possible to * reclaim an array, a new one will be allocated so that a tile * is guaranteed to be generated. If a reclaimed array is used * to create the tile, its elements should be set to zero before * the tile is returned to the caller. * * @param sampleModel The SampleModel to use in * creating the WritableRaster. * @param location The location (minX, minY) of * the WritableRaster; if null, * (0, 0) will be used. * @return A WritableRaster which might have a * DataBuffer created using a previously * allocated array. * @throws IllegalArgumentionException if sampleModel * is null. */ WritableRaster createTile(SampleModel sampleModel, Point location); } jai-core-1.1.4/src/share/classes/javax/media/jai/ParameterBlockJAI.java0000644000175000017500000012347310203035544025431 0ustar mathieumathieu/* * $RCSfile: ParameterBlockJAI.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:14 $ * $State: Exp $ */ package javax.media.jai; import com.sun.media.jai.util.CaselessStringArrayTable; import java.awt.image.renderable.ParameterBlock; import java.io.IOException; import java.io.NotSerializableException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Hashtable; import java.util.Vector; import javax.media.jai.util.CaselessStringKey; /** * A convenience subclass of ParameterBlock that * allows the use of default parameter values and getting/setting * sources and parameters by name. A ParameterBlockJAI is * constructed using either an OperationDescriptor, * or an operation name (that will be looked up in the appropriate * default OperationRegistry) and a mode which should * be in OperationDescriptor.getSupportedModes() (such * as rendered, renderable, collection or renderableCollection). If * the mode is not specified ParameterBlockJAI will by * default work with the first mode in the array of Strings * returned by OperationDescriptor.getSupportedModes(). * *

      Once constructed, a ParameterBlockJAI appears to * have no sources. It contains all the parameters required by its * OperationDescriptor for a specified mode, each having * its default value as given by the OperationDescriptor. * Such a ParameterBlockJAI may not yet be usable, its * sources (if any) are not set, and some or all of its parameters may * have inapproriate values. The addSource methods of * ParameterBlock may be used to initialize the source values, * and the set(value, index) methods may be used to modify * new parameter values. The preferred way of setting parameter values * is the setParameter(name, value) described below. The * add() methods should not be used since the parameter * list is already long enough to hold all of the parameters required by * the OperationDescriptor. * *

      Additionally, ParameterBlockJAI offers * setParameter(name, value) methods that take a * parameter name; the index of the parameter is determined from the * OperationDescriptor and the corresponding parameter * is set. (users are strongly recommended to use this method * instead of the equivalent set(value, index) or * the deprecated set(value, name) methods). As in * ParameterBlock, all parameters are stored internally * as subclasses of Object and all get/set methods that take or return * values of primitive types are simply convenience methods that transform * values between the primitive types and their corresponding wrapper * classes. * *

      The OperationDescriptor that is used to initialize * a ParameterBlockJAI at construction is not * serializable and thus cannot be serialized using the default * serialization mechanism. The operation name is serialized instead and * included in the serialized ParameterBlockJAI stream. * During de-serialization, the operation name is de-serialized and then * looked up in the default OperationRegistry available at * the time of de-serialization. If no OperationDescriptor * has been registered with this OperationRegistry * under the given operation name, a NotSerializableException will * be thrown. The serialization of ParameterBlockJAI * works correctly only if the OperationDescriptor * registered for the operation name in question is identical to the * OperationDescriptor that was registered with the * OperationRegistry available at serialization time. * *

      All parameter names are treated in a case-insensitive but * retentive manner. * *

      Warning: Serialized objects of this class will * not be compatible with future releases. The current serialization * support is appropriate for short term storage or RMI between * applications running the same version of JAI. A future release of JAI * will provide support for long term persistence. */ public class ParameterBlockJAI extends ParameterBlock implements ParameterList { /** * The OperationDescriptor associated with this * ParameterBlockJAI. */ private transient OperationDescriptor odesc; /** * The operation mode. */ private String modeName; /** * The ParameterListDescriptor for the specific mode for the operator. */ private ParameterListDescriptor pld; /** * A CaselessStringArrayTable of parameter indices hashed by * CaselessStringKey versions of the parameter names. */ private CaselessStringArrayTable paramIndices; /** * A CaselessStringArrayTable source indices hashed by * CaselessStringKey versions of the source names. */ private CaselessStringArrayTable sourceIndices; /** The number of parameters. Cached for convenience. */ private int numParameters; /** The names of the parameters. Cached for convenience. */ private String[] paramNames; /** The Class types of the parameters. */ private Class[] paramClasses; /** The Class types of the sources. */ private Class[] sourceClasses; private static String getDefaultMode(OperationDescriptor odesc) { if (odesc == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); return odesc.getSupportedModes()[0]; } /** * Constructs a ParameterBlockJAI for * use with an operation described by a particular * OperationDescriptor. It uses the first * mode in the array of Strings returned by * OperationDescriptor.getSupportedModes() * to get the ParameterListDescriptor from * OperationDescriptor. The default values of the * parameters are filled in. * * @param odesc the OperationDescriptor describing the parameters * to be managed. * * @throws IllegalArgumentException if odesc is null */ public ParameterBlockJAI(OperationDescriptor odesc) { this(odesc, getDefaultMode(odesc)); } /** * Constructs a ParameterBlockJAI for a particular * operation by name. The OperationRegistry associated * with the default instance of the JAI class is used * to locate the OperationDescriptor associated with * the operation name. * * It uses the first mode in the array of Strings * returned by OperationDescriptor.getSupportedModes() * to get the ParameterListDescriptor from * OperationDescriptor. The default values of the * parameters are filled in. * * @param operationName a String giving the name of the operation. * * @throws IllegalArgumentException if operationName is null. */ public ParameterBlockJAI(String operationName) { this((OperationDescriptor) JAI.getDefaultInstance().getOperationRegistry(). getDescriptor(OperationDescriptor.class, operationName)); } /** * Constructs a ParameterBlockJAI for * use with an operation described by a particular * OperationDescriptor and a registry mode. The default * values of the parameters are filled in. * * @param odesc the OperationDescriptor describing the parameters * to be managed. * @param modeName the operation mode whose paramters are to be managed. * * @throws IllegalArgumentException if modeName is null or odesc is null * * @since JAI 1.1 */ public ParameterBlockJAI(OperationDescriptor odesc, String modeName) { if ((odesc == null) || (modeName == null)) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); this.odesc = odesc; this.modeName = modeName; pld = odesc.getParameterListDescriptor(modeName); numParameters = pld.getNumParameters(); paramNames = pld.getParamNames(); paramIndices = new CaselessStringArrayTable(pld.getParamNames()); sourceIndices = new CaselessStringArrayTable(odesc.getSourceNames()); paramClasses = pld.getParamClasses(); sourceClasses = odesc.getSourceClasses(modeName); Object[] defaults = pld.getParamDefaults(); parameters = new Vector(numParameters); for (int i = 0; i < numParameters; i++) { parameters.addElement(defaults[i]); } } /** * Constructs a ParameterBlockJAI for a * particular operation by name and a registry mode. The * OperationRegistry associated with the default * instance of the JAI class is used to locate the * OperationDescriptor associated with the operation * name. The default values of the parameters are filled in. * * @param operationName a String giving the name of the * operation. * @param modeName the operation mode whose paramters are to be managed. * * @throws IllegalArgumentException if operationName or modeName is null * * @since JAI 1.1 */ public ParameterBlockJAI(String operationName, String modeName) { this((OperationDescriptor) JAI.getDefaultInstance().getOperationRegistry(). getDescriptor(modeName, operationName), modeName); } /** * Returns the zero-relative index of a named source within the list of * sources. * * @param sourceName a String containing the parameter name. * @throws IllegalArgumentException if source is null or if there is * no source with the specified name. * * @since JAI 1.1 */ public int indexOfSource(String sourceName) { return sourceIndices.indexOf(sourceName); } /** * Returns the zero-relative index of a named parameter within the list of * parameters. * * @param paramName a String containing the parameter name. * * @throws IllegalArgumentException if paramName is null or if there is * no parameter with the specified name. * * @since JAI 1.1 */ public int indexOfParam(String paramName) { return paramIndices.indexOf(paramName); } /** * Returns the OperationDescriptor associated with this * ParameterBlockJAI. */ public OperationDescriptor getOperationDescriptor() { return odesc; } /** * Returns the ParameterListDescriptor that provides * descriptions of the parameters associated with the operator * and mode. * * @since JAI 1.1 */ public ParameterListDescriptor getParameterListDescriptor() { return pld; } /** * Get the operation mode used to determine parameter names, * classes and default values. * * @since JAI 1.1 */ public String getMode() { return modeName; } /** * Sets a named source to a given Object value. * * @param sourceName a String naming a source. * @param source an Object value for the source. * * @throws IllegalArgumentException if source is null. * @throws IllegalArgumentException if sourceName is null. * @throws IllegalArgumentException if source is not * an instance of (any of) the * expected class(es). * @throws IllegalArgumentException if the associated operation has * no source with the supplied name. * * @since JAI 1.1 */ public ParameterBlockJAI setSource(String sourceName, Object source) { if ((source == null) || (sourceName == null)) { throw new IllegalArgumentException( JaiI18N.getString("Generic0")); } int index = indexOfSource(sourceName); if (!sourceClasses[index].isInstance(source)) { throw new IllegalArgumentException( JaiI18N.getString("ParameterBlockJAI4")); } if (index >= odesc.getNumSources()) { addSource(source); } else { setSource(source, index); } return this; } /** * Returns an array of Class objects describing the types * of the parameters. This is a more efficient implementation than that * of the superclass as the parameter classes are known a priori. * * @since JAI 1.1 */ public Class [] getParamClasses() { // Just return the Class array obtained from the OD's PLD. return paramClasses; } /** * Gets the named parameter as an Object. * * @throws IllegalStateException if the param value is * ParameterListDescriptor.NO_PARAMETER_DEFAULT */ private Object getObjectParameter0(String paramName) { Object obj = getObjectParameter(indexOfParam(paramName)); if (obj == ParameterListDescriptor.NO_PARAMETER_DEFAULT) throw new IllegalStateException(paramName + ":" + JaiI18N.getString("ParameterBlockJAI6")); return obj; } /** * Gets a named parameter as an Object. Parameters belonging to a * primitive type, such as int, will be returned as a * member of the corresponding Number subclass, such as * Integer. * * @param paramName the name of the parameter to be returned. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws IllegalStateException if the parameter value is still * ParameterListDescriptor.NO_PARAMETER_DEFAULT */ public Object getObjectParameter(String paramName) { return getObjectParameter0(paramName); } /** * A convenience method to return a parameter as a byte. An * exception will be thrown if the parameter is of a different * type. * * @param paramName the name of the parameter to be returned. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws ClassCastException if the parameter is of a different type. * @throws IllegalStateException if the parameter value is still * ParameterListDescriptor.NO_PARAMETER_DEFAULT */ public byte getByteParameter(String paramName) { return ((Byte)getObjectParameter0(paramName)).byteValue(); } /** * A convenience method to return a parameter as a boolean. An * exception will be thrown if the parameter is of a different * type. * * @param paramName the name of the parameter to be returned. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws ClassCastException if the parameter is of a different type. * @throws IllegalStateException if the parameter value is still * ParameterListDescriptor.NO_PARAMETER_DEFAULT * * @since JAI 1.1 */ public boolean getBooleanParameter(String paramName) { return ((Boolean)getObjectParameter0(paramName)).booleanValue(); } /** * A convenience method to return a parameter as a char. An * exception will be thrown if the parameter is of a different * type. * * @param paramName the name of the parameter to be returned. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws ClassCastException if the parameter is of a different type. * @throws IllegalStateException if the parameter value is still * ParameterListDescriptor.NO_PARAMETER_DEFAULT */ public char getCharParameter(String paramName) { return ((Character)getObjectParameter0(paramName)).charValue(); } /** * A convenience method to return a parameter as an short. An * exception will be thrown if the parameter is of a different * type. * * @param paramName the name of the parameter to be returned. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws ClassCastException if the parameter is of a different type. * @throws IllegalStateException if the parameter value is still * ParameterListDescriptor.NO_PARAMETER_DEFAULT * * @since JAI 1.1 */ public short getShortParameter(String paramName) { return ((Short)getObjectParameter0(paramName)).shortValue(); } /** * A convenience method to return a parameter as an int. An * exception will be thrown if the parameter is of a different * type. * * @param paramName the name of the parameter to be returned. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws ClassCastException if the parameter is of a different type. * @throws IllegalStateException if the parameter value is still * ParameterListDescriptor.NO_PARAMETER_DEFAULT */ public int getIntParameter(String paramName) { return ((Integer)getObjectParameter0(paramName)).intValue(); } /** * A convenience method to return a parameter as a long. An * exception will be thrown if the parameter is of a different * type. * * @param paramName the name of the parameter to be returned. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws ClassCastException if the parameter is of a different type. * @throws IllegalStateException if the parameter value is still * ParameterListDescriptor.NO_PARAMETER_DEFAULT */ public long getLongParameter(String paramName) { return ((Long)getObjectParameter0(paramName)).longValue(); } /** * A convenience method to return a parameter as a float. An * exception will be thrown if the parameter is of a different * type. * * @param paramName the name of the parameter to be returned. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws ClassCastException if the parameter is of a different type. * @throws IllegalStateException if the parameter value is still * ParameterListDescriptor.NO_PARAMETER_DEFAULT */ public float getFloatParameter(String paramName) { return ((Float)getObjectParameter0(paramName)).floatValue(); } /** * A convenience method to return a parameter as a double. An * exception will be thrown if the parameter is of a different * type. * * @param paramName the name of the parameter to be returned. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws ClassCastException if the parameter is of a different type. * @throws IllegalStateException if the parameter value is still * ParameterListDescriptor.NO_PARAMETER_DEFAULT */ public double getDoubleParameter(String paramName) { return ((Double)getObjectParameter0(paramName)).doubleValue(); } // NEW ParameterList methods. /** * Sets a named parameter to a byte value. * Checks are made to verify that the parameter is of the right * Class type and that the value is valid. * * @param paramName a String naming a parameter. * @param b a byte value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws IllegalArgumentException if the class type of parameter * pointed to by the paramName is not a Byte * @throws IllegalArgumentException if the parameter value is invalid. * * @since JAI 1.1 */ public ParameterList setParameter(String paramName, byte b) { return setParameter0(paramName, new Byte(b)); } /** * Sets a named parameter to a boolean value. * Checks are made to verify that the parameter is of the right * Class type and that the value is valid. * * @param paramName a String naming a parameter. * @param b a boolean value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws IllegalArgumentException if the class type of parameter * pointed to by the paramName is not a Boolean * @throws IllegalArgumentException if the parameter value is invalid. * * @since JAI 1.1 */ public ParameterList setParameter(String paramName, boolean b) { return setParameter0(paramName, new Boolean(b)); } /** * Sets a named parameter to a char value. * Checks are made to verify that the parameter is of the right * Class type and that the value is valid. * * @param paramName a String naming a parameter. * @param c a char value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws IllegalArgumentException if the class type of parameter * pointed to by the paramName is not a Character * @throws IllegalArgumentException if the parameter value is invalid. * * @since JAI 1.1 */ public ParameterList setParameter(String paramName, char c) { return setParameter0(paramName, new Character(c)); } /** * Sets a named parameter to a short value. * Checks are made to verify that the parameter is of the right * Class type and that the value is valid. * * @param paramName a String naming a parameter. * @param s a short value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws IllegalArgumentException if the class type of parameter * pointed to by the paramName is not a Short * @throws IllegalArgumentException if the parameter value is invalid. * * @since JAI 1.1 */ public ParameterList setParameter(String paramName, short s) { return setParameter0(paramName, new Short(s)); } /** * Sets a named parameter to an int value. * Checks are made to verify that the parameter is of the right * Class type and that the value is valid. * * @param paramName a String naming a parameter. * @param i an int value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws IllegalArgumentException if the class type of parameter * pointed to by the paramName is not a Integer * @throws IllegalArgumentException if the parameter value is invalid. * * @since JAI 1.1 */ public ParameterList setParameter(String paramName, int i) { return setParameter0(paramName, new Integer(i)); } /** * Sets a named parameter to a long value. * Checks are made to verify that the parameter is of the right * Class type and that the value is valid. * * @param paramName a String naming a parameter. * @param l a long value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws IllegalArgumentException if the class type of parameter * pointed to by the paramName is not a Long * @throws IllegalArgumentException if the parameter value is invalid. * * @since JAI 1.1 */ public ParameterList setParameter(String paramName, long l) { return setParameter0(paramName, new Long(l)); } /** * Sets a named parameter to a float value. * Checks are made to verify that the parameter is of the right * Class type and that the value is valid. * * @param paramName a String naming a parameter. * @param f a float value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws IllegalArgumentException if the class type of parameter * pointed to by the paramName is not a Float * @throws IllegalArgumentException if the parameter value is invalid. * * @since JAI 1.1 */ public ParameterList setParameter(String paramName, float f) { return setParameter0(paramName, new Float(f)); } /** * Sets a named parameter to a double value. * Checks are made to verify that the parameter is of the right * Class type and that the value is valid. * * @param paramName a String naming a parameter. * @param d a double value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws IllegalArgumentException if the class type of parameter * pointed to by the paramName is not a Double * @throws IllegalArgumentException if the parameter value is invalid. * * @since JAI 1.1 */ public ParameterList setParameter(String paramName, double d) { return setParameter0(paramName, new Double(d)); } /** * Sets a named parameter to an Object value. The value * may be null, an instance of the class expected for this * parameter, or a DeferredData instance the * getDataClass() method of which returns the * expected class. If the object is a DeferredData instance, * then its wrapped data value is checked for validity if and only if * its isValid() method returns true. If the * object is not a DeferredData instance, then it is * always checked for validity. * * @param paramName a String naming a parameter. * @param obj an Object value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws IllegalArgumentException if the parameter value is invalid. * * @since JAI 1.1 */ public ParameterList setParameter(String paramName, Object obj) { return setParameter0(paramName, obj); } /** * Checks to see if the specified parameter is valid. * * @param paramName a String naming a parameter. * @param obj an Object value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws IllegalArgumentException if obj is * non-null and not an instance of the class * expected for the indicated parameter or if obj * is an invalid value for the indicated parameter. * * @return the index of the parameter */ private int checkParameter(String paramName, Object obj) { int index = indexOfParam(paramName); if(obj != null) { if (obj == ParameterListDescriptor.NO_PARAMETER_DEFAULT) { throw new IllegalArgumentException(paramName + ":" + JaiI18N.getString("ParameterBlockJAI8")); } if (obj instanceof DeferredData) { DeferredData dd = (DeferredData)obj; if (!paramClasses[index].isAssignableFrom(dd.getDataClass())) { throw new IllegalArgumentException(paramName + ":" + JaiI18N.getString("ParameterBlockJAI0")); } if (dd.isValid() && !pld.isParameterValueValid(paramName, dd.getData())) { throw new IllegalArgumentException(paramName + ":" + JaiI18N.getString("ParameterBlockJAI2")); } } else if (!paramClasses[index].isInstance(obj)) { throw new IllegalArgumentException(paramName + ":" + JaiI18N.getString("ParameterBlockJAI0")); } } if(obj == null || !(obj instanceof DeferredData)) { if (!pld.isParameterValueValid(paramName, obj)) { throw new IllegalArgumentException(paramName + ":" + JaiI18N.getString("ParameterBlockJAI2")); } } return index; } /** * Sets a named parameter to an Object value. The value may be * null, an instance of the class expected for this * parameter, or a DeferredData instance the * getDataClass() method of which returns the * expected class. If the object is a DeferredData instance, * then its wrapped data value is checked for validity if and only if * its isValid() method returns true. * If the object is not a DeferredData instance, then it is * always checked for validity. * * @param paramName a String naming a parameter. * @param obj an Object value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws IllegalArgumentException if obj is * non-null and not an instance of the class * expected for the indicated parameter or if obj * is an invalid value for the indicated parameter. */ private ParameterList setParameter0(String paramName, Object obj) { int index = checkParameter(paramName, obj); parameters.setElementAt(obj, index); return this; } /* ----- Superclass methods overridden for consistent behavior. ----- */ /** * Adds an object to the list of parameters. * * This method always throws an IllegalStateException * because the ParameterBlockJAI constructor initializes * all parameters with their default values. * * @throws IllegalStateException if parameters are added to an already * initialized ParameterBlockJAI * * @since JAI 1.1 */ public ParameterBlock add(Object obj) { throw new IllegalStateException(JaiI18N.getString("ParameterBlockJAI5")); } /** * Replaces an Object in the list of parameters. * * @param obj The new value of the parameter. * @param index The zero-relative index of the parameter. * * @throws ArrayIndexOutOfBoundsException if index * is negative or not less than the number of parameters * expected for the associated operation. * @throws IllegalArgumentException if obj is * non-null and not an instance of the class * expected for the indicated parameter or if obj * is an invalid value for the indicated parameter. * * @since JAI 1.1 */ public ParameterBlock set(Object obj, int index) { if(index < 0 || index >= pld.getNumParameters()) { throw new ArrayIndexOutOfBoundsException(); } // Not the most efficient implementation but has minimum duplication. setParameter0(paramNames[index], obj); return this; } /** * Sets the entire Vector of parameters to a given * Vector. The Vector is saved by reference. * * @throws IllegalArgumentException if the size of the supplied * Vector does not equal the number of parameters * of the associated operation. * @throws IllegalArgumentException if a non-null, * non-DeferredData value is not an instance of * the class expected for the indicated parameter or if * obj is an invalid value for the indicated * parameter. * @throws IllegalArgumentException if a non-null, * DeferredData value does not wrap an instance of * the class expected for the indicated parameter or if it is * valid but its wrapped value is invalid for the indicated * parameter. * * @since JAI 1.1 */ public void setParameters(Vector parameters) { if (parameters == null || parameters.size() != numParameters) { throw new IllegalArgumentException(JaiI18N.getString("ParameterBlockJAI7")); } for(int i = 0; i < numParameters; i++) { checkParameter(paramNames[i], parameters.get(i)); } this.parameters = parameters; } /********************** DEPRECATED METHODS *************************/ /** * Returns the zero-relative index of a named parameter within the list of * parameters. * * @param paramName a String containing the parameter name. * * @throws IllegalArgumentException if paramName is null or if there is * no parameter with the specified name. * * @deprecated as of JAI 1.1 - use "indexOfParam" instead. * * @see #indexOfParam */ public int indexOf(String paramName) { return indexOfParam(paramName); } /** * Sets a named parameter to a byte value. * * @param paramName a String naming a parameter. * @param b a byte value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * * @deprecated as of JAI 1.1 - use setParameter instead. * * @see #setParameter(String, byte) */ public ParameterBlock set(byte b, String paramName) { return set(new Byte(b), paramName); } /** * Sets a named parameter to a char value. * * @param paramName a String naming a parameter. * @param c a char value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * * @deprecated as of JAI 1.1 - use setParameter instead. * * @see #setParameter(String, char) */ public ParameterBlock set(char c, String paramName) { return set(new Character(c), paramName); } /** * Sets a named parameter to a short value. * * @param paramName a String naming a parameter. * @param s a short value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * * @deprecated as of JAI 1.1 - use setParameter instead. * * @see #setParameter(String, short) */ public ParameterBlock set(short s, String paramName) { return set(new Short(s), paramName); } /** * Sets a named parameter to an int value. * * @param paramName a String naming a parameter. * @param i an int value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * * @deprecated as of JAI 1.1 - use setParameter instead. * * @see #setParameter(String, int) */ public ParameterBlock set(int i, String paramName) { return set(new Integer(i), paramName); } /** * Sets a named parameter to a long value. * * @param paramName a String naming a parameter. * @param l a long value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * * @deprecated as of JAI 1.1 - use setParameter instead. * * @see #setParameter(String, long) */ public ParameterBlock set(long l, String paramName) { return set(new Long(l), paramName); } /** * Sets a named parameter to a float value. * * @param paramName a String naming a parameter. * @param f a float value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * * @deprecated as of JAI 1.1 - use setParameter instead. * * @see #setParameter(String, float) */ public ParameterBlock set(float f, String paramName) { return set(new Float(f), paramName); } /** * Sets a named parameter to a double value. * * @param paramName a String naming a parameter. * @param d a double value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * * @deprecated as of JAI 1.1 - use setParameter instead. * * @see #setParameter(String, double) */ public ParameterBlock set(double d, String paramName) { return set(new Double(d), paramName); } /** * Sets a named parameter to an Object value. * * @param paramName a String naming a parameter. * @param obj an Object value for the parameter. * * @throws IllegalArgumentException if obj is null, or if the class * type of obj does not match the class type of parameter * pointed to by the paramName. * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * * @deprecated as of JAI 1.1 - use setParameter instead. * * @see #setParameter(String, Object) */ public ParameterBlock set(Object obj, String paramName) { setParameter0(paramName, obj); return this; } // [De]serialization methods. /** * Serialize the ParameterBlockJAI. * @throws IOException */ private void writeObject(ObjectOutputStream out) throws IOException { // Write the non-static and non-transient fields. out.defaultWriteObject(); // Write out the operation name of the OperationDescriptor. out.writeObject(odesc.getName()); } /** * Deserialize the ParameterBlockJAI. * * @throws IOException * @throws NotSerializableException if no OperationDescriptor is * registered with the current OperationRegistry under the * deserialized operation name. */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { // Read the non-static and non-transient fields. in.defaultReadObject(); // Read the operation name String operationName = (String)in.readObject(); // Try to get the OperationDescriptor registered under this name odesc = (OperationDescriptor)JAI.getDefaultInstance(). getOperationRegistry(). getDescriptor(modeName, operationName); if (odesc == null) { throw new NotSerializableException(operationName + " " + JaiI18N.getString("ParameterBlockJAI1")); } } /** * Creates a copy of a ParameterBlockJAI. The source * and parameter Vectors are cloned, but the actual sources and * parameters are copied by reference. This allows modifications to * the order and number of sources and parameters in the clone to be * invisible to the original ParameterBlockJAI. Changes * to the shared sources or parameters themselves will still be * visible. * * @return an Object clone of the ParameterBlockJAI. * * @since JAI 1.1 */ public Object clone() { ParameterBlockJAI theClone = (ParameterBlockJAI)shallowClone(); if (sources != null) { theClone.setSources((Vector)sources.clone()); } if (parameters != null) { // Clone the parameter Vector without doing the parameter // validity checks. theClone.parameters = (Vector)parameters.clone(); } return (Object) theClone; } } jai-core-1.1.4/src/share/classes/javax/media/jai/InterpolationBicubic2.java0000644000175000017500000001342510203035544026377 0ustar mathieumathieu/* * $RCSfile: InterpolationBicubic2.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:10 $ * $State: Exp $ */ package javax.media.jai; /** * A class representing bicubic interpolation using a different * polynomial than InterpolationBicubic. * *

      InterpolationBicubic2 is a subclass of Interpolation that * performs interpolation using the piecewise cubic polynomial: * *

       * r(x) = (a + 2)|x|^3 - (a + 3)|x|^2         +  1 , 0 <= |x| < 1
       * r(x) =       a|x|^3 -      5a|x|^2 + 8a|x| - 4a , 1 <= |x| < 2
       * r(x) = 0                                        , otherwise
       * 
      * * with 'a' set to -1.0. * * This definition is also sometimes known as "cubic convolution", * using the parameter 'a' recommended by Keys. This interpolator * may produce somewhat sharper results than InterpolationBicubic, * but that result is image dependent. * (Reference: Digital Image Warping, George Wolberg, 1990, pp 129-131, * IEEE Computer Society Press, ISBN 0-8186-8944-7) * *

      A neighborhood extending one sample to the left of and above the * central sample, and two samples to the right of and below the central * sample is required to perform bicubic interpolation. * *

      This implementation creates an InterpolationTable * whose integer coefficients have eight bits of precision to the * right of the binary point. * *

      The diagrams below illustrate the pixels involved in one-dimensional * interpolation. Point s0 is the interpolation kernel key position. * xfrac and yfrac, indicated by the dots, represent the point of interpolation * between two pixels. This value lies between 0.0 and 1.0 exclusive for * floating point and 0 and 2subsampleBits exclusive for integer * interpolations. * *

       * 
       *         Horizontal              Vertical
       *
       *    s_    s0 .  s1    s2            s_                             
       *             ^                                                     
       *            xfrac                   s0                        
       *                                     .< yfrac                  
       *                                    s1                         
       *                                                                      
       *                                    s2                         
       * 
       * 
      * *

      The diagram below illustrates the pixels involved in * two-dimensional interpolation. * *

       * 
       *               s__    s_0    s_1    s_2                              
       *                                                                      
       *                                                                      
       *                                                                      
       *               s0_    s00    s01    s02                              
       *                                                                      
       *                          .             < yfrac                      
       *                                                                      
       *               s1_    s10    s11    s12                              
       *                                                                      
       *                                                                      
       *                                                                      
       *               s2_    s20    s21    s22                              
       *                          ^                                           
       *                         xfrac                                        
       * 
       * 
      *

      The class is marked 'final' so that it may be more easily inlined. * */ public final class InterpolationBicubic2 extends InterpolationTable { private static final int PRECISION_BITS = 8; private static float[] dataHelper(int subsampleBits) { int one = 1 << subsampleBits; int arrayLength = one * 4; float tableValues[] = new float[arrayLength]; float f; float onef = (float)one; float t; int count = 0; for (int i=0; i=1) { return (((B3 * x) + B2) * x + B1) * x + B0; } else { return ((A3 * x) + A2) * x * x + A0; } } /** * Constructs an InterpolationBicubic2 with a given subsample * precision, in bits. This precision is applied to both axes. * *

      This implementation creates an InterpolationTable * whose integer coefficients have eight bits of precision to the * right of the binary point. * * @param subsampleBits the subsample precision. * */ public InterpolationBicubic2(int subsampleBits) { super(1, 1, 4, 4, subsampleBits, subsampleBits, PRECISION_BITS, dataHelper(subsampleBits), null); } } jai-core-1.1.4/src/share/classes/javax/media/jai/BorderExtenderZero.java0000644000175000017500000001563610203035544025767 0ustar mathieumathieu/* * $RCSfile: BorderExtenderZero.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:04 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.WritableRaster; /** * A subclass of BorderExtender that implements * border extension by filling all pixels outside of the image * bounds with zeros. For example, the image: * *

      * * * * *
      ABC
      DEF
      GHI
      * *
      if extended by adding two extra rows to the top and bottom and * two extra columns on the left and right sides, would become: * *

      * * * * * * * * * *
      0000000
      0000000
      00ABC00
      00DEF00
      00GHI00
      0000000
      0000000
      * * @see BorderExtender */ public final class BorderExtenderZero extends BorderExtender { BorderExtenderZero() {} /** * Fills in the portions of a given Raster that lie * outside the bounds of a given PlanarImage with * zeros. * *

      The portion of raster that lies within * im.getBounds() is not altered. * * @param raster The WritableRaster the border area of * which is to be filled with zero. * @param im The PlanarImage which determines the * portion of the WritableRaster not * to be filled. * * @throws IllegalArgumentException if either parameter is * null. */ public final void extend(WritableRaster raster, PlanarImage im) { if ( raster == null || im == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } int width = raster.getWidth(); int height = raster.getHeight(); int numBands = raster.getNumBands(); int minX = raster.getMinX(); int maxX = minX + width; int minY = raster.getMinY(); int maxY = minY + height; int validMinX = Math.max(im.getMinX(), minX); int validMaxX = Math.min(im.getMaxX(), maxX); int validMinY = Math.max(im.getMinY(), minY); int validMaxY = Math.min(im.getMaxY(), maxY); int row; switch (raster.getSampleModel().getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_INT: int[] iData = new int[width*numBands]; if(validMinX > validMaxX || validMinY > validMaxY) { // Raster does not intersect image. for (row = minY; row < maxY; row++) { raster.setPixels(minX, row, width, 1, iData); } } else { for (row = minY; row < validMinY; row++) { raster.setPixels(minX, row, width, 1, iData); } for (row = validMinY; row < validMaxY; row++) { if (minX < validMinX) { raster.setPixels(minX, row, validMinX - minX, 1, iData); } if (validMaxX < maxX) { raster.setPixels(validMaxX, row, maxX - validMaxX, 1, iData); } } for (row = validMaxY; row < maxY; row++) { raster.setPixels(minX, row, width, 1, iData); } } break; case DataBuffer.TYPE_FLOAT: float[] fData = new float[width*numBands]; if(validMinX > validMaxX || validMinY > validMaxY) { // Raster does not intersect image. for (row = minY; row < maxY; row++) { raster.setPixels(minX, row, width, 1, fData); } } else { for (row = minY; row < validMinY; row++) { raster.setPixels(minX, row, width, 1, fData); } for (row = validMinY; row < validMaxY; row++) { if (minX < validMinX) { raster.setPixels(minX, row, validMinX - minX, 1, fData); } if (validMaxX < maxX) { raster.setPixels(validMaxX, row, maxX - validMaxX, 1, fData); } } for (row = validMaxY; row < maxY; row++) { raster.setPixels(minX, row, width, 1, fData); } } break; case DataBuffer.TYPE_DOUBLE: double[] dData = new double[width*numBands]; if(validMinX > validMaxX || validMinY > validMaxY) { // Raster does not intersect image. for (row = minY; row < maxY; row++) { raster.setPixels(minX, row, width, 1, dData); } } else { for (row = minY; row < validMinY; row++) { raster.setPixels(minX, row, width, 1, dData); } for (row = validMinY; row < validMaxY; row++) { if (minX < validMinX) { raster.setPixels(minX, row, validMinX - minX, 1, dData); } if (validMaxX < maxX) { raster.setPixels(validMaxX, row, maxX - validMaxX, 1, dData); } } for (row = validMaxY; row < maxY; row++) { raster.setPixels(minX, row, width, 1, dData); } } break; } } } jai-core-1.1.4/src/share/classes/javax/media/jai/DataBufferFloat.java0000644000175000017500000002266610203035544025205 0ustar mathieumathieu/* * $RCSfile: DataBufferFloat.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:07 $ * $State: Exp $ */ package javax.media.jai; import java.awt.image.DataBuffer; /** * An extension of DataBuffer that stores data internally in * float form. * * @see java.awt.image.DataBuffer */ public class DataBufferFloat extends DataBuffer { /** The array of data banks. */ protected float bankdata[][]; /** A reference to the default data bank. */ protected float data[]; /** * Constructs a float-based DataBuffer * with a specified size. * * @param size The number of elements in the DataBuffer. */ public DataBufferFloat(int size) { super(TYPE_FLOAT, size); data = new float[size]; bankdata = new float[1][]; bankdata[0] = data; } /** * Constructs a float-based DataBuffer * with a specified number of banks, all of which are of a * specified size. * * @param size The number of elements in each bank of the * DataBuffer. * @param numBanks The number of banks in the * DataBuffer. */ public DataBufferFloat(int size, int numBanks) { super(TYPE_FLOAT, size, numBanks); bankdata = new float[numBanks][]; for (int i= 0; i < numBanks; i++) { bankdata[i] = new float[size]; } data = bankdata[0]; } /** * Constructs a float-based DataBuffer * with the specified data array. Only the first * size elements are available for use by this * DataBuffer. The array must be large enough to * hold size elements. * * @param dataArray An array of floats to be used as the * first and only bank of this DataBuffer. * @param size The number of elements of the array to be used. */ public DataBufferFloat(float dataArray[], int size) { super(TYPE_FLOAT, size); if (dataArray.length < size) throw new RuntimeException(JaiI18N.getString("DataBuffer0")); data = dataArray; bankdata = new float[1][]; bankdata[0] = data; } /** * Constructs a float-based DataBuffer * with the specified data array. Only the elements between * offset and offset + size - 1 are * available for use by this DataBuffer. The array * must be large enough to hold offset + size * elements. * * @param dataArray An array of floats to be used as the * first and only bank of this DataBuffer. * @param size The number of elements of the array to be used. * @param offset The offset of the first element of the array * that will be used. */ public DataBufferFloat(float dataArray[], int size, int offset) { super(TYPE_FLOAT, size, 1, offset); if (dataArray.length < size) throw new RuntimeException(JaiI18N.getString("DataBuffer1")); data = dataArray; bankdata = new float[1][]; bankdata[0] = data; } /** * Constructs a float-based DataBuffer * with the specified data arrays. Only the first * size elements of each array are available for use * by this DataBuffer. The number of banks will be * equal to dataArray.length. * * @param dataArray An array of arrays of floats to be * used as the banks of this DataBuffer. * @param size The number of elements of each array to be used. */ public DataBufferFloat(float dataArray[][], int size) { super(TYPE_FLOAT, size, dataArray.length); bankdata = dataArray; data = bankdata[0]; } /** * Constructs a float-based DataBuffer * with the specified data arrays, size, and per-bank offsets. * The number of banks is equal to dataArray.length. * Each array must be at least as large as size plus the * corresponding offset. There must be an entry in the offsets * array for each data array. * * @param dataArray An array of arrays of floats to be * used as the banks of this DataBuffer. * @param size The number of elements of each array to be used. * @param offsets An array of integer offsets, one for each bank. */ public DataBufferFloat(float dataArray[][], int size, int offsets[]) { super(TYPE_FLOAT, size,dataArray.length, offsets); bankdata = dataArray; data = bankdata[0]; } /** Returns the float data array of the default(first) bank. */ public float[] getData() { return data; } /** Returns the data array for the specified bank. */ public float[] getData(int bank) { return bankdata[bank]; } /** Returns the data array for all banks. */ public float[][] getBankData() { return bankdata; } /** * Returns the requested data array element from the first * (default) bank rounded off as an int. * * @param i The desired data array element. * * @return The data entry as an int. */ public int getElem(int i) { return Math.round(data[i+offset]); } /** * Returns the requested data array element from the specified * bank rounded off as an int. * * @param bank The bank number. * @param i The desired data array element. * * @return The data entry as an int. */ public int getElem(int bank, int i) { return Math.round(bankdata[bank][i+offsets[bank]]); } /** * Sets the requested data array element in the first (default) * bank to the given int. * * @param i The desired data array element. * @param val The value to be set. */ public void setElem(int i, int val) { data[i+offset] = (float)val; } /** * Sets the requested data array element in the specified bank to * the given int. * * @param bank The bank number. * @param i The desired data array element. * @param val The value to be set. */ public void setElem(int bank, int i, int val) { bankdata[bank][i+offsets[bank]] = (float)val; } /** * Returns the requested data array element from the first * (default) bank as a float. * * @param i The desired data array element. * * @return The data entry as a float. */ public float getElemFloat(int i) { return data[i+offset]; } /** * Returns the requested data array element from the specified * bank as a float. * * @param bank The bank number. * @param i The desired data array element. * * @return The data entry as a float. */ public float getElemFloat(int bank, int i) { return bankdata[bank][i+offsets[bank]]; } /** * Sets the requested data array element in the first (default) * bank to the given float. * * @param i The desired data array element. * @param val The value to be set. */ public void setElemFloat(int i, float val) { data[i+offset] = val; } /** * Sets the requested data array element in the specified bank to * the given float. * * @param bank The bank number. * @param i The desired data array element. * @param val The value to be set. */ public void setElemFloat(int bank, int i, float val) { bankdata[bank][i+offsets[bank]] = val; } /** * Returns the requested data array element from the first * (default) bank as a double. * * @param i The desired data array element. * * @return The data entry as a double. */ public double getElemDouble(int i) { return (double)data[i+offset]; } /** * Returns the requested data array element from the specified * bank as a double. * * @param bank The bank number. * @param i The desired data array element. * * @return The data entry as a double. */ public double getElemDouble(int bank, int i) { return (double)bankdata[bank][i+offsets[bank]]; } /** * Sets the requested data array element in the first (default) * bank to the given double. * * @param i The desired data array element. * @param val The value to be set. */ public void setElemDouble(int i, double val) { data[i+offset] = (float)val; } /** * Sets the requested data array element in the specified bank to * the given double. * * @param bank The bank number. * @param i The desired data array element. * @param val The value to be set. */ public void setElemDouble(int bank, int i, double val) { bankdata[bank][i+offsets[bank]] = (float)val; } } jai-core-1.1.4/src/share/classes/javax/media/jai/BorderExtenderWrap.java0000644000175000017500000001325010203035544025747 0ustar mathieumathieu/* * $RCSfile: BorderExtenderWrap.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:04 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.WritableRaster; /** * A subclass of BorderExtender that implements * border extension by filling all pixels outside of the image * bounds with copies of the whole image. For example, the image: * *

      * * * * *
      ABC
      DEF
      GHI
      * *
      if extended by adding two extra rows to the top and bottom and * two extra columns on the left and right sides, would become: * *

      * * * * * * * * * *
      EFDEFDE
      HIGHIGH
      BCABCAB
      EFDEFDE
      HIGHIGH
      BCABCAB
      EFDEFDE
      * *

      This form of extension is appropriate for data that is inherently * periodic, such as the Fourier transform of an image, or a wallpaper * pattern. * * @see BorderExtender */ public class BorderExtenderWrap extends BorderExtender { BorderExtenderWrap() {} /** * Fills in the portions of a given Raster that lie * outside the bounds of a given PlanarImage with * copies of the entire image. * *

      The portion of raster that lies within * im.getBounds() is not altered. * * @param raster The WritableRaster the border area of * which is to be filled with copies of the given image. * @param im The PlanarImage which will be copied * to fill the border area of the * WritableRaster. * * @throws IllegalArgumentException if either parameter is * null. */ public final void extend(WritableRaster raster, PlanarImage im) { if ( raster == null || im == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } int width = raster.getWidth(); int height = raster.getHeight(); int minX = raster.getMinX(); int maxX = minX + width; int minY = raster.getMinY(); int maxY = minY + height; int imMinX = im.getMinX(); int imMinY = im.getMinY(); int imWidth = im.getWidth(); int imHeight = im.getHeight(); Rectangle rect = new Rectangle(); // Notionally extend the source image by treating it as a single // tile of an infinite tiled image. // Compute the min and max X and Y tile indices of the area // intersected by the output raster. int minTileX = PlanarImage.XToTileX(minX, imMinX, imWidth); int maxTileX = PlanarImage.XToTileX(maxX - 1, imMinX, imWidth); int minTileY = PlanarImage.YToTileY(minY, imMinY, imHeight); int maxTileY = PlanarImage.YToTileY(maxY - 1, imMinY, imHeight); // Loop over the tiles for (int tileY = minTileY; tileY <= maxTileY; tileY++) { int ty = tileY*imHeight + imMinY; for (int tileX = minTileX; tileX <= maxTileX; tileX++) { int tx = tileX*imWidth + imMinX; // Don't touch the central "tile" (actual image) if (tileX == 0 && tileY == 0) { continue; } // Clip the tile bounds against the bounds of the Raster. // Keep track of the (x, y) offset of the start of the tile. rect.x = tx; rect.y = ty; rect.width = imWidth; rect.height = imHeight; int xOffset = 0; if (rect.x < minX) { xOffset = minX - rect.x; rect.x = minX; rect.width -= xOffset; } int yOffset = 0; if (rect.y < minY) { yOffset = minY - rect.y; rect.y = minY; rect.height -= yOffset; } if (rect.x + rect.width > maxX) { rect.width = maxX - rect.x; } if (rect.y + rect.height > maxY) { rect.height = maxY - rect.y; } // Create a child raster with coordinates within the // actual image. WritableRaster child = RasterFactory.createWritableChild(raster, rect.x, rect.y, rect.width, rect.height, imMinX + xOffset, imMinY + yOffset, null); // Copy the data into the Raster im.copyData(child); } } } } jai-core-1.1.4/src/share/classes/javax/media/jai/ImageJAI.java0000644000175000017500000000055710203035544023555 0ustar mathieumathieu/* * $RCSfile: ImageJAI.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:09 $ * $State: Exp $ */ package javax.media.jai; /** * An interface implemented by all JAI image classes. */ public interface ImageJAI extends WritablePropertySource { } jai-core-1.1.4/src/share/classes/javax/media/jai/ColorModelFactory.java0000644000175000017500000000504310203035544025571 0ustar mathieumathieu/* * $RCSfile: ColorModelFactory.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:06 $ * $State: Exp $ */ package javax.media.jai; import java.awt.image.ColorModel; import java.awt.image.SampleModel; import java.util.List; import java.util.Map; import java.util.Vector; /** * Interface defining a callback which may be used to create a * ColorModel for the rendering of a node in an * operation chain. The value corresponding to the key * {@link JAI#KEY_COLOR_MODEL_FACTORY} in a configuration * mapping must be of type ColorModelFactory. This * configuration variable is recognized by the constructor * {@link OpImage#OpImage(Vector,ImageLayout,Map,boolean)}. * * @since JAI 1.1.2 */ public interface ColorModelFactory { /** * Create a ColorModel given the image * SampleModel and configuration variables. * When invoked in the context of * {@link OpImage#OpImage(Vector,ImageLayout,Map,boolean)}, * the SampleModel will be that of the * OpImage and the source list and configuration * mapping will be those which were supplied to the * OpImage constructor. * *

      The implementing class should in general ensure that the * ColorModel created is compatible with the * supplied SampleModel. If it is known a priori * that compatibility is verified by the object which invokes this * method, then such compatibility verification might be * safely omitted.

      * * @param sampleModel The SampleModel to which the * ColorModel to be created must correspond; * may not be null. * @param sources A List of RenderedImages; * may be null. * @param configuration A configuration mapping; may be * null. * @return A new ColorModel or null if it * is not possible for the ColorModelFactory * to create a ColorModel for the supplied * parameters. * @exception IllegalArgumentException if sampleModel * is null. */ ColorModel createColorModel(SampleModel sampleModel, List sources, Map configuration); } jai-core-1.1.4/src/share/classes/javax/media/jai/NullCRIF.java0000644000175000017500000001007010203035544023554 0ustar mathieumathieu/* * $RCSfile: NullCRIF.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:12 $ * $State: Exp $ */ package javax.media.jai; import java.awt.RenderingHints; import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D.Float; import java.awt.image.RenderedImage; import java.awt.image.renderable.ContextualRenderedImageFactory; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import java.awt.image.renderable.RenderContext; import javax.media.jai.ImageLayout; import javax.media.jai.JAI; import javax.media.jai.RenderedOp; /** * A ContextualRenderedImageFactory representing an operation * which performs no processing of its image source(s) per se, i.e., a no-op. * *

      The primary use of this image factory is as a utility class in * implementing operations which generate only non-image data via the * use of PropertyGenerators. A PropertyGenerator * is defined as always contributing to the property environment of a given * operation when it is returned by the getPropertyGenerators() * method of the OperationDescriptor corresponding to the * operation. * *

      The procedure to be followed to register an operation which generates * only non-image data as JAI image properties is as follows: * *

        *
      • Create a PropertyGenerator which calculates the * non-image data given the operation node; *
      • Create an OperationDescriptor the * getPropertyGenerators() method of which returns the * PropertyGenerator defined in the previous step; *
      • Register the OperationDescriptor with the * OperationRegistry as usual by passing it to * registerOperationDescriptor() along with the operation name; *
      • Register a NullCRIF as the image factory corresponding to * this operation. *
      * * The properties emitted by the associated PropertyGenerator(s) * will then be available by invoking getProperty() on the node * returned by JAI.create() using the registered operation name. * * @see CRIFImpl * @see java.awt.image.renderable.ContextualRenderedImageFactory * * @since JAI 1.1 */ public class NullCRIF extends CRIFImpl { /** * Image returned by RenderedImageFactory.create() * when there are ono sources. */ private static RenderedImage sourcelessImage = null; /** * Constructs a NullCRIF. The operationName * in the superclass is set to null. */ public NullCRIF() { super(); } /** * Sets the value of the RenderedImage to be returned by * the RenderedImageFactory.create() method when there are * no sources in the ParameterBlock. * * @param a RenderedImage or null. */ public static final synchronized void setSourcelessImage(RenderedImage im) { sourcelessImage = im; } /** * Gets the value of the RenderedImage to be returned by the RIF.create() * method when there are no sources in the ParameterBlock. * * @return a RenderedImage or null. */ public static final synchronized RenderedImage getSourcelessImage() { return sourcelessImage; } /** * Returns the first source in the source list in the * ParameterBlock or the value returned by * getSourcelessImage() if there are no soures. * * @throws ClassCastException if there are sources and the source * at index zero is not a RenderedImage. */ public RenderedImage create(ParameterBlock args, RenderingHints renderHints) { return args.getNumSources() == 0 ? getSourcelessImage() : args.getRenderedSource(0); } } jai-core-1.1.4/src/share/classes/javax/media/jai/RasterFormatTag.java0000644000175000017500000001602510203035544025251 0ustar mathieumathieu/* * $RCSfile: RasterFormatTag.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:18 $ * $State: Exp $ */ package javax.media.jai; import java.awt.image.SampleModel; import java.awt.image.ComponentSampleModel; import java.awt.Rectangle; /** * This class encapsulates the information needed for * RasterAccessor to understand how a Raster is laid out. It's * designed so that one RasterFormatTag can be constructed per * source and that RasterFormatTag can cache information that * the RasterAccessor would otherwise have to extract from the * Raster each time it's constructed (generally each time * OpImage.computeRect() is called.) Additionally, it can * cache various arrays (i.e. bankIndices[] and bandOffsets[]) * that would otherwise be cloned everytime they were requested. * * Because of the way SampleModel.createCompatibleSampleModel() * is designed not all fields of a particular SampleModel will * match those of the SampleModel returned by * SampleModel.createCompatibleSampleModel(). Values like * pixelStride and numBands won't change, but values like * bankIndicies[] and bandOffsets[] might if the underlying * Raster is not pixelSequential. Rasters which are pixelSequential * meet the following conditions 1) The SampleModel is a * ComponentSampleModel. 2) The pixelStride is equal to the number * of bands. 3) All the bankIndices[] are equal. 4) All the * bandOffsets[] values are less than pixelStride 5) No two * bandOffsets[] values are equal. For that reason, * RasterFormatTags representing non pixelSequential rasters don't * attempt to cache the bandOffsets[] or bankIndices[]. For such * rasters, this information should be taken directly from the * raster itself. Note that any RasterFormatTag that will cause * data to be copied from the Raster will be pixelSequential as that * is the format in which data is returned from Raster.getPixels() * returns. * */ public final class RasterFormatTag { private static final int COPY_MASK = RasterAccessor.COPY_MASK; private static final int UNCOPIED = RasterAccessor.UNCOPIED; private static final int COPIED = RasterAccessor.COPIED; private int formatTagID; private int bankIndices[]; private int numBands; private int bandOffsets[]; private int pixelStride; private boolean isPixelSequential; /** * Constructs a RasterFormatTag given a sampleModel and a * formatTagID. Generally, this constructor is called by * RasterAccessor.findCompatibleTags(RenderedImage[] srcs, * RenderedImage dst) and it takes care of setting the values * correctly. In special cases, OpImages need to construct * a RasterFormatTag without creating a RenderedImage. In this * case a RasterFormatTag can be created using a formatTagID * returned from * RasterAccessor.findCompatibleTag(SampleModel[] srcs, SampleModel dst) * and a sampleModel that was either passed in to the * findCompatibleTag() call or one that was created using * createCompatibleSampleModel() on one of the passed in * SampleModels. Attempting to use arbitrary SampleModels * with arbitrary formatTagIDs has undefined results. * * param sampleModel A SampleModel for the RasterFormagTag * param formatTagID An int to indicate format tag id * */ public RasterFormatTag(SampleModel sampleModel, int formatTagID) { this.formatTagID = formatTagID; if ((formatTagID & COPY_MASK) == UNCOPIED) { ComponentSampleModel csm = (ComponentSampleModel)sampleModel; this.bankIndices = csm.getBankIndices(); this.numBands = csm.getNumDataElements(); this.bandOffsets = csm.getBandOffsets(); this.pixelStride = csm.getPixelStride(); if (pixelStride != bandOffsets.length) { isPixelSequential = false; } else { isPixelSequential = true; for (int i = 0; i < bandOffsets.length; i++) { if (bandOffsets[i] >= pixelStride || bankIndices[i] != bankIndices[0]) { isPixelSequential = false; } for (int j = i+1; j < bandOffsets.length; j++) { if (bandOffsets[i] == bandOffsets[j]) { isPixelSequential = false; } } if (!isPixelSequential) break; } } } else if ((formatTagID & COPY_MASK) == COPIED) { numBands = sampleModel.getNumBands(); bandOffsets = new int[numBands]; pixelStride = numBands; bankIndices = new int[numBands]; for (int i = 0; i < numBands; i++) { bandOffsets[i] = i; bankIndices[i] = 0; } isPixelSequential = true; } } /** * Returns whether or not the SampleModel represented by the * RasterFormatTag is PixelSequential. * Note that RasterFormatTag's that indicate * data should be copied out of the Raster by the RasterAccessor * will always return true for isPixelSequential(). * RasterFormatTags that indicate no copying is needed will only * return true, if 1) The SampleModel is a ComponentSampleModel. * 2) The pixelStride is equal to the number of bands. * 3) All the bankIndices[] are equal. 4) All the bandOffsets[] * values are less than pixelStride 5) No two bandOffset values * are equal. */ public final boolean isPixelSequential() { return isPixelSequential; } /** * Returns the FormatTagID used to construct this RasterFormatTag. * Valid values are defined in javax.media.jai.RasterAccessor. */ public final int getFormatTagID() { return formatTagID; } /** * Returns the bankIndices for the Raster if isPixelSequential() * is true. Returns null otherwise. In the COPIED case, the * bankIndices will all be 0. */ public final int[] getBankIndices() { if (isPixelSequential) { return bankIndices; } else { return null; } } /** Returns the number of bands in the underlying Raster */ public final int getNumBands() { return numBands; } /** * Returns the bandOffsets for the Raster if isPixelSequential() is * true. Returns null otherwise. In the COPIED case, bankIndices * will be numBands sequential integers starting with 0. */ public final int[] getBandOffsets() { if (isPixelSequential) { return bandOffsets; } else { return null; } } /** Returns the pixelStride of the underlying Raster */ public final int getPixelStride() { return pixelStride; } } jai-core-1.1.4/src/share/classes/javax/media/jai/PropertyEnvironment.java0000644000175000017500000003000710203035544026251 0ustar mathieumathieu/* * $RCSfile: PropertyEnvironment.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:16 $ * $State: Exp $ */ package javax.media.jai; import com.sun.media.jai.util.CaselessStringKeyHashtable; import com.sun.media.jai.util.PropertyUtil; import java.awt.Rectangle; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import java.util.Vector; import javax.media.jai.util.CaselessStringKey; /** * A class that implements the PropertySource interface. * Property names are treated in a case-insensitive manner. * * * @since JAI 1.1 */ // In JAI 1.0.2 this class was namex javax.media.jai.PropertySourceImpl // and was package scope (as it is now). class PropertyEnvironment implements PropertySource { /** The local PropertyGenerators of this PropertyEnvironment. */ Vector pg; /** * The sources of the associated node. The elements should * be PropertySources. */ Vector sources; // Dummy value to associate with a key in "suppressed" private static final Object PRESENT = new Object(); /** The names of suppressed properties. */ CaselessStringKeyHashtable suppressed; /** * Sources of properties of this node. The keys are property names * and the values are Integers which should be indexes into the Vector * of sources. */ CaselessStringKeyHashtable sourceForProp; /** * The associated node, which is either a RenderedOp or a RenderableOp. */ private Object op; /** * Hash of property names. Values are either PropertyGenerators, * PropertySources, or Integers. */ private CaselessStringKeyHashtable propNames; /** * Locally added default PropertySource which will override the default * property inheritance mechanism, viz., inheritance from the lowest * indexed source if no other means was specified. */ private PropertySource defaultPropertySource = null; /** * Flag which is set to indicate that the default PropertySource * has not yet been mapped into the property name table. Initially * true as there is nothing to map. */ private boolean areDefaultsMapped = true; /** * Constructs a PropertyEnvironment * * @param sources PropertySources in operation source order. * @param generators PropertyGenerators. * @param suppressed Names of suppressed properties. * @param sourceForProp Hash by property name of indexes of * PropertySources in sources from * which to derive properties. * @param op The operation node. */ public PropertyEnvironment(Vector sources, Vector generators, Vector suppressed, Hashtable sourceForProp, Object op) { this.sources = sources; this.pg = generators == null ? null : (Vector)generators.clone(); // "suppressed" should never be null this.suppressed = new CaselessStringKeyHashtable(); if (suppressed != null) { Enumeration e = suppressed.elements(); while (e.hasMoreElements()) { this.suppressed.put(e.nextElement(), PRESENT); } } this.sourceForProp = (sourceForProp == null) ? null : new CaselessStringKeyHashtable(sourceForProp); this.op = op; hashNames(); } /** * Returns an array of Strings recognized as names by this * property source. * * @return an array of Strings giving the valid property names. */ public String[] getPropertyNames() { mapDefaults(); int count = 0; String names[] = new String[propNames.size()]; for (Enumeration e = propNames.keys(); e.hasMoreElements(); ) { names[count++] = ((CaselessStringKey)e.nextElement()).getName(); } return names; } /** * Returns an array of Strings recognized as names by * this property source that begin with the supplied prefix. If * no property names match, null will be returned. * The comparison is done in a case-independent manner. * *

      The default implementation calls getPropertyNames() * and searches the list of names for matches. * * @return an array of Strings giving the valid * property names. */ public String[] getPropertyNames(String prefix) { // This gives us a list of all non-suppressed properties String[] propertyNames = getPropertyNames(); return PropertyUtil.getPropertyNames(propertyNames, prefix); } /** * Returns the class expected to be returned by a request for * the property with the specified name. If this information * is unavailable, null will be returned. * *

      This implemention returns null to avoid * provoking deferred calculations. * * @return The Class expected to be return by a * request for the value of this property or null. */ public Class getPropertyClass(String propertyName) { if(propertyName == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return null; } /** * Returns the value of a property. * * @param name the name of the property, as a String. * @return the value of the property, as an Object. */ public Object getProperty(String name) { if(name == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } mapDefaults(); Object o = propNames.get(name); Object property = null; if (o == null) { return java.awt.Image.UndefinedProperty; } else if (o instanceof PropertyGenerator) { property = ((PropertyGenerator)o).getProperty(name, op); } else if (o instanceof Integer) { // copy from source int srcIndex = ((Integer)o).intValue(); PropertySource src = (PropertySource)sources.elementAt(srcIndex); property = src.getProperty(name); } else if (o instanceof PropertySource) { property = ((PropertySource)o).getProperty(name); } return property; } /** ---- Methods to modify the local property environment. ---- */ public void copyPropertyFromSource(String propertyName, int sourceIndex) { PropertySource propertySource = (PropertySource)sources.elementAt(sourceIndex); propNames.put(propertyName, propertySource); suppressed.remove(propertyName); } public void suppressProperty(String propertyName) { suppressed.put(propertyName, PRESENT); hashNames(); } public void addPropertyGenerator(PropertyGenerator generator) { if (pg == null) { pg = new Vector(); } pg.addElement(generator); // Remove suppressed status of any property being generated by // this PropertyGenerator removeSuppressedProps(generator); hashNames(); } /** * Sets a PropertySource from which to derive all non-suppressed * properties emitted by the PropertySource if and only if neither * a PropertyGenerator nor a copy-from-source directive exists * for the requested property. This PropertySource will supersede * automatic inheritance from any (operation) sources. It should * be used, for example, when a rendered node wishes to derive * property values from its rendering without overriding any user * configuration settings in the property environment of the node. */ public void setDefaultPropertySource(PropertySource ps) { // If no change just return. if(ps == defaultPropertySource) { return; } if(defaultPropertySource != null) { // Return the table to zero state if defaults existed before. hashNames(); } // Unset the flag. areDefaultsMapped = false; // Cache the parameter. defaultPropertySource = ps; } /** * Updates "propNames" hash with the default PropertySource. This * method allows deferred access to the default PropertySource so * as to postpone calculations which access thereto might incur. * Does nothing unless the default PropertySource has changed. */ private void mapDefaults() { if(!areDefaultsMapped) { // Set the flag. areDefaultsMapped = true; // Update with default PropertySource only if non-null. if(defaultPropertySource != null) { String[] names = defaultPropertySource.getPropertyNames(); if(names != null) { int length = names.length; for(int i = 0; i < length; i++) { if(!suppressed.containsKey(names[i])) { Object o = propNames.get(names[i]); if(o == null || // undefined property o instanceof Integer) { // default inheritance // Add "defaultPropertySource" or // replace default inheritance. propNames.put(names[i], defaultPropertySource); } } } } } } } private void removeSuppressedProps(PropertyGenerator generator) { String names[] = generator.getPropertyNames(); for (int i=0; i= 0; i--) { Object o = sources.elementAt(i); if (o instanceof PropertySource) { PropertySource source = (PropertySource)o; String[] propertyNames = source.getPropertyNames(); if (propertyNames != null) { for (int j = 0; j < propertyNames.length; j++) { String name = propertyNames[j]; if (!suppressed.containsKey(name)) { propNames.put(name, new Integer(i)); } } } } } } // Get non-suppressed properties from PropertyGenerators. // The propNames values are PropertyGenerator instances. if (pg != null) { PropertyGenerator generator; for (Iterator it = pg.iterator(); it.hasNext(); ) { generator = (PropertyGenerator)it.next(); if(generator.canGenerateProperties(op)) { String[] propertyNames = generator.getPropertyNames(); if (propertyNames != null) { for (int i = 0; i < propertyNames.length; i++) { String name = propertyNames[i]; if (!suppressed.containsKey(name)) { propNames.put(name, generator); } } } } } } // Lastly, honor all the copyPropertyFromSource directives // The propNames values are PropertySource instances. if (sourceForProp != null) { for (Enumeration e = sourceForProp.keys(); e.hasMoreElements(); ) { CaselessStringKey name = (CaselessStringKey)e.nextElement(); if (!suppressed.containsKey(name)) { Integer i = (Integer)sourceForProp.get(name); PropertySource propertySource = (PropertySource)sources.elementAt(i.intValue()); propNames.put(name, propertySource); } } } // Unset the default mapping flag. areDefaultsMapped = false; } } jai-core-1.1.4/src/share/classes/javax/media/jai/ParameterListImpl.java0000644000175000017500000004455210203035544025610 0ustar mathieumathieu/* * $RCSfile: ParameterListImpl.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:15 $ * $State: Exp $ */ package javax.media.jai; import com.sun.media.jai.util.CaselessStringArrayTable; import java.text.MessageFormat; import java.util.Hashtable; import java.util.Locale; import javax.media.jai.util.CaselessStringKey; /** * A concrete implementation of the ParameterList * interface. The number, names, Class types and default values are * specified via the associated ParameterListDescriptor * which should be supplied at construction time. This * default implementation should be sufficient for most * ParameterLists and normally need not be sub-classed. * * @see ParameterList * @see ParameterListDescriptor * @see ParameterListDescriptorImpl * * @since JAI 1.1 */ public class ParameterListImpl implements ParameterList, java.io.Serializable { private ParameterListDescriptor pld; /** * A CaselessStringArrayTable of parameter indices hashed by * CaselessStringKey versions of the names. */ private CaselessStringArrayTable paramIndices; /** * Something to hold the parameter values. */ private Object[] paramValues; /** * The parameter classes obtained from ParameterListDescriptor */ private Class[] paramClasses; /** * Creates a ParameterListImpl using the specified * ParameterListDescriptor. Initializes the * parameters to the defaults (could be * ParameterListDescriptor.NO_PARAMETER_DEFAULT) * specified by descriptor * * @param descriptor a ParameterListDescriptor describing * the parameter names, defaults etc. * * @throws IllegalArgumentException if descriptor is null */ public ParameterListImpl(ParameterListDescriptor descriptor) { if (descriptor == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); this.pld = descriptor; int numParams = pld.getNumParameters(); if (numParams > 0) { // Fill in the parameter defaults. Object[] paramDefaults = pld.getParamDefaults(); paramClasses = pld.getParamClasses(); paramIndices = new CaselessStringArrayTable(pld.getParamNames()); paramValues = new Object[numParams]; for (int i = 0; i < numParams; i++) { paramValues[i] = paramDefaults[i]; } } else { paramClasses = null; paramIndices = null; paramValues = null; } } /** * Returns the associated ParameterListDescriptor. */ public ParameterListDescriptor getParameterListDescriptor() { return pld; } /** * A private method (so that it may get inlined) which sets * the value of the specified parameter. * * Checks are made to verify that the parameter is of the right * Class type and that the value is valid. * * @param paramName a String naming a parameter. * @param b a byte value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws IllegalArgumentException if the class type of parameter * pointed to by the paramName is not a Byte * @throws IllegalArgumentException if the parameter value is invalid. */ private ParameterList setParameter0(String paramName, Object obj) { int index = paramIndices.indexOf(paramName); if ((obj != null) && !paramClasses[index].isInstance(obj)) throw new IllegalArgumentException(formatMsg( JaiI18N.getString("ParameterListImpl0"), new Object[] { obj.getClass().getName(), paramClasses[index].getName(), paramName })); if (!pld.isParameterValueValid(paramName, obj)) throw new IllegalArgumentException(paramName + ":" + JaiI18N.getString("ParameterListImpl1")); paramValues[index] = obj; return this; } /** * Sets a named parameter to a byte value. * Checks are made to verify that the parameter is of the right * Class type and that the value is valid. * * @param paramName a String naming a parameter. * @param b a byte value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws IllegalArgumentException if the class type of parameter * pointed to by the paramName is not a Byte * @throws IllegalArgumentException if the parameter value is invalid. */ public ParameterList setParameter(String paramName, byte b) { return setParameter0(paramName, new Byte(b)); } /** * Sets a named parameter to a boolean value. * Checks are made to verify that the parameter is of the right * Class type and that the value is valid. * * @param paramName a String naming a parameter. * @param b a boolean value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws IllegalArgumentException if the class type of parameter * pointed to by the paramName is not a Boolean * @throws IllegalArgumentException if the parameter value is invalid. */ public ParameterList setParameter(String paramName, boolean b) { return setParameter0(paramName, new Boolean(b)); } /** * Sets a named parameter to a char value. * Checks are made to verify that the parameter is of the right * Class type and that the value is valid. * * @param paramName a String naming a parameter. * @param c a char value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws IllegalArgumentException if the class type of parameter * pointed to by the paramName is not a Character * @throws IllegalArgumentException if the parameter value is invalid. */ public ParameterList setParameter(String paramName, char c) { return setParameter0(paramName, new Character(c)); } /** * Sets a named parameter to a short value. * Checks are made to verify that the parameter is of the right * Class type and that the value is valid. * * @param paramName a String naming a parameter. * @param s a short value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws IllegalArgumentException if the class type of parameter * pointed to by the paramName is not a Short * @throws IllegalArgumentException if the parameter value is invalid. */ public ParameterList setParameter(String paramName, short s) { return setParameter0(paramName, new Short(s)); } /** * Sets a named parameter to an int value. * Checks are made to verify that the parameter is of the right * Class type and that the value is valid. * * @param paramName a String naming a parameter. * @param i an int value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws IllegalArgumentException if the class type of parameter * pointed to by the paramName is not a Integer * @throws IllegalArgumentException if the parameter value is invalid. */ public ParameterList setParameter(String paramName, int i) { return setParameter0(paramName, new Integer(i)); } /** * Sets a named parameter to a long value. * Checks are made to verify that the parameter is of the right * Class type and that the value is valid. * * @param paramName a String naming a parameter. * @param l a long value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws IllegalArgumentException if the class type of parameter * pointed to by the paramName is not a Long * @throws IllegalArgumentException if the parameter value is invalid. */ public ParameterList setParameter(String paramName, long l) { return setParameter0(paramName, new Long(l)); } /** * Sets a named parameter to a float value. * Checks are made to verify that the parameter is of the right * Class type and that the value is valid. * * @param paramName a String naming a parameter. * @param f a float value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws IllegalArgumentException if the class type of parameter * pointed to by the paramName is not a Float * @throws IllegalArgumentException if the parameter value is invalid. */ public ParameterList setParameter(String paramName, float f) { return setParameter0(paramName, new Float(f)); } /** * Sets a named parameter to a double value. * Checks are made to verify that the parameter is of the right * Class type and that the value is valid. * * @param paramName a String naming a parameter. * @param d a double value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws IllegalArgumentException if the class type of parameter * pointed to by the paramName is not a Double * @throws IllegalArgumentException if the parameter value is invalid. */ public ParameterList setParameter(String paramName, double d) { return setParameter0(paramName, new Double(d)); } /** * Sets a named parameter to an Object value. * Checks are made to verify that the parameter is of the right * Class type and that the value is valid. * * @param paramName a String naming a parameter. * @param obj an Object value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws IllegalArgumentException if the parameter value is invalid. */ public ParameterList setParameter(String paramName, Object obj) { return setParameter0(paramName, obj); } /** * A private method (so that it can be inlined) to get the * value associated with the specified parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws IllegalStateException if the parameter value is still * ParameterListDescriptor.NO_PARAMETER_DEFAULT */ private Object getObjectParameter0(String paramName) { Object obj = paramValues[paramIndices.indexOf(paramName)]; if (obj == ParameterListDescriptor.NO_PARAMETER_DEFAULT) throw new IllegalStateException(paramName + ":" + JaiI18N.getString("ParameterListImpl2")); return obj; } /** * Gets a named parameter as an Object. Parameters * belonging to a primitive type, such as int, will be returned as a * member of the corresponding wrapper class, such as * Integer * * @param paramName the name of the parameter to be returned. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws IllegalStateException if the parameter value is still * ParameterListDescriptor.NO_PARAMETER_DEFAULT */ public Object getObjectParameter(String paramName) { return getObjectParameter0(paramName); } /** * A convenience method to return a parameter as a byte. * * @param paramName the name of the parameter to be returned. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws ClassCastException if the parameter is of a different type. * @throws IllegalStateException if the parameter value is still * ParameterListDescriptor.NO_PARAMETER_DEFAULT */ public byte getByteParameter(String paramName) { return ((Byte)getObjectParameter0(paramName)).byteValue(); } /** * A convenience method to return a parameter as a boolean. * * @param paramName the name of the parameter to be returned. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws ClassCastException if the parameter is of a different type. * @throws IllegalStateException if the parameter value is still * ParameterListDescriptor.NO_PARAMETER_DEFAULT */ public boolean getBooleanParameter(String paramName) { return ((Boolean)getObjectParameter0(paramName)).booleanValue(); } /** * A convenience method to return a parameter as a char. * * @param paramName the name of the parameter to be returned. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws ClassCastException if the parameter is of a different type. * @throws IllegalStateException if the parameter value is still * ParameterListDescriptor.NO_PARAMETER_DEFAULT */ public char getCharParameter(String paramName) { return ((Character)getObjectParameter0(paramName)).charValue(); } /** * A convenience method to return a parameter as a short. * * @param paramName the name of the parameter to be returned. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws ClassCastException if the parameter is of a different type. * @throws IllegalStateException if the parameter value is still * ParameterListDescriptor.NO_PARAMETER_DEFAULT */ public short getShortParameter(String paramName) { return ((Short)getObjectParameter0(paramName)).shortValue(); } /** * A convenience method to return a parameter as an int. * * @param paramName the name of the parameter to be returned. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws ClassCastException if the parameter is of a different type. * @throws IllegalStateException if the parameter value is still * ParameterListDescriptor.NO_PARAMETER_DEFAULT */ public int getIntParameter(String paramName) { return ((Integer)getObjectParameter0(paramName)).intValue(); } /** * A convenience method to return a parameter as a long. * * @param paramName the name of the parameter to be returned. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws ClassCastException if the parameter is of a different type. * @throws IllegalStateException if the parameter value is still * ParameterListDescriptor.NO_PARAMETER_DEFAULT */ public long getLongParameter(String paramName) { return ((Long)getObjectParameter0(paramName)).longValue(); } /** * A convenience method to return a parameter as a float. * * @param paramName the name of the parameter to be returned. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws ClassCastException if the parameter is of a different type. * @throws IllegalStateException if the parameter value is still * ParameterListDescriptor.NO_PARAMETER_DEFAULT */ public float getFloatParameter(String paramName) { return ((Float)getObjectParameter0(paramName)).floatValue(); } /** * A convenience method to return a parameter as a double. * * @param paramName the name of the parameter to be returned. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws ClassCastException if the parameter is of a different type. * @throws IllegalStateException if the parameter value is still * ParameterListDescriptor.NO_PARAMETER_DEFAULT */ public double getDoubleParameter(String paramName) { return ((Double)getObjectParameter0(paramName)).doubleValue(); } /** * Creates a MessageFormat object and set the * Locale to default. */ private String formatMsg(String key, Object[] args) { MessageFormat mf = new MessageFormat(key); mf.setLocale(Locale.getDefault()); return mf.format(args); } } jai-core-1.1.4/src/share/classes/javax/media/jai/NullOpImage.java0000644000175000017500000002231710203035544024361 0ustar mathieumathieu/* * $RCSfile: NullOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:12 $ * $State: Exp $ */ package javax.media.jai; import java.awt.RenderingHints; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.util.Hashtable; import java.util.Map; import javax.media.jai.util.CaselessStringKey; import com.sun.media.jai.util.JDKWorkarounds; /** * A trivial OpImage subclass that simply transmits its * source unchanged. This may be useful when an interface requires an * OpImage but another sort of RenderedImage * (such as a BufferedImage or TiledImage) * is available. Additionally, NullOpImage is able to * make use of JAI's tile caching mechanisms. * *

      Methods that get or set properties are implemented to forward * the requests to the source image; no independent property information * is stored in the NullOpImage itself. * * @see PointOpImage */ public class NullOpImage extends PointOpImage { protected int computeType; /** * Create a new ImageLayout from the source image optionally * overriding a ColorModel supplied via the layout. */ private static ImageLayout layoutHelper(RenderedImage source, ImageLayout layout) { // Create basic layout from the source. ImageLayout il = new ImageLayout(source); // If a layout containing a valid ColorModel field is supplied then // reset the ColorModel if it is compatible with the SampleModel. if(layout != null && layout.isValid(ImageLayout.COLOR_MODEL_MASK)) { ColorModel colorModel = layout.getColorModel(null); if(JDKWorkarounds.areCompatibleDataModels(source.getSampleModel(), colorModel)) { il.setColorModel(colorModel); } } return il; } /** * Constructs a NullOpImage. The superclass * constructor will be passed a new ImageLayout * object with all of its fields filled in. The ColorModel * may be overridden via the supplied ImageLayout; all * other layout fields are derived from the source image. Any * specified ColorModel will be used if and only if it * is compatible with the source image SampleModel. * * @param layout An ImageLayout optionally specifying * the image ColorModel; all other fields are * ignored. This parameter may be null. * @param source A RenderedImage; must not be * null or a IllegalArgumentException * will be thrown. * @param configuration Configurable attributes of the image including * configuration variables indexed by * RenderingHints.Keys and image properties indexed * by Strings or CaselessStringKeys. * This is simply forwarded to the superclass constructor. * @param computeType A tag indicating whether the source * is OpImage.OP_COMPUTE_BOUND, * OpImage.OP_IO_BOUND or * OpImage.OP_NETWORK_BOUND. This information is * used as a hint to optimize OpImage computation. * * @throws IllegalArgumentException if source * is null. * @throws IllegalArgumentException if computeType * is not one of the known OP_*_BOUND values. * * @since JAI 1.1 */ public NullOpImage(RenderedImage source, ImageLayout layout, Map configuration, int computeType) { // cobbleSources is irrelevant since we override getTile(). super(PlanarImage.wrapRenderedImage(source).createSnapshot(), layoutHelper(source, layout), configuration, false); if(computeType != OP_COMPUTE_BOUND && computeType != OP_IO_BOUND && computeType != OP_NETWORK_BOUND) { throw new IllegalArgumentException(JaiI18N.getString("NullOpImage0")); } this.computeType = computeType; } /** * Constructs a NullOpImage. The superclass * constructor will be passed a new ImageLayout * object with all of its fields filled in. The ColorModel * may be overridden via the supplied ImageLayout; all * other layout fields are derived from the source image. Any * specified ColorModel will be used if and only if it * is compatible with the source image SampleModel. * * @param source A RenderedImage; must not be * null or a IllegalArgumentException * will be thrown. * @param cache a TileCache object to store tiles from this OpImage, * or null. If null, a default cache will be used. * @param computeType A tag indicating whether the source * is OpImage.OP_COMPUTE_BOUND, * OpImage.OP_IO_BOUND or * OpImage.OP_NETWORK_BOUND. This information is * used as a hint to optimize OpImage computation. * @param layout An ImageLayout optionally specifying * the image ColorModel; all other fields are * ignored. This parameter may be null. * * @throws IllegalArgumentException if source * is null. * @throws IllegalArgumentException if computeType * is not one of the known OP_*_BOUND values. * * @deprecated as of JAI 1.1. */ public NullOpImage(RenderedImage source, TileCache cache, int computeType, ImageLayout layout) { this(source, layout, cache != null ? new RenderingHints(JAI.KEY_TILE_CACHE, cache) : null, computeType); } /** * Returns a tile for reading. * * @param tileX The X index of the tile. * @param tileY The Y index of the tile. * @return The tile as a Raster. */ public Raster computeTile(int tileX, int tileY) { return getSource(0).getTile(tileX, tileY); } /** * Returns false as NullOpImage can return via computeTile() * tiles that are internally cached. */ public boolean computesUniqueTiles() { return false; } /** * Returns the properties from the source image. */ protected synchronized Hashtable getProperties() { return getSource(0).getProperties(); } /** * Set the properties Hashtable of the source image * to the supplied Hashtable. */ protected synchronized void setProperties(Hashtable properties) { getSource(0).setProperties(properties); } /** * Returns the property names from the source image or null * if no property names are recognized. */ public String[] getPropertyNames() { return getSource(0).getPropertyNames(); } /** * Returns the property names with the supplied prefix from * the source image or null if no property names * are recognized. */ public String[] getPropertyNames(String prefix) { return getSource(0).getPropertyNames(prefix); } /** * Returns the class of the specified property from the source image. * * @since JAI 1.1 */ public Class getPropertyClass(String name) { return getSource(0).getPropertyClass(name); } /** * Retrieves a property from the source image by name or * java.awt.Image.UndefinedProperty if the property * with the specified name is not defined. */ public Object getProperty(String name) { return getSource(0).getProperty(name); } /** * Sets a property on the source image by name. */ public void setProperty(String name, Object value) { getSource(0).setProperty(name, value); } /** * Removes a property from the source image by name. * * @since JAI 1.1 */ public void removeProperty(String name) { getSource(0).removeProperty(name); } /** * Returns one of OP_COMPUTE_BOUND, OP_IO_BOUND, or * OP_NETWORK_BOUND to indicate how the operation is likely to * spend its time. The answer does not affect the output of the * operation, but may allow a scheduler to parallelize the * computation of multiple operations more effectively. The * default implementation returns OP_COMPUTE_BOUND. */ public int getOperationComputeType() { return computeType; } } jai-core-1.1.4/src/share/classes/javax/media/jai/ProductOperationGraph.java0000644000175000017500000000273210203035544026467 0ustar mathieumathieu/* * $RCSfile: ProductOperationGraph.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:16 $ * $State: Exp $ */ package javax.media.jai; import java.util.Enumeration; import java.util.Vector; /** * ProductOperationGraph manages a list of descriptors belonging to a * particular product. The descriptors have pairwise preferences between * them. * * This class extends "OperationGraph" which provide the * other operations such as remove, lookup, set/unset preference etc. * *

      This class is used by the implementation of the OperationRegistry * class and is not intended to be part of the API. * * @see OperationGraph * * - Moved most of the functionality to "OperationGraph" * which has been generalized to maintain product as well * as factory tree */ final class ProductOperationGraph extends OperationGraph implements java.io.Serializable { /** Constructs an ProductOperationGraph. */ ProductOperationGraph() { // Use the name of the PartialOrderNode for comparisions super(true); } /** * Adds a product to an ProductOperationGraph. A new * PartialOrderNode is constructed to hold the product * and its graph adjacency information. */ void addProduct(String productName) { addOp(new PartialOrderNode(new OperationGraph(), productName)); } } jai-core-1.1.4/src/share/classes/javax/media/jai/CollectionImageFactory.java0000644000175000017500000000543510203035544026575 0ustar mathieumathieu/* * $RCSfile: CollectionImageFactory.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:06 $ * $State: Exp $ */ package javax.media.jai; import java.awt.RenderingHints; import java.awt.image.renderable.ParameterBlock; /** * The CollectionImageFactory (CIF) interface is intended * to be implemented by classes that wish to act as factories to produce * different collection image operators. In JAI, the create() * method will be invoked in a chain of CollectionOps when the * operation is being executed in rendered mode. */ public interface CollectionImageFactory { /** * Creates a CollectionImage that represents the * result of an operation (or chain of operations) for a given * ParameterBlock and RenderingHints. * If the operation is unable to handle the input arguments, this * method should return null. * *

      Generally this method is expected to be invoked by an operation * being executed in rendered mode. * * @param args Input arguments to the operation, including * sources and/or parameters. * @param hints The rendering hints. * * @return A CollectionImage containing the desired output. */ CollectionImage create(ParameterBlock args, RenderingHints hints); /** * Attempts to modify a rendered CollectionImage previously * created by this CollectionImageFactory as a function * of how the sources, parameters and hints of the operation have * changed. The CollectionImage passed in should not be * modified in place but some or or all of its contents may be copied * by reference into the CollectionImage returned, if any. * If none of the contents of the old CollectionImage can * be re-used, then null should be returned. * * @throws IllegalArgumentException if the name of the operation * associated with the CollectionOp does not * match that expected by this CollectionImageFactory. * * @return A CollectionImage modified according to the * new values of the ParameterBlock and * RenderingHints or null if it * is impracticable to perform the update. * * @since JAI 1.1 */ CollectionImage update(ParameterBlock oldParamBlock, RenderingHints oldHints, ParameterBlock newParamBlock, RenderingHints newHints, CollectionImage oldRendering, CollectionOp op); } jai-core-1.1.4/src/share/classes/javax/media/jai/WarpGrid.java0000644000175000017500000004343710203035544023732 0ustar mathieumathieu/* * $RCSfile: WarpGrid.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:24 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Rectangle; import java.awt.geom.Point2D; /** * A regular grid-based description of an image warp. * *

      The mapping from destination pixels to source positions is * described by bilinear interpolation within a rectilinear grid of * points with known mappings. * *

      Given a destination pixel coordinate (x, y) that lies within * a cell having corners at (x0, y0), (x1, y0), (x0, y1) and (x1, y1), * with source coordinates defined at each respective corner equal * to (sx0, sy0), (sx1, sy1), (sx2, sy2) and (sx3, sy3), the * source position (sx, sy) that maps onto (x, y) is given by the formulas: * *

       * xfrac = (x - x0)/(x1 - x0)
       * yfrac = (y - y0)/(y1 - y0)
       *
       * s = sx0 + (sx1 - sx0)*xfrac
       * t = sy0 + (sy1 - sy0)*xfrac
       *
       * u = sx2 + (sx3 - sx2)*xfrac
       * v = sy2 + (sy3 - sy2)*xfrac
       *
       * sx = s + (u - s)*yfrac
       * sy = t + (v - t)*yfrac
       * 
      * *

      In other words, the source x and y values are interpolated * horizontally along the top and bottom edges of the grid cell, * and the results are interpolated vertically: * *

       * (x0, y0) ->            (x1, y0) ->
       *   (sx0, sy0)             (sx1, sy1)
       *    +------------+---------+
       *    |            |\        |
       *    |            | (s, t)  |
       *    |            |         |
       *    |            |         |
       *    |            |         |
       *    |            |         |
       *    | (x, y) ->  |         |
       *    |  (sx, sy)--+         |
       *    |            |         |
       *    |            |         |
       *    |            | (u, v)  |
       *    |            |/        |
       *    +------------+---------+
       * (x0, y1) ->          (x1, y1) ->
       *   (sx2, sy2)           (sx3, sy3)
       * 
      * *

      Points outside the bounds of the cells defining the grid warp will * be mapped to the source image using the identity transformation. * *

      WarpGrid is marked final so that it may be more easily inlined. * */ public final class WarpGrid extends Warp { private int xStart; private int yStart; private int xEnd; private int yEnd; private int xStep; private int yStep; private int xNumCells; private int yNumCells; private float[] xWarpPos; private float[] yWarpPos; /** * @param xStart * @param xStep * @param xNumCells * @param yStart * @param yStep * @param yNumCells * @param warpPositions */ private void initialize(int xStart, int xStep, int xNumCells, int yStart, int yStep, int yNumCells, float[] warpPositions) { this.xStart = xStart; this.yStart = yStart; this.xEnd = xStart + xStep * xNumCells; this.yEnd = yStart + yStep * yNumCells; this.xStep = xStep; this.yStep = yStep; this.xNumCells = xNumCells; this.yNumCells = yNumCells; int xNumGrids = xNumCells + 1; int yNumGrids = yNumCells + 1; int numNodes = yNumGrids*xNumGrids; xWarpPos = new float[numNodes]; yWarpPos = new float[numNodes]; int index = 0; for (int idx = 0; idx < numNodes; idx++) { xWarpPos[idx] = warpPositions[index++]; yWarpPos[idx] = warpPositions[index++]; } } /** * Constructs a WarpGrid with a given grid-based transform mapping * destination pixels into source space. Note that this is * a backward mapping as opposed to the forward mapping used in * AffineOpImage. * *

      The grid is defined by a set of equal-sized cells. * The grid starts at (xStart, yStart). Each cell has width * equal to xStep and height equal to yStep, and there are * xNumCells cells horizontally and yNumCells cells vertically. * *

      The local mapping within each cell is defined by * the values in the table parameter. This parameter must * contain 2*(xNumCells + 1)*(yNumCells + 1) values, which * alternately contain the source X and Y coordinates to which * each destination grid intersection point maps. * The cells are enumerated in row-major order, that is, * all the grid points along a row are enumerated first, then * the grid points for the next row are enumerated, and so on. * *

      As an example, suppose xNumCells is equal to 2 and * yNumCells is equal 1. Then the order of the data in table * would be: * *

           * x00, y00, x10, y10, x20, y20, x01, y01, x11, y11, x21, y21
           * 
      * * for a total of 2*(2 + 1)*(1 + 1) = 12 elements. * * @param xStart the minimum X coordinate of the grid. * @param xStep the horizontal spacing between grid cells. * @param xNumCells the number of grid cell columns. * @param yStart the minimum Y coordinate of the grid. * @param yStep the vertical spacing between grid cells. * @param yNumCells the number of grid cell rows. * @param warpPositions a float array of length 2*(xNumCells + 1)* * (yNumCells + 1) containing the warp positions at the * grid points, in row-major order. * @throws IllegalArgumentException if the length of warpPositions is incorrect */ public WarpGrid(int xStart, int xStep, int xNumCells, int yStart, int yStep, int yNumCells, float[] warpPositions) { if (warpPositions.length != 2 * (xNumCells + 1) * (yNumCells + 1)) { throw new IllegalArgumentException(JaiI18N.getString("WarpGrid0")); } initialize(xStart, xStep, xNumCells, yStart, yStep, yNumCells, warpPositions); } /** * Constructs a WarpGrid object by sampling the displacements * given by another Warp object of any kind. * *

      The grid is defined by a set of equal-sized cells. * The grid starts at (xStart, yStart). Each cell has width * equal to xStep and height equal to yStep, and there are * xNumCells cells horizontally and yNumCells cells vertically. * * @param master the Warp object used to initialize the grid * displacements. * @param xStart the minimum X coordinate of the grid. * @param xStep the horizontal spacing between grid cells. * @param xNumCells the number of grid cell columns. * @param yStart the minimum Y coordinate of the grid. * @param yStep the vertical spacing between grid cells. * @param yNumCells the number of grid cell rows. */ public WarpGrid(Warp master, int xStart, int xStep, int xNumCells, int yStart, int yStep, int yNumCells) { int size = 2 * (xNumCells + 1) * (yNumCells + 1); float[] warpPositions = new float[size]; warpPositions = master.warpSparseRect(xStart, yStart, xNumCells * xStep + 1, // width yNumCells * yStep + 1, // height xStep, yStep, warpPositions); initialize(xStart, xStep, xNumCells, yStart, yStep, yNumCells, warpPositions); } /** Returns the minimum X coordinate of the grid. */ public int getXStart() { return xStart; } /** Returns the minimum Y coordinate of the grid. */ public int getYStart() { return yStart; } /** Returns the horizontal spacing between grid cells. */ public int getXStep() { return xStep; } /** Returns the vertical spacing between grid cells. */ public int getYStep() { return yStep; } /** Returns the number of grid cell columns. */ public int getXNumCells() { return xNumCells; } /** Returns the number of grid cell rows. */ public int getYNumCells() { return yNumCells; } /** Returns the horizontal warp positions at the grid points. */ public float[] getXWarpPos() { return xWarpPos; } /** Returns the vertical warp positions at the grid points. */ public float[] getYWarpPos() { return yWarpPos; } /** * Copies source to destination, no warpping. * * @param x1 * @param x2 * @param y1 * @param y2 * @param periodX * @param periodY * @param offset * @param stride * @param destRect * @return An array of floats. * @throws IllegalArgumentException if destRect is null * @throws ArrayBoundsException if destRect is too small */ private float[] noWarpSparseRect(int x1, int x2, int y1, int y2, int periodX, int periodY, int offset, int stride, float[] destRect) { if ( destRect == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } for (int j = y1; j <= y2; j += periodY) { int index = offset; offset += stride; for (int i = x1; i <= x2; i += periodX) { destRect[index++] = i; destRect[index++] = j; } } return destRect; } /** * Computes the source subpixel positions for a given rectangular * destination region, subsampled with an integral period. * *

      Points outside the bounds of the cells defining the grid warp will * be mapped to the source image using the identity transformation. * @param x The minimum X coordinate of the destination region. * @param y The minimum Y coordinate of the destination region. * @param width The width of the destination region. * @param height The height of the destination region. * @param periodX The horizontal sampling period. * @param periodY The vertical sampling period. * @param destRect An int array containing at least * 2*((width+periodX-1)/periodX)*((height+periodY-1)/periodY) * elements, or null. If null, a * new array will be constructed. * * @return a reference to the destRect parameter if it is * non-null, or a new int array of length * 2*width*height otherwise. * @throws ArrayBoundsException if destRect is too small */ public float[] warpSparseRect(int x, int y, int width, int height, int periodX, int periodY, float[] destRect) { // Number of points (x, y) per scanline int stride = 2 * ((width + periodX - 1) / periodX); if (destRect == null) { destRect = new float[stride * ((height + periodY - 1) / periodY)]; } int x1 = x; // first x point int x2 = x + width - 1; // last x point int y1 = y; // first y point int y2 = y + height - 1; // last y point if (y1 >= yEnd || y2 < yStart || x1 >= xEnd || x2 < xStart) { // destRect is completely outside of warp grid return noWarpSparseRect(x1, x2, y1, y2, periodX, periodY, 0, stride, destRect); } if (y1 < yStart) { // the rectangle above the warp grid area int periods = (yStart - y1 + periodY - 1) / periodY; noWarpSparseRect(x1, x2, y1, yStart - 1, periodX, periodY, 0, stride, destRect); y1 += periods * periodY; } if (y2 >= yEnd) { // the rectangle below the warp grid area int periods = (yEnd - y + periodY - 1) / periodY; noWarpSparseRect(x1, x2, y + periods * periodY, y2, periodX, periodY, periods * stride, stride, destRect); // One period up should be inside warp grid y2 = y + (periods - 1) * periodY; } if (x1 < xStart) { // the rectangle left of the warp grid area int periods = (xStart - x1 + periodX - 1) / periodX; noWarpSparseRect(x1, xStart - 1, y1, y2, periodX, periodY, (y1 - y) / periodY * stride, stride, destRect); x1 += periods * periodX; } if (x2 >= xEnd) { // the rectangle right of the warp grid area int periods = (xEnd - x + periodX - 1) / periodX; noWarpSparseRect(x + periods * periodX, x2, y1, y2, periodX, periodY, (y1 - y) / periodY * stride + periods * 2, stride, destRect); // One period left should be inside warp grid x2 = x + (periods - 1) * periodX; } // // Now the rectangle is within warp grid, that is // xStart <= x1 <= x2 < xEnd and yStart <= y1 <= y2 < yEnd. // // address = s0(1-x)(1-y) + s1x(1-y) + s2(1-x)y + s3xy // // A table stores the number of points inside each cell int[] cellPoints = new int[xNumCells]; for (int i = x1; i <= x2; i += periodX) { cellPoints[(i - xStart) / xStep]++; } int offset = (y1 - y) / periodY * stride + (x1 - x) / periodX * 2; // Store the number of horizontal grid nodes. int xNumGrids = xNumCells + 1; // Fractional step in X. float deltaX = (float)periodX/(float)xStep; // The rectangle within the warp grid for (int j = y1; j <= y2; j += periodY) { int index = offset; offset += stride; int yCell = (j - yStart) / yStep; int yGrid = yStart + yCell * yStep; float yFrac = (float)(j + 0.5F - yGrid) / (float)yStep; // Cache some values to avoid two multiplications per x loop. float deltaTop = (1.0F - yFrac)*deltaX; float deltaBottom = yFrac*deltaX; int i = x1; while (i <= x2) { // Entering a new cell, set up int xCell = (i - xStart) / xStep; int xGrid = xStart + xCell * xStep; float xFrac = (float)(i + 0.5F - xGrid) / (float)xStep; int nodeOffset = yCell*xNumGrids + xCell; float wx0 = xWarpPos[nodeOffset]; float wy0 = yWarpPos[nodeOffset]; float wx1 = xWarpPos[++nodeOffset]; float wy1 = yWarpPos[nodeOffset]; nodeOffset += xNumCells; // NB: xNumCells == xNumGrids - 1 float wx2 = xWarpPos[nodeOffset]; float wy2 = yWarpPos[nodeOffset]; float wx3 = xWarpPos[++nodeOffset]; float wy3 = yWarpPos[nodeOffset]; float s = wx0 + (wx1 - wx0) * xFrac; float t = wy0 + (wy1 - wy0) * xFrac; float u = wx2 + (wx3 - wx2) * xFrac; float v = wy2 + (wy3 - wy2) * xFrac; float wx = s + (u - s) * yFrac; float wy = t + (v - t) * yFrac; // Delta in x and y. float dx = (wx1 - wx0)*deltaTop + (wx3 - wx2)*deltaBottom; float dy = (wy1 - wy0)*deltaTop + (wy3 - wy2)*deltaBottom; // The points inside the current cell int nPoints = cellPoints[xCell]; for (int k = 0; k < nPoints; k++) { destRect[index++] = wx - 0.5F; destRect[index++] = wy - 0.5F; wx += dx; wy += dy; i += periodX; } } } return destRect; } /** * Computes the source point corresponding to the supplied point. * *

      This method returns the value of pt in the following * code snippet: * *

           * float[] sxy = warpSparseRect((int)destPt.getX(), (int)destPt.getY(),
           *                              2, 2, 1, 1, null);
           *
           * double wtRight  = destPt.getX() - (int)destPt.getX();
           * double wtLeft   = 1.0 - wtRight;
           * double wtBottom = destPt.getY() - (int)destPt.getY();
           * double wtTop    = 1.0 - wtBottom;
           *
           * Point2D pt = (Point2D)destPt.clone();
           * pt.setLocation((sxy[0]*wtLeft + sxy[2]*wtRight)*wtTop +
           *                (sxy[4]*wtLeft + sxy[6]*wtRight)*wtBottom,
           *                (sxy[1]*wtLeft + sxy[3]*wtRight)*wtTop +
           *                (sxy[5]*wtLeft + sxy[7]*wtRight)*wtBottom);
           * 
      *

      * * @param destPt the position in destination image coordinates * to map to source image coordinates. * * @return a Point2D of the same class as * destPt. * * @throws IllegalArgumentException if destPt is * null. * * @since JAI 1.1.2 */ public Point2D mapDestPoint(Point2D destPt) { if (destPt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } float[] sxy = warpSparseRect((int)destPt.getX(), (int)destPt.getY(), 2, 2, 1, 1, null); double wtRight = destPt.getX() - (int)destPt.getX(); double wtLeft = 1.0 - wtRight; double wtBottom = destPt.getY() - (int)destPt.getY(); double wtTop = 1.0 - wtBottom; Point2D pt = (Point2D)destPt.clone(); pt.setLocation((sxy[0]*wtLeft + sxy[2]*wtRight)*wtTop + (sxy[4]*wtLeft + sxy[6]*wtRight)*wtBottom, (sxy[1]*wtLeft + sxy[3]*wtRight)*wtTop + (sxy[5]*wtLeft + sxy[7]*wtRight)*wtBottom); return pt; } } jai-core-1.1.4/src/share/classes/javax/media/jai/OperationDescriptor.java0000644000175000017500000005755210203035544026215 0ustar mathieumathieu/* * $RCSfile: OperationDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:12 $ * $State: Exp $ */ package javax.media.jai; import java.awt.RenderingHints; import java.awt.image.renderable.ParameterBlock; import java.util.Locale; import java.util.ResourceBundle; /** * This interface provides a comprehensive description of a specific * image operation. All information regarding the operation, such as * its name, version, input, and properties should be listed. Any * conditions placed on the operation, such as its source class types and * legal parameter range, should also be included, and the methods to * enforce these conditions should be implemented. A set of * PropertyGenerators may be specified to be used as a * basis for the operation's property management. * *

      Each image operation in JAI must have a descriptor * that implements this interface. The following basic resource data * must be provided: *

        *
      • A global operation name that is visible to all and is the same * in all Locales.
      • *
      • A localized operation name that may be used as a synonym for * the global operation name.
      • *
      • The name of the vendor defining this operation.
      • *
      • A brief description of this operation.
      • *
      • An URL where additional documentation on this operation may be * found.
      • *
      • The version of this operation.
      • *
      * Additional information must be provided when appropriate. Only then * can this operation be added to an OperationRegistry. * Furthermore, it is recommended that a detailed description of the * operation's functionality be included in the class comments. * *

      JAI currently knows about the following operation modes : * "rendered", "renderable", "collection" and "renderableCollection" * (these form a subset of the known registry modes returned by * RegistryMode.getModes()). All mode names are dealt * with in a case insensitive (but retentive) manner. All modes have * to accept the same number of source images and the same number of * parameters. All the source names and parameter names are also the * same across all modes. The class types of the sources and parameters * can be different for each mode. * *

      For example an operation supporting the "rendered" mode * takes RenderedImages as its sources, can only * be used in a rendered operation chain, and produces a * RenderedImage. An operation supporting the renderable * mode takes RenderableImages as its sources, can * only be used in a renderable operation chain, and produces a * RenderableImage. * * @see JAI * @see OperationDescriptorImpl * */ public interface OperationDescriptor extends RegistryElementDescriptor { /** * An Object that signifies that * a parameter has no default value. Same as * ParameterListDescriptor.NO_PARAMETER_DEFAULT */ public static final Object NO_PARAMETER_DEFAULT = ParameterListDescriptor.NO_PARAMETER_DEFAULT; /** * Returns the resource data for this operation in the specified * Locale. It must contain String data * for the following tags: *

        *
      • "GlobalName" - A global operation name that is visible to all * and is the same in all Locales.
      • *
      • "LocalName" - A localized operation name that may be used as a * synonym for the "GlobalName".
      • *
      • "Vendor" - The name of the vendor defining this operation. * Vendors are encouraged to use the Java convention * of reversed Internet addresses.
      • *
      • "Description" - A brief description of this operation.
      • *
      • "DocURL" - An URL where additional documentation on this * operation may be found.
      • *
      • "Version" - A free-form version indicator of this operation.
      • *
      * In addition, it may contain String data for the * following tags when appropriate: *
        *
      • "arg0Desc", "arg1Desc", ... - Description of the input * parameters.
      • *
      • "hint0Desc", hint1Desc", ... - Description of the rendering * hints.
      • *
      * * @param locale The Locale for which the information * should be localized. It may be different from the default * Locale. * * @return A two-dimensional array of Strings containing * the mandatory and optional resource tags and their * corresponding resource data. (String[i][0] is * the tag for the i-th resource and String[i][1] is the * corresponding data) */ String[][] getResources(Locale locale); /** * Returns the resource data for this operation in the specified * Locale in a ResourceBundle. The * resource data values are taken from the * getResources() method which must be implemented * by each operation descriptor. * * @param locale The Locale for which the information * should be localized. It may be different from the default * Locale. * * @return A ResourceBundle containing the mandatory * and optional resource information. */ ResourceBundle getResourceBundle(Locale locale); /** * Returns the number of sources required by this operation. * All modes have the same number of sources. */ int getNumSources(); /** * Returns an array of Classes that describe the types * of sources required by this operation for the specified mode. * If this operation has no sources, this method returns null. * * @param modeName the operation mode name * * @throws IllegalArgumentException if modeName is null * or if it is not one of the supported modes. * * @since JAI 1.1 */ Class[] getSourceClasses(String modeName); /** * Returns an array of Strings that are the names * of the sources of this operation. If this operation has no * sources, this method returns null. * * @since JAI 1.1 */ String[] getSourceNames(); /** * Returns a Class that describes the type of * destination this operation produces for the specified mode. * * @param modeName the operation mode name * * @throws IllegalArgumentException if modeName is null * or if it is not one of the supported modes. * * @since JAI 1.1 */ Class getDestClass(String modeName); /** * Returns true if this operation/mode is capable of * handling the input source(s) and/or parameter(s) * specified in the ParameterBlock, or * false otherwise, in which case an explanatory * message may be appended to the StringBuffer. * *

      This method is the standard place where input arguments are * validated against this operation's specification for the specified * mode. It is called by JAI.create() as a part of its * validation process. Thus it is strongly recommended that the * application programs use the JAI.create() methods to * instantiate all the rendered operations. * *

      This method sets all the undefined parameters in the * ParameterBlock to their default values, if the default * values are specified. * *

      Note that DeferredData parameters will not be * recognized as valid unless the parameter is defined to have class * DeferredData.class. * * @param modeName the operation mode name * @param args Input arguments, including source(s) and/or parameter(s). * @param msg A string that may contain error messages. * * @throws IllegalArgumentException if modeName is null * * @since JAI 1.1 */ boolean validateArguments(String modeName, ParameterBlock args, StringBuffer msg); /** * Returns true if the operation should be computed * immediately for all supported modes of this operation during * the call to JAI.create(); that is, the operation * is placed in immediate mode. If true, and * the computation fails, null will be returned * from JAI.create(). If false, * JAI.create() will return an instance of the * appropriate destination class that may be asked to compute itself * at a later time; this computation may fail at that time. * *

      Operations that rely on an external resource, such as * a source file, or that produce externally-visible side * effects, such as writing to an output file, should return * true from this method. Operations that rely * only on their sources and parameters usually wish to return * false in order to defer rendering as long as * possible. */ boolean isImmediate(); /************************ Generic Methods ************************/ /** * Calculates the region over which two distinct renderings * of an operation may be expected to differ. * *

      The class of the returned object will vary as a function of * the mode of the operation. For rendered and renderable two- * dimensional images this should be an instance of a class which * implements java.awt.Shape. * * @param registryModeName The name of the mode. * @param oldParamBlock The previous sources and parameters. * @param oldHints The previous hints. * @param newParamBlock The current sources and parameters. * @param newHints The current hints. * @param node The affected node in the processing chain. * * @return The region over which the data of two renderings of this * operation may be expected to be invalid or null * if there is no common region of validity. If an empty * java.awt.Shape is returned, this indicates * that all pixels within the bounds of the old rendering * remain valid. * * @throws IllegalArgumentException if registryModeName * is null or if the operation requires either * sources or parameters and either oldParamBlock * or newParamBlock is null. * @throws IllegalArgumentException if oldParamBlock or * newParamBlock do not contain sufficient sources * or parameters for the operation in question. * * @since JAI 1.1 */ Object getInvalidRegion(String registryModeName, ParameterBlock oldParamBlock, RenderingHints oldHints, ParameterBlock newParamBlock, RenderingHints newHints, OperationNode node); /********************** DEPRECATED METHODS *************************/ // All mode specific methods are deprecated since JAI 1.1 // in favor of the equivalent methods which accept a modeName // as a parameter. /** * Returns an array of PropertyGenerators implementing * the property inheritance for this operation. They may be used * as a basis for the operation's property management. * * @return An array of PropertyGenerators, or * null if this operation does not have any of * its own PropertyGenerators. * * @deprecated as of JAI 1.1 in favor of the equivalent method * that specifies the mode name. */ PropertyGenerator[] getPropertyGenerators(); /********************** Rendered Mode Methods (deprecated) *********/ /** * Returns true if this operation supports the rendered * image mode. That is, it may be performed on RenderedImage * sources in a rendered operation chain, and produces a rendered result. * The JAI.create() and the * JAI.createCollection() methods should be used to * instantiate the operation. * *

      If this method returns true, all the additional * methods that supply the rendered mode information must be * implemented. * * @deprecated as of JAI 1.1 in favor of isModeSupported("rendered") */ boolean isRenderedSupported(); /** * Returns an array of Classes that describe the types * of sources required by this operation in the rendered image mode. * If this operation has no source, this method returns null. * * @deprecated as of JAI 1.1 in favor of getSourceClasses("rendered") */ Class[] getSourceClasses(); /** * Returns a Class that describes the type of * destination this operation produces in the rendered image * mode. Currently JAI supports two destination class types: * java.awt.image.RenderedImage.class and * java.util.Collection.class. * * @deprecated as of JAI 1.1 in favor of getDestClass("rendered") */ Class getDestClass(); /** * Returns true if this operation is capable of * handling the input rendered source(s) and/or parameter(s) * specified in the ParameterBlock, or * false otherwise, in which case an explanatory * message may be appended to the StringBuffer. * *

      This method is the standard place where input arguments are * validated against this operation's specification for the rendered * mode. It is called by JAI.create() as a part of its * validation process. Thus it is strongly recommended that the * application programs use the JAI.create() methods to * instantiate all the rendered operations. * *

      This method sets all the undefined parameters in the * ParameterBlock to their default values, if the default * values are specified. * *

      Note that DeferredData parameters will not be * recognized as valid unless the parameter is defined to have class * DeferredData.class. * * @param args Input arguments, including source(s) and/or parameter(s). * @param msg A string that may contain error messages. * * @deprecated as of JAI 1.1 in favor of validateArguments("rendered", ...) */ boolean validateArguments(ParameterBlock args, StringBuffer msg); /********************* Renderable Mode Methods (deprecated) ********/ /** * Returns true if this operation supports the renderable * image mode. That is, it may be performed on RenderableImage * sources in a renderable operation chain, and produces a renderable * result. The JAI.createRenderable() and the * JAI.createCollection() methods should be used to * instantiate the operation. * *

      If this method returns true, all the additional * methods that supply the renderable mode information must be * implemented. * * @deprecated as of JAI 1.1 in favor of isModeSupported("renderable") */ boolean isRenderableSupported(); /** * Returns an array of Classes that describe the types * of sources required by this operation in the renderable image mode. * If this operation does not support the renderable mode, or if it * has no source, this method returns null. * * @deprecated as of JAI 1.1 in favor of getSourceClasses("renderable") */ Class[] getRenderableSourceClasses(); /** * Returns a Class that describes the type of * destination this operation produces in the renderable image * mode. Currently JAI supports two destination class types: * java.awt.image.renderable.RenderableImage.class and * java.util.Collection.class. * * @deprecated as of JAI 1.1 in favor of getDestClass("renderable") */ Class getRenderableDestClass(); /** * Returns true if this operation is capable of handling * the input renderable source(s) and/or parameter(s) specified * in the ParameterBlock, or false * otherwise, in which case an explanatory message may be appended * to the StringBuffer. * *

      This method is the standard place where input arguments are * validated against this operation's specification for the renderable * mode. It is called by JAI.createRenderable() as a * part of its validation process. Thus it is strongly recommended * that the application programs use the * JAI.createRenderable() method to instantiate all * the renderable operations. * *

      This method sets all the undefined parameters in the * ParameterBlock to their default values, if the default * values are specified. * *

      Note that DeferredData parameters will not be * recognized as valid unless the parameter is defined to have class * DeferredData.class. * *

      If this operation does not support the renderable mode, * this method returns false regardless of the input * arguments * * @param args Input arguments, including source(s) and/or parameter(s). * @param msg A string that may contain error messages. * * @deprecated as of JAI 1.1 in favor of validateArguments("renderable", ...) */ boolean validateRenderableArguments(ParameterBlock args, StringBuffer msg); /************************ Parameter Methods (deprecated) ***********/ /** * Returns the number of parameters (not including the sources) * required by this operation. * * @deprecated as of JAI 1.1 in favor of * getParameterListDescriptor(modeName).getNumParameters() * This will for the time being return the above value for * modeName = getSupportedModes()[0] */ int getNumParameters(); /** * Returns an array of Classes that describe the types * of parameters required by this operation. If this operation * has no parameter, this method returns null. * * @deprecated as of JAI 1.1 in favor of * getParameterListDescriptor(modeName).getParamClasses() * This will for the time being return the above value for * modeName = getSupportedModes()[0] */ Class[] getParamClasses(); /** * Returns an array of Strings that are the localized * parameter names of this operation. If this operation has no * parameter, this method returns null. * * @deprecated as of JAI 1.1 in favor of * getParameterListDescriptor(modeName).getParamNames() * This will for the time being return the above value for * modeName = getSupportedModes()[0] */ String[] getParamNames(); /** * Returns an array of Objects that define the default * values of the parameters for this operation. Default values may * be null. When instantiating the operation, the * default values may be used for those parameters whose values are * not supplied. The NO_PARAMETER_DEFAULT static * Object indicates that a parameter has no default * value. If this operation has no parameter, this method returns * null. * * @deprecated as of JAI 1.1 in favor of * getParameterListDescriptor(modeName).getParamDefaults() * This will for the time being return the above value for * modeName = getSupportedModes()[0] */ Object[] getParamDefaults(); /** * Returns the default value of a specified parameter. The default * value may be null. If a parameter has no default * value, this method returns NO_PARAMETER_DEFAULT. * * @param index The index of the parameter whose default * value is queried. * * @throws NullPointerException if this operation has no parameter. * @throws ArrayIndexOutOfBoundsException if there is no parameter * corresponding to the specified index. * * @deprecated as of JAI 1.1 in favor of * getParameterListDescriptor(modeName).getParamDefaultValue() * This will for the time being return the above value for * modeName = getSupportedModes()[0] */ Object getParamDefaultValue(int index); /** * Returns the minimum legal value of a specified numeric parameter * for this operation. If the specified parameter is non-numeric, * this method returns null. * *

      The return value should be of the class type appropriate for * the parameter's type, that is, Byte for a * byte parameter, Integer for an * int parameter, and so forth. * * @param index The index of the numeric parameter whose minimum * value is queried. * * @return A Number representing the minimum legal value * of the queried parameter, or null. * * @throws NullPointerException if this operation has no parameter. * @throws ArrayIndexOutOfBoundsException if there is no parameter * corresponding to the specified index. * * @deprecated as of JAI 1.1 in favor of * getParameterListDescriptor(modeName).getParamValueRange() * This will for the time being return "getMinValue" of the above * return value for modeName = getSupportedModes()[0] */ Number getParamMinValue(int index); /** * Returns the maximum legal value of a specified numeric parameter * for this operation. If the specified parameter is non-numeric, * this method returns null. * *

      The return value should be of the class type appropriate for * the parameter's type, that is, Byte for a * byte parameter, Integer for an * int parameter, and so forth. * * @param index The index of the numeric parameter whose maximum * value is queried. * * @return A Number representing the maximum legal value * of the queried parameter, or null. * * @throws NullPointerException if this operation has no parameter. * @throws ArrayIndexOutOfBoundsException if there is no parameter * corresponding to the specified index. * * @deprecated as of JAI 1.1 in favor of * getParameterListDescriptor(modeName).getParamValueRange() * This will for the time being return "getMaxValue" of the above * return value for modeName = getSupportedModes()[0] */ Number getParamMaxValue(int index); } jai-core-1.1.4/src/share/classes/javax/media/jai/registryFile.jai0000644000175000017500000007656610305430255024517 0ustar mathieumathieu# # $RCSfile: registryFile.jai,v $ # # Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. # # Use is subject to license terms. # # $Revision: 1.2 $ # $Date: 2005-08-31 22:35:25 $ # $State: Exp $ # # The master JAI registry initialization file # ############################################################################ # # Each line can be in one of the formats described below. Space or tab # characters separate keywords in each line. The comment character is # '#'; on each line all characters following the first comment character # are ignored. The file must be encoded in UTF-8. # # 1. To register descriptors : # # descriptor # odesc # # The second version above is deprecated and is retained for backward # compatibility with JAI 1.0.2. Descriptors are always registered # against .getName(). The in the # second version is always ignored. # # 2. To register factory objects under a product against a specific mode : # # # # # The first version above is used to register factory objects against # modes that support preferences. The second version is used for those # that do not support preferences. is an arbitrary name that # is unique for a given mode. This is (only) used later on in this file # to set preferences between factory objects. # # 3. To set preferences between products for a descriptor under a # specific mode : # # prefProduct # pref product # # The second version above is deprecated and is retained for backward # compatibility with JAI 1.0.2. This version is assumed to set # product preferences for the "rendered" mode. # # 4. To set preferences between factory objects for descriptor under a # a specific product and registry mode : # # pref # ############################################################################ # # Image operation descriptors : # descriptor javax.media.jai.operator.AbsoluteDescriptor descriptor javax.media.jai.operator.AddCollectionDescriptor descriptor javax.media.jai.operator.AddConstDescriptor descriptor javax.media.jai.operator.AddConstToCollectionDescriptor descriptor javax.media.jai.operator.AddDescriptor descriptor javax.media.jai.operator.AffineDescriptor descriptor javax.media.jai.operator.AndConstDescriptor descriptor javax.media.jai.operator.AndDescriptor descriptor javax.media.jai.operator.AWTImageDescriptor descriptor javax.media.jai.operator.BandCombineDescriptor descriptor javax.media.jai.operator.BandMergeDescriptor descriptor javax.media.jai.operator.BandSelectDescriptor descriptor javax.media.jai.operator.BinarizeDescriptor descriptor javax.media.jai.operator.BMPDescriptor descriptor javax.media.jai.operator.BorderDescriptor descriptor javax.media.jai.operator.BoxFilterDescriptor descriptor javax.media.jai.operator.ClampDescriptor descriptor javax.media.jai.operator.ColorConvertDescriptor descriptor javax.media.jai.operator.ColorQuantizerDescriptor descriptor javax.media.jai.operator.ConstantDescriptor descriptor javax.media.jai.operator.CompositeDescriptor descriptor javax.media.jai.operator.ConjugateDescriptor descriptor javax.media.jai.operator.ConvolveDescriptor descriptor javax.media.jai.operator.CropDescriptor descriptor javax.media.jai.operator.DCTDescriptor descriptor javax.media.jai.operator.DFTDescriptor descriptor javax.media.jai.operator.DilateDescriptor descriptor javax.media.jai.operator.DivideDescriptor descriptor javax.media.jai.operator.DivideComplexDescriptor descriptor javax.media.jai.operator.DivideByConstDescriptor descriptor javax.media.jai.operator.DivideIntoConstDescriptor descriptor javax.media.jai.operator.ErodeDescriptor descriptor javax.media.jai.operator.ErrorDiffusionDescriptor descriptor javax.media.jai.operator.EncodeDescriptor descriptor javax.media.jai.operator.ExpDescriptor descriptor javax.media.jai.operator.ExtremaDescriptor descriptor javax.media.jai.operator.FileLoadDescriptor descriptor javax.media.jai.operator.FileStoreDescriptor descriptor javax.media.jai.operator.FilteredSubsampleDescriptor descriptor javax.media.jai.operator.FormatDescriptor descriptor javax.media.jai.operator.FPXDescriptor descriptor javax.media.jai.operator.GIFDescriptor descriptor javax.media.jai.operator.GradientMagnitudeDescriptor descriptor javax.media.jai.operator.HistogramDescriptor descriptor javax.media.jai.operator.IDCTDescriptor descriptor javax.media.jai.operator.IDFTDescriptor descriptor javax.media.jai.operator.IIPDescriptor descriptor javax.media.jai.operator.IIPResolutionDescriptor descriptor javax.media.jai.operator.ImageFunctionDescriptor descriptor javax.media.jai.operator.InvertDescriptor descriptor javax.media.jai.operator.JPEGDescriptor descriptor javax.media.jai.operator.LogDescriptor descriptor javax.media.jai.operator.LookupDescriptor descriptor javax.media.jai.operator.MagnitudeDescriptor descriptor javax.media.jai.operator.MagnitudeSquaredDescriptor descriptor javax.media.jai.operator.MaxDescriptor descriptor javax.media.jai.operator.MaxFilterDescriptor descriptor javax.media.jai.operator.MatchCDFDescriptor descriptor javax.media.jai.operator.MeanDescriptor descriptor javax.media.jai.operator.MedianFilterDescriptor descriptor javax.media.jai.operator.MinDescriptor descriptor javax.media.jai.operator.MinFilterDescriptor descriptor javax.media.jai.operator.MosaicDescriptor descriptor javax.media.jai.operator.MultiplyConstDescriptor descriptor javax.media.jai.operator.MultiplyComplexDescriptor descriptor javax.media.jai.operator.MultiplyDescriptor descriptor javax.media.jai.operator.NotDescriptor descriptor javax.media.jai.operator.NullDescriptor descriptor javax.media.jai.operator.OrConstDescriptor descriptor javax.media.jai.operator.OrDescriptor descriptor javax.media.jai.operator.OrderedDitherDescriptor descriptor javax.media.jai.operator.OverlayDescriptor descriptor javax.media.jai.operator.PatternDescriptor descriptor javax.media.jai.operator.PeriodicShiftDescriptor descriptor javax.media.jai.operator.PhaseDescriptor descriptor javax.media.jai.operator.PiecewiseDescriptor descriptor javax.media.jai.operator.PNGDescriptor descriptor javax.media.jai.operator.PNMDescriptor descriptor javax.media.jai.operator.PolarToComplexDescriptor descriptor javax.media.jai.operator.RenderableDescriptor descriptor javax.media.jai.operator.RescaleDescriptor descriptor javax.media.jai.operator.RotateDescriptor descriptor javax.media.jai.operator.ScaleDescriptor descriptor javax.media.jai.operator.ShearDescriptor descriptor javax.media.jai.operator.StreamDescriptor descriptor javax.media.jai.operator.SubsampleAverageDescriptor descriptor javax.media.jai.operator.SubsampleBinaryToGrayDescriptor descriptor javax.media.jai.operator.SubtractDescriptor descriptor javax.media.jai.operator.SubtractConstDescriptor descriptor javax.media.jai.operator.SubtractFromConstDescriptor descriptor javax.media.jai.operator.TIFFDescriptor descriptor javax.media.jai.operator.ThresholdDescriptor descriptor javax.media.jai.operator.TranslateDescriptor descriptor javax.media.jai.operator.TransposeDescriptor descriptor javax.media.jai.operator.UnsharpMaskDescriptor descriptor javax.media.jai.operator.URLDescriptor descriptor javax.media.jai.operator.WarpDescriptor descriptor javax.media.jai.operator.XorConstDescriptor descriptor javax.media.jai.operator.XorDescriptor # # tile codec descriptor # descriptor javax.media.jai.tilecodec.GZIPTileCodecDescriptor descriptor javax.media.jai.tilecodec.JPEGTileCodecDescriptor descriptor javax.media.jai.tilecodec.RawTileCodecDescriptor # # remote descriptor # descriptor javax.media.jai.remote.JAIRMIDescriptor # # "rendered" factory objects # rendered com.sun.media.jai.opimage.AbsoluteCRIF com.sun.media.jai absolute sunabsoluterif rendered com.sun.media.jai.mlib.MlibAbsoluteRIF com.sun.media.jai absolute mlibabsoluterif rendered com.sun.media.jai.opimage.AddCollectionCRIF com.sun.media.jai addcollection sunaddcollectionrif rendered com.sun.media.jai.opimage.AddConstCRIF com.sun.media.jai addconst sunaddconstrif rendered com.sun.media.jai.mlib.MlibAddConstRIF com.sun.media.jai addconst mlibaddconstrif rendered com.sun.media.jai.opimage.AddCRIF com.sun.media.jai add sunaddrif rendered com.sun.media.jai.mlib.MlibAddRIF com.sun.media.jai add mlibaddrif rendered com.sun.media.jai.opimage.AffineCRIF com.sun.media.jai affine sunaffinerif rendered com.sun.media.jai.mlib.MlibAffineRIF com.sun.media.jai affine mlibaffinerif rendered com.sun.media.jai.opimage.AndConstCRIF com.sun.media.jai andconst sunandconstrif rendered com.sun.media.jai.mlib.MlibAndConstRIF com.sun.media.jai andconst mlibandconstrif rendered com.sun.media.jai.opimage.AndCRIF com.sun.media.jai and sunandrif rendered com.sun.media.jai.mlib.MlibAndRIF com.sun.media.jai and mlibandrif rendered com.sun.media.jai.opimage.AWTImageRIF com.sun.media.jai awtimage sunawtimagerif rendered com.sun.media.jai.opimage.BandCombineCRIF com.sun.media.jai bandcombine sunbandcombinerif rendered com.sun.media.jai.opimage.BandMergeCRIF com.sun.media.jai bandmerge sunbandmergerif rendered com.sun.media.jai.mlib.MlibBandCombineRIF com.sun.media.jai bandcombine mlibbandcombinerif rendered com.sun.media.jai.opimage.BandSelectCRIF com.sun.media.jai bandselect sunbandselectrif rendered com.sun.media.jai.mlib.MlibBandSelectRIF com.sun.media.jai bandselect mlibbandselectrif rendered com.sun.media.jai.opimage.BinarizeCRIF com.sun.media.jai binarize sunbinarizerif rendered com.sun.media.jai.mlib.MlibBinarizeRIF com.sun.media.jai binarize mlibbinarizerif rendered com.sun.media.jai.opimage.BMPRIF com.sun.media.jai bmp sunbmprif rendered com.sun.media.jai.opimage.BorderRIF com.sun.media.jai border sunborderrif rendered com.sun.media.jai.opimage.BoxFilterRIF com.sun.media.jai boxfilter sunboxfilterrif rendered com.sun.media.jai.mlib.MlibBoxFilterRIF com.sun.media.jai boxfilter mlibboxfilterrif rendered com.sun.media.jai.opimage.ClampCRIF com.sun.media.jai clamp sunclamprif rendered com.sun.media.jai.mlib.MlibClampRIF com.sun.media.jai clamp mlibclamprif rendered com.sun.media.jai.opimage.ColorConvertCRIF com.sun.media.jai colorconvert suncolorconvertrif rendered com.sun.media.jai.opimage.ColorQuantizerRIF com.sun.media.jai colorquantizer suncolorquantizerrif rendered com.sun.media.jai.opimage.CompositeCRIF com.sun.media.jai composite suncompositerif rendered com.sun.media.jai.mlib.MlibCompositeRIF com.sun.media.jai composite mlibcompositerif rendered com.sun.media.jai.opimage.ConstantCRIF com.sun.media.jai constant sunconstrif1 rendered com.sun.media.jai.opimage.ConjugateCRIF com.sun.media.jai conjugate sunconjugaterif rendered com.sun.media.jai.opimage.ConvolveRIF com.sun.media.jai convolve sunconvolverif rendered com.sun.media.jai.mlib.MlibConvolveRIF com.sun.media.jai convolve mlibconvolverif rendered com.sun.media.jai.opimage.CropCRIF com.sun.media.jai crop suncroprif rendered com.sun.media.jai.opimage.DCTCRIF com.sun.media.jai dct sundctrif rendered com.sun.media.jai.mlib.MlibDCTRIF com.sun.media.jai dct mlibdctrif rendered com.sun.media.jai.opimage.DFTCRIF com.sun.media.jai dft sundftrif rendered com.sun.media.jai.mlib.MlibDFTRIF com.sun.media.jai dft mlibdftrif rendered com.sun.media.jai.opimage.DilateRIF com.sun.media.jai dilate sundilaterif rendered com.sun.media.jai.mlib.MlibDilateRIF com.sun.media.jai dilate mlibdilaterif rendered com.sun.media.jai.opimage.DivideCRIF com.sun.media.jai divide sundividerif rendered com.sun.media.jai.opimage.DivideComplexCRIF com.sun.media.jai dividecomplex sundividecomplexrif rendered com.sun.media.jai.mlib.MlibDivideRIF com.sun.media.jai divide mlibdividerif rendered com.sun.media.jai.opimage.DivideByConstCRIF com.sun.media.jai dividebyconst sundividebyconstrif rendered com.sun.media.jai.mlib.MlibDivideByConstRIF com.sun.media.jai dividebyconst mlibdividebyconstrif rendered com.sun.media.jai.opimage.DivideIntoConstCRIF com.sun.media.jai divideintoconst sundivideintoconstrif rendered com.sun.media.jai.mlib.MlibDivideIntoConstRIF com.sun.media.jai divideintoconst mlibdivideintoconstrif rendered com.sun.media.jai.opimage.EncodeRIF com.sun.media.jai encode sunencoderif rendered com.sun.media.jai.opimage.ErodeRIF com.sun.media.jai erode suneroderif rendered com.sun.media.jai.mlib.MlibErodeRIF com.sun.media.jai erode mliberoderif rendered com.sun.media.jai.opimage.ErrorDiffusionRIF com.sun.media.jai errordiffusion sunerrordiffusionrif rendered com.sun.media.jai.opimage.ExpCRIF com.sun.media.jai exp sunexprif rendered com.sun.media.jai.mlib.MlibExpRIF com.sun.media.jai exp mlibexprif rendered com.sun.media.jai.opimage.ExtremaRIF com.sun.media.jai extrema sunextremarif rendered com.sun.media.jai.mlib.MlibExtremaRIF com.sun.media.jai extrema mlibextremarif rendered com.sun.media.jai.opimage.FileLoadRIF com.sun.media.jai fileload sunfileloadrif rendered com.sun.media.jai.opimage.FileStoreRIF com.sun.media.jai filestore sunfilestorerif rendered com.sun.media.jai.opimage.FilteredSubsampleRIF com.sun.media.jai filteredsubsample sunfilteredsubsamplerif rendered com.sun.media.jai.mlib.MlibFilteredSubsampleRIF com.sun.media.jai filteredsubsample mlibfilteredsubsamplerif rendered com.sun.media.jai.opimage.FormatCRIF com.sun.media.jai format sunformatrif rendered com.sun.media.jai.opimage.FPXRIF com.sun.media.jai fpx sunfpxrif rendered com.sun.media.jai.opimage.GIFRIF com.sun.media.jai gif sungifrif rendered com.sun.media.jai.mlib.MlibGradientRIF com.sun.media.jai gradientmagnitude mlibgradientrif rendered com.sun.media.jai.opimage.GradientRIF com.sun.media.jai gradientmagnitude sungradientrif rendered com.sun.media.jai.opimage.HistogramRIF com.sun.media.jai histogram sunhistogramrif rendered com.sun.media.jai.mlib.MlibHistogramRIF com.sun.media.jai histogram mlibhistogramrif rendered com.sun.media.jai.opimage.IDCTCRIF com.sun.media.jai idct sunidctrif rendered com.sun.media.jai.mlib.MlibIDCTRIF com.sun.media.jai idct mlibidctrif rendered com.sun.media.jai.opimage.IDFTCRIF com.sun.media.jai idft sunidftrif rendered com.sun.media.jai.mlib.MlibIDFTRIF com.sun.media.jai idft mlibidftrif rendered com.sun.media.jai.opimage.IIPCRIF com.sun.media.jai iip suniipcrif rendered com.sun.media.jai.opimage.IIPResolutionRIF com.sun.media.jai iipresolution suniipresolutionrif rendered com.sun.media.jai.opimage.ImageFunctionRIF com.sun.media.jai imagefunction sunimagefunctionrif rendered com.sun.media.jai.opimage.InvertCRIF com.sun.media.jai invert suninvertrif rendered com.sun.media.jai.mlib.MlibInvertRIF com.sun.media.jai invert mlibinvertrif rendered com.sun.media.jai.opimage.JPEGRIF com.sun.media.jai jpeg sunjpegrif rendered com.sun.media.jai.opimage.LogCRIF com.sun.media.jai log sunlogrif rendered com.sun.media.jai.mlib.MlibLogRIF com.sun.media.jai log mliblogrif rendered com.sun.media.jai.opimage.LookupCRIF com.sun.media.jai lookup sunlookuprif rendered com.sun.media.jai.mlib.MlibLookupRIF com.sun.media.jai lookup mliblookuprif rendered com.sun.media.jai.opimage.MagnitudeCRIF com.sun.media.jai magnitude sunmagnituderif rendered com.sun.media.jai.opimage.MagnitudeSquaredCRIF com.sun.media.jai magnitudesquared sunmagnitudesquaredrif rendered com.sun.media.jai.opimage.MatchCDFCRIF com.sun.media.jai matchcdf sunmatchcdfrif rendered com.sun.media.jai.opimage.MaxCRIF com.sun.media.jai max sunmaxrif rendered com.sun.media.jai.mlib.MlibMaxRIF com.sun.media.jai max mlibmaxrif rendered com.sun.media.jai.opimage.MaxFilterRIF com.sun.media.jai maxfilter sunmaxfilterrif rendered com.sun.media.jai.mlib.MlibMaxFilterRIF com.sun.media.jai maxfilter mlibmaxfilterrif rendered com.sun.media.jai.opimage.MeanRIF com.sun.media.jai mean sunmeanrif rendered com.sun.media.jai.mlib.MlibMeanRIF com.sun.media.jai mean mlibmeanrif rendered com.sun.media.jai.opimage.MedianFilterRIF com.sun.media.jai medianfilter sunmedianfilterrif rendered com.sun.media.jai.mlib.MlibMedianFilterRIF com.sun.media.jai medianfilter mlibmedianfilterrif rendered com.sun.media.jai.opimage.MinCRIF com.sun.media.jai min sunminrif rendered com.sun.media.jai.mlib.MlibMinRIF com.sun.media.jai min mlibminrif rendered com.sun.media.jai.opimage.MinFilterRIF com.sun.media.jai minfilter sunminfilterrif rendered com.sun.media.jai.mlib.MlibMinFilterRIF com.sun.media.jai minfilter mlibminfilterrif rendered com.sun.media.jai.opimage.MosaicRIF com.sun.media.jai mosaic sunmosaicrif rendered com.sun.media.jai.mlib.MlibMosaicRIF com.sun.media.jai mosaic mlibmosaicrif rendered com.sun.media.jai.opimage.MultiplyCRIF com.sun.media.jai multiply sunmultiplyrif rendered com.sun.media.jai.opimage.MultiplyComplexCRIF com.sun.media.jai multiplycomplex sunmultiplycomplexrif rendered com.sun.media.jai.mlib.MlibMultiplyRIF com.sun.media.jai multiply mlibmultiplyrif rendered com.sun.media.jai.opimage.MultiplyConstCRIF com.sun.media.jai multiplyconst sunmultiplyconstrif rendered com.sun.media.jai.mlib.MlibMultiplyConstRIF com.sun.media.jai multiplyconst mlibmultiplyconstrif rendered com.sun.media.jai.opimage.NotCRIF com.sun.media.jai not sunnotrif rendered com.sun.media.jai.mlib.MlibNotRIF com.sun.media.jai not mlibnotrif rendered javax.media.jai.NullCRIF javax.media.jai null sunullrif rendered com.sun.media.jai.opimage.OrConstCRIF com.sun.media.jai orconst sunorconstrif rendered com.sun.media.jai.mlib.MlibOrConstRIF com.sun.media.jai orconst mliborconstrif rendered com.sun.media.jai.opimage.OrCRIF com.sun.media.jai or sunorrif rendered com.sun.media.jai.mlib.MlibOrRIF com.sun.media.jai or mliborrif rendered com.sun.media.jai.opimage.OrderedDitherRIF com.sun.media.jai ordereddither sunorderedditherrif rendered com.sun.media.jai.opimage.OverlayCRIF com.sun.media.jai overlay sunoverlayrif rendered com.sun.media.jai.opimage.PatternRIF com.sun.media.jai pattern sunpatternrif rendered com.sun.media.jai.opimage.PhaseCRIF com.sun.media.jai phase sunphaserif rendered com.sun.media.jai.opimage.PeriodicShiftCRIF com.sun.media.jai periodicshift sunperiodicshiftrif rendered com.sun.media.jai.opimage.PiecewiseCRIF com.sun.media.jai piecewise sunpiecewiserif rendered com.sun.media.jai.opimage.PNGRIF com.sun.media.jai png sunpngrif rendered com.sun.media.jai.opimage.PNMRIF com.sun.media.jai pnm sunpnmrif rendered com.sun.media.jai.opimage.PolarToComplexCRIF com.sun.media.jai polartocomplex sunpolartocomplexrif rendered com.sun.media.jai.opimage.RescaleCRIF com.sun.media.jai rescale sunrescalerif rendered com.sun.media.jai.mlib.MlibRescaleRIF com.sun.media.jai rescale mlibrescalerif rendered com.sun.media.jai.opimage.RotateCRIF com.sun.media.jai rotate sunrotaterif rendered com.sun.media.jai.mlib.MlibRotateRIF com.sun.media.jai rotate mlibrotaterif rendered com.sun.media.jai.opimage.ScaleCRIF com.sun.media.jai scale sunscalerif rendered com.sun.media.jai.mlib.MlibScaleRIF com.sun.media.jai scale mlibscalerif rendered com.sun.media.jai.opimage.ShearRIF com.sun.media.jai shear sunshearrif rendered com.sun.media.jai.mlib.MlibShearRIF com.sun.media.jai shear mlibshearrif rendered com.sun.media.jai.opimage.StreamRIF com.sun.media.jai stream sunstreamrif rendered com.sun.media.jai.opimage.SubsampleBinaryToGrayCRIF com.sun.media.jai subsamplebinarytogray sunsubsamplebinarytograyrif rendered com.sun.media.jai.mlib.MlibSubsampleBinaryToGrayRIF com.sun.media.jai subsamplebinarytogray mlibsubsamplebinarytograyrif rendered com.sun.media.jai.opimage.SubsampleAverageCRIF com.sun.media.jai subsampleaverage sunsubsampleaveragerif rendered com.sun.media.jai.mlib.MlibSubsampleAverageRIF com.sun.media.jai subsampleaverage mlibsubsampleaveragerif rendered com.sun.media.jai.opimage.SubtractCRIF com.sun.media.jai subtract sunsubtractrif rendered com.sun.media.jai.mlib.MlibSubtractRIF com.sun.media.jai subtract mlibsubtractrif rendered com.sun.media.jai.opimage.SubtractConstCRIF com.sun.media.jai subtractconst sunsubtractconstrif rendered com.sun.media.jai.mlib.MlibSubtractConstRIF com.sun.media.jai subtractconst mlibsubtractconstrif rendered com.sun.media.jai.opimage.SubtractFromConstCRIF com.sun.media.jai subtractfromconst sunsubtractfromconstrif rendered com.sun.media.jai.mlib.MlibSubtractFromConstRIF com.sun.media.jai subtractfromconst mlibsubtractfromconstrif rendered com.sun.media.jai.opimage.TIFFRIF com.sun.media.jai tiff suntiffrif rendered com.sun.media.jai.opimage.ThresholdCRIF com.sun.media.jai threshold sunthresholdrif rendered com.sun.media.jai.mlib.MlibThresholdRIF com.sun.media.jai threshold mlibthresholdrif rendered com.sun.media.jai.opimage.TranslateCRIF com.sun.media.jai translate suntransrif rendered com.sun.media.jai.mlib.MlibTranslateRIF com.sun.media.jai translate mlibtranslaterif rendered com.sun.media.jai.opimage.TransposeCRIF com.sun.media.jai transpose suntransposerif rendered com.sun.media.jai.mlib.MlibTransposeRIF com.sun.media.jai transpose mlibtransposerif rendered com.sun.media.jai.opimage.UnsharpMaskRIF com.sun.media.jai unsharpmask sununsharpmaskrif rendered com.sun.media.jai.mlib.MlibUnsharpMaskRIF com.sun.media.jai unsharpmask mlibunsharpmaskrif rendered com.sun.media.jai.opimage.URLRIF com.sun.media.jai url sunurlrif rendered com.sun.media.jai.opimage.WarpRIF com.sun.media.jai warp sunwarprif rendered com.sun.media.jai.mlib.MlibWarpRIF com.sun.media.jai warp mlibwarprif rendered com.sun.media.jai.opimage.XorConstCRIF com.sun.media.jai xorconst sunxorconstrif rendered com.sun.media.jai.mlib.MlibXorConstRIF com.sun.media.jai xorconst mlibxorconstrif rendered com.sun.media.jai.opimage.XorCRIF com.sun.media.jai xor sunxorrif rendered com.sun.media.jai.mlib.MlibXorRIF com.sun.media.jai xor mlibxorrif # # "renderable" factory objects # renderable com.sun.media.jai.opimage.AbsoluteCRIF absolute renderable com.sun.media.jai.opimage.AddCollectionCRIF addcollection renderable com.sun.media.jai.opimage.AddConstCRIF addconst renderable com.sun.media.jai.opimage.AddCRIF add renderable com.sun.media.jai.opimage.AffineCRIF affine renderable com.sun.media.jai.opimage.AndConstCRIF andconst renderable com.sun.media.jai.opimage.AndCRIF and renderable com.sun.media.jai.opimage.BandCombineCRIF bandcombine renderable com.sun.media.jai.opimage.BandMergeCRIF bandmerge renderable com.sun.media.jai.opimage.BandSelectCRIF bandselect renderable com.sun.media.jai.opimage.BinarizeCRIF binarize renderable com.sun.media.jai.opimage.ClampCRIF clamp renderable com.sun.media.jai.opimage.ColorConvertCRIF colorconvert renderable com.sun.media.jai.opimage.CompositeCRIF composite renderable com.sun.media.jai.opimage.ConjugateCRIF conjugate renderable com.sun.media.jai.opimage.ConstantCRIF constant renderable com.sun.media.jai.opimage.CropCRIF crop renderable com.sun.media.jai.opimage.DCTCRIF dct renderable com.sun.media.jai.opimage.DFTCRIF dft renderable com.sun.media.jai.opimage.DivideCRIF divide renderable com.sun.media.jai.opimage.DivideComplexCRIF dividecomplex renderable com.sun.media.jai.opimage.DivideByConstCRIF dividebyconst renderable com.sun.media.jai.opimage.DivideIntoConstCRIF divideintoconst renderable com.sun.media.jai.opimage.ExpCRIF exp renderable com.sun.media.jai.opimage.FormatCRIF format renderable com.sun.media.jai.opimage.IDCTCRIF idct renderable com.sun.media.jai.opimage.IDFTCRIF idft renderable com.sun.media.jai.opimage.IIPCRIF iip renderable com.sun.media.jai.opimage.InvertCRIF invert renderable com.sun.media.jai.opimage.LogCRIF log renderable com.sun.media.jai.opimage.LookupCRIF lookup renderable com.sun.media.jai.opimage.MagnitudeCRIF magnitude renderable com.sun.media.jai.opimage.MagnitudeSquaredCRIF magnitudesquared renderable com.sun.media.jai.opimage.MatchCDFCRIF matchcdf renderable com.sun.media.jai.opimage.MaxCRIF max renderable com.sun.media.jai.opimage.MinCRIF min renderable com.sun.media.jai.opimage.MultiplyCRIF multiply renderable com.sun.media.jai.opimage.MultiplyComplexCRIF multiplycomplex renderable com.sun.media.jai.opimage.MultiplyConstCRIF multiplyconst renderable com.sun.media.jai.opimage.NotCRIF not renderable javax.media.jai.NullCRIF null renderable com.sun.media.jai.opimage.OrConstCRIF orconst renderable com.sun.media.jai.opimage.OrCRIF or renderable com.sun.media.jai.opimage.OverlayCRIF overlay renderable com.sun.media.jai.opimage.PeriodicShiftCRIF periodicshift renderable com.sun.media.jai.opimage.PhaseCRIF phase renderable com.sun.media.jai.opimage.PiecewiseCRIF piecewise renderable com.sun.media.jai.opimage.PolarToComplexCRIF polartocomplex renderable com.sun.media.jai.opimage.RenderableCRIF renderable renderable com.sun.media.jai.opimage.RescaleCRIF rescale renderable com.sun.media.jai.opimage.RotateCRIF rotate renderable com.sun.media.jai.opimage.ScaleCRIF scale renderable com.sun.media.jai.opimage.SubsampleBinaryToGrayCRIF subsamplebinarytogray renderable com.sun.media.jai.opimage.SubsampleAverageCRIF subsampleaverage renderable com.sun.media.jai.opimage.SubtractCRIF subtract renderable com.sun.media.jai.opimage.SubtractConstCRIF subtractconst renderable com.sun.media.jai.opimage.SubtractFromConstCRIF subtractfromconst renderable com.sun.media.jai.opimage.ThresholdCRIF threshold renderable com.sun.media.jai.opimage.TranslateCRIF translate renderable com.sun.media.jai.opimage.TransposeCRIF transpose renderable com.sun.media.jai.opimage.XorConstCRIF xorconst renderable com.sun.media.jai.opimage.XorCRIF xor # # "collection" factory objects # collection com.sun.media.jai.opimage.AddConstToCollectionCIF com.sun.media.jai addconsttocollection sunaddconsttocollectioncif # # tile decoder factory objects # tileDecoder com.sun.media.jai.tilecodec.GZIPTileDecoderFactory com.sun.media.jai gzip sungziptiledecoderfactory tileDecoder com.sun.media.jai.tilecodec.JPEGTileDecoderFactory com.sun.media.jai jpeg suntiledecoderfactory tileDecoder com.sun.media.jai.tilecodec.RawTileDecoderFactory com.sun.media.jai raw sunrawtiledecoderfactory # # tile encoder factory objects # tileEncoder com.sun.media.jai.tilecodec.GZIPTileEncoderFactory com.sun.media.jai gzip sungziptileencoderfactory tileEncoder com.sun.media.jai.tilecodec.JPEGTileEncoderFactory com.sun.media.jai jpeg sunjpegtileencoderfactory tileEncoder com.sun.media.jai.tilecodec.RawTileEncoderFactory com.sun.media.jai raw sunrawtileencoderfactory # # remote rendered factory objects # remoterendered com.sun.media.jai.rmi.JAIRMICRIF jairmi # # remote renderable factory objects # remoterenderable com.sun.media.jai.rmi.JAIRMICRIF jairmi # # "rendered" factory object preferences # pref rendered absolute com.sun.media.jai mlibabsoluterif sunabsoluterif pref rendered addconst com.sun.media.jai mlibaddconstrif sunaddconstrif pref rendered add com.sun.media.jai mlibaddrif sunaddrif pref rendered affine com.sun.media.jai mlibaffinerif sunaffinerif pref rendered and com.sun.media.jai mlibandrif sunandrif pref rendered andconst com.sun.media.jai mlibandconstrif sunandconstrif pref rendered bandcombine com.sun.media.jai mlibbandcombinerif sunbandcombinerif pref rendered bandselect com.sun.media.jai mlibbandselectrif sunbandselectrif pref rendered binarize com.sun.media.jai mlibbinarizerif sunbinarizerif pref rendered boxfilter com.sun.media.jai mlibboxfilterrif sunboxfilterrif pref rendered clamp com.sun.media.jai mlibclamprif sunclamprif pref rendered composite com.sun.media.jai mlibcompositerif suncompositerif pref rendered convolve com.sun.media.jai mlibconvolverif sunconvolverif pref rendered dct com.sun.media.jai mlibdctrif sundctrif pref rendered dilate com.sun.media.jai mlibdilaterif sundilaterif pref rendered idct com.sun.media.jai mlibidctrif sunidctrif pref rendered dft com.sun.media.jai mlibdftrif sundftrif pref rendered idft com.sun.media.jai mlibidftrif sunidftrif pref rendered divide com.sun.media.jai mlibdividerif sundividerif pref rendered dividebyconst com.sun.media.jai mlibdividebyconstrif sundividebyconstrif pref rendered divideintoconst com.sun.media.jai mlibdivideintoconstrif sundivideintoconstrif pref rendered erode com.sun.media.jai mliberoderif suneroderif pref rendered exp com.sun.media.jai mlibexprif sunexprif pref rendered extrema com.sun.media.jai mlibextremarif sunextremarif pref rendered filteredsubsample com.sun.media.jai mlibfilteredsubsamplerif sunfilteredsubsamplerif pref rendered gradientmagnitude com.sun.media.jai mlibgradientrif sungradientrif pref rendered histogram com.sun.media.jai mlibhistogramrif sunhistogramrif pref rendered invert com.sun.media.jai mlibinvertrif suninvertrif pref rendered log com.sun.media.jai mliblogrif sunlogrif pref rendered lookup com.sun.media.jai mliblookuprif sunlookuprif pref rendered max com.sun.media.jai mlibmaxrif sunmaxrif pref rendered maxfilter com.sun.media.jai mlibmaxfilterrif sunmaxfilterrif pref rendered mean com.sun.media.jai mlibmeanrif sunmeanrif pref rendered medianfilter com.sun.media.jai mlibmedianfilterrif sunmedianfilterrif pref rendered min com.sun.media.jai mlibminrif sunminrif pref rendered minfilter com.sun.media.jai mlibminfilterrif sunminfilterrif pref rendered mosaic com.sun.media.jai mlibmosaicrif sunmosaicrif pref rendered multiply com.sun.media.jai mlibmultiplyrif sunmultiplyrif pref rendered multiplyconst com.sun.media.jai mlibmultiplyconstrif sunmultiplyconstrif pref rendered not com.sun.media.jai mlibnotrif sunnotrif pref rendered or com.sun.media.jai mliborrif sunorrif pref rendered orconst com.sun.media.jai mliborconstrif sunorconstrif pref rendered rescale com.sun.media.jai mlibrescalerif sunrescalerif pref rendered rotate com.sun.media.jai mlibrotaterif sunrotaterif pref rendered scale com.sun.media.jai mlibscalerif sunscalerif pref rendered shear com.sun.media.jai mlibshearrif sunshearrif pref rendered subsampleaverage com.sun.media.jai mlibsubsampleaveragerif sunsubsampleaveragerif pref rendered subsamplebinarytogray com.sun.media.jai mlibsubsamplebinarytograyrif sunsubsamplebinarytograyrif pref rendered subtractconst com.sun.media.jai mlibsubtractconstrif sunsubtractconstrif pref rendered subtractfromconst com.sun.media.jai mlibsubtractfromconstrif sunsubtractfromconstrif pref rendered subtract com.sun.media.jai mlibsubtractrif sunsubtractrif pref rendered threshold com.sun.media.jai mlibthresholdrif sunthresholdrif pref rendered translate com.sun.media.jai mlibtranslaterif suntransrif pref rendered transpose com.sun.media.jai mlibtransposerif suntransposerif pref rendered unsharpmask com.sun.media.jai mlibunsharpmaskrif sununsharpmaskrif pref rendered warp com.sun.media.jai mlibwarprif sunwarprif pref rendered xor com.sun.media.jai mlibxorrif sunxorrif pref rendered xorconst com.sun.media.jai mlibxorconstrif sunxorconstrif jai-core-1.1.4/src/share/classes/javax/media/jai/IntegerSequence.java0000644000175000017500000001322510203035544025271 0ustar mathieumathieu/* * $RCSfile: IntegerSequence.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:10 $ * $State: Exp $ */ package javax.media.jai; import java.util.NoSuchElementException; /** * A growable sorted integer set. Adding an integer to the sequence * results in it being placed into the sequence in sorted order. Adding * an integer that is already part of the sequence has no effect. * *

      This structure is used by various subclasses of * OpImage to keep track of horizontal and vertical * source splits. Each instance of IntegerSequence provides an * internal enumeration by means of which the elements of the sequence * may be accessed in order. The enumerator is initialized by the * startEnumeration method, and the * hasMoreElements and nextElement methods * allow looping through the elements. Only one enumeration at a time * is supported. Calling insert() from multiple threads * is not supported. * */ public class IntegerSequence extends Object { /** Lower bound of the valid integer range. */ private int min; /** Upper bound of the valid integer range. */ private int max; /** The default initial capacity of iArray. */ private static final int DEFAULT_CAPACITY = 16; /** The array storing the unsorted integer values. */ private int[] iArray = null; /** The capacity of iArray. */ private int capacity = 0; /** The number of (non-unique) elements actually stored in iArray. */ private int numElts = 0; /** True if iArray has been sorted and purged of duplicates. */ private boolean isSorted = false; /** The current element of the iteration. */ private int currentIndex = -1; /** Constructs a sequence bounded by an inclusive range of values. * @param min Lower bound of the valid integer range. * @param max Upper bound of the valid integer range. * @throws IllegalArgumentException if min > max. */ public IntegerSequence(int min, int max) { if (min > max) throw new IllegalArgumentException(JaiI18N.getString("IntegerSequence1")); this.min = min; this.max = max; this.capacity = DEFAULT_CAPACITY; this.iArray = new int[capacity]; this.numElts = 0; this.isSorted = true; } /** Constructs a sequence that may contain any integer value. */ public IntegerSequence() { this(java.lang.Integer.MIN_VALUE, java.lang.Integer.MAX_VALUE); } /** * Inserts an integer into the sequence. If the value falls out * of the desired range, it will be silently rejected. Inserting * an element that is already a member of the sequence has no * effect. * * @param element The int to be inserted. */ public void insert(int element) { // Ignore elements that fall outside the desired range. if (element < min || element > max) { return; } if (numElts >= capacity) { int newCapacity = 2*capacity; int[] newArray = new int[newCapacity]; System.arraycopy(iArray, 0, newArray, 0, capacity); this.capacity = newCapacity; this.iArray = newArray; } isSorted = false; iArray[numElts++] = element; } /** Resets the iterator to the beginning of the sequence. */ public void startEnumeration() { if (!isSorted) { // Sort the contents of iArray java.util.Arrays.sort(iArray, 0, numElts); // Compact the array, removing duplicate entries. int readPos = 1; int writePos = 1; int prevElt = iArray[0]; // // Loop invariants: writePos <= readPos // iArray[0..readPos - 1] contains no duplicates // for (readPos = 1; readPos < numElts; ++readPos) { int currElt = iArray[readPos]; if (currElt != prevElt) { iArray[writePos++] = currElt; prevElt = currElt; } } numElts = writePos; isSorted = true; } currentIndex = 0; } /** Returns true if more elements are available to be iterated over. */ public boolean hasMoreElements() { return currentIndex < numElts; } /** * Returns the next element of the iteration in ascending order. * If the end of the array has been reached, a * java.util.NoSuchElementException will be thrown. * * @throws NoSuchElementException if the end of the array has * been reached. */ public int nextElement() { if (currentIndex < numElts) { return iArray[currentIndex++]; } else { throw new NoSuchElementException(JaiI18N.getString("IntegerSequence0")); } } /** * Returns the number of elements contained within this IntegerSequence. */ public int getNumElements() { return numElts; } /** Returns a String representation of the sequence. */ public String toString() { String s; int i; if (numElts == 0) { s = "[]"; } else { s = "["; startEnumeration(); for (i = 0; i < numElts - 1; i++) { s += iArray[i]; s += ", "; } s += iArray[numElts - 1]; s += "]"; } return s; } } jai-core-1.1.4/src/share/classes/javax/media/jai/PropertyChangeSupportJAI.java0000644000175000017500000001255510203035544027063 0ustar mathieumathieu/* * $RCSfile: PropertyChangeSupportJAI.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:16 $ * $State: Exp $ */ package javax.media.jai; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; /** * Extension of the beans utility class PropertyChangeSupport * which adds an accessor for the parameter passed to the constructor. All * events fired by the firePropertyChange() methods of this * class are instances of PropertyChangeEventJAI; consequently * all property names are forced to lower case for recognition purposes. * The property name-specific PropertyChangeListener registration * and unregistration methods defined in this class also force the supplied * property name to lower case. * * @see PropertyChangeSupport * * @since JAI 1.1 */ public final class PropertyChangeSupportJAI extends PropertyChangeSupport { /** * The PropertyChangeEvent source. */ protected Object propertyChangeEventSource; /** * Constructs a PropertyChangeSupportJAI object. The * parameter is cached for later use and retrieval. * * @param propertyChangeEventSource The property change event source. * @throws If propertyChangeEventSource is null * then a NullPointerException will be thrown * in the superclass. */ public PropertyChangeSupportJAI(Object propertyChangeEventSource) { // if propertyChangeEventSource is null, a NullPointerException // is thrown in the superclass. super(propertyChangeEventSource); this.propertyChangeEventSource = propertyChangeEventSource; } /** * Retrieve the parameter passed to the constructor. * * @return The property change event source. */ public Object getPropertyChangeEventSource() { return propertyChangeEventSource; } /** * Add a PropertyChangeListener for a specific property. * The propertyName is forced to lower case. * * @exception IllegalArgumentException if propertyName is * null. */ public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { if ( propertyName == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } super.addPropertyChangeListener(propertyName.toLowerCase(), listener); } /** * Remove a PropertyChangeListener for a specific property. * The propertyName is forced to lower case. * * @exception IllegalArgumentException if propertyName is * null. */ public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { if ( propertyName == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } super.removePropertyChangeListener(propertyName.toLowerCase(), listener); } /** * Report a bound property update to any registered listeners. * If the supplied object is not a PropertyChangeEventJAI * then a PropertyChangeEventJAI is constructed from the * event object's accessors and fired instead. * * @param evt The PropertyChangeEvent object. */ public void firePropertyChange(PropertyChangeEvent evt) { if(!(evt instanceof PropertyChangeEventJAI)) { evt = new PropertyChangeEventJAI(evt.getSource(), evt.getPropertyName(), evt.getOldValue(), evt.getNewValue()); } super.firePropertyChange(evt); } /** * Report a bound property update to any registered listeners. * A PropertyChangeEventJAI is created from the cached * property event source and the supplied parameters and fired using * the superclass firePropertyChange(PropertyChangeEvent) * method. * * @param propertyName The name of the changed property. * @param oldValue The old value of the property. * @param newValue The new value of the property. */ public void firePropertyChange(String propertyName, Object oldValue, Object newValue) { PropertyChangeEventJAI evt = new PropertyChangeEventJAI(propertyChangeEventSource, propertyName, oldValue, newValue); super.firePropertyChange(evt); } /** * Check whether there are any listeners for a specific property. * The propertyName is forced to lower case. * * @param propertyName The name of the property. * @return true if there are one or more listeners for * the given property */ public synchronized boolean hasListeners(String propertyName) { return super.hasListeners(propertyName.toLowerCase()); } } jai-core-1.1.4/src/share/classes/javax/media/jai/RegistryElementDescriptor.java0000644000175000017500000000726010203035544027366 0ustar mathieumathieu/* * $RCSfile: RegistryElementDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:19 $ * $State: Exp $ */ package javax.media.jai; /** * An interface for all JAI descriptors that register themselves * with the OperationRegistry. Examples include * OperationDescriptor, TileCodecDescriptor, * RemoteDescriptor etc. * * @see OperationRegistry * @see RegistryMode * * @since JAI 1.1 */ public interface RegistryElementDescriptor { /** * The name this descriptor will be registered under in the * OperationRegistry. Individual descriptors * implementing this interface will define what this name means * in their space. For example this would be "operation name" for * OperationDescriptor and "format name" for * TileCodecDescriptor etc. The descriptor * names are to be treated in a case-insensitive (but retentive) manner. */ String getName(); /** * The registry modes supported by this descriptor. Known modes * include those returned by RegistryMode.getModes(). * * @return an array of Strings specifying the supported modes. * * @see RegistryMode */ String[] getSupportedModes(); /** * Whether this descriptor supports the specified registry mode. * The modeNames are to be treated in a case-insensitive * (but retentive) manner. * * @param modeName the registry mode name * * @return true, if the implementation of this descriptor supports * the specified mode. false otherwise. * * @throws IllegalArgumentException if modeName is null */ boolean isModeSupported(String modeName); /** * Whether this descriptor supports JAI properties. * * @return true, if the implementation of this descriptor * supports JAI properties. false otherwise. * * @see PropertyGenerator */ boolean arePropertiesSupported(); /** * Returns an array of PropertyGenerators implementing * the property inheritance for this descriptor. They may be used * as a basis for the descriptor's property management. * * @param modeName the registry mode name * * @return An array of PropertyGenerators, or * null if this operation does not have any of * its own PropertyGenerators. * * @throws IllegalArgumentException if modeName is null * or if it is not one of the supported modes. * @throws UnsupportedOperationException if arePropertiesSupported() * returns false */ PropertyGenerator[] getPropertyGenerators(String modeName); /** * Returns the ParameterListDescriptor that describes * the associated parameters (not sources). This method returns * null if the specified modeName does not support parameters. * If the specified modeName supports parameters but the * implementing class does not have parameters, then this method * returns a non-null ParameterListDescriptor whose * getNumParameters() returns 0. * * @param modeName the registry mode name. * * @throws IllegalArgumentException if modeName is null * or if it is not one of the supported modes. */ ParameterListDescriptor getParameterListDescriptor(String modeName); } jai-core-1.1.4/src/share/classes/javax/media/jai/CanvasJAI.java0000644000175000017500000000303010203035544023733 0ustar mathieumathieu/* * $RCSfile: CanvasJAI.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:05 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Canvas; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; /** * An extension of java.awt.Canvas for use with JAI. * CanvasJAI automatically returns an instance of * GraphicsJAI from its getGraphics() * method. This guarantees that the update(Graphics g) * and paint(Graphics g) methods will receive a * GraphicsJAI instance for accelerated rendering of * JAI images. * *

      In circumstances where it is not possible to use * CanvasJAI, a similar effect may be obtained by * manually calling GraphicsJAI.createGraphicsJAI() to * "wrap" a Graphics2D object. * * @see GraphicsJAI */ public class CanvasJAI extends Canvas { /** * Constructs an instance of CanvasJAI using the * given GraphicsConfiguration. */ public CanvasJAI(GraphicsConfiguration config) { super(config); } /** * Returns an instance of GraphicsJAI for drawing to * this canvas. */ public Graphics getGraphics() { Graphics2D g = (Graphics2D)super.getGraphics(); return GraphicsJAI.createGraphicsJAI(g, this); } } jai-core-1.1.4/src/share/classes/javax/media/jai/DataBufferDouble.java0000644000175000017500000002262610203035544025346 0ustar mathieumathieu/* * $RCSfile: DataBufferDouble.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:07 $ * $State: Exp $ */ package javax.media.jai; import java.awt.image.DataBuffer; /** * An extension of DataBuffer that stores data internally * in double form. * * @see java.awt.image.DataBuffer */ public class DataBufferDouble extends DataBuffer { /** The array of data banks. */ protected double bankdata[][]; /** A reference to the default data bank. */ protected double data[]; /** * Constructs a double-based DataBuffer * with a specified size. * * @param size The number of elements in the DataBuffer. */ public DataBufferDouble(int size) { super(TYPE_DOUBLE, size); data = new double[size]; bankdata = new double[1][]; bankdata[0] = data; } /** * Constructs a double-based DataBuffer * with a specified number of banks, all of which are of a * specified size. * * @param size The number of elements in each bank of the * DataBuffer. * @param numBanks The number of banks in the DataBuffer. */ public DataBufferDouble(int size, int numBanks) { super(TYPE_DOUBLE, size, numBanks); bankdata = new double[numBanks][]; for (int i= 0; i < numBanks; i++) { bankdata[i] = new double[size]; } data = bankdata[0]; } /** * Constructs a double-based DataBuffer * with the specified data array. Only the first * size elements are available for use by this * DataBuffer. The array must be large enough to * hold size elements. * * @param dataArray An array of doubles to be used as the * first and only bank of this DataBuffer. * @param size The number of elements of the array to be used. */ public DataBufferDouble(double dataArray[], int size) { super(TYPE_DOUBLE, size); if (dataArray.length < size) throw new RuntimeException(JaiI18N.getString("DataBuffer0")); data = dataArray; bankdata = new double[1][]; bankdata[0] = data; } /** * Constructs a double-based DataBuffer * with the specified data array. Only the elements between * offset and offset + size - 1 are * available for use by this DataBuffer. The array * must be large enough to hold offset + size elements. * * @param dataArray An array of doubles to be used as the * first and only bank of this DataBuffer. * @param size The number of elements of the array to be used. * @param offset The offset of the first element of the array * that will be used. */ public DataBufferDouble(double dataArray[], int size, int offset) { super(TYPE_DOUBLE, size, 1, offset); if (dataArray.length < size) throw new RuntimeException(JaiI18N.getString("DataBuffer1")); data = dataArray; bankdata = new double[1][]; bankdata[0] = data; } /** * Constructs a double-based DataBuffer * with the specified data arrays. Only the first * size elements of each array are available for use * by this DataBuffer. The number of banks will be * equal to dataArray.length. * * @param dataArray An array of arrays of doubles to be * used as the banks of this DataBuffer. * @param size The number of elements of each array to be used. */ public DataBufferDouble(double dataArray[][], int size) { super(TYPE_DOUBLE, size, dataArray.length); bankdata = dataArray; data = bankdata[0]; } /** * Constructs a double-based DataBuffer * with the specified data arrays, size, and per-bank offsets. * The number of banks is equal to dataArray.length. Each array * must be at least as large as size plus the corresponding * offset. There must be an entry in the offsets * array for each data array. * * @param dataArray An array of arrays of doubles to be * used as the banks of this DataBuffer. * @param size The number of elements of each array to be used. * @param offsets An array of integer offsets, one for each bank. */ public DataBufferDouble(double dataArray[][], int size, int offsets[]) { super(TYPE_DOUBLE, size, dataArray.length, offsets); bankdata = dataArray; data = bankdata[0]; } /** Returns the double data array of the default(first) bank. */ public double[] getData() { return data; } /** Returns the data array for the specified bank. */ public double[] getData(int bank) { return bankdata[bank]; } /** Returns the data array for all banks. */ public double[][] getBankData() { return bankdata; } /** * Returns the requested data array element from the first * (default) bank as an int. * * @param i The desired data array element. * * @return The data entry as an int. */ public int getElem(int i) { return (int)(data[i+offset]); } /** * Returns the requested data array element from the specified * bank as an int. * * @param bank The bank number. * @param i The desired data array element. * * @return The data entry as an 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 given int. * * @param i The desired data array element. * @param val The value to be set. */ public void setElem(int i, int val) { data[i+offset] = (double)val; } /** * Sets the requested data array element in the specified bank * to the given int. * * @param bank The bank number. * @param i The desired data array element. * @param val The value to be set. */ public void setElem(int bank, int i, int val) { bankdata[bank][i+offsets[bank]] = (double)val; } /** * Returns the requested data array element from the first * (default) bank as a float. * * @param i The desired data array element. * * @return The data entry as a float. */ public float getElemFloat(int i) { return (float)data[i+offset]; } /** * Returns the requested data array element from the specified * bank as a float. * * @param bank The bank number. * @param i The desired data array element. * * @return The data entry as a float. */ public float getElemFloat(int bank, int i) { return (float)bankdata[bank][i+offsets[bank]]; } /** * Sets the requested data array element in the first (default) * bank to the given float. * * @param i The desired data array element. * @param val The value to be set. */ public void setElemFloat(int i, float val) { data[i+offset] = (double)val; } /** * Sets the requested data array element in the specified bank to * the given float. * * @param bank The bank number. * @param i The desired data array element. * @param val The value to be set. */ public void setElemFloat(int bank, int i, float val) { bankdata[bank][i+offsets[bank]] = (double)val; } /** * Returns the requested data array element from the first * (default) bank as a double. * * @param i The desired data array element. * * @return The data entry as a double. */ public double getElemDouble(int i) { return data[i+offset]; } /** * Returns the requested data array element from the specified * bank as a double. * * @param bank The bank number. * @param i The desired data array element. * * @return The data entry as a double. */ public double getElemDouble(int bank, int i) { return bankdata[bank][i+offsets[bank]]; } /** * Sets the requested data array element in the first (default) * bank to the given double. * * @param i The desired data array element. * @param val The value to be set. */ public void setElemDouble(int i, double val) { data[i+offset] = val; } /** * Sets the requested data array element in the specified bank to * the given double. * * @param bank The bank number. * @param i The desired data array element. * @param val The value to be set. */ public void setElemDouble(int bank, int i, double val) { bankdata[bank][i+offsets[bank]] = val; } } jai-core-1.1.4/src/share/classes/javax/media/jai/BorderExtenderCopy.java0000644000175000017500000003224110203035544025751 0ustar mathieumathieu/* * $RCSfile: BorderExtenderCopy.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:04 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.WritableRaster; import com.sun.media.jai.util.JDKWorkarounds; /** * A subclass of BorderExtender that implements * border extension by filling all pixels outside of the image * bounds with copies of the edge pixels. For example, the image: * *

      * * * * *
      ABC
      DEF
      GHI
      * *
      if extended by adding two extra rows to the top and bottom and * two extra columns on the left and right sides, would become: * *

      * * * * * * * * * *
      AAABCCC
      AAABCCC
      AAABCCC
      DDDEFFF
      GGGHIII
      GGGHIII
      GGGHIII
      * *

      Although this type of extension is not particularly * visually appealing, it is very useful as a way of padding * source images prior to area or geometric operations, such as * convolution, scaling, or rotation. * * @see BorderExtender */ public final class BorderExtenderCopy extends BorderExtender { BorderExtenderCopy() {} /** * Fills in the portions of a given Raster that lie * outside the bounds of a given PlanarImage with * copies of the edge pixels of the image. * *

      The portion of raster that lies within * im.getBounds() is not altered. * * @param raster The WritableRaster the border area of * which is to be filled with copies of the edge pixels * of the image. * @param im The PlanarImage which will provide the * edge data with which to fill the border area of the * WritableRaster. * * @throws IllegalArgumentException if either parameter is * null. */ public final void extend(WritableRaster raster, PlanarImage im) { if ( raster == null || im == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } int width = raster.getWidth(); int height = raster.getHeight(); int numBands = raster.getNumBands(); int minX = raster.getMinX(); int maxX = minX + width; int minY = raster.getMinY(); int maxY = minY + height; int validMinX = Math.max(im.getMinX(), minX); int validMaxX = Math.min(im.getMaxX(), maxX); int validMinY = Math.max(im.getMinY(), minY); int validMaxY = Math.min(im.getMaxY(), maxY); if(validMinX > validMaxX || validMinY > validMaxY) { // Raster does not intersect image. Determine the location // and size of the smallest rectangle containing the Raster // and which intersects the image. if(validMinX > validMaxX) { // no intersetion in X if(minX == validMinX) { minX = im.getMaxX() - 1; } else { maxX = im.getMinX(); } } if(validMinY > validMaxY) { // no intersetion in Y if(minY == validMinY) { minY = im.getMaxY() - 1; } else { maxY = im.getMinY(); } } // Create minimum Raster. WritableRaster wr = raster.createCompatibleWritableRaster(minX, minY, maxX - minX, maxY - minY); // Extend the data. extend(wr, im); // Create a child with same bounds as the target Raster. Raster child = wr.createChild(raster.getMinX(), raster.getMinY(), raster.getWidth(), raster.getHeight(), raster.getMinX(), raster.getMinY(), null); // Copy the data from the child. JDKWorkarounds.setRect(raster, child, 0, 0); return; } Rectangle rect = new Rectangle(); int size = Math.max(width, height); int row, col; switch (raster.getSampleModel().getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_INT: int[] iData = new int[size*numBands]; if (minX < validMinX) { rect.x = validMinX; rect.y = validMinY; rect.width = 1; rect.height = validMaxY - validMinY; if (rect.height > 0) { Raster leftEdge = im.getData(rect); leftEdge.getPixels(validMinX, validMinY, 1, rect.height, iData); for (col = minX; col < validMinX; col++) { raster.setPixels(col, validMinY, 1, rect.height, iData); } } } if (validMaxX < maxX) { rect.x = validMaxX - 1; rect.y = validMinY; rect.width = 1; rect.height = validMaxY - validMinY; if (rect.height > 0) { Raster rightEdge = im.getData(rect); rightEdge.getPixels(validMaxX - 1, validMinY, 1, rect.height, iData); for (col = validMaxX; col < maxX; col++) { raster.setPixels(col, validMinY, 1, rect.height, iData); } } } if (minY < validMinY) { rect.x = minX; rect.y = validMinY; rect.width = width; rect.height = 1; Raster topRow = im.getExtendedData(rect, this); topRow.getPixels(minX, validMinY, width, 1, iData); for (row = minY; row < validMinY; row++) { raster.setPixels(minX, row, width, 1, iData); } } if (validMaxY < maxY) { rect.x = minX; rect.y = validMaxY - 1; rect.width = width; rect.height = 1; Raster bottomRow = im.getExtendedData(rect, this); bottomRow.getPixels(minX, validMaxY - 1, width, 1, iData); for (row = validMaxY; row < maxY; row++) { raster.setPixels(minX, row, width, 1, iData); } } break; case DataBuffer.TYPE_FLOAT: float[] fData = new float[size*numBands]; if (minX < validMinX) { rect.x = validMinX; rect.y = validMinY; rect.width = 1; rect.height = validMaxY - validMinY; if (rect.height > 0) { Raster leftEdge = im.getData(rect); leftEdge.getPixels(validMinX, validMinY, 1, rect.height, fData); for (col = minX; col < validMinX; col++) { raster.setPixels(col, validMinY, 1, rect.height, fData); } } } if (validMaxX < maxX) { rect.x = validMaxX - 1; rect.y = validMinY; rect.width = 1; rect.height = validMaxY - validMinY; if (rect.height > 0) { Raster rightEdge = im.getData(rect); rightEdge.getPixels(validMaxX - 1, validMinY, 1, rect.height, fData); for (col = validMaxX; col < maxX; col++) { raster.setPixels(col, validMinY, 1, rect.height, fData); } } } if (minY < validMinY) { rect.x = minX; rect.y = validMinY; rect.width = width; rect.height = 1; Raster topRow = im.getExtendedData(rect, this); topRow.getPixels(minX, validMinY, width, 1, fData); for (row = minY; row < validMinY; row++) { raster.setPixels(minX, row, width, 1, fData); } } if (validMaxY < maxY) { rect.x = minX; rect.y = validMaxY - 1; rect.width = width; rect.height = 1; Raster bottomRow = im.getExtendedData(rect, this); bottomRow.getPixels(minX, validMaxY - 1, width, 1, fData); for (row = validMaxY; row < maxY; row++) { raster.setPixels(minX, row, width, 1, fData); } } break; case DataBuffer.TYPE_DOUBLE: double[] dData = new double[size*numBands]; if (minX < validMinX) { rect.x = validMinX; rect.y = validMinY; rect.width = 1; rect.height = validMaxY - validMinY; if (rect.height > 0) { Raster leftEdge = im.getData(rect); leftEdge.getPixels(validMinX, validMinY, 1, rect.height, dData); for (col = minX; col < validMinX; col++) { raster.setPixels(col, validMinY, 1, rect.height, dData); } } } if (validMaxX < maxX) { rect.x = validMaxX - 1; rect.y = validMinY; rect.width = 1; rect.height = validMaxY - validMinY; if (rect.height > 0) { Raster rightEdge = im.getData(rect); rightEdge.getPixels(validMaxX - 1, validMinY, 1, rect.height, dData); for (col = validMaxX; col < maxX; col++) { raster.setPixels(col, validMinY, 1, rect.height, dData); } } } if (minY < validMinY) { rect.x = minX; rect.y = validMinY; rect.width = width; rect.height = 1; Raster topRow = im.getExtendedData(rect, this); topRow.getPixels(minX, validMinY, width, 1, dData); for (row = minY; row < validMinY; row++) { raster.setPixels(minX, row, width, 1, dData); } } if (validMaxY < maxY) { rect.x = minX; rect.y = validMaxY - 1; rect.width = width; rect.height = 1; Raster bottomRow = im.getExtendedData(rect, this); bottomRow.getPixels(minX, validMaxY - 1, width, 1, dData); for (row = validMaxY; row < maxY; row++) { raster.setPixels(minX, row, width, 1, dData); } } break; } } } jai-core-1.1.4/src/share/classes/javax/media/jai/RenderingChangeEvent.java0000644000175000017500000000431010203035544026223 0ustar mathieumathieu/* * $RCSfile: RenderingChangeEvent.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:21 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Shape; /** * Class representing the event that occurs when a RenderedOp * node is re-rendered. * * @since JAI 1.1 */ public class RenderingChangeEvent extends PropertyChangeEventJAI { private Shape invalidRegion; /** * Constructs a RenderingChangeEvent. The inherited * getSource() method of the event would return the * RenderedOp source; the inherited methods * getOldValue() and getNewValue() * would return the old and new renderings, respectively. If * either the new rendering or the invalid region is null, the * data of the node's rendering need to be re-requested. * *

      The invalid region should be null if there is no * area of the extant rendering which remains valid. If the invalid * region is empty, this serves to indicate that all pixels within the * bounds of the old rendering remain valid. Pixels outside the image * bounds proper but within the bounds of all tiles of the image are * not guaranteed to be valid of the invalid region is empty. */ public RenderingChangeEvent(RenderedOp source, PlanarImage oldRendering, PlanarImage newRendering, Shape invalidRegion) { super(source, "Rendering", oldRendering, newRendering); this.invalidRegion = invalidRegion; } /** * Returns an object which represents the region over which the * the two renderings should differ. * * @return The region over which the two renderings differ or * null to indicate that they differ everywhere. * An empty Shape indicates that all pixels * within the bounds of the old rendering remain valid. */ public Shape getInvalidRegion() { return invalidRegion; } } jai-core-1.1.4/src/share/classes/javax/media/jai/RenderedOp.java0000644000175000017500000036231410444633025024245 0ustar mathieumathieu/* * $RCSfile: RenderedOp.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2006-06-16 22:52:04 $ * $State: Exp $ */ package javax.media.jai; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.util.PropertyUtil; import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.geom.Area; import java.awt.geom.GeneralPath; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.image.ColorModel; import java.awt.image.ImageProducer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.awt.image.renderable.ParameterBlock; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.ref.WeakReference; import java.util.Arrays; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Set; import java.util.Vector; import javax.media.jai.registry.RIFRegistry; import javax.media.jai.registry.RenderedRegistryMode; import javax.media.jai.remote.PlanarImageServerProxy; import javax.media.jai.remote.SerializableRenderedImage; import javax.media.jai.util.CaselessStringKey; import javax.media.jai.util.ImagingListener; /** * A node in a rendered imaging chain. A RenderedOp stores * an operation name, a ParameterBlock containing sources and * parameters, and a RenderingHints containing hints which * may be used in rendering the node. A set of nodes may be joined together * via the source Vectors within their respective * ParameterBlocks to form a directed acyclic * graph (DAG). The topology, i.e., connectivity, of the graph may be * altered by changing the node's sources. The operation name, parameters, * and rendering hints may also be changed. * *

      Such chains are useful for example as arguments to a * RemoteImage; they convey the structure of an imaging * chain in a compact representation and at a suitably high level of * abstraction to allow the server some leeway in materializing the * results. They are also useful in that a chain may be manipulated * dynamically and rendered multiple times. Thus for example the same * chain of operations may be applied to different images or the parameters * of certain operations in a chain may be modified interactively. * *

      A RenderedOp may be constructed directly as, for example, *

       * 
       * ParameterBlock pb =
       *     (new ParameterBlock()).add("SomeFile.tif");
       * RenderedOp node = new RenderedOp("fileload", pb, null);
       * 
       * 
      * or via the create or createNS() methods defined * in the JAI class. The difference between direct construction * of a node and creation via a convenience method is that in the latter case: * *
        *
      1. It is verified that the operation supports the rendered mode.
      2. *
      3. Using the validateArguments() method of the associated * OperationDescriptor, the arguments (sources and parameters) * are validated as being compatible with the specified operation.
      4. *
      5. Global RenderingHints maintained by the JAI * instance are merged with the local RenderingHints with the * local hints taking precedence.
      6. *
      7. If the operation is defined to be "immediate" (the * isImmediate() method of the corresponding * OperationDescriptor returns true) * then the node is rendered.
      8. *
      * *

      When a chain of nodes is rendered by any means a "parallel" chain of * RenderedImages is created. Each node in the chain of * RenderedOps corresponds to a node in the chain of * RenderedImages. RenderedImage methods invoked * on the RenderedOp are in general forwarded to the associated * RenderedImage which is referred to as the rendering * of the node. * *

      The translation between RenderedOp chains and * RenderedImage (usually OpImage) chains makes * use of two levels of indirection provided by the * OperationRegistry and RenderedImageFactory * (RIF) facilities. First, the local OperationRegistry is * used to map the operation name into a RIF. This RIF then constructs * a RenderedImage (usually an OpImage) which * does the actual image data processing. The local * OperationRegistry is used in order to take advantage * of the best possible implementation of the operation, e.g., RIFs that * provide acceleration for certain cases or RIFs that are known to a server * without having to burden the client. * *

      A node may be rendered explicitly by invoking the method * getRendering() which also returns the rendering of the * node. A node may be rendered implicitly by invoking any method * defined in the RenderedImage interface. A node may also be * rendered implicitly by invoking any method the execution of which *

        *
      • requires some dimensional quantity of the image such as its * bounds or tile layout;
      • *
      • retrieves image data by any means;
      • *
      * The current rendering may be obtained without forcing the rendering of * an unrendered node via the method getCurrentRendering(). * A node may also be re-rendered via getNewRendering() which * regenerates the rendering from the existing set of sources, parameters, * and hints. * *

      A rendering of a node may also be obtained by means of the * createInstance() method. This method returns a * PlanarImage rendering without marking the node as * having been rendered. If the node is not marked as rendered then it * will not fire RenderingChangeEvents as described below. * *

      RenderedOp nodes may participate in Java Bean-style * events. The PropertyChangeEmitter methods may be used * to register and unregister PropertyChangeListeners. * RenderedOps are also PropertyChangeListeners * so that they may be registered as listeners of other * PropertyChangeEmitters or the equivalent. Each * RenderedOp also automatically receives any * RenderingChangeEvents emitted by any of its sources which * are also RenderedOps or any CollectionChangeEvents * from any CollectionOp sources. * *

      Certain PropertyChangeEvents may be emitted by the * RenderedOp. These include the * PropertyChangeEventJAIs and * PropertySourceChangeEvents required by virtue of implementing * the OperationNode interface. Additionally a * RenderingChangeEvent may be emitted if the node has already * been rendered and both of the following conditions are satisfied: *

        *
      1. A. any of the critical attributes is changed (edited), i.e., the * operation name, operation registry, node sources, parameters, or rendering * hints; or *
        B. the node receives a RenderingChangeEvent from one of * its RenderedOp sources or a CollectionChangeEvent * from one of its CollectionOp sources.
      2. *
      3. the old and new renderings differ over some non-empty region.
      4. *
      * *

      When a rendered RenderedOp node receives a * RenderingChangeEvent from a RenderedOp source, * then if the rendering is an OpImage, the region of * the current rendering which may be retained will be determined by using * mapSourceRect() to forward map the bounds of the invalid * region. A similar procedure is used for "InvalidRegion" events emitted * by source RenderedImages such as TiledImages. * If a critical attribute of the node is edited, then the * getInvalidRegion() method of the corresponding * OperationDescriptor will be used to determine the * invalid region. If the complement of the invalid region contains any tiles * of the current rendering and the rendering is an OpImage, a * new rendering of the node will be generated and the * identified tiles will be retained from the old rendering insofar as * possible. This might involve for example adding tiles to a * TileCache under the ownership of the new rendering. A * RenderingChangeEvent will then be fired to all * PropertyChangeListeners of the node, and to any sinks that * are PropertyChangeListeners. The newRendering * parameter of the event constructor (which may be retrieved via the * getNewValue() method of the event) will be set to either * the new rendering of the node or to null if it was not * possible to retain any tiles of the previous rendering. * *

      RenderedOp nodes are WritablePropertySources * and so manage a name-value database of image meta-data also known as image * properties. Properties may be set on and requested from a node. The * value of a property not explicitly set on the node (via * setProperty()) is obtained from the property environment of * the node. When a property is derived from the property environment it is * cached locally to ensure synchronization, i.e., that properties do not * change spontaneously if for example the same property is modified upstream. * *

      The property environment of a RenderedOp is initially * derived from that of the corresponding OperationDescriptor * as maintained by the OperationRegistry. It may be modified * locally by adding PropertyGenerators, directives to copy * certain properties from specific sources, or requests to suppress certain * properties. These modifications per se cannot be undone directly but * may be eliminated as a side effect of other changes to the node as * described below. * *

      The RenderedOp itself synthesizes several property values, * which may neither be set nor removed. These are: image_width, * image_height, image_min_x_coord, * image_min_y_coord, tile_cache and * tile_cache_key. These properties are referred to as * synthetic properties. The property tile_cache_key * has a value of type {@link TileCache} which indicates where the tiles * of the rendering are cached, if anywhere. The value of the property * tile_cache_key is a {@link RenderedImage} by which the * cached tiles are referenced in the indicated cache. If the rendering * is of type {@link OpImage} or * {@link javax.media.jai.remote.PlanarImageServerProxy} then the value of * tile_cache_key will be set to the rendering itself and the * value of tile_cache to the value returned by invoking * getTileCache() on the rendering. Otherwise these properties * will be set to the same values as the properties of the same names set * on the rendering. It is legal for these properties to have the value * java.awt.Image.UndefinedProperty. * *

      When a property value is requested an attempt will be made to derive * it from the several entities in the following order of precedence: *

        *
      1. synthetic properties;
      2. *
      3. local properties;
      4. *
      5. the rendering of the node;
      6. *
      7. any registered PropertyGenerators, or *
        a source specified via a copy-from-source directive;
      8. *
      9. the first node source which defines the property.
      10. *
      * Local properties are those which have been cached locally either by virtue * of direct invocation of setProperty() or due to caching of a * property derived from the property environment. Note that the properties * of a node are not copied to its rendering. * *

      All dynamically computed properties of a RenderedOp which * have been cached locally, i.e., those cached properties which were not set * by an explicit call to setProperty(), will be cleared when any * of the critical attributes of the node is edited. By implication these * properties will also be cleared when a RenderingChangeEvent * is received from any node source. The property environment or the cached * properties may also be cleared by invoking resetProperties(). * *

      As mentioned, a RenderedOp chain created on a client * may be passed to a server via a RemoteImage. Whether the * node has been previously rendered is irrelevant to its ability to be * serialized. Any RenderedImage sources which are not * Serializable will be wrapped in * SerializableRenderedImages for serialization. The tile * transmission parameters will be determined from the * RenderingHints of the node. All other non-serializable * objects will attempt to be serialized using * SerializerFactory. If no Serializer is * available for a particular object, a * java.io.NotSerializableException may result. Image * properties (meta-data) are serialized insofar as they are serializable: * non-serializable components are simply eliminated from the local cache * of properties and from the property environment. * *

      Note that RenderedOp nodes used to instantiate * operations which have a corresponding OperationDescriptor * the isImmediate() method of which returns * true are rendered upon deserialization. * *

      RenderedOp represents a single PlanarImage * as a node in a RenderedImage operation chain. Its companion * classes, RenderableOp and CollectionOp, represent * nodes in operation chains of RenderableImages and * CollectionImages, respectively. * * * @see CollectionOp * @see JAI * @see OperationDescriptor * @see OperationRegistry * @see OpImage * @see RenderableOp * @see RenderingChangeEvent * @see javax.media.jai.remote.SerializableRenderedImage * @see javax.media.jai.remote.Serializer * @see javax.media.jai.remote.SerializerFactory * @see java.awt.RenderingHints * @see java.awt.image.renderable.ParameterBlock * @see java.awt.image.renderable.RenderedImageFactory * */ public class RenderedOp extends PlanarImage implements OperationNode, PropertyChangeListener, Serializable { /** * An object to assist in implementing OperationNode. * * @since JAI 1.1 */ protected OperationNodeSupport nodeSupport; /** * The PropertySource containing the combined properties * of all of the node's sources. */ protected transient PropertySource thePropertySource; /** The rendering of the current image, not preserved over RMI. */ protected transient PlanarImage theImage; /** * The RenderingHints when the node was last rendered, i.e., when * "theImage" was set to its current value. */ private transient RenderingHints oldHints; /** Names of synthesized properties. */ // XXX Synthetic properties should never be inherited. This might imply // a need for setting non-inheritable in addition to suppressed properties. private static List synthProps; /** Synthesized properties. */ private Hashtable synthProperties = null; /** Node event names. */ private static Set nodeEventNames = null; /** * Whether dispose() has been invoked. */ private boolean isDisposed = false; static { CaselessStringKey[] propKeys = new CaselessStringKey[] { new CaselessStringKey("image_width"), new CaselessStringKey("image_height"), new CaselessStringKey("image_min_x_coord"), new CaselessStringKey("image_min_y_coord"), new CaselessStringKey("tile_cache"), new CaselessStringKey("tile_cache_key") }; synthProps = Arrays.asList(propKeys); nodeEventNames = new HashSet(); nodeEventNames.add("operationname"); nodeEventNames.add("operationregistry"); nodeEventNames.add("parameterblock"); nodeEventNames.add("sources"); nodeEventNames.add("parameters"); nodeEventNames.add("renderinghints"); } /** * Constructs a RenderedOp that will be used to * instantiate a particular rendered operation from the specified * operation registry, an operation name, a ParameterBlock, * and a set of rendering hints. * *

      This method does not validate the contents of the supplied * ParameterBlock. The caller should ensure that * the sources and parameters in the ParameterBlock * are suitable for the operation this node represents; otherwise * some form of error or exception may occur at the time of rendering. * *

      The ParameterBlock may include * DeferredData parameters. These will not be evaluated * until their values are actually required, i.e., when the node is * rendered. * *

      The node is added automatically as a sink of any * PlanarImage or CollectionImage sources. * * @param registry The OperationRegistry to be used for * instantiation. if null, the default registry * is used. Saved by reference. * @param opName The operation name. Saved by reference. * @param pb The sources and parameters. If null, * it is assumed that this node has no sources and parameters. * This parameter is cloned. * @param hints The rendering hints. If null, it is assumed * that no hints are associated with the rendering. * This parameter is cloned. * * @throws IllegalArgumentException if opName is * null. */ public RenderedOp(OperationRegistry registry, String opName, ParameterBlock pb, RenderingHints hints) { super(new ImageLayout(), null, null); if(pb == null) { // Ensure that the PB is non-null. pb = new ParameterBlock(); } else { // Clone the PB per the doc. pb = (ParameterBlock)pb.clone(); } if(hints != null) { // Clone the hints per the doc. hints = (RenderingHints)hints.clone(); } nodeSupport = new OperationNodeSupport(getRegistryModeName(), opName, registry, pb, hints, eventManager); // Add the node as a PropertyChangeListener of itself for // the critical attributes of the node. Case is ignored // in the property names but infix caps are used here anyway. addPropertyChangeListener("OperationName", this); addPropertyChangeListener("OperationRegistry", this); addPropertyChangeListener("ParameterBlock", this); addPropertyChangeListener("Sources", this); addPropertyChangeListener("Parameters", this); addPropertyChangeListener("RenderingHints", this); // Add self as a sink of any PlanarImage or CollectionImage sources. Vector nodeSources = pb.getSources(); if(nodeSources != null) { Iterator it = nodeSources.iterator(); while(it.hasNext()) { Object src = it.next(); if(src instanceof PlanarImage) { ((PlanarImage)src).addSink(this); } else if(src instanceof CollectionImage) { ((CollectionImage)src).addSink(this); } } } } /** * Constructs a RenderedOp that will be used to * instantiate a particular rendered operation from the default * operation registry, an operation name, a ParameterBlock, * and a set of rendering hints. The default operation registry * is used. * *

      This method does not validate the contents of the supplied * ParameterBlock. The caller should ensure that * the sources and parameters in the ParameterBlock * are suitable for the operation this node represents; otherwise * some form of error or exception may occur at the time of rendering. * *

      The ParameterBlock may include * DeferredData parameters. These will not be evaluated * until their values are actually required, i.e., when the node is * rendered. * *

      The node is added automatically as a sink of any * PlanarImage or CollectionImage sources. * * @param opName The operation name. Saved by reference. * @param pb The sources and parameters. If null, * it is assumed that this node has no sources and parameters. * This parameter is cloned. * @param hints The rendering hints. If null, it is assumed * that no hints are associated with the rendering. * This parameter is cloned. * * @throws IllegalArgumentException if opName is * null. */ public RenderedOp(String opName, ParameterBlock pb, RenderingHints hints) { this(null, opName, pb, hints); } /** * A TileComputationListener to pass to the * scheduleTiles() method of the rendering to intercept * method calls such that the image reference is this * RenderedOp. */ private class TCL implements TileComputationListener { RenderedOp node; private TCL(RenderedOp node) { this.node = node; } public void tileComputed(Object eventSource, TileRequest[] requests, PlanarImage image, int tileX, int tileY, Raster tile) { if(image == theImage) { // Forward call to all listeners. TileComputationListener[] listeners = getTileComputationListeners(); if(listeners != null) { int numListeners = listeners.length; for(int i = 0; i < numListeners; i++) { listeners[i].tileComputed(node, requests, image, tileX, tileY, tile); } } } } public void tileCancelled(Object eventSource, TileRequest[] requests, PlanarImage image, int tileX, int tileY) { if(image == theImage) { // Forward call to all listeners. TileComputationListener[] listeners = getTileComputationListeners(); if(listeners != null) { int numListeners = listeners.length; for(int i = 0; i < numListeners; i++) { listeners[i].tileCancelled(node, requests, image, tileX, tileY); } } } } public void tileComputationFailure(Object eventSource, TileRequest[] requests, PlanarImage image, int tileX, int tileY, Throwable situation) { if(image == theImage) { // Forward call to all listeners. TileComputationListener[] listeners = getTileComputationListeners(); if(listeners != null) { int numListeners = listeners.length; for(int i = 0; i < numListeners; i++) { listeners[i].tileComputationFailure(node, requests, image, tileX, tileY, situation); } } } } } /** * Returns the name of the RegistryMode corresponding to * this RenderedOp. This method always returns the * String "rendered". * * @since JAI 1.1 */ public String getRegistryModeName() { return RegistryMode.getMode("rendered").getName(); } /* ----- Critical attribute main accessors and mutators. ----- */ /** * Returns the OperationRegistry that is used * by this node. If the registry is not set, the default * registry is returned. */ public synchronized OperationRegistry getRegistry() { return nodeSupport.getRegistry(); } /** * Sets the OperationRegistry that is used by * this node. If the specified registry is null, the * default registry is used. The parameter is saved by reference. * *

      If the supplied registry does not equal the current registry, a * PropertyChangeEventJAI named "OperationRegistry" * will be fired and a RenderingChangeEvent may be * fired if the node has already been rendered. * * @param registry The new OperationRegistry to be set; * it may be null. */ public synchronized void setRegistry(OperationRegistry registry) { nodeSupport.setRegistry(registry); } /** * Returns the name of the operation this node represents as * a String. */ public synchronized String getOperationName() { return nodeSupport.getOperationName(); } /** * Sets the name of the operation this node represents. * The parameter is saved by reference. * *

      If the supplied name does not equal the current operation name, a * PropertyChangeEventJAI named "OperationName" * will be fired and a RenderingChangeEvent may be * fired if the node has already been rendered. * * @param opName The new operation name to be set. * * @throws IllegalArgumentException if opName is * null. */ public synchronized void setOperationName(String opName) { nodeSupport.setOperationName(opName); } /** Returns a clone of the ParameterBlock of this node. */ public synchronized ParameterBlock getParameterBlock() { return (ParameterBlock)nodeSupport.getParameterBlock().clone(); } /** * Sets the ParameterBlock of this node. * If the specified new ParameterBlock is null, * it is assumed that this node has no input sources and parameters. * The supplied parameter is cloned. * *

      This method does not validate the content of the supplied * ParameterBlock. The caller should ensure that * the sources and parameters in the ParameterBlock * are suitable for the operation this node represents; otherwise * some form of error or exception may occur at the time of rendering. * *

      If the supplied ParameterBlock does not equal the * current ParameterBlock, a * PropertyChangeEventJAI named "ParameterBlock", "Sources", * or "Parameters" will be fired. A RenderingChangeEvent * may also be fired if the node has already been rendered. * *

      The ParameterBlock may include * DeferredData parameters. These will not be evaluated * until their values are actually required, i.e., when the node is * rendered. * *

      The node is registered as a sink of any PlanarImage * or CollectionImage sources contained in the supplied * ParameterBlock. The node is also removed as a sink of * any previous PlanarImage or CollectionImage * sources if these are not in the new ParameterBlock. * * @param pb The new ParameterBlock to be set; * it may be null. */ public synchronized void setParameterBlock(ParameterBlock pb) { Vector nodeSources = nodeSupport.getParameterBlock().getSources(); if(nodeSources != null && nodeSources.size() > 0) { Iterator it = nodeSources.iterator(); while(it.hasNext()) { Object src = it.next(); if(src instanceof PlanarImage) { ((PlanarImage)src).removeSink(this); } else if(src instanceof CollectionImage) { ((CollectionImage)src).removeSink(this); } } } if(pb != null) { Vector newSources = pb.getSources();; if(newSources != null && newSources.size() > 0) { Iterator it = newSources.iterator(); while(it.hasNext()) { Object src = it.next(); if(src instanceof PlanarImage) { ((PlanarImage)src).addSink(this); } else if(src instanceof CollectionImage) { ((CollectionImage)src).addSink(this); } } } } nodeSupport.setParameterBlock(pb == null ? new ParameterBlock() : (ParameterBlock)pb.clone()); } /** * Returns a clone of the RenderingHints of this node or * null. */ public RenderingHints getRenderingHints() { RenderingHints hints = nodeSupport.getRenderingHints(); return hints == null ? null : (RenderingHints)hints.clone(); } /** * Sets the RenderingHints of this node. * The supplied parameter is cloned if non-null. * *

      If the supplied RenderingHints does not equal the * current RenderingHints, a * PropertyChangeEventJAI named "RenderingHints" * will be fired and a RenderingChangeEvent may be * fired if the node has already been rendered. * * @param hints The new RenderingHints to be set; * it may be null. */ public synchronized void setRenderingHints(RenderingHints hints) { if(hints != null) { hints = (RenderingHints)hints.clone(); } nodeSupport.setRenderingHints(hints); } /* ----- Rendering generation methods. ----- */ /** * Instantiate a PlanarImage that computes the result * of this RenderedOp. The local * OperationRegistry of this node is used to translate * the operation name into a RenderedImageFactory and * eventually an actual RenderedImage (usually an * OpImage). * *

      During this method, all the sources supplied in the * ParameterBlock are checked. If any of the sources * is a RenderedOp, a rendering of that source is * created. This propagates all the way up to the top of the op * chain. If any of the sources is a Collection, * then the collection is passed to the operation as-is. If there * is a RenderedOp anywhere in the collection, it is * up to the individual operation to create the rendering for that * RenderedOp. * *

      This method does not validate the sources and parameters * stored in the ParameterBlock against the specification * of the operation this node represents. It is the responsibility * of the caller to ensure that the data in the * ParameterBlock are suitable for this operation. * Otherwise, some kind of exception or error will occur. * *

      Invoking this method will cause any source RenderedOp * nodes to be rendered using getRendering() and any * source CollectionOp nodes to be rendered using * getCollection(). Any DeferredData parameters * in the ParameterBlock will also be evaluated. * *

      The RenderedImage generated by the selected * RenderedImageFactory will be converted to a * PlanarImage by invoking * PlanarImage.wrapRenderedImage(). * * @return The resulting image as a PlanarImage. * * @throws RuntimeException if the image factory charged with rendering * the node is unable to create a rendering. */ public synchronized PlanarImage createInstance() { return createInstance(false); } /** * This method performs the actions described by the documentation of * createInstance() optionally marking the node as rendered * according to the parameter. * * @throws RuntimeException if the image factory charged with rendering * the node is unable to create a rendering. * * @see #createInstance() * * @since JAI 1.1 */ protected synchronized PlanarImage createInstance(boolean isNodeRendered) { ParameterBlock pb = new ParameterBlock(); Vector parameters = nodeSupport.getParameterBlock().getParameters(); // Evaluate and DeferredData parameters. pb.setParameters(ImageUtil.evaluateParameters(parameters)); int numSources = getNumSources(); for (int i = 0; i < numSources; i++) { Object source = getNodeSource(i); Object ai = null; if (source instanceof RenderedOp) { RenderedOp src = (RenderedOp)source; ai = isNodeRendered ? src.getRendering() : src.createInstance(); } else if (source instanceof CollectionOp) { ai = ((CollectionOp)source).getCollection(); } else if ((source instanceof RenderedImage) || (source instanceof Collection)) { // XXX: RenderedImageList - bpb 8 dec 2000 // If source is a RenderedImageAdapter which is wrapping a // RenderedImageList whose primary image is a RenderedOp, // set ai to the rendering of that RenderedOp. ai = source; } else { // Source is some other type. Pass on (for now). ai = source; } pb.addSource(ai); } // Create the rendering. RenderedImage rendering = RIFRegistry.create(getRegistry(), nodeSupport.getOperationName(), pb, nodeSupport.getRenderingHints()); // Throw an exception if the rendering is null. if (rendering == null) { throw new RuntimeException(JaiI18N.getString("RenderedOp0")); } // XXX: RenderedImageList - bpb 8 dec 2000 // If rendering is a wrapped RenderedImageList whose primary image // is a RenderedOp, reset the sources of the primary image // to the source List of this node. That is to say, replace // the OpImage sources with RenderedOp sources. Also, register // this node as a PropertyChangeListener of the primary image. // Somehow this node also needs to be able to identify // RenderingChangeEvents emitted by the primary image. // The invalid region would be extracted from such RCEs and used // in creating a new RCE with this node as its source which would // be fired as usual to all listeners and sinks. // Ensure that the rendering is a PlanarImage. PlanarImage instance = PlanarImage.wrapRenderedImage(rendering); // Save the RenderingHints. oldHints = nodeSupport.getRenderingHints() == null ? null : (RenderingHints)nodeSupport.getRenderingHints().clone(); return instance; } /** * Creates a PlanarImage rendering if none exists * and sets theImage to the resulting value. This method * performs the same actions as createInstance() but sets * theImage to the result. * * @throws RuntimeException if the image factory charged with rendering * the node is unable to create a rendering. * * @see #createInstance() * * @since JAI 1.1 */ protected synchronized void createRendering() { if (theImage == null) { setImageLayout(new ImageLayout(theImage = createInstance(true))); if(theImage != null) { // Get listeners, wrap, and add to OpImage listener list. theImage.addTileComputationListener(new TCL(this)); } } } /** * Returns the PlanarImage rendering associated with this * RenderedOp node. This method performs the same action * as createRendering() but returns theImage. * * @throws RuntimeException if the image factory charged with rendering * the node is unable to create a rendering. * * @see #createRendering() * @see #createInstance() */ public PlanarImage getRendering() { createRendering(); return theImage; } /** * Returns the value of the protected variable theImage * which may be null if no rendering has yet been created. * This method does not force the node to be rendered. * * @since JAI 1.1 */ public PlanarImage getCurrentRendering() { return theImage; } /** * Forces the node to be re-rendered and returns the new rendering. * *

      If the node has not yet been rendered this method is identical to * getRendering(). * *

      If the node has already been rendered, then a new rendering will be * generated. The synthetic and locally cached properties and the property * environment of the node will all be reset. All registered * PropertyChangeListeners and any * PropertyChangeListener sinks will be notifed of the * change in the rendering via a RenderingChangeEvent * the invalid region of which will be null. * *

      This method could be used for example to trigger a * re-rendering of the node in cases where this would not happen * automatically but is desirable to the application. One * example occurs if a parameter of the operation is a referent of * some other entity which changes but the parameter itself does not * change according to equals(). This could occur for * example for an image file input operation wherein the path to the * file remains the same but the content of the file changes. * * @return The (possibly regenerated) rendering of the node. This value * may be ignored if the intent of invoking the method was merely to * re-render the node and generate events for * RenderingChangeEvent listeners. * * @since JAI 1.1 */ public PlanarImage getNewRendering() { if(theImage == null) { return getRendering(); } // Save the previous rendering. PlanarImage theOldImage = theImage; // Clear the current rendering. theImage = null; // XXX The rest of this method is effectively duplicated from the // end of propertyChange(). Should another method be created to be // called in these two places in order to avoid code duplication? // Re-render the node. createRendering(); // Clear the synthetic and cached properties and reset the // property source. resetProperties(true); // Create the event object. RenderingChangeEvent rcEvent = new RenderingChangeEvent(this, theOldImage, theImage, null); // Fire to all registered listeners. eventManager.firePropertyChange(rcEvent); // Fire an event to all PropertyChangeListener sinks. Vector sinks = getSinks(); if(sinks != null) { int numSinks = sinks.size(); for(int i = 0; i < numSinks; i++) { Object sink = sinks.get(i); if(sink instanceof PropertyChangeListener) { ((PropertyChangeListener)sink).propertyChange(rcEvent); } } } return theImage; } /* ----- PropertyChangeListener method. ----- */ /** * Implementation of PropertyChangeListener. * *

      When invoked with an event which is an instance of * RenderingChangeEvent or CollectionChangeEvent * emitted by a RenderedOp or CollectionOp, * respectively, the node will respond by re-rendering itself while * retaining any tiles possible. It will respond to an "InvalidRegion" * event emitted by a source RenderedImage in a manner * similar to that applied for RenderingChangeEvents. * * @see TiledImage#propertyChange * * @since JAI 1.1 */ public synchronized void propertyChange(PropertyChangeEvent evt) { // // React if and only if the node has been rendered and // A: a non-PropertySourceChangeEvent PropertyChangeEventJAI // was received from this node, or // B: a RenderingChangeEvent was received from a source RenderedOp, or // C: a CollectionChangeEvent was received from a source CollectionOp, or // D: an "InvalidRegion" event was received from a source RenderedImage. // // Cache event and node sources. Object evtSrc = evt.getSource(); Vector nodeSources = nodeSupport.getParameterBlock().getSources(); // Get the name of the bean property and convert it to lower // case now for efficiency later. String propName = evt.getPropertyName().toLowerCase(Locale.ENGLISH); if(theImage != null && ((evt instanceof PropertyChangeEventJAI && evtSrc == this && !(evt instanceof PropertySourceChangeEvent) && nodeEventNames.contains(propName)) || ((evt instanceof RenderingChangeEvent || evt instanceof CollectionChangeEvent || (evt instanceof PropertyChangeEventJAI && evtSrc instanceof RenderedImage && propName.equals("invalidregion"))) && nodeSources.contains(evtSrc)))) { // Save the previous rendering. PlanarImage theOldImage = theImage; // Initialize the event flag. boolean fireEvent = false; // Set default invalid region to null (the entire image). Shape invalidRegion = null; if(evtSrc == this && (propName.equals("operationname") || propName.equals("operationregistry"))) { // Operation name or OperationRegistry changed: // invalidate the entire rendering. fireEvent = true; theImage = null; } else if(evt instanceof RenderingChangeEvent || (evtSrc instanceof RenderedImage && propName.equals("invalidregion"))) { // Set the event flag. fireEvent = true; Shape srcInvalidRegion = null; if(evt instanceof RenderingChangeEvent) { // RenderingChangeEvent presumably from a source RenderedOp. RenderingChangeEvent rcEvent = (RenderingChangeEvent)evt; // Get the invalidated region of the source. srcInvalidRegion = rcEvent.getInvalidRegion(); // If entire source is invalid replace with source bounds. if(srcInvalidRegion == null) { srcInvalidRegion = ((PlanarImage)rcEvent.getOldValue()).getBounds(); } } else { // Get the invalidated region of the source. srcInvalidRegion = (Shape)evt.getNewValue(); // If entire source is invalid replace with source bounds. if(srcInvalidRegion == null) { RenderedImage rSrc = (RenderedImage)evtSrc; srcInvalidRegion = new Rectangle(rSrc.getMinX(), rSrc.getMinY(), rSrc.getWidth(), rSrc.getHeight()); } } // Only process further if the rendering is an OpImage. if(!(theImage instanceof OpImage)) { // Clear the current rendering. theImage = null; } else { // Save the previous rendering as an OpImage. OpImage oldOpImage = (OpImage)theImage; // Cache source invalid bounds. Rectangle srcInvalidBounds = srcInvalidRegion.getBounds(); // If bounds are empty, replace srcInvalidRegion with // the complement of the image bounds within the // bounds of all tiles. if(srcInvalidBounds.isEmpty()) { int x = oldOpImage.tileXToX(oldOpImage.getMinTileX()); int y = oldOpImage.tileYToY(oldOpImage.getMinTileY()); int w = oldOpImage.getNumXTiles()* oldOpImage.getTileWidth(); int h = oldOpImage.getNumYTiles()* oldOpImage.getTileHeight(); Rectangle tileBounds = new Rectangle(x, y, w, h); Rectangle imageBounds = oldOpImage.getBounds(); if(!tileBounds.equals(imageBounds)) { Area tmpArea = new Area(tileBounds); tmpArea.subtract(new Area(imageBounds)); srcInvalidRegion = tmpArea; srcInvalidBounds = srcInvalidRegion.getBounds(); } } // ----- Determine invalid destination region. ----- boolean saveAllTiles = false; ArrayList validTiles = null; if(srcInvalidBounds.isEmpty()) { invalidRegion = srcInvalidRegion; saveAllTiles = true; } else { // Get index of source which changed. int idx = nodeSources.indexOf(evtSrc); // Determine bounds of invalid destination region. Rectangle dstRegionBounds = oldOpImage.mapSourceRect(srcInvalidBounds, idx); if(dstRegionBounds == null) { dstRegionBounds = oldOpImage.getBounds(); } // Determine invalid destination region. Point[] indices = getTileIndices(dstRegionBounds); int numIndices = indices != null ? indices.length : 0; GeneralPath gp = null; for(int i = 0; i < numIndices; i++) { if (i % 1000 == 0 && gp != null) gp = new GeneralPath(new Area(gp)); Rectangle dstRect = getTileRect(indices[i].x, indices[i].y); Rectangle srcRect = oldOpImage.mapDestRect(dstRect, idx); if(srcRect == null) { gp = null; break; } if(srcInvalidRegion.intersects(srcRect)) { if(gp == null) { gp = new GeneralPath(dstRect); } else { gp.append(dstRect, false); } } else { if(validTiles == null) { validTiles = new ArrayList(); } validTiles.add(indices[i]); } } invalidRegion = (gp == null) ? null : new Area(gp); } // Clear the current rendering. theImage = null; // Retrieve the old TileCache. TileCache oldCache = oldOpImage.getTileCache(); // Only perform further processing if there is a cache // and there are tiles to save. if(oldCache != null && (saveAllTiles || validTiles != null)) { // Re-render the node. createRendering(); // Only perform further processing if the new // rendering is an OpImage with a non-null TileCache. if(theImage instanceof OpImage && ((OpImage)theImage).getTileCache() != null) { OpImage newOpImage = (OpImage)theImage; TileCache newCache = newOpImage.getTileCache(); Object tileCacheMetric = newOpImage.getTileCacheMetric(); if(saveAllTiles) { Raster[] tiles = oldCache.getTiles(oldOpImage); int numTiles = tiles == null ? 0 : tiles.length; for(int i = 0; i < numTiles; i++) { Raster tile = tiles[i]; int tx = newOpImage.XToTileX(tile.getMinX()); int ty = newOpImage.YToTileY(tile.getMinY()); newCache.add(newOpImage, tx, ty, tile, tileCacheMetric); } } else { // save some, but not all, tiles int numValidTiles = validTiles.size(); for(int i = 0; i < numValidTiles; i++) { Point tileIndex = (Point)validTiles.get(i); Raster tile = oldCache.getTile(oldOpImage, tileIndex.x, tileIndex.y); if(tile != null) { newCache.add(newOpImage, tileIndex.x, tileIndex.y, tile, tileCacheMetric); } } } } } } } else { // not op name or registry change nor RenderingChangeEvent ParameterBlock oldPB = null; ParameterBlock newPB = null; boolean checkInvalidRegion = false; if(propName.equals("parameterblock")) { oldPB = (ParameterBlock)evt.getOldValue(); newPB = (ParameterBlock)evt.getNewValue(); checkInvalidRegion = true; } else if(propName.equals("sources")) { // Replace source(s) Vector params = nodeSupport.getParameterBlock().getParameters(); oldPB = new ParameterBlock((Vector)evt.getOldValue(), params); newPB = new ParameterBlock((Vector)evt.getNewValue(), params); checkInvalidRegion = true; } else if(propName.equals("parameters")) { // Replace parameter(s) oldPB = new ParameterBlock(nodeSources, (Vector)evt.getOldValue()); newPB = new ParameterBlock(nodeSources, (Vector)evt.getNewValue()); checkInvalidRegion = true; } else if(propName.equals("renderinghints")) { oldPB = newPB = nodeSupport.getParameterBlock(); checkInvalidRegion = true; } else if(evt instanceof CollectionChangeEvent) { // Event from a CollectionOp source. // Replace appropriate source. int collectionIndex = nodeSources.indexOf(evtSrc); Vector oldSources = (Vector)nodeSources.clone(); Vector newSources = (Vector)nodeSources.clone(); oldSources.set(collectionIndex, evt.getOldValue()); newSources.set(collectionIndex, evt.getNewValue()); Vector params = nodeSupport.getParameterBlock().getParameters(); oldPB = new ParameterBlock(oldSources, params); newPB = new ParameterBlock(newSources, params); checkInvalidRegion = true; } if(checkInvalidRegion) { // Set event flag. fireEvent = true; // Get the associated OperationDescriptor. OperationRegistry registry = nodeSupport.getRegistry(); OperationDescriptor odesc = (OperationDescriptor) registry.getDescriptor(OperationDescriptor.class, nodeSupport.getOperationName()); // Evaluate any DeferredData parameters. oldPB = ImageUtil.evaluateParameters(oldPB); newPB = ImageUtil.evaluateParameters(newPB); // Determine the invalid region. invalidRegion = (Shape) odesc.getInvalidRegion(RenderedRegistryMode.MODE_NAME, oldPB, oldHints, newPB, nodeSupport.getRenderingHints(), this); if(invalidRegion == null || !(theImage instanceof OpImage)) { // Can't save any tiles; clear the rendering. theImage = null; } else { // Create a new rendering. OpImage oldRendering = (OpImage)theImage; theImage = null; createRendering(); // If the new rendering is also an OpImage, // save some tiles. if(theImage instanceof OpImage && oldRendering.getTileCache() != null && ((OpImage)theImage).getTileCache() != null) { OpImage newRendering = (OpImage)theImage; // Save some values. TileCache oldCache = oldRendering.getTileCache(); TileCache newCache = newRendering.getTileCache(); Object tileCacheMetric = newRendering.getTileCacheMetric(); // If bounds are empty, replace invalidRegion with // the complement of the image bounds within the // bounds of all tiles. if(invalidRegion.getBounds().isEmpty()) { int x = oldRendering.tileXToX( oldRendering.getMinTileX()); int y = oldRendering.tileYToY( oldRendering.getMinTileY()); int w = oldRendering.getNumXTiles()* oldRendering.getTileWidth(); int h = oldRendering.getNumYTiles()* oldRendering.getTileHeight(); Rectangle tileBounds = new Rectangle(x, y, w, h); Rectangle imageBounds = oldRendering.getBounds(); if(!tileBounds.equals(imageBounds)) { Area tmpArea = new Area(tileBounds); tmpArea.subtract(new Area(imageBounds)); invalidRegion = tmpArea; } } if(invalidRegion.getBounds().isEmpty()) { // Save all tiles. Raster[] tiles = oldCache.getTiles(oldRendering); int numTiles = tiles == null ? 0 : tiles.length; for(int i = 0; i < numTiles; i++) { Raster tile = tiles[i]; int tx = newRendering.XToTileX(tile.getMinX()); int ty = newRendering.YToTileY(tile.getMinY()); newCache.add(newRendering, tx, ty, tile, tileCacheMetric); } } else { // Copy tiles not in invalid region from old // TileCache to new TileCache. Raster[] tiles = oldCache.getTiles(oldRendering); int numTiles = tiles == null ? 0 : tiles.length; for(int i = 0; i < numTiles; i++) { Raster tile = tiles[i]; Rectangle bounds = tile.getBounds(); if(!invalidRegion.intersects(bounds)) { newCache.add(newRendering, newRendering.XToTileX(bounds.x), newRendering.YToTileY(bounds.y), tile, tileCacheMetric); } } } } } } } // Re-render the node. This will only occur if theImage // has been set to null above. createRendering(); // Fire an event if the flag was set. if(fireEvent) { // Clear the synthetic and cached properties and reset the // property source. resetProperties(true); // Create the event object. RenderingChangeEvent rcEvent = new RenderingChangeEvent(this, theOldImage, theImage, invalidRegion); // Fire to all registered listeners. eventManager.firePropertyChange(rcEvent); // Fire an event to all PropertyChangeListener sinks. Vector sinks = getSinks(); if(sinks != null) { int numSinks = sinks.size(); for(int i = 0; i < numSinks; i++) { Object sink = sinks.get(i); if(sink instanceof PropertyChangeListener) { ((PropertyChangeListener)sink).propertyChange(rcEvent); } } } } } } /* ----- Node source methods: interact with ParameterBlock sources ----- */ /** * Adds a source to the ParameterBlock of this node. * This is a convenience method that invokes * setParameterBlock() and so adheres to the same event * firing behavior. * * @param source The source to be added to the * ParameterBlock * @deprecated as of JAI 1.1 Use addSource(Object). */ public synchronized void addNodeSource(Object source) { addSource(source); } /** * Sets the specified source stored in the ParameterBlock * of this node to a new source object. * This is a convenience method that invokes * setParameterBlock() and so adheres to the same event * firing behavior. * * @param source The Source to be set. * @param index The Index at which it is to be set. * * @throws IllegalArgumentException if * source is null. * @throws ArrayIndexOutOfBoundsException if * index is invalid. * @deprecated as of JAI 1.1 Use setSource(Object,int). */ public synchronized void setNodeSource(Object source, int index) { setSource(source, index); } /** * Returns the specified source stored in the * ParameterBlock of this node. * If there is no source corresponding to the specified index, an * ArrayIndexOutOfBoundsException will be thrown. * * @param index The index of the source. * @deprecated as of JAI 1.1 Use getSourceObject(int). */ public synchronized Object getNodeSource(int index) { return nodeSupport.getParameterBlock().getSource(index); } /* ----- Parameter methods: interact with ParameterBlock params ----- */ /** * Returns the number of parameters stored in the * ParameterBlock of this node. */ public synchronized int getNumParameters() { return nodeSupport.getParameterBlock().getNumParameters(); } /** * Returns a clone of the Vector of parameters stored in the * ParameterBlock of this node. */ public synchronized Vector getParameters() { // In the Sun JDK ParameterBlock the parameter Vector is never null. Vector params = nodeSupport.getParameterBlock().getParameters(); return params == null ? null : (Vector)params.clone(); } /** * Returns the specified parameter stored in the * ParameterBlock of this node as a byte. * An ArrayIndexOutOfBoundsException may occur if an * invalid index is supplied * * @param index The index of the parameter. * * @throws ArrayIndexOutOfBoundsException if * index is invalid. */ public synchronized byte getByteParameter(int index) { return nodeSupport.getParameterBlock().getByteParameter(index); } /** * Returns the specified parameter stored in the * ParameterBlock of this node as a char. * An ArrayIndexOutOfBoundsException may occur if an * invalid index is supplied * * @param index The index of the parameter. * * @throws ArrayIndexOutOfBoundsException if * index is invalid. */ public synchronized char getCharParameter(int index) { return nodeSupport.getParameterBlock().getCharParameter(index); } /** * Returns the specified parameter stored in the * ParameterBlock of this node as a short. * An ArrayIndexOutOfBoundsException may occur if an * invalid index is supplied * * @param index The index of the parameter. * * @throws ArrayIndexOutOfBoundsException if * index is invalid. */ public synchronized short getShortParameter(int index) { return nodeSupport.getParameterBlock().getShortParameter(index); } /** * Returns the specified parameter stored in the * ParameterBlock of this node as an int. * An ArrayIndexOutOfBoundsException may occur if an * invalid index is supplied * * @param index The index of the parameter. * * @throws ArrayIndexOutOfBoundsException if * index is invalid. */ public synchronized int getIntParameter(int index) { return nodeSupport.getParameterBlock().getIntParameter(index); } /** * Returns the specified parameter stored in the * ParameterBlock of this node as a long. * An ArrayIndexOutOfBoundsException may occur if an * invalid index is supplied * * @param index The index of the parameter. * * @throws ArrayIndexOutOfBoundsException if * index is invalid. */ public synchronized long getLongParameter(int index) { return nodeSupport.getParameterBlock().getLongParameter(index); } /** * Returns the specified parameter stored in the * ParameterBlock of this node as a float. * An ArrayIndexOutOfBoundsException may occur if an * invalid index is supplied * * @param index The index of the parameter. * * @throws ArrayIndexOutOfBoundsException if * index is invalid. */ public synchronized float getFloatParameter(int index) { return nodeSupport.getParameterBlock().getFloatParameter(index); } /** * Returns the specified parameter stored in the * ParameterBlock of this node as a double. * An ArrayIndexOutOfBoundsException may occur if an * invalid index is supplied * * @param index The index of the parameter. * * @throws ArrayIndexOutOfBoundsException if * index is invalid. */ public synchronized double getDoubleParameter(int index) { return nodeSupport.getParameterBlock().getDoubleParameter(index); } /** * Returns the specified parameter stored in the * ParameterBlock of this node as an Object. * An ArrayIndexOutOfBoundsException may occur if an * invalid index is supplied * * @param index The index of the parameter. * * @throws ArrayIndexOutOfBoundsException if * index is invalid. */ public synchronized Object getObjectParameter(int index) { return nodeSupport.getParameterBlock().getObjectParameter(index); } /** * Sets all the parameters of this node. * This is a convenience method that invokes * setParameterBlock() and so adheres to the same event * firing behavior. * *

      The Vector may include * DeferredData parameters. These will not be evaluated * until their values are actually required, i.e., when the node is * rendered. * * @since JAI 1.1 */ public synchronized void setParameters(Vector parameters) { ParameterBlock pb = (ParameterBlock)nodeSupport.getParameterBlock().clone(); pb.setParameters(parameters); nodeSupport.setParameterBlock(pb); } /** * Sets one of the node's parameters to a byte. * If the index lies beyond the current source list, * the list is extended with nulls as needed. * This is a convenience method that invokes * setParameter(Object,int) and so adheres to the same event * firing behavior. * * @param param The parameter, as a byte. * @param index The index of the parameter. */ public synchronized void setParameter(byte param, int index) { setParameter(new Byte(param), index); } /** * Sets one of the node's parameters to a char. * If the index lies beyond the current source list, * the list is extended with nulls as needed. * This is a convenience method that invokes * setParameter(Object,int) and so adheres to the same event * firing behavior. * * @param param The parameter, as a char. * @param index The index of the parameter. */ public synchronized void setParameter(char param, int index) { setParameter(new Character(param), index); } /** * Sets one of the node's parameters to a short. * If the index lies beyond the current source list, * the list is extended with nulls as needed. * * @param param The parameter, as a short. * @param index The index of the parameter. */ public synchronized void setParameter(short param, int index) { setParameter(new Short(param), index); } /** * Sets one of the node's parameters to an int. * If the index lies beyond the current source list, * the list is extended with nulls as needed. * * @param param The parameter, as an int. * @param index The index of the parameter. */ public synchronized void setParameter(int param, int index) { setParameter(new Integer(param), index); } /** * Sets one of the node's parameters to a long. * If the index lies beyond the current source list, * the list is extended with nulls as needed. * * @param param The parameter, as a long. * @param index The index of the parameter. */ public synchronized void setParameter(long param, int index) { setParameter(new Long(param), index); } /** * Sets one of the node's parameters to a float. * If the index lies beyond the current source list, * the list is extended with nulls as needed. * * @param param The parameter, as a float. * @param index The index of the parameter. */ public synchronized void setParameter(float param, int index) { setParameter(new Float(param), index); } /** * Sets one of the node's parameters to a double. * If the index lies beyond the current source list, * the list is extended with nulls as needed. * * @param param The parameter, as a double. * @param index The index of the parameter. */ public synchronized void setParameter(double param, int index) { setParameter(new Double(param), index); } /** * Sets one of the node's parameters to an Object. * If the index lies beyond the current source list, * the list is extended with nulls as needed. * This is a convenience method that invokes * setParameterBlock() and so adheres to the same event * firing behavior. * *

      The Object may be a * DeferredData instance. It will not be evaluated * until its value is actually required, i.e., when the node is * rendered. * * @param param The parameter, as an Object. * @param index The index of the parameter. */ public synchronized void setParameter(Object param, int index) { ParameterBlock pb = (ParameterBlock)nodeSupport.getParameterBlock().clone(); pb.set(param, index); nodeSupport.setParameterBlock(pb); } /* ----- RenderingHints methods. ----- */ /** * Sets a hint in the RenderingHints of this node. This * is a convenience method which calls setRenderingHints() * and so adheres to the same event firing behavior. * * @throws IllegalArgumentException * if the key or value is null. * @throws IllegalArgumentException * value is not appropriate for the specified key. * * @since JAI 1.1 */ public synchronized void setRenderingHint(RenderingHints.Key key, Object value) { if ( key == null || value == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } RenderingHints rh = nodeSupport.getRenderingHints(); if(rh == null) { nodeSupport.setRenderingHints(new RenderingHints(key, value)); } else { rh.put(key, value); nodeSupport.setRenderingHints(rh); } } /** * Gets a hint from the RenderingHints of this node. * * @return the value associated with the specified key or * null if the key is not mapped to any value. * * @since JAI 1.1 */ public synchronized Object getRenderingHint(RenderingHints.Key key) { RenderingHints rh = nodeSupport.getRenderingHints(); return rh == null ? null : rh.get(key); } /* ----- Property-related methods. ----- */ /** Creates a PropertySource if none exists. */ private synchronized void createPropertySource() { if (thePropertySource == null) { // Create a PropertySource wrapper of the rendering. PropertySource defaultPS = new PropertySource() { /** * Retrieve the names from an instance of the node. */ public String[] getPropertyNames() { return getRendering().getPropertyNames(); } public String[] getPropertyNames(String prefix) { return PropertyUtil.getPropertyNames( getPropertyNames(), prefix); } public Class getPropertyClass(String name) { return null; } /** * Retrieve the actual property values from a rendering * of the node. */ public Object getProperty(String name) { return getRendering().getProperty(name); } }; // Create a PropertySource encapsulating the // property environment of the node. thePropertySource = nodeSupport.getPropertySource(this, defaultPS); // Add the PropertySource to the helper object. properties.addProperties(thePropertySource); } } /** * Resets the PropertySource. If the parameter is * true then the property environment is completely * reset; if false then only cached properties are * cleared, i.e., those which were derived from the property * environment and are now stored in the local cache. * * @since JAI 1.1 */ protected synchronized void resetProperties(boolean resetPropertySource) { properties.clearCachedProperties(); if (resetPropertySource && thePropertySource != null) { synthProperties = null; properties.removePropertySource(thePropertySource); thePropertySource = null; } } /** * Returns the names of properties available from this node. * These properties are a combination of those derived * from prior nodes in the imaging chain, those set locally, * and a number of locally derived, immutable properties * based on the rendering associated with this node -- * height, width, and so forth. * * @return An array of Strings containing valid * property names. */ public synchronized String[] getPropertyNames() { createPropertySource(); // Initialize names to synthetic property names. Vector names = new Vector(synthProps); // Create a dummy key for later use. CaselessStringKey key = new CaselessStringKey(""); // Get property names managed by WritablePropertySourceImpl. // This includes those of thePropertySource. String[] localNames = properties.getPropertyNames(); if(localNames != null) { int length = localNames.length; for(int i = 0; i < length; i++) { key.setName(localNames[i]); // Check for duplicates being inserted if (!names.contains(key)) { names.add(key.clone()); } } } // Return an array. String[] propertyNames = null; int numNames = names.size(); if(numNames > 0) { propertyNames = new String[numNames]; for(int i = 0; i < numNames; i++) { propertyNames[i] = ((CaselessStringKey)names.get(i)).getName(); } } return propertyNames; } /** * Returns the class expected to be returned by a request for * the property with the specified name. If this information * is unavailable, null will be returned. * * @exception IllegalArgumentException if name * is null. * * @return The Class expected to be return by a * request for the value of this property or null. * * @since JAI 1.1 */ public Class getPropertyClass(String name) { createPropertySource(); return properties.getPropertyClass(name); } /** Initialize the synthProperties Hashtable if needed. */ private synchronized void createSynthProperties() { if (synthProperties == null) { synthProperties = new Hashtable(); synthProperties.put(new CaselessStringKey("image_width"), new Integer(theImage.getWidth())); synthProperties.put(new CaselessStringKey("image_height"), new Integer(theImage.getHeight())); synthProperties.put(new CaselessStringKey("image_min_x_coord"), new Integer(theImage.getMinX())); synthProperties.put(new CaselessStringKey("image_min_y_coord"), new Integer(theImage.getMinY())); if(theImage instanceof OpImage) { synthProperties.put(new CaselessStringKey("tile_cache_key"), theImage); Object tileCache = ((OpImage)theImage).getTileCache(); synthProperties.put(new CaselessStringKey("tile_cache"), tileCache == null ? java.awt.Image.UndefinedProperty : tileCache); } else if(theImage instanceof PlanarImageServerProxy) { synthProperties.put(new CaselessStringKey("tile_cache_key"), theImage); Object tileCache = ((PlanarImageServerProxy)theImage).getTileCache(); synthProperties.put(new CaselessStringKey("tile_cache"), tileCache == null ? java.awt.Image.UndefinedProperty : tileCache); } else { Object tileCacheKey = theImage.getProperty("tile_cache_key"); synthProperties.put(new CaselessStringKey("tile_cache_key"), tileCacheKey == null ? java.awt.Image.UndefinedProperty : tileCacheKey); Object tileCache = theImage.getProperty("tile_cache"); synthProperties.put(new CaselessStringKey("tile_cache"), tileCache == null ? java.awt.Image.UndefinedProperty : tileCache); } } } /** * Returns the property associated with the specified property name, * or java.awt.Image.UndefinedProperty if the specified * property is not set on the image. If name equals the * name of any synthetic property, i.e., image_width, * image_height, image_min_x_coord, or * image_min_y_coord, then the node will be rendered. * * @param name A String naming the property. * * @throws IllegalArgumentException if * name is null. */ public synchronized Object getProperty(String name) { if (name == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); createPropertySource(); CaselessStringKey key = new CaselessStringKey(name); // Attempt to retrieve from synthetic properties. // If present, return the value directly. if (synthProps.contains(key)) { createRendering(); // Create synthProperties Hashtable "just in time." createSynthProperties(); return synthProperties.get(key); } // Attempt to retrieve from local properties. Object value = properties.getProperty(name); // If still undefined, query the property environment. if(value == java.awt.Image.UndefinedProperty) { value = thePropertySource.getProperty(name); } // Special case handling of ROI property: clip to destination bounds. // XXX Do we really want to do this (clip ROI to dest bounds)? if(value != java.awt.Image.UndefinedProperty && name.equalsIgnoreCase("roi") && value instanceof ROI) { ROI roi = (ROI)value; Rectangle imageBounds = getBounds(); if(!imageBounds.contains(roi.getBounds())) { value = roi.intersect(new ROIShape(imageBounds)); } } return value; } /** * Sets a local property on a node. The synthetic properties * (containing image width, height, and location) may not be set. * Local property settings override properties derived from prior * nodes in the imaging chain. * *

      If the node is serialized then serializable properties will * also be serialized but non-serializable properties will be lost. * * @param name A String representing the property name. * @param value The property's value, as an Object. * * @throws IllegalArgumentException if * name is null. * @throws IllegalArgumentException if * value is null. * @throws RuntimeException if name * conflicts with Synthetic property. */ public synchronized void setProperty(String name, Object value) { if (name == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); if (value == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); // Check whether property conflicts with synthetic properties. if (synthProps.contains(new CaselessStringKey(name))) { throw new RuntimeException(JaiI18N.getString("RenderedOp4")); } createPropertySource(); super.setProperty(name, value); } /** * Removes the named property from the local property * set of the RenderedOp as well as from its property * environment. The synthetic properties * (containing image width, height, and position) may not be removed. * * @exception IllegalArgumentException if name * is null. * @throws RuntimeException if name * conflicts with Synthetic property. * * @since JAI 1.1 */ public void removeProperty(String name) { if (name == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); // Check whether property conflicts with synthetic properties. if (synthProps.contains(new CaselessStringKey(name))) { throw new RuntimeException(JaiI18N.getString("RenderedOp4")); } createPropertySource(); properties.removeProperty(name); } /** * Returns the property associated with the specified property name, * or java.awt.Image.UndefinedProperty if the specified * property is not set on the image. This method is dynamic in the * sense that subsequent invocations of this method on the same object * may return different values as a function of changes in the property * environment of the node, e.g., a change in which * PropertyGenerators are registered or in the values * associated with properties of node sources. The case of the property * name passed to this method is ignored. * * @param name A String naming the property. * * @throws IllegalArgumentException if * name is null. * * @since JAI 1.1 */ public synchronized Object getDynamicProperty(String name) { createPropertySource(); return thePropertySource.getProperty(name); } /** * Adds a PropertyGenerator to the node. The property values * emitted by this property generator override any previous * definitions. * * @param pg A PropertyGenerator to be added to this node's * property environment. * * @throws IllegalArgumentException if * pg is null. */ public synchronized void addPropertyGenerator(PropertyGenerator pg) { nodeSupport.addPropertyGenerator(pg); } /** * Forces a property to be copied from the specified source node. * By default, a property is copied from the first source node * that emits it. The result of specifying an invalid source is * undefined. * * @param propertyName the name of the property to be copied. * @param sourceIndex the index of the from which to copy the property. * @throws IllegalArgumentException if propertyName is * null. * * @since JAI 1.1 */ public synchronized void copyPropertyFromSource(String propertyName, int sourceIndex) { nodeSupport.copyPropertyFromSource(propertyName, sourceIndex); } /** * Removes a named property from the property environment of this * node. Unless the property is stored locally either due * to having been set explicitly via setProperty() * or to having been cached for property * synchronization purposes, subsequent calls to * getProperty(name) will return * java.awt.Image.UndefinedProperty, and name * will not appear on the list of properties emitted by * getPropertyNames(). To delete the property from the * local property set of the node, removeProperty() should * be used. * * @param name A String naming the property to be suppressed. * * @throws IllegalArgumentException if * name is null. * @throws IllegalArgumentException if name * conflicts with Synthetic property. */ public synchronized void suppressProperty(String name) { if (name == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); if (synthProps.contains(new CaselessStringKey(name))) { throw new IllegalArgumentException(JaiI18N.getString("RenderedOp5")); } nodeSupport.suppressProperty(name); } /***************************************************************** * The following methods override public or protected methods in * * PlanarImage thus causing this node to be rendered. * ****************************************************************/ /** * Renders the node if it has not already been rendered, and returns * the X coordinate of the leftmost column of the rendered image. */ public int getMinX() { createRendering(); return theImage.getMinX(); } /** * Renders the node if it has not already been rendered, and returns * the X coordinate of the uppermost row of the rendered image. */ public int getMinY() { createRendering(); return theImage.getMinY(); } /** * Renders the node if it has not already been rendered, * and returns the width of the rendered image. */ public int getWidth() { createRendering(); return theImage.getWidth(); } /** * Renders the node if it has not already been rendered, * and returns the height of the rendered image. */ public int getHeight() { createRendering(); return theImage.getHeight(); } /** * Renders the node if it has not already been rendered, * and returns the tile width of the rendered image. */ public int getTileWidth() { createRendering(); return theImage.getTileWidth(); } /** * Renders the node if it has not already been rendered, * and returns the tile height of the rendered image. */ public int getTileHeight() { createRendering(); return theImage.getTileHeight(); } /** * Renders the node if it has not already been rendered, * and returns the tile grid X offset of the rendered image. */ public int getTileGridXOffset() { createRendering(); return theImage.getTileGridXOffset(); } /** * Renders the node if it has not already been rendered, * and returns the tile grid Y offset of the rendered image. */ public int getTileGridYOffset() { createRendering(); return theImage.getTileGridYOffset(); } /** * Renders the node if it has not already been rendered, and * returns the SampleModel of the rendered image. */ public SampleModel getSampleModel() { createRendering(); return theImage.getSampleModel(); } /** * Renders the node if it has not already been rendered, and * returns the ColorModel of the rendered image. */ public ColorModel getColorModel() { createRendering(); return theImage.getColorModel(); } /** * Renders the node if it has not already been rendered, * and returns the specified tile of the rendered image. * * @param tileX The X index of the tile. * @param tileY The Y index of the tile. * * @return The requested tile as a Raster. */ public Raster getTile(int tileX, int tileY) { createRendering(); return theImage.getTile(tileX, tileY); } /** * Renders the node if it has not already been rendered, and * returns the entire rendered image as a Raster. */ public Raster getData() { createRendering(); return theImage.getData(); } /** * Renders the node if it has not already been rendered, and * returns a specified rectangular region of the rendered * image as a Raster. */ public Raster getData(Rectangle rect) { createRendering(); return theImage.getData(rect); } /** * Renders the node if it has not already been rendered, and * copies and returns the entire rendered image into a single raster. */ public WritableRaster copyData() { createRendering(); return theImage.copyData(); } /** * Renders the node if it has not already been rendered, and * copies a specified rectangle of the rendered image into * the given WritableRaster. * * @param raster A WritableRaster to be filled with image data. * @return A reference to the supplied WritableRaster. * */ public WritableRaster copyData(WritableRaster raster) { createRendering(); return theImage.copyData(raster); } /** * Renders the node if it has not already been rendered, and * returns the tiles indicated by the tileIndices * of the rendered image as an array of Rasters. * * @param tileIndices An array of Points representing TileIndices. * @return An array of Raster containing the tiles corresponding * to the given TileIndices. * * @throws IllegalArgumentException If tileIndices is * null. */ public Raster[] getTiles(Point tileIndices[]) { createRendering(); return theImage.getTiles(tileIndices); } /** * Queues a list of tiles for computation. Registered listeners * will be notified after each tile has been computed. The event * source parameter passed to such listeners will be the node itself; * the image parameter will be the rendering of the node. The * RenderedOp itself in fact should monitor any * TileComputationListener events of its rendering and * forward any such events to any of its registered listeners. * * @param tileIndices A list of tile indices indicating which tiles * to schedule for computation. * @throws IllegalArgumentException If tileIndices is * null. * * @since JAI 1.1 */ public TileRequest queueTiles(Point[] tileIndices) { createRendering(); return theImage.queueTiles(tileIndices); } /** * Issue an advisory cancellation request to nullify processing of * the indicated tiles. * * @param request The request for which tiles are to be cancelled. * @param tileIndices The tiles to be cancelled; may be null. * Any tiles not actually in the TileRequest will be * ignored. * @throws IllegalArgumentException If request is * null. * * @since JAI 1.1 */ public void cancelTiles(TileRequest request, Point[] tileIndices) { if (request == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic4")); } createRendering(); theImage.cancelTiles(request, tileIndices); } /** * Renders the node if it has not already been rendered. * Hints that the given tiles of the rendered image might be * needed in the near future. * * @param tileIndices A list of tileIndices indicating which tiles * to prefetch. * * @throws IllegalArgumentException If tileIndices is * null. */ public void prefetchTiles(Point tileIndices[]) { createRendering(); theImage.prefetchTiles(tileIndices); } // ----- Methods dealing with source Vector: forward call to ParamBlock. /** * Adds a PlanarImage source to the * ParameterBlock of this node. * This is a convenience method that invokes * setParameterBlock() and so adheres to the same event * firing behavior. * *

      Note that the behavior of this method has changed as of * Java Advanced Imaging 1.1. To obtain the previous behavior use * getRendering().addSource(). The description of the * previous behavior is as follows: *

      * * Renders the node if it has not already been rendered, and * adds a PlanarImage source to the list of sources * of the rendered image. * *
      * * * @param source The source to be added to the * ParameterBlock * * @throws IllegalArgumentException if source is * null. * * @deprecated as of JAI 1.1. Use addSource(Object). */ public synchronized void addSource(PlanarImage source) { Object sourceObject = source; addSource(sourceObject); } /** * Sets the specified source stored in the ParameterBlock * of this node to a new PlanarImage source. * This is a convenience method that invokes * setParameterBlock() and so adheres to the same event * firing behavior. * *

      Note that the behavior of this method has changed as of * Java Advanced Imaging 1.1. To obtain the previous behavior use * getRendering().setSource(). The description of the * previous behavior is as follows: *

      * * Renders the node if it has not already been rendered, and * sets the specified source of the rendered image to the * supplied PlanarImage. * An ArrayIndexOutOfBoundsException may be thrown if * an invalid index is supplied. * *
      * * * @param source The source, as a PlanarImage. * @param index The index of the source. * @throws IllegalArgumentException if source is * null. * @throws ArrayIndexOutOfBoundsException if * index is invalid. * * @deprecated as of JAI 1.1. Use setSource(Object, int). */ public synchronized void setSource(PlanarImage source, int index) { Object sourceObject = source; setSource(sourceObject, index); } /** * Returns the specified PlanarImage source stored in the * ParameterBlock of this node. * If there is no source corresponding to the specified index, an * ArrayIndexOutOfBoundsException will be thrown. * *

      Note that the behavior of this method has changed as of * Java Advanced Imaging 1.1. To obtain the previous behavior use * getRendering().getSource(). The description of the * previous behavior is as follows: *

      * * Renders the node if it has not already been rendered, and * returns the specified PlanarImage source of * the rendered image. If there is no source corresponding to * the specified index, this method will throw an * ArrayIndexOutOfBoundsException. * The source returned may differ from the source stored in * the ParameterBlock of this node. * *
      * * * @param index The index of the desired source. * @return A PlanarImage source. * @throws ArrayIndexOutOfBoundsException if * index is invalid. * @throws ClassCastException if the source at the indicated index is * not a PlanarImage. * * @deprecated as of JAI 1.1. Use getSourceObject(). */ public PlanarImage getSource(int index) { return (PlanarImage)nodeSupport.getParameterBlock().getSource(index); } /** * Removes the specified PlanarImage source from the * ParameterBlock of this node. * *

      Note that the behavior of this method has changed as of * Java Advanced Imaging 1.1. To obtain the previous behavior use * getRendering().removeSource(). The description of the * previous behavior is as follows: *

      * * Renders the node if it has not already been rendered, and * removes a PlanarImage source from the list * of sources of the rendered image. * *
      * * * @param source A PlanarImage to be removed. * * @throws IllegalArgumentException if * source is null. * @return true if the element was present, false * otherwise. * * @deprecated as of JAI 1.1. Use removeSource(Object). */ public synchronized boolean removeSource(PlanarImage source) { Object sourceObject = source; return removeSource(sourceObject); } /** * Adds a source to the ParameterBlock of this node. * This is a convenience method that invokes * setParameterBlock() and so adheres to the same event * firing behavior. * *

      The node is added automatically as a sink if the source is a * PlanarImage or a CollectionImage. * * @param source The source to be added to the * ParameterBlock * @throws IllegalArgumentException if * source is null. * * @since JAI 1.1 */ public synchronized void addSource(Object source) { if (source == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); ParameterBlock pb = (ParameterBlock)nodeSupport.getParameterBlock().clone(); pb.addSource(source); nodeSupport.setParameterBlock(pb); if(source instanceof PlanarImage) { ((PlanarImage)source).addSink(this); } else if(source instanceof CollectionImage) { ((CollectionImage)source).addSink(this); } } /** * Sets the specified source stored in the ParameterBlock * of this node to a new source object. * This is a convenience method that invokes * setParameterBlock() and so adheres to the same event * firing behavior. * *

      The node is added automatically as a sink if the source is a * PlanarImage or a CollectionImage. If * appropriate the node is removed as a sink of any previous source * at the same index. * * @param source The Source to be set. * @param index The Index at which it is to be set. * * @throws IllegalArgumentException if * source is null. * @throws ArrayIndexOutOfBoundsException if * index is invalid. * * @since JAI 1.1 */ public synchronized void setSource(Object source, int index) { if (source == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); ParameterBlock pb = (ParameterBlock)nodeSupport.getParameterBlock().clone(); if(index < pb.getNumSources()) { Object priorSource = pb.getSource(index); if(priorSource instanceof PlanarImage) { ((PlanarImage)priorSource).removeSink(this); } else if(priorSource instanceof CollectionImage) { ((CollectionImage)priorSource).removeSink(this); } } pb.setSource(source, index); nodeSupport.setParameterBlock(pb); if(source instanceof PlanarImage) { ((PlanarImage)source).addSink(this); } else if(source instanceof CollectionImage) { ((CollectionImage)source).addSink(this); } } /** * Removes the specified Object source from the * ParameterBlock of this node. * *

      The node is removed automatically as a sink if the source is a * PlanarImage or a CollectionImage. * * @param source A Object to be removed. * * @throws IllegalArgumentException if * source is null. * @return true if the element was present, false * otherwise. * * @since JAI 1.1 */ public synchronized boolean removeSource(Object source) { if (source == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } ParameterBlock pb = (ParameterBlock)nodeSupport.getParameterBlock().clone(); Vector nodeSources = pb.getSources(); if(nodeSources.contains(source)) { if(source instanceof PlanarImage) { ((PlanarImage)source).removeSink(this); } else if(source instanceof CollectionImage) { ((CollectionImage)source).removeSink(this); } } boolean result = nodeSources.remove(source); nodeSupport.setParameterBlock(pb); return result; } /** * Returns the specified PlanarImage source stored in the * ParameterBlock of this node. * If there is no source corresponding to the specified index, an * ArrayIndexOutOfBoundsException will be thrown. * * @param index The index of the desired source. * @return A PlanarImage source. * @throws ArrayIndexOutOfBoundsException if * index is invalid. * @throws ClassCastException if the source at the indicated index is * not a PlanarImage. * * @since JAI 1.1 */ public PlanarImage getSourceImage(int index) { return (PlanarImage)nodeSupport.getParameterBlock().getSource(index); } /** * Returns the specified source stored in the * ParameterBlock of this node. * If there is no source corresponding to the specified index, an * ArrayIndexOutOfBoundsException will be thrown. * * @param index The index of the source. * @throws ArrayIndexOutOfBoundsException if * index is invalid. * * @since JAI 1.1 */ public synchronized Object getSourceObject(int index) { return nodeSupport.getParameterBlock().getSource(index); } /** * Returns the number of sources stored in the * ParameterBlock of this node. * This may differ from the number of sources of the rendered image. */ public int getNumSources() { // This method must return the number of sources of this node, // not the number of sources of the rendered image. Otherwise, // it'll cause exception in some cases. return nodeSupport.getParameterBlock().getNumSources(); } /** * Returns a clone of the Vector of sources stored in the * ParameterBlock of this node. * This may differ from the source vector of the rendering of the node. */ public synchronized Vector getSources() { Vector srcs = nodeSupport.getParameterBlock().getSources(); return srcs == null ? null : (Vector)srcs.clone(); } /** * Replaces the sources in the ParameterBlock of this node * with a new list of sources. * This is a convenience method that invokes * setParameterBlock() and so adheres to the same event * firing behavior. * *

      The node is added automatically as a sink of any source which is a * PlanarImage or a CollectionImage. It is * also automatically removed as a sink of any such prior sources which * are no longer sources. * * @param sourceList A List of sources. * * @throws IllegalArgumentException if * sourceList is null. */ public synchronized void setSources(List sourceList) { if (sourceList == null) throw new IllegalArgumentException( JaiI18N.getString("Generic0")); ParameterBlock pb = (ParameterBlock)nodeSupport.getParameterBlock().clone(); Iterator it = pb.getSources().iterator(); while(it.hasNext()) { Object priorSource = it.next(); if(!sourceList.contains(priorSource)) { if(priorSource instanceof PlanarImage) { ((PlanarImage)priorSource).removeSink(this); } else if(priorSource instanceof CollectionImage) { ((CollectionImage)priorSource).removeSink(this); } } } pb.removeSources(); int size = sourceList.size(); for (int i = 0; i < size; i++) { Object src = sourceList.get(i); pb.addSource(src); if(src instanceof PlanarImage) { ((PlanarImage)src).addSink(this); } else if(src instanceof CollectionImage) { ((CollectionImage)src).addSink(this); } } nodeSupport.setParameterBlock(pb); } /** * Removes all the sources stored in the * ParameterBlock of this node. * This is a convenience method that invokes * setParameterBlock() and so adheres to the same event * firing behavior. * *

      The node is removed automatically as a sink of any source which * is a PlanarImage or a CollectionImage. */ public synchronized void removeSources() { ParameterBlock pb = (ParameterBlock)nodeSupport.getParameterBlock().clone(); Iterator it = pb.getSources().iterator(); while(it.hasNext()) { Object priorSource = it.next(); if(priorSource instanceof PlanarImage) { ((PlanarImage)priorSource).removeSink(this); } else if(priorSource instanceof CollectionImage) { ((CollectionImage)priorSource).removeSink(this); } it.remove(); } nodeSupport.setParameterBlock(pb); } // ----- Methods dealing with sinks Vector. /** * Adds a PlanarImage sink to the list of sinks of the node. * *

      Note that the behavior of this method has changed as of * Java Advanced Imaging 1.1. To obtain the previous behavior use * getRendering().addSink(). The description of the * previous behavior is as follows: *

      * * Renders the node if it has not already been rendered, and * adds a PlanarImage sink to the list of sinks * of the rendered image. * *
      * *

      Note also that this class no longer overrides * getSinks(). To obtain the previous behavior of * getSinks() use getRendering().getSinks(). * * * @throws IllegalArgumentException if * sink is null. */ public synchronized void addSink(PlanarImage sink) { if (sink == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } super.addSink(sink); } /** * Removes a PlanarImage sink from the list of sinks of * the node. * *

      Note that the behavior of this method has changed as of * Java Advanced Imaging 1.1. To obtain the previous behavior use * getRendering().removeSink(). The description of the * previous behavior is as follows: *

      * * Renders the node if it has not already been rendered, and * removes a PlanarImage sink from the list of sinks * of the rendered image. * *
      * *

      Note also that this class no longer overrides * getSinks(). To obtain the previous behavior of * getSinks() use getRendering().getSinks(). * * * @throws IllegalArgumentException if * sink is null. */ public synchronized boolean removeSink(PlanarImage sink) { if (sink == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return super.removeSink(sink); } /** * Removes all sinks from the list of sinks of the node. * * @since JAI 1.1 */ public void removeSinks() { super.removeSinks(); } /** * Adds a sink to the list of node sinks. If the sink is an * instance of PropertyChangeListener it will be * notified in the same manner as registered listeners for the * changes to the "Rendering" property of this node as long as its * WeakReference has not yet been cleared. * * @throws IllegalArgumentException if * sink is null. * * @since JAI 1.1 */ public boolean addSink(Object sink) { if (sink == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return super.addSink(sink); } /** * Removes a sink from the list of node sinks. If the sink * is a PropertyChangeListener for the "Rendering" * property of this node it will no longer be eligible for * notification events indicating a change in this property. * * @throws IllegalArgumentException if * sink is null. * * @since JAI 1.1 */ public boolean removeSink(Object sink) { if (sink == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return super.removeSink(sink); } /* ----- Image coordinate mapping methods ----- */ /** * Computes the position in the specified source that best * matches the supplied destination image position. If it * is not possible to compute the requested position, * null will be returned. If the point is mapped * outside the source bounds, the coordinate value or null * may be returned at the discretion of the implementation. * *

      If the rendering of the node is an OpImage, the * call is forwarded to the equivalent method of the rendering. * Otherwise destPt is returned to indicate the identity * mapping. In either case if the node had not been rendered it will * be.

      * * @param destPt the position in destination image coordinates * to map to source image coordinates. * @param sourceIndex the index of the source image. * * @return a Point2D of the same class as * destPt or null. * * @throws IllegalArgumentException if destPt is * null. * @throws IndexOutOfBoundsException if sourceIndex is * negative or greater than or equal to the number of sources. * * @since JAI 1.1.2 */ public Point2D mapDestPoint(Point2D destPt, int sourceIndex) { if (destPt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } else if (sourceIndex < 0 || sourceIndex >= getNumSources()) { throw new IndexOutOfBoundsException(JaiI18N.getString("Generic1")); } createRendering(); if(theImage != null && theImage instanceof OpImage) { return ((OpImage)theImage).mapDestPoint(destPt, sourceIndex); } return destPt; } /** * Computes the position in the destination that best * matches the supplied source image position. If it * is not possible to compute the requested position, * null will be returned. If the point is mapped * outside the destination bounds, the coordinate value or * null may be returned at the discretion of the * implementation. * *

      If the rendering of the node is an OpImage, the * call is forwarded to the equivalent method of the rendering. * Otherwise sourcePt is returned to indicate the identity * mapping. In either case if the node had not been rendered it will * be.

      * * @param sourcePt the position in source image coordinates * to map to destination image coordinates. * @param sourceIndex the index of the source image. * * @return a Point2D of the same class as * sourcePt or null. * * @throws IllegalArgumentException if sourcePt is * null. * @throws IndexOutOfBoundsException if sourceIndex is * negative or greater than or equal to the number of sources. * * @since JAI 1.1.2 */ public Point2D mapSourcePoint(Point2D sourcePt, int sourceIndex) { if (sourcePt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } else if (sourceIndex < 0 || sourceIndex >= getNumSources()) { throw new IndexOutOfBoundsException(JaiI18N.getString("Generic1")); } createRendering(); if(theImage != null && theImage instanceof OpImage) { return ((OpImage)theImage).mapSourcePoint(sourcePt, sourceIndex); } return sourcePt; } /* ----- Object cleanup ----- */ /** * Hints that this node and its rendering will no longer be used. * *

      If theImage is non-null, then this * call is first forwarded to theImage.dispose(). * Subsequent to this super.dispose() is invoked.

      * *

      The results of referencing an image after a call to * dispose() are undefined.

      * * @since JAI 1.1.2 */ public synchronized void dispose() { if(isDisposed) { return; } isDisposed = true; if(theImage != null) { theImage.dispose(); } super.dispose(); } /* ----- [De]serialization methods ----- */ /** Serializes the RenderedOp. */ private void writeObject(ObjectOutputStream out) throws IOException { // Write non-static and non-transient fields. out.defaultWriteObject(); // Explicitly serialize the required superclass fields. out.writeObject(eventManager); out.writeObject(properties); } /** * Deserialize the RenderedOp. */ private synchronized void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { // Read non-static and non-transient fields. in.defaultReadObject(); // Explicitly deserialize the required superclass fields. eventManager = (PropertyChangeSupportJAI)in.readObject(); properties = (WritablePropertySourceImpl)in.readObject(); // If this operation requires immediate rendering then render it. OperationDescriptor odesc = (OperationDescriptor) getRegistry().getDescriptor( "rendered", nodeSupport.getOperationName()); if (odesc.isImmediate()) { createRendering(); } } void sendExceptionToListener(String message, Exception e) { ImagingListener listener = (ImagingListener)getRenderingHints().get(JAI.KEY_IMAGING_LISTENER); listener.errorOccurred(message, e, this, false); } } jai-core-1.1.4/src/share/classes/javax/media/jai/PropertyChangeEventJAI.java0000644000175000017500000000471610203035544026470 0ustar mathieumathieu/* * $RCSfile: PropertyChangeEventJAI.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:16 $ * $State: Exp $ */ package javax.media.jai; import java.beans.PropertyChangeEvent; /** * A class instances of which represent Java Bean-style events emitted by * JAI objects. This class definition adds no functionality to that provided * by the superclass. The significance of the derivation is that bean events * will be easily identifiable as having been generated by JAI classes by * virtue of their being instances of this event class. Note that this does * not prevent JAI properties from colliding with other Java Bean properties * in the Bean property name space. * * @since JAI 1.1 */ public class PropertyChangeEventJAI extends PropertyChangeEvent { /** * The case-retained property name as supplied to the constructor. */ private String originalPropertyName; /** * Constructs a PropertyChangeEventJAI. * propertyName is forced to lower case; all other * parameters are passed unmodified to the superclass constructor. * The original property name may be obtained by invoking * getOriginalPropertyName(). * * @exception NullPointerException if propertyName is * null. * @exception IllegalArgumentException if source is * null or if oldValue and * newValue are both null. */ public PropertyChangeEventJAI(Object source, String propertyName, Object oldValue, Object newValue) { super(source, propertyName.toLowerCase(), oldValue, newValue); if(source == null) { throw new IllegalArgumentException(JaiI18N.getString("PropertyChangeEventJAI0")); } else if(oldValue == null && newValue == null) { throw new IllegalArgumentException(JaiI18N.getString("PropertyChangeEventJAI1")); } originalPropertyName = propertyName.equals(getPropertyName()) ? getPropertyName() : propertyName; } /** * Returns the value of propertyName originally passed to * the class constructor. This name has its case retained. */ public String getOriginalPropertyName() { return originalPropertyName; } } jai-core-1.1.4/src/share/classes/javax/media/jai/PointOpImage.java0000644000175000017500000012454010203035544024541 0ustar mathieumathieu/* * $RCSfile: PointOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:15 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.ComponentSampleModel; import java.awt.image.DataBuffer; import java.awt.image.IndexColorModel; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.SinglePixelPackedSampleModel; import java.awt.image.WritableRaster; import java.awt.image.WritableRenderedImage; import java.lang.reflect.Method; import java.util.Map; import java.util.Vector; import java.security.AccessController; import java.security.PrivilegedAction; import javax.media.jai.util.CaselessStringKey; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.util.JDKWorkarounds; /** * An abstract base class for image operators that require only the * (x, y) pixel from each source image in order to compute the * destination pixel (x, y). * *

      PointOpImage is intended as a convenient * superclass for OpImages that only need to look at each * destination pixel's corresponding source pixels. Some examples are * lookup, contrast adjustment, pixel arithmetic, and color space * conversion. * * @see OpImage */ public abstract class PointOpImage extends OpImage { /* Flag indicating that dispose() has been invoked. */ private boolean isDisposed = false; /* Flag indicating whether the various flags have been set. */ private boolean areFieldsInitialized = false; /* Flag indicating that in-place compatibility should be checked. */ private boolean checkInPlaceOperation = false; /* Flag indicating whether in-place operation is enabled. */ private boolean isInPlaceEnabled = false; /// BEGIN: Variable fields used only when in-place operation is enabled. /* The first source cast to a WritableRenderedImage. */ private WritableRenderedImage source0AsWritableRenderedImage; /* The first source cast to an OpImage. */ private OpImage source0AsOpImage; /* Flag indicating whether the first source is a WritableRenderedImage. */ private boolean source0IsWritableRenderedImage; /// END: Variable fields use only when in-place operation is enabled. /* Flag indicating whether the bounds are the same as for all sources. */ private boolean sameBounds; /* Flag indicating whether the tile grid is the same as for all sources. */ private boolean sameTileGrid; // END in-place fields. /** Fills in the default layout settings. */ private static ImageLayout layoutHelper(ImageLayout layout, Vector sources, Map config) { int numSources = sources.size(); if(numSources < 1) { throw new IllegalArgumentException(JaiI18N.getString("Generic5")); } RenderedImage source0 = (RenderedImage)sources.get(0); Rectangle isect = new Rectangle(source0.getMinX(), source0.getMinY(), source0.getWidth(), source0.getHeight()); Rectangle rect = new Rectangle(); for (int i = 1; i < numSources; i++) { RenderedImage s = (RenderedImage)sources.get(i); rect.setBounds(s.getMinX(), s.getMinY(), s.getWidth(), s.getHeight()); isect = isect.intersection(rect); } if (isect.isEmpty()) { throw new IllegalArgumentException( JaiI18N.getString("PointOpImage0")); } if(layout == null) { layout = new ImageLayout(isect.x, isect.y, isect.width, isect.height); } else { layout = (ImageLayout)layout.clone(); if (!layout.isValid(ImageLayout.MIN_X_MASK)) { layout.setMinX(isect.x); } if (!layout.isValid(ImageLayout.MIN_Y_MASK)) { layout.setMinY(isect.y); } if (!layout.isValid(ImageLayout.WIDTH_MASK)) { layout.setWidth(isect.width); } if (!layout.isValid(ImageLayout.HEIGHT_MASK)) { layout.setHeight(isect.height); } Rectangle r = new Rectangle(layout.getMinX(null), layout.getMinY(null), layout.getWidth(null), layout.getHeight(null)); if (r.isEmpty()) { throw new IllegalArgumentException( JaiI18N.getString("PointOpImage1")); } if (!isect.contains(r)) { throw new IllegalArgumentException( JaiI18N.getString("PointOpImage2")); } } // If no SampleModel is given, create a new SampleModel (and perhaps // ColorModel) corresponding to the minimum band count and maximum // data depth. if (numSources > 1 && !layout.isValid(ImageLayout.SAMPLE_MODEL_MASK)) { // Determine the min number of bands and max range of data SampleModel sm = source0.getSampleModel(); ColorModel cm = source0.getColorModel(); int dtype0 = getAppropriateDataType(sm); int bands0 = getBandCount(sm, cm); int dtype = dtype0; int bands = bands0; for (int i = 1; i < numSources; i++) { RenderedImage source = (RenderedImage)sources.get(i); sm = source.getSampleModel(); cm = source.getColorModel(); int sourceBands = getBandCount(sm, cm); dtype = mergeTypes(dtype, getPixelType(sm)); bands = Math.min(bands, sourceBands); } // Force data type to byte for multi-band bilevel data. if(dtype == PixelAccessor.TYPE_BIT && bands > 1) { dtype = DataBuffer.TYPE_BYTE; } // Set a new SampleModel if and only if that of source 0 is // not compatible. SampleModel sm0 = source0.getSampleModel(); if(dtype != sm0.getDataType() || bands != sm0.getNumBands()) { int tw = layout.getTileWidth(source0); int th = layout.getTileHeight(source0); SampleModel sampleModel; if(dtype == PixelAccessor.TYPE_BIT) { sampleModel = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, tw, th, 1); } else { sampleModel = RasterFactory.createPixelInterleavedSampleModel(dtype, tw, th, bands); } layout.setSampleModel(sampleModel); // Set a new ColorModel only if this one is incompatible. if(cm != null && !JDKWorkarounds.areCompatibleDataModels(sampleModel, cm)) { cm = ImageUtil.getCompatibleColorModel(sampleModel, config); layout.setColorModel(cm); } } } return layout; } /** * Returns the pixel type. */ private static int getPixelType(SampleModel sampleModel) { return ImageUtil.isBinary(sampleModel) ? PixelAccessor.TYPE_BIT : sampleModel.getDataType(); } /** * Returns the number of bands. */ private static int getBandCount(SampleModel sampleModel, ColorModel colorModel) { if(ImageUtil.isBinary(sampleModel)) { return 1; } else if (colorModel instanceof IndexColorModel) { return colorModel.getNumComponents(); } else { return sampleModel.getNumBands(); } } /** * Determine the appropriate data type for the SampleModel. */ private static int getAppropriateDataType(SampleModel sampleModel) { int dataType = sampleModel.getDataType(); int retVal = dataType; if (ImageUtil.isBinary(sampleModel)) { retVal = PixelAccessor.TYPE_BIT; } else if (dataType == DataBuffer.TYPE_USHORT || dataType == DataBuffer.TYPE_INT) { boolean canUseBytes = true; boolean canUseShorts = true; int[] ss = sampleModel.getSampleSize(); for (int i = 0; i < ss.length; i++) { if (ss[i] > 16) { canUseBytes = false; canUseShorts = false; break; } if (ss[i] > 8) { canUseBytes = false; } } if (canUseBytes) { retVal = DataBuffer.TYPE_BYTE; } else if (canUseShorts) { retVal = DataBuffer.TYPE_USHORT; } } return retVal; } /** * Returns a type (one of the enumerated constants from * DataBuffer) that has sufficent range to contain values from * either of two given types. This corresponds to an upwards move * in the type lattice. * *

      Note that the merge of SHORT and USHORT is INT, so it is not * correct to simply use the larger of the types. */ private static int mergeTypes(int type0, int type1) { if (type0 == type1) { return type0; } // Default to second type. int type = type1; // Use switch logic to avoid depending on monotonicity of // DataBuffer.TYPE_*. switch(type0) { case PixelAccessor.TYPE_BIT: case DataBuffer.TYPE_BYTE: // Do nothing. break; case DataBuffer.TYPE_SHORT: if(type1 == DataBuffer.TYPE_BYTE) { type = DataBuffer.TYPE_SHORT; } else if(type1 == DataBuffer.TYPE_USHORT) { type = DataBuffer.TYPE_INT; } break; case DataBuffer.TYPE_USHORT: if(type1 == DataBuffer.TYPE_BYTE) { type = DataBuffer.TYPE_USHORT; } else if(type1 == DataBuffer.TYPE_SHORT) { type = DataBuffer.TYPE_INT; } break; case DataBuffer.TYPE_INT: if(type1 == DataBuffer.TYPE_BYTE || type1 == DataBuffer.TYPE_SHORT || type1 == DataBuffer.TYPE_USHORT) { type = DataBuffer.TYPE_INT; } break; case DataBuffer.TYPE_FLOAT: if(type1 != DataBuffer.TYPE_DOUBLE) { type = DataBuffer.TYPE_FLOAT; } break; case DataBuffer.TYPE_DOUBLE: type = DataBuffer.TYPE_DOUBLE; break; } return type; } /** * Constructor. * *

      There must be at least one valid source supplied via the * sources argument. However, there is no upper limit * on the number of sources this image may have. * *

      The image's layout is encapsulated in the layout * argument. If the image bounds are supplied they must be contained * within the intersected source bounds which must be non-empty. * If the bounds are not supplied, they are calculated to be the * intersection of the bounds of all sources. * *

      If no SampleModel is specified in the layout, a new * SampleModel will be created. This SampleModel * will have a number of bands equal to the minimum band count of all * sources and a depth which can accomodate the data of all sources. * The band count of sources which have an IndexColorModel * will be set to the number of components of the * IndexColorModel instead of to the number of bands of the * SampleModel. * *

      In all cases, the layout is forwarded to the OpImage * constructor which sets the default layout values in the standard way. * * @param layout The layout parameters of the destination image. * @param sources The source images. * @param configuration Configurable attributes of the image including * configuration variables indexed by * RenderingHints.Keys and image properties indexed * by Strings or CaselessStringKeys. * This is simply forwarded to the superclass constructor. * @param cobbleSources true if computeRect() expects * contiguous sources. * * @throws IllegalArgumentException If sources or any * object in sources is null. * @throws IllegalArgumentException if sources does not * contain at least one element. * @throws ClassCastException If any object in sources * is not a RenderedImage. * @throws IllegalArgumentException If combining the intersected * source bounds with the user-specified bounds, if any, * yields an empty rectangle, or the user-specified image bounds * extends beyond the intersection of all the source bounds. * * @since JAI 1.1 */ public PointOpImage(Vector sources, ImageLayout layout, Map configuration, boolean cobbleSources) { super(checkSourceVector(sources, true), layoutHelper(layout, sources, configuration), configuration, cobbleSources); } /** * Constructs a PointOpImage with one source image. * The image layout is computed as described in the constructor * taking a Vector of sources. * * @param layout The layout parameters of the destination image. * @param source The source image. * @param configuration Configurable attributes of the image including * configuration variables indexed by * RenderingHints.Keys and image properties indexed * by Strings or CaselessStringKeys. * This is simply forwarded to the superclass constructor. * @param cobbleSources Indicates whether computeRect() * expects contiguous sources. * * @throws IllegalArgumentException if source * is null. * * @since JAI 1.1 */ public PointOpImage(RenderedImage source, ImageLayout layout, Map configuration, boolean cobbleSources) { this(vectorize(source), // vectorize() checks for null source. layout, configuration, cobbleSources); } /** * Constructs a PointOpImage with two source images. * The image layout is computed as described in the constructor * taking a Vector of sources. * * @param layout The layout parameters of the destination image. * @param source0 The first source image. * @param source1 The second source image. * @param configuration Configurable attributes of the image including * configuration variables indexed by * RenderingHints.Keys and image properties indexed * by Strings or CaselessStringKeys. * This is simply forwarded to the superclass constructor. * @param cobbleSources Indicates whether computeRect() * expects contiguous sources. * * @throws IllegalArgumentException if source0 or * source1 is null. * * @since JAI 1.1 */ public PointOpImage(RenderedImage source0, RenderedImage source1, ImageLayout layout, Map configuration, boolean cobbleSources) { this(vectorize(source0, source1), // vectorize() checks for null sources. layout, configuration, cobbleSources); } /** * Constructs a PointOpImage with three source * images. The image layout is computed as described in the * constructor taking a Vector of sources. * * @param layout The layout parameters of the destination image. * @param source0 The first source image. * @param source1 The second source image. * @param source2 The third source image. * @param configuration Configurable attributes of the image including * configuration variables indexed by * RenderingHints.Keys and image properties indexed * by Strings or CaselessStringKeys. * This is simply forwarded to the superclass constructor. * @param cobbleSources Indicates whether computeRect() * expects contiguous sources. * * @throws IllegalArgumentException if source0 or * source1 or source2 is * null. * * @since JAI 1.1 */ public PointOpImage(RenderedImage source0, RenderedImage source1, RenderedImage source2, ImageLayout layout, Map configuration, boolean cobbleSources) { this(vectorize(source0, source1, source2), // vectorize() checks null layout, configuration, cobbleSources); } /* * Initialize flags and instance variables. */ private synchronized void initializeFields() { if (areFieldsInitialized) return; PlanarImage source0 = getSource(0); if (checkInPlaceOperation) { // Set the in-place operation flag. // XXX: In-place operation could work equally well when source0 // is a WritableRenderedImage. However this could produce // unexpected results so consequently it would be desirable if // some kind of hint could be set that in-place operation is to // be used. The following statement should then be changed such // that instead of // // source0 instanceof OpImage && // // we have // // (source0 instanceof OpImage || // (source0 instanceof WritableRenderedImage && // isInPlaceHintSet)) // Vector source0Sinks = source0.getSinks(); isInPlaceEnabled = source0 != null && getTileGridXOffset() == source0.getTileGridXOffset() && getTileGridYOffset() == source0.getTileGridYOffset() && getBounds().equals(source0.getBounds()) && source0 instanceof OpImage && hasCompatibleSampleModel(source0) && !(source0Sinks != null && source0Sinks.size() > 1); // Ensure that source0 computes unique tiles, i.e., // computesUniqueTiles() returns false. This disqualifies // for example in-place operations when source0 is an instance // of NullOpImage or of a subclass of NullOpImage or // SourcelessOpImage which does not override // computesUniqueTiles() to return true. if (isInPlaceEnabled && !((OpImage)source0).computesUniqueTiles()) { isInPlaceEnabled = false; } // Unset the in-place flag if getTile() is overridden by the // class of which source0 is an instance. if (isInPlaceEnabled) { try { Method getTileMethod = source0.getClass().getMethod("getTile", new Class[] {int.class, int.class}); Class opImageClass = Class.forName("javax.media.jai.OpImage"); Class declaringClass = getTileMethod.getDeclaringClass(); // Unset in-place flag if getTile() is overridden. if(!declaringClass.equals(opImageClass)) { isInPlaceEnabled = false; } } catch(ClassNotFoundException e) { isInPlaceEnabled = false; } catch(NoSuchMethodException e) { isInPlaceEnabled = false; } } // Set local fields as a function of the in-place operation flag. if (isInPlaceEnabled) { // Set the flag indicating source0's type. source0IsWritableRenderedImage = source0 instanceof WritableRenderedImage; // Cast the first source to one of the cached image variables. if (source0IsWritableRenderedImage) { source0AsWritableRenderedImage = (WritableRenderedImage)source0; } else { source0AsOpImage = (OpImage)source0; } } // Unset this flag. checkInPlaceOperation = false; } // Get the number of sources. int numSources = getNumSources(); // Initialize the bounds and tile grid flags. sameBounds = true; sameTileGrid = true; // Loop over all sources or until both flags are false. for(int i = 0; i < numSources && (sameBounds || sameTileGrid); i++) { PlanarImage source = getSource(i); // Update the bounds flag. if (sameBounds) { sameBounds = sameBounds && minX == source.minX && minY == source.minY && width == source.width && height == source.height; } // Update the tile grid flag. if (sameTileGrid) { sameTileGrid = sameTileGrid && tileGridXOffset == source.tileGridXOffset && tileGridYOffset == source.tileGridYOffset && tileWidth == source.tileWidth && tileHeight == source.tileHeight; } } // Set this flag. areFieldsInitialized = true; } /* * Check whether the SampleModel of the argument * PlanarImageis compatible with that of this * PointOpImage. * * @param src The PlanarImage whose SampleModel * is to be checked. * @return Whether the parameter has a compatible SampleModel. */ private boolean hasCompatibleSampleModel(PlanarImage src) { SampleModel srcSM = src.getSampleModel(); int numBands = sampleModel.getNumBands(); boolean isCompatible = srcSM.getTransferType() == sampleModel.getTransferType() && srcSM.getWidth() == sampleModel.getWidth() && srcSM.getHeight() == sampleModel.getHeight() && srcSM.getNumBands() == numBands && srcSM.getClass().equals(sampleModel.getClass()); if (isCompatible) { if (sampleModel instanceof ComponentSampleModel) { ComponentSampleModel smSrc = (ComponentSampleModel)srcSM; ComponentSampleModel smDst = (ComponentSampleModel)sampleModel; isCompatible = isCompatible && smSrc.getPixelStride() == smDst.getPixelStride() && smSrc.getScanlineStride() == smDst.getScanlineStride(); int[] biSrc = smSrc.getBankIndices(); int[] biDst = smDst.getBankIndices(); int[] boSrc = smSrc.getBandOffsets(); int[] boDst = smDst.getBandOffsets(); for(int b = 0; b < numBands && isCompatible; b++) { isCompatible = isCompatible && biSrc[b] == biDst[b] && boSrc[b] == boDst[b]; } } else if (sampleModel instanceof SinglePixelPackedSampleModel) { SinglePixelPackedSampleModel smSrc = (SinglePixelPackedSampleModel)srcSM; SinglePixelPackedSampleModel smDst = (SinglePixelPackedSampleModel)sampleModel; isCompatible = isCompatible && smSrc.getScanlineStride() == smDst.getScanlineStride(); int[] bmSrc = smSrc.getBitMasks(); int[] bmDst = smDst.getBitMasks(); for(int b = 0; b < numBands && isCompatible; b++) { isCompatible = isCompatible && bmSrc[b] == bmDst[b]; } } else if (sampleModel instanceof MultiPixelPackedSampleModel) { MultiPixelPackedSampleModel smSrc = (MultiPixelPackedSampleModel)srcSM; MultiPixelPackedSampleModel smDst = (MultiPixelPackedSampleModel)sampleModel; isCompatible = isCompatible && smSrc.getPixelBitStride() == smDst.getPixelBitStride() && smSrc.getScanlineStride() == smDst.getScanlineStride() && smSrc.getDataBitOffset() == smDst.getDataBitOffset(); } else { isCompatible = false; } } return isCompatible; } /** * Causes a flag to be set to indicate that in-place operation should * be permitted if the image bounds, tile grid offset, tile dimensions, * and SampleModels of the source and destination images are compatible. * This method should be invoked in the constructor of the implementation * of a given operation only if that implementation is amenable to * in-place computation. Invocation of this method is a necessary but * not a sufficient condition for in-place computation actually to occur. * If the system property "javax.media.jai.PointOpImage.InPlace" is equal * to the string "false" in a case-insensitive fashion then in-place * operation will not be permitted. */ protected void permitInPlaceOperation() { // Retrieve the in-place property. Object inPlaceProperty = null; try { inPlaceProperty = AccessController.doPrivileged(new PrivilegedAction() { public Object run() { String name = "javax.media.jai.PointOpImage.InPlace"; return System.getProperty(name); } }); } catch (SecurityException se) { /// as if the property isn't set } // Set the flag to false if and only if the property is set to // the string "false" (case-insensitive). checkInPlaceOperation = !(inPlaceProperty != null && inPlaceProperty instanceof String && ((String)inPlaceProperty).equalsIgnoreCase("false")); } /** * Indicates whether the operation is being effected directly on the * associated colormap. This method will in general return * true if the image is the destination of a unary, * shift-invariant operation with an IndexColorModel equal * to that of its unique source. * *

      When this method returns true the * computeTile() method in this class will return either * a copy of the corresponding region of the first source image or, * if the operation is being performed in place, the corresponding * tile of the first source image. * *

      The implementation in this class always returns false. * * @since JAI 1.1 */ protected boolean isColormapOperation() { return false; } /** * Computes a tile. If source cobbling was requested at * construction time, the source tile boundaries are overlayed * onto the destination and computeRect(Raster[], * WritableRaster, Rectangle) is called for each of the * resulting regions. Otherwise, computeRect(PlanarImage[], * WritableRaster, Rectangle) is called once to compute the * entire active area of the tile. * *

      The image bounds may be larger than the bounds of the * source image. In this case, samples for which there are no * corresponding sources are set to zero. * * @param tileX The X index of the tile. * @param tileY The Y index of the tile. */ public Raster computeTile(int tileX, int tileY) { if (!cobbleSources) { return super.computeTile(tileX, tileY); } // Make sure the fields are initialized. initializeFields(); // Get a WritableRaster to represent this tile. WritableRaster dest = null; if (isInPlaceEnabled) { if (source0IsWritableRenderedImage) { // Check one out from the WritableRenderedImage source. dest = source0AsWritableRenderedImage.getWritableTile(tileX, tileY); } else { // source0 is OpImage // Re-use one from the OpImage source. // First check whether the source raster is cached. Raster raster = source0AsOpImage.getTileFromCache(tileX, tileY); if (raster == null) { // Compute the tile. try { raster = source0AsOpImage.computeTile(tileX, tileY); if (raster instanceof WritableRaster) { dest = (WritableRaster)raster; } } catch(Exception e) { // Do nothing: this catch is simply in case the // OpImage in question does not itself implement // computeTile() in which case it may be resolved // to OpImage.computeTile() which will throw an // Exception. } } } } // Set tile recycling flag. boolean recyclingSource0Tile = dest != null; if (!recyclingSource0Tile) { // Create a new WritableRaster. Point org = new Point(tileXToX(tileX), tileYToY(tileY)); dest = createWritableRaster(sampleModel, org); } // Colormap operation: return the source Raster if operating // in place or a copy thereof otherwise. if(isColormapOperation()) { if(!recyclingSource0Tile) { PlanarImage src = getSource(0); Raster srcTile = null; Rectangle srcRect = null; Rectangle dstRect = dest.getBounds(); // Confirm that the tile grids of the source and destination // are the same if (sameTileGrid) { // Tile grids are aligned so the tile indices correspond // to pixels at the same locations in source and destination srcTile = getSource(0).getTile(tileX, tileY); } else if (dstRect.intersects(src.getBounds())) { // Tile grids are not aligned but the destination rectangle // intersects the source bounds so get the data using // the destination rectangle srcTile = src.getData(dstRect); } else { // The destination rectangle does not interest the source // bounds so just return the destination. return dest; } srcRect = srcTile.getBounds(); // Ensure that the source tile doesn't lie outside the // destination tile. if(!dstRect.contains(srcRect)) { srcRect = dstRect.intersection(srcRect); srcTile = srcTile.createChild(srcTile.getMinX(), srcTile.getMinY(), srcRect.width, srcRect.height, srcRect.x, srcRect.y, null); } JDKWorkarounds.setRect(dest, srcTile, 0, 0); } return dest; } // Output bounds are initially equal to the tile bounds. int destMinX = dest.getMinX(); int destMinY = dest.getMinY(); int destMaxX = destMinX + dest.getWidth(); int destMaxY = destMinY + dest.getHeight(); // Clip output bounds to the dest image bounds. Rectangle bounds = getBounds(); if (destMinX < bounds.x) { destMinX = bounds.x; } int boundsMaxX = bounds.x + bounds.width; if (destMaxX > boundsMaxX) { destMaxX = boundsMaxX; } if (destMinY < bounds.y) { destMinY = bounds.y; } int boundsMaxY = bounds.y + bounds.height; if (destMaxY > boundsMaxY) { destMaxY = boundsMaxY; } // Get the number of sources. int numSrcs = getNumSources(); // Branch to actual destination rectangle computation as a // function of in-place operation and layout compatibility. if (recyclingSource0Tile && numSrcs == 1) { // Recycling tile from a single source. Raster[] sources = new Raster[] {dest}; Rectangle destRect = new Rectangle(destMinX, destMinY, destMaxX - destMinX, destMaxY - destMinY); computeRect(sources, dest, destRect); } else if (recyclingSource0Tile && sameBounds && sameTileGrid) { // Recycling tile from first of layout-compatible sources. Raster[] sources = new Raster[numSrcs]; sources[0] = dest; for(int i = 1; i < numSrcs; i++) { sources[i] = getSource(i).getTile(tileX, tileY); } Rectangle destRect = new Rectangle(destMinX, destMinY, destMaxX - destMinX, destMaxY - destMinY); computeRect(sources, dest, destRect); } else { // Clip against source bounds only if necessary. if (!sameBounds) { // Clip output bounds to each source image bounds for (int i = recyclingSource0Tile ? 1 : 0; i < numSrcs; i++) { bounds = getSource(i).getBounds(); if (destMinX < bounds.x) { destMinX = bounds.x; } boundsMaxX = bounds.x + bounds.width; if (destMaxX > boundsMaxX) { destMaxX = boundsMaxX; } if (destMinY < bounds.y) { destMinY = bounds.y; } boundsMaxY = bounds.y + bounds.height; if (destMaxY > boundsMaxY) { destMaxY = boundsMaxY; } if (destMinX >= destMaxX || destMinY >= destMaxY) { return dest; // no corresponding source region } } } // Initialize the (possibly clipped) destination Rectangle. Rectangle destRect = new Rectangle(destMinX, destMinY, destMaxX - destMinX, destMaxY - destMinY); // Allocate memory for source Rasters. Raster[] sources = new Raster[numSrcs]; if (sameTileGrid) { // All sources share the tile grid of the destination so // there is no need for splits. if (recyclingSource0Tile) { sources[0] = dest; } for (int i = recyclingSource0Tile ? 1 : 0; i < numSrcs; i++) { sources[i] = getSource(i).getTile(tileX, tileY); } computeRect(sources, dest, destRect); } else { // // The tileWidth and tileHeight of the source image // may differ from this tileWidth and tileHeight. // IntegerSequence xSplits = new IntegerSequence(destMinX, destMaxX); xSplits.insert(destMinX); xSplits.insert(destMaxX); IntegerSequence ySplits = new IntegerSequence(destMinY, destMaxY); ySplits.insert(destMinY); ySplits.insert(destMaxY); for (int i = recyclingSource0Tile ? 1 : 0; i < numSrcs; i++) { PlanarImage s = getSource(i); s.getSplits(xSplits, ySplits, destRect); } // // Divide destRect into sub rectangles based on the source // splits, and compute each sub rectangle separately. // int x1, x2, y1, y2, w, h; Rectangle subRect = new Rectangle(); ySplits.startEnumeration(); for (y1 = ySplits.nextElement(); ySplits.hasMoreElements(); y1 = y2) { y2 = ySplits.nextElement(); h = y2 - y1; xSplits.startEnumeration(); for (x1 = xSplits.nextElement(); xSplits.hasMoreElements(); x1 = x2) { x2 = xSplits.nextElement(); w = x2 - x1; // Get sources. if (recyclingSource0Tile) { sources[0] = dest; } for (int i = recyclingSource0Tile ? 1: 0; i < numSrcs; i++) { PlanarImage s = getSource(i); int tx = s.XToTileX(x1); int ty = s.YToTileY(y1); sources[i] = s.getTile(tx, ty); } subRect.x = x1; subRect.y = y1; subRect.width = w; subRect.height = h; computeRect(sources, dest, subRect); } } } } if (recyclingSource0Tile && source0IsWritableRenderedImage) { source0AsWritableRenderedImage.releaseWritableTile(tileX, tileY); } return dest; } /** * Returns a conservative estimate of the destination region that * can potentially be affected by the pixels of a rectangle of a * given source. The resulting Rectangle is not * clipped to the destination image bounds. * * @param sourceRect the Rectangle in source coordinates. * @param sourceIndex the index of the source image. * @return a Rectangle indicating the potentially affected * destination region, or null if the region is unknown. * @throws IllegalArgumentException if sourceIndex is * negative or greater than the index of the last source. * @throws IllegalArgumentException if sourceRect is * null. */ public final Rectangle mapSourceRect(Rectangle sourceRect, int sourceIndex) { if ( sourceRect == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sourceIndex < 0 || sourceIndex >= getNumSources()) { throw new IllegalArgumentException(JaiI18N.getString("Generic1")); } return new Rectangle(sourceRect); } /** * Returns a conservative estimate of the region of a specific * source that is required in order to compute the pixels of a * given destination rectangle. The resulting Rectangle * is not clipped to the source image bounds. * * @param destRect the Rectangle in source coordinates. * @param sourceIndex the index of the source image. * @return a Rectangle indicating the potentially affected * destination region. * * @throws IllegalArgumentException if sourceIndex is * negative or greater than the index of the last source. * @throws IllegalArgumentException if destRect is * null. */ public final Rectangle mapDestRect(Rectangle destRect, int sourceIndex) { if ( destRect == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sourceIndex < 0 || sourceIndex >= getNumSources()) { throw new IllegalArgumentException(JaiI18N.getString("Generic1")); } return new Rectangle(destRect); } /** * Disposes of any remaining tiles in the TileCache. * *

      If cache is non-null, in place operation * is enabled, and tileRecycler is non-null, * then all tiles owned by this specific image are removed from the cache. * Subsequent to this super.dispose() is invoked.

      * * @since JAI 1.1.2 */ public synchronized void dispose() { if(isDisposed) { return; } isDisposed = true; if(cache != null && isInPlaceEnabled && tileRecycler != null) { cache.removeTiles(this); } super.dispose(); } } jai-core-1.1.4/src/share/classes/javax/media/jai/ImagePyramid.java0000644000175000017500000002677210203035544024566 0ustar mathieumathieu/* * $RCSfile: ImagePyramid.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:09 $ * $State: Exp $ */ package javax.media.jai; import java.awt.image.RenderedImage; import java.util.Vector; /** * A class implementing the "Pyramid" operation on a * RenderedImage. Given a RenderedImage * which represents the image at the highest resolution level, * the images at lower resolution levels may be derived by * performing a specific chain of operations to downsample the * image at the higher resolution level repeatedly. Similarly, * once an image at a lower resolution level is obtained, the images * at higher resolution levels may be retrieved by performing a * specific chain of operations to upsample the image at the lower * resolution level repeatedly. * *

      When an image is downsampled, the image at the higher resolution * level is lost. However, the difference image between the original image * and the image obtained by up sampling the downsampled result image is * saved. This difference image, combined with the up sampling operations * is used to retrieve the image at a higher resolution level from the * image at a lower resolution level. * *

      This is a bi-directional operation. A user may request an image * at any resolution level greater than or equal to the highest * resolution level, which is defined as level 0. * *

      The downSampler is a chain of operations that is * used to derive the image at the next lower resolution level from * the image at the current resolution level. That is, given an image * at resolution level i, downSampler is * used to obtain the image at resolution level i+1. * The chain may contain one or more operation nodes; however, each * node must be a RenderedOp. The parameter points to the * last node in the chain. The very first node in the chain must be * a RenderedOp that takes one RenderedImage * as its source. All other nodes may have multiple sources. When * traversing back up the chain, if a node has more than one source, * the first source, source0, is used to move up the * chain. This parameter is saved by reference. * *

      The upSampler is a chain of operations that is * used to derive the image at the next higher resolution level from * the image at the current resolution level. That is, given an image * at resolution level i, upSampler is * used to obtain the image at resolution level i-1. * The requirement for this parameter is identical to that of the * downSampler parameter. * *

      The differencer is a chain of operations that is used * to find the difference between an image at a particular resolution * level and the image obtained by first down sampling that image * then up sampling the result image of the down sampling operations. * The chain may contain one or more operation nodes; however, each * node must be a RenderedOp. The parameter points to the * last node in the chain. The very first node in the chain must be * a RenderedOp that takes two RenderedImages * as its sources. When traversing back up the chain, if a node has * more than one source, the first source, source0, is * used to move up the chain. This parameter is saved by reference. * *

      The combiner is a chain of operations that is * used to combine the result image of the up sampling operations * and the difference image saved to retrieve an image at a higher * resolution level. The requirement for this parameter is identical * to that of the differencer parameter. * *

      Reference: * "The Laplacian Pyramid as a Compact Image Code" * Peter J. Burt and Edward H. Adelson * IEEE Transactions on Communications, Vol. COM-31, No. 4, April 1983 * * @see ImageMIPMap * */ public class ImagePyramid extends ImageMIPMap { /** The operation chain used to derive the higher resolution images. */ protected RenderedOp upSampler; /** The operation chain used to differ two images. */ protected RenderedOp differencer; /** The operation chain used to combine two images. */ protected RenderedOp combiner; /** The default constructor. */ protected ImagePyramid() {} /** The saved difference images. */ private Vector diffImages = new Vector(); /** * Constructor. The RenderedOp parameters point to * the last operation node in each chain. The first operation in * each chain must not have any source images specified; that is, * its number of sources must be 0. All input parameters are saved * by reference. * * @param image The image with the highest resolution. * @param downSampler The operation chain used to derive the lower * resolution images. * @param upSampler The operation chain used to derive the higher * resolution images. * @param differencer The operation chain used to differ two images. * @param combiner The operation chain used to combine two images. * * @throws IllegalArgumentException if image is null. * @throws IllegalArgumentException if downSampler is * null. * @throws IllegalArgumentException if upSampler is * null. * @throws IllegalArgumentException if differencer is * null. * @throws IllegalArgumentException if combiner is * null. */ public ImagePyramid(RenderedImage image, RenderedOp downSampler, RenderedOp upSampler, RenderedOp differencer, RenderedOp combiner) { super(image, downSampler); if (upSampler == null || differencer == null || combiner == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.upSampler = upSampler; this.differencer = differencer; this.combiner = combiner; } /** * Constructor. The RenderedOp parameters point to * the last operation node in each chain. The first operation in * the downSampler chain must have the image with * the highest resolution as its source. The first operation in * all other chains must not have any source images specified; * that is, its number of sources must be 0. All input parameters * are saved by reference. * * @param downSampler The operation chain used to derive the lower * resolution images. * @param upSampler The operation chain used to derive the higher * resolution images. * @param differencer The operation chain used to differ two images. * @param combiner The operation chain used to combine two images. * * @throws IllegalArgumentException if downSampler is * null. * @throws IllegalArgumentException if upSampler is * null. * @throws IllegalArgumentException if differencer is * null. * @throws IllegalArgumentException if combiner is * null. * @throws IllegalArgumentException if downSampler * has no sources. * @throws IllegalArgumentException if an object other than a * RenderedImage is found in the * downSampler chain. */ public ImagePyramid(RenderedOp downSampler, RenderedOp upSampler, RenderedOp differencer, RenderedOp combiner) { super(downSampler); if (upSampler == null || differencer == null || combiner == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.upSampler = upSampler; this.differencer = differencer; this.combiner = combiner; } /** * Returns the image at the specified resolution level. The * requested level must be greater than or equal to 0 or * null will be returned. The image is obtained * by either down sampling or up sampling the current image. * * @param level The specified resolution level. */ public RenderedImage getImage(int level) { if (level < 0) { return null; } while (currentLevel < level) { getDownImage(); } while (currentLevel > level) { getUpImage(); } return currentImage; } /** * Returns the image at the next lower resolution level, * obtained by applying the downSampler on the * image at the current resolution level. */ public RenderedImage getDownImage() { currentLevel++; // Duplicate the downSampler op chain. RenderedOp downOp = duplicate(downSampler, vectorize(currentImage)); // Save the difference image. RenderedOp upOp = duplicate(upSampler, vectorize(downOp.getRendering())); RenderedOp diffOp = duplicate(differencer, vectorize(currentImage, upOp.getRendering())); diffImages.add(diffOp.getRendering()); currentImage = downOp.getRendering(); return currentImage; } /** * Returns the image at the previous higher resolution level, * If the current image is already at level 0, then the current * image will be returned without further up sampling. * *

      The image is obtained by first up sampling the current * image, then combine the result image with the previously saved * difference image using the combiner op chain. */ public RenderedImage getUpImage() { if (currentLevel > 0) { currentLevel--; // Duplicate the upSampler op chain. RenderedOp upOp = duplicate(upSampler, vectorize(currentImage)); // Retrieve diff image for this level. RenderedImage diffImage = (RenderedImage)diffImages.elementAt(currentLevel); diffImages.removeElementAt(currentLevel); RenderedOp combOp = duplicate(combiner, vectorize(upOp.getRendering(), diffImage)); currentImage = combOp.getRendering(); } return currentImage; } /** * Returns the difference image between the current image and the * image obtained by first down sampling the current image then up * sampling the result image of down sampling. This is done using * the differencer op chain. The current level and * current image will not be changed. */ public RenderedImage getDiffImage() { // First downsample. RenderedOp downOp = duplicate(downSampler, vectorize(currentImage)); // Then upsample. RenderedOp upOp = duplicate(upSampler, vectorize(downOp.getRendering())); // Find the difference image. RenderedOp diffOp = duplicate(differencer, vectorize(currentImage, upOp.getRendering())); return diffOp.getRendering(); } } jai-core-1.1.4/src/share/classes/javax/media/jai/javax.media.jai.properties0000644000175000017500000004526010203035544026413 0ustar mathieumathieu# # $RCSfile: javax.media.jai.properties,v $ # # Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. # # Use is subject to license terms. # # $Revision: 1.1 $ # $Date: 2005-02-11 04:57:25 $ # $State: Exp $ # Generic0=The input argument(s) may not be null. Generic1=The sourceIndex must be non-negative and less than the number of sources this image has. Generic2=The argument(s) may not be null or of zero length. Generic3=Unsupported data type. Generic4=The TileRequest parameter may not be null. Generic5=Source Vector must contain at least one element. AreaOpImage0=The user-supplied image bounds do not intersect the source bounds. AttributedImageCollection0=The collection passed to the constructor cannot be null. AttributedImageCollection1=The object(s) could not be added to the collection. BorderExtender0=Illegal value of extenderType. BorderExtenderConstant0=Band count exceeds non-unity number of constants. CRIFImpl0=Cannot render a node for the operation : CollectionOp0= - Cannot render CollectionOp for this operation. ColorCube0=Unsupported data type requested. ColorCube1=Zero-length dimension array. ColorCube2=Zero-valued dimension. ColorCube3=Color cube too large for 32 bit addressability. ColorCube4=Color cube offset + size exceeds type maximum. ColorCube5=This statement should be unreachable. ColorCube6=Non-byte data types not yet implemented. ColorCube7=Unsupported data type. ColorSpaceJAI0=Source raster should not be null. ColorSpaceJAI1=Source should have 3 bands. ColorSpaceJAI2=Destination should have 3 bands. ColorSpaceJAI3=Length of source component size array should be 3. ColorSpaceJAI4=Length of Destination component size array should be 3. DataBuffer0=Size of supplied array should be greater or equal to "size" parameter DataBuffer1=Size of supplied array should be greater or equal to size + offset parameters DeferredData0=Null parameter supplied to constructor. DeferredData1=The data object must be null or an instance of dataClass. DeferredProperty0=The specified property is not emitted by the PropertySource. DescriptorCache0=A descriptor is already registered against the name "{0}" under registry mode "{1}" DescriptorCache1=No descriptor is registered against the name "{0}" under registry mode "{1}" DescriptorCache2=PropertyGenerator #{0,number,integer} is null for descriptor "{1}" under registry mode "{2}" DescriptorCache3=No products are registered against the descriptor "{0}" under registry mode "{1}" DescriptorCache4=No product by name "{2}" is registered against the descriptor "{0}" under registry mode "{1}" DescriptorCache5=No product preferences have been set for descriptor "{0}" under registry mode "{1}" DescriptorCache6=Registry mode "{0}" does not support preferences. Can not set/unset/clear/get product preferences. DescriptorCache7=Registry mode "{0}" does not support properties. FactoryCache0=Input factory object of class "{0}" is not an instance of registry mode "{1}"`s factory class "{2}". FactoryCache1=Registry mode "{0}" does not support preferences. Can not set/unset/clear/get factory preferences. FactoryCache2=No preference was previously set between "{0}" and "{1}" for mode = "{2}", descriptor = "{3}" and product = "{4}". FactoryCache3=Specified instance of "{0}" was not previously registered against descriptor "{1}" under "{2}". FactoryCache4=Specified instance of "{0}" was not previously registered against descriptor "{1}". FloatDoubleColorModel1=getRed(int) not supported by this ColorModel. FloatDoubleColorModel0=transferType must be DataBuffer.TYPE_FLOAT or DataBuffer.TYPE_DOUBLE. FloatDoubleColorModel2=getGreen(int) not supported by this ColorModel. FloatDoubleColorModel3=getBlue(int) not supported by this ColorModel. FloatDoubleColorModel4=getAlpha(int) not supported by this ColorModel. FloatDoubleColorModel5=getRGB(int) not supported by this ColorModel. FloatDoubleColorModel6=raster transfer type must match that of this ColorModel. FloatDoubleColorModel7=Type of pixel does not match transfer type. FloatDoubleColorModel8=pixel array is not large enough to hold all color/alpha components. FloatDoubleColorModel9=Pixel values for FloatDoubleColorModel cannot be represented as a single integer. FloatDoubleColorModel10=elements required in the components array. Histogram0=The three arguments, numBins, lowValue, and highValue, do not have the same array length. Histogram1=The array lengths of the three arguments are 0. Histogram2=The numBins is less than or equal to 0; it should be greater than 0. Histogram3=The lowValue is greater than or equal to its corresponding highValue; it should be less than the highValue. Histogram4=The pixels stored in the Raster and the histogram`s bins do not have the same number of bands. Histogram5=The requested bin sub-range falls outside of the overall bin range of the indicated band . Histogram6=The requested moment number is non-positive. Histogram7=The specified smoothing parameter is negative. Histogram8=The specified standard deviation is negative. Histogram9=The specified sample proportion is not in the range (0,1). Histogram10=minBin is greater than maxBin; it must be less than or equal to maxBin. Histogram11=data type must be one of the DataBuffer.TYPE_BYTE,..., DataBuff.TYPE_DOUBLE. ImageLayout0=The specified dimensional parameter is non-positive. ImageMIPMap0=No highest resolution image found in the downSampler chain. ImageMIPMap1=An object other than a RenderedImage is in the downSampler chain. IntegerSequence0=Illegal element requested while enumerating. IntegerSequence1=min is greater than max. Interpolation0=Unrecognized interpolation type. InterpolationTable0=dataH must contain exactly width * 2^subsampleBitsH entries. InterpolationTable1=dataV must contain exactly height * 2^subsampleBitsV entries. JAI0=No OperationDescriptor is registered in the current operation registry under this name. JAI2=This operation does not produce a java.awt.image.RenderedImage. JAI4=This operation does not produce a java.awt.image.renderable.RenderableImage. JAI5=This operation does not produce a java.awt.image.RenderedImage or a javax.media.jai.CollectionImage. JAI6=This operation does not produce a java.awt.image.renderable.RenderableImage or a javax.media.jai.CollectionImage. JAI7=key parameter is null. JAI8=At least one of the default rendering size dimensions must be positive. JAI9=value parameter is null. JAI10=TileCache capacity must be a non-negative number. JAI13=JAI Build version unavailable. JAI14=The operation name parameter may not be null. JAI15=The ParameterBlock parameter may not be null. JAI16=Cannot create an instance of : KernelJAI0=Kernel width must be a positive number. KernelJAI1=Kernel height must be a positive number. KernelJAI2=Kernel data array must have width*height elements. KernelJAI3=Separable KernelJAI constructor argument dataH array must have width elements. KernelJAI4=Separable KernelJAI constructor argument dataV array must have height elements. LookupTableJAI0=data.getType() is not one of the legal data types. LookupTableJAI1=src argument is null. LookupTableJAI2=src must have a SampleModel of integral type. LookupTableJAI3=dst does not have the correct data type or number of bands. MultiResolutionRenderableImage0=The rendering-independent height must be positive. MultiResolutionRenderableImage1=Non-positive width and height specified. NullOpImage0=The specified computeType is not among the known values. OperationDescriptorImpl0=Need an array of {0} for each mode (numModes = {1,number,integer}). OperationDescriptorImpl1=Number of source names {0,number,integer} != number of source classes {1,number,integer} per mode. OperationDescriptorImpl2=Number of source classes {0,number,integer} != number of sources {1,number,integer} for mode "{2}". OperationDescriptorImpl3=Mode "{0}" does not support properties. OperationDescriptorImpl4=operation "{0}" requires {1,number,integer} source object(s). OperationDescriptorImpl5=operation "{0}" requires {1,number,integer} parameter object(s). OperationDescriptorImpl6=operation "{0}" requires {1,number,integer} source object(s). OperationDescriptorImpl7=operation "{0}" requires all source objects to be valid input; a null is supplied. OperationDescriptorImpl8=operation "{0}" requires source at index {1,number,integer} to be an object of {2}; an object of {3} was supplied. OperationDescriptorImpl9=operation "{0}" requires {1,number,integer} parameter object(s). OperationDescriptorImpl10="{0}" operation`s value for parameter "{1}" is invalid. OperationDescriptorImpl11=operation "{0}" requires parameter at index {1,number,integer} to be non-null. OperationDescriptorImpl12=registry mode name can not be null. OperationDescriptorImpl13=operation "{0}" does not support registry mode "{1}". OperationNodeSupport0=Non-serializable source in this operation`s ParameterBlock. OperationNodeSupport1=Non-serializable parameter in this operation`s ParameterBlock. OperationRegistry0=Mode name "{0}" is not a valid (registered) registry mode. OperationRegistry1=Registry initialization file not found. OperationRegistry2=Error in parsing registry initialization file. OperationRegistry3=Can not descriptor "{0}" under mode "{1}". Mode "{1}" is not a valid registry mode. OperationRegistry4=There no registry modes associated with descriptor class "{0}". OperationRegistry5=No descriptor by name "{0}" is registered under mode "{1}". OperationRegistry6=One factory fails for the operation OperationRegistry7=All factories fail for the operation OpImage0=must override the implementation of void computeRect(Raster[], WritableRaster, Rectangle) from javax.media.jai.OpImage. OpImage1=must override the implementation of void computeRect(PlanarImage[], WritableRaster, Rectangle) from javax.media.jai.OpImage. OpImage2=The supplied source Vector must not be null. OpImage3=The supplied RenderedImage source parameter(s) may not be null. ParameterBlockJAI0=The parameter does not have the correct class type. ParameterBlockJAI1=does not have an OperationDescriptor registered with the OperationRegistry. ParameterBlockJAI2= the parameter value is not valid. ParameterBlockJAI3=The supplied source is null. ParameterBlockJAI4=The supplied source does not have the correct class type for either rendered or renderable mode. ParameterBlockJAI5=Use the set methods to add parameters to the ParameterBlockJAI ParameterBlockJAI6= the parameter value has not yet been set and has no default value. ParameterBlockJAI7=The length of the supplied parameter Vector does no match the number of parameters of the corresponding operation. ParameterBlockJAI8=can not set parameter to NO_PARAMETER_DEFAULT ParameterListDescriptorImpl0=EnumeratedParameter with duplicate name or value. ParameterListDescriptorImpl1=Number of parameter defaults not the same as number of parameter names. ParameterListDescriptorImpl2=Number of valid parameter values not the same as number of parameter names. ParameterListDescriptorImpl3=Number of parameter classes not the same as number of parameter names. ParameterListDescriptorImpl4=Parameter default`s class "{0}" is not an instance of the parameter class "{1}" for parameter "{2}". ParameterListDescriptorImpl5=Parameter "{0}" is an enumerated parameter, but it`s validParamValue is not a Set. ParameterListDescriptorImpl6=The element class of Range ({0}) does not match with the parameter class ({1}) for parameter "{2}". ParameterListDescriptorImpl7=Valid parameter value`s class ({0}) is not an instance of the parameter class ({1}) for parameter "{2}". ParameterListDescriptorImpl8= is not an enumerated parameter. ParameterListDescriptorImpl9=Parameter value`s class ({0}) is not an instance of the parameter class ({1}) for parameter "{2}". ParameterListDescriptorImpl10=The input parameter class ({0}) is not an EnumeratedParameter.class ParameterListImpl0=Object`s class ({0}) is not an instance of the parameter class ({1}) for parameter "{2}". ParameterListImpl1= parameter value is invalid. ParameterListImpl2= the parameter value has not yet been set and has no default value. PerspectiveTransform0=PerspectiveTransform.createInverse PerspectiveTransform1=Divide by zero error. PixelAccessor0=The specified Rectangle is not completely contained within the Raster`s bounds. PixelAccessor1=The specified type is not one of the valid data types defined in DataBuffer. PixelAccessor2=The specified type is not large enough to hold the Raster`s pixel samples. PixelAccessor3=The pixel data described by the specified Raster`s SampleModel is not single-band and single-bit. PixelAccessor4=The specified type is not large enough to hold the image`s color/alpha components. PixelAccessor5=The image does not have a valid ColorModel that is compatible with the image`s SampleModel. PlanarImage0=There is no source corresponding to the specified index value. PlanarImage1=getGraphics() is not implemented in this class. PlanarImage2=Requested region cannot be represented by a single Raster. PlanarImage3=The supplied ColorModel is not compatible with this image`s SampleModel. PlanarImage4=The specified region, if not null, must intersect with the image`s bounds. PlanarImage5=The specified ColorModel is incompatible with the image SampleModel. PlanarImage6=No ColorModel is supplied and the image ColorModel is null. PlanarImage7=Null element encountered in sources Vector. PointOpImage0=The intersection of all the source bounds is empty. PointOpImage1=The user-supplied image bounds is empty. PointOpImage2=The user-supplied image bounds is not within the intersection of all the source bounds. PropertyChangeEventJAI0=The source of the PropertyChangeEvent is null. PropertyChangeEventJAI1=The old and new values of the PropertyChangeEvent are both null. PropertySourceChangeEvent0=The old value of the PropertySourceChangeEvent is null. PropertySourceChangeEvent1=The new value of the PropertySourceChangeEvent is null. ROI0=Can construct from single-banded images only. ROI1=Not yet implemented. ROI2=The requested area is not within the ROI. ROI3=mask argument array is too small. ROI4=Unsupported data type. ROI5=The supplied AffineTransform is null. ROI6=The supplied Interpolation object is null. ROIShape0=Mask argument array is too small. ROIShape1=Unknown polygon type. ROIShape2=Constructor parameter is null. ROIShape3=The supplied ROI parameter is null. RasterAccessor0=DataBuffer must have getOffsets().length equal to 1 or numBands. RasterAccessor1=Binary tag case encountered for non-binary SampleModel. RasterAccessor2=Requested rectangle bounds is not contained in the input raster`s bounds. RasterFactory0=Number of bands must be greater than 0. RasterFactory1=Bank indices array is null. RasterFactory2=bankIndices.length != bandOffsets.length RasterFactory3=Unsupported data type. RasterFactory4=Band offsets array is null. RasterFactory5=Offsets between bands must be less than the scanline stride. RasterFactory6=Pixel stride times width must be less than the scanline stride. RasterFactory7=Pixel stride must be greater than or equal to the offset between bands. RasterFactory8=This method does not support the input data type. RasterFactory9=parentX lies outside raster. RasterFactory10=parentY lies outside raster. RasterFactory11=(parentX + width) is outside raster. RasterFactory12=(parentY + height) is outside raster. RasterFactory13=Illegal value for transparency. RasterFactory14=Transparency cannot be opaque when useAlpha is true. RasterFactory15=bitsPerBands must be greater than 0. RasterFactory16=Size of array must be smaller than Integer.MAX_VALUE. RegistryFileParser0=Error in registry file at line number #{0,number,integer} RegistryFileParser1=Format expected: descriptor RegistryElementDescriptor-class-name RegistryFileParser2=Format expected: modeName factory-class-name product-name descriptor-name local-name RegistryFileParser3=Format expected: modeName factory-class-name descriptor-name RegistryFileParser4=Format expected: pref modeName descriptor-name product-name preferred-local-name other-local-name RegistryFileParser5=Format expected: productPref modeName descriptor-name preferred-product-name other-product-name RegistryFileParser6=Can not parse line. RegistryFileParser7=Can not set factory preferences for registry modes that do not support preferences. RegistryFileParser8=Local name does not map to a registered factory object. RegistryFileParser9=Can not set product preferences for registry modes that do not support preferences. RegistryFileParser10=Can not add registry mode since it already exists. RegistryFileParser11=Error while parsing JAI registry file "{0}" : RemoteImage0=Caught RemoteException, going to sleep. RemoteImage1=Source parameter may not be null. RemoteImage2=Rect must be null or must intersect the image bounds. RemoteImage3=Retries parameter must be positive. RemoteImage4=Timeout parameter must be positive. RenderableImageAdapter0=The supplied String parameter is null. RenderedImageList0=Argument must not be null. RenderedImageList1=The list must not be empty. RenderedImageList2=The specified object must be an instance of RenderedImage. RenderedImageList3=Index is out of bounds. RenderableGraphics0=Empty dimensions parameter. RenderableGraphics1=One of w and h must be positive for scaled rendering. RenderableOp2=No adequate CRIF exists in the registry. RenderableOp3=One of w or h must be non-zero. RenderedOp0= - Unable to render RenderedOp for this operation. RenderedOp3=Attempt to set a property on a rendered node! RenderedOp4=Attempted to set a synthetic property! RenderedOp5=Synthetic properties cannot be suppressed. RenderedOp6=pg must not be null. SourcelessOpImage0=Can not perform rectangle mapping between source and destinatioon because the image has no sources. TiledImage0=Cannot construct graphics objects for non-integral data types. TiledImage1=More releases than gets! TiledImage2=Cannot clear tiles while any tile is being held by a writer. TiledImageGraphics0=Cannot construct a TiledImageGraphics object from a TiledImage with non-integral data type. TiledImageGraphics1=Unable to derive an appropriate ColorModel. TiledImageGraphics2=Can not find the method: TiledImageGraphics3=The affine transformation is not invertible. TiledImageGraphics4=Fails to invoke the method: Warp0=Supplied warp destination array is too small. WarpAffine0=WarpAffine requires 3 coefficients each for X and Y coordinates. WarpCubic0=WarpCubic requires 10 coefficients each for X and Y coordinates. WarpGrid0=WarpPositions.length != 2*xNumCells + 1*yNumCells + 1. WarpPerspective0=WarpPerspective constructor requires a valid input; null is supplied. WarpPolynomial0=Wrong number of coefficients for X and/or Y coordinate supplied to polynomial warp. WarpPolynomial1=Insufficient number of points available to compute polynomial. WarpQuadratic0=WarpQuadratic requires 6 coefficients each for X and Y coordinates. WritableRenderedImageAdapter0=The TileObserver parameter supplied is null. WritableRenderedImageAdapter1=The Raster parameter supplied is null. jai-core-1.1.4/src/share/classes/javax/media/jai/RegistryFileParser.java0000644000175000017500000004504210203035544025772 0ustar mathieumathieu/* * $RCSfile: RegistryFileParser.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:19 $ * $State: Exp $ */ package javax.media.jai; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Reader; import java.io.StreamTokenizer; import java.net.URL; import java.text.MessageFormat; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Vector; import javax.media.jai.util.CaselessStringKey; /** * A class to parse the JAI registry file. * * @since JAI 1.1 */ class RegistryFileParser { /** * Load the OperationRegistry with the descriptors, * factories and their preferences from the input stream. */ static void loadOperationRegistry(OperationRegistry or, ClassLoader cl, InputStream is) throws IOException { (new RegistryFileParser(or, cl, is)).parseFile(); } /** * Load the OperationRegistry with the descriptors, * factories and their preferences from the URL. */ static void loadOperationRegistry(OperationRegistry or, ClassLoader cl, URL url) throws IOException { (new RegistryFileParser(or, cl, url)).parseFile(); } private URL url; private InputStream is; private ClassLoader classLoader; // The OperationRegistry being read in. private OperationRegistry or; private StreamTokenizer st; // The current token - the one last returned by StreamTokenizer private int token; // The current line number being parsed in the registry file. private int lineno; // Table used to map local-factory names to factory instances // on a per mode basis. private Hashtable localNamesTable; /** * Create a JAI registry file parser from an URL */ private RegistryFileParser(OperationRegistry or, ClassLoader cl, URL url) throws IOException { this(or, cl, url.openStream()); this.url = url; } /** * Create a JAI registry file parser from the InputStream */ private RegistryFileParser(OperationRegistry or, ClassLoader cl, InputStream is) throws IOException { if (or == null) or = JAI.getDefaultInstance().getOperationRegistry(); this.is = is; this.url = null; this.or = or; this.classLoader = cl; // Set up streamtokenizer BufferedReader reader = new BufferedReader(new InputStreamReader(is)); st = new StreamTokenizer(reader); st.commentChar('#'); st.eolIsSignificant(true); st.slashSlashComments(true); st.slashStarComments(true); token = st.ttype; lineno = -1; // Initialize a table to map local names to factories. localNamesTable = new Hashtable(); String modeNames[] = RegistryMode.getModeNames(); for (int i = 0; i < modeNames.length; i++) localNamesTable.put( new CaselessStringKey(modeNames[i]), new Hashtable()); } /** * Skip all the empty tokens generated due to empty lines * and comments. */ private int skipEmptyTokens() throws IOException { while (st.sval == null) { if (token == StreamTokenizer.TT_EOF) return token; token = st.nextToken(); } return token; } /** * Get an array of Strings of words in the * next line after skipping over empty and comment lines. */ private String[] getNextLine() throws IOException { if (skipEmptyTokens() == StreamTokenizer.TT_EOF) return null; Vector v = new Vector(); lineno = st.lineno(); while ((token != StreamTokenizer.TT_EOL) && (token != StreamTokenizer.TT_EOF)) { if (st.sval != null) v.addElement(st.sval); token = st.nextToken(); } if (v.size() == 0) return null; return (String[])v.toArray(new String[0]); } // Aliases for backward compatibility private static String[][] aliases = { { "odesc" , "descriptor" }, { "rif" , "rendered" }, { "crif" , "renderable" }, { "cif" , "collection" }, }; /** * Map old keywords to the new keywords */ private String mapName(String key) { for (int i = 0; i < aliases.length; i++) if (key.equalsIgnoreCase(aliases[i][0])) return aliases[i][1]; return key; } /** * Create an instance given the class name. */ private Object getInstance(String className) { try { Class descriptorClass = null; String errorMsg = null; // Since the classes listed in the registryFile can // reside anywhere (core, ext, classpath or the specified // classloader) we have to try every place. // First try the specified classloader if (classLoader != null) { try { descriptorClass = Class.forName(className, true, classLoader); } catch (Exception e) { errorMsg = e.getMessage(); } } // Next try the callee classloader if (descriptorClass == null) { try { descriptorClass = Class.forName(className); } catch (Exception e) { errorMsg = e.getMessage(); } } // Then try the System classloader (because the specified // classloader might be null and the callee classloader // might be an ancestor of the SystemClassLoader if (descriptorClass == null) { try { descriptorClass = Class.forName(className, true, ClassLoader.getSystemClassLoader()); } catch (Exception e) { errorMsg = e.getMessage(); } } if (descriptorClass == null) { registryFileError(errorMsg); return null; } return descriptorClass.newInstance(); } catch (Exception e) { registryFileError(e.getMessage()); e.printStackTrace(); } return null; } /** * Parse the entire registry file and load internal structures * with the info. */ boolean parseFile() throws IOException { // If the file has already been parsed do nothing. if (token == StreamTokenizer.TT_EOF) return true; String[] keys; token = st.nextToken(); while (token != StreamTokenizer.TT_EOF) { if ((keys = getNextLine()) == null) break; RegistryMode mode; String key = mapName(keys[0]); // This indicates a new registry mode to be added. if (key.equalsIgnoreCase("registryMode")) { mode = (RegistryMode)getInstance(keys[1]); if (mode != null) { if (RegistryMode.addMode(mode) == false) registryFileError( JaiI18N.getString("RegistryFileParser10")); } // Old format operation-descriptor line OR // the new generic RegistryElementDescriptor line } else if (key.equalsIgnoreCase("descriptor")) { registerDescriptor(keys); // If it is a registry mode name, then register the // factory object. } else if ((mode = RegistryMode.getMode(key)) != null) { registerFactory(mode, keys); // If the line starts with a "pref" there are two options } else if (key.equalsIgnoreCase("pref")) { key = mapName(keys[1]); // If what follows is the keyword "product" then // it is assumed to be setting product preferences // for the "rendered" mode (old file format) if (key.equalsIgnoreCase("product")) { setProductPreference( RegistryMode.getMode("rendered"), keys); // If it is followed by a modeName then it is // for setting preferences between factory object. } else if ((mode = RegistryMode.getMode(key)) != null) { setFactoryPreference(mode, keys); } else { registryFileError(JaiI18N.getString("RegistryFileParser4")); } // For setting product preferences } else if (key.equalsIgnoreCase("productPref")) { key = mapName(keys[1]); // If it is followed by a modeName then it is // for setting preferences between products if ((mode = RegistryMode.getMode(key)) != null) { setProductPreference(mode, keys); } else { registryFileError(JaiI18N.getString("RegistryFileParser5")); } } else { registryFileError(JaiI18N.getString("RegistryFileParser6")); } } // If this was read in from an URL, we created the InputStream // and so we should close it. if (url != null) is.close(); return true; } /** * Register a descriptor with operation registry. */ private void registerDescriptor(String[] keys) { if (keys.length >= 2) { RegistryElementDescriptor red = (RegistryElementDescriptor)getInstance(keys[1]); if (red != null) { try { or.registerDescriptor(red); } catch (Exception e) { registryFileError(e.getMessage()); } } } else { registryFileError(JaiI18N.getString("RegistryFileParser1")); } } /** * Register a factory instance against a registry mode * under given product and local-name. */ private void registerFactory(RegistryMode mode, String[] keys) { Object factory; if (mode.arePreferencesSupported()) { if (keys.length >= 5) { if ((factory = getInstance(keys[1])) != null) { try { or.registerFactory( mode.getName(), keys[3], keys[2], factory); mapLocalNameToObject(mode.getName(), keys[4], factory); } catch (Exception e) { registryFileError(e.getMessage()); } } } else { registryFileError( JaiI18N.getString("RegistryFileParser2")); } } else { if (keys.length >= 3) { if ((factory = getInstance(keys[1])) != null) { try { or.registerFactory( mode.getName(), keys[2], null, factory); } catch (Exception e) { registryFileError(e.getMessage()); } } } else { registryFileError( JaiI18N.getString("RegistryFileParser3")); } } } /** * Register a factory instance against a registry mode * under given product and local-name. */ private void setProductPreference(RegistryMode mode, String[] keys) { String modeName = mode.getName(); if (mode.arePreferencesSupported()) { if (keys.length >= 5) { try { or.setProductPreference( modeName, keys[2], keys[3], keys[4]); } catch (Exception e) { registryFileError(e.getMessage()); } } else { registryFileError( JaiI18N.getString("RegistryFileParser5")); } } else { registryFileError(JaiI18N.getString("RegistryFileParser9")); } } /** * Register a factory instance against a registry mode * under given product and local-name. */ private void setFactoryPreference(RegistryMode mode, String[] keys) { String modeName = mode.getName(); Object factory; if (mode.arePreferencesSupported()) { if (keys.length >= 6) { Object preferred = getObjectFromLocalName(modeName, keys[4]); Object other = getObjectFromLocalName(modeName, keys[5]); if ((preferred != null) && (other != null)) { try { or.setFactoryPreference( modeName, keys[2], keys[3], preferred, other); } catch (Exception e) { registryFileError(e.getMessage()); } } } else { registryFileError( JaiI18N.getString("RegistryFileParser4")); } } else { registryFileError(JaiI18N.getString("RegistryFileParser7")); } } /** * Map local names to factory instances, so that they can * be used to directly set preferences later. */ private void mapLocalNameToObject(String modeName, String localName, Object factory) { Hashtable modeTable = (Hashtable) localNamesTable.get(new CaselessStringKey(modeName)); modeTable.put(new CaselessStringKey(localName), factory); } /** * Get object registered under the local name for under the mode. */ private Object getObjectFromLocalName(String modeName, String localName) { Hashtable modeTable = (Hashtable) localNamesTable.get(new CaselessStringKey(modeName)); Object obj = modeTable.get(new CaselessStringKey(localName)); if (obj == null) registryFileError(localName + ": " + JaiI18N.getString("RegistryFileParser8")); return obj; } private boolean headerLinePrinted = false; /** * Print the line number and then print the passed in message. */ private void registryFileError(String msg) { if (!headerLinePrinted) { if (url != null) { errorMsg(JaiI18N.getString("RegistryFileParser11"), new Object[] { url.getPath() }); } headerLinePrinted = true; } errorMsg(JaiI18N.getString("RegistryFileParser0"), new Object[] { new Integer(lineno) }); if (msg != null) errorMsg(msg, null); } /** * Creates a MessageFormat object and set the * Locale to default and formats the message */ private void errorMsg(String key, Object[] args) { MessageFormat mf = new MessageFormat(key); mf.setLocale(Locale.getDefault()); if (System.err != null) System.err.println(mf.format(args)); } /** * Write the OperationRegistry out to the output stream. */ static void writeOperationRegistry(OperationRegistry or, OutputStream os) throws IOException { writeOperationRegistry(or, new BufferedWriter(new OutputStreamWriter(os))); } /** * Write the OperationRegistry out to the output stream. */ static void writeOperationRegistry(OperationRegistry or, BufferedWriter bw) throws IOException { // First cycle through all the descriptor classes Iterator dcit = RegistryMode.getDescriptorClasses().iterator(); String tab = " "; while (dcit.hasNext()) { Class descriptorClass = (Class)dcit.next(); List descriptors = or.getDescriptors(descriptorClass); // First write all the descriptors corresponding // to this descriptorClass bw.write("#"); bw.newLine(); bw.write("# Descriptors corresponding to class : " + descriptorClass.getName()); bw.newLine(); bw.write("#"); bw.newLine(); if ((descriptors == null) || (descriptors.size() <= 0)) { bw.write("# "); bw.newLine(); } else { Iterator it = descriptors.iterator(); while (it.hasNext()) { bw.write("descriptor" + tab); bw.write(it.next().getClass().getName()); bw.newLine(); } } bw.newLine(); // Now cycle through all registry modes associated // with this descriptorClass and write out the // factories and their preferences String modeNames[] = RegistryMode.getModeNames(descriptorClass); boolean empty; int i, j, k, l; for (i = 0; i < modeNames.length; i++) { bw.write("#"); bw.newLine(); bw.write("# Factories registered under mode : " + modeNames[i]); bw.newLine(); bw.write("#"); bw.newLine(); RegistryMode mode = RegistryMode.getMode(modeNames[i]); boolean prefs = mode.arePreferencesSupported(); String[] descriptorNames = or.getDescriptorNames(modeNames[i]); // Over all descriptor names for this mode. for (j = 0, empty = true; j < descriptorNames.length; j++) { if (prefs) { Vector productVector = or.getOrderedProductList(modeNames[i], descriptorNames[j]); if (productVector == null) continue; String[] productNames = (String[])productVector.toArray(new String[0]); // Over all products under which there are // factories registered under this descriptor name for (k = 0; k < productNames.length; k++) { List factoryList = or.getOrderedFactoryList(modeNames[i], descriptorNames[j], productNames[k]); Iterator fit = factoryList.iterator(); while (fit.hasNext()) { Object instance = fit.next(); if (instance == null) continue; bw.write(modeNames[i] + tab); bw.write(instance.getClass().getName() + tab); bw.write(productNames[k] + tab); bw.write(descriptorNames[j] + tab); bw.write(or.getLocalName(modeNames[i], instance)); bw.newLine(); empty = false; } } } else { Iterator fit = or.getFactoryIterator( modeNames[i], descriptorNames[j]); while (fit.hasNext()) { Object instance = fit.next(); if (instance == null) continue; bw.write(modeNames[i] + tab); bw.write(instance.getClass().getName() + tab); bw.write(descriptorNames[j]); bw.newLine(); empty = false; } } } if (empty) { bw.write("# "); bw.newLine(); } bw.newLine(); // If the mode does not support preferences // then just continue if (!prefs) { bw.write("#"); bw.newLine(); bw.write("# Preferences not supported for mode : " + modeNames[i]); bw.newLine(); bw.write("#"); bw.newLine(); bw.newLine(); continue; } // Next, write the product preferences for this mode bw.write("#"); bw.newLine(); bw.write("# Product preferences for mode : " + modeNames[i]); bw.newLine(); bw.write("#"); bw.newLine(); for (j = 0, empty = true; j < descriptorNames.length; j++) { String[][] productPrefs = or.getProductPreferences(modeNames[i], descriptorNames[j]); if (productPrefs == null) continue; for (k = 0; k < productPrefs.length; k++) { bw.write("productPref" + tab); bw.write(modeNames[i] + tab); bw.write(descriptorNames[j] + tab); bw.write(productPrefs[k][0] + tab); bw.write(productPrefs[k][1]); bw.newLine(); empty = false; } } if (empty) { bw.write("# "); bw.newLine(); } bw.newLine(); // Next, write the factory preferences for this mode bw.write("#"); bw.newLine(); bw.write("# Factory preferences for mode : " + modeNames[i]); bw.newLine(); bw.write("#"); bw.newLine(); // Over all descriptor names for this mode. for (j = 0, empty = true; j < descriptorNames.length; j++) { if (prefs) { Vector productVector = or.getOrderedProductList(modeNames[i], descriptorNames[j]); if (productVector == null) continue; String[] productNames = (String[])productVector.toArray(new String[0]); // Over all products under which there are // factories registered under this descriptor name for (k = 0; k < productNames.length; k++) { Object fprefs[][] = or.getFactoryPreferences( modeNames[i], descriptorNames[j], productNames[k]); if (fprefs == null) continue; for (l = 0; l < fprefs.length; l++) { bw.write("pref" + tab); bw.write(modeNames[i] + tab); bw.write(descriptorNames[j] + tab); bw.write(productNames[k] + tab); bw.write(or.getLocalName(modeNames[i], fprefs[l][0]) + tab); bw.write(or.getLocalName(modeNames[i], fprefs[l][1])); bw.newLine(); empty = false; } } } } if (empty) { bw.write("# "); bw.newLine(); } bw.newLine(); } } bw.flush(); } } jai-core-1.1.4/src/share/classes/javax/media/jai/CachedTile.java0000644000175000017500000000253110203035544024166 0ustar mathieumathieu/* * $RCSfile: CachedTile.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:05 $ * $State: Exp $ */ package javax.media.jai; import java.awt.image.RenderedImage; import java.awt.image.Raster; /** * Public interface for cached tiles used to * retrieve information about the tile. * * @since JAI 1.1 */ public interface CachedTile { /** Returns the image operation to which this * cached tile belongs. In Sun Microsystems * implementation, this is a RenderedImage. */ RenderedImage getOwner(); /** Returns the cached tile. In Sun Microsystems * implementation, this object is a Raster. */ Raster getTile(); /** Returns a cost metric associated with the tile. * This value is used to determine which tiles get * removed from the cache. */ Object getTileCacheMetric(); /** Returns the time stamp of the cached tile. */ long getTileTimeStamp(); /** Returns the memory size of the cached tile */ long getTileSize(); /** Returns information about which method * triggered a notification event. In the * Sun Microsystems implementation, events * include add, remove and update tile * information. */ int getAction(); } jai-core-1.1.4/src/share/classes/javax/media/jai/CollectionImage.java0000644000175000017500000004125610203035544025246 0ustar mathieumathieu/* * $RCSfile: CollectionImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:05 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Image; import java.beans.PropertyChangeListener; import java.lang.ref.WeakReference; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.Vector; import com.sun.media.jai.util.PropertyUtil; /** * An abstract superclass for classes representing a Collection * of images. It may be a Collection of * RenderedImages or RenderableImages, a * Collection of Collections that include images. * In other words, this class supports nested Collections, but * at the very bottom, there must be images associated with the * Collection objects. * * */ public abstract class CollectionImage implements ImageJAI, Collection { /** * A Collection of objects. It may be a * Collection of images of the same type, a * Collection of objects of the same type, each * containing an image, or a Collection of * Collections whose leaf objects are images * or objects that contain images. */ protected Collection imageCollection; /** * The CollectionImageFactory which created this * CollectionImage; may be null which * implies that the CollectionImage was not created * by a CollectionImageFactory. * * @since JAI 1.1 */ protected CollectionImageFactory imageFactory; private Boolean isFactorySet = Boolean.FALSE; /** * A helper object to manage firing events. * * @since JAI 1.1 */ protected PropertyChangeSupportJAI eventManager = null; /** * A helper object to manage the image properties. * * @since JAI 1.1 */ protected WritablePropertySourceImpl properties = null; /** * A Set of WeakReferences to the * sinks of this CollectionImage. * * @since JAI 1.1 */ protected Set sinks; /** * Default constructor. The imageCollection parameter is * null. Subclasses that use this constructor must either * set the imageCollection parameter themselves, or override * the methods defined in the Collection interface. * Otherwise, a NullPointerException may be thrown at a later * time when methods which use to the imageCollection * instance variable are invoked. */ protected CollectionImage() { eventManager = new PropertyChangeSupportJAI(this); properties = new WritablePropertySourceImpl(null, null, eventManager); } /** * Constructs a class that contains an image Collection. * * @param collection A Collection of objects that * include images. * * @throws IllegalArgumentException if collection is * null. */ public CollectionImage(Collection collection) { this(); if ( collection == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } imageCollection = collection; } /* ----- Element retrieval method. ----- */ /** * Returns the element at the given index in imageCollection. * If imageCollection is a List then the call is * forwarded to imageCollection; otherwise an array is created * by applying toArray() to imageCollection and * the indicated element of that array is returned. Note that in the * latter case no guarantee as to element ordering beyond that stated in * the specification of Collection.toArray(). * * @param index The index of the desired element. * @throws IndexOutOfBoundsException if the index is out of range * (index < 0 || index ≥ * imageCollection.size()). * * @since JAI 1.1 */ public Object get(int index) { if(index < 0 || index >= imageCollection.size()) { throw new IndexOutOfBoundsException(); // No message needed. } if(imageCollection instanceof List) { return ((List)imageCollection).get(index); } else { return imageCollection.toArray((Object[])null)[index]; } } /* ----- Image factory methods. ----- */ /** * Sets the imageFactory instance variable to the supplied * value. The parameter may be null. It is recommended * that this method be invoked as soon as the CollectionImage * is constructed. * * @param imageFactory The creating CollectionImageFactory or * null * @throws IllegalStateException if the corresponding instance variable * was already set. * * @since JAI 1.1 */ public void setImageFactory(CollectionImageFactory imageFactory) { synchronized(isFactorySet) { if(isFactorySet.booleanValue()) { throw new IllegalStateException(); } this.imageFactory = imageFactory; isFactorySet = Boolean.TRUE; } } /** * If this CollectionImage was created by a * CollectionImageFactory then return a reference to * that factory; otherwise return null. * * @since JAI 1.1 */ public CollectionImageFactory getImageFactory() { synchronized(isFactorySet) { return imageFactory; } } /* ----- Sink methods. ----- */ /** * Adds a sink to the set of sinks. * * @since JAI 1.1 */ public synchronized boolean addSink(Object sink) { if(sink == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if(sinks == null) { sinks = new HashSet(); } return sinks.add(new WeakReference(sink)); } /** * Removes a sink from the set of sinks. * * @return true if and only if the set of sinks * changed as a result of the call. * * @since JAI 1.1 */ public synchronized boolean removeSink(Object sink) { if (sink == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sinks == null) { return false; } boolean result = false; Iterator it = sinks.iterator(); while(it.hasNext()) { Object referent = ((WeakReference)it.next()).get(); if(referent == sink) { // Remove the sink. it.remove(); result = true; // Do not break: could be more than one. } else if(referent == null) { // A cleared reference: might as well remove it. it.remove(); // ignore return value here. } } return result; } /** * Retrieves the set of sinks or null if * there are none. * * @since JAI 1.1 */ public synchronized Set getSinks() { Set v = null; if (sinks != null && sinks.size() > 0) { v = new HashSet(sinks.size()); Iterator it = sinks.iterator(); while(it.hasNext()) { Object o = ((WeakReference)it.next()).get(); if (o != null) { v.add(o); } } if (v.size() == 0) { v = null; } } return v; } /** * Removes all sinks from the set of sinks. * * @since JAI 1.1 */ public synchronized void removeSinks() { sinks = null; } /* ----- WritablePropertySource methods. ----- */ /** * Returns an array of Strings recognized as names by this * property source. If no property names match, null * will be returned. * * @return An array of Strings which are the valid * property names or null if there are none. */ public String[] getPropertyNames() { return properties.getPropertyNames(); } /** * Returns an array of Strings recognized as names by * this property source that begin with the supplied prefix. If * no property names are recognized, or no property names match, * null will be returned. * The comparison is done in a case-independent manner. * *

      The default implementation calls * getPropertyNames() and searches the list of names * for matches. * * @return An array of Strings giving the valid * property names or null if there are none. * * @throws IllegalArgumentException if prefix * is null. */ public String[] getPropertyNames(String prefix) { return PropertyUtil.getPropertyNames(getPropertyNames(), prefix); } /** * Returns the class expected to be returned by a request for * the property with the specified name. If this information * is unavailable, null will be returned. * * @return The Class expected to be return by a * request for the value of this property or null. * * @exception IllegalArgumentException if name * is null. * * @since JAI 1.1 */ public Class getPropertyClass(String name) { return properties.getPropertyClass(name); } /** * Returns the specified property. The default implementation * returns java.awt.Image.UndefinedProperty. * * @exception IllegalArgumentException if name * is null. */ public Object getProperty(String name) { return properties.getProperty(name); } /** * Returns the specified property. The default implementation * returns java.awt.Image.UndefinedProperty. * * @exception IllegalArgumentException if name * is null. * @deprecated as of JAI 1.1. */ public Object getProperty(String name, Collection collection) { return Image.UndefinedProperty; } /** * Sets a property on a CollectionImage. Some * CollectionImage subclasses may ignore attempts to set * properties. * * @param name a String containing the property's name. * @param value the property, as a general Object. * * @throws IllegalArgumentException If name or * value is null. * * @since JAI 1.1 */ public void setProperty(String name, Object value) { properties.setProperty(name, value); } /** * Removes the named property from the CollectionImage. * Some CollectionImage subclasses may ignore attempts to * remove properties. * * @since JAI 1.1 */ public void removeProperty(String name) { properties.removeProperty(name); } /* ----- PropertyChangeEmitter methods. ----- */ /** * Add a PropertyChangeListener to the listener list. The * listener is registered for all properties. * * @since JAI 1.1 */ public void addPropertyChangeListener(PropertyChangeListener listener) { eventManager.addPropertyChangeListener(listener); } /** * Add a PropertyChangeListener for a specific property. The * listener will be invoked only when a call on * firePropertyChange names that specific property. The case of * the name is ignored. * * @since JAI 1.1 */ public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { eventManager.addPropertyChangeListener(propertyName, listener); } /** * Remove a PropertyChangeListener from the listener list. This * removes a PropertyChangeListener that was registered for all * properties. * * @since JAI 1.1 */ public void removePropertyChangeListener(PropertyChangeListener listener) { eventManager.removePropertyChangeListener(listener); } /** * Remove a PropertyChangeListener for a specific property. The case * of the name is ignored. * * @since JAI 1.1 */ public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { eventManager.removePropertyChangeListener(propertyName, listener); } /* ----- Collection methods. ----- */ /** Returns the number of elements in this Collection. */ public int size() { return imageCollection.size(); } /** * Returns true if this Collection * contains no elements. */ public boolean isEmpty() { return imageCollection.isEmpty(); } /** * Returns true if this Collection * contains the specified object. */ public boolean contains(Object o) { return imageCollection.contains(o); } /** * Returns an Iterator over the elements in this * Collection. */ public Iterator iterator() { return imageCollection.iterator(); } /** * Returns an array containing all of the elements in this * Collection. */ public Object[] toArray() { return imageCollection.toArray(); } /** * Returns an array containing all of the elements in this collection * whose runtime type is that of the specified array. * * @throws ArrayStoreException if the runtime type of the specified array * is not a supertype of the runtime type of every element in this * Collection. */ public Object[] toArray(Object[] a) { return imageCollection.toArray(a); } /** * Adds the specified object to this Collection. * * @return true if and only if the parameter is added to the * Collection. */ public boolean add(Object o) { return imageCollection.add(o); } /** * Removes the specified object from this Collection. * * @return true if and only if the parameter is removed * from the Collection. */ public boolean remove(Object o) { return imageCollection.remove(o); } /** * Returns true if this Collection contains * all of the elements in the specified Collection. */ public boolean containsAll(Collection c) { return imageCollection.containsAll(c); } /** * Adds all of the elements in the specified Collection * to this Collection. * * @return true if this Collection changed * as a result of the call. */ public boolean addAll(Collection c) { return imageCollection.addAll(c); } /** * Removes all this collection's elements that are also contained in the * specified Collection. * * @return true if this Collection changed * as a result of the call. */ public boolean removeAll(Collection c) { return imageCollection.removeAll(c); } /** * Retains only the elements in this Collection that are * contained in the specified Collection. * * @return true if this Collection changed * as a result of the call. */ public boolean retainAll(Collection c) { return imageCollection.retainAll(c); } /** Removes all of the elements from this Collection. */ public void clear() { imageCollection.clear(); } } jai-core-1.1.4/src/share/classes/javax/media/jai/PixelAccessor.java0000644000175000017500000023417210203035544024755 0ustar mathieumathieu/* * $RCSfile: PixelAccessor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:15 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.ComponentColorModel; import java.awt.image.ComponentSampleModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferInt; import java.awt.image.DataBufferShort; import java.awt.image.DataBufferUShort; import java.awt.image.IndexColorModel; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.PackedColorModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.SinglePixelPackedSampleModel; import java.awt.image.WritableRaster; import java.util.Vector; import com.sun.media.jai.util.DataBufferUtils; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.util.JDKWorkarounds; /** * This is a utility class that may be used to access the pixel data * stored in a RenderedImage's Rasters, as * well as performing pixel-to-color data translation based on the * image's SampleModel and ColorModel. It * also provides several static methods to determine information about * the image data. * *

      This class is intended to help classes that need to access the * pixel and/or color data of a RenderedImage, such as an * OpImage, in an optimized fashion. Most of the variables * defined in this class are public so that other classes may use them * directly. However, the variables are also declared final * so that other classes can not modify their values. * *

      In general, the pixel data of a RenderedImage may * be obtained by calling the getPixels() method. By * definition, the pixel data of an image are the data described * by the image's SampleModel and stored in the image's * Rasters. No consideration of any kind is given to the * image's ColorModel. If no error is found, the pixel * data are returned in the primitive arrays of the type specified by * the caller in an unpacked format, along with access information. * Therefore, the specified data type must be one of the valid types * defined in DataBuffer and large enough (in bit depth) * to hold the pixel data of the image. * *

      The pixel data of a binary image may be obtained in a packed * format by calling the getPackedPixels() method. It * returns the data in a packed byte array, with 8 pixels * packed into 1 byte. The format of the data in the array is * similar to the format described by the * MultiPixelPackedSampleModel, where the end of each * scanline is padded to the end of the byte if necessary. Note that this * method returns a valid result only if and only if the image is a * single-band bit image, that is, each pixel has only 1 sample with a * sample size of 1 bit. * *

      Two corresponding "set" methods are also provided for setting the * computed pixel data back into the Raster's * DataBuffer: setPixels() for unpacked data, * and setPackedPixels() for packed data. It is very * important that the caller uses the correct "set" method that matches * the "get" method used to obtain the data, or errors will occur. * *

      The color/alpha data of the RenderedImage may be * obtained by calling the getComponents() method which * returns the unnormalized data in the ColorSpace specified * in the ColorModel, or the getComponentsRGB() * method which returns the data scaled from 0 to 255 in the default sRGB * ColorSpace. These methods retrieve the pixel data from * the Raster, and perform the pixel-to-color translation. * Therefore, in order for these two methods to return a valid result, the * image must have a valid ColorModel. * *

      Similarly, two "set" methods may be used to perform the * color-to-pixel translation, and set the pixel data back to the * Raster's DataBuffer. Again, it is important * that the "get" and "set" methods are matched up correctly. * *

      In addition, several static methods are included in this class * for the convenience of OpImage developers, who may use them to * help determine the appropriate destination SampleModel * type. * * @since JAI 1.1 * */ public final class PixelAccessor { /** Tag for single-bit data type. */ public static final int TYPE_BIT = -1; /** The image's SampleModel. */ public final SampleModel sampleModel; /** The image's ColorModel. */ public final ColorModel colorModel; // The following information comes from the image's SampleModel. /** * true if the image has a * ComponentSampleModel; * false otherwise. */ public final boolean isComponentSM; /** * true if the image has a * MultiPixelPackedSampleModel; * false otherwise. */ public final boolean isMultiPixelPackedSM; /** * true if the image has a * SinglePixelPackedSampleModel; * false otherwise. */ public final boolean isSinglePixelPackedSM; /** The data type of the pixel samples, determined based on the sample size. */ public final int sampleType; /** * The type of the DataBuffer's data array used to store the * pixel data by the image's SampleModel. This is the same * value as that returned by SampleModel.getDataType(). */ public final int bufferType; /** * The type of the primitive array used to transfer the pixel data by * the image's SampleModel. This is the same value as * that returned by SampleModel.getTransferType(). */ public final int transferType; /** * The number of bands (samples) per pixel. This is the same value * as that returned by SampleModel.getNumBands(). */ public final int numBands; /** * The size, in number of bits, of all the pixel samples. This is * the same array as that returned by * SampleModel.getSampleSize(). */ public final int[] sampleSize; /** * Set to true if the pixel data of this image may be * packed into a byte array. That is, each pixel has * 1 sample (1 band) with a sample size of 1 bit. If this variable * is true, getPackedPixels() should return * a valid result, with 8 pixels packed into 1 byte. */ public final boolean isPacked; // The following information come from the image's ColorModel. /** * Set to true if the image has a non-null * ColorModel which is compatible with the image's * SampleModel; false otherwise. */ public final boolean hasCompatibleCM; /** * Set to true if the image has a * ComponentColorModel; * false otherwise. */ public final boolean isComponentCM; /** * Set to true if the image has an * IndexColorModel; * false otherwise. */ public final boolean isIndexCM; /** * Set to true if the image has a * PackedColorModel; * false otherwise. */ public final boolean isPackedCM; /** * The type of the color/alpha components, determined based on the * component size. */ public final int componentType; /** * The total number of color/alpha components in the image's * ColorModel. This is the same value as that * returned by ColorModel.getNumComponents(). */ public final int numComponents; /** * The size, in number of bits, of all the color/alpha components. * This is the same array as that returned by * ColorModel.getComponentSize(). */ public final int[] componentSize; /** * Returns the image's SampleModel. * * @throws IllegalArgumentException if image is * null. */ private static SampleModel getSampleModel(RenderedImage image) { if(image == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return image.getSampleModel(); } /** * Constructs a PixelAccessor from a * RenderedImage. * The RenderedImage must have a valid * SampleModel, but may or may not have a valid * ColorModel. * * @param image The image whose data are to be accessed. * * @throws IllegalArgumentException If image is * null, or if the image does not have a valid * SampleModel. */ public PixelAccessor(RenderedImage image) { this(getSampleModel(image), image.getColorModel()); } /** * Constructs a PixelAccessor given a valid * SampleModel and a (possibly null) * ColorModel. * * @param sm The SampleModel for the image to be accessed. * Must be valid. * @param cm The ColorModel for the image to be accessed. * May be null. * @throws IllegalArgumentException If sm is null. */ public PixelAccessor(SampleModel sm, ColorModel cm) { if ( sm == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } sampleModel = sm; colorModel = cm; // Information from the SampleModel. isComponentSM = sampleModel instanceof ComponentSampleModel; isMultiPixelPackedSM = sampleModel instanceof MultiPixelPackedSampleModel; isSinglePixelPackedSM = sampleModel instanceof SinglePixelPackedSampleModel; bufferType = sampleModel.getDataType(); transferType = sampleModel.getTransferType(); numBands = sampleModel.getNumBands(); sampleSize = sampleModel.getSampleSize(); sampleType = isComponentSM ? bufferType : getType(sampleSize); // Indicates whether the pixel data may be stored in packed format. isPacked = sampleType == TYPE_BIT && numBands == 1; // Information from the ColorModel. hasCompatibleCM = colorModel != null && JDKWorkarounds.areCompatibleDataModels(sampleModel, colorModel); if (hasCompatibleCM) { isComponentCM = colorModel instanceof ComponentColorModel; isIndexCM = colorModel instanceof IndexColorModel; isPackedCM = colorModel instanceof PackedColorModel; numComponents = colorModel.getNumComponents(); componentSize = colorModel.getComponentSize(); int tempType = getType(componentSize); componentType = (tempType == TYPE_BIT) ? DataBuffer.TYPE_BYTE : tempType; } else { isComponentCM = false; isIndexCM = false; isPackedCM = false; numComponents = numBands; componentSize = sampleSize; componentType = sampleType; } } /** * Determines the data type based on the data sizes in number of bits. * Note that for data size between 9 and 16, this method returns * TYPE_USHORT, and for size between 17 and 32, this * method returns TYPE_INT. The minimum valid size is 1, * and the maximum valid size is 64. * * @param size An array containing the bit width of each band. * @return The minimum size data type which can hold any band. */ private static int getType(int[] size) { int maxSize = size[0]; // maximum sample size for (int i = 1; i < size.length; i++) { maxSize = Math.max(maxSize, size[i]); } int type; if (maxSize < 1) { type = DataBuffer.TYPE_UNDEFINED; } else if (maxSize == 1) { type = TYPE_BIT; } else if (maxSize <= 8) { type = DataBuffer.TYPE_BYTE; } else if (maxSize <= 16) { type = DataBuffer.TYPE_USHORT; } else if (maxSize <= 32) { type = DataBuffer.TYPE_INT; } else if (maxSize <= 64) { type = DataBuffer.TYPE_DOUBLE; } else { type = DataBuffer.TYPE_UNDEFINED; } return type; } /** * Determines the pixel type based on the SampleModel. * The pixel type signifies the data type for a PixelAccessor. * For ComponentSampleModel, the pixel type is the same * as the type of the DataBuffer used to store the pixel * data. For all other types of SampleModel, the pixel * type is determined based on the sample sizes. * * @param sm The SampleModel of the image. * @return The pixel type for this sample model. */ public static int getPixelType(SampleModel sm) { return sm instanceof ComponentSampleModel ? sm.getDataType() : getType(sm.getSampleSize()); } /** * Returns the largest data type of all the sources. This method * may be used to determine the pixel sample type of a destination * in the default situation. It guarantees that the destination can * store the resulting pixel values without losing any precision. * The pixel type signifies the data type for a PixelAccessor. * *

      If all the sources are single-bit images, this method returns * TYPE_BIT (defined in this class) so that the * destination does not use unnecessary memory for some operations. * This includes all images whose SampleModel is * single-banded and whose sample size is 1, regardless of the type * of ColorModel the image may have. * If an operation does not wish to deal with packed data, it * should use TYPE_BYTE for pixel computation. * *

      If there is no object in the source Vector, this * method returns TYPE_UNDEFINED. All the objects in * the source Vector must be RenderedImages. * *

      When determining the result, only information from each image's * SampleModel is used. No consideration is given to the * image's ColorModel. * * @param sources A Vector of RenderedImage * sources. * @return The largest data type which can accomodate all sources. * @throws IllegalArgumentException If sources is * null. * @throws ClassCastException If any object in sources * is not a RenderedImage. */ public static int getDestPixelType(Vector sources) { if ( sources == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } int type = DataBuffer.TYPE_UNDEFINED; int size = sources.size(); if (size > 0) { RenderedImage src = (RenderedImage)sources.get(0); SampleModel sm = src.getSampleModel(); type = getPixelType(sm); for (int i = 1; i < size; i++) { src = (RenderedImage)sources.get(i); sm = src.getSampleModel(); int t = getPixelType(sm); // Only int can handle ushort/short combination. type = (type == DataBuffer.TYPE_USHORT && t == DataBuffer.TYPE_SHORT) || (type == DataBuffer.TYPE_SHORT && t == DataBuffer.TYPE_USHORT) ? DataBuffer.TYPE_INT : Math.max(type, t); } } return type; } /** * Returns the smallest number of bands of all the sources. * This method may be used to determine the number of bands a * destination should have in the default situation. It guarantees * that every destination band has a corresponding source band. * *

      In general, if an operation has multiple sources, and some * sources have 1 band and others have multiple bands, the single * band may be applied to the multiple bands one at a time. (An * example of this would be the MultiplyOpImage). Therefore, * in such a case, this method returns the smallest band count among the * multi-band sources. * *

      If there is no object in the source Vector, this * method returns 0. All the objects in the source Vector * must be RenderedImages. * *

      When determining the result, only information from each image's * SampleModel are used. No consideration is given to the * image's ColorModel. * * @param sources A Vector of RenderedImage * sources. * @return The minimum number of destination bands. * @throws IllegalArgumentException If sources is * null. * @throws ClassCastException If any object in sources * is not a RenderedImage. */ public static int getDestNumBands(Vector sources) { if ( sources == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } int bands = 0; int size = sources.size(); if (size > 0) { RenderedImage src = (RenderedImage)sources.get(0); SampleModel sm = src.getSampleModel(); bands = sm.getNumBands(); for (int i = 1; i < size; i++) { src = (RenderedImage)sources.get(i); sm = src.getSampleModel(); int b = sm.getNumBands(); bands = bands == 1 || b == 1 ? Math.max(bands, b) : Math.min(bands, b); } } return bands; } /** * Returns true if the destination and/or all the * sources are single-bit, single-band images, and their pixel * data may be packed into a byte array. * If so, then the operations may be done in the packed format. * @param srcs The array of source PixelAccesors. * @param dst The destination PixelAccesor. * @return true if a packed operation is possible. */ public static boolean isPackedOperation(PixelAccessor[] srcs, PixelAccessor dst) { boolean canBePacked = dst.isPacked; if (canBePacked && srcs != null) { for (int i = 0; i < srcs.length; i++) { canBePacked = canBePacked && srcs[i].isPacked; if (!canBePacked) { // no need to check further break; } } } return canBePacked; } /** * Returns true if the destination and the source * are both single-bit, single-band images, and their pixel * data may be packed into a byte array. * If so, then the operations may be done in the packed format. * @param srcs The source PixelAccesor. * @param dst The destination PixelAccesor. * @return true if a packed operation is possible. */ public static boolean isPackedOperation(PixelAccessor src, PixelAccessor dst) { return src.isPacked && dst.isPacked; } /** * Returns true if the destination and both sources * are all single-bit, single-band images, and their pixel * data may be packed into a byte array. * If so, then the operations may be done in the packed format. * @param src1 The first source PixelAccesor. * @param src2 The second source PixelAccesor. * @param dst The destination PixelAccesor. * @return true if a packed operation is possible. */ public static boolean isPackedOperation(PixelAccessor src1, PixelAccessor src2, PixelAccessor dst) { return src1.isPacked && src2.isPacked && dst.isPacked; } /** * Returns a region of the pixel data within a Raster * in an unpacked primitive array. The returned data are * retrieved from the Raster's DataBuffer; * no pixel-to-color translation is performed. * *

      The primitive array is of the type specified by the * type argument. It must be one of the valid data * types defined in DataBuffer and large (in bit depth) * enough to hold the pixel samples, or an exception will be thrown. * This means type should be greater than or equal to * sampleType. * *

      The Rectangle specifies the region of interest * within which the pixel data are to be retrieved. It must be * completely inside the Raster's boundary, or else * this method throws an exception. * *

      This method tries to avoid copying data as much as possible. * If it is unable to reformat the pixel data in the way requested, * or if the pixels do not have enough data to satisfy the request, * this method throws an exception. * * @param raster The Raster that contains the pixel data. * @param rect The region of interest within the Raster * where the pixels are accessed. * @param type The type of the primitive array used to return the * pixel samples. * @param isDest Indicates whether this Raster is a * destination Raster. That is, its pixels have * not been computed. * * @return The pixel data in an UnpackedImageData object. * @throws IllegalArgumentException If type is not a * valid data type defined in DataBuffer, or * is not large enough to hold the pixel samples from the * specified Raster. * @throws IllegalArgumentException If rect is not * contained by the bounds of the specified Raster. */ public UnpackedImageData getPixels(Raster raster, Rectangle rect, int type, boolean isDest) { if (!raster.getBounds().contains(rect)) { throw new IllegalArgumentException( JaiI18N.getString("PixelAccessor0")); } if (type < DataBuffer.TYPE_BYTE || type > DataBuffer.TYPE_DOUBLE) { // unknown data type throw new IllegalArgumentException( JaiI18N.getString("PixelAccessor1")); } if (type < sampleType || (sampleType == DataBuffer.TYPE_USHORT && type == DataBuffer.TYPE_SHORT)) { // type not large enough throw new IllegalArgumentException( JaiI18N.getString("PixelAccessor2")); } if (isComponentSM) { return getPixelsCSM(raster, rect, type, isDest); } else { // The total number of data elements needed. int size = rect.width * rect.height * numBands; Object data = null; switch (type) { case DataBuffer.TYPE_BYTE: byte[] bd; if (isDest) { bd = new byte[size]; } else { if (isMultiPixelPackedSM && transferType == DataBuffer.TYPE_BYTE) { bd = (byte[])raster.getDataElements( rect.x, rect.y, rect.width, rect.height, null); } else { bd = new byte[size]; int[] d = raster.getPixels(rect.x, rect.y, rect.width, rect.height, (int[])null); for (int i = 0; i < size; i++) { bd[i] = (byte)(d[i] & 0xff); } } } data = repeatBand(bd, numBands); break; case DataBuffer.TYPE_USHORT: short[] usd; if (isDest) { usd = new short[size]; } else { if (isMultiPixelPackedSM && transferType == DataBuffer.TYPE_USHORT) { usd = (short[])raster.getDataElements( rect.x, rect.y, rect.width, rect.height, null); } else { usd = new short[size]; int[] d = raster.getPixels(rect.x, rect.y, rect.width, rect.height, (int[])null); for (int i = 0; i < size; i++) { usd[i] = (short)(d[i] & 0xffff); } } } data = repeatBand(usd, numBands); break; case DataBuffer.TYPE_SHORT: short[] sd = new short[size]; if (!isDest) { int[] d = raster.getPixels(rect.x, rect.y, rect.width, rect.height, (int[])null); for (int i = 0; i < size; i++) { sd[i] = (short)d[i]; } } data = repeatBand(sd, numBands); break; case DataBuffer.TYPE_INT: return getPixelsInt(raster, rect, isDest); case DataBuffer.TYPE_FLOAT: return getPixelsFloat(raster, rect, isDest); case DataBuffer.TYPE_DOUBLE: return getPixelsDouble(raster, rect, isDest); } return new UnpackedImageData( raster, rect, type, data, numBands, numBands * rect.width, getInterleavedOffsets(numBands), isDest & (raster instanceof WritableRaster)); } } /** * Returns the pixel data in a pixel-interleaved, unpacked array * where the Raster has a ComponentSampleModel. * @return The pixel data in an UnpackedImageData object. */ private UnpackedImageData getPixelsCSM(Raster raster, Rectangle rect, int type, boolean isDest) { Object data = null; int pixelStride, lineStride; int[] offsets; boolean set; //ComponentSampleModel sm = (ComponentSampleModel)sampleModel; // For bug 4696966: when the raster bounds is not coincide with a // tile bounds. ComponentSampleModel sm = (ComponentSampleModel)raster.getSampleModel(); if (type == sampleType) { // Data are stored in the requested array type; no need to copy. DataBuffer db = raster.getDataBuffer(); int[] bankIndices = sm.getBankIndices(); switch (sampleType) { case DataBuffer.TYPE_BYTE: byte[][] bbd = ((DataBufferByte)db).getBankData(); byte[][] bd = new byte[numBands][]; for (int b = 0; b < numBands; b++) { bd[b] = bbd[bankIndices[b]]; } data = bd; break; case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: short[][] sbd = sampleType == DataBuffer.TYPE_USHORT ? ((DataBufferUShort)db).getBankData() : ((DataBufferShort)db).getBankData(); short[][] sd = new short[numBands][]; for (int b = 0; b < numBands; b++) { sd[b] = sbd[bankIndices[b]]; } data = sd; break; case DataBuffer.TYPE_INT: int[][] ibd = ((DataBufferInt)db).getBankData(); int[][] id = new int[numBands][]; for (int b = 0; b < numBands; b++) { id[b] = ibd[bankIndices[b]]; } data = id; break; case DataBuffer.TYPE_FLOAT: float[][] fbd = DataBufferUtils.getBankDataFloat(db); float[][] fd = new float[numBands][]; for (int b = 0; b < numBands; b++) { fd[b] = fbd[bankIndices[b]]; } data = fd; break; case DataBuffer.TYPE_DOUBLE: double[][] dbd = DataBufferUtils.getBankDataDouble(db); double[][] dd = new double[numBands][]; for (int b = 0; b < numBands; b++) { dd[b] = dbd[bankIndices[b]]; } data = dd; break; } pixelStride = sm.getPixelStride(); lineStride = sm.getScanlineStride(); // Determine offsets. int[] dbOffsets = db.getOffsets(); // DataBuffer offsets int x = rect.x - raster.getSampleModelTranslateX(); int y = rect.y - raster.getSampleModelTranslateY(); offsets = new int[numBands]; for (int b = 0; b < numBands; b++) { offsets[b] = sm.getOffset(x, y, b) + dbOffsets[bankIndices[b]]; } set = false; // no need to copy } else { // need to reformat data switch (type) { case DataBuffer.TYPE_INT: return getPixelsInt(raster, rect, isDest); case DataBuffer.TYPE_FLOAT: return getPixelsFloat(raster, rect, isDest); case DataBuffer.TYPE_DOUBLE: return getPixelsDouble(raster, rect, isDest); /* * Since the requested type must be greater than or equal to * sampleType, if type is byte, sampleType must also be byte, * because the smallest sampleType of ComponentSampleModel is * byte. This case falls into the above uncopied case. * * If the Raster is a destination, then the pixel data have * not been computed and stored in the buffer yet. * Just create a new array, but no need to copy anything. */ default: // byte to ushort or short // The total number of data elements needed. int size = rect.width * rect.height * numBands; short[] sd = new short[size]; if (!isDest) { // need to copy byte data // Only byte is smaller than short or ushort. UnpackedImageData uid = getPixelsCSM(raster, rect, sampleType, isDest); byte[][] bdata = uid.getByteData(); for (int b = 0; b < numBands; b++) { byte[] bd = bdata[b]; // band data int lo = uid.getOffset(b); // line offset for (int i = b, h = 0; h < rect.height; h++) { int po = lo; // pixel offset lo += uid.lineStride; for (int w = 0; w < rect.width; w++) { sd[i] = (short)(bd[po] & 0xff); po += uid.pixelStride; i += numBands; } } } } data = repeatBand(sd, numBands); break; } pixelStride = numBands; lineStride = pixelStride * rect.width; offsets = getInterleavedOffsets(numBands); set = isDest & (raster instanceof WritableRaster); } return new UnpackedImageData(raster, rect, type, data, pixelStride, lineStride, offsets, set); } /** * Returns the pixel data in an pixel-interleaved, unpacked, * integer array. * @return The pixel data in an UnpackedImageData object. */ private UnpackedImageData getPixelsInt(Raster raster, Rectangle rect, boolean isDest) { // The total number of data elements needed. int size = rect.width * rect.height * numBands; /* * If the Raster is destination, then the pixel data have * not been computed and stored in the buffer yet. * Just create a new array, but no need to copy anything. * Otherwise, copy the data from the Raster. */ int[] d = isDest ? new int[size] : raster.getPixels(rect.x, rect.y, rect.width, rect.height, (int[])null); return new UnpackedImageData( raster, rect, DataBuffer.TYPE_INT, repeatBand(d, numBands), numBands, numBands * rect.width, getInterleavedOffsets(numBands), isDest & (raster instanceof WritableRaster)); } /** * Returns the pixel data in an pixel-interleaved, unpacked, * float array. * @return The pixel data in an UnpackedImageData object. */ private UnpackedImageData getPixelsFloat(Raster raster, Rectangle rect, boolean isDest) { // The total number of data elements needed. int size = rect.width * rect.height * numBands; /* * If the Raster is destination, then the pixel data have * not been computed and stored in the buffer yet. * Just create a new array, but no need to copy anything. * Otherwise, copy the data from the Raster. */ float[] d = isDest ? new float[size] : raster.getPixels(rect.x, rect.y, rect.width, rect.height, (float[])null); return new UnpackedImageData( raster, rect, DataBuffer.TYPE_FLOAT, repeatBand(d, numBands), numBands, numBands * rect.width, getInterleavedOffsets(numBands), isDest & (raster instanceof WritableRaster)); } /** * Returns the pixel data in an pixel-interleaved, unpacked, * double array. * @return The pixel data in an UnpackedImageData object. */ private UnpackedImageData getPixelsDouble(Raster raster, Rectangle rect, boolean isDest) { // The total number of data elements needed. int size = rect.width * rect.height * numBands; /* * If the Raster is destination, then the pixel data have * not been computed and stored in the buffer yet. * Just create a new array, but no need to copy anything. * Otherwise, copy the data from the Raster. */ double[] d = isDest ? new double[size] : raster.getPixels(rect.x, rect.y, rect.width, rect.height, (double[])null); return new UnpackedImageData( raster, rect, DataBuffer.TYPE_DOUBLE, repeatBand(d, numBands), numBands, numBands * rect.width, getInterleavedOffsets(numBands), isDest & (raster instanceof WritableRaster)); } /** Repeats a one-dimensional array into a two-dimensional array. */ private byte[][] repeatBand(byte[] d, int numBands) { byte[][] data = new byte[numBands][]; for (int i = 0; i < numBands; i++) { data[i] = d; } return data; } private short[][] repeatBand(short[] d, int numBands) { short[][] data = new short[numBands][]; for (int i = 0; i < numBands; i++) { data[i] = d; } return data; } private int[][] repeatBand(int[] d, int numBands) { int[][] data = new int[numBands][]; for (int i = 0; i < numBands; i++) { data[i] = d; } return data; } private float[][] repeatBand(float[] d, int numBands) { float[][] data = new float[numBands][]; for (int i = 0; i < numBands; i++) { data[i] = d; } return data; } private double[][] repeatBand(double[] d, int numBands) { double[][] data = new double[numBands][]; for (int i = 0; i < numBands; i++) { data[i] = d; } return data; } /** Returns pixel interleaved offsets for copy case. */ private int[] getInterleavedOffsets(int numBands) { int[] offsets = new int[numBands]; for (int i = 0; i < numBands; i++) { offsets[i] = i; } return offsets; } /** * Sets a region of the pixel data within a Raster * using a primitive array. This method copies data only if * the set flag in UnpackedImageData is * true. Performs clamping by default. * *

      The UnpackedImageData should be obtained by * calling the getPixels() method. * * @param uid The UnpackedImageData object to set. * @throws IllegalArgumentException If the uid is * null. */ public void setPixels(UnpackedImageData uid) { if ( uid == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } setPixels(uid, true); } /** * Sets a region of the pixel data within a Raster * using a primitive array. This method only copies data only if * the set flag in UnpackedImageData is * true. * *

      The UnpackedImageData should be obtained by * calling the getPixels() method. * * @param uid The UnpackedImageData object to set. * @param clamp A boolean set to true if clamping * is to be performed. * @throws IllegalArgumentException If the uid is * null. */ public void setPixels(UnpackedImageData uid, boolean clamp) { if ( uid == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (!uid.convertToDest) { return; } if (clamp) { // clamp all array elements switch (sampleType) { case DataBuffer.TYPE_BYTE: clampByte(uid.data, uid.type); break; case DataBuffer.TYPE_USHORT: clampUShort(uid.data, uid.type); break; case DataBuffer.TYPE_SHORT: clampShort(uid.data, uid.type); break; case DataBuffer.TYPE_INT: clampInt(uid.data, uid.type); break; case DataBuffer.TYPE_FLOAT: clampFloat(uid.data, uid.type); break; } } WritableRaster raster = (WritableRaster)uid.raster; Rectangle rect = uid.rect; int type = uid.type; switch (type) { case DataBuffer.TYPE_BYTE: byte[] bd = uid.getByteData(0); if (isMultiPixelPackedSM && transferType == DataBuffer.TYPE_BYTE) { raster.setDataElements(rect.x, rect.y, rect.width, rect.height, bd); } else { int size = bd.length; int[] d = new int[size]; for (int i = 0; i < size; i++) { d[i] = bd[i] & 0xff; } raster.setPixels(rect.x, rect.y, rect.width, rect.height, d); } break; case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: short[] sd = uid.getShortData(0); if (isComponentSM) { // The only time this needs to set to is a byte buffer. UnpackedImageData buid = getPixelsCSM(raster, rect, DataBuffer.TYPE_BYTE, true); byte[][] bdata = buid.getByteData(); for (int b = 0; b < numBands; b++) { byte[] d = bdata[b]; int lo = buid.getOffset(b); for (int i = b, h = 0; h < rect.height; h++) { int po = lo; lo += buid.lineStride; for (int w = 0; w < rect.width; w++) { d[po] = (byte)sd[i]; po += buid.pixelStride; i += numBands; } } } } else if (isMultiPixelPackedSM && transferType == DataBuffer.TYPE_USHORT) { raster.setDataElements(rect.x, rect.y, rect.width, rect.height, sd); } else { int size = sd.length; int[] d = new int[size]; if (type == DataBuffer.TYPE_USHORT) { for (int i = 0; i < size; i++) { d[i] = sd[i] & 0xffff; } } else { for (int i = 0; i < size; i++) { d[i] = sd[i]; } } raster.setPixels(rect.x, rect.y, rect.width, rect.height, d); } break; case DataBuffer.TYPE_INT: raster.setPixels(rect.x, rect.y, rect.width, rect.height, uid.getIntData(0)); break; case DataBuffer.TYPE_FLOAT: raster.setPixels(rect.x, rect.y, rect.width, rect.height, uid.getFloatData(0)); break; case DataBuffer.TYPE_DOUBLE: raster.setPixels(rect.x, rect.y, rect.width, rect.height, uid.getDoubleData(0)); break; } } /** Clamps the data array. */ private void clampByte(Object data, int type) { int bands, size; switch (type) { case DataBuffer.TYPE_USHORT: short[][] usd = (short[][])data; bands = usd.length; for (int j = 0; j < bands; j++) { short[] d = usd[j]; size = d.length; for (int i = 0; i < size; i++) { int n = d[i] & 0xffff; d[i] = (short)(n > 0xff ? 0xff : n); } } break; case DataBuffer.TYPE_SHORT: short[][] sd = (short[][])data; bands = sd.length; for (int j = 0; j < bands; j++) { short[] d = sd[j]; size = d.length; for (int i = 0; i < size; i++) { int n = d[i]; d[i] = (short)(n > 0xff ? 0xff : (n < 0 ? 0 : n)); } } break; case DataBuffer.TYPE_INT: int[][] id = (int[][])data; bands = id.length; for (int j = 0; j < bands; j++) { int[] d = id[j]; size = d.length; for (int i = 0; i < size; i++) { int n = d[i]; d[i] = n > 0xff ? 0xff : (n < 0 ? 0 : n); } } break; case DataBuffer.TYPE_FLOAT: float[][] fd = (float[][])data; bands = fd.length; for (int j = 0; j < bands; j++) { float[] d = fd[j]; size = d.length; for (int i = 0; i < size; i++) { float n = d[i]; d[i] = n > 0xff ? 0xff : (n < 0 ? 0 : n); } } break; case DataBuffer.TYPE_DOUBLE: double[][] dd = (double[][])data; bands = dd.length; for (int j = 0; j < bands; j++) { double[] d = dd[j]; size = d.length; for (int i = 0; i < size; i++) { double n = d[i]; d[i] = n > 0xff ? 0xff : (n < 0 ? 0 : n); } } break; } } private void clampUShort(Object data, int type) { int bands, size; switch (type) { case DataBuffer.TYPE_INT: int[][] id = (int[][])data; bands = id.length; for (int j = 0; j < bands; j++) { int[]d = id[j]; size = d.length; for (int i = 0; i < size; i++) { int n = d[i]; d[i] = n > 0xffff ? 0xffff : (n < 0 ? 0 : n); } } break; case DataBuffer.TYPE_FLOAT: float[][] fd = (float[][])data; bands = fd.length; for (int j = 0; j < bands; j++) { float[] d = fd[j]; size = d.length; for (int i = 0; i < size; i++) { float n = d[i]; d[i] = n > 0xffff ? 0xffff : (n < 0 ? 0 : n); } } break; case DataBuffer.TYPE_DOUBLE: double[][] dd = (double[][])data; bands = dd.length; for (int j = 0; j < bands; j++) { double[] d = dd[j]; size = d.length; for (int i = 0; i < size; i++) { double n = d[i]; d[i] = n > 0xffff ? 0xffff : (n < 0 ? 0 : n); } } break; } } private void clampShort(Object data, int type) { int bands, size; switch (type) { case DataBuffer.TYPE_INT: int[][] id = (int[][])data; bands = id.length; for (int j = 0; j < bands; j++) { int[] d = id[j]; size = d.length; for (int i = 0; i < size; i++) { int n = d[i]; d[i] = n > Short.MAX_VALUE ? Short.MAX_VALUE : (n < Short.MIN_VALUE ? Short.MIN_VALUE : n); } } break; case DataBuffer.TYPE_FLOAT: float[][] fd = (float[][])data; bands = fd.length; for (int j = 0; j < bands; j++) { float[] d = fd[j]; size = d.length; for (int i = 0; i < size; i++) { float n = d[i]; d[i] = n > Short.MAX_VALUE ? Short.MAX_VALUE : (n < Short.MIN_VALUE ? Short.MIN_VALUE : n); } } break; case DataBuffer.TYPE_DOUBLE: double[][] dd = (double[][])data; bands = dd.length; for (int j = 0; j < bands; j++) { double[] d = dd[j]; size = d.length; for (int i = 0; i < size; i++) { double n = d[i]; d[i] = n > Short.MAX_VALUE ? Short.MAX_VALUE : (n < Short.MIN_VALUE ? Short.MIN_VALUE : n); } } break; } } private void clampInt(Object data, int type) { int bands, size; switch (type) { case DataBuffer.TYPE_FLOAT: float[][] fd = (float[][])data; bands = fd.length; for (int j = 0; j < bands; j++) { float[] d = fd[j]; size = d.length; for (int i = 0; i < size; i++) { float n = d[i]; d[i] = n > Integer.MAX_VALUE ? Integer.MAX_VALUE : (n < Integer.MIN_VALUE ? Integer.MIN_VALUE : n); } } break; case DataBuffer.TYPE_DOUBLE: double[][] dd = (double[][])data; bands = dd.length; for (int j = 0; j < bands; j++) { double[] d = dd[j]; size = d.length; for (int i = 0; i < size; i++) { double n = d[i]; d[i] = n > Integer.MAX_VALUE ? Integer.MAX_VALUE : (n < Integer.MIN_VALUE ? Integer.MIN_VALUE : n); } } break; } } private void clampFloat(Object data, int type) { int bands, size; switch (type) { case DataBuffer.TYPE_DOUBLE: double[][] dd = (double[][])data; bands = dd.length; for (int j = 0; j < bands; j++) { double[] d = dd[j]; size = d.length; for (int i = 0; i < size; i++) { double n = d[i]; d[i] = n > Float.MAX_VALUE ? Float.MAX_VALUE : (n < -Float.MAX_VALUE ? -Float.MAX_VALUE : n); } } break; } } /** * Returns a region of the pixel data within a Raster * in a packed byte array. The returned data are * retrieved from the Raster's DataBuffer; * no pixel-to-color translation is performed. * *

      This method only returns a valid result when the pixels are * single-band and single-bit. All other types of data result in * an exception. The data are packed in such a format that eight * pixels are packed into one byte, and the end of each scanline is * padded with zeros to the end of the byte. * *

      In general, this method is called when operations are to be * performed on the bit data in a packed format directly, to save * memory usage. The static method isPackedOperation * should be used to determine whether the destination and/or its sources * are suitable for performing operations to a packed array. * *

      The Rectangle specifies the region of interest * within which the pixel data are to be retrieved. It must be * completely inside the Raster's boundary, or * this method will throw an exception. * * @param raster The Raster that contains the pixel data. * @param rect The region of interest within the Raster * where the pixels are accessed. * @param isDest Indicates whether this Raster is a * destination Raster. That is, its pixels have * not been computed. * @param coerceZeroOffset If true the returned * PackedImageData will be forced to have a * bitOffset and offset of zero * and a lineStride of (rect.width+7)/8. * The coercedZeroOffset field of the returned * PackedImageData will be set to true. * @return The PackedImageData with its data filled in. * * @throws IllegalArgumentException If data described by the * Raster's SampleModel are not * single-band and single-bit. * @throws IllegalArgumentException If rect is not * within the bounds of the specified Raster. */ public PackedImageData getPackedPixels(Raster raster, Rectangle rect, boolean isDest, boolean coerceZeroOffset) { if (!isPacked) { throw new IllegalArgumentException( JaiI18N.getString("PixelAccessor3")); } if (!raster.getBounds().contains(rect)) { throw new IllegalArgumentException( JaiI18N.getString("PixelAccessor0")); } byte[] data; // packed pixels int lineStride, offset, bitOffset; // access information boolean set; // true if need to and can set data if (isMultiPixelPackedSM) { set = isDest; if(coerceZeroOffset) { data = ImageUtil.getPackedBinaryData(raster, rect); lineStride = (rect.width + 7)/8; offset = bitOffset = 0; } else { MultiPixelPackedSampleModel sm = (MultiPixelPackedSampleModel)sampleModel; DataBuffer db = raster.getDataBuffer(); int dbOffset = db.getOffset(); int x = rect.x - raster.getSampleModelTranslateX(); int y = rect.y - raster.getSampleModelTranslateY(); int smLineStride = sm.getScanlineStride(); int minOffset = sm.getOffset(x, y) + dbOffset; int maxOffset = sm.getOffset(x + rect.width - 1, y) + dbOffset; int numElements = maxOffset - minOffset + 1; // per line int smBitOffset = sm.getBitOffset(x); switch (bufferType) { // DataBuffer type case DataBuffer.TYPE_BYTE: // no need to copy data = ((DataBufferByte)db).getData(); lineStride = smLineStride; offset = minOffset; bitOffset = smBitOffset; set = false; // no need to set for destination break; // Copy even if it's destination so they can easily // be set back. case DataBuffer.TYPE_USHORT: lineStride = numElements * 2; // 2 bytes for each ushort offset = smBitOffset / 8; bitOffset = smBitOffset % 8; data = new byte[lineStride * rect.height]; short[] sd = ((DataBufferUShort)db).getData(); for (int i = 0, h = 0; h < rect.height; h++) { for (int w = minOffset; w <= maxOffset; w++) { short d = sd[w]; data[i++] = (byte)((d >>> 8) & 0xff); data[i++] = (byte)(d & 0xff); } minOffset += smLineStride; maxOffset += smLineStride; } break; case DataBuffer.TYPE_INT: lineStride = numElements * 4; // 4 bytes for each int offset = smBitOffset / 8; bitOffset = smBitOffset % 8; data = new byte[lineStride * rect.height]; int[] id = ((DataBufferInt)db).getData(); for (int i = 0, h = 0; h < rect.height; h++) { for (int w = minOffset; w <= maxOffset; w++) { int d = id[w]; data[i++] = (byte)((d >>> 24) & 0xff); data[i++] = (byte)((d >>> 16) & 0xff); data[i++] = (byte)((d >>> 8) & 0xff); data[i++] = (byte)(d & 0xff); } minOffset += smLineStride; maxOffset += smLineStride; } break; default: throw new RuntimeException(); // should never get here } } } else { // unknown SampleModel lineStride = (rect.width + 7) / 8; offset = 0; bitOffset = 0; set = isDest & (raster instanceof WritableRaster); data = new byte[lineStride * rect.height]; if (!isDest) { // copy one line at a time to int size = lineStride * 8; // avoid using too much memory int[] p = new int[size]; for (int i = 0, h = 0; h < rect.height; h++) { p = raster.getPixels(rect.x, rect.y + h, rect.width, 1, p); for (int w = 0; w < size; w += 8) { data[i++] = (byte)(p[w] << 7 | p[w+1] << 6 | p[w+2] << 5 | p[w+3] << 4 | p[w+4] << 3 | p[w+5] << 2 | p[w+6] << 1 | p[w+7]); } } } } return new PackedImageData(raster, rect, data, lineStride, offset, bitOffset, coerceZeroOffset, set); } /** * Sets a region of the pixel data within a Raster * using a primitive array. This method copies data only if * the set flag in PackedImageData is * true. * *

      The PackedImageData should be obtained by * calling the getPackedPixels() method. * * @param pid The PackedImageData object whose pixels * are to be written. * @throws IllegalArgumentException If the pid is * null. */ public void setPackedPixels(PackedImageData pid) { if ( pid == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (!pid.convertToDest) { return; } Raster raster = pid.raster; Rectangle rect = pid.rect; byte[] data = pid.data; if (isMultiPixelPackedSM) { if(pid.coercedZeroOffset) { ImageUtil.setPackedBinaryData(data, (WritableRaster)raster, rect); } else { MultiPixelPackedSampleModel sm = (MultiPixelPackedSampleModel)sampleModel; DataBuffer db = raster.getDataBuffer(); int dbOffset = db.getOffset(); int x = rect.x - raster.getSampleModelTranslateX(); int y = rect.y - raster.getSampleModelTranslateY(); int lineStride = sm.getScanlineStride(); int minOffset = sm.getOffset(x, y) + dbOffset; int maxOffset = sm.getOffset(x + rect.width - 1, y) + dbOffset; // Only need to set for buffer types of ushort and int. switch (bufferType) { case DataBuffer.TYPE_USHORT: short[] sd = ((DataBufferUShort)db).getData(); for (int i = 0, h = 0; h < rect.height; h++) { for (int w = minOffset; w <= maxOffset; w++) { sd[w] = (short)(data[i++] << 8 | data[i++]); } minOffset += lineStride; maxOffset += lineStride; } break; case DataBuffer.TYPE_INT: int[] id = ((DataBufferInt)db).getData(); for (int i = 0, h = 0; h < rect.height; h++) { for (int w = minOffset; w <= maxOffset; w++) { id[w] = data[i++] << 24 | data[i++] << 16 | data[i++] << 8 | data[i++]; } minOffset += lineStride; maxOffset += lineStride; } break; } } } else { /* * The getPackedData() method should set "set" to false if * the Raster is not writable. * Copy one line at a time to avoid using too much memory. */ WritableRaster wr = (WritableRaster)raster; int size = pid.lineStride * 8; int[] p = new int[size]; for (int i = 0, h = 0; h < rect.height; h++) { for (int w = 0; w < size; w += 8) { p[w] = (data[i] >>> 7) & 0x1; p[w+1] = (data[i] >>> 6) & 0x1; p[w+2] = (data[i] >>> 5) & 0x1; p[w+3] = (data[i] >>> 4) & 0x1; p[w+4] = (data[i] >>> 3) & 0x1; p[w+5] = (data[i] >>> 2) & 0x1; p[w+6] = (data[i] >>> 1) & 0x1; p[w+7] = data[i] & 0x1; i++; } wr.setPixels(rect.x, rect.y + h, rect.width, 1, p); } } } /** * Returns an array of unnormalized color/alpha components in the * ColorSpace defined in the image's * ColorModel. This method retrieves the pixel data * within the specified rectangular region from the * Raster, performs the pixel-to-color translation based * on the image's ColorModel, and returns the components * in the order specified by the ColorSpace. * *

      In order for this method to return a valid result, the * image must have a valid ColorModel that is compatible * with the image's SampleModel. Further, the * SampleModel and ColorModel must have * the same transferType. * *

      The component data are stored in a primitive array of the * type specified by the type argument. It must be one * of the valid data types defined in DataBuffer and * large (in bit depth) enough to hold the color/alpha components, * or an exception is thrown. This means type should * be greater than or equal to componentType. To avoid * extra array copy, it is best to use * DataBuffer.TYPE_INT for this argument. * *

      The Rectangle specifies the region of interest * within which the pixel data are to be retrieved. It must be * completely inside the Raster's boundary, or else * this method throws an exception. * * @param raster The Raster that contains the pixel data. * @param rect The region of interest within the Raster * where the pixels are accessed. * @param type The type of the primitive array used to return the * color/alpha components with. * @return The UnpackedImageData with its data filled in. * * @throws IllegalArgumentException If the image does not have a valid * ColorModel that is compatible with its * SampleModel. * @throws IllegalArgumentException If type is not a * valid data type defined in DataBuffer, or * is not large enough to hold the translated color/alpha * components. * @throws IllegalArgumentException If rect is not * contained by the bounds of the specified Raster. */ public UnpackedImageData getComponents(Raster raster, Rectangle rect, int type) { if (!hasCompatibleCM) { throw new IllegalArgumentException( JaiI18N.getString("PixelAccessor5")); } if (!raster.getBounds().contains(rect)) { throw new IllegalArgumentException( JaiI18N.getString("PixelAccessor0")); } if (type < DataBuffer.TYPE_BYTE || type > DataBuffer.TYPE_DOUBLE) { // unknown data type throw new IllegalArgumentException( JaiI18N.getString("PixelAccessor1")); } if (type < componentType || (componentType == DataBuffer.TYPE_USHORT && type == DataBuffer.TYPE_SHORT)) { // type not large enough throw new IllegalArgumentException( JaiI18N.getString("PixelAccessor4")); } // Get color/alpha components in an integer array. int size = rect.width * rect.height * numComponents; int[] ic = new int[size]; int width = rect.x + rect.width; int height = rect.y + rect.height; for (int i = 0, y = rect.y; y < height; y++) { for (int x = rect.x; x < width; x++) { Object p = raster.getDataElements(x, y, null); colorModel.getComponents(p, ic, i); i += numComponents; } } // Reformat components into the specified data type. Object data = null; switch (type) { case DataBuffer.TYPE_BYTE: byte[] bc = new byte[size]; for (int i = 0; i < size; i++) { bc[i] = (byte)(ic[i] & 0xff); } data = repeatBand(bc, numComponents); break; case DataBuffer.TYPE_USHORT: short[] usc = new short[size]; for (int i = 0; i < size; i++) { usc[i] = (short)(ic[i] & 0xffff); } data = repeatBand(usc, numComponents); break; case DataBuffer.TYPE_SHORT: short[] sc = new short[size]; for (int i = 0; i < size; i++) { sc[i] = (short)ic[i]; } data = repeatBand(sc, numComponents); break; case DataBuffer.TYPE_INT: data = repeatBand(ic, numComponents); break; case DataBuffer.TYPE_FLOAT: float[] fc = new float[size]; for (int i = 0; i < size; i++) { fc[i] = ic[i]; } data = repeatBand(fc, numComponents); break; case DataBuffer.TYPE_DOUBLE: double[] dc = new double[size]; for (int i = 0; i < size; i++) { dc[i] = ic[i]; } data = repeatBand(dc, numComponents); break; } return new UnpackedImageData( raster, rect, type, data, numComponents, numComponents * rect.width, getInterleavedOffsets(numComponents), raster instanceof WritableRaster); } /** * Given an array of unnormalized color/alpha components, this * method performs color-to-pixel translation, and sets the * translated pixel data back to the Raster within * a specific region. It is very important that the components * array along with access information are obtained by calling * the getComponents() method, or errors * will occur. * *

      In order for this method to return a valid result, the * image must have a valid ColorModel that is compatible * with the image's SampleModel. Further, the * SampleModel and ColorModel must have * the same transferType. * *

      This method sets data only if the set flag in * UnpackedImageData is true. * * @param uid The UnpackedImageData whose data is to be set. * @throws IllegalArgumentException If the uid is * null. */ public void setComponents(UnpackedImageData uid) { if ( uid == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (!uid.convertToDest) { return; } WritableRaster raster = (WritableRaster)uid.raster; Rectangle rect = uid.rect; int type = uid.type; int size = rect.width * rect.height * numComponents; int[] ic = null; switch (type) { case DataBuffer.TYPE_BYTE: byte[] bc = uid.getByteData(0); ic = new int[size]; for (int i = 0; i < size; i++) { ic[i] = bc[i] & 0xff; } break; case DataBuffer.TYPE_USHORT: short[] usc = uid.getShortData(0); ic = new int[size]; for (int i = 0; i < size; i++) { ic[i] = usc[i] & 0xffff; } break; case DataBuffer.TYPE_SHORT: short[] sc = uid.getShortData(0); ic = new int[size]; for (int i = 0; i < size; i++) { ic[i] = sc[i]; } break; case DataBuffer.TYPE_INT: ic = uid.getIntData(0); break; case DataBuffer.TYPE_FLOAT: float[] fc = uid.getFloatData(0); ic = new int[size]; for (int i = 0; i < size; i++) { ic[i] = (int)fc[i];; } break; case DataBuffer.TYPE_DOUBLE: double[] dc = uid.getDoubleData(0); ic = new int[size]; for (int i = 0; i < size; i++) { ic[i] = (int)dc[i]; } break; } int width = rect.x + rect.width; int height = rect.y + rect.height; for (int i = 0, y = rect.y; y < height; y++) { for (int x = rect.x; x < width; x++) { Object p = colorModel.getDataElements(ic, i, null); raster.setDataElements(x, y, p); i += numComponents; } } } /** * Returns an array of color/alpha components scaled from 0 to 255 * in the default sRGB ColorSpace. This method * retrieves the pixel data within the specified rectangular region * from the Raster, performs the pixel-to-color translation * based on the image's ColorModel, and returns the * components in the order specified by the ColorSpace. * *

      In order for this method to return a valid result, the * image must have a valid ColorModel that is compatible * with the image's SampleModel. Further, the * SampleModel and ColorModel must have * the same transferType. * *

      The component data are stored in a two-dimensional, * band-interleaved, byte array, because the components * are always scaled from 0 to 255. Red is band 0, green is band 1, * blue is band 2, and alpha is band 3. * *

      The Rectangle specifies the region of interest * within which the pixel data are to be retrieved. It must be * completely inside the Raster's boundary, or * this method will throw an exception. * * @param raster The Raster that contains the pixel data. * @param rect The region of interest within the Raster * where the pixels are accessed. * @return The UnpackedImageData with its data filled in. * * @throws IllegalArgumentException If the image does not have a valid * ColorModel that is compatible with its * SampleModel. * @throws IllegalArgumentException If rect is not * contained by the bounds of the specified Raster. */ public UnpackedImageData getComponentsRGB(Raster raster, Rectangle rect) { if (!hasCompatibleCM) { throw new IllegalArgumentException( JaiI18N.getString("PixelAccessor5")); } if (!raster.getBounds().contains(rect)) { throw new IllegalArgumentException( JaiI18N.getString("PixelAccessor0")); } int size = rect.width * rect.height; byte[][] data = new byte[4][size]; byte[] r = data[0]; // red byte[] g = data[1]; // green byte[] b = data[2]; // blue byte[] a = data[3]; // alpha // Get color/alpha components in an integer array. int maxX = rect.x + rect.width; int maxY = rect.y + rect.height; if(isIndexCM) { // Cast the CM and get the size of the ICM tables. IndexColorModel icm = (IndexColorModel)colorModel; int mapSize = icm.getMapSize(); // Load the ICM tables. byte[] reds = new byte[mapSize]; icm.getReds(reds); byte[] greens = new byte[mapSize]; icm.getGreens(greens); byte[] blues = new byte[mapSize]; icm.getBlues(blues); byte[] alphas = null; if(icm.hasAlpha()) { alphas = new byte[mapSize]; icm.getAlphas(alphas); } // Get the index values. int[] indices = raster.getPixels(rect.x, rect.y, rect.width, rect.height, (int[])null); // Use the ICM tables to get the [A]RGB values. if(alphas == null) { // No alpha. for (int i = 0, y = rect.y; y < maxY; y++) { for (int x = rect.x; x < maxX; x++) { int index = indices[i]; r[i] = reds[index]; g[i] = greens[index]; b[i] = blues[index]; i++; } } } else { // Alpha. for (int i = 0, y = rect.y; y < maxY; y++) { for (int x = rect.x; x < maxX; x++) { int index = indices[i]; r[i] = reds[index]; g[i] = greens[index]; b[i] = blues[index]; a[i] = alphas[index]; i++; } } } } else { // XXX If ColorSpaceJAI is implemented use the // Raster-based methods here. // Not an IndexColorModel: use the "slow method". for (int i = 0, y = rect.y; y < maxY; y++) { for (int x = rect.x; x < maxX; x++) { Object p = raster.getDataElements(x, y, null); r[i] = (byte)colorModel.getRed(p); g[i] = (byte)colorModel.getGreen(p); b[i] = (byte)colorModel.getBlue(p); a[i] = (byte)colorModel.getAlpha(p); i++; } } } return new UnpackedImageData( raster, rect, DataBuffer.TYPE_BYTE, data, 1, rect.width, new int[4], // all entries automatically initialized to 0 raster instanceof WritableRaster); } /** * Given an array of normalized (between 0 and 255) alpha/RGB color * components, this method performs color-to-pixel translation, and * sets the translated pixel data back to the Raster * within a specific region. It is very important that the components * array along with access information are obtained by calling * the getComponentsRGB() method, or errors * will occur. * *

      In order for this method to return a valid result, the * image must have a valid ColorModel that is compatible * with the image's SampleModel. Furthermore, the * SampleModel and ColorModel must have * the same transferType. * *

      This method sets data only if the set flag in * UnpackedImageData is true. * * @param uid The UnpackedImageData to set. * @throws IllegalArgumentException If the uid is * null. */ public void setComponentsRGB(UnpackedImageData uid) { if ( uid == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (!uid.convertToDest) { return; } byte[][] data = uid.getByteData(); byte[] r = data[0]; // red byte[] g = data[1]; // green byte[] b = data[2]; // blue byte[] a = data[3]; // alpha WritableRaster raster = (WritableRaster)uid.raster; Rectangle rect = uid.rect; int maxX = rect.x + rect.width; int maxY = rect.y + rect.height; for (int i = 0, y = rect.y; y < maxY; y++) { for (int x = rect.x; x < maxX; x++) { int rgb = (a[i] << 24) | (b[i] << 16) | (g[i] << 8) | r[i]; Object p = colorModel.getDataElements(rgb, null); raster.setDataElements(x, y, p); i++; } } } } jai-core-1.1.4/src/share/classes/javax/media/jai/FloatDoubleColorModel.java0000644000175000017500000011655210336476314026405 0ustar mathieumathieu/* * $RCSfile: FloatDoubleColorModel.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-11-16 00:26:52 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Point; import java.awt.Transparency; import java.awt.color.ColorSpace; import java.awt.image.ColorModel; import java.awt.image.ComponentColorModel; import java.awt.image.ComponentSampleModel; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; /** * A ColorModel class that works with pixel values that * represent color and alpha information as separate samples, using * float or double elements. This class can be used with an arbitrary * ColorSpace. The number of color samples in the pixel * values must be same as the number of color components in the * ColorSpace. There may be a single alpha sample. * *

      Sample values are taken as ranging from 0.0 to 1.0; that is, * when converting to 8-bit RGB, a multiplication by 255 is performed * and values outside of the range 0-255 are clamped at the closest * endpoint. * *

      For maximum efficiency, pixel data being interpreted by this * class should be in the sRGB color space. This will result in * only the trivial conversion (scaling by 255 and dividing by any * premultiplied alpha) to be performed. Other color spaces require * more general conversions. * *

      For those methods that use a primitive array pixel * representation of type transferType, the array length * is the same as the number of color and alpha samples. Color * samples are stored first in the array followed by the alpha sample, * if present. The order of the color samples is specified by the * ColorSpace. Typically, this order reflects the name * of the color space type. For example, for TYPE_RGB, * index 0 corresponds to red, index 1 to green, and index 2 to blue. * The transfer types supported are * DataBuffer.TYPE_FLOAT, * DataBuffer.TYPE_DOUBLE. * *

      The translation from pixel values to color/alpha components for * display or processing purposes is a one-to-one correspondence of * samples to components. * *

      Methods that use a single int pixel representation throw an * IllegalArgumentException. * *

      A FloatDoubleColorModel can be used in * conjunction with a ComponentSampleModelJAI. * * @see java.awt.image.ColorModel * @see java.awt.color.ColorSpace * @see java.awt.image.ComponentSampleModel * @see ComponentSampleModelJAI */ public class FloatDoubleColorModel extends ComponentColorModel { /** * The associated ColorSpace. * * @since JAI 1.1 */ protected ColorSpace colorSpace; /** * The type or family of the associated ColorSpace. * * @since JAI 1.1 */ protected int colorSpaceType; /** * The number of components of the associated ColorSpace. * * @since JAI 1.1 */ protected int numColorComponents; /** * The number of components represented by this ColorModel. * This will differ from the number of components of the associated * ColorSpace if there is an alpha channel. * * @since JAI 1.1 */ protected int numComponents; /** * Specifies what alpha values can be represented by this * ColorModel. * * @since JAI 1.1 */ protected int transparency; /** * Whether this ColorModel supports alpha. * * @since JAI 1.1 */ protected boolean hasAlpha; /** * Whether alpha is premultiplied. * * @since JAI 1.1 */ protected boolean isAlphaPremultiplied; private static int[] bitsHelper(int transferType, ColorSpace colorSpace, boolean hasAlpha) { int numBits = (transferType == DataBuffer.TYPE_FLOAT) ? 32 : 64; int numComponents = colorSpace.getNumComponents(); if (hasAlpha) { ++numComponents; } int[] bits = new int[numComponents]; for (int i = 0; i < numComponents; i++) { bits[i] = numBits; } return bits; } /** * Constructs a ComponentColorModel from the * specified parameters. Color components will be in the specified * ColorSpace. hasAlpha indicates * whether alpha information is present. If hasAlpha * is true, then the boolean isAlphaPremultiplied * specifies how to interpret color and alpha samples in pixel * values. If the boolean is true, color samples are * assumed to have been multiplied by the alpha sample. The * transparency specifies what alpha values can be * represented by this color model. The transferType * is the type of primitive array used to represent pixel values. * * @param colorSpace The ColorSpace associated with * this color model. * @param hasAlpha If true, this color model supports alpha. * @param isAlphaPremultiplied If true, alpha is premultiplied. * @param transparency Specifies what alpha values can be represented * by this color model. * @param transferType Specifies the type of primitive array used to * represent pixel values, one of * DataBuffer.TYPE_FLOAT or TYPE_DOUBLE. * @throws IllegalArgumentException If the transfer type is not * DataBuffer.TYPE_FLOAT or TYPE_DOUBLE. * * @see java.awt.color.ColorSpace * @see java.awt.Transparency */ public FloatDoubleColorModel(ColorSpace colorSpace, boolean hasAlpha, boolean isAlphaPremultiplied, int transparency, int transferType) { super(colorSpace, bitsHelper(transferType, colorSpace, hasAlpha), hasAlpha, isAlphaPremultiplied, transparency, transferType); if (transferType != DataBuffer.TYPE_FLOAT && transferType != DataBuffer.TYPE_DOUBLE) { throw new IllegalArgumentException(JaiI18N.getString("FloatDoubleColorModel0")); } this.colorSpace = colorSpace; this.colorSpaceType = colorSpace.getType(); this.numComponents = this.numColorComponents = colorSpace.getNumComponents(); if (hasAlpha) { ++numComponents; } this.transparency = transparency; this.hasAlpha = hasAlpha; this.isAlphaPremultiplied = isAlphaPremultiplied; } /** * Throws an IllegalArgumentException, since pixel * values for this ColorModel are not conveniently * representable as a single int. */ public int getRed(int pixel) { throw new IllegalArgumentException(JaiI18N.getString("FloatDoubleColorModel1")); } /** * Throws an IllegalArgumentException, since pixel * values for this ColorModel are not conveniently * representable as a single int. */ public int getGreen(int pixel) { throw new IllegalArgumentException(JaiI18N.getString("FloatDoubleColorModel2")); } /** * Throws an IllegalArgumentException, since pixel * values for this ColorModel are not conveniently * representable as a single int. */ public int getBlue(int pixel) { throw new IllegalArgumentException(JaiI18N.getString("FloatDoubleColorModel3")); } /** * Throws an IllegalArgumentException, since pixel * values for this ColorModel are not conveniently * representable as a single int. */ public int getAlpha(int pixel) { throw new IllegalArgumentException(JaiI18N.getString("FloatDoubleColorModel4")); } /** * Throws an IllegalArgumentException, since pixel * values for this ColorModel are not conveniently * representable as a single int. */ public int getRGB(int pixel) { throw new IllegalArgumentException(JaiI18N.getString("FloatDoubleColorModel5")); } private final int clamp(float value) { // Ensure NaN maps to 0 return (value >= 0.0F) ? ((value > 255.0F) ? 255 : (int)value) : 0; } private final int clamp(double value) { // Ensure NaN maps to 0 return (value >= 0.0) ? ((value > 255.0) ? 255 : (int)value) : 0; } private int getSample(Object inData, int sample) { boolean needAlpha = (hasAlpha && isAlphaPremultiplied); int type = colorSpaceType; boolean is_sRGB = colorSpace.isCS_sRGB(); if (type == ColorSpace.TYPE_GRAY) { sample = 0; is_sRGB = true; } if (is_sRGB) { if (transferType == DataBuffer.TYPE_FLOAT) { float[] fdata = (float[])inData; float fsample = fdata[sample]*255; if (needAlpha) { float falp = fdata[numColorComponents]; if (falp == 0.0) return 0; else return clamp(fsample/falp); } else { return clamp(fsample); } } else { double[] ddata = (double[])inData; double dsample = ddata[sample]*255.0; if (needAlpha) { double dalp = ddata[numColorComponents]; if (dalp == 0.0) return 0; else return clamp(dsample/dalp); } else { return clamp(dsample); } } } // Not TYPE_GRAY or TYPE_RGB ColorSpace float[] norm; float[] rgb; if (transferType == DataBuffer.TYPE_FLOAT) { float[] fdata = (float[])inData; if (needAlpha) { float falp = fdata[numColorComponents]; if (falp == 0.0) return 0; norm = new float[numColorComponents]; for (int i = 0; i < numColorComponents; i++) { norm[i] = fdata[i]/falp; } rgb = colorSpace.toRGB(norm); } else { rgb = colorSpace.toRGB(fdata); } return (int)(rgb[sample]*255 + 0.5F); } else { double[] ddata = (double[])inData; norm = new float[numColorComponents]; if (needAlpha) { double dalp = ddata[numColorComponents]; if (dalp == 0.0) return 0; for (int i = 0; i < numColorComponents; i++) { norm[i] = (float)(ddata[i]/dalp); } rgb = colorSpace.toRGB(norm); } else { for (int i = 0; i < numColorComponents; i++) { norm[i] = (float)ddata[i]; } rgb = colorSpace.toRGB(norm); } return (int)(rgb[sample]*255 + 0.5); } } /** * Returns the red color component for the specified pixel, scaled * from 0 to 255 in the default RGB ColorSpace, sRGB. A color * conversion is done if necessary. The pixel value * is specified by an array of data elements of type * transferType passed in as an object reference. The * returned value will be a non pre-multiplied value. If the alpha * is premultiplied, this method divides it out before returning * the value (if the alpha value is 0, the red value will be 0). * * @param inData The pixel from which to get the red * color component, specified by an array of data elements of type * transferType. * * @return The red color component for the specified pixel, as an * int. * * @throws ClassCastException If inData is not a * primitive array of type transferType. * @throws ArrayIndexOutOfBoundsException if inData * is not large enough to hold a pixel value for this * ColorModel. */ public int getRed(Object inData) { return getSample(inData, 0); } /** * Returns the green color component for the specified pixel, scaled * from 0 to 255 in the default RGB ColorSpace, sRGB. A color * conversion is done if necessary. The pixel value * is specified by an array of data elements of type * transferType passed in as an object reference. The * returned value will be a non pre-multiplied value. If the alpha * is premultiplied, this method divides it out before returning * the value (if the alpha value is 0, the green value will be 0). * * @param inData The pixel from which to get the green * color component, specified by an array of data elements of type * transferType. * * @return The green color component for the specified pixel, as an * int. * * @throws ClassCastException If inData is not a * primitive array of type transferType. * @throws ArrayIndexOutOfBoundsException if inData * is not large enough to hold a pixel value for this * ColorModel. */ public int getGreen(Object inData) { return getSample(inData, 1); } /** * Returns the blue color component for the specified pixel, scaled * from 0 to 255 in the default RGB ColorSpace, sRGB. A color * conversion is done if necessary. The pixel value * is specified by an array of data elements of type * transferType passed in as an object reference. The * returned value will be a non pre-multiplied value. If the alpha * is premultiplied, this method divides it out before returning * the value (if the alpha value is 0, the blue value will be 0). * * @param inData The pixel from which to get the blue * color component, specified by an array of data elements of type * transferType. * * @return The blue color component for the specified pixel, as an * int. * * @throws ClassCastException If inData is not a * primitive array of type transferType. * @throws ArrayIndexOutOfBoundsException if inData * is not large enough to hold a pixel value for this * ColorModel. */ public int getBlue(Object inData) { return getSample(inData, 2); } /** * Returns the alpha component for the specified pixel, scaled * from 0 to 255. The pixel value is specified by an array of * data elements of type transferType passed in as an * object reference. If the ColorModel does not have * alpha, 255 is returned. * * @param inData The pixel from which to get the alpha * component, specified by an array of data elements of type * transferType. * * @return The alpha component for the specified pixel, as an int. * * @throws IllegalArgumentException if inData is * null and the colorModel has alpha. * @throws ClassCastException If inData is not a * primitive array of type transferType and the * ColorModel has alpha. * @throws ArrayIndexOutOfBoundsException if inData * is not large enough to hold a pixel value for this * ColorModel and the ColorModel has * alpha. */ public int getAlpha(Object inData) { if ( inData == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (hasAlpha == false) { return 255; } if (transferType == DataBuffer.TYPE_FLOAT) { float[] fdata = (float[])inData; return (int)(fdata[numColorComponents]*255.0F + 0.5F); } else { double[] ddata = (double[])inData; return (int)(ddata[numColorComponents]*255.0 + 0.5); } } /** * Returns the color/alpha components for the specified pixel in * the default RGB color model format. A color conversion is done * if necessary. The pixel value is specified by an array of data * elements of type transferType passed in as an * object reference. The returned value is in a non * pre-multiplied format. If the alpha is premultiplied, this * method divides it out of the color components (if the alpha * value is 0, the color values will be 0). * * @param inData The pixel from which to get the * color/alpha components, specified by an array of data elements * of type transferType. * * @return The color/alpha components for the specified pixel, as an int. * * @throws ClassCastException If inData is not a * primitive array of type transferType. * @throws ArrayIndexOutOfBoundsException if inData * is not large enough to hold a pixel value for this * ColorModel. */ public int getRGB(Object inData) { boolean needAlpha = (hasAlpha && isAlphaPremultiplied); int alpha = 255; int red, green, blue; if (colorSpace.isCS_sRGB()) { if (transferType == DataBuffer.TYPE_FLOAT) { float[] fdata = (float[])inData; float fred = fdata[0]; float fgreen = fdata[1]; float fblue = fdata[2]; float fscale = 255.0F; if (needAlpha) { float falpha = fdata[3]; fscale /= falpha; alpha = clamp(255.0F*falpha); } red = clamp(fred*fscale); green = clamp(fgreen*fscale); blue = clamp(fblue*fscale); } else { double[] ddata = (double[])inData; double dred = ddata[0]; double dgreen = ddata[1]; double dblue = ddata[2]; double dscale = 255.0; if (needAlpha) { double dalpha = ddata[3]; dscale /= dalpha; alpha = clamp(255.0*dalpha); } red = clamp(dred*dscale); green = clamp(dgreen*dscale); blue = clamp(dblue*dscale); } } else if (colorSpaceType == ColorSpace.TYPE_GRAY) { if (transferType == DataBuffer.TYPE_FLOAT) { float[] fdata = (float[])inData; float fgray = fdata[0]; if (needAlpha) { float falp = fdata[1]; red = green = blue = clamp(fgray*255.0F/falp); alpha = clamp(255.0F*falp); } else { red = green = blue = clamp(fgray*255.0F); } } else { double[] ddata = (double[])inData; double dgray = ddata[0]; if (needAlpha) { double dalp = ddata[1]; red = green = blue = clamp(dgray*255.0/dalp); alpha = clamp(255.0*dalp); } else { red = green = blue = clamp(dgray*255.0); } } } else { // Not Gray or sRGB float[] norm; float[] rgb; if (transferType == DataBuffer.TYPE_FLOAT) { float[] fdata = (float[])inData; if (needAlpha) { float falp = fdata[numColorComponents]; float invfalp = 1.0F/falp; norm = new float[numColorComponents]; for (int i = 0; i < numColorComponents; i++) { norm[i] = fdata[i]*invfalp; } alpha = clamp(255.0F*falp); } else { norm = fdata; } } else { double[] ddata = (double[])inData; norm = new float[numColorComponents]; if (needAlpha) { double dalp = ddata[numColorComponents]; double invdalp = 1.0/dalp; for (int i = 0; i < numColorComponents; i++) { norm[i] = (float)(ddata[i]*invdalp); } alpha = clamp(255.0*dalp); } else { for (int i = 0; i < numColorComponents; i++) { norm[i] = (float)ddata[i]; } } } // Perform color conversion rgb = colorSpace.toRGB(norm); red = clamp(rgb[0]*255.0F); green = clamp(rgb[1]*255.0F); blue = clamp(rgb[2]*255.0F); } return (alpha << 24) | (red << 16) | (green << 8) | blue; } /** * Returns a data element array representation of a pixel in this * ColorModel, given an integer pixel representation * in the default RGB color model. This array can then be passed * to the setDataElements method of a * WritableRaster object. If the pixel * parameter is null, a new array is allocated. * If the colorSpaceType is of TYPE_GRAY then the rgb components * are converted to gray using appropriate weights * * @param rgb An ARGB value packed into an int. * @param pixel The float or double array representation of the pixel. * * @throws ClassCastException If pixel is not null and * is not a primitive array of type transferType. * * @throws ArrayIndexOutOfBoundsException If pixel is * not large enough to hold a pixel value for this * ColorModel. */ public Object getDataElements(int rgb, Object pixel) { if (transferType == DataBuffer.TYPE_FLOAT) { float[] floatPixel; if (pixel == null) { floatPixel = new float[numComponents]; } else { if (!(pixel instanceof float[])) { throw new ClassCastException(JaiI18N.getString("FloatDoubleColorModel7")); } floatPixel = (float[])pixel; if (floatPixel.length < numComponents) { throw new ArrayIndexOutOfBoundsException(JaiI18N.getString("FloatDoubleColorModel8")); } } float inv255 = 1.0F/255.0F; if (colorSpace.isCS_sRGB()) { int alp = (rgb >> 24) & 0xff; int red = (rgb >> 16) & 0xff; int grn = (rgb >> 8) & 0xff; int blu = (rgb ) & 0xff; float norm = inv255; if (isAlphaPremultiplied) { norm *= alp; } floatPixel[0] = red*norm; floatPixel[1] = grn*norm; floatPixel[2] = blu*norm; if (hasAlpha) { floatPixel[3] = alp*inv255; } } else if (colorSpaceType == ColorSpace.TYPE_GRAY) { float gray = ((((rgb>>16)&0xff)*(.299F*inv255)) + (((rgb>>8) &0xff)*(.587F*inv255)) + (((rgb) &0xff)*(.114F*inv255))); floatPixel[0] = gray; if (hasAlpha) { int alpha = (rgb>>24) & 0xff; floatPixel[1] = alpha*inv255; } } else { // Need to convert the color float[] norm = new float[3]; norm[0] = ((rgb>>16) & 0xff)*inv255; norm[1] = ((rgb>>8) & 0xff)*inv255; norm[2] = ((rgb) & 0xff)*inv255; norm = colorSpace.fromRGB(norm); for (int i = 0; i < numColorComponents; i++) { floatPixel[i] = norm[i]; } if (hasAlpha) { int alpha = (rgb>>24) & 0xff; floatPixel[numColorComponents] = alpha*inv255; } } return floatPixel; } else { // transferType == DataBuffer.TYPE_DOUBLE double[] doublePixel; if (pixel == null) { doublePixel = new double[numComponents]; } else { if (!(pixel instanceof double[])) { throw new ClassCastException(JaiI18N.getString("FloatDoubleColorModel7")); } doublePixel = (double[])pixel; if (doublePixel.length < numComponents) { throw new ArrayIndexOutOfBoundsException(JaiI18N.getString("FloatDoubleColorModel8")); } } double inv255 = 1.0/255.0; if (colorSpace.isCS_sRGB()) { int alp = (rgb>>24) & 0xff; int red = (rgb>>16) & 0xff; int grn = (rgb>>8) & 0xff; int blu = (rgb) & 0xff; double norm = inv255; if (isAlphaPremultiplied) { norm *= alp; } doublePixel[0] = red*norm; doublePixel[1] = grn*norm; doublePixel[2] = blu*norm; if (hasAlpha) { doublePixel[3] = alp*inv255; } } else if (colorSpaceType == ColorSpace.TYPE_GRAY) { double gray = ((((rgb>>16) & 0xff)*(.299*inv255)) + (((rgb>>8) & 0xff)*(.587*inv255)) + (((rgb) & 0xff)*(.114*inv255))); doublePixel[0] = gray; if (hasAlpha) { int alpha = (rgb>>24) & 0xff; doublePixel[1] = alpha*inv255; } } else { float inv255F = 1.0F/255.0F; // Need to convert the color, need data in float form float[] norm = new float[3]; norm[0] = ((rgb>>16) & 0xff)*inv255F; norm[1] = ((rgb>>8) & 0xff)*inv255F; norm[2] = ((rgb) & 0xff)*inv255F; norm = colorSpace.fromRGB(norm); for (int i = 0; i < numColorComponents; i++) { doublePixel[i] = (double)norm[i]; } if (hasAlpha) { int alpha = (rgb>>24) & 0xff; doublePixel[numColorComponents] = alpha*inv255; } } return doublePixel; } } /** * Throws an IllegalArgumentException, since pixel * values for this ColorModel are not conveniently * representable as a single int. */ public int[] getComponents(int pixel, int[] components, int offset) { throw new IllegalArgumentException(JaiI18N.getString("FloatDoubleColorModel9")); } /** * Throws an IllegalArgumentException since * the pixel values cannot be placed into an int array. */ public int[] getComponents(Object pixel, int[] components, int offset) { throw new IllegalArgumentException(JaiI18N.getString("FloatDoubleColorModel9")); } /** * Throws an IllegalArgumentException, since pixel * values for this ColorModel are not conveniently * representable as a single int. */ public int getDataElement(int[] components, int offset) { throw new IllegalArgumentException(JaiI18N.getString("FloatDoubleColorModel9")); } /** * Returns a data element array representation of a pixel in this * ColorModel, given an array of unnormalized * color/alpha components. This array can then be passed to the * setDataElements method of a * WritableRaster object. * * @param components An array of unnormalized color/alpha * components. * @param offset The integer offset into the * components array. * @param obj The object in which to store the data element array * representation of the pixel. If obj variable is * null, a new array is allocated. If obj is not * null, it must be a primitive array of type * transferType. An * ArrayIndexOutOfBoundsException is thrown if * obj is not large enough to hold a pixel value for * this ColorModel. * * @return The data element array representation of a pixel * in this ColorModel. * * @throws IllegalArgumentException If the components array * is not large enough to hold all the color and alpha components * (starting at offset). * @throws ClassCastException If obj is not null and * is not a primitive array of type transferType. * @throws ArrayIndexOutOfBoundsException If obj is * not large enough to hold a pixel value for this * ColorModel. */ public Object getDataElements(int[] components, int offset, Object obj) { if ((components.length-offset) < numComponents) { throw new IllegalArgumentException(numComponents + " " + JaiI18N.getString("FloatDoubleColorModel10")); } if (transferType == DataBuffer.TYPE_FLOAT) { float[] pixel; if (obj == null) { pixel = new float[components.length]; } else { pixel = (float[])obj; } for (int i=0; i < numComponents; i++) { pixel[i] = (float)(components[offset + i]); } return pixel; } else { double[] pixel; if (obj == null) { pixel = new double[components.length]; } else { pixel = (double[])obj; } for (int i=0; i < numComponents; i++) { pixel[i] = (double)(components[offset + i]); } return pixel; } } /** * Forces the raster data to match the state specified in the * isAlphaPremultiplied variable, assuming the data * is currently correctly described by this ColorModel. * It may multiply or divide the color raster data by alpha, or * do nothing if the data is in the correct state. If the data needs * to be coerced, this method also returns an instance of * FloatDoubleColorModel with * the isAlphaPremultiplied flag set appropriately. * * @throws IllegalArgumentException if transfer type of * raster is not the same as that of this * FloatDoubleColorModel. */ public ColorModel coerceData (WritableRaster raster, boolean isAlphaPremultiplied) { if ((hasAlpha == false) || (this.isAlphaPremultiplied == isAlphaPremultiplied)) { // Nothing to do return this; } int w = raster.getWidth(); int h = raster.getHeight(); int aIdx = raster.getNumBands() - 1; int rminX = raster.getMinX(); int rY = raster.getMinY(); int rX; if (raster.getTransferType() != transferType) { throw new IllegalArgumentException( JaiI18N.getString("FloatDoubleColorModel6")); } if (isAlphaPremultiplied) { switch (transferType) { case DataBuffer.TYPE_FLOAT: { float pixel[] = null; for (int y = 0; y < h; y++, rY++) { rX = rminX; for (int x = 0; x < w; x++, rX++) { pixel = (float[])raster.getDataElements(rX, rY, pixel); float fAlpha = pixel[aIdx]; if (fAlpha != 0) { for (int c=0; c < aIdx; c++) { pixel[c] *= fAlpha; } raster.setDataElements(rX, rY, pixel); } } } } break; case DataBuffer.TYPE_DOUBLE: { double pixel[] = null; for (int y = 0; y < h; y++, rY++) { rX = rminX; for (int x = 0; x < w; x++, rX++) { pixel = (double[])raster.getDataElements(rX, rY, pixel); double dAlpha = pixel[aIdx]; if (dAlpha != 0) { for (int c=0; c < aIdx; c++) { pixel[c] *= dAlpha; } raster.setDataElements(rX, rY, pixel); } } } } break; default: throw new RuntimeException(JaiI18N.getString("FloatDoubleColorModel0")); } if (isAlphaPremultiplied) { } } else { // We are premultiplied and want to divide it out switch (transferType) { case DataBuffer.TYPE_FLOAT: { for (int y = 0; y < h; y++, rY++) { rX = rminX; for (int x = 0; x < w; x++, rX++) { float pixel[] = null; pixel = (float[])raster.getDataElements(rX, rY, pixel); float fAlpha = pixel[aIdx]; if (fAlpha != 0) { float invFAlpha = 1.0F/fAlpha; for (int c=0; c < aIdx; c++) { pixel[c] *= invFAlpha; } } raster.setDataElements(rX, rY, pixel); } } } break; case DataBuffer.TYPE_DOUBLE: { for (int y = 0; y < h; y++, rY++) { rX = rminX; for (int x = 0; x < w; x++, rX++) { double pixel[] = null; pixel = (double[])raster.getDataElements(rX, rY, pixel); double dAlpha = pixel[aIdx]; if (dAlpha != 0) { double invDAlpha = 1.0/dAlpha; for (int c=0; c < aIdx; c++) { pixel[c] *= invDAlpha; } } raster.setDataElements(rX, rY, pixel); } } } break; default: throw new RuntimeException(JaiI18N.getString("FloatDoubleColorModel0")); } } // Return a new color model return new FloatDoubleColorModel(colorSpace, hasAlpha, isAlphaPremultiplied, transparency, transferType); } /** * Returns true if the supplied Raster's * SampleModel is compatible with this * FloatDoubleColorModel. * * @param raster a Rasterto be checked for compatibility. */ public boolean isCompatibleRaster(Raster raster) { SampleModel sm = raster.getSampleModel(); return isCompatibleSampleModel(sm); } /** * Creates a WritableRaster with the specified width * and height, that has a data layout (SampleModel) * compatible with this ColorModel. The returned * WritableRaster's SampleModel will be * an instance of ComponentSampleModel. * * @param w The width of the WritableRaster * @param h The height of the WritableRaster * * @return A WritableRaster that is compatible with * this ColorModel. * * @see java.awt.image.WritableRaster * @see java.awt.image.SampleModel */ public WritableRaster createCompatibleWritableRaster(int w, int h) { SampleModel sm = createCompatibleSampleModel(w, h); return RasterFactory.createWritableRaster(sm, new Point(0, 0)); } /** * Creates a SampleModel with the specified width and * height that has a data layout compatible with this * ColorModel. The returned SampleModel * will be an instance of ComponentSampleModel. * * @param w The width of the SampleModel. * @param h The height of the SampleModel. * * @return A SampleModel that is compatible with this * ColorModel. * * @see java.awt.image.SampleModel * @see java.awt.image.ComponentSampleModel */ public SampleModel createCompatibleSampleModel(int w, int h) { int[] bandOffsets = new int[numComponents]; for (int i = 0; i < numComponents; i++) { bandOffsets[i] = i; } return new ComponentSampleModelJAI(transferType, w, h, numComponents, w*numComponents, bandOffsets); } /** * Checks whether or not the specified SampleModel is * compatible with this ColorModel. A * SampleModel is compatible if it is an instance of * ComponentSampleModel, has the sample number of * bands as the total number of components (including alpha) in * the ColorSpace used by this * ColorModel, and has the same data type (float or * double) as this ColorModel. * * @param sm The SampleModel to test for compatibility. * * @return true if the SampleModel is * compatible with this ColorModel, * false if it is not. * * @see java.awt.image.SampleModel * @see java.awt.image.ComponentSampleModel */ public boolean isCompatibleSampleModel(SampleModel sm) { if (sm instanceof ComponentSampleModel) { if (sm.getNumBands() != getNumComponents()) { return false; } if (sm.getDataType() != transferType) { return false; } return true; } else { return false; } } /** Returns a String containing the values of all valid fields. */ public String toString() { return "FloatDoubleColorModel: " + super.toString(); } } jai-core-1.1.4/src/share/classes/javax/media/jai/ImageSequence.java0000644000175000017500000001362210203035544024717 0ustar mathieumathieu/* * $RCSfile: ImageSequence.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:09 $ * $State: Exp $ */ package javax.media.jai; import java.util.Collection; import java.util.Iterator; /** * A class representing a sequence of images, each associated with a * time stamp and a camera position. The images are of the type * javax.media.jai.PlanarImage; the time stamps are of the * type float; the camera positions are of the type * java.lang.Object. The tuple (image, time stamp, camera * position) is represented by class * javax.media.jai.SequentialImage. * *

      This class can be used to represent video or time-lapse photography. * * @see PlanarImage * @see SequentialImage * * @deprecated as of JAI 1.1. Use * AttributedImageCollection instead. */ public class ImageSequence extends CollectionImage { /** The default constrctor. */ protected ImageSequence() {} /** * Constructs a class that represents a sequence of images. * * @param images A collection of SequentialImage. * * @throws IllegalArgumentException if images is null. */ public ImageSequence(Collection images) { super(images); } /** * Returns the image associated with the specified time stamp, * or null if no match is found. */ public PlanarImage getImage(float ts) { Iterator iter = iterator(); while (iter.hasNext()) { SequentialImage si = (SequentialImage)iter.next(); if (si.timeStamp == ts) { return si.image; } } return null; } /** * Returns the image associated with the specified camera position, * or null if cp is null or * if no match is found. */ public PlanarImage getImage(Object cp) { if (cp != null) { Iterator iter = iterator(); while (iter.hasNext()) { SequentialImage si = (SequentialImage)iter.next(); if (si.cameraPosition.equals(cp)) { return si.image; } } } return null; } /** * Returns the time stamp associated with the specified image, or * -Float.MAX_VALUE if pi is null * or if no match is found. */ public float getTimeStamp(PlanarImage pi) { if (pi != null) { Iterator iter = iterator(); while (iter.hasNext()) { SequentialImage si = (SequentialImage)iter.next(); if (si.image.equals(pi)) { return si.timeStamp; } } } return -Float.MAX_VALUE; } /** * Returns the camera position associated with the specified image, * or null if pi is null or * if no match is found. */ public Object getCameraPosition(PlanarImage pi) { if (pi != null) { Iterator iter = iterator(); while (iter.hasNext()) { SequentialImage si = (SequentialImage)iter.next(); if (si.image.equals(pi)) { return si.cameraPosition; } } } return null; } /** * Adds a SequentialImage to this collection. If the * specified image is null, it is not added to the * collection. * * @return true if and only if the * SequentialImage is added to the collection. */ public boolean add(Object o) { if (o != null && o instanceof SequentialImage) { return super.add(o); } else { return false; } } /** * Removes the SequentialImage that contains the * specified image from this collection. * * @return true if and only if a * SequentialImage with the * specified image is removed from the collection. */ public boolean remove(PlanarImage pi) { if (pi != null) { Iterator iter = iterator(); while (iter.hasNext()) { SequentialImage si = (SequentialImage)iter.next(); if (si.image.equals(pi)) { return super.remove(si); } } } return false; } /** * Removes the SequentialImage that contains the * specified time stamp from this collection. * * @return true if and only if a * SequentialImage with the * specified time stamp is removed from the collection. */ public boolean remove(float ts) { Iterator iter = iterator(); while (iter.hasNext()) { SequentialImage si = (SequentialImage)iter.next(); if (si.timeStamp == ts) { return super.remove(si); } } return false; } /** * Removes the SequentialImage that contains the * specified camera position from this collection. * * @return true if and only if a * SequentialImage with the * specified camera position is removed from the collection. */ public boolean remove(Object cp) { if (cp != null) { Iterator iter = iterator(); while (iter.hasNext()) { SequentialImage si = (SequentialImage)iter.next(); if (si.cameraPosition.equals(cp)) { return super.remove(si); } } } return false; } } jai-core-1.1.4/src/share/classes/javax/media/jai/JaiI18N.java0000644000175000017500000000134010203035544023301 0ustar mathieumathieu/* * $RCSfile: JaiI18N.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:11 $ * $State: Exp $ */ package javax.media.jai; import com.sun.media.jai.util.PropertyUtil; import java.text.MessageFormat; import java.util.Locale; class JaiI18N { static String packageName = "javax.media.jai"; public static String getString(String key) { return PropertyUtil.getString(packageName, key); } public static String formatMsg(String key, Object[] args) { MessageFormat mf = new MessageFormat(getString(key)); mf.setLocale(Locale.getDefault()); return mf.format(args); } } jai-core-1.1.4/src/share/classes/javax/media/jai/PropertyChangeEmitter.java0000644000175000017500000000361010203035544026464 0ustar mathieumathieu/* * $RCSfile: PropertyChangeEmitter.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:16 $ * $State: Exp $ */ package javax.media.jai; import java.beans.PropertyChangeListener; /** * A class which emits PropertyChangeEvents. * This abstraction permits objects of disparate types to be recognized * as sources of PropertyChangeEvents. * PropertyChangeEvents emitted by JAI objects will be * PropertyChangeEventJAI instances. * *

      Note that the case of property names used in this context is * significant. * * @see PropertyChangeEventJAI * * @since JAI 1.1 */ public interface PropertyChangeEmitter { /** * Add a PropertyChangeListener to the listener list. The * listener is registered for all properties. */ void addPropertyChangeListener(PropertyChangeListener listener); /** * Add a PropertyChangeListener for a specific property. The * listener will be invoked only when a call on * firePropertyChange names that specific property. * * @throws IllegalArgumentException for null propertyName. */ void addPropertyChangeListener(String propertyName, PropertyChangeListener listener); /** * Remove a PropertyChangeListener from the listener list. This * removes a PropertyChangeListener that was registered for all * properties. */ void removePropertyChangeListener(PropertyChangeListener listener); /** * Remove a PropertyChangeListener for a specific property. * * @throws IllegalArgumentException for null propertyName. */ void removePropertyChangeListener(String propertyName, PropertyChangeListener listener); } jai-core-1.1.4/src/share/classes/javax/media/jai/Interpolation.java0000644000175000017500000013235310203035544025036 0ustar mathieumathieu/* * $RCSfile: Interpolation.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:10 $ * $State: Exp $ */ package javax.media.jai; import java.io.Serializable; /** * An object encapsulating a particular algorithm for image * interpolation (resampling). An Interpolation captures the notion * of performing sampling on a regular grid of pixels using a local * neighborhood. It is intended to be used by operations that * resample their sources, including affine mapping and warping. * *

      Resampling is the action of computing a pixel value at a * possibly non-integral position of an image. The image defines * pixel values at integer lattice points, and it is up to the * resampler to produce a reasonable value for positions not falling * on the lattice. A number of techniques are used in practice, the * most common being nearest-neighbor, which simply takes the value of * the closest lattice point; bilinear, which interpolates linearly * between the four closest lattice points; and bicubic, which applies * a piecewise polynomial function to a 4x4 neighborhood of nearby * points. The area over which a resampling function needs to be * computed is referred to as its support; thus the standard * resampling functions have supports of 1, 4, and 16 pixels * respectively. Mathematically, the ideal resampling function for a * band-limited image (one containing no energy above a given * frequency) is the sinc function, equal to sin(x)/x. This has * practical limitations, in particular its infinite support, which * lead to the use of the standard approximations described above. * *

      Other interpolation functions may be required to solve problems * other than the resampling of band-limited image data. When * shrinking an image, it is common to use a function that combines * area averaging with resampling in order to remove undesirable high * frequencies as part of the interpolation process. Other * application areas may use interpolating functions that operate under * other assumptions about image data, such as taking the maximum * value of a 2x2 neighborhood. The interpolation class provides a * framework in which a variety of interpolation schemes may be * expressed. * *

      Many interpolations are separable, that is, they may be * equivalently rewritten as a horizontal interpolation followed * by a vertical one (or vice versa). In practice, some precision * may be lost by the rounding and truncation that takes place * between the passes. The Interpolation class assumes separability * and implements all vertical interpolation methods in terms of * corresponding horizontal methods, and defines isSeparable() to * return true. A subclass may override these methods to provide * distinct implementations of horizontal and vertical interpolation. * Some subclasses may implement the two-dimensional interpolation * methods directly, yielding more precise results, while others * may implement these using a two-pass approach. * *

      A minimal Interpolation subclass must call the Interpolation * constructor (super()) and then set at least the following fields. *

       *   leftPadding
       *   rightPadding
       *   topPadding
       *   bottomPadding
       *   width
       *   height
       *   subsampleBitsH
       *   subsampleBitsV
       * 
      *

      It must also implement at least the following methods. *

       *   int interpolateH(int[] samples, int xfrac)
       *   float interpolateH(float[] samples, float xfrac)
       *   double interpolateH(double[] samples, float xfrac)
       * 
      * All other methods are defined in terms of these methods for ease of * implementation of new Interpolation subclasses. * *

      Since interpolation is generally performed for every pixel of a * destination image, efficiency is important. In particular, passing * source samples by means of arrays is likely to be unacceptably * slow. Accordingly, methods are provided for the common cases of * 2x1, 1x2, 4x1, 1x4, 2x2, and 4x4 input grids. These methods are * defined in the superclass to package their arguments into arrays * and forward the call to the array versions, in order to simplify * implementation. They should be called only on Interpolation objects * with the correct width and height. In other words, an implementor * of an Interpolation subclass may implement "interpolateH(int s0, int s1, * int xfrac)" assuming that the interpolation width is in fact equal to * 2, and does not need to enforce this constraint. * *

      The fractional position of interpolation (xfrac, yfrac) is always * between 0.0 and 1.0 (not including 1.0). For integral image data, * the fraction is represented as a scaled integer between 0 and * 2n - 1, where n is a small integer. The value of n * in the horizontal and vertical directions may be * obtained by calling getSubsampleBitsH() and getSubsampleBitsV(). * In general, code that makes use of an externally-provided * Interpolation object must query that object to determine its * desired positional precision. * *

      For float and double images, a float between 0.0F and 1.0F * (not including 1.0F) is used as a positional specifier in the interest of * greater accuracy. * *

      It is important to understand that the subsampleBits precision * is used only to indicate the scaling implicit in the fractional locations * (xfrac, yfrac) for integral image data types. For example, for * subsampleBitsH=8, xfrac must lie between 0 and 255 inclusive. * An implementation is not required to actually quantize its interpolation * coefficients to match the specified subsampling precision. * *

      The diagrams below illustrate the pixels involved in one-dimensional * interpolation. Point s0 is the interpolation kernel key position. * xfrac and yfrac, indicated by the dots, represent the point of interpolation * between two pixels. This value lies between 0.0 and 1.0 exclusive for * floating point and 0 and 2subsampleBits exclusive for integer * interpolations. * *

       * 
       *         Horizontal              Vertical
       *
       *    s_    s0 .  s1    s2            s_                             
       *             ^                                                     
       *            xfrac                   s0                        
       *                                     .< yfrac                  
       *                                    s1                         
       *                                                                      
       *                                    s2                         
       *                                                                      
       *
       * 
       * 
      * *

      The diagram below illustrates the pixels involved in * two-dimensional interpolation. Point s00 is the interpolation kernel * key position. * *

       * 
       *                                                                      
       *               s__    s_0    s_1    s_2                              
       *                                                                      
       *                                                                      
       *                                                                      
       *               s0_    s00    s01    s02                              
       *                                                                      
       *                          .             < yfrac                      
       *                                                                      
       *               s1_    s10    s11    s12                              
       *                                                                      
       *                                                                      
       *                                                                      
       *               s2_    s20    s21    s22                              
       *                          ^                                           
       *                         xfrac                                        
       *                                                                      
       * 
       * 
      * *

      The subclasses of Interpolation include InterpolationNearest, * InterpolationBilinear, InterpolationBicubic, and * InterpolationBicubic2 (a variant defined by a different polynomial * function). These subclasses are marked 'final,' so users may * identify them by name (using 'instanceof') and write specialized * code for them. This may also allow inlining to occur on some virtual * machines. These classes do provide correct, if less than * optimal code for performing their interpolations, so it is * possible to use any Interpolation object in a generic manner. * The Sun-provided InterpolationBilinear and InterpolationBicubic * classes provide a more optimal implementation while using the same semantics. * *

      The InterpolationTable class is a subclass of Interpolation * that divides the set of subsample positions into a fixed number of * "bins" and stores a kernel for each bin. InterpolationBicubic and * InterpolationBicubic2 are implemented in terms of * InterpolationTable since a direct implementation is very expensive. * * @see InterpolationNearest * @see InterpolationBilinear * @see InterpolationBicubic * @see InterpolationBicubic2 * @see InterpolationTable * */ public abstract class Interpolation extends Object implements Serializable { /** * A constant specifying interpolation by the InterpolationNearest class. */ public static final int INTERP_NEAREST = 0; /** * A constant specifying interpolation by the InterpolationBilinear class. */ public static final int INTERP_BILINEAR = 1; /** * A constant specifying interpolation by the InterpolationBicubic class. */ public static final int INTERP_BICUBIC = 2; /** * A constant specifying interpolation by the InterpolationBicubic2 class. */ public static final int INTERP_BICUBIC_2 = 3; private static Interpolation nearestInstance = null; private static Interpolation bilinearInstance = null; private static Interpolation bicubicInstance = null; private static Interpolation bicubic2Instance = null; /** * The number of pixels lying to the left * of the interpolation kernel key position. */ protected int leftPadding; /** * The number of pixels lying to the right * of the interpolation kernel key position. */ protected int rightPadding; /** * The number of pixels lying above the interpolation kernel key position. */ protected int topPadding; /** * The number of pixels lying below the interpolation kernel key position. */ protected int bottomPadding; /** * The numbers of bits used for the horizontal subsample position. * This value determines how integer fractional positons are * to be interpreted. */ protected int subsampleBitsH; /** * The number of bits used for the vertical subsample position. * This value determines how integer fractional positons are * to be interpreted. */ protected int subsampleBitsV; /** * The width of the interpolation kernel in pixels. */ protected int width; /** * The height of the interpolation kernel in pixels. */ protected int height; /** * Creates an interpolation of one of the standard types. * This is intended strictly as a convenience method. * The resulting static object is cached for later reuse. * * @param type one of: * INTERP_NEAREST, * INTERP_BILINEAR, * INTERP_BICUBIC, or * INTERP_BICUBIC_2 * @return an appropriate Interpolation object. * @throws IllegalArgumentException if an unrecognized type is supplied. */ public synchronized static Interpolation getInstance(int type) { Interpolation interp = null; switch (type) { case INTERP_NEAREST: if (nearestInstance == null) { interp = nearestInstance = new InterpolationNearest(); } else { interp = nearestInstance; } break; case INTERP_BILINEAR: if (bilinearInstance == null) { interp = bilinearInstance = new InterpolationBilinear(); } else { interp = bilinearInstance; } break; case INTERP_BICUBIC: if (bicubicInstance == null) { interp = bicubicInstance = new InterpolationBicubic(8); } else { interp = bicubicInstance; } break; case INTERP_BICUBIC_2: if (bicubic2Instance == null) { interp = bicubic2Instance = new InterpolationBicubic2(8); } else { interp = bicubic2Instance; } break; default: throw new IllegalArgumentException( JaiI18N.getString("Interpolation0")); } return interp; } /** * Constructs an Interpolation object with no fields set. * This constructor is only invoked by subclasses which * will subsequently set all fields themselves. */ protected Interpolation() {} /** * Construct interpolation object with all parameters set. * Subclasses must supply all parameters. */ public Interpolation(int width, int height, int leftPadding, int rightPadding, int topPadding, int bottomPadding, int subsampleBitsH, int subsampleBitsV) { this.width = width; this.height = height; this.leftPadding = leftPadding; this.rightPadding = rightPadding; this.topPadding = topPadding; this.bottomPadding = bottomPadding; this.subsampleBitsH = subsampleBitsH; this.subsampleBitsV = subsampleBitsV; } /** Returns the number of samples required to the left of the key element. */ public int getLeftPadding() { return leftPadding; } /** Returns the number of samples required to the right of the key element. */ public int getRightPadding() { return rightPadding; } /** Returns the number of samples required above the key element. */ public int getTopPadding() { return topPadding; } /** Returns the number of samples required below the key element. */ public int getBottomPadding() { return bottomPadding; } /** Returns the number of samples required for horizontal resampling. */ public int getWidth() { return width; } /** Returns the number of samples required for vertical resampling. */ public int getHeight() { return height; } /** * Returns true if the interpolation can be performed in a separable * manner, that is, by performing a separate pass in each dimension. * It is the caller's responsibility to deal with issues of precision. * By default, true is returned. */ public boolean isSeparable() { return true; } /** * Returns the number of bits used to index subsample positions in * the horizontal direction. All integral 'xfrac' parameters * should range between 0 and 2(getSubsampleBitsH()) - 1. * *

      In general, the caller is responsible for determining the * number of subsample bits of any Interpolation object it * receives and setting up its position variables accordingly. * Some Interpolation objects allow the number of bits to be set * at construction time. */ public int getSubsampleBitsH() { return subsampleBitsH; } /** * Returns the number of bits used to index subsample positions in * the vertical direction. All integral 'yfrac' parameters * should range between 0 and 2(getSubsampleBitsV()) - 1. */ public int getSubsampleBitsV() { return subsampleBitsV; } /** * Performs horizontal interpolation on a 1-dimensional array of * integral samples. *

      An implementation is not required to actually quantize its * interpolation coefficients to match the specified subsampling precision. * However, the supplied value of xfrac (or yfrac) must match the precision * of its corresponding subsampleBits. For example, with a subsampleBitsH * value of 8, xfrac must lie between 0 and 255. * * @param samples an array of ints. * @param xfrac the subsample position, multiplied by 2(subsampleBitsH). * @return the interpolated value as an int. */ public abstract int interpolateH(int[] samples, int xfrac); /** * Performs vertical interpolation on a 1-dimensional array of * integral samples. * *

      By default, vertical interpolation is defined to be the * same as horizontal interpolation. Subclasses may choose to * implement them differently. * * @param samples an array of ints. * @param yfrac the Y subsample position, multiplied by 2(subsampleBitsV). * @return the interpolated value as an int. * @see #interpolateH(int[], int) */ public int interpolateV(int[] samples, int yfrac) { return interpolateH(samples, yfrac); } /** * Performs interpolation on a 2-dimensional array of integral samples. * By default, this is implemented using a two-pass approach. * * @param samples a two-dimensional array of ints. * @param xfrac the X subsample position, multiplied by 2(subsampleBitsH). * @param yfrac the Y subsample position, multiplied by 2(subsampleBitsV). * @return the interpolated value as an int. * @see #interpolateH(int[], int) */ public int interpolate(int[][] samples, int xfrac, int yfrac) { int[] interpH = new int[height]; for (int i = 0; i < height; i++) { interpH[i] = interpolateH(samples[i], xfrac); } return interpolateV(interpH, yfrac); } /** * Performs horizontal interpolation on a pair of integral samples. * Subclasses may implement this method to provide a speed improvement * over the array method. This base class method merely calls the * array method. It should only be called if width == 2 and * leftPadding == 0. * * @param s0 the central sample. * @param s1 the sample to the right of the central sample. * @param xfrac the subsample position, ranging from zero to * 2(subsampleBitsH) - 1. * @return the interpolated value as an int. * @see #interpolateH(int[], int) */ public int interpolateH(int s0, int s1, int xfrac) { int[] s = new int[2]; s[0] = s0; s[1] = s1; return interpolateH(s, xfrac); } /** * Performs horizontal interpolation on a quadruple of integral samples. * Subclasses may implement this method to provide a speed improvement * over the array method. This base class method merely calls the * array method. * It should only be called if width == 4 and leftPadding == 1. * * @param s_ the sample to the left of the central sample. * @param s0 the central sample. * @param s1 the sample to the right of the central sample. * @param s2 the sample to the right of s1. * @param xfrac the subsample position, multiplied by 2(subsampleBitsH). * @return the interpolated value as an int. * @see #interpolateH(int[], int) */ public int interpolateH(int s_, int s0, int s1, int s2, int xfrac) { int[] s = new int[4]; s[0] = s_; s[1] = s0; s[2] = s1; s[3] = s2; return interpolateH(s, xfrac); } /** * Performs vertical interpolation on a pair of integral samples. * Subclasses may implement this method to provide a speed improvement * over the array method. This base class method merely calls the * array method. * It should only be called if height == 2 and topPadding == 0. * *

      By default, vertical interpolation is identical to * horizontal interpolation. Subclasses may choose to implement * them differently. * * @param s0 the central sample. * @param s1 the sample below the central sample. * @param yfrac the Y subsample position, multiplied by 2(subsampleBitsV). * @return the interpolated value as an int. * @see #interpolateH(int[], int) */ public int interpolateV(int s0, int s1, int yfrac) { int[] s = new int[2]; s[0] = s0; s[1] = s1; return interpolateV(s, yfrac); } /** * Performs vertical interpolation on a quadruple of integral samples. * Subclasses may implement this method to provide a speed improvement * over the array method. This base class method merely calls the * array method. * It should only be called if height == 4 and topPadding == 1. * *

      By default, vertical interpolation is identical to * horizontal interpolation. Subclasses may choose to implement * them differently. * * @param s_ the sample above the central sample. * @param s0 the central sample. * @param s1 the sample below the central sample. * @param s2 the sample below s1. * @param yfrac the Y subsample position, multiplied by 2(subsampleBitsV). * @return the interpolated value as an int. * @see #interpolateH(int[], int) */ public int interpolateV(int s_, int s0, int s1, int s2, int yfrac) { int[] s = new int[4]; s[0] = s_; s[1] = s0; s[2] = s1; s[3] = s2; return interpolateV(s, yfrac); } /** * Performs interpolation on a 2x2 grid of integral samples. * Subclasses may implement this method to provide a speed improvement * over the array method. This base class method merely calls the * array method. * It should only be called if width == height == 2 and * leftPadding == topPadding == 0. * * @param s00 the central sample. * @param s01 the sample to the right of the central sample. * @param s10 the sample below the central sample. * @param s11 the sample below and to the right of the central sample. * @param xfrac the X subsample position, multiplied by 2(subsampleBitsH). * @param yfrac the Y subsample position, multiplied by 2(subsampleBitsV). * @return the interpolated value as an int. * @see #interpolateH(int[], int) */ public int interpolate(int s00, int s01, int s10, int s11, int xfrac, int yfrac) { int[][] s = new int[4][4]; s[0][0] = s00; s[0][1] = s01; s[1][0] = s10; s[1][1] = s11; return interpolate(s, xfrac, yfrac); } /** * Performs interpolation on a 4x4 grid of integral samples. * Subclasses may implement this method to provide a speed improvement * over the array method. This base class method merely calls the * array method. * It should only be called if width == height == 4 and * leftPadding == topPadding == 1. * * @param s__ the sample above and to the left of the central sample. * @param s_0 the sample above the central sample. * @param s_1 the sample above and one to the right of the central sample. * @param s_2 the sample above and two to the right of the central sample. * @param s0_ the sample to the left of the central sample. * @param s00 the central sample. * @param s01 the sample to the right of the central sample. * @param s02 the sample two to the right of the central sample. * @param s1_ the sample below and one to the left of the central sample. * @param s10 the sample below the central sample. * @param s11 the sample below and one to the right of the central sample. * @param s12 the sample below and two to the right of the central sample. * @param s2_ the sample two below and one to the left of the central sample. * @param s20 the sample two below the central sample. * @param s21 the sample two below and one to the right of the central sample. * @param s22 the sample two below and two to the right of the central sample. * @param xfrac the X subsample position, multiplied by 2(subsampleBitsH). * @param yfrac the Y subsample position, multiplied by 2(subsampleBitsV). * @return the interpolated value as an int. * @see #interpolateH(int[], int) */ public int interpolate(int s__, int s_0, int s_1, int s_2, int s0_, int s00, int s01, int s02, int s1_, int s10, int s11, int s12, int s2_, int s20, int s21, int s22, int xfrac, int yfrac) { int[][] s = new int[4][4]; s[0][0] = s__; s[0][1] = s_0; s[0][2] = s_1; s[0][3] = s_2; s[1][0] = s0_; s[1][1] = s00; s[1][2] = s01; s[1][3] = s02; s[2][0] = s1_; s[2][1] = s10; s[2][2] = s11; s[2][3] = s12; s[3][0] = s2_; s[3][1] = s20; s[3][2] = s21; s[3][3] = s22; return interpolate(s, xfrac, yfrac); } /** * Performs horizontal interpolation on a 1-dimensional array of * floating-point samples representing a row of samples. * The setting of subsampleBits need not have any effect on the * interpolation accuracy of an implementation of this method. * * @param samples an array of floats. * @param xfrac the X subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a float. */ public abstract float interpolateH(float[] samples, float xfrac); /** * Performs vertical interpolation on a 1-dimensional array of * floating-point samples representing a column of samples. * The setting of subsampleBits need not have any effect on the * interpolation accuracy of an implementation of this method. * *

      By default, vertical interpolation is identical to * horizontal interpolation. Subclasses may choose to implement * them differently. * * @param samples an array of floats. * @param yfrac the Y subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a float. */ public float interpolateV(float[] samples, float yfrac) { return interpolateH(samples, yfrac); } /** * Performs interpolation on a 2-dimensional array of * floating-point samples. By default, this is implemented using * a two-pass approach. * The setting of subsampleBits need not have any effect on the * interpolation accuracy of an implementation of this method. * * * @param samples an array of floats. * @param xfrac the X subsample position, in the range [0.0F, 1.0F). * @param yfrac the Y subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a float. */ public float interpolate(float[][] samples, float xfrac, float yfrac) { float[] interpH = new float[height]; for (int i = 0; i < height; i++) { interpH[i] = interpolateH(samples[i], xfrac); } return interpolateV(interpH, yfrac); } /** * Performs horizontal interpolation on a pair of floating-point * samples. Subclasses may implement this method to provide a * speed improvement over the array method. This base class method * merely calls the array method. * It should only be called if width == 2 and leftPadding == 0. * The setting of subsampleBits need not have any effect on the * interpolation accuracy of an implementation of this method. * * @param s0 the central sample. * @param s1 the sample to the right of the central sample. * @param xfrac the subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a float. */ public float interpolateH(float s0, float s1, float xfrac) { float[] s = new float[2]; s[0] = s0; s[1] = s1; return interpolateH(s, xfrac); } /** * Performs horizontal interpolation on a quadruple of floating-point samples. * Subclasses may implement this method to provide a speed improvement * over the array method. This base class method merely calls the * array method. It should only be called if width == 4 and * leftPadding == 1. * The setting of subsampleBits need not have any effect on the * interpolation accuracy of an implementation of this method. * * @param s_ the sample to the left of the central sample. * @param s0 the central sample. * @param s1 the sample to the right of the central sample. * @param s2 the sample to the right of s1. * @param xfrac the subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a float. */ public float interpolateH(float s_, float s0, float s1, float s2, float xfrac) { float[] s = new float[4]; s[0] = s_; s[1] = s0; s[2] = s1; s[3] = s2; return interpolateH(s, xfrac); } /** * Performs vertical interpolation on a pair of floating-point samples. * Subclasses may implement this method to provide a speed improvement * over the array method. This base class method merely calls the * array method. * It should only be called if height == 2 and topPadding == 0. * The setting of subsampleBits need not have any effect on the * interpolation accuracy of an implementation of this method. * *

      By default, vertical interpolation is identical to * horizontal interpolation. Subclasses may choose to implement * them differently. * * @param s0 the central sample. * @param s1 the sample below the central sample. * @param yfrac the Y subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a float. */ public float interpolateV(float s0, float s1, float yfrac) { float[] s = new float[2]; s[0] = s0; s[1] = s1; return interpolateV(s, yfrac); } /** * Performs vertical interpolation on a quadruple of floating-point * samples. * Subclasses may implement this method to provide a speed improvement * over the array method. This base class method merely calls the * array method. * It should only be called if height == 4 and topPadding == 1. * The setting of subsampleBits need not have any effect on the * interpolation accuracy of an implementation of this method. * *

      By default, vertical interpolation is identical to * horizontal interpolation. Subclasses may choose to implement * them differently. * * @param s_ the sample above the central sample. * @param s0 the central sample. * @param s1 the sample below the central sample. * @param s2 the sample below s1. * @param yfrac the Y subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a float. */ public float interpolateV(float s_, float s0, float s1, float s2, float yfrac) { float[] s = new float[4]; s[0] = s_; s[1] = s0; s[2] = s1; s[3] = s2; return interpolateV(s, yfrac); } /** * Performs interpolation on a 2x2 grid of floating-point samples. * Subclasses may implement this method to provide a speed improvement * over the array method. This base class method merely calls the * array method. * It should only be called if width == height == 2 and * leftPadding == topPadding == 0. * The setting of subsampleBits need not have any effect on the * interpolation accuracy of an implementation of this method. * * @param s00 the central sample. * @param s01 the sample to the right of the central sample. * @param s10 the sample below the central sample. * @param s11 the sample below and to the right of the central sample. * @param xfrac the X subsample position, in the range [0.0F, 1.0F). * @param yfrac the Y subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a float. */ public float interpolate(float s00, float s01, float s10, float s11, float xfrac, float yfrac) { float[][] s = new float[4][4]; s[0][0] = s00; s[0][1] = s01; s[1][0] = s10; s[1][1] = s11; return interpolate(s, xfrac, yfrac); } /** * Performs interpolation on a 4x4 grid of floating-point samples. * Subclasses may implement this method to provide a speed improvement * over the array method. This base class method merely calls the * array method. * It should only be called if width == height == 4 and * leftPadding == topPadding == 1. * The setting of subsampleBits need not have any effect on the * interpolation accuracy of an implementation of this method. * * @param s__ the sample above and to the left of the central sample. * @param s_0 the sample above the central sample. * @param s_1 the sample above and one to the right of the central sample. * @param s_2 the sample above and two to the right of the central sample. * @param s0_ the sample to the left of the central sample. * @param s00 the central sample. * @param s01 the sample to the right of the central sample. * @param s02 the sample two to the right of the central sample. * @param s1_ the sample below and one to the left of the central sample. * @param s10 the sample below the central sample. * @param s11 the sample below and one to the right of the central sample. * @param s12 the sample below and two to the right of the central sample. * @param s2_ the sample two below and one to the left of the central sample. * @param s20 the sample two below the central sample. * @param s21 the sample two below and one to the right of the central sample. * @param s22 the sample two below and two to the right of the central sample. * @param xfrac the X subsample position, in the range [0.0F, 1.0F). * @param yfrac the Y subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a float. */ public float interpolate(float s__, float s_0, float s_1, float s_2, float s0_, float s00, float s01, float s02, float s1_, float s10, float s11, float s12, float s2_, float s20, float s21, float s22, float xfrac, float yfrac) { float[][] s = new float[4][4]; s[0][0] = s__; s[0][1] = s_0; s[0][2] = s_1; s[0][3] = s_2; s[1][0] = s0_; s[1][1] = s00; s[1][2] = s01; s[1][3] = s02; s[2][0] = s1_; s[2][1] = s10; s[2][2] = s11; s[2][3] = s12; s[3][0] = s2_; s[3][1] = s20; s[3][2] = s21; s[3][3] = s22; return interpolate(s, xfrac, yfrac); } /** * Performs horizontal interpolation on a 1-dimensional array of * double samples representing a row of samples. * The setting of subsampleBits need not have any effect on the * interpolation accuracy of an implementation of this method. * * @param samples an array of doubles. * @param xfrac the X subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a double. */ public abstract double interpolateH(double[] samples, float xfrac); /** * Performs vertical interpolation on a 1-dimensional array of * double samples representing a column of samples. * The setting of subsampleBits need not have any effect on the * interpolation accuracy of an implementation of this method. * *

      By default, vertical interpolation is identical to * horizontal interpolation. Subclasses may choose to implement * them differently. * * @param samples an array of doubles. * @param yfrac the Y subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a double. */ public double interpolateV(double[] samples, float yfrac) { return interpolateH(samples, yfrac); } /** * Performs interpolation on a 2-dimensional array of * double samples. By default, this is implemented using * a two-pass approach. * The setting of subsampleBits need not have any effect on the * interpolation accuracy of an implementation of this method. * * @param samples an array of doubles. * @param xfrac the X subsample position, in the range [0.0F, 1.0F). * @param yfrac the Y subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a double. */ public double interpolate(double[][] samples, float xfrac, float yfrac) { double[] interpH = new double[height]; for (int i = 0; i < height; i++) { interpH[i] = interpolateH(samples[i], xfrac); } return interpolateV(interpH, yfrac); } /** * Performs horizontal interpolation on a pair of double samples. * Subclasses may implement this method to provide a speed improvement * over the array method. This base class method merely calls the * array method. * It should only be called if width == 2 and leftPadding == 0. * The setting of subsampleBits need not have any effect on the * interpolation accuracy of an implementation of this method. * * @param s0 the central sample. * @param s1 the sample to the right of the central sample. * @param xfrac the subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a double. */ public double interpolateH(double s0, double s1, float xfrac) { double[] s = new double[2]; s[0] = s0; s[1] = s1; return interpolateH(s, xfrac); } /** * Performs horizontal interpolation on a quadruple of double samples. * Subclasses may implement this method to provide a speed improvement * over the array method. This base class method merely calls the * array method. * It should only be called if width == 4 and leftPadding == 1. * The setting of subsampleBits need not have any effect on the * interpolation accuracy of an implementation of this method. * * @param s_ the sample to the left of the central sample. * @param s0 the central sample. * @param s1 the sample to the right of the central sample. * @param s2 the sample to the right of s1. * @param xfrac the subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a double. */ public double interpolateH(double s_, double s0, double s1, double s2, float xfrac) { double[] s = new double[4]; s[0] = s_; s[1] = s0; s[2] = s1; s[3] = s2; return interpolateH(s, xfrac); } /** * Performs vertical interpolation on a pair of double samples. * Subclasses may implement this method to provide a speed improvement * over the array method. This base class method merely calls the * array method. * It should only be called if height == 2 and topPadding == 0. * The setting of subsampleBits need not have any effect on the * interpolation accuracy of an implementation of this method. * *

      By default, vertical interpolation is identical to * horizontal interpolation. Subclasses may choose to implement * them differently. * * @param s0 the central sample. * @param s1 the sample below the central sample. * @param yfrac the Y subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a double. */ public double interpolateV(double s0, double s1, float yfrac) { double[] s = new double[2]; s[0] = s0; s[1] = s1; return interpolateV(s, yfrac); } /** * Performs vertical interpolation on a quadruple of double samples. * Subclasses may implement this method to provide a speed improvement * over the array method. This base class method merely calls the * array method. * It should only be called if height == 4 and topPadding == 1. * The setting of subsampleBits need not have any effect on the * interpolation accuracy of an implementation of this method. * *

      By default, vertical interpolation is identical to * horizontal interpolation. Subclasses may choose to implement * them differently. * * @param s_ the sample above the central sample. * @param s0 the central sample. * @param s1 the sample below the central sample. * @param s2 the sample below s1. * @param yfrac the Y subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a double. */ public double interpolateV(double s_, double s0, double s1, double s2, float yfrac) { double[] s = new double[4]; s[0] = s_; s[1] = s0; s[2] = s1; s[3] = s2; return interpolateV(s, yfrac); } /** * Performs interpolation on a 2x2 grid of double samples. * Subclasses may implement this method to provide a speed improvement * over the array method. This base class method merely calls the * array method. * It should only be called if width == height == 2 and * leftPadding == topPadding == 0. * The setting of subsampleBits need not have any effect on the * interpolation accuracy of an implementation of this method. * * @param s00 the central sample. * @param s01 the sample to the right of the central sample. * @param s10 the sample below the central sample. * @param s11 the sample below and to the right of the central sample. * @param xfrac the X subsample position, in the range [0.0F, 1.0F). * @param yfrac the Y subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a double. */ public double interpolate(double s00, double s01, double s10, double s11, float xfrac, float yfrac) { double[][] s = new double[4][4]; s[0][0] = s00; s[0][1] = s01; s[1][0] = s10; s[1][1] = s11; return interpolate(s, xfrac, yfrac); } /** * Performs interpolation on a 4x4 grid of double samples. * It should only be called if width == height == 4 and * leftPadding == topPadding == 1. * The setting of subsampleBits need not have any effect on the * interpolation accuracy of an implementation of this method. * * @param s__ the sample above and to the left of the central sample. * @param s_0 the sample above the central sample. * @param s_1 the sample above and one to the right of the central sample. * @param s_2 the sample above and two to the right of the central sample. * @param s0_ the sample to the left of the central sample. * @param s00 the central sample. * @param s01 the sample to the right of the central sample. * @param s02 the sample two to the right of the central sample. * @param s1_ the sample below and one to the left of the central sample. * @param s10 the sample below the central sample. * @param s11 the sample below and one to the right of the central sample. * @param s12 the sample below and two to the right of the central sample. * @param s2_ the sample two below and one to the left of the central sample. * @param s20 the sample two below the central sample. * @param s21 the sample two below and one to the right of the central sample. * @param s22 the sample two below and two to the right of the central sample. * @param xfrac the X subsample position, in the range [0.0F, 1.0F). * @param yfrac the Y subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a double. */ public double interpolate(double s__, double s_0, double s_1, double s_2, double s0_, double s00, double s01, double s02, double s1_, double s10, double s11, double s12, double s2_, double s20, double s21, double s22, float xfrac, float yfrac) { double[][] s = new double[4][4]; s[0][0] = s__; s[0][1] = s_0; s[0][2] = s_1; s[0][3] = s_2; s[1][0] = s0_; s[1][1] = s00; s[1][2] = s01; s[1][3] = s02; s[2][0] = s1_; s[2][1] = s10; s[2][2] = s11; s[2][3] = s12; s[3][0] = s2_; s[3][1] = s20; s[3][2] = s21; s[3][3] = s22; return interpolate(s, xfrac, yfrac); } } jai-core-1.1.4/src/share/classes/javax/media/jai/BorderExtenderReflect.java0000644000175000017500000003032210203035544026421 0ustar mathieumathieu/* * $RCSfile: BorderExtenderReflect.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:04 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.WritableRaster; import com.sun.media.jai.util.JDKWorkarounds; /** * A subclass of BorderExtender that implements * border extension by filling all pixels outside of the image * bounds with copies of the whole image. For example, the image: * *

      * * *
      |>
      |\
      * *
      if extended by adding two extra rows to the top and bottom and * one extra column on the left and right sides, would become: * *

      * * * * * * *
      <|
      /|
      |>
      |\
      <|
      /|
      \|
      <|
      |/
      |>
      \|
      <|
      <|
      /|
      |>
      |\
      <|
      /|
      \|
      <|
      |/
      |>
      \|
      <|
      <|
      /|
      |>
      |\
      <|
      /|
      * *

      This form of extension avoids discontinuities around the edges * of the image. */ public final class BorderExtenderReflect extends BorderExtender { BorderExtenderReflect() {} private void flipX(WritableRaster raster) { int minX = raster.getMinX(); int minY = raster.getMinY(); int height = raster.getHeight(); int width = raster.getWidth(); int maxX = minX + width - 1; // Inclusive int numBands = raster.getNumBands(); switch (raster.getSampleModel().getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_INT: int[] iData0 = new int[height*numBands]; int[] iData1 = new int[height*numBands]; for (int i = 0; i < width/2; i++) { raster.getPixels(minX + i, minY, 1, height, iData0); raster.getPixels(maxX - i, minY, 1, height, iData1); raster.setPixels(minX + i, minY, 1, height, iData1); raster.setPixels(maxX - i, minY, 1, height, iData0); } break; case DataBuffer.TYPE_FLOAT: float[] fData0 = new float[height*numBands]; float[] fData1 = new float[height*numBands]; for (int i = 0; i < width/2; i++) { raster.getPixels(minX + i, minY, 1, height, fData0); raster.getPixels(maxX - i, minY, 1, height, fData1); raster.setPixels(minX + i, minY, 1, height, fData1); raster.setPixels(maxX - i, minY, 1, height, fData0); } break; case DataBuffer.TYPE_DOUBLE: double[] dData0 = new double[height*numBands]; double[] dData1 = new double[height*numBands]; for (int i = 0; i < width/2; i++) { raster.getPixels(minX + i, minY, 1, height, dData0); raster.getPixels(maxX - i, minY, 1, height, dData1); raster.setPixels(minX + i, minY, 1, height, dData1); raster.setPixels(maxX - i, minY, 1, height, dData0); } break; } } private void flipY(WritableRaster raster) { int minX = raster.getMinX(); int minY = raster.getMinY(); int height = raster.getHeight(); int width = raster.getWidth(); int maxY = minY + height - 1; // Inclusive int numBands = raster.getNumBands(); switch (raster.getSampleModel().getDataType()) { case DataBuffer.TYPE_BYTE: case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_INT: int[] iData0 = new int[width*numBands]; int[] iData1 = new int[width*numBands]; for (int i = 0; i < height/2; i++) { raster.getPixels(minX, minY + i, width, 1, iData0); raster.getPixels(minX, maxY - i, width, 1, iData1); raster.setPixels(minX, minY + i, width, 1, iData1); raster.setPixels(minX, maxY - i, width, 1, iData0); } break; case DataBuffer.TYPE_FLOAT: float[] fData0 = new float[width*numBands]; float[] fData1 = new float[width*numBands]; for (int i = 0; i < height/2; i++) { raster.getPixels(minX, minY + i, width, 1, fData0); raster.getPixels(minX, maxY - i, width, 1, fData1); raster.setPixels(minX, minY + i, width, 1, fData1); raster.setPixels(minX, maxY - i, width, 1, fData0); } break; case DataBuffer.TYPE_DOUBLE: double[] dData0 = new double[width*numBands]; double[] dData1 = new double[width*numBands]; for (int i = 0; i < height/2; i++) { raster.getPixels(minX, minY + i, width, 1, dData0); raster.getPixels(minX, maxY - i, width, 1, dData1); raster.setPixels(minX, minY + i, width, 1, dData1); raster.setPixels(minX, maxY - i, width, 1, dData0); } break; } } /** * Fills in the portions of a given Raster that lie * outside the bounds of a given PlanarImage with * suitably reflected copies of the entire image. * *

      The portion of raster that lies within * im.getBounds() is not altered. * * @param raster The WritableRaster the border area of * which is to be filled with suitably reflected copies * of portions of the specified image. * @param im The PlanarImage the data of which is * to be reflected and used to fill the * WritableRaster border. * * @throws IllegalArgumentException if either parameter is * null. */ public final void extend(WritableRaster raster, PlanarImage im) { if ( raster == null || im == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } int width = raster.getWidth(); int height = raster.getHeight(); int minX = raster.getMinX(); int maxX = minX + width; int minY = raster.getMinY(); int maxY = minY + height; int imMinX = im.getMinX(); int imMinY = im.getMinY(); int imWidth = im.getWidth(); int imHeight = im.getHeight(); int validMinX = Math.max(imMinX, minX); int validMaxX = Math.min(imMinX + imWidth, maxX); int validMinY = Math.max(imMinY, minY); int validMaxY = Math.min(imMinY + imHeight, maxY); if(validMinX > validMaxX || validMinY > validMaxY) { // Raster does not intersect image. Determine the location // and size of the smallest rectangle containing the Raster // and which intersects the image. if(validMinX > validMaxX) { // no intersetion in X if(minX == validMinX) { minX = im.getMaxX() - 1; } else { maxX = im.getMinX(); } } if(validMinY > validMaxY) { // no intersetion in Y if(minY == validMinY) { minY = im.getMaxY() - 1; } else { maxY = im.getMinY(); } } // Create minimum Raster. WritableRaster wr = raster.createCompatibleWritableRaster(minX, minY, maxX - minX, maxY - minY); // Extend the data. extend(wr, im); // Create a child with same bounds as the target Raster. Raster child = wr.createChild(raster.getMinX(), raster.getMinY(), raster.getWidth(), raster.getHeight(), raster.getMinX(), raster.getMinY(), null); // Copy the data from the child. JDKWorkarounds.setRect(raster, child, 0, 0); return; } Rectangle rect = new Rectangle(); // Notionally extend the source image by treating it as a single // tile of an infinite tiled image. Adjacent tiles are reflections // of one another. // Compute the min and max X and Y tile indices of the area // intersected by the output raster. int minTileX = PlanarImage.XToTileX(minX, imMinX, imWidth); int maxTileX = PlanarImage.XToTileX(maxX - 1, imMinX, imWidth); int minTileY = PlanarImage.YToTileY(minY, imMinY, imHeight); int maxTileY = PlanarImage.YToTileY(maxY - 1, imMinY, imHeight); // Loop over the tiles for (int tileY = minTileY; tileY <= maxTileY; tileY++) { int ty = tileY*imHeight + imMinY; for (int tileX = minTileX; tileX <= maxTileX; tileX++) { int tx = tileX*imWidth + imMinX; // Don't touch the central "tile" (actual image) if (tileX == 0 && tileY == 0) { continue; } boolean flipX = (Math.abs(tileX) % 2) == 1; boolean flipY = (Math.abs(tileY) % 2) == 1; // Clip the tile bounds against the bounds of the Raster. // Keep track of the (x, y) offset of the start of the tile. rect.x = tx; rect.y = ty; rect.width = imWidth; rect.height = imHeight; int xOffset = 0; if (rect.x < minX) { xOffset = minX - rect.x; rect.x = minX; rect.width -= xOffset; } int yOffset = 0; if (rect.y < minY) { yOffset = minY - rect.y; rect.y = minY; rect.height -= yOffset; } if (rect.x + rect.width > maxX) { rect.width = maxX - rect.x; } if (rect.y + rect.height > maxY) { rect.height = maxY - rect.y; } int imX; if (flipX) { if (xOffset == 0) { imX = imMinX + imWidth - rect.width; } else { imX = imMinX; } } else { imX = imMinX + xOffset; } int imY; if (flipY) { if (yOffset == 0) { imY = imMinY + imHeight - rect.height; } else { imY = imMinY; } } else { imY = imMinY + yOffset; } // Create a child raster with coordinates within the // actual image. WritableRaster child = RasterFactory.createWritableChild(raster, rect.x, rect.y, rect.width, rect.height, imX, imY, null); // Copy the data into the Raster im.copyData(child); if (flipX) { flipX(child); } if (flipY) { flipY(child); } } } } } jai-core-1.1.4/src/share/classes/javax/media/jai/ParameterListDescriptor.java0000644000175000017500000001222710203035544027017 0ustar mathieumathieu/* * $RCSfile: ParameterListDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:14 $ * $State: Exp $ */ package javax.media.jai; import javax.media.jai.util.Range; /** A class that signifies that a parameter has no default value. */ class ParameterNoDefault implements java.io.Serializable { ParameterNoDefault() {} public String toString() { return "No Parameter Default"; } } /** * This interface provides a comprehensive description of a set of * parameters including parameter names, parameter defaults, * valid parameter value ranges, etc. * * The parameter names should be used in a case retentive manner. i.e. * all lookups and comparisons are case-insensitive but any request * for a parameter name should return the original name with the case * preserved. * * @see ParameterList * * @since JAI 1.1 */ public interface ParameterListDescriptor { /** * An Object that signifies that a parameter has * no default value. */ public static final Object NO_PARAMETER_DEFAULT = ParameterNoDefault.class; /** * Returns the total number of parameters. */ int getNumParameters(); /** * Returns an array of Classes that describe the types * of parameters. If there are no parameters, this method returns * null. */ Class[] getParamClasses(); /** * Returns an array of Strings that are the * names of the parameters associated with this descriptor. If there * are no parameters, this method returns null. */ String[] getParamNames(); /** * Returns an array of Objects that define the default * values of the parameters. Since null might be a * valid parameter value, the NO_PARAMETER_DEFAULT * static Object is used to indicate that a parameter * has no default value. If there are no parameters, this method * returns null. */ Object[] getParamDefaults(); /** * Returns the default value of a specified parameter. The default * value may be null. If a parameter has no default * value, this method returns NO_PARAMETER_DEFAULT. * * @param parameterName The name of the parameter whose default * value is queried. * * @throws IllegalArgumentException if parameterName is null * or if the parameter does not exist. */ Object getParamDefaultValue(String parameterName); /** * Returns the Range that represents the range of valid * values for the specified parameter. Returns null if * the parameter can take on any value or if the valid values are * not representable as a Range. * * @param parameterName The name of the parameter whose valid range * of values is to be determined. * * @throws IllegalArgumentException if parameterName is null * or if the parameter does not exist. */ Range getParamValueRange(String parameterName); /** * Return an array of the names of all parameters the type of which is * EnumeratedParameter. * * @return The requested array of names or null if there * are no parameters with EnumeratedParameter type. */ String[] getEnumeratedParameterNames(); /** * Return an array of EnumeratedParameter objects * corresponding to the parameter with the specified name. * * @param parameterName The name of the parameter for which the * EnumeratedParameter array is to be returned. * * @throws IllegalArgumentException if parameterName is null * or if the parameter does not exist. * @throws UnsupportedOperationException if there are no enumerated * parameters associated with the descriptor. * @throws IllegalArgumentException if parameterName is * a parameter the class of which is not a subclass of * EnumeratedParameter. * * @return An array of EnumeratedParameter objects * representing the range of values for the named parameter. */ EnumeratedParameter[] getEnumeratedParameterValues(String parameterName); /** * Checks to see whether the specified parameter can take on the specified * value. * * @param parameterName The name of the parameter for which the * validity check is to be performed. * * @throws IllegalArgumentException if parameterName is null * or if the parameter does not exist. * @throws IllegalArgumentException if the class of the object "value" * is not an instance of the class type of parameter * pointed to by the parameterName * * @return true, if it is valid to pass this value in for this * parameter, false otherwise. */ boolean isParameterValueValid(String parameterName, Object value); } jai-core-1.1.4/src/share/classes/javax/media/jai/PackedImageData.java0000644000175000017500000001021610203035544025124 0ustar mathieumathieu/* * $RCSfile: PackedImageData.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:14 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; /** * This class is used by PixelAccessor to store packed * image data along with access information. The data must be single * banded and one bit in depth, and are stored in a one-dimensional * byte array, with eight pixels packed into one * byte. To insure that each scanline has an integral number * of bytes the last byte may be padded with zeros. * * @since JAI 1.1 * */ public final class PackedImageData { /** The Raster containing the pixel data. */ public final Raster raster; /** * The rectangular region within the Raster from which the * data are to be retrieved. */ public final Rectangle rect; /** The data array supplied to store the converted data. */ public final byte[] data; /** The number of array elements in each scanline. */ public final int lineStride; /** * The number of array elements from the beginning of the data array * to the first pixel of the Rectangle. Since there is * only one sample per pixel, there is only one offset value. */ public final int offset; /** * The number of bits into the byte that contains the first pixel * of each scanline. This is the same for every scanline. Note * that the bit offset is counted from left to right in a byte. That * is, the most significant bit (bit 7) has an offset of 0. */ public final int bitOffset; /** Whether the data have been coerced to have zero offsets. */ public final boolean coercedZeroOffset; /** * Indicates whether the PixelAccessor can and must set the * data back into the Raster. If the data does not need * to be copied back to the Raster, this variable should be * set to false. Only destinations can be set. */ public final boolean convertToDest; /** Constructs a PackedImageRaster. * @param raster The Raster containing the pixel data. * @param rect The rectangular region from which the data are extracted. * @param data The byte data array supplied to store the data. * @param lineStride The data array increment to move from the coordinate * x of line i to coordinate x of line i+1. * @param offset The number of bytes from the start of the data array * at which to store the first pixel of the rectangle. * @param bitOffset The number of bits into the byte that contains the * first pixel of each scanline. This is the same for * every scanline. Note that the bit offset is counted * from left to right in a byte. That is, the most * significant bit of a byte (bit 7) has an offset of 0. * @param coercedZeroOffset Whether the data have been coerced to * have zero offsets. * @param convertToDest A boolean indicating whether the data * can and must be set back into the * Raster. This applies only to * destinations. */ public PackedImageData(Raster raster, Rectangle rect, byte[] data, int lineStride, int offset, int bitOffset, boolean coercedZeroOffset, boolean convertToDest) { this.raster = raster; this.rect = rect; this.data = data; this.lineStride = lineStride; this.offset = offset; this.bitOffset = bitOffset; this.coercedZeroOffset = coercedZeroOffset; this.convertToDest = convertToDest; } } jai-core-1.1.4/src/share/classes/javax/media/jai/RemoteImage.java0000644000175000017500000007107010203035544024403 0ustar mathieumathieu/* * $RCSfile: RemoteImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:19 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Image; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.awt.image.renderable.RenderableImage; import java.awt.image.renderable.RenderContext; import java.io.Serializable; import java.net.InetAddress; import java.rmi.Naming; import java.rmi.RemoteException; import java.util.Vector; import javax.media.jai.remote.SerializableRenderedImage; import com.sun.media.jai.rmi.RMIImage; import com.sun.media.jai.rmi.RasterProxy; import com.sun.media.jai.rmi.RenderContextProxy; /** * A sub-class of PlanarImage which represents an image on a * remote server machine. * *

      The image may be constructed from a RenderedImage or from * an imaging chain in either the rendered or renderable mode. Network errors * (detected via throws of RemoteExceptions) are dealt with through retries; * when the limit of retries is exceeded, a null Raster may be returned. * The default number of retries is set to 5 and the default timeout is set * to 1 second. * *

      Note that the registry of the server will be used. In particular if * an OperationRegistry was present in the * RenderingHints used to construct a RenderedOp * or RenderableOp it will not be serialized and transmitted * to the server. * *

      Image layout attributes, once requested, are cached locally for speed. * * @deprecated as of JAI 1.1 in favor of * javax.media.jai.remote.RemoteJAI. * */ public class RemoteImage extends PlanarImage { /** The amount of time to wait between retries. */ static final int DEFAULT_TIMEOUT = 1000; // Milliseconds /** The default number of retries. */ static final int DEFAULT_NUM_RETRIES = 5; /** Index of local variable. */ static final int VAR_MIN_X = 0; /** Index of local variable. */ static final int VAR_MIN_Y = 1; /** Index of local variable. */ static final int VAR_WIDTH = 2; /** Index of local variable. */ static final int VAR_HEIGHT = 3; /** Index of local variable. */ static final int VAR_TILE_WIDTH = 4; /** Index of local variable. */ static final int VAR_TILE_HEIGHT = 5; /** Index of local variable. */ static final int VAR_TILE_GRID_X_OFFSET = 6; /** Index of local variable. */ static final int VAR_TILE_GRID_Y_OFFSET = 7; /** Index of local variable. */ static final int VAR_SAMPLE_MODEL = 8; /** Index of local variable. */ static final int VAR_COLOR_MODEL = 9; /** Index of local variable. */ static final int VAR_SOURCES = 10; /** Index of local variable. */ static final int NUM_VARS = 11; /** The class of the serializable representation of a NULL property. */ private static final Class NULL_PROPERTY_CLASS = com.sun.media.jai.rmi.RMIImageImpl.NULL_PROPERTY.getClass(); /** The RMIImage our data will come from. */ protected RMIImage remoteImage; /** The RMI ID of this object. */ private Long id = null; /** Valid bits for locally cached variables. */ protected boolean [] fieldValid = new boolean[NUM_VARS]; /** Locally cached version of properties. */ protected String[] propertyNames = null; /** The amount of time between retries (milliseconds). */ protected int timeout = DEFAULT_TIMEOUT; /** The number of retries. */ protected int numRetries = DEFAULT_NUM_RETRIES; /** The bounds of this image. */ private Rectangle imageBounds = null; private static Vector vectorize(RenderedImage image) { Vector v = new Vector(1); v.add(image); return v; } /** * Constructs a RemoteImage from a RenderedImage. * *

      The RenderedImage source should ideally be a lightweight * reference to an image available locally on the server or over a * further network link. * *

      Although it is legal to use any RenderedImage, one should be * aware that this will require copying of the image data via transmission * over a network link. * *

      The name of the server must be supplied in the form appropriate to * the implementation. In the reference port of JAI, RMI is used to * implement remote imaging so that the server name must be supplied in * the format *

           * host:port
           * 
      * where the port number is optional and may be supplied only if * the host name is supplied. If this parameter is null the default * is to search for the RMIImage service on the local host at the * default rmiregistry port (1099). * * @param serverName The name of the server in the appropriate format. * @param source A RenderedImage source which must not be null. * @throws IllegalArgumentException if source is * null. */ public RemoteImage(String serverName, RenderedImage source) { super(null, null, null); if(serverName == null) serverName = getLocalHostAddress(); // Look for a separator indicating the remote image chaining hack // in which case the serverName argument contains host[:port]::id // where id is the RMI ID of the image on the indicated server. int index = serverName.indexOf("::"); boolean remoteChainingHack = index != -1; if(!remoteChainingHack && source == null) { // Don't throw the NullPointerException if it's the hack. throw new IllegalArgumentException(JaiI18N.getString("RemoteImage1")); } if(remoteChainingHack) { // Extract the RMI ID from the servername string and replace // the original serverName string with one of the usual type. id = Long.valueOf(serverName.substring(index+2)); serverName = serverName.substring(0, index); } // Construct the remote RMI image. getRMIImage(serverName); if(!remoteChainingHack) { // Get the RMI ID for this object. getRMIID(); } // Cache the server name and RMI ID in a property. setRMIProperties(serverName); if(source != null) { // Source may be null only for the hack. try { if(source instanceof Serializable) { remoteImage.setSource(id, source); } else { remoteImage.setSource(id, new SerializableRenderedImage(source)); } } catch(RemoteException e) { throw new RuntimeException(e.getMessage()); } } } /** * Constructs a RemoteImage from a RenderedOp, * i.e., an imaging directed acyclic graph (DAG). * *

      This DAG will be copied over to the server where it will be * transformed into an OpImage chain using the server's local * OperationRegistry and available RenderedImageFactory objects. * *

      The name of the server must be supplied in the form appropriate to * the implementation. In the reference port of JAI, RMI is used to * implement remote imaging so that the server name must be supplied in * the format *

           * host:port
           * 
      * where the port number is optional and may be supplied only if * the host name is supplied. If this parameter is null the default * is to search for the RMIImage service on the local host at the * default rmiregistry port (1099). * *

      Note that the properties of the RemoteImage will be * those of the RenderedOp node and not of its rendering. * * @param serverName The name of the server in the appropriate format. * @param source A RenderedOp source which must not be null. * @throws IllegalArgumentException if source is * null. */ public RemoteImage(String serverName, RenderedOp source) { super(null, null, null); if(serverName == null) serverName = getLocalHostAddress(); if(source == null) { throw new IllegalArgumentException(JaiI18N.getString("RemoteImage1")); } // Construct the remote RMI image. getRMIImage(serverName); // Get the RMI ID for this object. getRMIID(); // Cache the server name and RMI ID in a property. setRMIProperties(serverName); try { remoteImage.setSource(id, source); } catch(RemoteException e) { throw new RuntimeException(e.getMessage()); } } /** * Constructs a RemoteImage from a RenderableOp * and RenderContext. * The entire RenderableOp DAG will be copied over to the server. * *

      The name of the server must be supplied in the form appropriate to * the implementation. In the reference port of JAI, RMI is used to * implement remote imaging so that the server name must be supplied in * the format *

           * host:port
           * 
      * where the port number is optional and may be supplied only if * the host name is supplied. If this parameter is null the default * is to search for the RMIImage service on the local host at the * default rmiregistry port (1099). * *

      Note that the properties of the RemoteImage will be * those of the RenderableOp node and not of its rendering. * * @param serverName The name of the server in the appropriate format. * @param source A RenderableOp source which must not be null. * @param renderContext The rendering context which may be null. * @throws IllegalArgumentException if source is * null. */ public RemoteImage(String serverName, RenderableOp source, RenderContext renderContext) { super(null, null, null); if(serverName == null) serverName = getLocalHostAddress(); if(source == null) { throw new IllegalArgumentException(JaiI18N.getString("RemoteImage1")); } if (renderContext == null) { renderContext = new RenderContext(new AffineTransform()); } // Construct the remote RMI image. getRMIImage(serverName); // Get the RMI ID for this object. getRMIID(); // Cache the server name and RMI ID in a property. setRMIProperties(serverName); // Create the serializable form of the RenderContext. RenderContextProxy rcp = new RenderContextProxy(renderContext); try { remoteImage.setSource(id, source, rcp); } catch(RemoteException e) { e.printStackTrace(); throw new RuntimeException(e.getMessage()); } } /** * Construct an RMIImage on the indicated server. * *

      The name of the server must be supplied in the form *

           * host:port
           * 
      * where the port number is optional and may be supplied only if * the host name is supplied. If this parameter is null the default * is to search for the RMIImage service on the local host at the * default rmiregistry port (1099). * *

      The result is cached in the instance variable "remoteImage". * * @param serverName The name of the server in the format described. */ private void getRMIImage(String serverName) { // Set the server name to the local host if null. if(serverName == null) serverName = getLocalHostAddress(); // Derive the service name. String serviceName = new String("rmi://"+serverName+"/"+ RMIImage.RMI_IMAGE_SERVER_NAME); // Look up the remote object. remoteImage = null; try { remoteImage = (RMIImage)Naming.lookup(serviceName); } catch(Exception e) { throw new RuntimeException(e.getMessage()); } } /** * Get the default server name, that is, the local host. * When the provided server name is null, use * this method to obtain the local host IP address as the server name. */ private String getLocalHostAddress() { String serverName; try { serverName = InetAddress.getLocalHost().getHostAddress(); } catch(Exception e) { throw new RuntimeException(e.getMessage()); } return serverName; } /** * Get the unique ID to be used to refer to this object on the server. * The result is cached in the instance variable "id". */ private void getRMIID() { try { id = remoteImage.getRemoteID(); } catch(Exception e) { throw new RuntimeException(e.getMessage()); } } /** * Cache the argument and the RMI ID as local properties. * This is a gross hack to permit chaining of remote images. * * @param serverName The server name as described in the constructors. */ private void setRMIProperties(String serverName) { setProperty(getClass().getName()+".serverName", serverName); setProperty(getClass().getName()+".id", id); } /** * Disposes of any resources allocated for remote operation. */ protected void finalize() { try { remoteImage.dispose(id); } catch(Exception e) { // Ignore the Exception. } } /** * Set the amount of time between retries. * * @param timeout The time interval between retries (milliseconds). If * this is non-positive the time interval is not changed. */ public void setTimeout(int timeout) { if(timeout > 0) { this.timeout = timeout; } } /** * Gets the amount of time between retries. */ public int getTimeout() { return timeout; } /** * Set the number of retries. * * @param numRetries The number of retries. If this is non-positive the * number of retries is not changed. */ public void setNumRetries(int numRetries) { if(numRetries > 0) { this.numRetries = numRetries; } } /** * Gets the number of retries. */ public int getNumRetries() { return numRetries; } /** * Cause an instance variable of the remote object to be cached * locally, retrying a given number of times with a given timeout. * * @param fieldIndex the index of the desired field. * @param retries the maximum number of retries; must be positive. * @param timeout the timeout interval between retries, in milliseconds; * must be positive. * @throws ArrayIndexOutOfBoundsException if fieldIndex * is negative or >= NUM_VARS. * @throws IllegalArgumentException if retries or timeout * is non-positive. */ protected void requestField(int fieldIndex, int retries, int timeout) { if(retries < 0) { throw new IllegalArgumentException(JaiI18N.getString("RemoteImage3")); } else if(timeout < 0) { throw new IllegalArgumentException(JaiI18N.getString("RemoteImage4")); } int count = 0; if (fieldValid[fieldIndex]) return; while (count++ < retries) { try { switch (fieldIndex) { case VAR_MIN_X: minX = remoteImage.getMinX(id); break; case VAR_MIN_Y: minY = remoteImage.getMinY(id); break; case VAR_WIDTH: width = remoteImage.getWidth(id); break; case VAR_HEIGHT: height = remoteImage.getHeight(id); break; case VAR_TILE_WIDTH: tileWidth = remoteImage.getTileWidth(id); break; case VAR_TILE_HEIGHT: tileHeight = remoteImage.getTileHeight(id); break; case VAR_TILE_GRID_X_OFFSET: tileGridXOffset = remoteImage.getTileGridXOffset(id); break; case VAR_TILE_GRID_Y_OFFSET: tileGridYOffset = remoteImage.getTileGridYOffset(id); break; case VAR_SAMPLE_MODEL: sampleModel = (SampleModel)remoteImage.getSampleModel(id).getSampleModel(); break; case VAR_COLOR_MODEL: colorModel = (ColorModel)remoteImage.getColorModel(id).getColorModel(); break; case VAR_SOURCES: { Vector localSources = remoteImage.getSources(id); int numSources = localSources.size(); for(int i = 0; i < numSources; i++) { RenderedImage src = (RenderedImage)localSources.get(i); addSource(PlanarImage.wrapRenderedImage(src)); } } break; } fieldValid[fieldIndex] = true; return; } catch (RemoteException e) { System.err.println(JaiI18N.getString("RemoteImage0")); try { java.lang.Thread.sleep(timeout); } catch (java.lang.InterruptedException f) { } } } } /** * Causes an instance variable of the remote object to be cached * locally, retrying with a default/user specified timeout. * * @param fieldIndex the index of the desired field. * @throws ArrayIndexOutOfBoundsException if fieldIndex * is negative or >= NUM_VARS. */ protected void requestField(int fieldIndex) { requestField(fieldIndex, numRetries, timeout); } /** * Returns the X coordinate of the leftmost column of the image. */ public int getMinX() { requestField(VAR_MIN_X); return minX; } /** * Returns the X coordinate of the column immediately to the right * of the rightmost column of the image. */ public int getMaxX() { requestField(VAR_MIN_X); requestField(VAR_WIDTH); return minX + width; } /** Returns the Y coordinate of the uppermost row of the image. */ public int getMinY() { requestField(VAR_MIN_Y); return minY; } /** * Returns the Y coordinate of the row immediately below the * bottom row of the image. */ public int getMaxY() { requestField(VAR_MIN_Y); requestField(VAR_HEIGHT); return minY + height; } /** Returns the width of the RemoteImage in pixels. */ public int getWidth() { requestField(VAR_WIDTH); return width; } /** Returns the height of the RemoteImage in pixels. */ public int getHeight() { requestField(VAR_HEIGHT); return height; } /** Returns the width of a tile in pixels. */ public int getTileWidth() { requestField(VAR_TILE_WIDTH); return tileWidth; } /** Returns the height of a tile in pixels. */ public int getTileHeight() { requestField(VAR_TILE_HEIGHT); return tileHeight; } /** Returns the X offset of the tile grid. */ public int getTileGridXOffset() { requestField(VAR_TILE_GRID_X_OFFSET); return tileGridXOffset; } /** Returns the Y offset of the tile grid. */ public int getTileGridYOffset() { requestField(VAR_TILE_GRID_Y_OFFSET); return tileGridYOffset; } /** Returns the SampleModel associated with this image. */ public SampleModel getSampleModel() { requestField(VAR_SAMPLE_MODEL); return sampleModel; } /** Returns the ColorModel associated with this image. */ public ColorModel getColorModel() { requestField(VAR_COLOR_MODEL); return colorModel; } /** * Returns a vector of RenderedImages that are the sources of * image data for this RenderedImage. Note that this method * will often return null. */ public Vector getSources() { requestField(VAR_SOURCES); return super.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. * * @exception IllegalArgumentException if propertyName * is null. */ public Object getProperty(String name) { // Try to get property locally. Object property = super.getProperty(name); if (property == null || property == Image.UndefinedProperty) { // We have never requested this property, get it from the server. int count = 0; while (count++ < numRetries) { try { property = remoteImage.getProperty(id, name); if(NULL_PROPERTY_CLASS.isInstance(property)) { property = Image.UndefinedProperty; } break; } catch (RemoteException e) { try { java.lang.Thread.sleep(timeout); } catch (java.lang.InterruptedException f) { } } } if (property == null) { property = Image.UndefinedProperty; } if(property != Image.UndefinedProperty) { setProperty(name, property); // Cache property locally } } return property; } /** Returns a list of names recognized by getProperty. */ public String[] getPropertyNames() { // Retrieve local property names. String[] localPropertyNames = super.getPropertyNames(); // Put local names in a Vector. Vector names = new Vector(); if (localPropertyNames != null) { for(int i = 0; i < localPropertyNames.length; i++) { names.add(localPropertyNames[i]); } } // Get the remote property names. int count = 0; String[] remotePropertyNames = null; while (count++ < numRetries) { try { remotePropertyNames = remoteImage.getPropertyNames(id); break; } catch (RemoteException e) { try { java.lang.Thread.sleep(timeout); } catch (java.lang.InterruptedException f) { } } } // Put the remote names, if any, in the Vector. if(remotePropertyNames != null) { for(int i = 0; i < remotePropertyNames.length; i++) { if(!names.contains(remotePropertyNames[i])) { names.add(remotePropertyNames[i]); } } } // Set the return value from the vector. propertyNames = names.size() == 0 ? null : (String[])names.toArray(new String[names.size()]); return propertyNames; } /** * Returns tile (x, y). Note that x and y are indexes into the * tile array not pixel locations. The Raster that is returned * is a copy. * * @param x the X index of the requested tile in the tile array * @param y the Y index of the requested tile in the tile array */ public Raster getTile(int x, int y) { int count = 0; while (count++ < numRetries) { try { RasterProxy rp = remoteImage.getTile(id, x, y); return rp.getRaster(); } catch (RemoteException e) { try { java.lang.Thread.sleep(timeout); } catch (java.lang.InterruptedException f) { } } } return null; } /** * Returns the image as one large tile. */ public Raster getData() { int count = 0; while (count++ < numRetries) { try { RasterProxy rp = remoteImage.getData(id); return rp.getRaster(); } catch (RemoteException e) { try { java.lang.Thread.sleep(timeout); } catch (java.lang.InterruptedException f) { } } } return null; } /** * Returns an arbitrary rectangular region of the RemoteImage. * *

      The rect parameter may be * null, in which case the entire image data is * returned in the Raster. * *

      If rect is non-null but does * not intersect the image bounds at all, an * IllegalArgumentException will be thrown. * * @param rect The Rectangle of interest. */ public Raster getData(Rectangle rect) { if(imageBounds == null) { imageBounds = getBounds(); } if (rect == null) { rect = imageBounds; } else if (!rect.intersects(imageBounds)) { throw new IllegalArgumentException(JaiI18N.getString("RemoteImage2")); } int count = 0; while (count++ < numRetries) { try { RasterProxy rp = remoteImage.getData(id, rect); return rp.getRaster(); } catch (RemoteException e) { try { java.lang.Thread.sleep(timeout); } catch (java.lang.InterruptedException f) { } } } return null; } /** * Returns an arbitrary rectangular region of the RemoteImage * in a user-supplied WritableRaster. * The rectangular region is the entire image if the argument is * null or the intersection of the argument bounds with the image * bounds if the region is non-null. * If the argument is non-null but has bounds which have an empty * intersection with the image bounds the return value will be null. * The return value may also be null if the argument is non-null but * is incompatible with the Raster returned from the remote image. */ public WritableRaster copyData(WritableRaster raster) { int count = 0; Rectangle bounds = ((raster == null) ? new Rectangle(getMinX(), getMinY(), getWidth(), getHeight()) : raster.getBounds()); while (count++ < numRetries) { try { RasterProxy rp = remoteImage.copyData(id, bounds); try { if(raster == null) { raster = (WritableRaster)rp.getRaster(); } else { raster.setDataElements(bounds.x, bounds.y, (Raster)rp.getRaster()); } break; } catch(ArrayIndexOutOfBoundsException e) { raster = null; break; } } catch (RemoteException e) { try { java.lang.Thread.sleep(timeout); } catch (java.lang.InterruptedException f) { } } } return raster; } } jai-core-1.1.4/src/share/classes/javax/media/jai/WarpQuadratic.java0000644000175000017500000002022010203035544024743 0ustar mathieumathieu/* * $RCSfile: WarpQuadratic.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:25 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Rectangle; import java.awt.geom.Point2D; /** * A quadratic-based description of an image warp. * *

      The source position (x', y') of a point (x, y) is given by the * quadratic bivariate polynomials: * *

       * x' = p(x, y) = c1 + c2*x + c3*y + c4*x^2 + c5*x*y + c6*y^2
       * y' = q(x, y) = c7 + c8*x + c9*y + c10*x^2 + c11*x*y + c12*y^2
       * 
      * *

      WarpQuadratic is marked final so that it may be * more easily inlined. * * @see WarpPolynomial * */ public final class WarpQuadratic extends WarpPolynomial { private float c1, c2, c3, c4, c5, c6; // coefficients for X private float c7, c8, c9, c10, c11, c12; // coefficients for Y /** * Constructs a WarpQuadratic with a given transform mapping * destination pixels into source space. Note that this is * a backward mapping as opposed to the forward mapping used in * AffineOpImage. The coeffs arrays must each contain 6 floats * corresponding to the coefficients c1, c2, etc. as shown in the * class comment. * * @param xCoeffs The six destination to source transform coefficients for * the X coordinate. * @param yCoeffs The six destination to source transform coefficients for * the Y coordinate. * @param preScaleX The scale factor to apply to input (dest) X positions. * @param preScaleY The scale factor to apply to input (dest) Y positions. * @param postScaleX The scale factor to apply to the result of the * X polynomial evaluation * @param postScaleY The scale factor to apply to the result of the * Y polynomial evaluation * @throws IllegalArgumentException if the xCoeff and yCoeff arrays * do not each have size entries. */ public WarpQuadratic(float[] xCoeffs, float[] yCoeffs, float preScaleX, float preScaleY, float postScaleX, float postScaleY) { super(xCoeffs, yCoeffs, preScaleX, preScaleY, postScaleX, postScaleY); if (xCoeffs.length != 6 || yCoeffs.length != 6) { throw new IllegalArgumentException(JaiI18N.getString("WarpQuadratic0")); } c1 = xCoeffs[0]; c2 = xCoeffs[1]; c3 = xCoeffs[2]; c4 = xCoeffs[3]; c5 = xCoeffs[4]; c6 = xCoeffs[5]; c7 = yCoeffs[0]; c8 = yCoeffs[1]; c9 = yCoeffs[2]; c10 = yCoeffs[3]; c11 = yCoeffs[4]; c12 = yCoeffs[5]; } /** * Constructs a WarpQuadratic with pre- and * post-scale factors of 1. * * @param xCoeffs The 6 destination to source transform coefficients for * the X coordinate. * @param yCoeffs The 6 destination to source transform coefficients for * the Y coordinate. * @throws IllegalArgumentException if the xCoeff and yCoeff arrays * do not each have size entries. */ public WarpQuadratic(float[] xCoeffs, float[] yCoeffs) { this(xCoeffs, yCoeffs, 1.0F, 1.0F, 1.0F, 1.0F); } /** * Computes the source subpixel positions for a given rectangular * destination region, subsampled with an integral period. The * destination region is specified using normal integral (full * pixel) coordinates. The source positions returned by the * method are specified in floating point. * * @param x The minimum X coordinate of the destination region. * @param y The minimum Y coordinate of the destination region. * @param width The width of the destination region. * @param height The height of the destination region. * @param periodX The horizontal sampling period. * @param periodY The vertical sampling period. * * @param destRect A float array containing at least * 2*((width+periodX-1)/periodX)* * ((height+periodY-1)/periodY) * elements, or null. If null, a * new array will be constructed. * * @return A reference to the destRect parameter if * it is non-null, or a new * float array otherwise. * @throws ArrayBoundsException if destRect is too small */ public float[] warpSparseRect(int x, int y, int width, int height, int periodX, int periodY, float[] destRect) { //XXX: This method should do its calculations in doubles if (destRect == null) { destRect = new float[((width + periodX - 1) / periodX) * ((height + periodY - 1) / periodY) * 2]; } // // x' = c1 + c2*x + c3*y + c4*x^2 + c5*x*y + c6*y^2 // y' = c7 + c8*x + c9*y + c10*x^2 + c11*x*y + c12*y^2 // float px1 = periodX * preScaleX; // powers for periodX float px2 = px1 * px1; // Delta delta x for both polys. float ddx = c4 * 2 * px2; float ddy = c10 * 2 * px2; float x1 = (x + 0.5F) * preScaleX; // powers for x float x2 = x1 * x1; width += x; height += y; int index = 0; for (int j = y; j < height; j += periodY) { // Pre-scaled input coordinates and step. float y1 = (j + 0.5F) * preScaleY; // powers for current y float y2 = y1 * y1; // The warped position for the first point of the current line float wx = c1 + c2 * x1 + c3 * y1 + c4 * x2 + c5 * x1 * y1 + c6 * y2; float wy = c7 + c8 * x1 + c9 * y1 + c10 * x2 + c11 * x1 * y1 + c12 * y2; // Delta x and delta y float dx = c2 * px1 + c4 * (2 * x1 * px1 + px2) + c5 * px1 * y1; float dy = c8 * px1 + c10 * (2 * x1 * px1 + px2) + c11 * px1 * y1; for (int i = x; i < width; i += periodX) { destRect[index++] = wx * postScaleX - 0.5F; destRect[index++] = wy * postScaleY - 0.5F; wx += dx; wy += dy; dx += ddx; dy += ddy; } } return destRect; } /** * Computes the source point corresponding to the supplied point. * *

      This method returns the value of pt in the following * code snippet: * *

           * double x1 = (destPt.getX() + 0.5F)*preScaleX;
           * double x2 = x1*x1;
           *
           * double y1 = (destPt.getY() + 0.5F)*preScaleY;
           * double y2 = y1*y1;
           *
           * double x = c1 + c2*x1 + c3*y1 + c4*x2 + c5*x1*y1 + c6*y2;
           * double y = c7 + c8*x1 + c9*y1 + c10*x2 + c11*x1*y1 + c12*y2;
           *
           * Point2D pt = (Point2D)destPt.clone();
           * pt.setLocation(x*postScaleX - 0.5, y*postScaleY - 0.5);
           * 
      *

      * * @param destPt the position in destination image coordinates * to map to source image coordinates. * * @return a Point2D of the same class as * destPt. * * @throws IllegalArgumentException if destPt is * null. * * @since JAI 1.1.2 */ public Point2D mapDestPoint(Point2D destPt) { if (destPt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } double x1 = (destPt.getX() + 0.5F)*preScaleX; double x2 = x1*x1; double y1 = (destPt.getY() + 0.5F)*preScaleY; double y2 = y1*y1; double x = c1 + c2*x1 + c3*y1 + c4*x2 + c5*x1*y1 + c6*y2; double y = c7 + c8*x1 + c9*y1 + c10*x2 + c11*x1*y1 + c12*y2; Point2D pt = (Point2D)destPt.clone(); pt.setLocation(x*postScaleX - 0.5, y*postScaleY - 0.5); return pt; } } jai-core-1.1.4/src/share/classes/javax/media/jai/BorderExtender.java0000644000175000017500000001627610203035544025130 0ustar mathieumathieu/* * $RCSfile: BorderExtender.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:04 $ * $State: Exp $ */ package javax.media.jai; import java.io.Serializable; import java.awt.Rectangle; import java.awt.image.WritableRaster; /** * An abstract superclass for classes that extend or "pad" a * WritableRaster with additional pixel data taken from a * PlanarImage. Instances of BorderExtender * are used by the getExtendedData() and * copyExtendedData() methods in * PlanarImage. * *

      Each instance of BorderExtender has an * extend() method that takes a * WritableRaster and a PlanarImage. The * portion of the raster that intersects the bounds of the image will * already contain a copy of the image data. The remaining area is to * be filled in according to the policy of the * BorderExtender subclass. * *

      The standard subclasses of BorderExtender are * BorderExtenderZero, which fills pixels with zeros; * BorderExtenderConstant, which fills pixels with a * given constant value; BorderExtenderCopy, which copies * the edge pixels of the image; BorderExtenderWrap, * which tiles the plane with repeating copies of the image; and * BorderExtenderReflect, which is like * BorderExtenderWrap except that each copy of the image * is suitably reflected. * *

      Instances of BorderExtenderConstant are * constructed in the usual way. Instances of the other standard subclasses * are obtained by means of the createInstance() method * of this class. * *

      BorderExtenderCopy is particularly useful as a way * of padding image data prior to performing area or geometric * operations such as convolution, scaling, and rotation. * * @see PlanarImage#getExtendedData * @see PlanarImage#copyExtendedData * @see BorderExtenderConstant * @see BorderExtenderCopy * @see BorderExtenderReflect * @see BorderExtenderWrap * @see BorderExtenderZero */ public abstract class BorderExtender implements Serializable { /** A constant for use in the createInstance method. */ public static final int BORDER_ZERO = 0; /** A constant for use in the createInstance method. */ public static final int BORDER_COPY = 1; /** A constant for use in the createInstance method. */ public static final int BORDER_REFLECT = 2; /** A constant for use in the createInstance method. */ public static final int BORDER_WRAP = 3; /** Lazily-constructed singleton BorderExtenderZero. */ private static BorderExtender borderExtenderZero = null; /** Lazily-constructed singleton BorderExtenderCopy. */ private static BorderExtender borderExtenderCopy = null; /** Lazily-constructed singleton BorderExtenderReflect. */ private static BorderExtender borderExtenderReflect = null; /** Lazily-constructed singleton BorderExtenderWrap. */ private static BorderExtender borderExtenderWrap = null; /** * Fills in the portions of a given WritableRaster that * lie outside the bounds of a given PlanarImage. * Depending on the policy of the BorderExtender, data * might or might not be derived from the PlanarImage. * *

      The portion of raster that lies within * im.getBounds() must not be altered. The pixels * within this region should not be assumed to have any particular * values. * *

      Each subclass may implement a different policy regarding * how the extension data is computed. * * @param raster The WritableRaster the border area of * which is to be filled according to the policy of the * BorderExtender. * @param im The PlanarImage which may provide the * data with which to fill the border area of the * WritableRaster. * * @throws IllegalArgumentException if either parameter is * null. */ public abstract void extend(WritableRaster raster, PlanarImage im); /** * Returns an instance of BorderExtender that * implements a given extension policy. The policies understood * by this method are: * *

      BORDER_ZERO: set sample values to zero. * *

      BORDER_COPY: set sample values to copies of * the nearest valid pixel. For example, pixels to the left of * the valid rectangle will take on the value of the valid edge * pixel in the same row. Pixels both above and to the left of * the valid rectangle will take on the value of the upper-left * pixel. * *

      BORDER_REFLECT: the output image is defined * as if mirrors were placed along the edges of the source image. * Thus if the left edge of the valid rectangle lies at X = 10, * pixel (9, Y) will be a copy of pixel (10, Y); pixel (6, Y) * will be a copy of pixel (13, Y). * *

      BORDER_WRAP: the source image is tiled repeatedly * in the plane. * *

      Note that this method may not be used to create an instance * of BorderExtenderConstant. * *

      Any other input value will cause an * IllegalArgumentException to be thrown. * * @param extenderType The type of BorderExtender to create. * Must be one of the predefined class constants * BORDER_COPY, * BORDER_REFLECT, * BORDER_WRAP, or * BORDER_ZERO. * * @throws IllegalArgumentException if the supplied * parameter is not one of the supported predefined constants. */ public static BorderExtender createInstance(int extenderType) { switch (extenderType) { case BORDER_ZERO: if(borderExtenderZero == null) { borderExtenderZero = new BorderExtenderZero(); } return borderExtenderZero; case BORDER_COPY: if(borderExtenderCopy == null) { borderExtenderCopy = new BorderExtenderCopy(); } return borderExtenderCopy; case BORDER_REFLECT: if(borderExtenderReflect == null) { borderExtenderReflect = new BorderExtenderReflect(); } return borderExtenderReflect; case BORDER_WRAP: if(borderExtenderWrap == null) { borderExtenderWrap = new BorderExtenderWrap(); } return borderExtenderWrap; default: throw new IllegalArgumentException(JaiI18N.getString("BorderExtender0")); } } } jai-core-1.1.4/src/share/classes/javax/media/jai/ColorSpaceJAI.java0000644000175000017500000017422410203035544024570 0ustar mathieumathieu/* * $RCSfile: ColorSpaceJAI.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:06 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Point; import java.awt.color.ColorSpace; import java.awt.color.ICC_ColorSpace; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferShort; import java.awt.image.DataBufferInt; import java.awt.image.SampleModel; /** * An abstract subclass of ColorSpace which adds methods to * transform colors represented as pixels in a Raster between * a specific color space and either sRGB or a well-defined C.I.E. X,Y,Z * color space. As mentioned in the documentation of {@link ColorSpace}, * sRGB is a proposed standard default RGB color space for the Internet. * *

      This class is particularly applicable for use with color * spaces which are mathematically defined and for which no I.C.C. profile * is readily available. (Note however that color conversions specified * by a simple matrix transformation might best be effected using the * "BandCombine" operation.) The JAI "ColorConvert" operation recognizes when * an instance of ColorSpaceJAI is present and uses the * Raster-based conversion methods to improve performance. * This is possible because without the ColorSpaceJAI definition, * a ColorSpace which was not an {@link ICC_ColorSpace} * would permit color conversion only by means of pixel-by-pixel invocations * of toCIEXYZ(float[]) and fromCIEXYZ(float[]) (or, * equivalently toRGB(float[]) and * fromRGB(float[])).

      * * @see java.awt.color.ColorSpace * @see java.awt.color.ICC_ColorSpace * @see javax.media.jai.operator.ColorConvertDescriptor * @see javax.media.jai.operator.BandCombineDescriptor * * @since JAI 1.1 */ public abstract class ColorSpaceJAI extends ColorSpace { /** Cache the maximum value for XYZ color space. */ private static final double maxXYZ = 1 + 32767.0 / 32768.0; /** Cache the power value for XYZ to RGB */ private static final double power1 = 1.0 / 2.4; /** The map from byte RGB to the step before matrix operation. */ private static double[] LUT = new double[256]; static { for (int i = 0; i < 256; i++) { double v = i / 255.0; if (v < 0.040449936) LUT[i] = v / 12.92; else LUT[i] = Math.pow((v + 0.055) / 1.055, 2.4); } } /** * Whether conversion to/from this ColorSpaceJAI * is more efficient using the sRGB methods. */ private boolean isRGBPreferredIntermediary; /** * Transforms the pixel data in the source Raster from * CIEXYZ to sRGB. It is assumed that the input XYZ values are * represented relative to the CIE D50 white point of the * ColorSpace.CS_CIEXYZ color space. Integral data will * be normalized according to the number of bits specified for the * respective component; floating point data should be between 0.0 and * 1.0 + (32767.0 / 32768.0). All integral data * are assumed to be unsigned; signed data should be shifted by the * caller before invoking this method. * *

      The exact sequence of transformations applied is as follows:

      *

        *
      1. If the source data are integral, convert the digital codes to * CIE XYZ values in the range [0.0, Fmax] * as: *
             * Fd50 = Fmax * Isrc / 2Msrc
             * 
        * * where * *
             * Isrc is the digital code of a source color component
             * Msrc is the number of significant bits per source color
             * component
             * Fmax is 1.0 + (32767.0 / 32768.0)
             * Fd50 is the corresponding CIE XYZ value relative to CIE D50
             * 
        * * If the source data are floating point no scaling is performed and it * is assumed that the data are already clipped to the range * [0.0, 1.0 + (32767.0 / 32768.0)].
      2. *

      3. Perform chromatic adaptation from the CIE D50 white point to the * CIE D65 white point as described in {@link ICC_ColorSpace#fromCIEXYZ}: *
             * Xd65 = Xd50 * (Xwd65 / Xwd50)
             * Yd65 = Yd50 * (Ywd65 / Ywd50)
             * Zd65 = Zd50 * (Zwd65 / Zwd50)
             * 
        * * where * *
             * Xd50, Yd50, Zd50 are the XYZ values relative to CIE D50
             * Xwd65, Ywd65, Zwd65 are the CIE D65 white point values
             * Xwd50, Ywd50, Zwd50 are the CIE D50 white point values
             * Xd65, Yd65, Zd65 are the XYZ values relative to CIE D65
             * 
        * * Substituting the actual CIE D50 and D65 white point values in the * above gives: * *
             * Xd65 = Xd50 * (0.3127/0.3457)
             * Yd65 = Yd50 * (0.3291/0.3585)
             * Zd65 = Zd50 * (0.3582/0.2958)
             * 
      4. *
      5. Calculate sRGB tristimulus values as: *
             * [ RsRGB ]   [  3.2406 -1.5372 -0.4986 ] [ Xd65 ]
             * [ GsRGB ] = [ -0.9689  1.8758  0.0415 ] [ Yd65 ]
             * [ BsRGB ]   [  0.0557 -0.2040  1.0570 ] [ Zd65 ]
             * 
      6. *

      7. * Clip sRGB tristimulus values to the range [0.0, 1.0]. *
      8. *
      9. Transform sRGB tristimulus values to non-linear sR'G'B' values as: *
             * C'sRGB = 12.92*CsRGB if CsRGB <= 0.0031308
             * C'sRGB = 1.055*CsRGB(1.0/2.4) - 0.055 if CsRGB > 0.0031308
             * 
        * * where * *
             * CsRGB is a sRGB tristimulus value
             * C'sRGB is the corresponding non-linear sR'G'B' value
             * 
      10. *
      11. If the destination data are integral, convert the non-linear * sR'G'B' values to digital codes as: *
             * Idest = round(C'sRGB * 2Mdest)
             * 
        * * where * *
             * C'sRGB is the a non-linear sR'G'B' value
             * Mdest is the number of significant bits per destination color
             * component
             * Idest is the digital code of a destination color component
             * 
        * If the destination data are floating point neither scaling nor rounding * is performed. *
      12. *

      * *

      If the destination WritableRaster is null, * a new WritableRaster will be created. The * Rasters are treated as having no alpha channel, i.e., * all bands are color bands.

      * *

      This method is provided for the convenience of extenders defining * a color space for which the conversion is defined with respect to * CIEXYZ.

      * *

      It should be noted that there is no official specification * of sRGB with respect to digital codes of depths other than 8 bits. * The present implementation for other bit depths is provided as an * extrapolation of the 8-bit sRGB standard and its use should be * recognized as such. The extrapolation is implemented by replacing * the white digital count (WDC) and black digital count * (KDC) in the 8-bit sRGB specification with values * corresponding to the extrema of the data type in question when * treated as unsigned. For all data types KDC is zero * and WDC is specified by the component size * parameters.

      * * @param src the source Raster to be converted. * @param srcComponentSize array that specifies the number of significant * bits per source color component; ignored for floating point data. * If null defaults to the value returned by * src.getSampleModel().getSampleSize(). * @param dest the destination WritableRaster, * or null. * @param destComponentSize array that specifies the number of significant * bits per destination color component; ignored for floating point * data. If null, defaults to the value returned by * dest.getSampleModel().getSampleSize(), or the sample * size of the newly created destination WritableRaster if dest is * null. * @return dest color converted from src * or a new, WritableRaster containing the converted * pixels if dest is null. * @exception IllegalArgumentException if src is * null, the number of source or destination * bands is not 3, or either component size array * is non-null and has length not equal to 3. */ public static WritableRaster CIEXYZToRGB(Raster src, int[] srcComponentSize, WritableRaster dest, int[] destComponentSize) { // Validate the parameters checkParameters(src, srcComponentSize, dest,destComponentSize) ; SampleModel srcSampleModel = src.getSampleModel() ; /*if the parameter srcComponentSize is null, use the sample size * of the source raster. */ if (srcComponentSize == null) srcComponentSize = srcSampleModel.getSampleSize() ; // if the destination raster is null, create a new WritableRaster if (dest == null) { Point origin = new Point(src.getMinX(), src.getMinY()) ; dest = RasterFactory.createWritableRaster(srcSampleModel, origin) ; } /* if the parameter dstComponentSize is null, use the sample size * of the source raster. */ SampleModel dstSampleModel = dest.getSampleModel() ; if (destComponentSize == null) destComponentSize = dstSampleModel.getSampleSize() ; PixelAccessor srcAcc = new PixelAccessor(srcSampleModel, null) ; UnpackedImageData srcUid = srcAcc.getPixels(src, src.getBounds(), srcSampleModel.getDataType(), false) ; switch (srcSampleModel.getDataType()) { case DataBuffer.TYPE_BYTE: CIEXYZToRGBByte(srcUid, srcComponentSize, dest, destComponentSize) ; break ; case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: CIEXYZToRGBShort(srcUid, srcComponentSize, dest, destComponentSize) ; break ; case DataBuffer.TYPE_INT: CIEXYZToRGBInt(srcUid, srcComponentSize, dest, destComponentSize) ; break ; case DataBuffer.TYPE_FLOAT: CIEXYZToRGBFloat(srcUid, srcComponentSize, dest, destComponentSize) ; break ; case DataBuffer.TYPE_DOUBLE: CIEXYZToRGBDouble(srcUid, srcComponentSize, dest, destComponentSize) ; break ; } return dest ; } /** * Verify that the parameters are compatible with the limitations * of the static methods {@link #CIEXYZToRGB} and {@link #RGBToCIEXYZ}. * If any of the parameters are not compatible with the requirements * of these methods, throw an IllegalArgumentException * * @throws IllegalArgumentException if src is * null. * @throws IllegalArgumentException if src.getNumBands() * does not return the value 3. * @throws IllegalArgumentException if dst is * non-null and dst.getNumBands() * does not return the value 3. * @throws IllegalArgumentException if srcComponentSize is * non-null but its length is not 3. * @throws IllegalArgumentException if destComponentSize is * non-null but its length is not 3. */ protected static void checkParameters(Raster src, int[] srcComponentSize, WritableRaster dest, int[] destComponentSize) { if (src == null) throw new IllegalArgumentException( JaiI18N.getString("ColorSpaceJAI0")) ; if (src.getNumBands() != 3) throw new IllegalArgumentException( JaiI18N.getString("ColorSpaceJAI1")) ; if (dest != null && dest.getNumBands() != 3) throw new IllegalArgumentException( JaiI18N.getString("ColorSpaceJAI2")) ; if (srcComponentSize != null && srcComponentSize.length != 3) throw new IllegalArgumentException( JaiI18N.getString("ColorSpaceJAI3")) ; if (destComponentSize != null && destComponentSize.length != 3) throw new IllegalArgumentException( JaiI18N.getString("ColorSpaceJAI4")) ; } /** * After conversion, the range of a signed short is * [0, Short.MAX_Value-Short.MIN_VALUE] and that * of an integer is [0, 0xFFFFFFFFL]. To avoid * clamping, convert the value to a signed integer with the same * binary bits. If the dataType parameter is either * DataBuffer.TYPE_SHORT or DataBuffer.TYPE_INT * then the array is modified in place; otherwise the method has no * effect. * * @param buf Array of post-conversion digital codes. * @param dataType The data type: one of the constants TYPE_* * defined in {@link DataBuffer}. */ static void convertToSigned(double[] buf, int dataType) { if (dataType == DataBuffer.TYPE_SHORT) { for (int i = 0; i < buf.length; i++) { short temp = (short)(((int) buf[i])&0xFFFF) ; buf[i] = temp ; } } else if (dataType == DataBuffer.TYPE_INT) { for (int i = 0; i < buf.length; i++) { int temp = (int)(((long)buf[i]) & 0xFFFFFFFFl) ; buf[i] = temp ; } } } static void XYZ2RGB(float[] XYZ, float[] RGB) { RGB[0] = 2.9311227F * XYZ[0] - 1.4111496F * XYZ[1] - 0.6038046F * XYZ[2]; RGB[1] = -0.87637005F * XYZ[0] + 1.7219844F * XYZ[1] + 0.0502565F * XYZ[2]; RGB[2] = 0.05038065F * XYZ[0] - 0.187272F * XYZ[1] + 1.280027F * XYZ[2]; for (int i = 0; i < 3; i++) { float v = RGB[i]; if (v < 0.0F) v = 0.0F; if (v < 0.0031308F) RGB[i] = 12.92F * v; else { if (v > 1.0F) v = 1.0F; RGB[i] = (float)(1.055 * Math.pow(v, power1) - 0.055); } } } private static void roundValues(double[] data) { for (int i = 0; i < data.length; i++) data[i] = (long)(data[i] + 0.5); } // Convert a byte raster from CIEXYZ to RGB color space static void CIEXYZToRGBByte(UnpackedImageData src, int[] srcComponentSize, WritableRaster dest, int[] destComponentSize) { byte[] xBuf = src.getByteData(0) ; byte[] yBuf = src.getByteData(1) ; byte[] zBuf = src.getByteData(2) ; // maps the integral XYZ into floating-point float normx = (float)(maxXYZ / ((1L << srcComponentSize[0]) - 1)); float normy = (float)(maxXYZ / ((1L << srcComponentSize[1]) - 1)); float normz = (float)(maxXYZ / ((1L << srcComponentSize[2]) - 1)); // the upper bounds for the red, green and blue bands double upperr = 1.0, upperg = 1.0, upperb = 1.0 ; int dstType = dest.getSampleModel().getDataType(); // for the integer type, re-calculate the bounds if (dstType < DataBuffer.TYPE_FLOAT) { upperr = (1L << destComponentSize[0]) - 1 ; upperg = (1L << destComponentSize[1]) - 1 ; upperb = (1L << destComponentSize[2]) - 1 ; } int height = dest.getHeight() ; int width = dest.getWidth() ; double[] dstPixels = new double[3 * height * width] ; int xStart = src.bandOffsets[0] ; int yStart = src.bandOffsets[1] ; int zStart = src.bandOffsets[2] ; int srcPixelStride = src.pixelStride; int srcLineStride = src.lineStride; float[] XYZ = new float[3]; float[] RGB = new float[3]; int dIndex = 0 ; for (int j = 0 ; j < height; j++, xStart += srcLineStride, yStart += srcLineStride, zStart += srcLineStride) { for (int i=0, xIndex = xStart, yIndex = yStart, zIndex = zStart ; i < width; i++, xIndex += srcPixelStride, yIndex += srcPixelStride, zIndex += srcPixelStride) { XYZ[0] = (xBuf[xIndex] & 0xFF) * normx ; XYZ[1] = (yBuf[yIndex] & 0xFF) * normy ; XYZ[2] = (zBuf[zIndex] & 0xFF) * normz ; XYZ2RGB(XYZ, RGB); dstPixels[dIndex++] = upperr * RGB[0] ; dstPixels[dIndex++] = upperg * RGB[1] ; dstPixels[dIndex++] = upperb * RGB[2] ; } } // Because of 4738524: setPixels should round the provided double // value instead of casting // If it is fixed, then this piece of code can be removed. if (dstType < DataBuffer.TYPE_FLOAT) roundValues(dstPixels); convertToSigned(dstPixels, dstType) ; dest.setPixels(dest.getMinX(), dest.getMinY(),width,height,dstPixels) ; } // convert a short type raster from CIEXYZ to RGB private static void CIEXYZToRGBShort(UnpackedImageData src, int[] srcComponentSize, WritableRaster dest, int[] destComponentSize) { short[] xBuf = src.getShortData(0) ; short[] yBuf = src.getShortData(1) ; short[] zBuf = src.getShortData(2) ; // maps the integral XYZ into floating-point float normx = (float)(maxXYZ / ((1L << srcComponentSize[0]) - 1)); float normy = (float)(maxXYZ / ((1L << srcComponentSize[1]) - 1)); float normz = (float)(maxXYZ / ((1L << srcComponentSize[2]) - 1)); // the upper bounds for the red, green and blue bands double upperr = 1.0, upperg = 1.0, upperb = 1.0 ; int dstType = dest.getSampleModel().getDataType(); // for the integer type, re-calculate the norm and bands if (dstType < DataBuffer.TYPE_FLOAT) { upperr = (1L << destComponentSize[0]) - 1 ; upperg = (1L << destComponentSize[1]) - 1 ; upperb = (1L << destComponentSize[2]) - 1 ; } int height = dest.getHeight() ; int width = dest.getWidth() ; double[] dstPixels = new double[3 * height * width] ; int xStart = src.bandOffsets[0] ; int yStart = src.bandOffsets[1] ; int zStart = src.bandOffsets[2] ; int srcPixelStride = src.pixelStride; int srcLineStride = src.lineStride; float[] XYZ = new float[3]; float[] RGB = new float[3]; int dIndex = 0 ; for (int j = 0 ; j < height; j++, xStart += srcLineStride, yStart += srcLineStride, zStart += srcLineStride) { for (int i = 0, xIndex = xStart, yIndex = yStart, zIndex = zStart ; i < width; i++, xIndex += srcPixelStride, yIndex += srcPixelStride, zIndex += srcPixelStride) { XYZ[0] = (xBuf[xIndex] & 0xFFFF) * normx ; XYZ[1] = (yBuf[yIndex] & 0xFFFF) * normy ; XYZ[2] = (zBuf[zIndex] & 0xFFFF) * normz ; XYZ2RGB(XYZ, RGB); dstPixels[dIndex++] = upperr * RGB[0] ; dstPixels[dIndex++] = upperg * RGB[1] ; dstPixels[dIndex++] = upperb * RGB[2] ; } } // Because of 4738524: setPixels should round the provided double // value instead of casting // If it is fixed, then this piece of code can be removed. if (dstType < DataBuffer.TYPE_FLOAT) roundValues(dstPixels); convertToSigned(dstPixels, dstType) ; dest.setPixels(dest.getMinX(), dest.getMinY(),width,height,dstPixels) ; } // convert an int type raster from CIEXYZ to RGB private static void CIEXYZToRGBInt(UnpackedImageData src, int[] srcComponentSize, WritableRaster dest, int[] destComponentSize) { int[] xBuf = src.getIntData(0) ; int[] yBuf = src.getIntData(1) ; int[] zBuf = src.getIntData(2) ; // maps the integral XYZ into floating-point float normx = (float)(maxXYZ / ((1L << srcComponentSize[0]) - 1)); float normy = (float)(maxXYZ / ((1L << srcComponentSize[1]) - 1)); float normz = (float)(maxXYZ / ((1L << srcComponentSize[2]) - 1)); // the upper bound for each band double upperr = 1.0, upperg = 1.0, upperb = 1.0 ; int dstType = dest.getSampleModel().getDataType(); // for the integer type, re-calculate the bounds if (dstType < DataBuffer.TYPE_FLOAT) { upperr = (1L << destComponentSize[0]) - 1 ; upperg = (1L << destComponentSize[1]) - 1 ; upperb = (1L << destComponentSize[2]) - 1 ; } int height = dest.getHeight() ; int width = dest.getWidth() ; double[] dstPixels = new double[3 * height * width] ; int xStart = src.bandOffsets[0] ; int yStart = src.bandOffsets[1] ; int zStart = src.bandOffsets[2] ; int srcPixelStride = src.pixelStride; int srcLineStride = src.lineStride; float[] XYZ = new float[3]; float[] RGB = new float[3]; int dIndex = 0 ; for (int j = 0 ; j < height; j++, xStart += srcLineStride, yStart += srcLineStride, zStart += srcLineStride) { for (int i = 0, xIndex = xStart, yIndex = yStart, zIndex = zStart ; i < width; i++, xIndex += srcPixelStride, yIndex += srcPixelStride, zIndex += srcPixelStride) { XYZ[0] = (xBuf[xIndex] & 0xFFFFFFFFl) * normx ; XYZ[1] = (yBuf[yIndex] & 0xFFFFFFFFl) * normy ; XYZ[2] = (zBuf[zIndex] & 0xFFFFFFFFl) * normz ; XYZ2RGB(XYZ, RGB); dstPixels[dIndex++] = upperr * RGB[0] ; dstPixels[dIndex++] = upperg * RGB[1] ; dstPixels[dIndex++] = upperb * RGB[2] ; } } // Because of 4738524: setPixels should round the provided double // value instead of casting // If it is fixed, then this piece of code can be removed. if (dstType < DataBuffer.TYPE_FLOAT) roundValues(dstPixels); convertToSigned(dstPixels, dstType) ; dest.setPixels(dest.getMinX(), dest.getMinY(),width,height,dstPixels) ; } // convert a float type ratser from CIEXYZ to RGB private static void CIEXYZToRGBFloat(UnpackedImageData src, int[] srcComponentSize, WritableRaster dest, int[] destComponentSize) { float[] xBuf = src.getFloatData(0) ; float[] yBuf = src.getFloatData(1) ; float[] zBuf = src.getFloatData(2) ; // the upper bounds for the 3 bands double upperr = 1.0, upperg = 1.0, upperb = 1.0 ; int dstType = dest.getSampleModel().getDataType(); // for the integer type, re-calculate the bounds if (dstType < DataBuffer.TYPE_FLOAT) { upperr = (1L << destComponentSize[0]) - 1 ; upperg = (1L << destComponentSize[1]) - 1 ; upperb = (1L << destComponentSize[2]) - 1 ; } int height = dest.getHeight() ; int width = dest.getWidth() ; double[] dstPixels = new double[3 * height * width] ; int xStart = src.bandOffsets[0] ; int yStart = src.bandOffsets[1] ; int zStart = src.bandOffsets[2] ; int srcPixelStride = src.pixelStride; int srcLineStride = src.lineStride; float[] XYZ = new float[3]; float[] RGB = new float[3]; int dIndex = 0 ; for (int j = 0 ; j < height; j++, xStart += srcLineStride, yStart += srcLineStride, zStart += srcLineStride) { for (int i = 0, xIndex = xStart, yIndex = yStart, zIndex = zStart ; i < width; i++, xIndex += srcPixelStride, yIndex += srcPixelStride, zIndex += srcPixelStride) { XYZ[0] = xBuf[xIndex]; XYZ[1] = yBuf[yIndex]; XYZ[2] = zBuf[zIndex]; XYZ2RGB(XYZ, RGB); dstPixels[dIndex++] = upperr * RGB[0] ; dstPixels[dIndex++] = upperg * RGB[1] ; dstPixels[dIndex++] = upperb * RGB[2] ; } } // Because of 4738524: setPixels should round the provided double // value instead of casting // If it is fixed, then this piece of code can be removed. if (dstType < DataBuffer.TYPE_FLOAT) roundValues(dstPixels); convertToSigned(dstPixels, dstType) ; dest.setPixels(dest.getMinX(), dest.getMinY(),width,height,dstPixels) ; } // convert a double type ratser form CIEXYZ to RGB color space private static void CIEXYZToRGBDouble(UnpackedImageData src, int[] srcComponentSize, WritableRaster dest, int[] destComponentSize) { double[] xBuf = src.getDoubleData(0) ; double[] yBuf = src.getDoubleData(1) ; double[] zBuf = src.getDoubleData(2) ; // the upper bound of each band double upperr = 1.0, upperg = 1.0, upperb = 1.0 ; int dstType = dest.getSampleModel().getDataType(); // for the integer type, re-calculate the bounds if (dstType < DataBuffer.TYPE_FLOAT) { upperr = (1L << destComponentSize[0]) - 1 ; upperg = (1L << destComponentSize[1]) - 1 ; upperb = (1L << destComponentSize[2]) - 1 ; } int height = dest.getHeight() ; int width = dest.getWidth() ; double[] dstPixels = new double[3 * height * width] ; int xStart = src.bandOffsets[0] ; int yStart = src.bandOffsets[1] ; int zStart = src.bandOffsets[2] ; int srcPixelStride = src.pixelStride; int srcLineStride = src.lineStride; float[] XYZ = new float[3]; float[] RGB = new float[3]; int dIndex = 0 ; for (int j = 0 ; j < height; j++, xStart += srcLineStride, yStart += srcLineStride, zStart += srcLineStride) { for (int i = 0, xIndex = xStart, yIndex = yStart, zIndex = zStart ; i < width; i++, xIndex += srcPixelStride, yIndex += srcPixelStride, zIndex += srcPixelStride) { XYZ[0] = (float)xBuf[xIndex]; XYZ[1] = (float)yBuf[yIndex]; XYZ[2] = (float)zBuf[zIndex]; XYZ2RGB(XYZ, RGB); dstPixels[dIndex++] = upperr * RGB[0] ; dstPixels[dIndex++] = upperg * RGB[1] ; dstPixels[dIndex++] = upperb * RGB[2] ; } } // Because of 4738524: setPixels should round the provided double // value instead of casting // If it is fixed, then this piece of code can be removed. if (dstType < DataBuffer.TYPE_FLOAT) roundValues(dstPixels); convertToSigned(dstPixels, dstType) ; dest.setPixels(dest.getMinX(), dest.getMinY(),width,height,dstPixels) ; } /** * Transforms the pixel data in the source Raster from * sRGB to CIEXYZ. The output XYZ values are represented relative to * the CIE D50 white point of the ColorSpace.CS_CIEXYZ * color space. Integral data will be normalized according to the * number of bits specified for the respective component; floating * point data should be between 0.0 and 1.0. All integral data are * assumed to be unsigned; signed data should be shifted by the caller * before invoking this method. * *

      The exact sequence of transformations applied is as follows:

      *

        *
      1. If the source data are integral, convert the digital codes to * non-linear sRGB values in the range [0.0, 1.0] as: *
             * C'sRGB = Isrc / 2Msrc
             * 
        * * where * *
             * Isrc is the digital code of a source color component
             * Msrc is the number of significant bits per source color
             * component
             * C'sRGB is the corresponding sR'G'B' value
             * 
        * * If the source data are floating point no scaling is performed and it * is assumed that the data are already clipped to the range * [0.0, 1.0].
      2. *

      3. Transform non-linear sR'G'B' values to sRGB tristimulus values * as: *
             * CsRGB = C'sRGB/12.92 if C'sRGB <= 0.04045
             * CsRGB = [(C'sRGB + 0.055)/1.055]2.4 if C'sRGB > 0.04045
             * 
        * * where * *
             * C'sRGB is a non-linear sR'G'B' value
             * CsRGB is the corresponding sRGB tristimulus value
             * 
      4. *
      5. Calculate CIE XYZ D65-relative values as: *
             * [ Xd65 ]   [ 0.4124 0.3576 0.1805 ] [ RsRGB ]
             * [ Yd65 ] = [ 0.2126 0.7152 0.0722 ] [ GsRGB ]
             * [ Zd65 ]   [ 0.0193 0.1192 0.9505 ] [ BsRGB ]
             * 
      6. *
      7. Perform chromatic adaptation from the CIE D65 white point to the * CIE D50 white point as described in {@link ICC_ColorSpace#toCIEXYZ}: *
             * Xd50 = Xd65 * (Xwd50 / Xwd65)
             * Yd50 = Yd65 * (Ywd50 / Ywd65)
             * Zd50 = Zd65 * (Zwd50 / Zwd65)
             * 
        * * where * *
             * Xd65, Yd65, Zd65 are the XYZ values relative to CIE D65
             * Xwd50, Ywd50, Zwd50 are the CIE D50 white point values
             * Xwd65, Ywd65, Zwd65 are the CIE D65 white point values
             * Xd50, Yd50, Zd50 are the XYZ values relative to CIE D50
             * 
        * * Substituting the actual CIE D50 and D65 white point values in the * above gives: * *
             * Xd50 = Xd65 * (0.3457/0.3127)
             * Yd50 = Yd65 * (0.3585/0.3291)
             * Zd50 = Zd65 * (0.2958/0.3582)
             * 
      8. *
      9. If the destination data are integral, convert the CIE XYZ * values to digital codes as: *
             * Idest = round(Fd50 * 2Mdest / Fmax)
             * 
        * * where * *
             * Fd50 is a CIE XYZ value relative to CIE D50
             * Mdest is the number of significant bits per destination color
             * component
             * Fmax is 1.0 + (32767.0 / 32768.0)
             * Idest is the digital code of a destination color component
             * 
        * If the destination data are floating point neither scaling nor rounding * is performed. *
      10. *

      * *

      If the destination WritableRaster is null, * a new WritableRaster will be created. The * Rasters are treated as having no alpha channel, i.e., * all bands are color bands. * *

      This method is provided for the convenience of extenders defining * a color space for which the conversion is defined with respect to * sRGB. * *

      It should be noted that there is no official specification * of sRGB with respect to digital codes of depths other than 8 bits. * The present implementation for other bit depths is provided as an * extrapolation of the 8-bit sRGB standard and its use should be * recognized as such. The extrapolation is implemented by replacing * the white digital count (WDC) and black digital count * (KDC) in the 8-bit sRGB specification with values * corresponding to the extrema of the data type in question when * treated as unsigned. For all data types KDC is zero * and WDC is specified by the component size * parameters.

      * * @param src the source Raster to be converted. * @param srcComponentSize array that specifies the number of significant * bits per source color component; ignored for floating point data. * If null defaults to the value returned by * src.getSampleModel().getSampleSize(). * @param dest the destination WritableRaster, * or null. * @param destComponentSize array that specifies the number of significant * bits per destination color component; ignored for floating point * data. If null, defaults to the value returned by * dest.getSampleModel().getSampleSize(), or the sample * size of the newly created destination WritableRaster if dest is * null. * @return dest color converted from src * or a new, WritableRaster containing the converted * pixels if dest is null. * @exception IllegalArgumentException if src is * null, the number of source or destination * bands is not 3, or either component size array * is non-null and has length not equal to 3. */ public static WritableRaster RGBToCIEXYZ(Raster src, int[] srcComponentSize, WritableRaster dest, int[] destComponentSize) { checkParameters(src, srcComponentSize, dest,destComponentSize) ; SampleModel srcSampleModel = src.getSampleModel() ; // if the srcComponentSize is not provided, use the sample sizes // from the source's sample model if (srcComponentSize == null) srcComponentSize = srcSampleModel.getSampleSize() ; // if the destination raster is not provided, create a new one if (dest == null) { Point origin = new Point(src.getMinX(), src.getMinY()) ; dest = RasterFactory.createWritableRaster(srcSampleModel, origin) ; } SampleModel dstSampleModel = dest.getSampleModel() ; //if the destComponentSize is not provided, use the sample sizes //from the destination's sample model if (destComponentSize == null) destComponentSize = dstSampleModel.getSampleSize() ; PixelAccessor srcAcc = new PixelAccessor(srcSampleModel, null) ; UnpackedImageData srcUid = srcAcc.getPixels(src, src.getBounds(), srcSampleModel.getDataType(), false) ; switch (srcSampleModel.getDataType()) { case DataBuffer.TYPE_BYTE: RGBToCIEXYZByte(srcUid, srcComponentSize, dest, destComponentSize) ; break ; case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: RGBToCIEXYZShort(srcUid, srcComponentSize, dest, destComponentSize) ; break ; case DataBuffer.TYPE_INT: RGBToCIEXYZInt(srcUid, srcComponentSize, dest, destComponentSize) ; break ; case DataBuffer.TYPE_FLOAT: RGBToCIEXYZFloat(srcUid, srcComponentSize, dest, destComponentSize) ; break ; case DataBuffer.TYPE_DOUBLE: RGBToCIEXYZDouble(srcUid, srcComponentSize, dest, destComponentSize) ; break ; } return dest ; } static void RGB2XYZ(float[] RGB, float[] XYZ) { for (int i = 0; i < 3; i++) { if (RGB[i] < 0.040449936F) RGB[i] /= 12.92F; else RGB[i] = (float)(Math.pow((RGB[i] + 0.055) / 1.055, 2.4)); } XYZ[0] = 0.45593763F * RGB[0] + 0.39533819F * RGB[1] + 0.19954964F * RGB[2]; XYZ[1] = 0.23157515F * RGB[0] + 0.77905262F * RGB[1] + 0.07864978F * RGB[2]; XYZ[2] = 0.01593493F * RGB[0] + 0.09841772F * RGB[1] + 0.78488615F * RGB[2]; } // convert a byte ratser from RGB to CIEXYZ private static void RGBToCIEXYZByte(UnpackedImageData src, int[] srcComponentSize, WritableRaster dest, int[] destComponentSize) { byte[] rBuf = src.getByteData(0) ; byte[] gBuf = src.getByteData(1) ; byte[] bBuf = src.getByteData(2) ; // used to left-shift the value to fill in all the 8-bits int normr = 8 - srcComponentSize[0] ; int normg = 8 - srcComponentSize[1] ; int normb = 8 - srcComponentSize[2] ; // the norms used to map the color value to the desired range double normx = 1.0, normy = normx, normz = normx ; int dstType = dest.getSampleModel().getDataType() ; boolean isInt = (dstType < DataBuffer.TYPE_FLOAT) ; // for the integer type, redefine the norms and upper bounds // because rgb={1.0, 1.0, 1.0} is xyz={0.950456, 1.0, 1.088754}, // so for normx, normz, they are specially treated if (isInt) { normx = ((1L << destComponentSize[0]) - 1) / maxXYZ ; normy = ((1L << destComponentSize[1]) - 1) / maxXYZ; normz = ((1L << destComponentSize[2]) - 1) / maxXYZ ; } int height = dest.getHeight() ; int width = dest.getWidth() ; double[] dstPixels = new double[3 * height * width] ; int rStart = src.bandOffsets[0] ; int gStart = src.bandOffsets[1] ; int bStart = src.bandOffsets[2] ; int srcPixelStride = src.pixelStride; int srcLineStride = src.lineStride; int dIndex = 0 ; for (int j = 0 ; j < height; j++, rStart += srcLineStride, gStart += srcLineStride, bStart += srcLineStride) { for (int i = 0, rIndex = rStart, gIndex = gStart, bIndex = bStart ; i < width; i++, rIndex += srcPixelStride, gIndex += srcPixelStride, bIndex += srcPixelStride) { double R = LUT[(rBuf[rIndex] & 0xFF) << normr] ; double G = LUT[(gBuf[gIndex] & 0xFF) << normg] ; double B = LUT[(bBuf[bIndex] & 0xFF) << normb] ; if (isInt) { double X, Y, Z ; dstPixels[dIndex++] = (0.45593763 * R + 0.39533819 * G + 0.19954964 * B) * normx; dstPixels[dIndex++] = (0.23157515 * R + 0.77905262 * G + 0.07864978 * B) * normy; dstPixels[dIndex++] = (0.01593493 * R + 0.09841772 * G + 0.78488615 * B) * normz; } else { dstPixels[dIndex++] = 0.45593763 * R + 0.39533819 * G + 0.19954964 * B ; dstPixels[dIndex++] = 0.23157515 * R + 0.77905262 * G + 0.07864978 * B ; dstPixels[dIndex++] = 0.01593493 * R + 0.09841772 * G + 0.78488615 * B ; } } } // Because of 4738524: setPixels should round the provided double // value instead of casting // If it is fixed, then this piece of code can be removed. if (dstType < DataBuffer.TYPE_FLOAT) roundValues(dstPixels); convertToSigned(dstPixels, dstType) ; dest.setPixels(dest.getMinX(), dest.getMinY(),width,height,dstPixels) ; } // convert a short ratser from RGB to CIEXYZ private static void RGBToCIEXYZShort(UnpackedImageData src, int[] srcComponentSize, WritableRaster dest, int[] destComponentSize) { short[] rBuf = src.getShortData(0) ; short[] gBuf = src.getShortData(1) ; short[] bBuf = src.getShortData(2) ; // used to left-shift the input value to fill all the bits float normr = (1 << srcComponentSize[0]) - 1 ; float normg = (1 << srcComponentSize[1]) - 1 ; float normb = (1 << srcComponentSize[2]) - 1 ; // used to map the output to the desired range double normx = 1.0, normy = 1.0, normz = 1.0 ; int dstType = dest.getSampleModel().getDataType() ; boolean isInt = (dstType < DataBuffer.TYPE_FLOAT) ; // define the norms and upper bounds for the integer types // see the comments in RGBToCIEXYZByte if (isInt) { normx = ((1L << destComponentSize[0]) - 1) / maxXYZ ; normy = ((1L << destComponentSize[1]) - 1) / maxXYZ; normz = ((1L << destComponentSize[2]) - 1) / maxXYZ ; } int height = dest.getHeight() ; int width = dest.getWidth() ; double[] dstPixels = new double[3 * height * width] ; int rStart = src.bandOffsets[0] ; int gStart = src.bandOffsets[1] ; int bStart = src.bandOffsets[2] ; int srcPixelStride = src.pixelStride; int srcLineStride = src.lineStride; float[] XYZ = new float[3]; float[] RGB = new float[3]; int dIndex = 0 ; for (int j = 0 ; j < height; j++, rStart += srcLineStride, gStart += srcLineStride, bStart += srcLineStride) { for (int i = 0, rIndex = rStart, gIndex = gStart, bIndex = bStart ; i < width; i++, rIndex += srcPixelStride, gIndex += srcPixelStride, bIndex += srcPixelStride) { RGB[0] = (rBuf[rIndex] & 0xFFFF) / normr ; RGB[1] = (gBuf[gIndex] & 0xFFFF) / normg ; RGB[2] = (bBuf[bIndex] & 0xFFFF) / normb ; RGB2XYZ(RGB, XYZ); if (isInt) { dstPixels[dIndex++] = XYZ[0] * normx ; dstPixels[dIndex++] = XYZ[1] * normy ; dstPixels[dIndex++] = XYZ[2] * normz ; } else { dstPixels[dIndex++] = XYZ[0] ; dstPixels[dIndex++] = XYZ[1] ; dstPixels[dIndex++] = XYZ[2] ; } } } // Because of 4738524: setPixels should round the provided double // value instead of casting // If it is fixed, then this piece of code can be removed. if (dstType < DataBuffer.TYPE_FLOAT) roundValues(dstPixels); convertToSigned(dstPixels, dstType) ; dest.setPixels(dest.getMinX(), dest.getMinY(),width,height,dstPixels) ; } // convert a int type ratser from RGB to CIEXYZ private static void RGBToCIEXYZInt(UnpackedImageData src, int[] srcComponentSize, WritableRaster dest, int[] destComponentSize) { int[] rBuf = src.getIntData(0) ; int[] gBuf = src.getIntData(1) ; int[] bBuf = src.getIntData(2) ; // used to left-shift the input to fill all the bits float normr = (1L << srcComponentSize[0]) - 1 ; float normg = (1L << srcComponentSize[1]) - 1 ; float normb = (1L << srcComponentSize[2]) - 1 ; // norms to map the output to the desired range double normx = 1.0, normy = 1.0, normz = 1.0 ; int dstType = dest.getSampleModel().getDataType() ; boolean isInt = (dstType < DataBuffer.TYPE_FLOAT) ; // define the norm and upper bounds for the integer output types // see also the comments in RGBToCIEXYZByte if (isInt) { normx = ((1L << destComponentSize[0]) - 1) / maxXYZ ; normy = ((1L << destComponentSize[1]) - 1) / maxXYZ; normz = ((1L << destComponentSize[2]) - 1) / maxXYZ ; } int height = dest.getHeight() ; int width = dest.getWidth() ; double[] dstPixels = new double[3 * height * width] ; int rStart = src.bandOffsets[0] ; int gStart = src.bandOffsets[1] ; int bStart = src.bandOffsets[2] ; int srcPixelStride = src.pixelStride; int srcLineStride = src.lineStride; float[] XYZ = new float[3]; float[] RGB = new float[3]; int dIndex = 0 ; for (int j = 0 ; j < height; j++, rStart += srcLineStride, gStart += srcLineStride, bStart += srcLineStride) { for (int i = 0, rIndex = rStart, gIndex = gStart, bIndex = bStart ; i < width; i++, rIndex += srcPixelStride, gIndex += srcPixelStride, bIndex += srcPixelStride) { RGB[0] = (rBuf[rIndex] & 0xFFFFFFFFl) / normr ; RGB[1] = (gBuf[gIndex] & 0xFFFFFFFFl) / normg ; RGB[2] = (bBuf[bIndex] & 0xFFFFFFFFl) / normb ; RGB2XYZ(RGB, XYZ); if (isInt) { dstPixels[dIndex++] = XYZ[0] * normx; dstPixels[dIndex++] = XYZ[1] * normx; dstPixels[dIndex++] = XYZ[2] * normx; } else { dstPixels[dIndex++] = XYZ[0] ; dstPixels[dIndex++] = XYZ[1] ; dstPixels[dIndex++] = XYZ[2] ; } } } // Because of 4738524: setPixels should round the provided double // value instead of casting // If it is fixed, then this piece of code can be removed. if (dstType < DataBuffer.TYPE_FLOAT) roundValues(dstPixels); convertToSigned(dstPixels, dstType) ; dest.setPixels(dest.getMinX(), dest.getMinY(),width,height,dstPixels) ; } // convert a float type ratser from RGB to CIEXYZ color space private static void RGBToCIEXYZFloat(UnpackedImageData src, int[] srcComponentSize, WritableRaster dest, int[] destComponentSize) { float[] rBuf = src.getFloatData(0) ; float[] gBuf = src.getFloatData(1) ; float[] bBuf = src.getFloatData(2) ; // norms to map the output value to the desired range double normx = 1.0, normy = 1.0, normz = 1.0 ; int dstType = dest.getSampleModel().getDataType() ; boolean isInt = (dstType < DataBuffer.TYPE_FLOAT) ; // define the norms and upper bounds for the integer types if (isInt) { normx = ((1L << destComponentSize[0]) - 1) / maxXYZ ; normy = ((1L << destComponentSize[1]) - 1) / maxXYZ; normz = ((1L << destComponentSize[2]) - 1) / maxXYZ ; } int height = dest.getHeight() ; int width = dest.getWidth() ; double[] dstPixels = new double[3 * height * width] ; int rStart = src.bandOffsets[0] ; int gStart = src.bandOffsets[1] ; int bStart = src.bandOffsets[2] ; int srcPixelStride = src.pixelStride; int srcLineStride = src.lineStride; float[] XYZ = new float[3]; float[] RGB = new float[3]; int dIndex = 0 ; for (int j = 0 ; j < height; j++, rStart += srcLineStride, gStart += srcLineStride, bStart += srcLineStride) { for (int i = 0, rIndex = rStart, gIndex = gStart, bIndex = bStart ; i < width; i++, rIndex += srcPixelStride, gIndex += srcPixelStride, bIndex += srcPixelStride) { RGB[0] = rBuf[rIndex] ; RGB[1] = gBuf[gIndex] ; RGB[2] = bBuf[bIndex] ; RGB2XYZ(RGB, XYZ); if (isInt) { dstPixels[dIndex++] = XYZ[0] * normx; dstPixels[dIndex++] = XYZ[1] * normx; dstPixels[dIndex++] = XYZ[2] * normx; } else { dstPixels[dIndex++] = XYZ[0] ; dstPixels[dIndex++] = XYZ[1] ; dstPixels[dIndex++] = XYZ[2] ; } } } // Because of 4738524: setPixels should round the provided double // value instead of casting // If it is fixed, then this piece of code can be removed. if (dstType < DataBuffer.TYPE_FLOAT) roundValues(dstPixels); convertToSigned(dstPixels, dstType) ; dest.setPixels(dest.getMinX(), dest.getMinY(),width,height,dstPixels) ; } // convert a double type raster from RGB to CIEXYZ private static void RGBToCIEXYZDouble(UnpackedImageData src, int[] srcComponentSize, WritableRaster dest, int[] destComponentSize) { double[] rBuf = src.getDoubleData(0) ; double[] gBuf = src.getDoubleData(1) ; double[] bBuf = src.getDoubleData(2) ; // norms to map the output to the desired range double normx = 1.0, normy = 1.0, normz = 1.0 ; int dstType = dest.getSampleModel().getDataType() ; boolean isInt = (dstType < DataBuffer.TYPE_FLOAT) ; if (isInt) { normx = ((1L << destComponentSize[0]) - 1) / maxXYZ ; normy = ((1L << destComponentSize[1]) - 1) / maxXYZ; normz = ((1L << destComponentSize[2]) - 1) / maxXYZ ; } int height = dest.getHeight() ; int width = dest.getWidth() ; double[] dstPixels = new double[3 * height * width] ; int rStart = src.bandOffsets[0] ; int gStart = src.bandOffsets[1] ; int bStart = src.bandOffsets[2] ; int srcPixelStride = src.pixelStride; int srcLineStride = src.lineStride; float[] XYZ = new float[3]; float[] RGB = new float[3]; int dIndex = 0 ; for (int j = 0 ; jColorSpaceJAI object given the color space * type, the number of components, and an indicator of the preferred * intermediary or connection color space. * * @param type The color space type (ColorSpace.TYPE_*). * @param numComponents The number of color components. * @param isRGBPreferredIntermediary Whether sRGB (true) * or CIEXYZ (false) is the preferred connection * color space. * * @exception IllegalArgumentException if numComponents * is non-positive. */ protected ColorSpaceJAI(int type, int numComponents, boolean isRGBPreferredIntermediary) { super(type, numComponents); this.isRGBPreferredIntermediary = isRGBPreferredIntermediary; } /** * Whether sRGB is the preferred intermediary color space when converting * to another color space which is neither sRGB nor CIEXYZ. This * serves to indicate the more efficient conversion pathway. * * @return true if sRGB is preferred, or false * if CIEXYZ is preferred. */ public boolean isRGBPreferredIntermediary() { return this.isRGBPreferredIntermediary ; } /** * Transforms the pixel data in the source Raster from * CIEXYZ values with respect to the CIE D50 white point to the color * space represented by this class. If the destination * WritableRaster is null, a new * WritableRaster will be created. The * Rasters are treated as having no alpha channel, i.e., * all bands are color bands. * *

      If required by the underlying transformation, integral data will * be normalized according to the number of bits of the respective * component; floating point data should be between 0.0 and * 1.0 + (32767.0 / 32768.0). * All integral data are assumed to be unsigned; signed data should be * shifted by the caller before invoking this method. * * @param src the source Raster to be converted. * @param srcComponentSize array that specifies the number of significant * bits per source color component; ignored for floating point data. * If null defaults to the value returned by * src.getSampleModel().getSampleSize(). * @param dest the destination WritableRaster, * or null. * @param destComponentSize array that specifies the number of significant * bits per destination color component; ignored for floating point * data. If null, defaults to the value returned by * dest.getSampleModel().getSampleSize(), or the sample * size of the newly created destination WritableRaster if dest is * null. * @return dest color converted from src * or a new, WritableRaster containing the converted * pixels if dest is null. * @exception IllegalArgumentException if src is * null, the number of source or destination * bands does not equal the number of components of the * respective color space, or either component size array * is non-null and has length not equal to the number of * bands in the respective Raster. * */ public abstract WritableRaster fromCIEXYZ(Raster src, int[] srcComponentSize, WritableRaster dest, int[] destComponentSize); /** * Transforms the pixel data in the source Raster from * sRGB to the color space represented by this class. * If the destination WritableRaster is null, * a new WritableRaster will be created. The * Rasters are treated as having no alpha channel, i.e., * all bands are color bands. * *

      If required by the underlying transformation, integral data will * be normalized according to the number of bits of the respective * component; floating point data should be between 0.0 and 1.0. * All integral data are assumed to be unsigned; signed data should be * shifted by the caller before invoking this method. * * @param src the source Raster to be converted. * @param srcComponentSize array that specifies the number of significant * bits per source color component; ignored for floating point data. * If null defaults to the value returned by * src.getSampleModel().getSampleSize(). * @param dest the destination WritableRaster, * or null. * @param destComponentSize array that specifies the number of significant * bits per destination color component; ignored for floating point * data. If null, defaults to the value returned by * dest.getSampleModel().getSampleSize(), or the sample * size of the newly created destination WritableRaster if dest is * null. * @return dest color converted from src * or a new, WritableRaster containing the converted * pixels if dest is null. * @exception IllegalArgumentException if src is * null, the number of source or destination * bands does not equal the number of components of the * respective color space, or either component size array * is non-null and has length not equal to the number of * bands in the respective Raster. * */ public abstract WritableRaster fromRGB(Raster src, int[] srcComponentSize, WritableRaster dest, int[] destComponentSize); /** * Transforms the pixel data in the source Raster from * the color space represented by this class to CIEXYZ values with * respect to the CIE D50 white point. If the destination * WritableRaster is null, a new * WritableRaster will be created. The * Rasters are treated as having no alpha channel, i.e., * all bands are color bands. * *

      If required by the underlying transformation, integral data will * be normalized according to the number of bits of the respective * component; floating point data should be between 0.0 and 1.0. * All integral data are assumed to be unsigned; signed data should be * shifted by the caller before invoking this method. * * @param src the source Raster to be converted. * @param srcComponentSize array that specifies the number of significant * bits per source color component; ignored for floating point data. * If null defaults to the value returned by * src.getSampleModel().getSampleSize(). * @param dest the destination WritableRaster, * or null. * @param destComponentSize array that specifies the number of significant * bits per destination color component; ignored for floating point * data. If null, defaults to the value returned by * dest.getSampleModel().getSampleSize(), or the sample * size of the newly created destination WritableRaster if dest is * null. * @return dest color converted from src * or a new, WritableRaster containing the converted * pixels if dest is null. * @exception IllegalArgumentException if src is * null, the number of source or destination * bands does not equal the number of components of the * respective color space, or either component size array * is non-null and has length not equal to the number of * bands in the respective Raster. * */ public abstract WritableRaster toCIEXYZ(Raster src, int[] srcComponentSize, WritableRaster dest, int[] destComponentSize); /** * Transforms the pixel data in the source Raster from * the color space represented by this class to sRGB. * If the destination WritableRaster is null, * a new WritableRaster will be created. The * Rasters are treated as having no alpha channel, i.e., * all bands are color bands. * *

      If required by the underlying transformation, integral data will * be normalized according to the number of bits of the respective * component; floating point data should be between 0.0 and 1.0. * All integral data are assumed to be unsigned; signed data should be * shifted by the caller before invoking this method. * * @param src the source Raster to be converted. * @param srcComponentSize array that specifies the number of significant * bits per source color component; ignored for floating point data. * If null defaults to the value returned by * src.getSampleModel().getSampleSize(). * @param dest the destination WritableRaster, * or null. * @param destComponentSize array that specifies the number of significant * bits per destination color component; ignored for floating point * data. If null, defaults to the value returned by * dest.getSampleModel().getSampleSize(), or the sample * size of the newly created destination WritableRaster if dest is * null. * @return dest color converted from src * or a new, WritableRaster containing the converted * pixels if dest is null. * @exception IllegalArgumentException if src is * null, the number of source or destination * bands does not equal the number of components of the * respective color space, or either component size array * is non-null and has length not equal to the number of * bands in the respective Raster. * */ public abstract WritableRaster toRGB(Raster src, int[] srcComponentSize, WritableRaster dest, int[] destComponentSize); } jai-core-1.1.4/src/share/classes/javax/media/jai/UnpackedImageData.java0000644000175000017500000002100110203035544025461 0ustar mathieumathieu/* * $RCSfile: UnpackedImageData.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:23 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.Raster; /** * This class is used by PixelAccessor to store unpacked * image data and the information needed to access it. The data are stored in * a two-dimensional primitive array. * * @since JAI 1.1 * */ public final class UnpackedImageData { /** The Raster containing the pixel data. */ public final Raster raster; /** * The rectangular region within the Raster where the * data are to be accessed. */ public final Rectangle rect; /** The type of the primitive array used to store the data. */ public final int type; /** The data array supplied to store the unpacked data. */ public final Object data; /** * The number of array elements to skip to get to the next * pixel on the same scanline. */ public final int pixelStride; /** The number of array elements per scanline. */ public final int lineStride; /** * The number of array elements from the beginning of the data array * to the first pixel of the Rectangle for all bands. */ public final int[] bandOffsets; /** * Indicates whether the PixelAccessor can and must set the * data back into the Raster. If the data does not need * to be copied back to the Raster, this variable should be * set to false. Only destinations can be set. */ public final boolean convertToDest; /** Constructs a PackedImageRaster. * @param raster The Raster containing the pixel data. * @param rect The rectangular region containing the data. * @param type The type of the primitive array supplied to store the data. * @param data The data array supplied to store the data. * @param pixelStride The data array increment needed to move from band x * of pixel i to band x of pixel i+1 on the same scanline. * @param lineStride The data array increment to move from the pixel x * of line i to pixel x of line i+1. * @param bandOffsets The number of bytes from the start of the data array * to the location of the first pixel of the rectangle * for all bands. * @param convertToDest A boolean indicating whether the data can and * must be set back into the Raster. This applies * only to destinations. */ public UnpackedImageData(Raster raster, Rectangle rect, int type, Object data, int pixelStride, int lineStride, int[] bandOffsets, boolean convertToDest) { this.raster = raster; this.rect = rect; this.type = type; this.data = data; this.pixelStride = pixelStride; this.lineStride = lineStride; this.bandOffsets = bandOffsets; this.convertToDest = convertToDest; } /** * Returns the two-dimensional byte data array, or null * if the data are not stored in a byte array. * The array format is data[band][] where the second * index is navigated using the pixel and line strides. * @return The two-dimensional byte data array. */ public byte[][] getByteData() { return type == DataBuffer.TYPE_BYTE ? (byte[][])data : null; } /** * Returns byte data array for a specific band, or null * if the data are not stored in a byte array. * @param b The band whose data array is to be retrieved. * @return The one-dimensional byte data array for the requested band. */ public byte[] getByteData(int b) { byte[][] d = getByteData(); return d == null ? null : d[b]; } /** * Returns the two-dimensional short data array, or null * if the data are not stored in a short array. * The array format is data[band][] where the second * index is navigated using the pixel and line strides. * @return The two-dimensional short data array. */ public short[][] getShortData() { return (type == DataBuffer.TYPE_USHORT || type == DataBuffer.TYPE_SHORT) ? (short[][])data : null; } /** * Returns short data array for a specific band, or null * if the data are not stored in a short array. * @param b The band whose data array is to be retrieved. * @return The one-dimensional short data array for the requested band. */ public short[] getShortData(int b) { short[][] d = getShortData(); return d == null ? null : d[b]; } /** * Returns the two-dimensional integer data array, or null * if the data are not stored in an integer array. * The array format is data[band][] where the second * index is navigated using the pixel and line strides. * @return The two-dimensional int data array. */ public int[][] getIntData() { return type == DataBuffer.TYPE_INT ? (int[][])data : null; } /** * Returns integer data array for a specific band, or null * if the data are not stored in an integer array. * @param b The band whose data array is to be retrieved. * @return The one-dimensional int data array for the requested band. */ public int[] getIntData(int b) { int[][] d = getIntData(); return d == null ? null : d[b]; } /** * Returns the two-dimensional float data array, or null * if the data are not stored in a float array. * The array format is data[band][] where the second * index is navigated using the pixel and line strides. * @return The two-dimensional float data array. */ public float[][] getFloatData() { return type == DataBuffer.TYPE_FLOAT ? (float[][])data : null; } /** * Returns float data array for a specific band, or null * if the data are not stored in a float array. * @param b The band whose data array is to be retrieved. * @return The one-dimensional float data array for the requested band. */ public float[] getFloatData(int b) { float[][] d = getFloatData(); return d == null ? null : d[b]; } /** * Returns the two-dimensional double data array, or null * if the data are not stored in a double array. * The array format is data[band][] where the second * index is navigated using the pixel and line strides. * @return The two-dimensional double data array. */ public double[][] getDoubleData() { return type == DataBuffer.TYPE_DOUBLE ? (double[][])data : null; } /** * Returns double data array for a specific band, or null * if the data are not stored in a double array. * @param b The band whose data array is to be retrieved. * @return The one-dimensional double data array for the requested band. */ public double[] getDoubleData(int b) { double[][] d = getDoubleData(); return d == null ? null : d[b]; } /** Returns the offset for a band. * @param b The band whose offset is to be returned. * @return The offset of the requested band. */ public int getOffset(int b) { return bandOffsets[b]; } /** Returns the minimum offset of all bands. * @return The minimum offset of all bands. */ public int getMinOffset() { int min = bandOffsets[0]; for (int i = 1; i < bandOffsets.length; i++) { min = Math.min(min, bandOffsets[i]); } return min; } /** Returns the maximum offset of all bands. * @return The maximum offset of all the bands. */ public int getMaxOffset() { int max = bandOffsets[0]; for (int i = 1; i < bandOffsets.length; i++) { max = Math.max(max, bandOffsets[i]); } return max; } } jai-core-1.1.4/src/share/classes/javax/media/jai/OperationNodeSupport.java0000644000175000017500000007611410203035544026354 0ustar mathieumathieu/* * $RCSfile: OperationNodeSupport.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:13 $ * $State: Exp $ */ package javax.media.jai; import java.awt.RenderingHints; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Hashtable; import java.util.Iterator; import java.util.Observable; import java.util.Observer; import java.util.Vector; import javax.media.jai.remote.SerializableState; import javax.media.jai.remote.SerializerFactory; /** * This is a utility class that can be used by OperationNodes * to consolidate common functionality. An instance of this class may be * used as a member field of the OperationNode and some of the * OperationNode's work delegated to it. * * @since JAI 1.1 */ public class OperationNodeSupport implements Serializable { // Constants supporting compare(). private static final int PB_EQUAL = 0x0; private static final int PB_SOURCES_DIFFER = 0x1; private static final int PB_PARAMETERS_DIFFER = 0x2; private static final int PB_DIFFER = PB_SOURCES_DIFFER | PB_PARAMETERS_DIFFER; // The OperationRegistryMode. private String registryModeName; // Critical attributes. private String opName; private transient OperationRegistry registry; private transient ParameterBlock pb; private transient RenderingHints hints; // Event helper. private PropertyChangeSupportJAI eventManager; /** * This instance variable is lazily constructed only when one of the * PropertySource methods or one of the local property environment * mutators is accessed. PropertyEnvironment is a package scope class. */ private transient PropertyEnvironment propertySource = null; /** * Stores local property environment modifications sequentially as * a PropertyGenerator, a String, or a CopyDirective depending * on which local property environment mutator method was invoked. */ private Vector localPropEnv = new Vector(); /** * Map of ParamObservers of instances of * DeferredData in the parameter Vector. */ private Hashtable paramObservers = new Hashtable(); /** * Compare the contents of two ParameterBlocks. */ private static int compare(ParameterBlock pb1, ParameterBlock pb2) { if(pb1 == null && pb2 == null) { return PB_EQUAL; } if((pb1 == null && pb2 != null) || (pb1 != null && pb2 == null)) { return PB_DIFFER; } int result = PB_EQUAL; if(!equals(pb1.getSources(), pb2.getSources())) { result |= PB_SOURCES_DIFFER; } if(!equals(pb1.getParameters(), pb2.getParameters())) { result |= PB_PARAMETERS_DIFFER; } return result; } private static boolean equals(ParameterBlock pb1, ParameterBlock pb2) { return pb1 == null ? pb2 == null : equals(pb1.getSources(), pb2.getSources()) && equals(pb1.getParameters(), pb2.getParameters()); } private static boolean equals(Object o1, Object o2) { return o1 == null ? o2 == null : o1.equals(o2); } /** * Constructs an OperationNodeSupport instance. * All parameters except opName may be null. * If non-null the PropertyChangeSupportJAI * should have been created with the node as its event source (note * that this cannot be verified). * *

      The ParameterBlock may include * DeferredData parameters. These will not be evaluated * until their values are actually required, i.e., when the node is * rendered. Any Observable events generated by such * DeferredData parameters will be trapped by the node, * converted to a PropertyChangeEventJAI named "parameters", * and forwarded to any listeners registered with the supplied. * eventManager. The old and new values of the event object * so generated will be the previous and current values, respectively, of * the data object wrapped by the DeferredData parameter, * and thus will be instances of the class returned by the * getDataClass() method of the DeferredData * parameter. * * @param registryModeName The name of the registry mode concerned. * @param opName The operation name to set. * @param registry The OperationRegistry to set; * it may be null in which case the registry * will be set to the default JAI registry. * @param pb The ParameterBlock to set; * it may be null. * @param hints The new RenderingHints to be set; * it may be null. * @param eventManager The event helper object. The property change * event source of this object should be the * OperationNode which is using the constructed * OperationNodeSupport instance. If null * no events will be fired. * * @throws IllegalArgumentException if registryModeName * or opName is null. */ public OperationNodeSupport(String registryModeName, String opName, OperationRegistry registry, ParameterBlock pb, RenderingHints hints, PropertyChangeSupportJAI eventManager) { if(registryModeName == null || opName == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } // Set instance variables. this.registryModeName = registryModeName; this.opName = opName; if (registry == null) this.registry = JAI.getDefaultInstance().getOperationRegistry(); else this.registry = registry; this.pb = pb; this.hints = hints; this.eventManager = eventManager; // Set any DeferredData Observers. if(pb != null) { updateObserverMap(pb.getParameters()); } } /** * Class representing a copy-from-source directive set via * copyPropertyFromSource(). */ private class CopyDirective implements Serializable { /** The name of the property. */ private String name; /** The index of the source from which to copy the property. */ private int index; /** * Constructor. * * @param name The name of the property. * @param index The index of the source from which to copy the * property. */ CopyDirective(String name, int index) { if(name == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.name = name; this.index = index; } String getName() { return name; } int getIndex() { return index; } } /** * Class which is an Observer of a DeferredData * parameter. */ private class ParamObserver implements Observer { /** The index of the associated parameter. */ final int paramIndex; /** The DeferredData object to observe. */ final DeferredData dd; /** * Constructor. * * @param paramIndex The index of the associated parameter. * @param dd The DeferredData object to observe. */ ParamObserver(int paramIndex, DeferredData dd) { if(dd == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } else if(paramIndex < 0 || (pb != null && (paramIndex >= ((ParameterBlock)pb).getNumParameters()))) { throw new ArrayIndexOutOfBoundsException(); } this.paramIndex = paramIndex; this.dd = dd; // Add this object as an Observer of the Deferred Data. dd.addObserver(this); } /** * Implementation of Observer. An update from the * observed DeferredData causes an event to be fired * if the DeferredData had been previously evaluated * and there are event listeners. */ public synchronized void update(Observable o, Object arg) { if(!(o == dd)) { return; } // Do nothing unless the DeferredData was already evaluated. if(arg != null && eventManager != null) { Vector params = pb.getParameters(); Vector oldParams = (Vector)params.clone(); Vector newParams = (Vector)params.clone(); oldParams.set(paramIndex, arg); newParams.set(paramIndex, dd.getData()); fireEvent("Parameters", oldParams, newParams); } } } /** * Updates the Map of Observers of * DeferredData instances in the parameter * Vector. */ private void updateObserverMap(Vector parameters) { if(parameters == null) { return; } int numParameters = parameters.size(); for(int i = 0; i < numParameters; i++) { Object parameter = parameters.get(i); Integer index = new Integer(i); // Replace or remove ParamObserver as needed. Object oldObs; if(parameter instanceof DeferredData) { Observer obs = new ParamObserver(i, (DeferredData)parameter); oldObs = paramObservers.put(index, obs); } else { oldObs = paramObservers.remove(index); } // Unregister Observer from the associated DeferredData. if(oldObs != null) { ParamObserver obs = (ParamObserver)oldObs; obs.dd.deleteObserver(obs); } } } /** * Returns the name of RegistryMode corresponding to * this OperationNode. This value shoud be immutable * for a given node. The value is returned by reference. */ public String getRegistryModeName() { return registryModeName; } /** * Returns the name of the operation the associated node represents. * The value is returned by reference. */ public String getOperationName() { return opName; } /** * Sets the name of the operation the associated node represents. * The value is set by reference. * *

      If the operation name changes as a result of calling this * method according to a case-insensitive * comparison by equals() of the old and new names, * a PropertyChangeEventJAI named "OperationName" * will be fired by the event helper object with old and new values * set to the old and new values of the operation name, respectively. * * @param opName The new operation name to be set. * * @throws IllegalArgumentException if opName is * null. */ public void setOperationName(String opName) { if(opName == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if(opName.equalsIgnoreCase(this.opName)) return; String oldOpName = this.opName; this.opName = opName; fireEvent("OperationName", oldOpName, opName); resetPropertyEnvironment(false); } /** * Returns the OperationRegistry used by the associated * node. The value is returned by reference. */ public OperationRegistry getRegistry() { return registry; } /** * Sets the OperationRegistry that is used by the associated * node. If the specified registry is null, the * registry will be set to the default JAI registry. The value is * set by reference. * *

      If the registry changes according to a direct comparison * of the old and new registry references, * a PropertyChangeEventJAI named "OperationRegistry" * will be fired by the event helper object with old and new values * set to the old and new values of the registry, respectively. * * @param registry The new OperationRegistry to be set; * it may be null. */ public void setRegistry(OperationRegistry registry) { if(registry == null) { registry = JAI.getDefaultInstance().getOperationRegistry(); } if(registry != this.registry) { OperationRegistry oldRegistry = this.registry; this.registry = registry; fireEvent("OperationRegistry", oldRegistry, registry); resetPropertyEnvironment(false); } } /** * Returns the ParameterBlock of the associated node * by reference. Nodes desirous of maintaining a consistent state * for their ParameterBlock may prefer to clone the * value returned by this method. */ public ParameterBlock getParameterBlock() { return pb; } /** * Sets the ParameterBlock of the associated node by * reference. If the specified ParameterBlock is * null, it is assumed that the associated node has * neither input sources nor parameters. Nodes desirous of maintaining * a consistent state for their ParameterBlock may prefer * to clone any user-supplied ParameterBlock before passing * it to this method. * *

      This method does not validate the content of the supplied * ParameterBlock. The caller should ensure that * the sources and parameters in the ParameterBlock * are suitable for the operation the associated node represents; otherwise * some form of error or exception may occur at the time of rendering. * *

      If the ParameterBlock changes according to a * comparison of the sources and parameters Vectors of the * old and new ParameterBlocks using equals(), * a PropertyChangeEventJAI named "ParameterBlock" * will be fired by the event helper object with old and new values * set to the old and new values of the ParameterBlock, * respectively. A PropertyChangeEventJAI named "Sources" or * "Parameters" will instead be fired if it can be determined that the * ParameterBlock modification has affected only the sources * or parameters of the node, respectively. * *

      The ParameterBlock may include * DeferredData parameters. These will not be evaluated * until their values are actually required, i.e., when the node is * rendered. Any Observable events generated by such * DeferredData parameters will be trapped by the node, * converted to a PropertyChangeEventJAI named "parameters", * and forwarded to any listeners registered with the supplied. * eventManager. The old and new values of the event object * so generated will be the previous and current values, respectively, of * the data object wrapped by the DeferredData parameter, * and thus will be instances of the class returned by the * getDataClass() method of the DeferredData * parameter. * * @param pb The new ParameterBlock to be set; * it may be null. */ public void setParameterBlock(ParameterBlock pb) { int comparison = compare(this.pb, pb); if(comparison == PB_EQUAL) { return; } ParameterBlock oldPB = this.pb; this.pb = pb; // Set any DeferredData Observers. if(pb != null) { updateObserverMap(pb.getParameters()); } if(comparison == PB_SOURCES_DIFFER) { // Sources have changed. fireEvent("Sources", oldPB.getSources(), pb.getSources()); } else if(comparison == PB_PARAMETERS_DIFFER) { // Parameters have changed. fireEvent("Parameters", oldPB.getParameters(), pb.getParameters()); } else { // Sources and parameters have changed. fireEvent("ParameterBlock", oldPB, pb); } resetPropertyEnvironment(false); } /** * Returns the RenderingHints of the associated node * by reference. Nodes desirous of maintaining a consistent state * for their RenderingHints may prefer to clone the * value returned by this method. */ public RenderingHints getRenderingHints() { return hints; } /** * Sets the RenderingHints of the associated node. It is * legal for nodes to ignore RenderingHints set on them by * this mechanism. Nodes desirous of maintaining * a consistent state for their RenderingHints may prefer * to clone any user-supplied RenderingHints before passing * it to this method. * *

      If the RenderingHints changes according to a * comparison by equals() of the old and new hints, * a PropertyChangeEventJAI named "RenderingHints" * will be fired by the event helper object with old and new values * set to the old and new values of the RenderingHints, * respectively. * * @param hints The new RenderingHints to be set; * it may be null. */ public void setRenderingHints(RenderingHints hints) { if(equals(this.hints, hints)) { return; } RenderingHints oldHints = this.hints; this.hints = hints; fireEvent("RenderingHints", oldHints, hints); resetPropertyEnvironment(false); } /** * Adds a PropertyGenerator to the node. The property values * emitted by this property generator override any previous definitions. * * @param pg A PropertyGenerator to be added to the * associated node's property environment. * * @throws IllegalArgumentException if * pg is null. */ public void addPropertyGenerator(PropertyGenerator pg) { if(pg == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } localPropEnv.add(pg); if(propertySource != null) { propertySource.addPropertyGenerator(pg); } } /** * Forces a property to be copied from the specified source node. * By default, a property is copied from the first source node that * that emits it. The result of specifying an invalid source is * undefined. * * @param propertyName the name of the property to be copied. * @param sourceIndex the index of the source to copy the property from. * @throws IllegalArgumentException if propertyName is null. */ public void copyPropertyFromSource(String propertyName, int sourceIndex) { if(propertyName == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } localPropEnv.add(new CopyDirective(propertyName, sourceIndex)); if(propertySource != null) { propertySource.copyPropertyFromSource(propertyName, sourceIndex); } } /** * Removes a named property from the property environment of the * associated node. Unless the property is stored locally either due * to having been set explicitly or to having been cached for property * synchronization purposes, subsequent calls to * getProperty(name) will return * java.awt.Image.UndefinedProperty, and name * will not appear on the list of properties emitted by * getPropertyNames(). * * @param name A String naming the property to be suppressed. * * @throws IllegalArgumentException if * name is null. */ public void suppressProperty(String name) { if(name == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } localPropEnv.add(name); if(propertySource != null) { propertySource.suppressProperty(name); } } /** * Constructs and returns a PropertySource suitable for * use by the specified OperationNode. If the registry mode * identified by getRegistryModeName() supports properties, * i.e., the statement *

           * Registry.getMode(getRegistryModeName()).arePropertiesSupported()
           * 
      * evaluates to true, then the PropertySource * will include the global property environment as managed by the * OperationRegistry for the corresponding operation. * Prior and subsequent modifications to the local property environment * made via this object will be reflected in the returned * PropertySource. * * @param opNode the OperationNode requesting its * PropertySource. * @param defaultPS a PropertySource to be used to derive * property values if and only if they would otherwise be * derived by inheritance from a source rather than from a * a PropertyGenerator or a copy-from-source * directive. * * @throws IllegalArgumentException if opNode is null. * * @return A PropertySource including the local and, if * applicable, the global property environment for the operation. * * @see RegistryMode * @see OperationRegistry#getPropertySource(OperationNode op) */ public PropertySource getPropertySource(OperationNode opNode, PropertySource defaultPS) { if ( opNode == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if(propertySource == null) { synchronized(this) { RegistryMode regMode = RegistryMode.getMode(registryModeName); if(regMode != null && regMode.arePropertiesSupported()) { // Get the global property environment. propertySource = (PropertyEnvironment)registry.getPropertySource(opNode); } else { // This mode does not support properties so we create // a default environment to permit property inheritance // from the sources. The PropertyGenerators, // copy-from-source directives, and suppressed properties // are null. propertySource = new PropertyEnvironment(pb != null ? pb.getSources() : null, null, null, null, opNode); } // Update from the local environment. updatePropertyEnvironment(propertySource); } } // Add the specified default source. propertySource.setDefaultPropertySource(defaultPS); return propertySource; } /** * Resets the property environment. The list of local property * environment modifications made directly on this object is reset * if and only if the parameter is true. * * @param resetLocalEnvironment Whether to clear the list of property * environment changes made directly on this object. */ public void resetPropertyEnvironment(boolean resetLocalEnvironment) { propertySource = null; if(resetLocalEnvironment) { localPropEnv.clear(); } } // Add items from local environment cache. private void updatePropertyEnvironment(PropertyEnvironment pe) { if(pe != null) { // "pe" should never null but check anyway. synchronized(this) { // Add items from the local environment. int size = localPropEnv.size(); for(int i = 0; i < size; i++) { Object element = localPropEnv.get(i); if(element instanceof String) { // suppressed property pe.suppressProperty((String)element); } else if(element instanceof CopyDirective) { CopyDirective cd = (CopyDirective)element; pe.copyPropertyFromSource(cd.getName(), cd.getIndex()); } else if(element instanceof PropertyGenerator) { pe.addPropertyGenerator((PropertyGenerator)element); } } } } } private void fireEvent(String propName, Object oldVal, Object newVal) { if(eventManager != null) { Object eventSource = eventManager.getPropertyChangeEventSource(); PropertyChangeEventJAI evt = new PropertyChangeEventJAI(eventSource, propName, oldVal, newVal); eventManager.firePropertyChange(evt); } } // Note that at present in RenderedOp and RenderableOp the only // non-serializable classes handled are RenderedImage, Raster, and // RenderingHints. How should this best be handled? Should an OpNode // be forced to implement for example // // void writePB(ParameterBlock pb, ObjectOutputStream out) // void ParameterBlock readPB(ObjectInputStream in) // // perhaps in a SerializableOperationNode? // Or does this require a more generic approach using Proxy? /** * Serializes the OperationNodeSupport. */ private void writeObject(ObjectOutputStream out) throws IOException { ParameterBlock pbClone = pb; boolean pbCloned = false; // Wrap RenderedImage sources in RenderedImageStates. for (int index = 0; index < pbClone.getNumSources(); index++) { Object source = pbClone.getSource(index); if (source != null && !(source instanceof Serializable)) { if (!pbCloned) { pbClone = (ParameterBlock)pb.clone(); pbCloned = true; } if (source instanceof RenderedImage) { SerializableState serializableImage = SerializerFactory.getState(source, null); pbClone.setSource(serializableImage, index); } else { throw new RuntimeException(source.getClass().getName() + JaiI18N.getString("OperationNodeSupport0")); } } } // Wrap RenderedImage parameters in RenderedImageState objects; // wrap Raster parameters in RasterState objects; // check other parameters for serializability. for (int index = 0; index < pbClone.getNumParameters(); index++) { Object parameter = pbClone.getObjectParameter(index); if (parameter != null && !(parameter instanceof Serializable)) { if (!pbCloned) { pbClone = (ParameterBlock)pb.clone(); pbCloned = true; } if (parameter instanceof Raster) { pbClone.set(SerializerFactory.getState(parameter, null), index); } else if (parameter instanceof RenderedImage) { RenderedImage ri = (RenderedImage)parameter; RenderingHints hints = new RenderingHints(null); hints.put(JAI.KEY_SERIALIZE_DEEP_COPY, new Boolean(true)); pbClone.set(SerializerFactory.getState(ri, hints), index); } else { throw new RuntimeException(parameter.getClass().getName() + JaiI18N.getString("OperationNodeSupport1")); } } } // Serialize the object. // Write non-static and non-transient fields. out.defaultWriteObject(); // Write ParameterBlock. out.writeObject(pbClone); // Write RenderingHints. out.writeObject(SerializerFactory.getState(hints, null)); } /** * Deserializes the OperationNodeSupport. */ private synchronized void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { // Read non-static and non-transient fields. in.defaultReadObject(); // Read ParameterBlock. pb = (ParameterBlock)in.readObject(); // Read RenderingHints. SerializableState ss = (SerializableState)in.readObject(); hints = (RenderingHints)ss.getObject(); // Wrap any RenderedImageState sources in PlanarImage objects. for (int index = 0; index < pb.getNumSources(); index++) { Object source = pb.getSource(index); if (source instanceof SerializableState) { ss = (SerializableState)source; PlanarImage pi = PlanarImage.wrapRenderedImage((RenderedImage)ss.getObject()); pb.setSource(pi, index); } } // Extract Raster and PlanarImage parameters from RasterState and // RenderedImageState wrappers, respectively. for (int index = 0; index < pb.getNumParameters(); index++) { Object parameter = pb.getObjectParameter(index); if (parameter instanceof SerializableState) { Object object = ((SerializableState)parameter).getObject(); if (object instanceof Raster) pb.set(object, index); else if (object instanceof RenderedImage) pb.set(PlanarImage.wrapRenderedImage((RenderedImage)object), index); else pb.set(object, index); } } registry = JAI.getDefaultInstance().getOperationRegistry(); } } jai-core-1.1.4/src/share/classes/javax/media/jai/OperationNode.java0000644000175000017500000002401210203035544024745 0ustar mathieumathieu/* * $RCSfile: OperationNode.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:13 $ * $State: Exp $ */ package javax.media.jai; import java.awt.RenderingHints; import java.awt.image.renderable.ParameterBlock; /** * A class which is a node in a chain of operations. This interface * aggregates the minimal set of methods which would be expected to be * implemented by such a class. * *

      Accessors and mutators of the critical attributes of the node * are provided: * *

        *
      • The name of the operation as a String; *
      • The OperationRegistry to be used to resolve the * operation name into an image factory which renders the node; *
      • The lists of sources and parameters of the node as a * ParameterBlock; and *
      • The mapping of hints to be used when rendering the node. *
      * * Whether an implementing class maintains these critical attributes by * reference or by copying or cloning is left to the discretion of the * implementation. * *

      OperationNodes should fire a * PropertyChangeEventJAI when any of the critical attributes of * the node is modified. These events should be named "OperationName", * "OperationRegistry", "ParameterBlock", and "RenderingHints" corresponding * to the respective critical attributes. Events named "Sources" and * "Parameters" may instead be fired if it can be determined that a * ParameterBlock modification has affected only the sources * or parameters of the node, respectively. Nodes which implement convenience * methods to edit individual node sources, parameters, or hints should * still fire an event for the attribute as a whole. Note that this might * require cloning the respective object. OperationNodes are * also required to fire PropertySourceChangeEvents by virtue * of their being a PropertySource as well as a * PropertyChangeEmitter. * *

      Methods are also provided to modify the local property environment * of the node. The global property environment is maintained by the * associated OperationRegistry and used to initialize the * local property environment. Methods are provided to: * *

        *
      • Add a PropertyGenerator; *
      • Direct that a given property be copied from a certain source; and *
      • Suppress the emission of a certain property. *
      * * Invocation of these methods would not affect the global property * environment of the operation as maintained by the * OperationRegistry. * * @since JAI 1.1 */ public interface OperationNode extends PropertySource, PropertyChangeEmitter { /** * Returns the name of the RegistryMode corresponding to * this OperationNode. This value should be immutable * for a given node. */ String getRegistryModeName(); /** * Returns the name of the operation this node represents as * a String. */ String getOperationName(); /** * Sets the name of the operation this node represents. * *

      If the operation name changes according to a case-insensitive * comparison by equals() of the old and new names, * a PropertyChangeEventJAI named "OperationName" * should be fired with * source equal to this node and old and new values set to the old * and new values of the operation name, respectively. * * @param opName The new operation name to be set. * * @throws IllegalArgumentException if opName is * null. */ void setOperationName(String opName); /** * Returns the OperationRegistry that is used * by this node. If the registry is not set, the default * registry is returned. */ OperationRegistry getRegistry(); /** * Sets the OperationRegistry that is used by * this node. If the specified registry is null, the * default registry is used. * *

      If the registry changes according to a direct comparison * of the old and new registry references, * a PropertyChangeEventJAI named "OperationRegistry" * should be fired with * source equal to this node and old and new values set to the old * and new values of the registry, respectively. * * @param registry The new OperationRegistry to be set; * it may be null. */ void setRegistry(OperationRegistry registry); /** * Returns the ParameterBlock of this node. */ ParameterBlock getParameterBlock(); /** * Sets the ParameterBlock of this node. If the specified * new ParameterBlock is null, it is assumed * that this node has no input sources and parameters. * *

      This method does not validate the content of the supplied * ParameterBlock. The caller should ensure that * the sources and parameters in the ParameterBlock * are suitable for the operation this node represents; otherwise * some form of error or exception may occur at the time of rendering. * *

      If the ParameterBlock changes according to a * comparison of the sources and parameters Vectors of the * old and new ParameterBlocks using equals(), * a PropertyChangeEventJAI named "ParameterBlock" * should be fired with * source equal to this node and old and new values set to the old * and new values of the ParameterBlock, respectively. * A PropertyChangeEventJAI named "Sources" or * "Parameters" may instead be fired if it can be determined that the * ParameterBlock modification has affected only the sources * or parameters of the node, respectively. * *

      The ParameterBlock may include * DeferredData parameters. These will not be evaluated * until their values are actually required, i.e., when the node is * rendered. Any Observable events generated by such * DeferredData parameters will be trapped by the node * and acted upon. * * @param pb The new ParameterBlock to be set; * it may be null. */ void setParameterBlock(ParameterBlock pb); /** * Returns the RenderingHints of this node. * It may be null. */ RenderingHints getRenderingHints(); /** * Sets the RenderingHints of this node. It is legal * for nodes to ignore RenderingHints set on them by * this mechanism. * *

      If the RenderingHints changes according to a * comparison by equals() of the old and new hints, * a PropertyChangeEventJAI named "RenderingHints" * should be fired with * source equal to this node and old and new values set to the old * and new values of the hints, respectively. * * @param hints The new RenderingHints to be set; * it may be null. */ void setRenderingHints(RenderingHints hints); /** * Returns the property associated with the specified property name, * or java.awt.Image.UndefinedProperty if the specified * property is not set on the image. This method is dynamic in the * sense that subsequent invocations of this method on the same object * may return different values as a function of changes in the property * environment of the node, e.g., a change in which * PropertyGenerators are registered or in the values * associated with properties of node sources. The case of the property * name passed to this method is ignored. * * @param name A String naming the property. * * @throws IllegalArgumentException if * name is null. */ Object getDynamicProperty(String name); /** * Adds a PropertyGenerator to the node. The property values * emitted by this property generator override any previous * definitions. * * @param pg A PropertyGenerator to be added to this node's * property environment. * * @throws IllegalArgumentException if * pg is null. */ void addPropertyGenerator(PropertyGenerator pg); /** * Forces a property to be copied from the specified source node. * By default, a property is copied from the first source node * that emits it. The result of specifying an invalid source is * undefined. * * @param propertyName the name of the property to be copied. * @param sourceIndex the index of the from which to copy the property. * @throws IllegalArgumentException if propertyName is * null. */ void copyPropertyFromSource(String propertyName, int sourceIndex); /** * Removes a named property from the property environment of this * node. Unless the property is stored locally either due * to having been set explicitly or to having been cached for property * synchronization purposes, subsequent calls to * getProperty(name) will return * java.awt.Image.UndefinedProperty, and name * will not appear on the list of properties emitted by * getPropertyNames(). * * @param name A String naming the property to be suppressed. * * @throws IllegalArgumentException if * name is null. */ void suppressProperty(String name); } jai-core-1.1.4/src/share/classes/javax/media/jai/PropertySourceChangeEvent.java0000644000175000017500000000415110203035544027316 0ustar mathieumathieu/* * $RCSfile: PropertySourceChangeEvent.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:17 $ * $State: Exp $ */ package javax.media.jai; /** * A class instances of which represent JAI properties as emitted for * example by a PropertySource but in the guise of an event * as defined for Java Beans. This class definition adds no functionality * to that provided by the superclass per se. The significance of the * derivation is that instances of this event by definition refer to properties * in the JAI sense of the term. Otherwise put, this class provides an extra * level of indirection. * * @see PropertyChangeEventJAI * @see PropertySource * * @since JAI 1.1 */ public class PropertySourceChangeEvent extends PropertyChangeEventJAI { /** * Constructs a PropertySourceChangeEvent. * propertyName is forced to lower case; all other * parameters are passed unmodified to the superclass constructor. * If oldValue or newValue is to indicate * a property for which no value is defined, then the object * java.awt.Image.UndefinedProperty should be passed. * * @exception NullPointerException if propertyName is * null. * @exception IllegalArgumentException if source, * oldValue or newValue is * null. */ public PropertySourceChangeEvent(Object source, String propertyName, Object oldValue, Object newValue) { super(source, propertyName, oldValue, newValue); // Note: source and propertyName are checked for null in superclass. if(oldValue == null) { throw new IllegalArgumentException(JaiI18N.getString("PropertySourceChangeEvent0")); } else if(newValue == null) { throw new IllegalArgumentException(JaiI18N.getString("PropertySourceChangeEvent1")); } } } jai-core-1.1.4/src/share/classes/javax/media/jai/WarpCubic.java0000644000175000017500000002347010203035544024065 0ustar mathieumathieu/* * $RCSfile: WarpCubic.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:23 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Rectangle; import java.awt.geom.Point2D; /** * A cubic-based description of an image warp. * *

      The source position (x', y') of a point (x, y) is given by the * cubic polynomial: * *

       * x' = p(x, y) = c1 + c2*x + c3*y + c4*x^2 + c5*x*y + c6*y^2 +
       *                c7*x^3 + c8*x^2*y + c9*x*y^2 + c10*y^3
       * y' = q(x, y) = c11 + c12*x + c13*y + c14*x^2 + c15*x*y + c16*y^2 +
       *                c17*x^3 + c18*x^2*y + c19*x*y^2 + c20*y^3
       * 
      * *

      WarpCubic is marked final so that it may be * more easily inlined. * * @see WarpPolynomial * */ public final class WarpCubic extends WarpPolynomial { private float c1, c2, c3, c4, c5, c6, c7, c8, c9, c10; private float c11, c12, c13, c14, c15, c16, c17, c18, c19, c20; /** * Constructs a WarpCubic with a given transform mapping * destination pixels into source space. Note that this is * a backward mapping as opposed to the forward mapping used in * AffineOpImage. The coeffs arrays must each contain 10 floats * corresponding to the coefficients c1, c2, etc. as shown in the * class comment. * * @param xCoeffs The 10 destination to source transform coefficients for * the X coordinate. * @param yCoeffs The 10 destination to source transform coefficients for * the Y coordinate. * @param preScaleX The scale factor to apply to input (dest) X positions. * @param preScaleY The scale factor to apply to input (dest) Y positions. * @param postScaleX The scale factor to apply to the result of the X polynomial evaluation * @param postScaleY The scale factor to apply to the result of the Y polynomial evaluation * @throws IllegalArgumentException if the length of the xCoeffs and yCoeffs arrays are not both 10. */ public WarpCubic(float[] xCoeffs, float[] yCoeffs, float preScaleX, float preScaleY, float postScaleX, float postScaleY) { super(xCoeffs, yCoeffs, preScaleX, preScaleY, postScaleX, postScaleY); if (xCoeffs.length != 10 || yCoeffs.length != 10) { throw new IllegalArgumentException(JaiI18N.getString("WarpCubic0")); } c1 = xCoeffs[0]; // x coefficients c2 = xCoeffs[1]; c3 = xCoeffs[2]; c4 = xCoeffs[3]; c5 = xCoeffs[4]; c6 = xCoeffs[5]; c7 = xCoeffs[6]; c8 = xCoeffs[7]; c9 = xCoeffs[8]; c10 = xCoeffs[9]; c11 = yCoeffs[0]; // y coefficients c12 = yCoeffs[1]; c13 = yCoeffs[2]; c14 = yCoeffs[3]; c15 = yCoeffs[4]; c16 = yCoeffs[5]; c17 = yCoeffs[6]; c18 = yCoeffs[7]; c19 = yCoeffs[8]; c20 = yCoeffs[9]; } /** * Constructs a WarpCubic with pre- and post-scale * factors of 1. * * @param xCoeffs The 10 destination to source transform coefficients for * the X coordinate. * @param yCoeffs The 10 destination to source transform coefficients for * the Y coordinate. * @throws IllegalArgumentException if the length of the xCoeffs and yCoeffs arrays are not both 10. */ public WarpCubic(float[] xCoeffs, float[] yCoeffs) { this(xCoeffs, yCoeffs, 1.0F, 1.0F, 1.0F, 1.0F); } /** * Computes the source subpixel positions for a given rectangular * destination region, subsampled with an integral period. The * destination region is specified using normal integral (full * pixel) coordinates. The source positions returned by the * method are specified in floating point. * * @param x The minimum X coordinate of the destination region. * @param y The minimum Y coordinate of the destination region. * @param width The width of the destination region. * @param height The height of the destination region. * @param periodX The horizontal sampling period. * @param periodY The vertical sampling period. * * @param destRect A float array containing at least * 2*((width+periodX-1)/periodX)* * ((height+periodY-1)/periodY) * elements, or null. If null, a * new array will be constructed. * * @return A reference to the destRect parameter if * it is non-null, or a new * float array otherwise. * @throws ArrayBoundsException if destRect is too small */ public float[] warpSparseRect(int x, int y, int width, int height, int periodX, int periodY, float[] destRect) { // XXX: This method should do its calculations in doubles if (destRect == null) { destRect = new float[((width + periodX - 1) / periodX) * ((height + periodY - 1) / periodY) * 2]; } // // x' = c1 + c2*x + c3*y + c4*x^2 + c5*x*y + c6*y^2 + // c7*x^3 + c8*x^2*y + c9*x*y^2 + c10*y^3 // y' = c11 + c12*x + c13*y + c14*x^2 + c15*x*y + c16*y^2 + // c17*x^3 + c18*x^2*y + c19*x*y^2 + c20*y^3 // float px1 = periodX * preScaleX; // power for periodX float px2 = px1 * px1; float px3 = px2 * px1; // Delta delta delta x and delta delta delta y float dddx = c7 * 6 * px3; float dddy = c17 * 6 * px3; float x1 = (x + 0.5F) * preScaleX; // power for x float x2 = x1 * x1; float x3 = x2 * x1; width += x; height += y; int index = 0; for (int j = y; j < height; j += periodY) { float y1 = (j + 0.5F) * preScaleY; // power for the current y float y2 = y1 * y1; float y3 = y2 * y1; // The warped position for the first point of the current line float wx = c1 + c2 * x1 + c3 * y1 + c4 * x2 + c5 * x1 * y1 + c6 * y2 + c7 * x3 + c8 * x2 * y1 + c9 * x1 * y2 + c10 * y3; float wy = c11 + c12 * x1 + c13 * y1 + c14 * x2 + c15 * x1 * y1 + c16 * y2 + c17 * x3 + c18 * x2 * y1 + c19 * x1 * y2 + c20 * y3; // Delta x and delta y float dx = c2 * px1 + c4 * (2 * x1 * px1 + px2) + c5 * px1 * y1 + c7 * (3 * x2 * px1 + 3 * x1 * px2 + px3) + c8 * (2 * x1 * px1 + px2) * y1 + c9 * px1 * y2; float dy = c12 * px1 + c14 * (2 * x1 * px1 + px2) + c15 * px1 * y1 + c17 * (3 * x2 * px1 + 3 * x1 * px2 + px3) + c18 * (2 * x1 * px1 + px2) * y1 + c19 * px1 * y2; // Delta delta x and delta delta y float ddx = c4 * 2 * px2 + c7 * (6 * x1 * px2 + 6 * px3) + c8 * 2 * px2 * y1; float ddy = c14 * 2 * px2 + c17 * (6 * x1 * px2 + 6 * px3) + c18 * 2 * px2 * y1; for (int i = x; i < width; i += periodX) { destRect[index++] = wx * postScaleX - 0.5F; destRect[index++] = wy * postScaleY - 0.5F; wx += dx; wy += dy; dx += ddx; dy += ddy; ddx += dddx; ddy += dddy; } } return destRect; } /** * Computes the source point corresponding to the supplied point. * *

      This method returns the value of pt in the following * code snippet: * *

           * double x1 = (destPt.getX() + 0.5F)*preScaleX;
           * double x2 = x1*x1;
           * double x3 = x2*x1;
           *
           * double y1 = (destPt.getY() + 0.5F)*preScaleY;
           * double y2 = y1*y1;
           * double y3 = y2*y1;
           *
           * double sx = c1 + c2*x1 + c3*y1 +
           *     c4*x2 + c5*x1*y1 + c6*y2 +
           *     c7*x3 + c8*x2*y1 + c9*x1*y2 + c10*y3;
           * double sy = c11 + c12*x1 + c13*y1 +
           *     c14*x2 + c15*x1*y1 + c16*y2 +
           *     c17*x3 + c18*x2*y1 + c19*x1*y2 + c20*y3;
           *
           * Point2D pt = (Point2D)destPt.clone();
           * pt.setLocation(sx*postScaleX - 0.5, sy*postScaleY - 0.5);
           * 
      *

      * * @param destPt the position in destination image coordinates * to map to source image coordinates. * * @return a Point2D of the same class as * destPt. * * @throws IllegalArgumentException if destPt is * null. * * @since JAI 1.1.2 */ public Point2D mapDestPoint(Point2D destPt) { if (destPt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } double x1 = (destPt.getX() + 0.5F)*preScaleX; double x2 = x1*x1; double x3 = x2*x1; double y1 = (destPt.getY() + 0.5F)*preScaleY; double y2 = y1*y1; double y3 = y2*y1; double sx = c1 + c2*x1 + c3*y1 + c4*x2 + c5*x1*y1 + c6*y2 + c7*x3 + c8*x2*y1 + c9*x1*y2 + c10*y3; double sy = c11 + c12*x1 + c13*y1 + c14*x2 + c15*x1*y1 + c16*y2 + c17*x3 + c18*x2*y1 + c19*x1*y2 + c20*y3; Point2D pt = (Point2D)destPt.clone(); pt.setLocation(sx*postScaleX - 0.5, sy*postScaleY - 0.5); return pt; } } jai-core-1.1.4/src/share/classes/javax/media/jai/BorderExtenderConstant.java0000644000175000017500000002437310203035544026637 0ustar mathieumathieu/* * $RCSfile: BorderExtenderConstant.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:04 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.WritableRaster; /** * A subclass of BorderExtender that implements * border extension by filling all pixels outside of the image * bounds with constant values. For example, the image: * *

      * * * * *
      ABC
      DEF
      GHI
      * *
      if extended by adding two extra rows to the top and bottom and * two extra columns on the left and right sides, would become: * *

      * * * * * * * * * *
      XXXXXXX
      XXXXXXX
      XXABCXX
      XXDEFXX
      XXGHIXX
      XXXXXXX
      XXXXXXX
      * * where X is the constant fill value. The set of constants is clamped to * the range and precision of the data type of the WritableRaster * being filled. The number of constants used is given by the number of bands * of the WritableRaster. If the WritableRaster has * b bands, and there are c constants, constants * 0 through b - 1 are used when * b <= c. If there is only a single constant, then it is used * for all bands. If b > c, an * UnsupportedOperationException is thrown. * * @see BorderExtender */ public final class BorderExtenderConstant extends BorderExtender { private double[] constants; /** * Constructs an instance of BorderExtenderConstant * with a given set of constants. The constants are specified * as an array of doubles. */ public BorderExtenderConstant(double[] constants) { this.constants = constants; } private int clamp(int band, int min, int max) { int length = constants.length; double c; if(length == 1) { c = constants[0]; } else if (band < length) { c = constants[band]; } else { throw new UnsupportedOperationException(JaiI18N.getString("BorderExtenderConstant0")); } return (c > min) ? ((c > max) ? max : (int)c) : min; } /** * Returns a clone of the constants array originally * supplied to the constructor. * * @since JAI 1.1.2 */ public final double[] getConstants() { return (double[])constants; } /** * Fills in the portions of a given Raster that lie * outside the bounds of a given PlanarImage with * constant values. * *

      The portion of raster that lies within * im.getBounds() is not altered. * * @param raster The WritableRaster the border area of * which is to be filled with constants. * @param im The PlanarImage which determines the * portion of the WritableRaster not * to be filled. * * @throws IllegalArgumentException if either parameter is * null. * @throws UnsupportedOperationException if the number * of image bands exceeds the number of constants and the * latter is not unity. */ public final void extend(WritableRaster raster, PlanarImage im) { if ( raster == null || im == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } int width = raster.getWidth(); int height = raster.getHeight(); int numBands = raster.getNumBands(); int minX = raster.getMinX(); int maxX = minX + width; int minY = raster.getMinY(); int maxY = minY + height; int validMinX = Math.max(im.getMinX(), minX); int validMaxX = Math.min(im.getMaxX(), maxX); int validMinY = Math.max(im.getMinY(), minY); int validMaxY = Math.min(im.getMaxY(), maxY); int row, index; int dataType = raster.getSampleModel().getDataType(); if(dataType == DataBuffer.TYPE_FLOAT) { float[] fBandData = new float[numBands]; for (int b = 0; b < numBands; b++) { fBandData[b] = (b < constants.length) ? (float)constants[b] : 0.0F; } float[] fData = new float[width*numBands]; index = 0; for (int i = 0; i < width; i++) { for (int b = 0; b < numBands; b++) { fData[index++] = fBandData[b]; } } if(validMinX > validMaxX || validMinY > validMaxY) { // Raster does not intersect image. for (row = minY; row < maxY; row++) { raster.setPixels(minX, row, width, 1, fData); } } else { for (row = minY; row < validMinY; row++) { raster.setPixels(minX, row, width, 1, fData); } for (row = validMinY; row < validMaxY; row++) { if (minX < validMinX) { raster.setPixels(minX, row, validMinX - minX, 1, fData); } if (validMaxX < maxX) { raster.setPixels(validMaxX, row, maxX - validMaxX, 1, fData); } } for (row = validMaxY; row < maxY; row++) { raster.setPixels(minX, row, width, 1, fData); } } } else if(dataType == DataBuffer.TYPE_DOUBLE) { double[] dBandData = new double[numBands]; for (int b = 0; b < numBands; b++) { dBandData[b] = (b < constants.length) ? constants[b] : 0.0; } double[] dData = new double[width*numBands]; index = 0; for (int i = 0; i < width; i++) { for (int b = 0; b < numBands; b++) { dData[index++] = dBandData[b]; } } if(validMinX > validMaxX || validMinY > validMaxY) { // Raster does not intersect image. for (row = minY; row < maxY; row++) { raster.setPixels(minX, row, width, 1, dData); } } else { for (row = minY; row < validMinY; row++) { raster.setPixels(minX, row, width, 1, dData); } for (row = validMinY; row < validMaxY; row++) { if (minX < validMinX) { raster.setPixels(minX, row, validMinX - minX, 1, dData); } if (validMaxX < maxX) { raster.setPixels(validMaxX, row, maxX - validMaxX, 1, dData); } } for (row = validMaxY; row < maxY; row++) { raster.setPixels(minX, row, width, 1, dData); } } } else { int[] iBandData = new int[numBands]; switch(dataType) { case DataBuffer.TYPE_BYTE: for (int b = 0; b < numBands; b++) { iBandData[b] = clamp(b, 0, 255); } break; case DataBuffer.TYPE_SHORT: for (int b = 0; b < numBands; b++) { iBandData[b] = clamp(b, Short.MIN_VALUE, Short.MAX_VALUE); } break; case DataBuffer.TYPE_USHORT: for (int b = 0; b < numBands; b++) { iBandData[b] = clamp(b, 0, 65535); } break; case DataBuffer.TYPE_INT: for (int b = 0; b < numBands; b++) { iBandData[b] = clamp(b, Integer.MIN_VALUE, Integer.MAX_VALUE); } break; default: throw new IllegalArgumentException(JaiI18N.getString("Generic3")); } int[] iData = new int[width*numBands]; index = 0; for (int i = 0; i < width; i++) { for (int b = 0; b < numBands; b++) { iData[index++] = iBandData[b]; } } if(validMinX > validMaxX || validMinY > validMaxY) { // Raster does not intersect image. for (row = minY; row < maxY; row++) { raster.setPixels(minX, row, width, 1, iData); } } else { for (row = minY; row < validMinY; row++) { raster.setPixels(minX, row, width, 1, iData); } for (row = validMinY; row < validMaxY; row++) { if (minX < validMinX) { raster.setPixels(minX, row, validMinX - minX, 1, iData); } if (validMaxX < maxX) { raster.setPixels(validMaxX, row, maxX - validMaxX, 1, iData); } } for (row = validMaxY; row < maxY; row++) { raster.setPixels(minX, row, width, 1, iData); } } } } } jai-core-1.1.4/src/share/classes/javax/media/jai/ROIShape.java0000644000175000017500000013655010341201564023624 0ustar mathieumathieu/* * $RCSfile: ROIShape.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-11-24 00:04:04 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Polygon; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.Transparency; import java.awt.color.ColorSpace; import java.awt.geom.AffineTransform; import java.awt.geom.Area; import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.ComponentColorModel; import java.awt.image.DataBuffer; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Arrays; import java.util.Comparator; import java.util.LinkedList; import java.util.ListIterator; import java.util.Vector; /** * A class representing a region of interest within an image as a * Shape. Such regions are binary by definition. Using * a Shape representation allows boolean operations to be * performed quickly and with compact storage. If a * PropertyGenerator responsible for generating the * ROI property of a particular * OperationDescriptor (e.g., a warp) cannot reasonably * produce an ROIShape representing the region, it should * call getAsImage() on its sources and produce its * output ROI in image form. * */ public class ROIShape extends ROI { /** The internal Shape that defines this mask. */ transient Shape theShape = null; /** * Calculate the point of intersection of two line segments. * This method assumes that the line segments do in fact intersect. * * @param x1 The abscissa of the first end point of the first segment. * @param y1 The ordinate of the first end point of the first segment. * @param x2 The abscissa of the second end point of the first segment. * @param y2 The ordinate of the second end point of the first segment. * @param u1 The abscissa of the first end point of the second segment. * @param v1 The ordinate of the first end point of the second segment. * @param u2 The abscissa of the second end point of the second segment. * @param v2 The ordinate of the second end point of the second segment. * * @return The point of intersection. */ private static Point2D.Double getIntersection(double x1, double y1, double x2, double y2, double u1, double v1, double u2, double v2) { double[][] a = new double[2][2]; a[0][0] = y2 - y1; a[0][1] = x1 - x2; a[1][0] = v2 - v1; a[1][1] = u1 - u2; double[] c = new double[2]; c[0] = y1*(x1 - x2) + x1*(y2 - y1); c[1] = v1*(u1 - u2) + u1*(v2 - v1); double det = a[0][0]*a[1][1] - a[0][1]*a[1][0]; double tmp = a[0][0]; a[0][0] = a[1][1]/det; a[0][1] = -a[0][1]/det; a[1][0] = -a[1][0]/det; a[1][1] = tmp/det; double x = a[0][0]*c[0] + a[0][1]*c[1]; double y = a[1][0]*c[0] + a[1][1]*c[1]; return new Point2D.Double(x, y); } /** * Convert a Polygon into a LinkedList of * Rectangles representing run lengths of pixels contained * within the Polygon. * * @param clip The clipping Rectangle. * @param poly The Polygon to examine. * @return The LinkedList of run length * Rectangles. */ private LinkedList polygonToRunLengthList(Rectangle clip, Polygon poly) { PolyShape ps = new PolyShape(poly, clip); return ps.getAsRectList(); } /** * Converts a LinkedList of Rectangles into an * array of integers representing a bit mask. * * @param rectangleList The list of Rectangles. * @param clip The clipping Rectangle. * @param mask A two-dimensional array of ints at least * (width + 31)/32 entries wide and (height) entries tall, * or null. * * @return An integer array representing a bit mask. */ private static int[][] rectangleListToBitmask(LinkedList rectangleList, Rectangle clip, int[][] mask) { int bitField = 0x80000000; // Determine the minimum required width of the bitmask in integers. int bitmaskIntWidth = (clip.width + 31)/32; // Construct bitmask array if argument is null. if (mask == null) { mask = new int[clip.height][bitmaskIntWidth]; } else if (mask.length < clip.height || mask[0].length < bitmaskIntWidth) { throw new RuntimeException(JaiI18N.getString("ROIShape0")); } // Iterate over the list of Rectangles. ListIterator rectangleIter = rectangleList.listIterator(0); while (rectangleIter.hasNext()) { // Only set bits corresponding to pixels in the clip Rectangle. Rectangle rect; if (clip.intersects(rect = (Rectangle)rectangleIter.next())) { rect = clip.intersection(rect); // Set the extremal indexes for the current Rectangle. int yMin = rect.y - clip.y; int xMin = rect.x - clip.x; int yMax = yMin + rect.height - 1; int xMax = xMin + rect.width - 1; // Set all bits within the current Rectangle. for (int y = yMin; y <= yMax; y++) { int[] bitrow = mask[y]; for (int x = xMin; x <= xMax; x++) { int index = x / 32; int shift = x % 32; bitrow[index] |= (bitField >>> shift); } } } } return mask; } /** * Constructs an ROIShape from a Shape. * * @param s A Shape. * * @throws IllegalArgumentException if s is null. */ public ROIShape(Shape s) { if(s == null) { throw new IllegalArgumentException(JaiI18N.getString("ROIShape2")); } theShape = s; } /** * Constructs an ROIShape from an Area. * * @param a An Area. */ public ROIShape(Area a) { AffineTransform at = new AffineTransform(); // Identity PathIterator pi = a.getPathIterator(at); GeneralPath gp = new GeneralPath(pi.getWindingRule()); gp.append(pi, false); theShape = gp; } /** * Instance inner class used for scan conversion of a polygonal * Shape. */ private class PolyShape { /** A polygon which has yet to be classified as one * of the following types. */ private static final int POLYGON_UNCLASSIFIED = 0; /** A degenerate polygon, i.e., all vertices equal or on the same line. */ private static final int POLYGON_DEGENERATE = POLYGON_UNCLASSIFIED + 1; /** A convex polygon. */ private static final int POLYGON_CONVEX = POLYGON_DEGENERATE + 1; /** A concave polygon (simple or non-simple). */ private static final int POLYGON_CONCAVE = POLYGON_CONVEX + 1; /** The internal polygon. */ private Polygon poly; /** The clipping Rectangle. */ private Rectangle clip; /** The type of polygon. */ private int type = POLYGON_UNCLASSIFIED; /** Flag indicating whether the supplied clipping Rectangle * is inside the Polygon. */ private boolean insidePolygon = false; /** * Constructs a new PolyShape. The Polygon argument is * clipped against the supplied Rectangle. * * @param polygon The Polygon. * @param clipRect The clipping Rectangle. */ PolyShape(Polygon polygon, Rectangle clipRect) { // Cache the arguments. poly = polygon; clip = clipRect; // Determine whether the clipping Rectangle is inside the Polygon. insidePolygon = poly.contains(clipRect); type = POLYGON_UNCLASSIFIED; } /** * Inner class representing a polygon edge. */ private class PolyEdge implements Comparator { /** X coordinate of intersection of edge with current scanline. */ public double x; /** Change in X with respect to Y. */ public double dx; /** The edge number: edge i goes from vertex i to vertex i+1. */ public int i; /** * Construct a PolyEdge object. * * @param x X coordinate of edge intersection with scanline. * @param dx The change in X with respect to Y. * @param i The edge number. */ PolyEdge(double x, double dx, int i) { this.x = x; this.dx = dx; this.i = i; } /** * Implementation of java.util.Comparator.compare. The argument * Objects are assumed to be PolyEdges * and are sorted on the basis of their respective x components. * * @param o1 The first PolyEdge object. * @param o2 The second PolyEdge object. * * @return -1 if o1 < o2, 1 if o1 > o2, 0 if o1 == o2. */ public int compare(Object o1, Object o2) { double x1 = ((PolyEdge)o1).x; double x2 = ((PolyEdge)o2).x; int returnValue; if (x1 < x2) { returnValue = -1; } else if (x1 > x2) { returnValue = 1; } else { returnValue = 0; } return returnValue; } } /** * Perform scan conversion of the PolyShape to generate * a LinkedList of Rectangles. * * @return A LinkedList of Rectangles * representing the scan conversion of the PolyShape. */ public LinkedList getAsRectList() { LinkedList rectList = new LinkedList(); if (insidePolygon) { rectList.addLast((Object)poly.getBounds()); } else { // Classify the polygon as one of the pre-defined types. classifyPolygon(); // Perform scan conversion according to polygon type. switch(type) { case POLYGON_DEGENERATE: rectList = null; break; case POLYGON_CONVEX: rectList = scanConvex(rectList); break; case POLYGON_CONCAVE: rectList = scanConcave(rectList); break; default: throw new RuntimeException(JaiI18N.getString("ROIShape1")); } } return rectList; } /** * Classify a Polygon as one of the pre-defined types * for this class. */ private int classifyPolygon() { if (type != POLYGON_UNCLASSIFIED) { return type; } int n = poly.npoints; if (n < 3) { type = POLYGON_DEGENERATE; return type; } else if (poly.getBounds().contains(clip)) { type = POLYGON_CONVEX; return type; } // Cache references to Polygon vertices. int[] x = poly.xpoints; int[] y = poly.ypoints; // Calculate the sign of the angle between the first and // second directed segments. int previousSign = sgn((x[0] - x[1])*(y[1] - y[2]) - (x[1] - x[2])*(y[0] - y[1])); boolean allZero = (previousSign == 0); // Calculate the initial lexicographic direction. int previousDirection; if (x[0] < x[1]) { previousDirection = -1; } else if (x[0] > x[1]) { previousDirection = 1; } else if (y[0] < y[1]) { previousDirection = -1; } else if (y[0] > y[1]) { previousDirection = 1; } else { previousDirection = 0; } // Calculate signs of all angles between segments. If all angles // are zero then the vertices all lie on a line. If all non-zero // angles have the same sign then the polygon is convex. Otherwise // the polygon is concave unless the lexicographic direction // changes sign more than twice. int numDirectionChanges = 0; for (int i = 1; i < n; i++) { // Set the indices of the next two vertices. int j = (i + 1)%n; int k = (i + 2)%n; // Calculate the initial lexicographic direction. int currentDirection; if (x[i] < x[j]) { currentDirection = -1; } else if (x[i] > x[j]) { currentDirection = 1; } else if (y[i] < y[j]) { currentDirection = -1; } else if (y[i] > y[j]) { currentDirection = 1; } else { currentDirection = 0; } // Increment the direction change counter if necessary. if (currentDirection != 0 && currentDirection == -previousDirection) { numDirectionChanges++; } previousDirection = currentDirection; // Calculate the sign of the angle between the current and // next directed segments. int sign = sgn((x[i] - x[j])*(y[j] - y[k]) - (x[j] - x[k])*(y[i] - y[j])); allZero = (allZero && sign == 0); if (!allZero) { if (sign != 0 && sign == -previousSign) { type = POLYGON_CONCAVE; break; } else if (sign != 0) { // Only cache non-zero signs. previousSign = sign; } } } if (type == POLYGON_UNCLASSIFIED) { if (allZero) { // All points on a line. type = POLYGON_DEGENERATE; } else if (numDirectionChanges > 2) { type = POLYGON_CONCAVE; } else { type = POLYGON_CONVEX; } } return type; } /** * Calculate the sign of the argument. * * @param i The integer the sign of which is to be determined. * * @return 1 for positive, -1 for negative, and 0 for zero arguments. */ private final int sgn(int i) { int sign; if (i > 0) { sign = 1; } else if (i < 0) { sign = -1; } else { // zero sign = 0; } return sign; } /** * Perform scan conversion of a convex polygon. * * @param rectList A LinkedList; may be null. * * @return A LinkedList of Rectangles * representing the scan conversion of the convex polygon. */ private LinkedList scanConvex(LinkedList rectList) { if (rectList == null) { rectList = new LinkedList(); } // Find the index of the top vertex. int yMin = poly.ypoints[0]; int topVertex = 0; int n = poly.npoints; for (int i = 1; i < n; i++) { if (poly.ypoints[i] < yMin) { yMin = poly.ypoints[i]; topVertex = i; } } // Left and right vertex indices. int leftIndex = topVertex; int rightIndex = topVertex; // Number of vertices remaining. int numRemaining = n; // Current scan line. int y = yMin; // Lower end of left & right edges. int intYLeft = y - 1; int intYRight = intYLeft; // Copy points to double precision arrays where necessary. double[] px = intArrayToDoubleArray(poly.xpoints); int[] py = poly.ypoints; double[] leftX = new double[1]; double[] leftDX = new double[1]; double[] rightX = new double[1]; double[] rightDX = new double[1]; // Scan along lines using new edges along the left and right sides // as the line crosses new vertices. while (numRemaining > 0) { int i; // Advance the left edge. while (intYLeft <= y && numRemaining > 0) { numRemaining--; i = leftIndex - 1; if (i < 0) i = n - 1; intersectX(px[leftIndex], py[leftIndex], px[i], py[i], y, leftX, leftDX); intYLeft = py[i]; leftIndex = i; } // Advance the right edge. while (intYRight <= y && numRemaining > 0) { numRemaining--; i = rightIndex + 1; if (i >= n) i = 0; intersectX(px[rightIndex], py[rightIndex], px[i], py[i], y, rightX, rightDX); intYRight = py[i]; rightIndex = i; } // Process until end of left or right edge. while (y < intYLeft && y < intYRight) { if (y >= clip.y && y < clip.getMaxY()) { Rectangle rect; if (leftX[0] <= rightX[0]) { rect = scanSegment(y, leftX[0], rightX[0]); } else { rect = scanSegment(y, rightX[0], leftX[0]); } if (rect != null) { rectList.addLast((Object)rect); } } y++; leftX[0] += leftDX[0]; rightX[0] += rightDX[0]; } } return rectList; } /** * Return a Rectangle for the supplied line and * abscissa end points. * * @param y The line number. * @param leftX The left end point of the segment. * @param rightX The right end point of the segment. * * @return The run length Rectangle for the segment. */ private Rectangle scanSegment(int y, double leftX, double rightX) { double x = leftX - 0.5; int xl = (x < clip.x) ? clip.x : (int)Math.ceil(x); int xr = (int)Math.floor(rightX - 0.5); if (xr >= clip.x + clip.width) xr = clip.x + clip.width - 1; if (xl > xr) return null; return new Rectangle(xl, y, xr - xl + 1, 1); } /** * For the line y + 0.5 calculate the intersection with the segment * (x1, y1) to (x2, y2) as well as the slope dx/dy at the point of * intersection. * * @param x1 Abscissa of first segment end point. * @param y1 Ordinate of first segment end point. * @param x2 Abscissa of second segment end point. * @param y2 Ordinate of second segment end point. * @param y The image line to intersect. * @param x The abscissa of the point of intersection. * @param dx The slope dx/dy of the point of intersection. */ private void intersectX(double x1, int y1, double x2, int y2, int y, double[] x, double[] dx) { int dy = y2 - y1; if (dy == 0) dy = 1; double frac = y - y1 + 0.5; dx[0] = (x2 - x1)/dy; x[0] = x1 + dx[0]*frac; } /** * Perform scan conversion of a concave polygon. * * @param rectList A LinkedList; may be null. * * @return A LinkedList of Rectangles * representing the scan conversion of the concave polygon. */ private LinkedList scanConcave(LinkedList rectList) { if (rectList == null) { rectList = new LinkedList(); } int numVertices = poly.npoints; if (numVertices <= 0) return null; // Create y-sorted Vector of indices into vertex arrays. Vector indVector = new Vector(); indVector.add(new Integer(0)); for (int count = 1; count < numVertices; count++) { // Search entire array until // vertexY[index] >= vertexY[count] or index == count. int index = 0; int value = poly.ypoints[count]; while (index < count) { int elt = ((Integer)(indVector.get(index))).intValue(); if (value <= poly.ypoints[elt]) break; index++; } indVector.insertElementAt((Object)new Integer(count), index); } // Convert the Vector of indices to an array of same. int[] ind = vectorToIntArray(indVector); // Create a Vector of active edges. Vector activeEdges = new Vector(numVertices); // Initialize the range of lines to examine. int y0 = Math.max((int)clip.getMinY(), (int)Math.ceil(poly.ypoints[ind[0]] - 0.5F)); int y1 = Math.min((int)clip.getMaxY(), (int)Math.floor(poly.ypoints[ind[numVertices-1]] - 0.5F)); // Loop over lines. The current line is at y + 0.5 in // continuous coordinates. int nextVertex = 0; for (int y = y0; y <= y1; y++) { // Check vertices between previous and current line. while (nextVertex < numVertices && poly.ypoints[ind[nextVertex]] <= y + 0.5F) { int i = ind[nextVertex]; // Delete old (previous) edges and insert new // (subsequent) edges if they cross current line. int j = i > 0 ? i - 1 : numVertices - 1; // Previous vertex if (poly.ypoints[j] <= y - 0.5F) { deleteEdge(activeEdges, j); } else if (poly.ypoints[j] > y + 0.5F) { appendEdge(activeEdges, j, y); } j = i < numVertices - 1 ? i + 1 : 0; // Next vertex. if (poly.ypoints[j] <= y - 0.5F) { deleteEdge(activeEdges, i); } else if (poly.ypoints[j] > y + 0.5F) { appendEdge(activeEdges, i, y); } nextVertex++; } // Sort active edges by the edge X coordinate. Object[] edges = activeEdges.toArray(); Arrays.sort(edges, (PolyEdge)edges[0]); // Extract run length Rectangles for current line. int numActive = activeEdges.size(); for (int k = 0; k < numActive; k += 2) { // Get left and right edges. PolyEdge edge1 = (PolyEdge)edges[k]; PolyEdge edge2 = (PolyEdge)edges[k+1]; // Clip left end point. int xl = (int)Math.ceil(edge1.x - 0.5); if (xl < clip.getMinX()) { xl = (int)clip.getMinX(); } // Clip right end point. int xr = (int)Math.floor(edge2.x - 0.5); if (xr > clip.getMaxX()) { xr = (int)clip.getMaxX(); } // Create run length Rectangle. if (xl <= xr) { Rectangle r = new Rectangle(xl, y, xr - xl + 1, 1); rectList.addLast((Object)r); } // Increment edge coordinates. edge1.x += edge1.dx; activeEdges.setElementAt((Object)edge1, k); edge2.x += edge2.dx; activeEdges.setElementAt((Object)edge2, k+1); } } return rectList; } /** * Delete a PolyEdge from the Vector of active edges. * * @param edges The Vector of PolyEdges. * @param i The number of the edge to be deleted. */ private void deleteEdge(Vector edges, int i) { int numActive = edges.size(); int j; for (j = 0; j < numActive; j++) { PolyEdge edge = (PolyEdge)edges.get(j); if (edge.i == i) break; } if (j < numActive) { edges.removeElementAt(j); } } /** * Append a PolyEdge to the Vector of active edges. * * @param edges The Vector of PolyEdges. * @param i The number of the edge to be appended. * @param y The y coordinate of the current scanline. */ private void appendEdge(Vector edges, int i, int y) { int j = (i + 1)%poly.npoints; int ip; int iq; if (poly.ypoints[i] < poly.ypoints[j]) { ip = i; iq = j; } else { ip = j; iq = i; } double dx = (double)(poly.xpoints[iq] - poly.xpoints[ip])/ (double)(poly.ypoints[iq] - poly.ypoints[ip]); double x = dx*(y + 0.5F - poly.ypoints[ip]) + poly.xpoints[ip]; edges.add(new PolyEdge(x, dx, i)); } /** * Convert an array of ints to an array of * doubles. */ private double[] intArrayToDoubleArray(int[] intArray) { int length = intArray.length; double[] doubleArray = new double[length]; for (int i = 0; i < length; i++) { doubleArray[i] = intArray[i]; } return doubleArray; } /** * Convert a Vector of Integers to an array * of ints. * * @param vector A Vector of Integers. * * @return The array of ints. */ private int[] vectorToIntArray(Vector vector) { int size = vector.size(); int[] array = new int[size]; Object[] objects = vector.toArray(); for (int i = 0; i < size; i++) { array[i] = ((Integer)objects[i]).intValue(); } return array; } } /** Returns the bounds of the mask as a Rectangle. */ public Rectangle getBounds() { return theShape.getBounds(); } /** Returns the bounds of the mask as a Rectangle2D. */ public Rectangle2D getBounds2D() { return theShape.getBounds2D(); } /** * Returns true if the mask contains a given Point. * * @param p a Point specifying the coordinates of the pixel to be queried. * @return true if the pixel lies within the mask. * * @throws IllegalArgumentException is p is null. */ public boolean contains(Point p) { if ( p == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return contains(p.x,p.y); } /** * Returns true if the mask contains a given Point2D. * * @param p A Point2D specifying the coordinates of the pixel * to be queried. * @throws IllegalArgumentException is p is null. * @return true if the pixel lies within the mask. */ public boolean contains(Point2D p) { if ( p == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return contains((int) p.getX(), (int) p.getY()); } /** * Returns true if the mask contains the point (x, y). * * @param x An int specifying the X coordinate of the pixel to be queried. * @param y An int specifying the Y coordinate of the pixel to be queried. * @return true if the pixel lies within the mask. */ public boolean contains(int x, int y) { return theShape.contains(x,y); } /** * Returns true if the mask contains the point (x, y). * * @param x A double specifying the X coordinate of the pixel * to be queried. * @param y A double specifying the Y coordinate of the pixel * to be queried. * @return true if the pixel lies within the mask. */ public boolean contains(double x, double y) { return contains((int)x,(int)y); } /** * Returns true if a given Rectangle is * entirely included within the mask. * * @param rect A Rectangle specifying the region to * be tested for inclusion. * @return true if the rectangle is * entirely contained within the mask. * @throws IllegalArgumentException is rect is null. */ public boolean contains(Rectangle rect) { if ( rect == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return contains(new Rectangle2D.Float((float) rect.x, (float) rect.y, (float) rect.width, (float) rect.height)); } /** * Returns true if a given Rectangle2D * is entirely included within the mask. * * @param rect A Rectangle2D specifying the region to * be tested for inclusion. * @return true if the rectangle is entirely * contained within the mask. * @throws IllegalArgumentException is rect is null. */ public boolean contains(Rectangle2D rect) { if ( rect == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return theShape.contains(rect); } /** * Returns true if a given rectangle (x, y, w, h) is entirely * included within the mask. * * @param x The int X coordinate of the upper left corner of the region. * @param y The int Y coordinate of the upper left corner of the region. * @param w The int width of the region. * @param h The int height of the region. * @return true if the rectangle is entirely * contained within the mask. */ public boolean contains(int x, int y, int w, int h) { return contains(new Rectangle2D.Float((float) x, (float) y, (float) w, (float) h)); } /** * Returns true if a given rectangle (x, y, w, h) is entirely * included within the mask. * * @param x The double X coordinate of the upper left corner of the region. * @param y The double Y coordinate of the upper left corner of the region. * @param w The double width of the region. * @param h The double height of the region. * @return true if the rectangle is entirely contained * within the mask. */ public boolean contains(double x, double y, double w, double h) { return theShape.contains(x, y, w, h); } /** * Returns true if a given Rectangle * intersects the mask. * * @param r A Rectangle specifying the region to be tested for * inclusion. * @return true if the rectangle intersects the mask. * @throws IllegalArgumentException is r is null. */ public boolean intersects(Rectangle r) { if ( r == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return intersects(new Rectangle2D.Float((float) r.x, (float) r.y, (float) r.width, (float) r.height)); } /** * Returns true if a given Rectangle2D * intersects the mask. * * @param r A Rectangle2D specifying the region to be * tested for inclusion. * @return true if the rectangle intersects the mask. * @throws IllegalArgumentException is r is null. */ public boolean intersects(Rectangle2D r) { if ( r == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return theShape.intersects(r); } /** * Returns true if a given rectangle (x, y, w, h) * intersects the mask. * * @param x The int X coordinate of the upper left corner of the region. * @param y The int Y coordinate of the upper left corner of the region. * @param w The int width of the region. * @param h The int height of the region. * @return true if the rectangle intersects the mask. */ public boolean intersects(int x, int y, int w, int h) { return intersects(new Rectangle2D.Float((float) x, (float) y, (float) w, (float) h)); } /** * Returns true if a given rectangle (x, y, w, h) * intersects the mask. * * @param x The double X coordinate of the upper left corner of the region. * @param y The double Y coordinate of the upper left corner of the region. * @param w The double width of the region. * @param h The double height of the region. * @return true if the rectangle intersects the mask. */ public boolean intersects(double x, double y, double w, double h) { return theShape.intersects(x, y, w, h); } /** * Adds another mask to this one. * This operation may force this mask to be rendered. * * @param roi A ROI. * @throws IllegalArgumentException is roi is null. */ public ROI add(ROI roi) { if (roi == null) { throw new IllegalArgumentException(JaiI18N.getString("ROIShape3")); } if (!(roi instanceof ROIShape)) { return super.add(roi); } else { ROIShape rois = (ROIShape) roi; Area a1 = new Area(theShape); Area a2 = new Area(rois.theShape); a1.add(a2); return new ROIShape(a1); } } /** * Subtracts another mask from this one. * This operation may force this mask to be rendered. * * @param roi A ROI. * @throws IllegalArgumentException is roi is null. */ public ROI subtract(ROI roi) { if (roi == null) { throw new IllegalArgumentException(JaiI18N.getString("ROIShape3")); } if (!(roi instanceof ROIShape)) { return super.subtract(roi); } else { ROIShape rois = (ROIShape)roi; Area a1 = new Area(theShape); Area a2 = new Area(rois.theShape); a1.subtract(a2); return new ROIShape(a1); } } /** * Sets the mask to its intersection with another mask. * This operation may force this mask to be rendered. * * @param roi A ROI. * @throws IllegalArgumentException is roi is null. */ public ROI intersect(ROI roi) { if (roi == null) { throw new IllegalArgumentException(JaiI18N.getString("ROIShape3")); } if (!(roi instanceof ROIShape)) { return super.intersect(roi); } else { ROIShape rois = (ROIShape)roi; Area a1 = new Area(theShape); Area a2 = new Area(rois.theShape); a1.intersect(a2); return new ROIShape(a1); } } /** * Sets the mask to its exclusive-or with another mask. * This operation may force this mask to be rendered. * * @param roi A ROI. * @throws IllegalArgumentException is roi is null. */ public ROI exclusiveOr(ROI roi) { if (roi == null) { throw new IllegalArgumentException(JaiI18N.getString("ROIShape3")); } if (!(roi instanceof ROIShape)) { return super.exclusiveOr(roi); } else { ROIShape rois = (ROIShape)roi; Area a1 = new Area(theShape); Area a2 = new Area(rois.theShape); a1.exclusiveOr(a2); return new ROIShape(a1); } } /** * Returns the internal Shape representation or null if a shape * representation is not possible. */ public Shape getAsShape() { return theShape; } /** * Returns the shape as a PlanarImage. This requires * performing an antialiased rendering of the internal Shape. * * @return If the upper-left corner of the bounds of this * ROIShape is (0, 0), the returned image is a * BufferedImage of type TYPE_BYTE_BINARY wrapped as * a PlanarImage. Otherwise, the returned image is a * (bilevel) TiledImage whose SampleModel * is an instance of MultiPixelPackedSampleModel. */ public PlanarImage getAsImage() { if (theImage != null) return theImage; Rectangle r = theShape.getBounds(); PlanarImage pi; Graphics2D g2d; if ((r.x == 0) && (r.y == 0)) { BufferedImage bi = new BufferedImage(r.width, r.height, BufferedImage.TYPE_BYTE_BINARY); pi = PlanarImage.wrapRenderedImage(bi); g2d = bi.createGraphics(); } else { SampleModel sm = new MultiPixelPackedSampleModel( DataBuffer.TYPE_BYTE, r.width, r.height, 1); // Create a TiledImage into which to write. TiledImage ti = new TiledImage( r.x, r.y, r.width, r.height, r.x, r.y, sm, PlanarImage.createColorModel(sm)); // Create the associated TiledImageGraphics. pi = ti; g2d = ti.createGraphics(); } // Write the Shape into the TiledImageGraphics. g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.fill(theShape); theImage = pi; // Cache the output return theImage; } /** * Transforms the current contents of the ROI by a * given AffineTransform. * * @param at An AffineTransform object. * @throws IllegalArgumentException if at is null. */ public ROI transform(AffineTransform at) { if ( at == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return new ROIShape(at.createTransformedShape(theShape)); } /** * Returns a bitmask for a given rectangular region of the ROI * indicating whether the pixel is included in the region of * interest. The results are packed into 32-bit integers, with * the MSB considered to lie on the left. The last entry in each * row of the result may have bits that lie outside of the * requested rectangle. These bits are guaranteed to be zeroed. * *

      The mask array, if supplied, must be of length * equal to or greater than height and each of its * subarrays must have length equal to or greater than (width + * 31)/32. If null is passed in, a suitable array * will be constructed. If the mask is non-null but has * insufficient size, an exception will be thrown. * * @param x The X coordinate of the upper left corner of the rectangle. * @param y The Y coordinate of the upper left corner of the rectangle. * @param width The width of the rectangle. * @param height The height of the rectangle. * @param mask A two-dimensional array of ints at least * (width + 31)/32 entries wide and (height) entries tall, * or null. * @return A reference to the mask parameter, or * to a newly constructed array if mask is * null. */ public int[][] getAsBitmask(int x, int y, int width, int height, int[][] mask) { // Get the un-merged Rectangle list (run length Rectangles only). LinkedList rectList = getAsRectangleList(x, y, width, height, false); if (rectList == null) { return null; } // Convert the Rectangle list to a bit mask. return rectangleListToBitmask(rectList, new Rectangle(x, y, width, height), mask); } /** * Returns a LinkedList of Rectangles * for a given rectangular region of the ROI. The * Rectangles in the list are merged into a minimal * set. * * @param x The X coordinate of the upper left corner of the rectangle. * @param y The Y coordinate of the upper left corner of the rectangle. * @param width The width of the rectangle. * @param height The height of the rectangle. * @return A LinkedList of Rectangles. */ public LinkedList getAsRectangleList(int x, int y, int width, int height) { return getAsRectangleList(x, y, width, height, true); } /** * Returns a LinkedList of Rectangles for * a given rectangular region of the ROI. * * @param x The X coordinate of the upper left corner of the rectangle. * @param y The Y coordinate of the upper left corner of the rectangle. * @param width The width of the rectangle. * @param height The height of the rectangle. * @param mergeRectangles true if the Rectangles * are to be merged into a minimal set. * @return A LinkedList of Rectangles. */ protected LinkedList getAsRectangleList(int x, int y, int width, int height, boolean mergeRectangles) { LinkedList rectangleList = null; // Create a clipping Rectangle. Rectangle clip = new Rectangle(x, y, width, height); /// XXX bpb 1998/09/28 Is it really necessary to use Area.intersects()? /// Note that the Polygon.intersects() method appears not to be /// implemented. Shape.intersects() also appeared not to work in /// testing the trivial case below. if (!((new Area(theShape)).intersects(clip))) { // Null overlap. return null; } else if (theShape instanceof Rectangle2D) { // Trivial case. // Return a list consisting of a single Rectangle which is the // intersection of the clipping Rectangle with the internal // Shape of the ROIShape rounded to integer coordinates. Rectangle2D.Double dstRect = new Rectangle2D.Double(); Rectangle2D.intersect((Rectangle2D)theShape, clip, dstRect); int rectX = (int)Math.round(dstRect.getMinX()); int rectY = (int)Math.round(dstRect.getMinY()); int rectW = (int)Math.round(dstRect.getMaxX() - rectX); int rectH = (int)Math.round(dstRect.getMaxY() - rectY); rectangleList = new LinkedList(); rectangleList.addLast((Object)new Rectangle(rectX, rectY, rectW, rectH)); } else if (theShape instanceof Polygon) { // Polygon. rectangleList = polygonToRunLengthList(clip, (Polygon)theShape); if (mergeRectangles && rectangleList != null) { rectangleList = mergeRunLengthList(rectangleList); } } else { // Generic case. // Get the corresponding PlanarImage. getAsImage(); // Call the super-class method. rectangleList = super.getAsRectangleList(x, y, width, height, mergeRectangles); } return rectangleList; } /** * Serialize the ROIShape. * * @param out The ObjectOutputStream. */ private void writeObject(ObjectOutputStream out) throws IOException { // Create a serializable form of the Shape. LinkedList rectList = null; if (theShape == null) { rectList = new LinkedList(); // zero-size LinkedList } else { Rectangle r = getBounds(); rectList = getAsRectangleList(r.x, r.y, r.width, r.height); } // Write serialized form to the stream. out.defaultWriteObject(); out.writeObject(rectList); } /** * Deserialize the ROIShape. * * @param in The ObjectInputStream. */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { // Read serialized form from the stream. LinkedList rectList = null; in.defaultReadObject(); rectList = (LinkedList)in.readObject(); // Restore the transient Shape as an Area. Area a = new Area(); int listSize = rectList.size(); for (int i = 0; i < listSize; i++) { a.add(new Area((Rectangle)rectList.get(i))); } theShape = a; } } jai-core-1.1.4/src/share/classes/javax/media/jai/RenderableOp.java0000644000175000017500000014217710203035544024556 0ustar mathieumathieu/* * $RCSfile: RenderableOp.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:20 $ * $State: Exp $ */ package javax.media.jai; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.util.PropertyUtil; import java.awt.Dimension; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.renderable.ContextualRenderedImageFactory; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderContext; import java.awt.image.renderable.RenderableImage; import java.awt.image.renderable.RenderableImageOp; import java.beans.PropertyChangeListener; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Enumeration; import java.util.Hashtable; import java.util.HashSet; import java.util.Vector; import javax.media.jai.util.CaselessStringKey; import javax.media.jai.registry.CRIFRegistry; import javax.media.jai.remote.SerializableRenderedImage; /** * A node in a renderable imaging chain. This is the Java Advanced * Imaging version of the Java2D class RenderableImageOp. * Instead of an explicit ContextualRenderedImageFactory, * the indirection of the OperationRegistry is used. * *

      A RenderableOp stores an operation name and a * ParameterBlock containing sources and parameters. A set of * nodes may be joined together via the source Vectors within * their respective ParameterBlocks to form a directed * acyclic graph (DAG). The topology, i.e., connectivity, of * the graph may be altered by changing the node's sources. The operation * name and parameters may also be changed. * *

      Such chains provide a framework for resolution- and rendering- * independent imaging. They are useful in that a chain may be manipulated * dynamically and rendered multiple times. Thus for example the same * chain of operations may be applied to different images or the parameters * of certain operations in a chain may be modified interactively. * *

      A RenderableOp may be constructed directly as, for example, *

       * 
       * RenderableImage addend1;
       * RenderableImage addend2;
       * ParameterBlock pb =
       *     (new ParameterBlock()).addSource(addend1).addSource(addend2);
       * RenderableOp node = new RenderableOp("add", pb);
       * 
       * 
      * or via the createRenderable() or * createRenderableNS() methods defined in the JAI * class. The difference between direct construction of a node and creation * via a convenience method is that in the latter case: * *
        *
      1. It is verified that the operation supports the renderable mode.
      2. *
      3. Using the validateArguments() method of the associated * OperationDescriptor, the arguments (sources and parameters) * are validated as being compatible with the specified operation.
      4. *
      5. Global RenderingHints maintained by the JAI * instance are set on the RenderableOp using * setRenderingHints().
      6. *
      * *

      When a chain of nodes is rendered by any any of the * createRendering() methods, a "parallel" chain of * RenderedImages is created. Each node in the chain of * RenderableOps corresponds to a node in the chain of * RenderedImages. A RenderedImage associated * with a given node is referred to as a rendering of the node. * *

      The translation between RenderableOp chains and * RenderedImage (usually OpImage) chains makes * use of three levels of indirection provided by the * OperationRegistry, ContextualRenderedImageFactory, * (CRIF), and RenderedImageFactory (RIF) facilities. First, the * OperationRegistry is used to map the operation name into a * CRIF. This CRIF then constructs a RenderedImage via its * create(RenderContext, ParameterBlock) method. The third * level of indirection results from the operation name being mapped within * create() into the optimum RIF which actually creates the * RenderedImage. (Note that this third level of indirection is a * function of the CRIF implementation of the renderable create() * method: that provided by the convenience class CRIFImpl * provides this indirection.) If the RenderedImage returned by * the CRIF create() invocation is a RenderedOp, it * is replaced with the rendering of the RenderedOp (usually an * OpImage). * *

      RenderingHints may be set on a RenderableOp * to provide a set of common hints to be used in all invocations of the * various createRendering() methods on the node. These hints * are merged with any hints supplied to createRendering() * either explicitly or via a RenderContext. Directly * supplied hints take precedence over the common hints. * *

      RenderableOp nodes may participate in Java Bean-style * events. The PropertyChangeEmitter methods may be used * to register and unregister PropertyChangeListeners. * Certain PropertyChangeEvents may be emitted by the * RenderableOp. These include the * PropertyChangeEventJAIs and * PropertySourceChangeEvents required by virtue of implementing * the OperationNode interface. * *

      RenderableOp nodes are WritablePropertySources * and so manage a name-value database of image meta-data also known as image * properties. Properties may be set on and requested from a node. The * value of a property not explicitly set on the node (via * setProperty()) is obtained from the property environment of * the node. When a property is derived from the property environment it is * cached locally to ensure synchronization, i.e., that properties do not * change spontaneously if for example the same property is modified upstream. * *

      The property environment of the RenderableOp is initially * derived from that of the corresponding OperationDescriptor * as maintained by the OperationRegistry. It may be modified * locally by adding a PropertyGenerator or by suppressing a * specific property. These modifications cannot be undone. * *

      When a property value is requested an attempt will be made to derive * it from the several entities in the following order of precedence: *

        *
      1. local properties;
      2. *
      3. any registered PropertyGenerators, or *
        a source specified via a copy-from-source directive;
      4. *
      5. the first source which defines the property.
      6. *
      * Local properties are those which have been cached locally either by virtue * of direct invocation of setProperty() or due to caching of a * property derived from the property environment. * *

      The properties of a RenderableOp node are copied to each * rendering generated by any of the createRendering() methods. * Properties already set on the rendering are not copied, i.e., those of the * rendering take precedence. * *

      A RenderableOp chain created on a client may be passed * to a server via a RemoteImage. Any RenderedImage * sources which are not Serializable will be wrapped in * SerializableRenderedImages for serialization. The tile * transmission parameters will be determined from the common * RenderingHints of the node. All other non-serializable * objects will attempt to be serialized using * SerializerFactory. If no Serializer is * available for a particular object, a * java.io.NotSerializableException may result. Image * properties (meta-data) are serialized insofar as they are serializable: * non-serializable components are simply eliminated from the local cache * of properties and from the property environment. * * @see CRIFImpl * @see CollectionOp * @see OperationRegistry * @see RenderedOp * @see java.awt.RenderingHints * @see java.awt.image.renderable.ContextualRenderedImageFactory * @see java.awt.image.renderable.RenderableImageOp * @see java.awt.image.renderable.RenderContext */ public class RenderableOp implements RenderableImage, OperationNode, WritablePropertySource, Serializable { /** * A helper object to manage firing events. * * @since JAI 1.1 */ protected PropertyChangeSupportJAI eventManager = null; /** * A helper object to manage the image properties. * * @since JAI 1.1 */ protected WritablePropertySourceImpl properties = null; /** * An object to assist in implementing OperationNode. * * @since JAI 1.1 */ protected OperationNodeSupport nodeSupport; /** * The PropertySource containing the combined properties * of all of the node's sources. */ protected transient PropertySource thePropertySource; /** * The ContextualRenderedImageFactory used to * generate renderings. */ protected transient ContextualRenderedImageFactory crif = null; /** * Constructs a RenderableOp given the name of the operation to be * performed and a ParameterBlock containing RenderableImage sources * and other parameters. Any RenderedImage sources referenced by the * ParameterBlock will be ignored. * *

      The ParameterBlock may include * DeferredData parameters. These will not be evaluated * until their values are actually required, i.e., when a rendering of * the node is requested or the renderable dimensions are queried. * * @param registry The OperationRegistry to be used for * instantiation. if null, the default registry * is used. Saved by reference. * @param opName The operation name. Saved by reference. * @param pb The sources and other parameters. If null, * it is assumed that this node has no sources and parameters. * This parameter is cloned. * @param hints The common node RenderingHints to be set; * it may be null. * This parameter is cloned. * * @throws IllegalArgumentException if opName is * null. * * @since JAI 1.1 */ public RenderableOp(OperationRegistry registry, String opName, ParameterBlock pb, RenderingHints hints) { if(pb == null) { // Ensure that the PB is non-null. pb = new ParameterBlock(); } else { // Clone the PB. pb = (ParameterBlock)pb.clone(); } // Clone the hints if non-null. if(hints != null) { hints = (RenderingHints)hints.clone(); } // Initialize the various helper objects. eventManager = new PropertyChangeSupportJAI(this); properties = new WritablePropertySourceImpl(null, null, eventManager); nodeSupport = new OperationNodeSupport(getRegistryModeName(), opName, registry, pb, hints, eventManager); } /** * Constructs a RenderableOp given the name of the operation to be * performed and a ParameterBlock containing RenderableImage sources * and other parameters. Any RenderedImage sources referenced by the * ParameterBlock will be ignored. * *

      The ParameterBlock may include * DeferredData parameters. These will not be evaluated * until their values are actually required, i.e., when a rendering of * the node is requested or the renderable dimensions are queried. * * @param registry The OperationRegistry to be used for * instantiation. if null, the default registry * is used. Saved by reference. * @param opName The operation name. Saved by reference. * @param pb The sources and other parameters. If null, * it is assumed that this node has no sources and parameters. * This parameter is cloned. * * @throws IllegalArgumentException if opName is * null. */ public RenderableOp(OperationRegistry registry, String opName, ParameterBlock pb) { this(registry, opName, pb, null); } /** * Constructs a RenderableOp given the name of the operation to be * performed and a ParameterBlock containing RenderableImage sources * and other parameters. The default operation registry * is used. Any RenderedImage sources referenced by the * ParameterBlock will be ignored. * *

      The ParameterBlock may include * DeferredData parameters. These will not be evaluated * until their values are actually required, i.e., when a rendering of * the node is requested or the renderable dimensions are queried. * * @param opName The operation name. Saved by reference. * @param pb The sources and other parameters. If null, * it is assumed that this node has no sources and parameters. * This parameter is cloned. * * @throws IllegalArgumentException if opName is null. */ public RenderableOp(String opName, ParameterBlock pb) { this(null, opName, pb); } /** * Returns the name of the RegistryMode corresponding to * this RenderableOp. This method always returns the * String "renderable". * * @since JAI 1.1 */ public String getRegistryModeName() { return RegistryMode.getMode("renderable").getName(); } /* ----- Critical attribute main accessors and mutators. ----- */ /** * Returns the OperationRegistry that is used * by this node. If the registry had not been set, the default * registry is returned. */ public synchronized OperationRegistry getRegistry() { return nodeSupport.getRegistry(); } /** * Sets the OperationRegistry that is used by * this node. If the specified registry is null, the * default registry is used. * *

      If the supplied registry does not equal the current registry, a * PropertyChangeEventJAI named "OperationRegistry" * will be fired */ public synchronized void setRegistry(OperationRegistry registry) { nodeSupport.setRegistry(registry); } /** * Returns the name of the operation this node represents as * a String. */ public String getOperationName() { return nodeSupport.getOperationName(); } /** * Sets the name of the operation this node represents. * The parameter is saved by reference. * *

      If the supplied name does not equal the current operation name, a * PropertyChangeEventJAI named "OperationName" * will be fired. * * @param opName The new operation name to be set. * * @throws IllegalArgumentException if opName is * null. */ public synchronized void setOperationName(String opName) { nodeSupport.setOperationName(opName); } /** Returns a clone of the ParameterBlock of this node. */ public ParameterBlock getParameterBlock() { return (ParameterBlock)nodeSupport.getParameterBlock().clone(); } /** * Sets the ParameterBlock of this node. * If the specified new ParameterBlock is null, * it is assumed that this node has no input sources and parameters. * The supplied parameter is cloned. * *

      This method does not validate the content of the supplied * ParameterBlock. The caller should ensure that * the sources and parameters in the ParameterBlock * are suitable for the operation this node represents; otherwise * some form of error or exception may occur at the time of rendering. * *

      If the supplied ParameterBlock does not equal the * current ParameterBlock, a * PropertyChangeEventJAI named "ParameterBlock", "Sources", * or "Parameters" will be fired. * *

      The ParameterBlock may include * DeferredData parameters. These will not be evaluated * until their values are actually required, i.e., when a rendering of * the node is requested or the renderable dimensions are queried. * * @param pb The new ParameterBlock to be set; * it may be null. */ public synchronized void setParameterBlock(ParameterBlock pb) { nodeSupport.setParameterBlock(pb == null ? new ParameterBlock() : (ParameterBlock)pb.clone()); } /** * Returns a clone of the common RenderingHints of this node * or null. * * @since JAI 1.1 */ public RenderingHints getRenderingHints() { RenderingHints hints = nodeSupport.getRenderingHints(); return hints == null ? null : (RenderingHints)hints.clone(); } /** * Sets the common RenderingHints of this node. * The supplied parameter is cloned if non-null. * *

      If the supplied RenderingHints does not equal the * current RenderingHints, a * PropertyChangeEventJAI named "RenderingHints" * will be fired. * * @param hints The new RenderingHints to be set; * it may be null. * * @since JAI 1.1 */ public synchronized void setRenderingHints(RenderingHints hints) { if(hints != null) { hints = (RenderingHints)hints.clone(); } nodeSupport.setRenderingHints(hints); } /* ----- RenderableImage methods (except properties). ----- */ private Vector getRenderableSources() { Vector sources = null; int numSrcs = nodeSupport.getParameterBlock().getNumSources(); if (numSrcs > 0) { sources = new Vector(); for (int i = 0; i < numSrcs; i++) { Object o = nodeSupport.getParameterBlock().getSource(i); if (o instanceof RenderableImage) { sources.add(o); } } } return sources; } /** * 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 sources * but none of them is a RenderableImage, or null to indicate the * image has no source of any type. * * @return a (possibly empty) Vector of RenderableImages, or null. */ public Vector getSources() { return getRenderableSources(); } /** Use registry to find an appropriate CRIF */ private synchronized ContextualRenderedImageFactory findCRIF() { if (crif == null) { // find the CRIF(JAI) from the registry. crif = CRIFRegistry.get(getRegistry(), getOperationName()); } if (crif == null) { throw new RuntimeException(JaiI18N.getString("RenderableOp2")); } return crif; } /** * Return the rendering-independent width of the image. * * @return the image width as a float. */ public float getWidth() { findCRIF(); ParameterBlock paramBlock = ImageUtil.evaluateParameters(nodeSupport.getParameterBlock()); Rectangle2D boundingBox = crif.getBounds2D(paramBlock); return (float)boundingBox.getWidth(); } /** * Return the rendering-independent height of the image. * * @return the image height as a float. */ public float getHeight() { findCRIF(); ParameterBlock paramBlock = ImageUtil.evaluateParameters(nodeSupport.getParameterBlock()); Rectangle2D boundingBox = crif.getBounds2D(paramBlock); return (float)boundingBox.getHeight(); } /** * Gets the minimum X coordinate of the rendering-independent image data. */ public float getMinX() { findCRIF(); ParameterBlock paramBlock = ImageUtil.evaluateParameters(nodeSupport.getParameterBlock()); Rectangle2D boundingBox = crif.getBounds2D(paramBlock); return (float)boundingBox.getX(); } /** * Gets the minimum Y coordinate of the rendering-independent image data. */ public float getMinY() { findCRIF(); ParameterBlock paramBlock = ImageUtil.evaluateParameters(nodeSupport.getParameterBlock()); Rectangle2D boundingBox = crif.getBounds2D(paramBlock); return (float)boundingBox.getY(); } /** * Returns a default rendering of this RenderableImage. * In all cases the area of interest will equal the image bounds. * Any hints set on the node via setRenderingHints() will * be used. * *

      The dimensions of the created RenderedImage are * determined in the following order of precedence: *

        *
      1. If a JAI.KEY_DEFAULT_RENDERING_SIZE hint is set on * the node it is used unless both its dimensions are non-positive.
      2. *
      3. The value returned by JAI.getDefaultRenderingSize() * is used unless it is null. *
      4. An identity transform from renderable to rendered coordinates * is applied. *
      * Either dimension of the default rendering size set in the hints or on * the default JAI instance may be non-positive in which case * the other dimension and the renderable aspect ratio will be used to * compute the rendered image size. * *

      This method does not validate sources and parameters supplied * in the ParameterBlock supplied at construction against * the specification of the operation this node represents. It is the * caller's responsibility to ensure that the data in the * ParameterBlock are suitable for this operation. * Otherwise, some kind of exception or error will occur. Invoking this * method will cause any DeferredData parameters to be * evaluated. * * @return The default RenderedImage. */ public RenderedImage createDefaultRendering() { // Get the default dimensions. Dimension defaultDimension = null; RenderingHints hints = nodeSupport.getRenderingHints(); if(hints != null && hints.containsKey(JAI.KEY_DEFAULT_RENDERING_SIZE)) { defaultDimension = (Dimension)hints.get(JAI.KEY_DEFAULT_RENDERING_SIZE); } if(defaultDimension == null || (defaultDimension.width <= 0 && defaultDimension.height <= 0)) { defaultDimension = JAI.getDefaultRenderingSize(); } // Initialize scale factors to represent the identify transform. double sx = 1.0; double sy = 1.0; // Reset the scale factors if a default dimension is set. if(defaultDimension != null && (defaultDimension.width > 0 || defaultDimension.height > 0)) { if(defaultDimension.width > 0 && defaultDimension.height > 0) { sx = defaultDimension.width/getWidth(); sy = defaultDimension.height/getHeight(); } else if(defaultDimension.width > 0) { sx = sy = defaultDimension.width/getWidth(); } else { // defaultDimension.height > 0 sx = sy = defaultDimension.height/getHeight(); } } // Create the renderable-to-rendered scaling. AffineTransform transform = AffineTransform.getScaleInstance(sx, sy); // Return the rendering applying the computed transform. return createRendering(new RenderContext(transform)); } /** * Gets 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. The rendering hints come from hints * passed in. These hints will be merged with any set on the node * via setRenderingHints() with the hints passed in taking * precedence. * *

      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. * *

      This method does not validate sources and parameters supplied * in the ParameterBlock supplied at construction against * the specification of the operation this node represents. It is the * caller's responsibility to ensure that the data in the * ParameterBlock are suitable for this operation. * Otherwise, some kind of exception or error will occur. Invoking this * method will cause any DeferredData parameters to be * evaluated. * * @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. * * @throws IllegalArgumentException if both w and h are zero. */ public RenderedImage createScaledRendering(int w, int h, RenderingHints hints) { if ((w == 0) && (h == 0)) { throw new IllegalArgumentException(JaiI18N.getString("RenderableOp3")); } if (w == 0) { w = Math.round(h*(getWidth()/getHeight())); } else if (h == 0) { h = Math.round(w*(getHeight()/getWidth())); } double sx = (double)w/getWidth(); double sy = (double)h/getHeight(); AffineTransform usr2dev = AffineTransform.getScaleInstance(sx, sy); RenderContext renderContext = new RenderContext(usr2dev, hints); return createRendering(renderContext); } /** * Gets a RenderedImage that represents a rendering of this image * using a given RenderContext. This is the most general way to obtain a * rendering of a RenderableImage. * *

      This method does not validate sources and parameters supplied * in the ParameterBlock supplied at construction against * the specification of the operation this node represents. It is the * caller's responsibility to ensure that the data in the * ParameterBlock are suitable for this operation. * Otherwise, some kind of exception or error will occur. Invoking this * method will cause any DeferredData parameters to be * evaluated. * *

      The RenderContext may contain a Shape * that represents the area-of-interest (aoi). If the aoi is specifed, * it is still legal to return an image that's larger than this aoi. * Therefore, by default, the aoi, if specified, is ignored at the * rendering. * *

      Any hints in the RenderContext will be merged with any * set on the node via setRenderingHints() with the hints * in the RenderContext taking precedence. * * @param renderContext the RenderContext to use to produce the rendering. * @return a RenderedImage containing the rendered data. */ public RenderedImage createRendering(RenderContext renderContext) { findCRIF(); // Clone the original ParameterBlock; if the ParameterBlock // contains RenderableImage sources, they will be replaced by // RenderedImages. ParameterBlock nodePB = nodeSupport.getParameterBlock(); Vector nodeParams = ImageUtil.evaluateParameters(nodePB.getParameters()); ParameterBlock renderedPB = new ParameterBlock((Vector)nodePB.getSources().clone(), nodeParams); Vector sources = getRenderableSources(); try { // This assumes that if there is no renderable source, that there // is a rendered source in the ParameterBlock. // If there are any hints set on the node, create a new // RenderContext which merges them with those in the RenderContext // passed in with the passed in hints taking precedence. RenderContext rcIn = renderContext; RenderingHints nodeHints = nodeSupport.getRenderingHints(); if(nodeHints != null) { RenderingHints hints = renderContext.getRenderingHints(); RenderingHints mergedHints = JAI.mergeRenderingHints(nodeHints, hints); if(mergedHints != hints) { rcIn = new RenderContext(renderContext.getTransform(), renderContext.getAreaOfInterest(), mergedHints); } } if (sources != null) { Vector renderedSources = new Vector(); for (int i = 0; i < sources.size(); i++) { RenderContext rcOut = crif.mapRenderContext(i, rcIn, renderedPB, this); RenderableImage src = (RenderableImage)sources.elementAt(i); RenderedImage renderedImage = src.createRendering(rcOut); if (renderedImage == null) { return null; } // Add this rendered image to the ParameterBlock's // list of RenderedImages. renderedSources.addElement(renderedImage); } if (renderedSources.size() > 0) { renderedPB.setSources(renderedSources); } } RenderedImage rendering = crif.create(rcIn, renderedPB); // Replace with the actual rendering if a RenderedOp. if(rendering instanceof RenderedOp) { rendering = ((RenderedOp)rendering).getRendering(); } // Copy properties to the rendered node. if(rendering != null && rendering instanceof WritablePropertySource) { String[] propertyNames = getPropertyNames(); if(propertyNames != null) { WritablePropertySource wps = (WritablePropertySource)rendering; // Save the names of rendered properties. HashSet wpsNameSet = null; String[] wpsNames = wps.getPropertyNames(); if(wpsNames != null) { wpsNameSet = new HashSet(); for(int j = 0; j < wpsNames.length; j++) { wpsNameSet.add(new CaselessStringKey(wpsNames[j])); } } // Copy any properties not already defined by the rendering. for(int j = 0; j < propertyNames.length; j++) { String name = propertyNames[j]; if(wpsNameSet == null || !wpsNameSet.contains(new CaselessStringKey(name))) { Object value = getProperty(name); if(value != null && value != java.awt.Image.UndefinedProperty) { wps.setProperty(name, value); } } } } } return rendering; } catch (ArrayIndexOutOfBoundsException e) { // This should never happen return null; } } /** * Returns false, i.e., successive renderings with the same arguments * will produce identical results. */ public boolean isDynamic() { return false; } /* ----- Property-related methods. ----- */ /** Creates a PropertySource if none exists. */ private synchronized void createPropertySource() { if (thePropertySource == null) { // Create a PropertySource encapsulating the // property environment of the node. thePropertySource = nodeSupport.getPropertySource(this, null); // Add the PropertySource to the helper object. properties.addProperties(thePropertySource); } } /** * Returns the names of properties available from this node. * These properties are a combination of those derived * from prior nodes in the imaging chain and those set locally. * * @return An array of Strings containing valid * property names or null if there are none. */ public String[] getPropertyNames () { createPropertySource(); return properties.getPropertyNames(); } /** * Returns an array of Strings recognized as names by * this property source that begin with the supplied prefix. If * no property names match, null will be returned. * The comparison is done in a case-independent manner. * * @throws IllegalArgumentException if prefix is null. * * @return an array of Strings giving the valid * property names. */ public String[] getPropertyNames(String prefix) { // This gives us a list of all non-suppressed properties return PropertyUtil.getPropertyNames(getPropertyNames(), prefix); } /** * Returns the class expected to be returned by a request for * the property with the specified name. If this information * is unavailable, null will be returned. * * @return The Class expected to be return by a * request for the value of this property or null. * @exception IllegalArgumentException if name * is null. * * @since JAI 1.1 */ public Class getPropertyClass(String name) { createPropertySource(); return properties.getPropertyClass(name); } /** * 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. * @exception IllegalArgumentException if name * is null. */ public Object getProperty(String name) { createPropertySource(); return properties.getProperty(name); } /** * Sets a local property on a node. Local property settings override * properties derived from prior nodes in the imaging chain. * *

      If the node is serialized then serializable properties will * also be serialized but non-serializable properties will be lost. * * @param name a String representing the property name. * @param value the property's value, as an Object. * @exception IllegalArgumentException if name * or value * is null. */ public void setProperty(String name, Object value) { createPropertySource(); properties.setProperty(name, value); } /** * Removes the named property from the local property * set of the RenderableOp as well as from its property * environment. * * @exception IllegalArgumentException if name * is null. * * @since JAI 1.1 */ public void removeProperty(String name) { createPropertySource(); properties.removeProperty(name); } /** * Returns the property associated with the specified property name, * or java.awt.Image.UndefinedProperty if the specified * property is not set on the image. This method is dynamic in the * sense that subsequent invocations of this method on the same object * may return different values as a function of changes in the property * environment of the node, e.g., a change in which * PropertyGenerators are registered or in the values * associated with properties of node sources. The case of the property * name passed to this method is ignored. * * @param name A String naming the property. * * @throws IllegalArgumentException if * name is null. * * @since JAI 1.1 */ public synchronized Object getDynamicProperty(String name) { createPropertySource(); return thePropertySource.getProperty(name); } /** * Adds a PropertyGenerator to the node. The property values * emitted by this property generator override any previous * definitions. * * @param pg a PropertyGenerator to be added to this node's * property environment. */ public void addPropertyGenerator(PropertyGenerator pg) { nodeSupport.addPropertyGenerator(pg); } /** * Forces a property to be copied from the specified source node. * By default, a property is copied from the first source node * that emits it. The result of specifying an invalid source is * undefined. * * @param propertyName the name of the property to be copied. * @param sourceIndex the index of the from which to copy the property. * @throws IllegalArgumentException if propertyName is * null. * * @since JAI 1.1 */ public synchronized void copyPropertyFromSource(String propertyName, int sourceIndex) { nodeSupport.copyPropertyFromSource(propertyName, sourceIndex); } /** * Removes a named property from the property environment of this * node. Unless the property is stored locally either due * to having been set explicitly via setProperty() * or to having been cached for property * synchronization purposes, subsequent calls to * getProperty(name) will return * java.awt.Image.UndefinedProperty, and name * will not appear on the list of properties emitted by * getPropertyNames(). To delete the property from the * local property set of the node, removeProperty() should * be used. * * @param name a String naming the property to be suppressed. * @throws IllegalArgumentException if * name is null. */ public void suppressProperty(String name) { nodeSupport.suppressProperty(name); } /* ----- PropertyChangeEmitter methods. ----- */ /** * Add a PropertyChangeListener to the listener list. The * listener is registered for all properties. * * @since JAI 1.1 */ public void addPropertyChangeListener(PropertyChangeListener listener) { eventManager.addPropertyChangeListener(listener); } /** * Add a PropertyChangeListener for a specific property. The * listener will be invoked only when a call on * firePropertyChange names that specific property. The case of * the name is ignored. * * @since JAI 1.1 */ public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { eventManager.addPropertyChangeListener(propertyName, listener); } /** * Remove a PropertyChangeListener from the listener list. This * removes a PropertyChangeListener that was registered for all * properties. * * @since JAI 1.1 */ public void removePropertyChangeListener(PropertyChangeListener listener) { eventManager.removePropertyChangeListener(listener); } /** * Remove a PropertyChangeListener for a specific property. The case * of the name is ignored. * * @since JAI 1.1 */ public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { eventManager.removePropertyChangeListener(propertyName, listener); } /* ----- Source Vector convenience methods. ----- */ /** * Returns one of the node's sources as an Object. * * @param index the index of the source. */ public Object getSource(int index) { Vector sources = nodeSupport.getParameterBlock().getSources(); return sources.elementAt(index); } /** * Sets one of the node's sources to an Object. * This is a convenience method that invokes * setParameterBlock() and so adheres to the same event * firing behavior. * * @param source the source, as an Object. * @param index the index of the source. * @throws IllegalArgumentException if source is * null. */ public void setSource(Object source, int index) { if (source == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0")); ParameterBlock pb = (ParameterBlock)nodeSupport.getParameterBlock().clone(); pb.setSource(source, index); nodeSupport.setParameterBlock(pb); } /** * Removes all the node's sources. * This is a convenience method that invokes * setParameterBlock() and so adheres to the same event * firing behavior. * * @since JAI 1.1 */ public void removeSources() { ParameterBlock pb = (ParameterBlock)nodeSupport.getParameterBlock().clone(); pb.removeSources(); nodeSupport.setParameterBlock(pb); } /* ----- Parameter Vector convenience methods. ----- */ /** * Returns one of the node's parameters, as a byte. * * @param index the index of the parameter. */ public byte getByteParameter(int index) { return nodeSupport.getParameterBlock().getByteParameter(index); } /** * Returns one of the node's parameters, as a char. * * @param index the index of the parameter. */ public char getCharParameter(int index) { return nodeSupport.getParameterBlock().getCharParameter(index); } /** * Returns one of the node's parameters, as a short. * * @param index the index of the parameter. */ public short getShortParameter(int index) { return nodeSupport.getParameterBlock().getShortParameter(index); } /** * Returns one of the node's parameters, as an int. * * @param index the index of the parameter. */ public int getIntParameter(int index) { return nodeSupport.getParameterBlock().getIntParameter(index); } /** * Returns one of the node's parameters, as a long. * * @param index the index of the parameter. */ public long getLongParameter(int index) { return nodeSupport.getParameterBlock().getLongParameter(index); } /** * Returns one of the node's parameters, as a float. * * @param index the index of the parameter. */ public float getFloatParameter(int index) { return nodeSupport.getParameterBlock().getFloatParameter(index); } /** * Returns one of the node's parameters, as a double. * * @param index the index of the parameter. */ public double getDoubleParameter(int index) { return nodeSupport.getParameterBlock().getDoubleParameter(index); } /** * Returns one of the node's parameters, as an Object. * * @param index the index of the parameter. */ public Object getObjectParameter(int index) { return nodeSupport.getParameterBlock().getObjectParameter(index); } /** * Sets one of the node's parameters to a byte. * This is a convenience method that invokes * setParameter(Object,int) and so adheres to the same event * firing behavior. * * @param param the parameter, as a byte. * @param index the index of the parameter. */ public void setParameter(byte param, int index) { setParameter(new Byte(param), index); } /** * Sets one of the node's parameters to a char. * This is a convenience method that invokes * setParameter(Object,int) and so adheres to the same event * firing behavior. * * @param param the parameter, as a char. * @param index the index of the parameter. */ public void setParameter(char param, int index) { setParameter(new Character(param), index); } /** * Sets one of the node's parameters to a short. * This is a convenience method that invokes * setParameter(Object,int) and so adheres to the same event * firing behavior. * * @param param the parameter, as a short. * @param index the index of the parameter. */ public void setParameter(short param, int index) { setParameter(new Short(param), index); } /** * Sets one of the node's parameters to an int. * This is a convenience method that invokes * setParameter(Object,int) and so adheres to the same event * firing behavior. * * @param param the parameter, as an int. * @param index the index of the parameter. */ public void setParameter(int param, int index) { setParameter(new Integer(param), index); } /** * Sets one of the node's parameters to a long. * This is a convenience method that invokes * setParameter(Object,int) and so adheres to the same event * firing behavior. * * @param param the parameter, as a long. * @param index the index of the parameter. */ public void setParameter(long param, int index) { setParameter(new Long(param), index); } /** * Sets one of the node's parameters to a float. * This is a convenience method that invokes * setParameter(Object,int) and so adheres to the same event * firing behavior. * * @param param the parameter, as a float. * @param index the index of the parameter. */ public void setParameter(float param, int index) { setParameter(new Float(param), index); } /** * Sets one of the node's parameters to a double. * This is a convenience method that invokes * setParameter(Object,int) and so adheres to the same event * firing behavior. * * @param param the parameter, as a double. * @param index the index of the parameter. */ public void setParameter(double param, int index) { setParameter(new Double(param), index); } /** * Sets one of the node's parameters to an Object. * This is a convenience method that invokes * setParameterBlock() and so adheres to the same event * firing behavior. * *

      The Object may be a * DeferredData instance. It will not be evaluated * until its value is actually required, i.e., when a rendering of * the node is requested or the renderable dimensions are queried. * * @param param the parameter, as an Object. * @param index the index of the parameter. */ public void setParameter(Object param, int index) { ParameterBlock pb = (ParameterBlock)nodeSupport.getParameterBlock().clone(); pb.set(param, index); nodeSupport.setParameterBlock(pb); } } jai-core-1.1.4/src/share/classes/javax/media/jai/InterpolationNearest.java0000644000175000017500000001740010203035544026353 0ustar mathieumathieu/* * $RCSfile: InterpolationNearest.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:11 $ * $State: Exp $ */ package javax.media.jai; /** * A class representing nearest-neighbor interpolation. Since * nearest-neighbor interpolation is simply pixel copying, and not * really interpolation at all, most code that performs * nearest-neighbor sampling will want to use special-purpose code. * However, this class is provided both as a way to specify such * interpolation, with the consumer making use of 'instanceof' to * detect the particular class, and as a way to force general * Interpolation users to use nearest-neighbor sampling. * * Note that this interpolator does not actually select the * "nearest" pixel, but only uses the truncated integer pixel * location (floor). This is an optimization reflecting an * assumption about the implementation of the resampler. It * is assumed that the conversion of continuous source * image coordinates to discrete pixel indices drops the final * subtraction of 0.5 for the case of a nearest-neighbor interpolator. * *

      Neighborhoods of sizes 2x1, 1x2, 2x2, 4x1, 1x4, 4x4, Nx1 and * 1xN, that is, all the interpolate() methods defined in the * Interpolation class, are supported in the interest of simplifying * code that handles a number of types of interpolation. In each * case, the central sample is returned and the rest are ignored. * *

      The class is marked 'final' so that it may be more easily inlined. */ public final class InterpolationNearest extends Interpolation { /** * Constructs an InterpolationNearest. The return * value of getSubsampleBitsH() and * getSubsampleBitsV() will be 0. */ public InterpolationNearest() { super(1, 1, 0, 0, 0, 0, 0, 0); } /** * Performs horizontal interpolation on a one-dimensional array of * integral samples. The central sample (samples[0]) is returned. */ public int interpolateH(int[] samples, int xfrac) { return samples[0]; } /** * Performs vertical interpolation on a one-dimensional array of * integral samples. The central sample (samples[0]) is returned. */ public int interpolateV(int[] samples, int yfrac) { return samples[0]; } /** * Performs interpolation on a two-dimensional array of integral samples. * The central sample (samples[0][0]) is returned. */ public int interpolate(int[][] samples, int xfrac, int yfrac) { return samples[0][0]; } /** * Performs horizontal interpolation on a pair of integral samples. * The central sample (s0) is returned. */ public int interpolateH(int s0, int s1, int xfrac) { return s0; } /** * Performs vertical interpolation on a pair of integral samples. * The central sample (s0) is returned. */ public int interpolateV(int s0, int s1, int yfrac) { return s0; } /** * Performs interpolation on a 2x2 grid of integral samples. * The central sample (s00) is returned. */ public int interpolate(int s00, int s01, int s10, int s11, int xfrac, int yfrac) { return s00; } /** * Performs interpolation on a 4x4 grid of integral samples. * The central sample (s00) is returned. */ public int interpolate(int s__, int s_0, int s_1, int s_2, int s0_, int s00, int s01, int s02, int s1_, int s10, int s11, int s12, int s2_, int s20, int s21, int s22, int xfrac, int yfrac) { return s00; } /** * Performs horizontal interpolation on a one-dimensional array of * floating-point samples. The central sample (s0) is returned. */ public float interpolateH(float[] samples, float xfrac) { return samples[0]; } /** * Performs vertical interpolation on a one-dimensional array of * floating-point samples. The central sample (s0) is returned. */ public float interpolateV(float[] samples, float yfrac) { return samples[0]; } /** * Performs interpolation on a two-dimensional array of * floating-point samples. The central sample (samples[0][0]) is * returned. */ public float interpolate(float[][] samples, float xfrac, float yfrac) { return samples[0][0]; } /** * Performs horizontal interpolation on a pair of floating-point * samples. The central sample (s0) is returned. */ public float interpolateH(float s0, float s1, float xfrac) { return s0; } /** * Performs vertical interpolation on a pair of floating-point * samples. The central sample (s0) is returned. */ public float interpolateV(float s0, float s1, float yfrac) { return s0; } /** * Performs interpolation on a 2x2 grid of floating-point samples. * The central sample (s00) is returned. */ public float interpolate(float s00, float s01, float s10, float s11, float xfrac, float yfrac) { return s00; } /** * Performs interpolation on a 4x4 grid of floating-point samples. * The central sample (s00) is returned. */ public float interpolate(float s__, float s_0, float s_1, float s_2, float s0_, float s00, float s01, float s02, float s1_, float s10, float s11, float s12, float s2_, float s20, float s21, float s22, float xfrac, float yfrac) { return s00; } /** * Performs horizontal interpolation on a one-dimensional array of * double samples. The central sample (s0) is returned. */ public double interpolateH(double[] samples, float xfrac) { return samples[0]; } /** * Performs vertical interpolation on a one-dimensional array of * double samples. The central sample (s0) is returned. */ public double interpolateV(double[] samples, float yfrac) { return samples[0]; } /** * Performs interpolation on a two-dimensional array of * double samples. The central sample (samples[0][0]) is * returned. */ public double interpolate(double[][] samples, float xfrac, float yfrac) { return samples[0][0]; } /** * Performs horizontal interpolation on a pair of double * samples. The central sample (s0) is returned. */ public double interpolateH(double s0, double s1, float xfrac) { return s0; } /** * Performs vertical interpolation on a pair of double * samples. The central sample (s0) is returned. */ public double interpolateV(double s0, double s1, float yfrac) { return s0; } /** * Performs interpolation on a 2x2 grid of double samples. * The central sample (s00) is returned. */ public double interpolate(double s00, double s01, double s10, double s11, float xfrac, float yfrac) { return s00; } /** * Performs interpolation on a 4x4 grid of double samples. * The central sample (s00) is returned. */ public double interpolate(double s__, double s_0, double s_1, double s_2, double s0_, double s00, double s01, double s02, double s1_, double s10, double s11, double s12, double s2_, double s20, double s21, double s22, float xfrac, float yfrac) { return s00; } } jai-core-1.1.4/src/share/classes/javax/media/jai/AttributedImageCollection.java0000644000175000017500000002235410203035544027274 0ustar mathieumathieu/* * $RCSfile: AttributedImageCollection.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:04 $ * $State: Exp $ */ package javax.media.jai; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.Set; import java.util.HashSet; /** * Class representing a CollectionImage wherein all image elements are * AttributedImage instances. All Collection methods will be overridden * such that contained images are forced to be AttributedImages. * *

      Note that the methods getAll(attribute) and * removeAll(attribute) use the equals() method * of the attribute parameter rather that that of the * AttributedImages in the Collection. This * permits "filtering" if the attribute of the AttributedImages * contains more than one type of value. For example, if the attribute * contained both position and time, then the parameter attribute * could be an instance of a class which compared only the position if it * were desired to obtain or remove all images at a given position * irrespective of the time stamp. * * @since JAI 1.1 */ public class AttributedImageCollection extends CollectionImage { protected AttributedImageCollection() { } /** * Constructs an AttributedImageCollection with contents * set to the contents of the supplied Collection. Only * elements in the Collection which are instances of * AttributedImage will be added. * * @throws IllegalArgumentException if images is null */ public AttributedImageCollection(Collection images) { super(); if ( images == null ) { throw new IllegalArgumentException(JaiI18N.getString("AttributedImageCollection0")); } try { // Try to create a Collection of the same class. imageCollection = (Collection)images.getClass().newInstance(); } catch(Exception e) { // As a fallback create a List. imageCollection = new ArrayList(images.size()); } // only add AttributedImages which have not yet been added Iterator iter = images.iterator(); while( iter.hasNext() ) { Object o = iter.next(); if ( o instanceof AttributedImage && !imageCollection.contains(o)) { imageCollection.add( o); } } } /** * Returns a Set of all AttributedImages the attribute of which is * equal to the parameter object according to the equals() method of * the parameter object. If no match is found null will be returned. * If the parameter is null a Set view of all AttributedImages in the * Collection will be returned. */ public Set getAll(Object attribute) { if ( attribute == null ) { return (Set) imageCollection; } else { HashSet set = null; Iterator iter = iterator(); while( iter.hasNext() ) { AttributedImage ai = (AttributedImage)iter.next(); if ( attribute.equals(ai.getAttribute()) ) { if ( set == null ) { set = new HashSet(); } set.add(ai); } } return set; } } /** * Returns a Set of all AttributedImages the image of which is equal * to the parameter image. If no match is found null will be returned. * If the parameter is null a Set view of all AttributedImages in the * Collection will be returned. */ public Set getAll(PlanarImage image) { if ( image == null ) { return (Set)imageCollection; } else { HashSet set = null; Iterator iter = iterator(); while( iter.hasNext() ) { AttributedImage ai = (AttributedImage)iter.next(); if ( image.equals(ai.getImage()) ) { if ( set == null ) { set = new HashSet(); } set.add(ai); } } return set; } } /** * Removes all AttributedImages the attribute of which is * equal to the parameter object according to the equals() method of the * parameter object. The returned value contains all AttributedImages * which were removed from the underlying Collection or null if no * match was found. If the parameter is null, null will be returned. */ public Set removeAll(Object attribute) { if ( attribute == null ) { return null; } else { Iterator iter = iterator(); Set removed = null; while( iter.hasNext() ) { AttributedImage ai = (AttributedImage)iter.next(); if ( attribute.equals(ai.getAttribute()) ) { iter.remove(); if(removed == null) { removed = new HashSet(); } removed.add(ai); } } return (Set)removed; } } /** * Removes all AttributedImages the image of which is equal to the * parameter image. The returned value contains all AttributedImages * which were removed from the underlying Collection or null if no * match was found. If the parameter is null, null will be returned. */ public Set removeAll(PlanarImage image) { if ( image == null ) { return null; } else { Iterator iter = iterator(); Set removed = null; while( iter.hasNext() ) { AttributedImage ai = (AttributedImage)iter.next(); if ( image.equals(ai.getImage()) ) { iter.remove(); if(removed == null) { removed = new HashSet(); } removed.add(ai); } } return (Set)removed; } } /* -- CollectionImage methods: ensure elements are AttributedImages. -- */ /** * Adds the specified object to this Collection. This * method overrides the superclass method in order to perform a * type check on the object being added. * * @throws IllegalArgumentException if o is null * or is not an AttributedImage. * * @return true if and only if the parameter is added to the * Collection. */ public boolean add(Object o) { if ( o == null || !(o instanceof AttributedImage) ) { throw new IllegalArgumentException(JaiI18N.getString("AttributedImageCollection1")); } // don't add an object that's there already if(imageCollection.contains(o)) { return false; } return imageCollection.add(o); } /** * Adds to this Collection all elements in the specified * Collection which are AttributedImages. * * @return true if this Collection changed * as a result of the call. */ public boolean addAll(Collection c) { if ( c == null ) return false; // iterate over collection Iterator iter = c.iterator(); boolean flag = false; while( iter.hasNext() ) { Object o = iter.next(); // only add AttributedImages which have not yet been added if ( o instanceof AttributedImage ) { if( !imageCollection.contains(o) && imageCollection.add(o) ) { flag = true; // one shot switch } } } return flag; } /** * Returns the first attributed image found in the collection * that contains the planar image argument. If the parameter is * null, null will be returned. */ public AttributedImage getAttributedImage(PlanarImage image) { if ( image == null ) { return null; } else { Iterator iter = iterator(); while( iter.hasNext() ) { AttributedImage ai = (AttributedImage)iter.next(); if ( image.equals(ai.getImage()) ) { return ai; } } } return null; } /** * Returns the first attributed image found in the collection * that contains the attribute. If the parameter is * null, null will be returned. */ public AttributedImage getAttributedImage(Object attribute) { if ( attribute == null ) { return null; } else { Iterator iter = iterator(); while( iter.hasNext() ) { AttributedImage ai = (AttributedImage)iter.next(); if ( attribute.equals(ai.getAttribute()) ) { return ai; } } } return null; } } jai-core-1.1.4/src/share/classes/javax/media/jai/PropertyGeneratorFromSource.java0000644000175000017500000000375410203035544027711 0ustar mathieumathieu/* * $RCSfile: PropertyGeneratorFromSource.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:17 $ * $State: Exp $ */ package javax.media.jai; import java.util.Vector; import com.sun.media.jai.util.PropertyGeneratorImpl; /** * A class that implements the PropertyGenerator interface. * This class is used when a property is to be calculated from a particular * source. All properties except the named one are ignored. If the given * source index out of range the property will be undefined, in particular * no exception will be thrown. * */ class PropertyGeneratorFromSource extends PropertyGeneratorImpl { int sourceIndex; String propertyName; PropertyGeneratorFromSource(int sourceIndex, String propertyName) { super(new String[] {propertyName}, new Class[] {Object.class}, // could be anything new Class[] {OperationNode.class}); if(propertyName == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } this.sourceIndex = sourceIndex; this.propertyName = propertyName; } public Object getProperty(String name, Object opNode) { if(name == null || opNode == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if(sourceIndex >= 0 && opNode instanceof OperationNode && propertyName.equalsIgnoreCase(name)) { OperationNode op = (OperationNode)opNode; Vector sources = op.getParameterBlock().getSources(); if(sources != null && sourceIndex < sources.size()) { Object src = sources.elementAt(sourceIndex); if(src instanceof PropertySource) { return ((PropertySource)src).getProperty(name); } } } return java.awt.Image.UndefinedProperty; } } jai-core-1.1.4/src/share/classes/javax/media/jai/DeferredProperty.java0000644000175000017500000001531410203035544025471 0ustar mathieumathieu/* * $RCSfile: DeferredProperty.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:07 $ * $State: Exp $ */ package javax.media.jai; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.Arrays; /** * A subclass of DeferredData to be used to wrap JAI property * values which will be computed at a later time. For example, an instance * of this class could be used to wrap a property emitted by an operation * node so that the actual computation of the property value was deferred * until it was actually needed. * * @see DeferredData * @see RenderedOp * * @since JAI 1.1 */ public class DeferredProperty extends DeferredData implements PropertyChangeListener { /** * The PropertySource from which the value of the named * property is to be drawn. */ protected transient PropertySource propertySource; /** * The name of the property the value of which is to be obtained. */ protected String propertyName; /** * Creates a DeferredProperty. If the specified * PropertySource is a PropertyChangeEmitter, * then this DeferredProperty object is registered as a * PropertyChangeListener of the PropertySource. * * @exception IllegalArgumentException if a parameter is null * or if the propertyName is not among those * emitted by the PropertySource. */ public DeferredProperty(PropertySource propertySource, String propertyName, Class propertyClass) { super(propertyClass); if(propertySource == null || propertyName == null) { throw new IllegalArgumentException(JaiI18N.getString("DeferredData0")); } String[] propertyNames = propertySource.getPropertyNames(); boolean isPropertyEmitted = false; if(propertyNames != null) { int length = propertyNames.length; for(int i = 0; i < length; i++) { if(propertyName.equalsIgnoreCase(propertyNames[i])) { isPropertyEmitted = true; break; } } } if(!isPropertyEmitted) { throw new IllegalArgumentException(JaiI18N.getString("DeferredProperty0")); } if(propertySource instanceof PropertyChangeEmitter) { PropertyChangeEmitter pce = (PropertyChangeEmitter)propertySource; pce.addPropertyChangeListener(propertyName, this); } this.propertySource = propertySource; this.propertyName = propertyName; } /** * Returns the PropertySource of the property value. */ public PropertySource getPropertySource() { return propertySource; } /** * Returns the name of the property the value of which is to be obtained. */ public String getPropertyName() { return propertyName; } /** * Returns the value of the image property associated with the * image and property name specified at construction. */ protected Object computeData() { return propertySource.getProperty(propertyName); } /** * Tests whether the parameter equals this object. Equality obtains * if the parameter object is a DeferredProperty and * the respective propertySource and * dataClass variables are equal according to their * respective equals() methods and the * propertyNames are equal ignoring case. The wrapped * data object is not tested unless the isValid() of * both objects returns true because requesting it via * getData() may provoke computation of a deferred quantity. */ public boolean equals(Object obj) { if(obj == null || !(obj instanceof DeferredProperty)) { return false; } DeferredProperty dp = (DeferredProperty)obj; return propertyName.equalsIgnoreCase(dp.getPropertyName()) && propertySource.equals(dp.getPropertySource()) && (!isValid() || !dp.isValid() || data.equals(dp.getData())); } /** * Returns a hash code value for the object. */ public int hashCode() { return propertySource.hashCode() ^ propertyName.toLowerCase().hashCode(); } /** * The implementation of PropertyChangeListener. This * method responds to certain PropertyChangeEvents generated * by the PropertySource used to construct this object. * *

      If the PropertyChangeEvent is named "Rendering" and is * an instance of javax.media.jai.RenderingChangeEvent, then * the source of the event is checked to determine whether it equals the * PropertySource used to construct this object. If this test * is passed then the PropertySource is a * RenderedOp the rendering of which has changed. Therefore * setData() will be invoked with a null argument. This will indicate to * any registered observers that the property data should be re-requested * by invoking getData() on this object. * *

      If the PropertyChangeEvent was generated by the * PropertySource used to construct this object, has name * equal to the name of the deferred property, and is an instance of * PropertySourceChangeEvent, then the value returned by * the getNewValue() method of the event object will be * passed to setData() unless getNewValue() * returns java.awt.Image.UndefinedProperty in which case * null will be passed to setData(). Registered * observers will be notified according to the specification of * setData(). */ public void propertyChange(PropertyChangeEvent evt) { if(evt.getSource() == propertySource) { if(evt instanceof RenderingChangeEvent) { setData(null); } else if(evt instanceof PropertySourceChangeEvent && propertyName.equalsIgnoreCase(evt.getPropertyName())) { Object newValue = evt.getNewValue(); setData(newValue == java.awt.Image.UndefinedProperty ? null : newValue); } } } } jai-core-1.1.4/src/share/classes/javax/media/jai/CollectionChangeEvent.java0000644000175000017500000000126110203035544026403 0ustar mathieumathieu/* * $RCSfile: CollectionChangeEvent.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:05 $ * $State: Exp $ */ package javax.media.jai; import java.util.Collection; /** * Class representing an event generated by a CollectionOp * when the wrapped Collection is regenerated. * * @since JAI 1.1 */ public class CollectionChangeEvent extends PropertyChangeEventJAI { public CollectionChangeEvent(CollectionOp source, Collection oldValue, Collection newValue) { super(source, "Collection", oldValue, newValue); } } jai-core-1.1.4/src/share/classes/javax/media/jai/ParameterList.java0000644000175000017500000002605410203035544024763 0ustar mathieumathieu/* * $RCSfile: ParameterList.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:14 $ * $State: Exp $ */ package javax.media.jai; /** * An interface to represent a list of parameter name-value pairs. * *

      All comparisons using Strings are done in a case * insensitive (but retentive) manner. * * @see ParameterListDescriptor * * @since JAI 1.1 */ public interface ParameterList { /** * Returns the associated ParameterListDescriptor. */ public ParameterListDescriptor getParameterListDescriptor(); /** * Sets a named parameter to a byte value. * * Implementing classes are free but not required to check class type, * ranges, and enumeration types. * * @param paramName a String naming a parameter. * @param b a byte value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. */ public ParameterList setParameter(String paramName, byte b); /** * Sets a named parameter to a boolean value. * * Implementing classes are free but not required to check class type, * ranges, and enumeration types. * * @param paramName a String naming a parameter. * @param b a boolean value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. */ public ParameterList setParameter(String paramName, boolean b); /** * Sets a named parameter to a char value. * * Implementing classes are free but not required to check class type, * ranges, and enumeration types. * * @param paramName a String naming a parameter. * @param c a char value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. */ public ParameterList setParameter(String paramName, char c); /** * Sets a named parameter to a short value. * * Implementing classes are free but not required to check class type, * ranges, and enumeration types. * * @param paramName a String naming a parameter. * @param s a short value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. */ public ParameterList setParameter(String paramName, short s); /** * Sets a named parameter to an int value. * * Implementing classes are free but not required to check class type, * ranges, and enumeration types. * * @param paramName a String naming a parameter. * @param i an int value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. */ public ParameterList setParameter(String paramName, int i); /** * Sets a named parameter to a long value. * * Implementing classes are free but not required to check class type, * ranges, and enumeration types. * * @param paramName a String naming a parameter. * @param l a long value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. */ public ParameterList setParameter(String paramName, long l); /** * Sets a named parameter to a float value. * * Implementing classes are free but not required to check class type, * ranges, and enumeration types. * * @param paramName a String naming a parameter. * @param f a float value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. */ public ParameterList setParameter(String paramName, float f); /** * Sets a named parameter to a double value. * * Implementing classes are free but not required to check class type, * ranges, and enumeration types. * * @param paramName a String naming a parameter. * @param d a double value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. */ public ParameterList setParameter(String paramName, double d); /** * Sets a named parameter to an Object value. * * Implementing classes are free but not required to check class type, * ranges, and enumeration types. * * @param paramName a String naming a parameter. * @param obj an Object value for the parameter. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * pointed to by the paramName. */ public ParameterList setParameter(String paramName, Object obj); /** * Gets a named parameter as an Object. Parameters * belonging to a primitive type, such as int, will be returned as a * member of the corresponding wrapper class, such as * Integer. * * @param paramName the name of the parameter to be returned. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws IllegalStateException if the parameter value is still * ParameterListDescriptor.NO_PARAMETER_DEFAULT */ public Object getObjectParameter(String paramName); /** * A convenience method to return a parameter as a byte. * * @param paramName the name of the parameter to be returned. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws ClassCastException if the parameter is of a different type. * @throws IllegalStateException if the parameter value is still * ParameterListDescriptor.NO_PARAMETER_DEFAULT */ public byte getByteParameter(String paramName); /** * A convenience method to return a parameter as a boolean. * * @param paramName the name of the parameter to be returned. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws ClassCastException if the parameter is of a different type. * @throws IllegalStateException if the parameter value is still * ParameterListDescriptor.NO_PARAMETER_DEFAULT */ public boolean getBooleanParameter(String paramName); /** * A convenience method to return a parameter as a char. * * @param paramName the name of the parameter to be returned. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws ClassCastException if the parameter is of a different type. * @throws IllegalStateException if the parameter value is still * ParameterListDescriptor.NO_PARAMETER_DEFAULT */ public char getCharParameter(String paramName); /** * A convenience method to return a parameter as a short. * * @param paramName the name of the parameter to be returned. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws ClassCastException if the parameter is of a different type. * @throws IllegalStateException if the parameter value is still * ParameterListDescriptor.NO_PARAMETER_DEFAULT */ public short getShortParameter(String paramName); /** * A convenience method to return a parameter as an int. * * @param paramName the name of the parameter to be returned. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws ClassCastException if the parameter is of a different type. * @throws IllegalStateException if the parameter value is still * ParameterListDescriptor.NO_PARAMETER_DEFAULT */ public int getIntParameter(String paramName); /** * A convenience method to return a parameter as a long. * * @param paramName the name of the parameter to be returned. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws ClassCastException if the parameter is of a different type. * @throws IllegalStateException if the parameter value is still * ParameterListDescriptor.NO_PARAMETER_DEFAULT */ public long getLongParameter(String paramName); /** * A convenience method to return a parameter as a float. * * @param paramName the name of the parameter to be returned. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws ClassCastException if the parameter is of a different type. * @throws IllegalStateException if the parameter value is still * ParameterListDescriptor.NO_PARAMETER_DEFAULT */ public float getFloatParameter(String paramName); /** * A convenience method to return a parameter as a double. * * @param paramName the name of the parameter to be returned. * * @throws IllegalArgumentException if paramName is null. * @throws IllegalArgumentException if there is no parameter with the * specified name. * @throws ClassCastException if the parameter is of a different type. * @throws IllegalStateException if the parameter value is still * ParameterListDescriptor.NO_PARAMETER_DEFAULT */ public double getDoubleParameter(String paramName); } jai-core-1.1.4/src/share/classes/javax/media/jai/TiledImage.java0000644000175000017500000020456310666040416024225 0ustar mathieumathieu/* * $RCSfile: TiledImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2007-08-31 16:25:50 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.Area; import java.awt.geom.PathIterator; import java.awt.image.BandedSampleModel; import java.awt.image.ColorModel; import java.awt.image.ComponentSampleModel; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.TileObserver; import java.awt.image.WritableRaster; import java.awt.image.WritableRenderedImage; import java.awt.image.renderable.ParameterBlock; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.Enumeration; import java.util.Iterator; import java.util.LinkedList; import java.util.Vector; import com.sun.media.jai.util.JDKWorkarounds; /** * A concrete implementation of WritableRenderedImage. * *

      TiledImage is the main class for writable images * in JAI. TiledImage provides a straightforward * implementation of the WritableRenderedImage interface, * taking advantage of that interface's ability to describe images * with multiple tiles. The tiles of a * WritableRenderedImage must share a * SampleModel, which determines their width, height, and * pixel format. The tiles form a regular grid, which may occupy any * rectangular region of the plane. Tile pixels the locations of which * lie outside the stated image bounds have undefined values. * *

      The contents of a TiledImage are defined by a * single RenderedImage source provided by means of one of * the set() methods or to a constructor which accepts a * RenderedImage. The set() methods provide * a way to selectively overwrite a portion of a TiledImage, * possibly using a region of interest (ROI). * *

      TiledImage also supports direct manipulation of * pixels by means of the getWritableTile() method. This * method returns a WritableRaster that can be modified directly. * Such changes become visible to readers according to the regular * thread synchronization rules of the Java virtual machine; JAI makes * no additional guarantees. When a writer is finished modifying a * tile, it should call releaseWritableTile(). A * shortcut is to call setData(), which copies a rectangular * region or an area specified by a ROI from a supplied * Raster directly into the TiledImage. * *

      A final way to modify the contents of a TiledImage * is through calls to the object returned by createGraphics(). * This returns a Graphics2D object that can be used to draw * line art, text, and images in the usual Abstract Window Toolkit (AWT) * manner. * *

      A TiledImage does not attempt to maintain * synchronous state on its own. That task is left to * SnapshotImage. If a synchronous (unchangeable) view * of a TiledImage is desired, its * createSnapshot() method must be used. Otherwise, * changes due to calls to set() or direct writing of tiles by objects * that call getWritableTile() will be visible. * *

      TiledImage does not actually cause its tiles to be * copied from the specified source until their contents are demanded. * Once a tile has been computed, its contents may be discarded if it can * be determined that it can be recomputed identically from the source. * The lockTile() method forces a tile to be computed and * maintained for the lifetime of the TiledImage. * * @see SnapshotImage * @see java.awt.image.RenderedImage * @see java.awt.image.WritableRenderedImage * */ public class TiledImage extends PlanarImage implements WritableRenderedImage, PropertyChangeListener { /** The number of tiles in the X direction. */ protected int tilesX; /** The number of tiles in the Y direction. */ protected int tilesY; /** The index of the leftmost column of tiles. */ protected int minTileX; /** The index of the uppermost row of tiles. */ protected int minTileY; /** The tile array. */ protected WritableRaster[][] tiles; /** The number of writers of each tile; -1 indicates a locked tile. */ protected int[][] writers; /** The current set of TileObservers. */ protected Vector tileObservers = null; /** Whether DataBuffers are shared with the source image. */ private boolean areBuffersShared = false; /** The parent TiledImage if this one was created using getSubImage(). */ private TiledImage parent = null; /** The SampleModel of the TiledImage ancestor. */ private SampleModel ancestorSampleModel = null; /** The sub-banding list with respect to the ancestor. */ private int[] bandList = null; /** The number of writable tiles; shared with all ancestors. */ private int[] numWritableTiles = null; /** The ROI to be used with the source image of uncomputed tiles. */ private ROI srcROI = null; /** The bounds of the intersection of the source image bounds with those of this image and with the source ROI if present. */ private Rectangle overlapBounds = null; /** * Derives a SampleModel with the specified dimensions * from the input SampleModel. If the input * SampleModel already has these dimensions, it is * used directly; otherwise a new SampleModel of the * required dimensions is derived and returned. */ private static SampleModel coerceSampleModel(SampleModel sampleModel, int sampleModelWidth, int sampleModelHeight) { return (sampleModel.getWidth() == sampleModelWidth && sampleModel.getHeight() == sampleModelHeight) ? sampleModel : sampleModel.createCompatibleSampleModel(sampleModelWidth, sampleModelHeight); } /* * Derives the values of minTileX, minTileY, tilesX, and tilesY * from minX, minY, width, height, tileGridXOffset, tileGridYOffset, * tileWidth, and tileHeight. If the image has a parent, its tile * grid minima are set to those of the parent. * * @param parent The parent TiledImage. */ private void initTileGrid(TiledImage parent) { if(parent != null) { this.minTileX = parent.minTileX; this.minTileY = parent.minTileY; } else { this.minTileX = getMinTileX(); this.minTileY = getMinTileY(); } int maxTileX = getMaxTileX(); int maxTileY = getMaxTileY(); this.tilesX = maxTileX - minTileX + 1; this.tilesY = maxTileY - minTileY + 1; } /** * Constructs a TiledImage with a given layout, * SampleModel, and ColorModel. The * width and height of the image tiles will be respectively equal * to the width and height of the SampleModel. The * tileFactory instance variable will be set to the * value of the JAI.KEY_TILE_FACTORY hint set on * the default instance of JAI. * * @param minX The X coordinate of the upper-left pixel * @param minY The Y coordinate of the upper-left pixel. * @param width The width of the image. * @param height The height of the image. * @param tileGridXOffset The X coordinate of the upper-left * pixel of tile (0, 0). * @param tileGridYOffset The Y coordinate of the upper-left * pixel of tile (0, 0). * @param tileSampleModel A SampleModel with which to be * compatible. * @param colorModel A ColorModel to associate with the * image. */ public TiledImage(int minX, int minY, int width, int height, int tileGridXOffset, int tileGridYOffset, SampleModel tileSampleModel, ColorModel colorModel) { this(null, minX, minY, width, height, tileGridXOffset, tileGridYOffset, tileSampleModel, colorModel); } /** * Constructs a child TiledImage. If the parent is null it is a root * TiledImage and all instance variables will be allocated. */ private TiledImage(TiledImage parent, int minX, int minY, int width, int height, int tileGridXOffset, int tileGridYOffset, SampleModel sampleModel, ColorModel colorModel) { super(new ImageLayout(minX, minY, width, height, tileGridXOffset, tileGridYOffset, sampleModel.getWidth(), sampleModel.getHeight(), sampleModel, colorModel), null, null); initTileGrid(parent); if(parent == null) { this.tiles = new WritableRaster[tilesX][tilesY]; this.writers = new int[tilesX][tilesY]; tileObservers = new Vector(); numWritableTiles = new int[1]; numWritableTiles[0] = 0; ancestorSampleModel = sampleModel; } else { this.parent = parent; this.tiles = parent.tiles; this.writers = parent.writers; tileObservers = parent.tileObservers; numWritableTiles = parent.numWritableTiles; ancestorSampleModel = parent.ancestorSampleModel; } tileFactory = (TileFactory)JAI.getDefaultInstance().getRenderingHint( JAI.KEY_TILE_FACTORY); } /** * Constructs a TiledImage with a * SampleModel that is compatible with a given * SampleModel, and given tile dimensions. The width * and height are taken from the SampleModel, and the * image begins at a specified point. The ColorModel * will be derived from the SampleModel using the * createColorModel method of PlanarImage. * Note that this implies that the ColorModel could be * null. * * @param origin A Point indicating the image's upper * left corner. * @param sampleModel A SampleModel with which to be * compatible. * @param tileWidth The desired tile width. * @param tileHeight The desired tile height. * * @deprecated as of JAI 1.1. */ public TiledImage(Point origin, SampleModel sampleModel, int tileWidth, int tileHeight) { this(origin.x, origin.y, sampleModel.getWidth(), sampleModel.getHeight(), origin.x, origin.y, coerceSampleModel(sampleModel, tileWidth, tileHeight), PlanarImage.createColorModel(sampleModel)); } /** * Constructs a TiledImage starting at the global * coordinate origin. The ColorModel * will be derived from the SampleModel using the * createColorModel method of PlanarImage. * Note that this implies that the ColorModel could be * null. * * @param sampleModel A SampleModel with which to be * compatible. * @param tileWidth The desired tile width. * @param tileHeight The desired tile height. * * @deprecated as of JAI 1.1. */ public TiledImage(SampleModel sampleModel, int tileWidth, int tileHeight) { this(0, 0, sampleModel.getWidth(), sampleModel.getHeight(), 0, 0, coerceSampleModel(sampleModel, tileWidth, tileHeight), PlanarImage.createColorModel(sampleModel)); } /** * Constructs a TiledImage equivalent to a given * RenderedImage but with specific tile dimensions. * Actual copying of the pixel data from the RenderedImage * will be deferred until the first time they are requested from the * TiledImage. * * @param source The source RenderedImage. * @param tileWidth The desired tile width. * @param tileHeight The desired tile height. * * @since JAI 1.1 */ public TiledImage(RenderedImage source, int tileWidth, int tileHeight) { this(source.getMinX(), source.getMinY(), source.getWidth(), source.getHeight(), source.getTileGridXOffset(), source.getTileGridYOffset(), coerceSampleModel(source.getSampleModel(), tileWidth, tileHeight), source.getColorModel()); set(source); } /** * Constructs a TiledImage equivalent to a given * RenderedImage. Actual copying of the pixel data from * the RenderedImage will be deferred until the first time * they are requested from the TiledImage. The tiles of * the TiledImage may optionally share * DataBuffers with the tiles of the source image but it * should be realized in this case that data written into the * TiledImage will be visible in the source image. * * @param source The source RenderedImage. * @param areBuffersShared Whether the tile DataBuffers * of the source are re-used in the tiles of * this image. If false new * WritableRasters will be * created. * * @since JAI 1.1 */ public TiledImage(RenderedImage source, boolean areBuffersShared) { this(source, source.getTileWidth(), source.getTileHeight()); // Do not re-use source tiles if the source does not compute unique tiles. RenderedImage sourceRendering = source instanceof RenderedOp ? ((RenderedOp)source).getRendering() : source; boolean suppressBufferSharing = sourceRendering instanceof OpImage && !((OpImage)sourceRendering).computesUniqueTiles(); this.areBuffersShared = areBuffersShared && !suppressBufferSharing; } /** * Returns a TiledImage making use of an * interleaved SampleModel with a given layout, * number of bands, and data type. The ColorModel * will be derived from the SampleModel using the * createColorModel method of PlanarImage. * Note that this implies that the ColorModel could be * null. * * @param minX The X coordinate of the upper-left pixel * @param minY The Y coordinate of the upper-left pixel. * @param width The width of the image. * @param height The height of the image. * @param numBands The number of bands in the image. * @param dataType The data type, from among the constants * DataBuffer.TYPE_*. * @param tileWidth The tile width. * @param tileHeight The tile height. * @param bandOffsets An array of non-duplicated integers between 0 and * numBands - 1 of length numBands * indicating the relative offset of each band. * * @deprecated as of JAI 1.1. */ public static TiledImage createInterleaved(int minX, int minY, int width, int height, int numBands, int dataType, int tileWidth, int tileHeight, int[] bandOffsets) { SampleModel sm = RasterFactory.createPixelInterleavedSampleModel(dataType, tileWidth, tileHeight, numBands, numBands*tileWidth, bandOffsets); return new TiledImage(minX, minY, width, height, minX, minY, sm, PlanarImage.createColorModel(sm)); } /** * Returns a TiledImage making use of an * banded SampleModel with a given layout, * number of bands, and data type. The ColorModel * will be derived from the SampleModel using the * createColorModel method of PlanarImage. * Note that this implies that the ColorModel could be * null. * * @param minX The X coordinate of the upper-left pixel * @param minY The Y coordinate of the upper-left pixel. * @param width The width of the image. * @param height The height of the image. * @param dataType The data type, from among the constants * DataBuffer.TYPE_*. * @param tileWidth The tile width. * @param tileHeight The tile height. * @param bankIndices An array of ints indicating the * index of the bank to use for each band. Bank indices * may be duplicated. * @param bandOffsets An array of integers indicating the starting * offset of each band within its bank. Bands stored in * the same bank must have sufficiently different offsets * so as not to overlap. * * @deprecated as of JAI 1.1. */ public static TiledImage createBanded(int minX, int minY, int width, int height, int dataType, int tileWidth, int tileHeight, int[] bankIndices, int[] bandOffsets) { SampleModel sm = new BandedSampleModel(dataType, tileWidth, tileHeight, tileWidth, bankIndices, bandOffsets); return new TiledImage(minX, minY, width, height, minX, minY, sm, PlanarImage.createColorModel(sm)); } /** * Overlays a rectangular area of pixels from an image onto a tile. * * @param tile * @param im * @param rect */ private void overlayPixels(WritableRaster tile, RenderedImage im, Rectangle rect) { // Create a child of tile occupying the intersection area WritableRaster child = tile.createWritableChild(rect.x, rect.y, rect.width, rect.height, rect.x, rect.y, bandList); im.copyData(child); } /** * Overlays a set of pixels described by an Area from an image * onto a tile. * * @param tile * @param im * @param a */ private void overlayPixels(WritableRaster tile, RenderedImage im, Area a) { ROIShape rs = new ROIShape(a); Rectangle bounds = rs.getBounds(); LinkedList rectList = rs.getAsRectangleList(bounds.x, bounds.y, bounds.width, bounds.height); int numRects = rectList.size(); for(int i = 0; i < numRects; i++) { Rectangle rect = (Rectangle)rectList.get(i); WritableRaster child = tile.createWritableChild(rect.x, rect.y, rect.width, rect.height, rect.x, rect.y, bandList); im.copyData(child); } } /** * Overlays a set of pixels described by a bitmask * onto a tile. */ private void overlayPixels(WritableRaster tile, RenderedImage im, Rectangle rect, int[][] bitmask) { Raster r = im.getData(rect); // If this is sub-banded child image, create a child of the // tile into which to write. if(bandList != null) { tile = tile.createWritableChild(rect.x, rect.y, rect.width, rect.height, rect.x, rect.y, bandList); } // Create a buffer suitable for transferring pixels Object data = r.getDataElements(rect.x, rect.y, null); // The bitmask passed in might have undefined values outside // the specified rect - therefore make sure that those bits // are ignored. int leftover = rect.width % 32; int bitWidth = ((rect.width+31)/32) - (leftover > 0 ? 1 : 0); int y = rect.y; for (int j = 0; j < rect.height; j++, y++) { int[] rowMask = bitmask[j]; int i, x = rect.x; for (i = 0; i < bitWidth; i++) { int mask32 = rowMask[i]; int bit = 0x80000000; for (int b = 0; b < 32; b++, x++) { if ((mask32 & bit) != 0) { r.getDataElements(x, y, data); tile.setDataElements(x, y, data); } bit >>>= 1; } } if (leftover > 0) { int mask32 = rowMask[i]; int bit = 0x80000000; for (int b = 0; b < leftover; b++, x++) { if ((mask32 & bit) != 0) { r.getDataElements(x, y, data); tile.setDataElements(x, y, data); } bit >>>= 1; } } } } /** * Overlays a given RenderedImage on top of the * current contents of the TiledImage. The source * image must have a SampleModel compatible with that * of this image. If the source image does not overlap this image * then invoking this method will have no effect. * *

      The source image is added as a fallback PropertySource * for the TiledImage: if a given property is not set directly * on the TiledImage an attempt will be made to obtain its * value from the source image. * * @param im A RenderedImage source to overlay. * * @throws IllegalArgumentException if im is * null. */ public void set(RenderedImage im) { if ( im == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } // Same source: do nothing. if(getNumSources() > 0 && im == getSourceImage(0)) { return; } Rectangle imRect = new Rectangle(im.getMinX(), im.getMinY(), im.getWidth(), im.getHeight()); // Return if the source image does not overlap this image. if((imRect = imRect.intersection(getBounds())).isEmpty()) { return; } // Unset buffer sharing flag. areBuffersShared = false; // Set tile index limits. int txMin = XToTileX(imRect.x); int tyMin = YToTileY(imRect.y); int txMax = XToTileX(imRect.x + imRect.width - 1); int tyMax = YToTileY(imRect.y + imRect.height - 1); // Loop over all in-bound tiles, find ones that have been computed for (int j = tyMin; j <= tyMax; j++) { for (int i = txMin; i <= txMax; i++) { WritableRaster t; if ((t = tiles[i - minTileX][j - minTileY]) != null && !isTileLocked(i, j)) { Rectangle tileRect = getTileRect(i, j); tileRect = tileRect.intersection(imRect); if(!tileRect.isEmpty()) { overlayPixels(t, im, tileRect); } } } } // Cache the (wrapped) source image and clear the source ROI. PlanarImage src = PlanarImage.wrapRenderedImage(im); if(getNumSources() == 0) { addSource(src); } else { setSource(src, 0); } srcROI = null; overlapBounds = imRect; // Add the source as fallback PropertySource. properties.addProperties(src); } /** * Overlays a given RenderedImage on top of the * current contents of the TiledImage and its * intersection with the supplied ROI. The source * image must have a SampleModel compatible with that * of this image. If the source image and the region of interest * do not both overlap this image then invoking this method will * have no effect. * *

      The source image is added as a fallback PropertySource * for the TiledImage: if a given property is not set directly * on the TiledImage an attempt will be made to obtain its * value from the source image. * * @param im A RenderedImage source to overlay. * @param roi The region of interest. * * @throws IllegalArgumentException either parameter is * null. */ public void set(RenderedImage im, ROI roi) { if ( im == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } // Same source: do nothing. if(getNumSources() > 0 && im == getSourceImage(0)) { return; } Rectangle imRect = new Rectangle(im.getMinX(), im.getMinY(), im.getWidth(), im.getHeight()); // Return if there is not a common intersection among this // image and the source image and the region of interest. Rectangle overlap = imRect.intersection(roi.getBounds()); if(overlap.isEmpty() || (overlap = overlap.intersection(getBounds())).isEmpty()) { return; } // Unset buffer sharing flag. areBuffersShared = false; // Set tile index limits. int txMin = XToTileX(overlap.x); int tyMin = YToTileY(overlap.y); int txMax = XToTileX(overlap.x + overlap.width - 1); int tyMax = YToTileY(overlap.y + overlap.height - 1); Shape roiShape = roi.getAsShape(); Area roiArea = null; if (roiShape != null) { roiArea = new Area(roiShape); } // Loop over all in-bound tiles, find ones that have been computed for (int j = tyMin; j <= tyMax; j++) { for (int i = txMin; i <= txMax; i++) { WritableRaster t; if ((t = tiles[i - minTileX][j - minTileY]) != null && !isTileLocked(i, j)) { Rectangle rect = getTileRect(i, j).intersection(overlap); if(!rect.isEmpty()) { if (roiShape != null) { Area a = new Area(rect); a.intersect(roiArea); if(!a.isEmpty()) { overlayPixels(t, im, a); } } else { int[][] bitmask = roi.getAsBitmask(rect.x, rect.y, rect.width, rect.height, null); if(bitmask != null && bitmask.length > 0) { overlayPixels(t, im, rect, bitmask); } } } } } } // Cache the (wrapped) source image and the source ROI. PlanarImage src = PlanarImage.wrapRenderedImage(im); if(getNumSources() == 0) { addSource(src); } else { setSource(src, 0); } srcROI = roi; overlapBounds = overlap; // Add the source as fallback PropertySource. properties.addProperties(src); } /** * Creates a Graphics object that can be used to * paint text and graphics onto the TiledImage. * The TiledImage must be of integral data type * or an UnsupportedOperationException will be thrown. * * @deprecated as of JAI 1.1. */ public Graphics getGraphics() { return createGraphics(); } /** * Creates a Graphics2D object that can be used to * paint text and graphics onto the TiledImage. * The TiledImage must be of integral data type * or an UnsupportedOperationException will be thrown. */ public Graphics2D createGraphics() { int dataType = sampleModel.getDataType(); if(dataType != DataBuffer.TYPE_BYTE && dataType != DataBuffer.TYPE_SHORT && dataType != DataBuffer.TYPE_USHORT && dataType != DataBuffer.TYPE_INT) { throw new UnsupportedOperationException(JaiI18N.getString("TiledImage0")); } return new TiledImageGraphics(this); } /** * Returns a TiledImage that shares the tile * Rasters of this image. The returned image * occupies a sub-area of the parent image, and possesses a * possibly permuted subset of the parent's bands. The two images * share a common coordinate system. * *

      The image bounds are clipped against the bounds of the * parent image. * *

      If the specified ColorModel is null * then the ColorModel of the sub-image will be set to * null unless bandSelect is either * null or equal in length to the number of bands in the * image in which cases the sub-image ColorModel will be * set to that of the current image. * * @param x the minimum X coordinate of the subimage. * @param y the minimum Y coordinate of the subimage. * @param w the width of the subimage. * @param h the height of the subimage. * @param bandSelect an array of band indices; if null, * all bands are selected. * @param cm the ColorModel of the sub-image. * * @return The requested sub-image or null if either * the specified rectangular area or its intersection with * the current image is empty. * * @since JAI 1.1 */ public TiledImage getSubImage(int x, int y, int w, int h, int[] bandSelect, ColorModel cm) { // Check for empty overlap. Rectangle subImageBounds = new Rectangle(x, y, w, h); if(subImageBounds.isEmpty()) { return null; } Rectangle overlap = subImageBounds.intersection(getBounds()); if(overlap.isEmpty()) { return null; } // Use the original SampleModel or create a subset of it. SampleModel sm = bandSelect != null ? getSampleModel().createSubsetSampleModel(bandSelect) : getSampleModel(); // Set the ColorModel. if(cm == null && (bandSelect == null || bandSelect.length == getSampleModel().getNumBands())) { cm = getColorModel(); } // Create the sub-image. TiledImage subImage = new TiledImage(this, overlap.x, overlap.y, overlap.width, overlap.height, getTileGridXOffset(), getTileGridYOffset(), sm, cm); // Derive sub-image sub-band list with respect to the ancestor // TiledImage. It is possible here that an // ArrayIndexOutOfBoundsException could be thrown if the user // is not careful. int[] subBandList = null; if(bandSelect != null) { // "this" is being sub-banded. if(bandList != null) { //"this" is a sub-band child. // Derive the sub-band list using the sub-band list of // "this" and the list passed in. subBandList = new int[bandSelect.length]; for(int band = 0; band < bandSelect.length; band++) { subBandList[band] = bandList[bandSelect[band]]; } } else { // "this" is not a sub-band child. // Set the sub-band list to the list passed in. subBandList = bandSelect; } } else { // "this" is not being sub-banded. // Pass on the sub-band list of "this". subBandList = bandList; } // Set the sub-band list of the newly created sub-image. subImage.bandList = subBandList; return subImage; } /** * Returns a TiledImage that shares the tile * Rasters of this image. The returned image * occupies a sub-area of the parent image, and possesses a * possibly permuted subset of the parent's bands. The two images * share a common coordinate system. The ColorModel * will be derived from the sub-image SampleModel using the * createColorModel method of PlanarImage. * Note that this implies that the ColorModel could be * null. * *

      The image bounds are clipped against the bounds of the * parent image. * * @param x the minimum X coordinate of the subimage. * @param y the minimum Y coordinate of the subimage. * @param w the width of the subimage. * @param h the height of the subimage. * @param bandSelect an array of band indices; if null, * all bands are selected. * * @return The requested sub-image or null if either * the specified rectangular area or its intersection with * the current image is empty. * * @deprecated as of JAI 1.1. */ public TiledImage getSubImage(int x, int y, int w, int h, int[] bandSelect) { SampleModel sm = bandSelect != null ? getSampleModel().createSubsetSampleModel(bandSelect) : getSampleModel(); return getSubImage(x, y, w, h, bandSelect, createColorModel(sm)); } /** * Returns a TiledImage that shares the tile * Rasters of this image. The returned image * occupies a subarea of the parent image. The two images share a * common coordinate system. * *

      The image bounds are clipped against the bounds of the * parent image. * * @param x the minimum X coordinate of the subimage. * @param y the minimum Y coordinate of the subimage. * @param w the width of the subimage. * @param h the height of the subimage. */ public TiledImage getSubImage(int x, int y, int w, int h) { return getSubImage(x, y, w, h, null, null); } /** * Returns a TiledImage that shares the tile * Rasters of this image. * *

      If the specified ColorModel is null * then the ColorModel of the sub-image will be set to * null unless bandSelect is equal in length * to the number of bands in the image in which cases the sub-image * ColorModel will be set to that of the current image. * * @param bandSelect an array of band indices. * @param cm the ColorModel of the sub-image. * * @throws IllegalArgumentException is bandSelect * is null. * * @since JAI 1.1 */ public TiledImage getSubImage(int[] bandSelect, ColorModel cm) { if(bandSelect == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return getSubImage(getMinX(), getMinY(), getWidth(), getHeight(), bandSelect, cm); } /** * Returns a TiledImage that shares the tile * Rasters of this image. The returned image * occupies the same area as the parent image, and possesses a * possibly permuted subset of the parent's bands. The * ColorModel will be derived from the sub-image * SampleModel using the createColorModel * method of PlanarImage. Note that this implies that * the ColorModel could be null. * * @param bandSelect an array of band indices. * * @deprecated as of JAI 1.1. */ public TiledImage getSubImage(int[] bandSelect) { if(bandSelect == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return getSubImage(getMinX(), getMinY(), getWidth(), getHeight(), bandSelect); // Deliberately using 5-param version. } /** * Forces the requested tile to be computed if has not already been so * and if a source is available. * * @throws ArrayIndexOutOfBoundsException if at least one of the supplied * tile indices is out of the image. */ private void createTile(int tileX, int tileY) { PlanarImage src = getNumSources() > 0 ? getSourceImage(0) : null; // If this is a child image with no source for uncomputed tiles // forward the call to the parent. if(src == null && parent != null) { parent.createTile(tileX, tileY); return; } synchronized(tiles) { // Do nothing if tile is non-null, i.e., already computed. if(tiles[tileX - minTileX][tileY - minTileY] == null) { // If sharing buffers, do so. if(areBuffersShared) { Raster srcTile = src.getTile(tileX, tileY); if(srcTile instanceof WritableRaster) { tiles[tileX - minTileX][tileY - minTileY] = (WritableRaster)srcTile; } else { Point location = new Point(srcTile.getMinX(), srcTile.getMinY()); tiles[tileX - minTileX][tileY - minTileY] = Raster.createWritableRaster(sampleModel, srcTile.getDataBuffer(), location); } return; } // Create the tile using the ancestor SampleModel. tiles[tileX - minTileX][tileY - minTileY] = createWritableRaster(ancestorSampleModel, new Point(tileXToX(tileX), tileYToY(tileY))); WritableRaster tile = tiles[tileX - minTileX][tileY - minTileY]; // If a source is available try to set the tile's data. if(src != null) { // Get the bounds of the tile's support. Rectangle tileRect = getTileRect(tileX, tileY); // Determine the intersection of the tile and the overlap. Rectangle rect = overlapBounds.intersection(tileRect); // Bail if this doesn't intersect the effective overlap. if(rect.isEmpty()) { return; } // If a source ROI is present, use it. if(srcROI != null) { // Attempt to get the ROI as a Shape. Shape roiShape = srcROI.getAsShape(); if (roiShape != null) { // Determine the area of overlap. Area a = new Area(rect); a.intersect(new Area(roiShape)); if(!a.isEmpty()) { // If the area is non-empty overlay the pixels. overlayPixels(tile, src, a); } } else { int[][] bitmask = srcROI.getAsBitmask(rect.x, rect.y, rect.width, rect.height, null); overlayPixels(tile, src, rect, bitmask); } } else { // If the intersection equals the tile area, copy data into // the entire tile. If the tile straddles the edge of the // source, copy only into the intersection. if(!rect.isEmpty()) { if(bandList == null && rect.equals(tileRect)) { // The current image has the same bands in the // same order as its ancestor TiledImage and // the requested tile is completely within "src". if (tileRect.equals(tile.getBounds())) src.copyData(tile); else src.copyData( tile.createWritableChild(rect.x, rect.y, rect.width, rect.height, rect.x, rect.y, null)); } else { overlayPixels(tile, src, rect); } } } } } } } /** * Retrieves a particular tile from the image for reading only. * The tile will be computed if it hasn't been previously. * Any attempt to write to the tile will produce undefined results. * * @param tileX the X index of the tile. * @param tileY the Y index of the tile. */ public Raster getTile(int tileX, int tileY) { if(tileX < minTileX || tileY < minTileY || tileX > getMaxTileX() || tileY > getMaxTileY()) { return null; } createTile(tileX, tileY); // For non-sub-banded image return the tile directly. if(bandList == null) { return (Raster)tiles[tileX - minTileX][tileY - minTileY]; } // For sub-banded image return appropriate band subset. Raster r = (Raster)tiles[tileX - minTileX][tileY - minTileY]; return r.createChild(r.getMinX(), r.getMinY(), r.getWidth(), r.getHeight(), r.getMinX(), r.getMinY(), bandList); } /** * Retrieves a particular tile from the image for reading and writing. * If the tile is locked, null will be returned. Otherwise, the tile * will be computed if it hasn't been previously. Updates of the tile * will become visible to readers of this image as they occur. * * @param tileX the X index of the tile. * @param tileY the Y index of the tile. * @return The requested tile or null if the tile is locked. */ public WritableRaster getWritableTile(int tileX, int tileY) { if(tileX < minTileX || tileY < minTileY || tileX > getMaxTileX() || tileY > getMaxTileY()) { return null; } if(isTileLocked(tileX, tileY)) { return null; } createTile(tileX, tileY); ++writers[tileX - minTileX][tileY - minTileY]; if (writers[tileX - minTileX][tileY - minTileY] == 1) { numWritableTiles[0]++; Enumeration e = tileObservers.elements(); while (e.hasMoreElements()) { TileObserver t = (TileObserver)e.nextElement(); t.tileUpdate(this, tileX, tileY, true); } } // For non-sub-banded image return the tile directly. if(bandList == null) { return tiles[tileX - minTileX][tileY - minTileY]; } // For sub-banded image return appropriate band subset. WritableRaster wr = tiles[tileX - minTileX][tileY - minTileY]; return wr.createWritableChild(wr.getMinX(), wr.getMinY(), wr.getWidth(), wr.getHeight(), wr.getMinX(), wr.getMinY(), bandList); } /** * Indicates that a writer is done updating a tile. * The effects of attempting to release a tile that has not been * grabbed, or releasing a tile more than once are undefined. * * @param tileX the X index of the tile. * @param tileY the Y index of the tile. */ public void releaseWritableTile(int tileX, int tileY) { if(isTileLocked(tileX, tileY)) { return; } --writers[tileX - minTileX][tileY - minTileY]; if (writers[tileX - minTileX][tileY - minTileY] < 0) { throw new RuntimeException(JaiI18N.getString("TiledImage1")); } if (writers[tileX - minTileX][tileY - minTileY] == 0) { numWritableTiles[0]--; Enumeration e = tileObservers.elements(); while (e.hasMoreElements()) { TileObserver t = (TileObserver)e.nextElement(); t.tileUpdate(this, tileX, tileY, false); } } } /** * Forces a tile to be computed, and its contents stored * indefinitely. A tile may not be locked if it is currently * writable. This method should only be used within JAI, in * order to optimize memory allocation. * * @param tileX the X index of the tile. * @param tileY the Y index of the tile. * @return Whether the tile was successfully locked. */ protected boolean lockTile(int tileX, int tileY) { if(tileX < minTileX || tileY < minTileY || tileX > getMaxTileX() || tileY > getMaxTileY()) { return false; } // Return false if the tile is writable. if(isTileWritable(tileX, tileY)) { return false; } // Force the tile to be computed if it has not yet been. createTile(tileX, tileY); // Set the corresponding writers count to -1. writers[tileX - minTileX][tileY - minTileY] = -1; return true; } /** * Returns true if a tile is locked. * * @param tileX the X index of the tile. * @param tileY the Y index of the tile. * @return Whether the tile is locked. */ protected boolean isTileLocked(int tileX, int tileY) { return writers[tileX - minTileX][tileY - minTileY] < 0; } /** * Sets a region of a TiledImage to be a copy of a * supplied Raster. The Raster's * coordinate system is used to position it within the image. * The computation of all overlapping tiles will be forced prior * to modification of the data of the affected area. * * @param r a Raster containing pixels to be copied * into the TiledImage. */ public void setData(Raster r) { // Return if the intersection of the image and Raster bounds is empty. Rectangle rBounds = r.getBounds(); if((rBounds = rBounds.intersection(getBounds())).isEmpty()) { return; } // Set tile index limits. int txMin = XToTileX(rBounds.x); int tyMin = YToTileY(rBounds.y); int txMax = XToTileX(rBounds.x + rBounds.width - 1); int tyMax = YToTileY(rBounds.y + rBounds.height - 1); for(int ty = tyMin; ty <= tyMax; ty++) { for(int tx = txMin; tx <= txMax; tx++) { WritableRaster wr = getWritableTile(tx, ty); if(wr != null) { // XXX bpb 02/04/1999 // All this checking shouldn't be necessary except // that it doesn't look as if WritableRaster.setRect() // correctly accounts for cases wherein the parameter // Raster is not contained in the target WritableRaster. Rectangle tileRect = getTileRect(tx, ty); if(tileRect.contains(rBounds)) { JDKWorkarounds.setRect(wr, r, 0, 0); } else { Rectangle xsect = rBounds.intersection(tileRect); Raster rChild = r.createChild(xsect.x, xsect.y, xsect.width, xsect.height, xsect.x, xsect.y, null); WritableRaster wChild = wr.createWritableChild(xsect.x, xsect.y, xsect.width, xsect.height, xsect.x, xsect.y, null); JDKWorkarounds.setRect(wChild, rChild, 0, 0); } releaseWritableTile(tx, ty); } } } } /** * Sets a region of a TiledImage to be a copy of a * supplied Raster. The Raster's * coordinate system is used to position it within the image. * The computation of all overlapping tiles will be forced prior * to modification of the data of the affected area. * * @param r a Raster containing pixels to be copied * into the TiledImage. * @param roi The region of interest. */ public void setData(Raster r, ROI roi) { // Return if the intersection of the image bounds, the Raster, // and the ROI bounds is empty. Rectangle rBounds = r.getBounds(); if((rBounds = rBounds.intersection(getBounds())).isEmpty() || (rBounds = rBounds.intersection(roi.getBounds())).isEmpty()) { return; } // Get the Rectangle list representation of the ROI. LinkedList rectList = roi.getAsRectangleList(rBounds.x, rBounds.y, rBounds.width, rBounds.height); // Set tile index limits. int txMin = XToTileX(rBounds.x); int tyMin = YToTileY(rBounds.y); int txMax = XToTileX(rBounds.x + rBounds.width - 1); int tyMax = YToTileY(rBounds.y + rBounds.height - 1); int numRects = rectList.size(); for(int ty = tyMin; ty <= tyMax; ty++) { for(int tx = txMin; tx <= txMax; tx++) { WritableRaster wr = getWritableTile(tx, ty); if(wr != null) { Rectangle tileRect = getTileRect(tx, ty); for(int i = 0; i < numRects; i++) { Rectangle rect = (Rectangle)rectList.get(i); rect = rect.intersection(tileRect); // XXX: Should the if-block below be split as in // set(RenderedImage, ROI) above? if(!rect.isEmpty()) { Raster rChild = r.createChild(rect.x, rect.y, rect.width, rect.height, rect.x, rect.y, null); WritableRaster wChild = wr.createWritableChild(rect.x, rect.y, rect.width, rect.height, rect.x, rect.y, null); JDKWorkarounds.setRect(wChild, rChild, 0, 0); } } releaseWritableTile(tx, ty); } } } } /** * Informs this TiledImage that another object is * interested in being notified whenever any tile becomes writable * or ceases to be writable. A tile becomes writable when it is * not currently writable and getWritableTile() is * called. A tile ceases to be writable when * releaseTile() is called and the number of calls to * getWritableTile() and * releaseWritableTile() are identical. * *

      It is the responsibility of the TiledImage to * inform all registered TileObserver objects of such * changes in tile writability before the writer has a chance to * make any modifications. * * @param observer An object implementing the * TileObserver interface. */ public void addTileObserver(TileObserver observer) { tileObservers.addElement(observer); } /** * Informs this TiledImage that a particular TileObserver no * longer wishes to receive updates on tile writability status. * The result of attempting to remove a listener that is not * registered is undefined. * * @param observer An object implementing the * TileObserver interface. */ public void removeTileObserver(TileObserver observer) { tileObservers.removeElement(observer); } /** * Returns a list of tiles that are currently held by one or more * writers or null of no tiles are so held. * * @return An array of Points representing tile indices * or null. */ public Point[] getWritableTileIndices() { Point[] indices = null; if(hasTileWriters()) { Vector v = new Vector(); int count = 0; for (int j = 0; j < tilesY; j++) { for (int i = 0; i < tilesX; i++) { if (writers[i][j] > 0) { v.addElement(new Point(i + minTileX, j + minTileY)); ++count; } } } indices = new Point[count]; for (int k = 0; k < count; k++) { indices[k] = (Point)v.elementAt(k); } } return indices; } /** * Returns true if any tile is being held by a * writer, false otherwise. This provides a quick * way to check whether it is necessary to make copies of tiles -- * if there are no writers, it is safe to use the tiles directly, * while registering to learn of future writers. */ public boolean hasTileWriters() { return numWritableTiles[0] > 0; } /** * Returns true if a tile has writers. * * @param tileX the X index of the tile. * @param tileY the Y index of the tile. */ public boolean isTileWritable(int tileX, int tileY) { return writers[tileX - minTileX][tileY - minTileY] > 0; } /** * Sets the tiles array to null so that * the image may be used again. * * @throws IllegalStateException if hasTileWriters() * returns true. * * @since JAI 1.1.2 */ public void clearTiles() { if(hasTileWriters()) { throw new IllegalStateException(JaiI18N.getString("TiledImage2")); } tiles = null; } /* private int[] XToTileXArray = null; private int[] YToTileYArray = null; private void initTileArrays() { if (XTileTileXArray == null) { XToTileXArray = new int[width]; YToTileYArray = new int[height]; for (int i = 0; i < width; i++) { XToTileXArray[i] = XToTileX(minX + i); } for (int j = 0; j < height; j++) { YToTileYArray[j] = YToTileY(minY + j); } } } */ /** * Sets a sample of a pixel to a given int value. * * @param x The X coordinate of the pixel. * @param y The Y coordinate of the pixel. * @param b The band of the sample within the pixel. * @param s The value to which to set the sample. */ public void setSample(int x, int y, int b, int s) { int tileX = XToTileX(x); int tileY = YToTileY(y); WritableRaster t = getWritableTile(tileX, tileY); if(t != null) { t.setSample(x, y, b, s); } releaseWritableTile(tileX, tileY); } /** * Returns the value of a given sample of a pixel as an int. * * @param x The X coordinate of the pixel. * @param y The Y coordinate of the pixel. * @param b The band of the sample within the pixel. */ public int getSample(int x, int y, int b) { int tileX = XToTileX(x); int tileY = YToTileY(y); Raster t = getTile(tileX, tileY); return t.getSample(x, y, b); } /** * Sets a sample of a pixel to a given float value. * * @param x The X coordinate of the pixel. * @param y The Y coordinate of the pixel. * @param b The band of the sample within the pixel. * @param s The value to which to set the sample. */ public void setSample(int x, int y, int b, float s) { int tileX = XToTileX(x); int tileY = YToTileY(y); WritableRaster t = getWritableTile(tileX, tileY); if(t != null) { t.setSample(x, y, b, s); } releaseWritableTile(tileX, tileY); } /** * Returns the value of a given sample of a pixel as a float. * * @param x The X coordinate of the pixel. * @param y The Y coordinate of the pixel. * @param b The band of the sample within the pixel. */ public float getSampleFloat(int x, int y, int b) { int tileX = XToTileX(x); int tileY = YToTileY(y); Raster t = getTile(tileX, tileY); return t.getSampleFloat(x, y, b); } /** * Sets a sample of a pixel to a given double value. * * @param x The X coordinate of the pixel. * @param y The Y coordinate of the pixel. * @param b The band of the sample within the pixel. * @param s The value to which to set the sample. */ public void setSample(int x, int y, int b, double s) { int tileX = XToTileX(x); int tileY = YToTileY(y); WritableRaster t = getWritableTile(tileX, tileY); if(t != null) { t.setSample(x, y, b, s); } releaseWritableTile(tileX, tileY); } /** * Returns the value of a given sample of a pixel as a double. * * @param x The X coordinate of the pixel. * @param y The Y coordinate of the pixel. * @param b The band of the sample within the pixel. */ public double getSampleDouble(int x, int y, int b) { int tileX = XToTileX(x); int tileY = YToTileY(y); Raster t = getTile(tileX, tileY); return t.getSampleDouble(x, y, b); } /** * Implementation of PropertyChangeListener. * *

      When invoked with an event emitted by the source image specified * for this TiledImage and the event is either a * PropertyChangeEventJAI named "InvalidRegion" * (case-insensitive) or a RenderingChangeEvent, then * all tiles which overlap the intersection of the invalid region and the * region of interest specified for this image (if any) will * be cleared. If the event is a RenderingChangeEvent then * the invalid region will be obtained from the getInvalidRegion * method of the event object; if a PropertyChangeEventJAI * it will be obtained from the getNewValue() method. * In either case, a new PropertyChangeEventJAI will be * fired to all registered listeners of the property name * "InvalidRegion" and to all known sinks which are * PropertyChangeListeners. Its old and new values will * contain the previous and current invalid regions. This may be used to * determine which tiles must be re-requested. The * TiledImage itself will not re-request the data. * * @since JAI 1.1 */ public synchronized void propertyChange(PropertyChangeEvent evt) { PlanarImage src = getNumSources() > 0 ? getSourceImage(0) : null; if(evt.getSource() == src && (evt instanceof RenderingChangeEvent || (evt instanceof PropertyChangeEventJAI && evt.getPropertyName().equalsIgnoreCase("InvalidRegion")))) { // Get the region. Shape invalidRegion = evt instanceof RenderingChangeEvent ? ((RenderingChangeEvent)evt).getInvalidRegion() : (Shape)evt.getNewValue(); // If empty, all is valid. Rectangle invalidBounds = invalidRegion.getBounds(); if(invalidBounds.isEmpty()) { return; } // Intersect with ROI. Area invalidArea = new Area(invalidRegion); if(srcROI != null) { Shape roiShape = srcROI.getAsShape(); if(roiShape != null) { invalidArea.intersect(new Area(roiShape)); } else { LinkedList rectList = srcROI.getAsRectangleList(invalidBounds.x, invalidBounds.y, invalidBounds.width, invalidBounds.height); Iterator it = rectList.iterator(); while(it.hasNext() && !invalidArea.isEmpty()) { invalidArea.intersect(new Area((Rectangle)it.next())); } } } // If empty, all is valid. if(invalidArea.isEmpty()) { return; } // Determine all possible overlapping tiles. Point[] tileIndices = getTileIndices(invalidArea.getBounds()); int numIndices = tileIndices.length; // Clear any tiles which intersect the invalid area. for(int i = 0; i < numIndices; i++) { int tx = tileIndices[i].x; int ty = tileIndices[i].y; Raster tile = tiles[tx][ty]; if ((tile != null) && invalidArea.intersects(tile.getBounds())) { tiles[tx][ty] = null; } } if(eventManager.hasListeners("InvalidRegion")) { // Determine the old invalid region. Shape oldInvalidRegion = new Rectangle(); // default is empty. // If there is a ROI, the old invalid region is the // complement of the ROI within the image bounds. if(srcROI != null) { Area oldInvalidArea = new Area(getBounds()); Shape roiShape = srcROI.getAsShape(); if(roiShape != null) { oldInvalidArea.subtract(new Area(roiShape)); } else { Rectangle oldInvalidBounds = oldInvalidArea.getBounds(); LinkedList rectList = srcROI.getAsRectangleList(oldInvalidBounds.x, oldInvalidBounds.y, oldInvalidBounds.width, oldInvalidBounds.height); Iterator it = rectList.iterator(); while(it.hasNext() && !oldInvalidArea.isEmpty()) { oldInvalidArea.subtract(new Area((Rectangle)it.next())); } } oldInvalidRegion = oldInvalidArea; } // Fire an InvalidRegion event. PropertyChangeEventJAI irEvt = new PropertyChangeEventJAI(this, "InvalidRegion", oldInvalidRegion, invalidRegion); // Fire an event to all registered PropertyChangeListeners. eventManager.firePropertyChange(irEvt); // Fire an event to all PropertyChangeListener sinks. Vector sinks = getSinks(); if(sinks != null) { int numSinks = sinks.size(); for(int i = 0; i < numSinks; i++) { Object sink = sinks.get(i); if(sink instanceof PropertyChangeListener) { ((PropertyChangeListener)sink).propertyChange(irEvt); } } } } } } } jai-core-1.1.4/src/share/classes/javax/media/jai/SourcelessOpImage.java0000644000175000017500000001452510203035544025600 0ustar mathieumathieu/* * $RCSfile: SourcelessOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:21 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.Raster; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.util.Map; import javax.media.jai.util.CaselessStringKey; /** * An abstract base class for image operators that have no image * sources. * *

      SourcelessOpImage is intended as a convenient superclass * for OpImages that have no source image. Some examples are * constant color images, file readers, protocol-based network readers, * and mathematically-defined imagery such as fractals. * *

      The computeTile method of this class will call the * computeRect(PlanarImage[], WritableRaster, Rectangle) * method of the subclass to perform the computation. The first * argument will be null as there are no source images. * * @see OpImage */ public abstract class SourcelessOpImage extends OpImage { private static ImageLayout layoutHelper(int minX, int minY, int width, int height, SampleModel sampleModel, ImageLayout il) { ImageLayout layout = (il == null) ? new ImageLayout() : (ImageLayout)il.clone(); layout.setMinX(minX); layout.setMinY(minY); layout.setWidth(width); layout.setHeight(height); layout.setSampleModel(sampleModel); if (!layout.isValid(ImageLayout.TILE_GRID_X_OFFSET_MASK)) { layout.setTileGridXOffset(layout.getMinX(null)); } if (!layout.isValid(ImageLayout.TILE_GRID_Y_OFFSET_MASK)) { layout.setTileGridYOffset(layout.getMinY(null)); } return layout; } /** * Constructs a SourcelessOpImage. The image bounds * and SampleModel are set explicitly; other layout * parameters may be set using the layout parameter. * The min X, min Y, width, height, and SampleModel * fields of the layout parameter are ignored. * *

      If sampleModel is null, no * exceptions will be thrown. However, the caller must be sure to * set the sampleModel instance variable before * construction terminates. This feature allows subclasses that * require external computation such as file loading to defer the * determination of their SampleModel until after the * call to super. * *

      Similarly, minX, minY, * width, and height may be dummy values * if care is taken to manually set all values that depend on * them, namely the tile grid offset, tile size, and * SampleModel width and height. * *

      The tile dimensions, tile grid X and Y offsets, and * ColorModel of the output will be set in the standard * way by the OpImage constructor. * * @param configuration Configurable attributes of the image including * configuration variables indexed by * RenderingHints.Keys and image properties indexed * by Strings or CaselessStringKeys. * This is simply forwarded to the superclass constructor. * @param layout an ImageLayout describing the layout. * * @since JAI 1.1 */ public SourcelessOpImage(ImageLayout layout, Map configuration, SampleModel sampleModel, int minX, int minY, int width, int height) { super(null, layoutHelper(minX, minY, width, height, sampleModel, layout), configuration, false); } /** * Returns false as SourcelessOpImages often return Rasters * via computeTile() tile that are internally cached. Some * subclasses may want to override this method and return true. */ public boolean computesUniqueTiles() { return false; } /** * Computes a tile. Since the operation has no sources, * there is no need to worry about cobbling. * *

      Subclasses should implement the * computeRect(PlanarImage[], WritableRaster, Rectangle) * method to perform the actual computation. * * @param tileX The X index of the tile. * @param tileY The Y index of the tile. */ public Raster computeTile(int tileX, int tileY) { /* Create a new WritableRaster to represent this tile. */ Point org = new Point(tileXToX(tileX), tileYToY(tileY)); WritableRaster dest = createWritableRaster(sampleModel, org); /* Clip output rectangle to image bounds. */ Rectangle rect = new Rectangle(org.x, org.y, sampleModel.getWidth(), sampleModel.getHeight()); Rectangle destRect = rect.intersection(getBounds()); computeRect((PlanarImage[])null, dest, destRect); return dest; } /** * Throws an IllegalArgumentException since the image has no image * sources. * * @param sourceRect ignored. * @param sourceIndex ignored. * * @throws IllegalArgumentException since the image has no sources. */ public Rectangle mapSourceRect(Rectangle sourceRect, int sourceIndex) { throw new IllegalArgumentException( JaiI18N.getString("SourcelessOpImage0")); } /** * Throws an IllegalArgumentException since the image has no image * sources. * * @param destRect ignored. * @param sourceIndex ignored. * * @throws IllegalArgumentException since the image has no sources. */ public Rectangle mapDestRect(Rectangle destRect, int sourceIndex) { throw new IllegalArgumentException( JaiI18N.getString("SourcelessOpImage0")); } } jai-core-1.1.4/src/share/classes/javax/media/jai/CoordinateImage.java0000644000175000017500000000243210203035544025233 0ustar mathieumathieu/* * $RCSfile: CoordinateImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:07 $ * $State: Exp $ */ package javax.media.jai; /** * A class representing an image that is associated with a coordinate. * This class is used with ImageStack. * * @see ImageStack * * @deprecated as of JAI 1.1. Use * AttributedImage instead. */ public class CoordinateImage { /** The image. */ public PlanarImage image; /** * The coordinate associated with the image. The type of this * parameter is Object so that the application may choose * any class to represent a coordinate based on the individual's * needs. */ public Object coordinate; /** * Constructor. * * @throws IllegalArgumentException if pi is null. * @throws IllegalArgumentException if c is null. */ public CoordinateImage(PlanarImage pi, Object c) { if (pi == null || c == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } image = pi; coordinate = c; } } jai-core-1.1.4/src/share/classes/javax/media/jai/iterator/0000755000175000017500000000000011633360403023171 5ustar mathieumathieujai-core-1.1.4/src/share/classes/javax/media/jai/iterator/RandomIter.java0000644000175000017500000000645310203035544026105 0ustar mathieumathieu/* * $RCSfile: RandomIter.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:26 $ * $State: Exp $ */ package javax.media.jai.iterator; /** * An iterator that allows random read-only access to any sample * within its bounding rectangle. This flexibility will generally * exact a corresponding price in speed and setup overhead. * *

      The iterator is initialized with a particular rectangle as its * bounds, which it is illegal to exceed. This initialization takes * place in a factory method and is not a part of the iterator * interface itself. * *

      The getSample(), getSampleFloat(), and getSampleDouble() * methods are provided to allow read-only access to the source data. * The getPixel() methods allow retrieval of all bands simultaneously. * *

      An instance of RandomIter may be obtained by means of the * RandomIterFactory.create() method, which returns an opaque * object implementing this interface. * * @see WritableRandomIter * @see RandomIterFactory */ public interface RandomIter { /** * Returns the specified sample from the image. * * @param x the X coordinate of the desired pixel. * @param y the Y coordinate of the desired pixel. * @param b the band to retrieve. */ int getSample(int x, int y, int b); /** * Returns the specified sample from the image as a float. * * @param x the X coordinate of the desired pixel. * @param y the Y coordinate of the desired pixel. * @param b the band to retrieve. */ float getSampleFloat(int x, int y, int b); /** * Returns the specified sample from the image as a double. * * @param x the X coordinate of the desired pixel. * @param y the Y coordinate of the desired pixel. * @param b the band to retrieve. */ double getSampleDouble(int x, int y, int b); /** * Returns the samples of the specified pixel from the image * in an array of int. * * @param x the X coordinate of the desired pixel. * @param y the Y coordinate of the desired pixel. * @param iArray An optionally preallocated int array. * @return the contents of the pixel as an int array. */ int[] getPixel(int x, int y, int[] iArray); /** * Returns the samples of the specified pixel from the image * in an array of float. * * @param x the X coordinate of the desired pixel. * @param y the Y coordinate of the desired pixel. * @param fArray An optionally preallocated float array. * @return the contents of the pixel as a float array. */ float[] getPixel(int x, int y, float[] fArray); /** * Returns the samples of the specified pixel from the image * in an array of double. * * @param x the X coordinate of the desired pixel. * @param y the Y coordinate of the desired pixel. * @param dArray An optionally preallocated double array. * @return the contents of the pixel as a double array. */ double[] getPixel(int x, int y, double[] dArray); /** * Informs the iterator that it may discard its internal data * structures. This method should be called when the iterator * will no longer be used. */ void done(); } jai-core-1.1.4/src/share/classes/javax/media/jai/iterator/RookIter.java0000644000175000017500000000764610203035544025604 0ustar mathieumathieu/* * $RCSfile: RookIter.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:27 $ * $State: Exp $ */ package javax.media.jai.iterator; /** * An iterator for traversing a read-only image using arbitrary * up-down and left-right moves. This will generally be somewhat * slower than a corresponding instance of RectIter, since it must * perform bounds checks against the top and left edges of tiles in * addition to their bottom and right edges. * *

      The iterator is initialized with a particular rectangle as its * bounds, which it is illegal to exceed. This initialization takes * place in a factory method and is not a part of the iterator * interface itself. Once initialized, the iterator may be reset to * its initial state by means of the startLine(), startPixels(), and * startBands() methods. As with RectIter, its position may be * advanced using the nextLine(), jumpLines(), nextPixel(), * jumpPixels(), and nextBand() methods. * *

      In addition, prevLine(), prevPixel(), and prevBand() methods * exist to move in the upwards and leftwards directions and to access * smaller band indices. The iterator may be set to the far edges of * the bounding rectangle by means of the endLines(), endPixels(), and * endBands() methods. * *

      The iterator's position may be tested against the bounding * rectangle by means of the finishedLines(), finishedPixels(), and * finishedBands() methods, as well as the hybrid methods * nextLineDone(), prevLineDone(), nextPixelDone(), prevPixelDone(), * nextBandDone(), and prevBandDone(). * *

      The getSample(), getSampleFloat(), and getSampleDouble() * methods are provided to allow read-only access to the source data. * The various source bands may also be accessed in random fashion * using the variants that accept a band index. The getPixel() methods * allow retrieval of all bands simultaneously. * *

      An instance of RookIter may be obtained by means * of the RookIterFactory.create() method, which returns * an opaque object implementing this interface. * * @see RectIter * @see RookIterFactory */ public interface RookIter extends RectIter { /** * Sets the iterator to the previous line of the image. The pixel * and band offsets are unchanged. If the iterator passes the * top line of the rectangle, calls to get() methods are not * valid. */ public void prevLine(); /** * Sets the iterator to the previous line in the image, and * returns true if the top row of the bounding rectangle has * been passed. */ public boolean prevLineDone(); /** * Sets the iterator to the last line of its bounding rectangle. * The pixel and band offsets are unchanged. */ void endLines(); /** * Sets the iterator to the previous pixel in the image (that is, * move leftward). The line and band offsets are unchanged. */ public void prevPixel(); /** * Sets the iterator to the previous pixel in the image (that is, * move leftward). Returns true if the left edge of the bounding * rectangle has been passed. The line and band offsets are * unchanged. */ public boolean prevPixelDone(); /** * Sets the iterator to the rightmost pixel of its bounding rectangle. * The line and band offsets are unchanged. */ void endPixels(); /** * Sets the iterator to the previous band in the image. * The pixel column and line are unchanged. */ void prevBand(); /** * Sets the iterator to the previous band in the image, and * returns true if the min band has been exceeded. The pixel * column and line are unchanged. */ boolean prevBandDone(); /** * Sets the iterator to the last band of the image. * The pixel column and line are unchanged. */ void endBands(); } jai-core-1.1.4/src/share/classes/javax/media/jai/iterator/javax.media.jai.iterator.properties0000644000175000017500000000034510203035544032067 0ustar mathieumathieu# # $RCSfile: javax.media.jai.iterator.properties,v $ # # Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. # # Use is subject to license terms. # # $Revision: 1.1 $ # $Date: 2005-02-11 04:57:28 $ # $State: Exp $ # jai-core-1.1.4/src/share/classes/javax/media/jai/iterator/RectIterFactory.java0000644000175000017500000001435710203035544027114 0ustar mathieumathieu/* * $RCSfile: RectIterFactory.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:27 $ * $State: Exp $ */ package javax.media.jai.iterator; import java.awt.Rectangle; import java.awt.image.ComponentSampleModel; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRenderedImage; import com.sun.media.jai.iterator.RectIterCSMByte; // import com.sun.media.jai.iterator.RectIterCSMShort; // import com.sun.media.jai.iterator.RectIterCSMUShort; // import com.sun.media.jai.iterator.RectIterCSMInt; import com.sun.media.jai.iterator.RectIterCSMFloat; // import com.sun.media.jai.iterator.RectIterCSMDouble; import com.sun.media.jai.iterator.RectIterFallback; import com.sun.media.jai.iterator.WrapperRI; import com.sun.media.jai.iterator.WrapperWRI; import com.sun.media.jai.iterator.WritableRectIterCSMByte; // import com.sun.media.jai.iterator.WritableRectIterCSMShort; // import com.sun.media.jai.iterator.WritableRectIterCSMUShort; // import com.sun.media.jai.iterator.WritableRectIterCSMInt; import com.sun.media.jai.iterator.WritableRectIterCSMFloat; // import com.sun.media.jai.iterator.WritableRectIterCSMDouble; import com.sun.media.jai.iterator.WritableRectIterFallback; /** * A factory class to instantiate instances of the RectIter and * WritableRectIter interfaces on sources of type Raster, * RenderedImage, and WritableRenderedImage. * * @see RectIter * @see WritableRectIter */ public class RectIterFactory { /** Prevent this class from ever being instantiated. */ private RectIterFactory() {} /** * Constructs and returns an instance of RectIter suitable * for iterating over the given bounding rectangle within the * given RenderedImage source. If the bounds parameter is null, * the entire image will be used. * * @param im a read-only RenderedImage source. * @param bounds the bounding Rectangle for the iterator, or null. * @return a RectIter allowing read-only access to the source. */ public static RectIter create(RenderedImage im, Rectangle bounds) { if (bounds == null) { bounds = new Rectangle(im.getMinX(), im.getMinY(), im.getWidth(), im.getHeight()); } SampleModel sm = im.getSampleModel(); if (sm instanceof ComponentSampleModel) { switch (sm.getDataType()) { case DataBuffer.TYPE_BYTE: return new RectIterCSMByte(im, bounds); case DataBuffer.TYPE_SHORT: // return new RectIterCSMShort(im, bounds); break; case DataBuffer.TYPE_USHORT: // return new RectIterCSMUShort(im, bounds); break; case DataBuffer.TYPE_INT: // return new RectIterCSMInt(im, bounds); break; case DataBuffer.TYPE_FLOAT: return new RectIterCSMFloat(im, bounds); case DataBuffer.TYPE_DOUBLE: // return new RectIterCSMDouble(im, bounds); break; } } return new RectIterFallback(im, bounds); } /** * Constructs and returns an instance of RectIter suitable * for iterating over the given bounding rectangle within the * given Raster source. If the bounds parameter is null, * the entire Raster will be used. * * @param ras a read-only Raster source. * @param bounds the bounding Rectangle for the iterator, or null. * @return a RectIter allowing read-only access to the source. */ public static RectIter create(Raster ras, Rectangle bounds) { RenderedImage im = new WrapperRI(ras); return create(im, bounds); } /** * Constructs and returns an instance of WritableRectIter suitable for * iterating over the given bounding rectangle within the given * WritableRenderedImage source. If the bounds parameter is null, * the entire image will be used. * * @param im a WritableRenderedImage source. * @param bounds the bounding Rectangle for the iterator, or null. * @return a WritableRectIter allowing read/write access to the source. */ public static WritableRectIter createWritable(WritableRenderedImage im, Rectangle bounds) { if (bounds == null) { bounds = new Rectangle(im.getMinX(), im.getMinY(), im.getWidth(), im.getHeight()); } SampleModel sm = im.getSampleModel(); if (sm instanceof ComponentSampleModel) { switch (sm.getDataType()) { case DataBuffer.TYPE_BYTE: return new WritableRectIterCSMByte(im, bounds); case DataBuffer.TYPE_SHORT: // return new WritableRectIterCSMShort(im, bounds); break; case DataBuffer.TYPE_USHORT: // return new WritableRectIterCSMUShort(im, bounds); break; case DataBuffer.TYPE_INT: // return new WritableRectIterCSMInt(im, bounds); break; case DataBuffer.TYPE_FLOAT: return new WritableRectIterCSMFloat(im, bounds); case DataBuffer.TYPE_DOUBLE: // return new WritableRectIterCSMDouble(im, bounds); break; } } return new WritableRectIterFallback(im, bounds); } /** * Constructs and returns an instance of WritableRectIter suitable for * iterating over the given bounding rectangle within the given * WritableRaster source. If the bounds parameter is null, * the entire Raster will be used. * * @param ras a WritableRaster source. * @param bounds the bounding Rectangle for the iterator, or null. * @return a WritableRectIter allowing read/write access to the source. */ public static WritableRectIter createWritable(WritableRaster ras, Rectangle bounds) { WritableRenderedImage im = new WrapperWRI(ras); return createWritable(im, bounds); } } jai-core-1.1.4/src/share/classes/javax/media/jai/iterator/WritableRectIter.java0000644000175000017500000000627710203035544027260 0ustar mathieumathieu/* * $RCSfile: WritableRectIter.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:27 $ * $State: Exp $ */ package javax.media.jai.iterator; /** * An iterator for traversing a read/write image in top-to-bottom, * left-to-right order. This will generally be the fastest style of * iterator, since it does not need to perform bounds checks against * the top or left edges of tiles. * *

      The iterator is initialized with a particular rectangle as its * bounds, which it is illegal to exceed. This initialization takes * place in a factory method and is not a part of the iterator * interface itself. Once initialized, the iterator may be reset to * its initial state by means of the startLine(), startPixels(), and * startBands() methods. Its position may be advanced using the * nextLine(), jumpLines(), nextPixel(), jumpPixels(), and nextBand() * methods. * *

      The iterator's position may be tested against the bounding * rectangle by means of the finishedLines(), finishedPixels(), * and finishedBands() methods, as well as the hybrid methods * nextLineDone(), nextPixelDone(), and nextBandDone(). * *

      The getSample(), getSampleFloat(), and getSampleDouble() * methods are provided to allow read-only access to the source data. * The various source bands may also be accessed in random fashion * using the variants that accept a band index. The getPixel() methods * allow retrieval of all bands simultaneously. * *

      WritableRookIter adds the ability to alter the source pixel * values using the various setSample() and setPixel() methods. * *

      An instance of WritableRectIter may be obtained by means * of the RectIterFactory.createWritable() method, which returns * an opaque object implementing this interface. * * @see RectIter * @see RectIterFactory */ public interface WritableRectIter extends RectIter { /** * Sets the current sample to an integral value. */ void setSample(int s); /** * Sets the specified sample of the current pixel to an integral value. */ void setSample(int b, int s); /** * Sets the current sample to a float value. */ void setSample(float s); /** * Sets the specified sample of the current pixel to a float value. */ void setSample(int b, float s); /** * Sets the current sample to a double value. */ void setSample(double x); /** * Sets the specified sample of the current pixel to a double value. */ void setSample(int b, double s); /** * Sets all samples of the current pixel to a set of int values. * * @param iArray an int array containing a value for each band. */ void setPixel(int[] iArray); /** * Sets all samples of the current pixel to a set of float values. * * @param fArray a float array containing a value for each band. */ void setPixel(float[] fArray); /** * Sets all samples of the current pixel to a set of double values. * * @param dArray a double array containing a value for each band. */ void setPixel(double[] dArray); } jai-core-1.1.4/src/share/classes/javax/media/jai/iterator/WritableRandomIter.java0000644000175000017500000000557310203035544027601 0ustar mathieumathieu/* * $RCSfile: WritableRandomIter.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:27 $ * $State: Exp $ */ package javax.media.jai.iterator; /** * An iterator that allows random read/write access to any sample * within its bounding rectangle. This flexibility will generally * exact a corresponding price in speed and setup overhead. * *

      The iterator is initialized with a particular rectangle as its * bounds, which it is illegal to exceed. This initialization takes * place in a factory method and is not a part of the iterator * interface itself. * *

      The setSample() and setPixel() methods allow individual source * samples and whole pixels to be written. * *

      An instance of RandomIter may be obtained by means of the * RandomIterFactory.createWritable() method, which returns an * opaque object implementing this interface. * * @see RandomIter * @see RandomIterFactory */ public interface WritableRandomIter extends RandomIter { /** * Sets the specified sample of the image to an integral value. * * @param x the X coordinate of the pixel. * @param y the Y coordinate of the pixel. * @param b the band to be set. * @param s the sample's new integral value. */ void setSample(int x, int y, int b, int s); /** * Sets the specified sample of the image to a float value. * * @param x the X coordinate of the pixel. * @param y the Y coordinate of the pixel. * @param b the band to be set. * @param s the sample's new float value. */ void setSample(int x, int y, int b, float s); /** * Sets the specified sample of the image to a double value. * * @param x the X coordinate of the pixel. * @param y the Y coordinate of the pixel. * @param b the band to be set. * @param s the sample's new double value. */ void setSample(int x, int y, int b, double s); /** * Sets a pixel in the image using an int array of samples for input. * * @param x the X coordinate of the pixel. * @param y the Y coordinate of the pixel. * @param iArray the input samples in an int array. */ void setPixel(int x, int y, int[] iArray); /** * Sets a pixel in the image using a float array of samples for input. * * @param x the X coordinate of the pixel. * @param y the Y coordinate of the pixel. * @param iArray the input samples in a float array. */ void setPixel(int x, int y, float[] fArray); /** * Sets a pixel in the image using a float array of samples for input. * * @param x the X coordinate of the pixel. * @param y the Y coordinate of the pixel. * @param dArray the input samples in a double array. */ void setPixel(int x, int y, double[] dArray); } jai-core-1.1.4/src/share/classes/javax/media/jai/iterator/RookIterFactory.java0000644000175000017500000001430010203035544027115 0ustar mathieumathieu/* * $RCSfile: RookIterFactory.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:27 $ * $State: Exp $ */ package javax.media.jai.iterator; import java.awt.Rectangle; import java.awt.image.ComponentSampleModel; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRenderedImage; // import com.sun.media.jai.iterator.RookIterCSMByte; // import com.sun.media.jai.iterator.RookIterCSMShort; // import com.sun.media.jai.iterator.RookIterCSMUShort; // import com.sun.media.jai.iterator.RookIterCSMInt; // import com.sun.media.jai.iterator.RookIterCSMFloat; // import com.sun.media.jai.iterator.RookIterCSMDouble; import com.sun.media.jai.iterator.RookIterFallback; import com.sun.media.jai.iterator.WrapperRI; import com.sun.media.jai.iterator.WrapperWRI; // import com.sun.media.jai.iterator.WritableRookIterCSMByte; // import com.sun.media.jai.iterator.WritableRookIterCSMShort; // import com.sun.media.jai.iterator.WritableRookIterCSMUShort; // import com.sun.media.jai.iterator.WritableRookIterCSMInt; // import com.sun.media.jai.iterator.WritableRookIterCSMFloat; // import com.sun.media.jai.iterator.WritableRookIterCSMDouble; import com.sun.media.jai.iterator.WritableRookIterFallback; /** * A factory class to instantiate instances of the RookIter and * WritableRookIter interfaces on sources of type Raster, * RenderedImage, and WritableRenderedImage. * * @see RookIter * @see WritableRookIter */ public class RookIterFactory { /** Prevent this class from ever being instantiated. */ private RookIterFactory() {} /** * Constructs and returns an instance of RookIter suitable * for iterating over the given bounding rectangle within the * given RenderedImage source. If the bounds parameter is null, * the entire image will be used. * * @param im a read-only RenderedImage source. * @param bounds the bounding Rectangle for the iterator, or null. * @return a RookIter allowing read-only access to the source. */ public static RookIter create(RenderedImage im, Rectangle bounds) { if (bounds == null) { bounds = new Rectangle(im.getMinX(), im.getMinY(), im.getWidth(), im.getHeight()); } SampleModel sm = im.getSampleModel(); if (sm instanceof ComponentSampleModel) { switch (sm.getDataType()) { case DataBuffer.TYPE_BYTE: // return new RookIterCSMByte(im, bounds); case DataBuffer.TYPE_SHORT: // return new RookIterCSMShort(im, bounds); case DataBuffer.TYPE_USHORT: // return new RookIterCSMUShort(im, bounds); case DataBuffer.TYPE_INT: // return new RookIterCSMInt(im, bounds); case DataBuffer.TYPE_FLOAT: // return new RookIterCSMFloat(im, bounds); case DataBuffer.TYPE_DOUBLE: // return new RookIterCSMDouble(im, bounds); } } return new RookIterFallback(im, bounds); } /** * Constructs and returns an instance of RookIter suitable * for iterating over the given bounding rectangle within the * given Raster source. If the bounds parameter is null, * the entire Raster will be used. * * @param ras a read-only Raster source. * @param bounds the bounding Rectangle for the iterator, or null. * @return a RookIter allowing read-only access to the source. */ public static RookIter create(Raster ras, Rectangle bounds) { RenderedImage im = new WrapperRI(ras); return create(im, bounds); } /** * Constructs and returns an instance of WritableRookIter suitable for * iterating over the given bounding rectangle within the given * WritableRenderedImage source. If the bounds parameter is null, * the entire image will be used. * * @param im a WritableRenderedImage source. * @param bounds the bounding Rectangle for the iterator, or null. * @return a WritableRookIter allowing read/write access to the source. */ public static WritableRookIter createWritable(WritableRenderedImage im, Rectangle bounds) { if (bounds == null) { bounds = new Rectangle(im.getMinX(), im.getMinY(), im.getWidth(), im.getHeight()); } SampleModel sm = im.getSampleModel(); if (sm instanceof ComponentSampleModel) { switch (sm.getDataType()) { case DataBuffer.TYPE_BYTE: // return new WritableRookIterCSMByte(im, bounds); case DataBuffer.TYPE_SHORT: // return new WritableRookIterCSMShort(im, bounds); case DataBuffer.TYPE_USHORT: // return new WritableRookIterCSMUShort(im, bounds); case DataBuffer.TYPE_INT: // return new WritableRookIterCSMInt(im, bounds); case DataBuffer.TYPE_FLOAT: // return new WritableRookIterCSMFloat(im, bounds); case DataBuffer.TYPE_DOUBLE: // return new WritableRookIterCSMDouble(im, bounds); } } return new WritableRookIterFallback(im, bounds); } /** * Constructs and returns an instance of WritableRookIter suitable for * iterating over the given bounding rectangle within the given * WritableRaster source. If the bounds parameter is null, * the entire Raster will be used. * * @param ras a WritableRaster source. * @param bounds the bounding Rectangle for the iterator, or null. * @return a WritableRookIter allowing read/write access to the source. */ public static WritableRookIter createWritable(WritableRaster ras, Rectangle bounds) { WritableRenderedImage im = new WrapperWRI(ras); return createWritable(im, bounds); } } jai-core-1.1.4/src/share/classes/javax/media/jai/iterator/JaiI18N.java0000644000175000017500000000074710203035544025144 0ustar mathieumathieu/* * $RCSfile: JaiI18N.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:26 $ * $State: Exp $ */ package javax.media.jai.iterator; import com.sun.media.jai.util.PropertyUtil; class JaiI18N { static String packageName = "javax.media.jai.iterator"; public static String getString(String key) { return PropertyUtil.getString(packageName, key); } } jai-core-1.1.4/src/share/classes/javax/media/jai/iterator/RectIter.java0000644000175000017500000001523410203035544025557 0ustar mathieumathieu/* * $RCSfile: RectIter.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:26 $ * $State: Exp $ */ package javax.media.jai.iterator; /** * An iterator for traversing a read-only image in top-to-bottom, * left-to-right order. This will generally be the fastest style of * iterator, since it does not need to perform bounds checks against * the top or left edges of tiles. * *

      The iterator is initialized with a particular rectangle as its * bounds, which it is illegal to exceed. This initialization takes * place in a factory method and is not a part of the iterator * interface itself. Once initialized, the iterator may be reset to * its initial state by means of the startLine(), startPixels(), and * startBands() methods. Its position may be advanced using the * nextLine(), jumpLines(), nextPixel(), jumpPixels(), and nextBand() * methods. * *

      The iterator's position may be tested against the bounding * rectangle by means of the finishedLines(), finishedPixels(), * and finishedBands() methods, as well as the hybrid methods * nextLineDone(), nextPixelDone(), and nextBandDone(). * *

      The getSample(), getSampleFloat(), and getSampleDouble() * methods are provided to allow read-only access to the source data. * The various source bands may also be accessed in random fashion * using the variants that accept a band index. The getPixel() methods * allow retrieval of all bands simultaneously. * *

      An instance of RectIter may be obtained by means * of the RectIterFactory.create() method, which returns * an opaque object implementing this interface. * * @see WritableRectIter * @see RectIterFactory */ public interface RectIter { /** * Sets the iterator to the first line of its bounding rectangle. * The pixel and band offsets are unchanged. */ void startLines(); /** * Sets the iterator to the next line of the image. The pixel and * band offsets are unchanged. If the iterator passes the bottom * line of the rectangles, calls to get() methods are not valid. */ void nextLine(); /** * Sets the iterator to the next line in the image, and returns * true if the bottom row of the bounding rectangle has been * passed. */ boolean nextLineDone(); /** * Jumps downward num lines from the current position. Num may be * negative. The pixel and band offsets are unchanged. If the * position after the jump is outside of the iterator's bounding * box, an IndexOutOfBoundsException will be thrown * and the position will be unchanged. * * @throws IndexOutOfBoundsException if the position goes outside * of the iterator's bounding box. */ void jumpLines(int num); /** * Returns true if the bottom row of the bounding rectangle has * been passed. */ boolean finishedLines(); /** * Sets the iterator to the leftmost pixel of its bounding rectangle. * The line and band offsets are unchanged. */ void startPixels(); /** * Sets the iterator to the next pixel in image (that is, move * rightward). The line and band offsets are unchanged. * *

      This method may be used in conjunction with the method * finishedPixels. Or the method nextPixelDone, * which sets the iterator to the next pixel and checks the bound, * may be used instead of this method. */ void nextPixel(); /** * Sets the iterator to the next pixel in the image (that is, move * rightward). Returns true if the right edge of the bounding * rectangle has been passed. The line and band offsets are * unchanged. */ boolean nextPixelDone(); /** * Jumps rightward num pixels from the current position. Num may * be negative. The line and band offsets are unchanged. If the * position after the jump is outside of the iterator's bounding * box, an IndexOutOfBoundsException will be thrown * and the position will be unchanged. * * @throws IndexOutOfBoundsException if the position goes outside * of the iterator's bounding box. */ void jumpPixels(int num); /** * Returns true if the right edge of the bounding rectangle has * been passed. */ boolean finishedPixels(); /** * Sets the iterator to the first band of the image. * The pixel column and line are unchanged. */ void startBands(); /** * Sets the iterator to the next band in the image. * The pixel column and line are unchanged. */ void nextBand(); /** * Sets the iterator to the next band in the image, and returns * true if the max band has been exceeded. The pixel column and * line are unchanged. */ boolean nextBandDone(); /** * Returns true if the max band in the image has been exceeded. */ boolean finishedBands(); /** * Returns the current sample as an integer. */ int getSample(); /** * Returns the specified sample of the current pixel as an integer. * * @param b the band index of the desired sample. */ int getSample(int b); /** * Returns the current sample as a float. */ float getSampleFloat(); /** * Returns the specified sample of the current pixel as a float. * * @param b the band index of the desired sample. */ float getSampleFloat(int b); /** * Returns the current sample as a double. */ double getSampleDouble(); /** * Returns the specified sample of the current pixel as a double. * * @param b the band index of the desired sample. */ double getSampleDouble(int b); /** * Returns the samples of the current pixel from the image * in an array of int. * * @param iArray An optionally preallocated int array. * @return the contents of the pixel as an int array. */ int[] getPixel(int[] iArray); /** * Returns the samples of the current pixel from the image * in an array of float. * * @param fArray An optionally preallocated float array. * @return the contents of the pixel as a float array. */ float[] getPixel(float[] fArray); /** * Returns the samples of the current pixel from the image * in an array of double. * * @param dArray An optionally preallocated double array. * @return the contents of the pixel as a double array. */ double[] getPixel(double[] dArray); } jai-core-1.1.4/src/share/classes/javax/media/jai/iterator/WritableRookIter.java0000644000175000017500000000541510203035544027266 0ustar mathieumathieu/* * $RCSfile: WritableRookIter.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:27 $ * $State: Exp $ */ package javax.media.jai.iterator; /** * An iterator for traversing a read/write image using arbitrary * up-down and left-right moves. This will generally be somewhat * slower than a corresponding instance of RectIter, since it must * perform bounds checks against the top and left edges of tiles in * addition to their botton and right edges. * *

      The iterator is initialized with a particular rectangle as its * bounds, which it is illegal to exceed. This initialization takes * place in a factory method and is not a part of the iterator * interface itself. Once initialized, the iterator may be reset to * its initial state by means of the startLine(), startPixels(), and * startBands() methods. As with RectIter, its position may be * advanced using the nextLine(), jumpLines(), nextPixel(), * jumpPixels(), and nextBand() methods. * *

      In addition, prevLine(), prevPixel(), and prevBand() methods * exist to move in the upwards and leftwards directions and to access * smaller band indices. The iterator may be set to the far edges of * the bounding rectangle by means of the endLines(), endPixels(), and * endBands() methods. * *

      The iterator's position may be tested against the bounding * rectangle by means of the finishedLines(), finishedPixels(), and * finishedBands() methods, as well as the hybrid methods * nextLineDone(), prevLineDone(), nextPixelDone(), prevPixelDone(), * nextBandDone(), and prevBandDone(). * *

      The getSample(), getSampleFloat(), and getSampleDouble() * methods are provided to allow read-only access to the source data. * The various source bands may also be accessed in random fashion * using the variants that accept a band index. The getPixel() methods * allow retrieval of all bands simultaneously. * *

      WritableRookIter adds the ability to alter the source pixel * values using the various setSample() and setPixel() methods. These * methods are inherited from the WritableRectIter interface * unchanged. * *

      An instance of WritableRookIter may be obtained by means * of the RookIterFactory.createWritable() method, which returns * an opaque object implementing this interface. * *

      Note that a WritableRookIter inherits multiply from RookIter * and WritableRectIter, and so may be passed into code expecting either * interface. WritableRookIter in fact adds no methods not found in * one of its parent interfaces. * * @see RookIter * @see WritableRectIter * @see RookIterFactory */ public interface WritableRookIter extends RookIter, WritableRectIter { // No new methods } jai-core-1.1.4/src/share/classes/javax/media/jai/iterator/RandomIterFactory.java0000644000175000017500000001440110203035544027425 0ustar mathieumathieu/* * $RCSfile: RandomIterFactory.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:26 $ * $State: Exp $ */ package javax.media.jai.iterator; import java.awt.Rectangle; import java.awt.image.ComponentSampleModel; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.WritableRenderedImage; import com.sun.media.jai.iterator.RandomIterCSMByte; import com.sun.media.jai.iterator.RandomIterCSMShort; import com.sun.media.jai.iterator.RandomIterCSMUShort; import com.sun.media.jai.iterator.RandomIterCSMInt; import com.sun.media.jai.iterator.RandomIterCSMFloat; import com.sun.media.jai.iterator.RandomIterCSMDouble; import com.sun.media.jai.iterator.RandomIterFallback; import com.sun.media.jai.iterator.WrapperRI; import com.sun.media.jai.iterator.WrapperWRI; import com.sun.media.jai.iterator.WritableRandomIterCSMByte; import com.sun.media.jai.iterator.WritableRandomIterCSMShort; import com.sun.media.jai.iterator.WritableRandomIterCSMUShort; import com.sun.media.jai.iterator.WritableRandomIterCSMInt; import com.sun.media.jai.iterator.WritableRandomIterCSMFloat; import com.sun.media.jai.iterator.WritableRandomIterCSMDouble; import com.sun.media.jai.iterator.WritableRandomIterFallback; /** * A factory class to instantiate instances of the RandomIter and * WritableRandomIter interfaces on sources of type Raster, * RenderedImage, and WritableRenderedImage. * * @see RandomIter * @see WritableRandomIter */ public class RandomIterFactory { /** Prevent this class from ever being instantiated. */ private RandomIterFactory() {} /** * Constructs and returns an instance of RandomIter suitable for * iterating over the given bounding rectangle within the given * RenderedImage source. If the bounds parameter is null, the * entire image will be used. * * @param im a read-only RenderedImage source. * @param bounds the bounding Rectangle for the iterator, or null. * @return a RandomIter allowing read-only access to the source. */ public static RandomIter create(RenderedImage im, Rectangle bounds) { if (bounds == null) { bounds = new Rectangle(im.getMinX(), im.getMinY(), im.getWidth(), im.getHeight()); } SampleModel sm = im.getSampleModel(); if (sm instanceof ComponentSampleModel) { switch (sm.getDataType()) { case DataBuffer.TYPE_BYTE: // return new RandomIterCSMByte(im, bounds); case DataBuffer.TYPE_SHORT: // return new RandomIterCSMShort(im, bounds); case DataBuffer.TYPE_USHORT: // return new RandomIterCSMUShort(im, bounds); case DataBuffer.TYPE_INT: // return new RandomIterCSMInt(im, bounds); case DataBuffer.TYPE_FLOAT: // return new RandomIterCSMFloat(im, bounds); case DataBuffer.TYPE_DOUBLE: // return new RandomIterCSMDouble(im, bounds); } } return new RandomIterFallback(im, bounds); } /** * Constructs and returns an instance of RandomIter suitable for * iterating over the given bounding rectangle within the given * Raster source. If the bounds parameter is null, the entire * Raster will be used. * * @param ras a read-only Raster source. * @param bounds the bounding Rectangle for the iterator, or null. * @return a RandomIter allowing read-only access to the source. */ public static RandomIter create(Raster ras, Rectangle bounds) { RenderedImage im = new WrapperRI(ras); return create(im, bounds); } /** * Constructs and returns an instance of WritableRandomIter * suitable for iterating over the given bounding rectangle within * the given WritableRenderedImage source. If the bounds * parameter is null, the entire image will be used. * * @param im a WritableRenderedImage source. * @param bounds the bounding Rectangle for the iterator, or null. * @return a WritableRandomIter allowing read/write access to the source. */ public static WritableRandomIter createWritable(WritableRenderedImage im, Rectangle bounds) { if (bounds == null) { bounds = new Rectangle(im.getMinX(), im.getMinY(), im.getWidth(), im.getHeight()); } SampleModel sm = im.getSampleModel(); if (sm instanceof ComponentSampleModel) { switch (sm.getDataType()) { case DataBuffer.TYPE_BYTE: // return new WritableRandomIterCSMByte(im, bounds); case DataBuffer.TYPE_SHORT: // return new WritableRandomIterCSMShort(im, bounds); case DataBuffer.TYPE_USHORT: // return new WritableRandomIterCSMUShort(im, bounds); case DataBuffer.TYPE_INT: // return new WritableRandomIterCSMInt(im, bounds); case DataBuffer.TYPE_FLOAT: // return new WritableRandomIterCSMFloat(im, bounds); case DataBuffer.TYPE_DOUBLE: // return new WritableRandomIterCSMDouble(im, bounds); } } return new WritableRandomIterFallback(im, bounds); } /** * Constructs and returns an instance of WritableRandomIter * suitable for iterating over the given bounding rectangle within * the given WritableRaster source. If the bounds parameter is * null, the entire Raster will be used. * * @param ras a WritableRaster source. * @param bounds the bounding Rectangle for the iterator, or null. * @return a WritableRandomIter allowing read/write access to the source. */ public static WritableRandomIter createWritable(WritableRaster ras, Rectangle bounds) { WritableRenderedImage im = new WrapperWRI(ras); return createWritable(im, bounds); } } jai-core-1.1.4/src/share/classes/javax/media/jai/GeometricOpImage.java0000644000175000017500000007224210240000604025355 0ustar mathieumathieu/* * $RCSfile: GeometricOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-05-10 00:34:12 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.Point2D; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.awt.Point; import java.util.Map; import java.util.Vector; import javax.media.jai.util.CaselessStringKey; import com.sun.media.jai.util.ImageUtil; /** * An abstract base class for image operators that perform a geometric * transformation of the source image. * *

      The geometric relationship between the source and destination images * will be determined by the specific behavior of the methods * backwardMapRect() and forwardMapRect() the * implementations of which must be provided by a subclass. * *

      The location of the source pixel corresponding to a given destination * pixel is determined by the aforementioned backward mapping transformation. * The value of the destination pixel is then calculated by interpolating the * values of a set of source pixels at locations in the vicinity of the * backward mapped destination pixel location according to the requirements * of a specified interpolation algorithm. In particular, a given * destination pixel value may be interpolated from the neighborhood of source * pixels beginning at (sx - leftPadding, sy - topPadding) and extending to * (sx + rightPadding, sy + bottomPadding), inclusive, where (sx, sy) is * the truncated backward mapped location of the destination pixel. The * actual amount of padding required is determined by a supplied * Interpolation object. * *

      Since this operator might need a region around each source pixel in * order to compute the destination pixel value, the border destination pixels * might not be able to be computed without any source extension mechanism. * The source extension method can be specified by supplying a * BorderExtender object that will define the pixel values of the * source outside the actual source area as a function of the actual source * pixel values. If no extension is specified, the destination samples that * cannot be computed will be written in the destination as the user-specified * background values. * * @see BorderExtender * @see Interpolation * @see InterpolationNearest * @see OpImage * * @since JAI 1.1 */ public abstract class GeometricOpImage extends OpImage { /** * The Interpolation object describing the subpixel * interpolation method. This variable should not be null. */ protected Interpolation interp; /** * The BorderExtender describing the method by which * source data are extended to provide sufficient context for * calculation of the pixel values of backward mapped coordinates * according to the interpolation method specified. If this * variable is null no extension will be performed. */ protected BorderExtender extender = null; /** * The computable bounds of this image within which the pixels of the * image may be computed and set. This is equal to the bounding box of * the set of pixels the locations of which backward map to within the * source image bounds contracted by the padding values required for * interpolation. * *

      The GeometricOpImage constructor sets the computable * bounds to the image bounds. Subclasses should set this value to * values reasonable for the operation in question. */ protected Rectangle computableBounds; /** * Indicates whether the background values are provided. */ protected boolean setBackground; /** * The user-specified background values. */ protected double[] backgroundValues; /** * The user-specified background values in integer. */ protected int[] intBackgroundValues; /** * Constructs a GeometricOpImage. The image layout * (image bounds, tile grid layout, SampleModel and * ColorModel) of the output are set in the standard * way by the OpImage constructor. * *

      Additional control over the image bounds, tile grid layout, * SampleModel, and ColorModel may be * obtained by specifying an ImageLayout parameter. * This parameter will be passed to the superclass constructor * unchanged. * * @param layout An ImageLayout containing the source * bounds before padding, and optionally containing the * tile grid layout, SampleModel, and * ColorModel. * @param sources The immediate sources of this image. * @param configuration Configurable attributes of the image including * configuration variables indexed by * RenderingHints.Keys and image properties indexed * by Strings or CaselessStringKeys. * This is simply forwarded to the superclass constructor. * @param cobbleSources A boolean indicating whether * computeRect() expects contiguous sources. * @param extender A BorderExtender, or null. * @param interp an Interpolation object to use for * interpolation of the backward mapped pixel values at * fractional positions. If the supplied parameter is * null the corresponding instance variable * will be initialized to an instance of * InterpolationNearest. * * @throws IllegalArgumentException if sources * is null. * @throws IllegalArgumentException If sources * is non-null and any object in * sources is null. * @throws IllegalArgumentException if combining the intersected * source bounds with the layout parameter results in negative * output width or height. */ public GeometricOpImage(Vector sources, ImageLayout layout, Map configuration, boolean cobbleSources, BorderExtender extender, Interpolation interp) { this(sources, layout, configuration, cobbleSources, extender, interp, null); } private static Map configHelper(Map configuration) { Map config; if (configuration == null) { config = new RenderingHints(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.TRUE); } else { config = configuration; // If the user specified a value for this hint, we don't // want to change that if (!config.containsKey(JAI.KEY_REPLACE_INDEX_COLOR_MODEL)) { RenderingHints hints = new RenderingHints(null); // This is effectively a clone of configuration hints.putAll(configuration); config = hints; config.put(JAI.KEY_REPLACE_INDEX_COLOR_MODEL, Boolean.TRUE); } } return config; } /** * Constructs a GeometricOpImage. The image layout * (image bounds, tile grid layout, SampleModel and * ColorModel) of the output are set in the standard * way by the OpImage constructor. * *

      Additional control over the image bounds, tile grid layout, * SampleModel, and ColorModel may be * obtained by specifying an ImageLayout parameter. * This parameter will be passed to the superclass constructor * unchanged. * *

      A RenderingHints for * JAI.KEY_REPLACE_INDEX_COLOR_MODEL with the value of * Boolean.TRUE is automatically added to the given * configuration and passed up to the superclass constructor * so that geometric operations are performed on the pixel values instead * of being performed on the indices into the color map for those * operations whose source(s) have an IndexColorModel. * This addition will take place only if a value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL has not already been * provided by the user. Note that the configuration Map * is cloned before the new hint is added to it. Regarding the value * for the JAI.KEY_REPLACE_INDEX_COLOR_MODEL * RenderingHints, the operator itself can be smart * based on the parameters, i.e. while the default value for * the JAI.KEY_REPLACE_INDEX_COLOR_MODEL is * Boolean.TRUE for operations that extend this class, * in some cases the operator could set the default. * * @param layout An ImageLayout containing the source * bounds before padding, and optionally containing the * tile grid layout, SampleModel, and * ColorModel. * @param sources The immediate sources of this image. * @param configuration Configurable attributes of the image including * configuration variables indexed by * RenderingHints.Keys and image properties indexed * by Strings or CaselessStringKeys. * This is simply forwarded to the superclass constructor. * @param cobbleSources A boolean indicating whether * computeRect() expects contiguous sources. * @param extender A BorderExtender, or null. * @param interp an Interpolation object to use for * interpolation of the backward mapped pixel values at * fractional positions. If the supplied parameter is * null the corresponding instance variable * will be initialized to an instance of * InterpolationNearest. * @param backgroundValues The user-specified background values. If the * provided array length is smaller than the number of bands, all * the bands will be filled with the first element of the array. * If the provided array is null, set it to new double[]{0.0} * . * * @throws IllegalArgumentException if sources * is null. * @throws IllegalArgumentException If sources * is non-null and any object in * sources is null. * @throws IllegalArgumentException if combining the intersected * source bounds with the layout parameter results in negative * output width or height. * @since JAI 1.1.2 */ public GeometricOpImage(Vector sources, ImageLayout layout, Map configuration, boolean cobbleSources, BorderExtender extender, Interpolation interp, double[] backgroundValues) { super(sources, layout, configHelper(configuration), cobbleSources); this.extender = extender; this.interp = interp != null ? interp : new InterpolationNearest(); if (backgroundValues == null) backgroundValues = new double[]{0.0}; this.setBackground = false; for (int i = 0; i < backgroundValues.length; i++) if(backgroundValues[i] != 0.0) this.setBackground = true; this.backgroundValues = backgroundValues; int numBands = getSampleModel().getNumBands(); if (backgroundValues.length < numBands) { this.backgroundValues = new double[numBands]; for (int i = 0; i < numBands; i++) this.backgroundValues[i] = backgroundValues[0]; } if (sampleModel.getDataType() <= DataBuffer.TYPE_INT) { int length = this.backgroundValues.length; intBackgroundValues = new int[length]; for (int i = 0; i < length; i++) intBackgroundValues[i] = (int)this.backgroundValues[i]; } // Initialize computable bounds to the current image bounds. computableBounds = getBounds(); } /** * Retrieve the Interpolation object associated with * this class instance. The object is returned by reference. * * @return The associated Interpolation object. */ public Interpolation getInterpolation() { return interp; } /** * Retrieve the BorderExtender object associated with * this class instance. The object is returned by reference. * * @return The associated BorderExtender object * or null. */ public BorderExtender getBorderExtender() { return extender; } /** * Computes the position in the specified source that best * matches the supplied destination image position. If it * is not possible to compute the requested position, * null will be returned. * *

      The implementation in this class returns the value of * pt in the following code snippet: * *

           * Rectangle destRect = new Rectangle((int)destPt.getX(),
           *                                    (int)destPt.getY(),
           *                                    1, 1);
           * Rectangle sourceRect = backwardMapRect(destRect, sourceIndex);
           * Point2D pt = (Point2D)destPt.clone();
           * pt.setLocation(sourceRect.x + (sourceRect.width - 1.0)/2.0,
           *                sourceRect.y + (sourceRect.height - 1.0)/2.0);
           * 
      * * Subclasses requiring different behavior should override this * method.

      * * @param destPt the position in destination image coordinates * to map to source image coordinates. * @param sourceIndex the index of the source image. * * @return a Point2D of the same class as * destPt or null. * * @throws IllegalArgumentException if destPt is * null. * @throws IndexOutOfBoundsException if sourceIndex is * negative or greater than or equal to the number of sources. * * @since JAI 1.1.2 */ public Point2D mapDestPoint(Point2D destPt, int sourceIndex) { if (destPt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } else if (sourceIndex < 0 || sourceIndex >= getNumSources()) { throw new IndexOutOfBoundsException(JaiI18N.getString("Generic1")); } Rectangle destRect = new Rectangle((int)destPt.getX(), (int)destPt.getY(), 1, 1); Rectangle sourceRect = backwardMapRect(destRect, sourceIndex); if(sourceRect == null) { return null; } Point2D pt = (Point2D)destPt.clone(); pt.setLocation(sourceRect.x + (sourceRect.width - 1.0)/2.0, sourceRect.y + (sourceRect.height - 1.0)/2.0); return pt; } /** * Computes the position in the destination that best * matches the supplied source image position. If it * is not possible to compute the requested position, * null will be returned. * *

      The implementation in this class returns the value of * pt in the following code snippet: * *

           * Rectangle sourceRect = new Rectangle((int)sourcePt.getX(),
           *                                      (int)sourcePt.getY(),
           *                                      1, 1);
           * Rectangle destRect = forwardMapRect(sourceRect, sourceIndex);
           * Point2D pt = (Point2D)sourcePt.clone();
           * pt.setLocation(destRect.x + (destRect.width - 1.0)/2.0,
           *                destRect.y + (destRect.height - 1.0)/2.0);
           * 
      * * Subclasses requiring different behavior should override this * method.

      * * @param sourcePt the position in source image coordinates * to map to destination image coordinates. * @param sourceIndex the index of the source image. * * @return a Point2D of the same class as * sourcePt or null. * * @throws IllegalArgumentException if sourcePt is * null. * @throws IndexOutOfBoundsException if sourceIndex is * negative or greater than or equal to the number of sources. * * @since JAI 1.1.2 */ public Point2D mapSourcePoint(Point2D sourcePt, int sourceIndex) { if (sourcePt == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } else if (sourceIndex < 0 || sourceIndex >= getNumSources()) { throw new IndexOutOfBoundsException(JaiI18N.getString("Generic1")); } Rectangle sourceRect = new Rectangle((int)sourcePt.getX(), (int)sourcePt.getY(), 1, 1); Rectangle destRect = forwardMapRect(sourceRect, sourceIndex); if(destRect == null) { return null; } Point2D pt = (Point2D)sourcePt.clone(); pt.setLocation(destRect.x + (destRect.width - 1.0)/2.0, destRect.y + (destRect.height - 1.0)/2.0); return pt; } /** * Returns the minimum bounding box of the region of the destination * to which a particular Rectangle of the specified source * will be mapped. * *

      The integral source rectangle coordinates should be considered * pixel indices. The "energy" of each pixel is defined to be * concentrated in the continuous plane of pixels at an offset of * (0.5, 0.5) from the index of the pixel. Forward mappings * must take this (0.5, 0.5) pixel center into account. Thus * given integral source pixel indices as input, the fractional * destination location, as calculated by functions Xf(xSrc, ySrc), * Yf(xSrc, ySrc), is given by: *

           *
           *     xDst = Xf(xSrc+0.5, ySrc+0.5) - 0.5
           *     yDst = Yf(xSrc+0.5, ySrc+0.5) - 0.5
           *
           * 
      * * @param sourceRect the Rectangle in source coordinates. * @param sourceIndex the index of the source image. * * @return a Rectangle indicating the destination * bounding box, or null if the bounding box * is unknown. * * @throws IllegalArgumentException if sourceIndex is * negative or greater than the index of the last source. * @throws IllegalArgumentException if sourceRect is * null. */ protected abstract Rectangle forwardMapRect(Rectangle sourceRect, int sourceIndex); /** * Returns the minimum bounding box of the region of the specified * source to which a particular Rectangle of the * destination will be mapped. * *

      The integral destination rectangle coordinates should be considered * pixel indices. The "energy" of each pixel is defined to be * concentrated in the continuous plane of pixels at an offset of * (0.5, 0.5) from the index of the pixel. Backward mappings * must take this (0.5, 0.5) pixel center into account. Thus * given integral destination pixel indices as input, the fractional * source location, as calculated by functions Xb(xDst, yDst), * Yb(xDst, yDst), is given by: *

           *
           *     xSrc = Xb(xDst+0.5, yDst+0.5) - 0.5
           *     ySrc = Yb(xDst+0.5, yDst+0.5) - 0.5
           *
           * 
      * * @param destRect the Rectangle in destination coordinates. * @param sourceIndex the index of the source image. * * @return a Rectangle indicating the source bounding box, * or null if the bounding box is unknown. * * @throws IllegalArgumentException if sourceIndex is * negative or greater than the index of the last source. * @throws IllegalArgumentException if destRect is * null. */ protected abstract Rectangle backwardMapRect(Rectangle destRect, int sourceIndex); /** * Returns a conservative estimate of the destination region that * can potentially be affected by the pixels of a rectangle of a * given source. The supplied Rectangle will first be * contracted according to the Interpolation object * characteristics and then forward mapped into destination coordinate * space using forwardMapRect(). The resulting * Rectangle is not clipped to the destination * image bounds. * * @param sourceRect the Rectangle in source coordinates. * @param sourceIndex the index of the source image. * @return a Rectangle indicating the potentially affected * destination region. This will equal the destination bounds * if forwardMapRect() returns null. * * @throws IllegalArgumentException if sourceIndex is * negative or greater than the index of the last source. * @throws IllegalArgumentException if sourceRect is * null. */ public Rectangle mapSourceRect(Rectangle sourceRect, int sourceIndex) { if (sourceRect == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sourceIndex < 0 || sourceIndex >= getNumSources()) { throw new IllegalArgumentException(JaiI18N.getString("Generic1")); } // Cache left and top padding. int lpad = interp.getLeftPadding(); int tpad = interp.getTopPadding(); // Shrink the source Rectangle according to the Interpolation. Rectangle srcRect = (Rectangle)sourceRect.clone(); srcRect.x += lpad; srcRect.y += tpad; srcRect.width -= (lpad + interp.getRightPadding()); srcRect.height -= (tpad + interp.getBottomPadding()); // Map the source Rectangle into destination space. Rectangle destRect = forwardMapRect(srcRect, sourceIndex); // Return Rectangle or destination bounds. return destRect == null ? getBounds() : destRect; } /** * Returns a conservative estimate of the region of a specified * source that is required in order to compute the pixels of a * given destination rectangle. The supplied Rectangle * will first be backward mapped into source coordinate space using * backwardMapRect() and then the resulting context * will be modified according to the Interpolation * object characteristics. The resulting Rectangle * is not clipped to the source image bounds. * * @param destRect the Rectangle in destination coordinates. * @param sourceIndex the index of the source image. * * @return a Rectangle indicating the required source region. * This will equal the bounds of the respective source * if backwardMapRect() returns null. * * @throws IllegalArgumentException if sourceIndex is * negative or greater than the index of the last source. * @throws IllegalArgumentException if destRect is * null. */ public Rectangle mapDestRect(Rectangle destRect, int sourceIndex) { if (destRect == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (sourceIndex < 0 || sourceIndex >= getNumSources()) { throw new IllegalArgumentException(JaiI18N.getString("Generic1")); } // Map the destination Rectangle into the appropriate source space. Rectangle sourceRect = backwardMapRect(destRect, sourceIndex); if(sourceRect == null) { return getSource(sourceIndex).getBounds(); } // Cache left and top padding. int lpad = interp.getLeftPadding(); int tpad = interp.getTopPadding(); // Return padded Rectangle. return new Rectangle(sourceRect.x - lpad, sourceRect.y - tpad, sourceRect.width + lpad + interp.getRightPadding(), sourceRect.height + tpad + interp.getBottomPadding()); } /** * Computes a tile. A new WritableRaster is created to * represent the requested tile. Its width and height are equal to this * image's tile width and tile height respectively. If the requested * tile lies outside of the image's boundary, or if the backward mapped * and padded tile region does not intersect all sources, the created * raster is returned with all of its pixels set to 0. * *

      Whether or not this method performs source cobbling is determined * by the cobbleSources variable set at construction time. * If cobbleSources is true, cobbling is * performed on the source for areas that intersect multiple tiles, * and computeRect(Raster[], WritableRaster, Rectangle) * is called to perform the actual computation. Otherwise, * computeRect(PlanarImage[], WritableRaster, Rectangle) * is called to perform the actual computation. * * @param tileX The X index of the tile. * @param tileY The Y index of the tile. * * @return The tile as a Raster. */ public Raster computeTile(int tileX, int tileY) { // The origin of the tile. Point org = new Point(tileXToX(tileX), tileYToY(tileY)); // Create a new WritableRaster to represent this tile. WritableRaster dest = createWritableRaster(sampleModel, org); // Find the intersection between this tile and the bounds. Rectangle destRect = getTileRect(tileX, tileY).intersection(getBounds()); if (destRect.isEmpty()) { if (setBackground) { ImageUtil.fillBackground(dest, destRect, backgroundValues); } return dest; // tile outside of destination bounds } int numSources = getNumSources(); if (cobbleSources) { Raster[] rasterSources = new Raster[numSources]; // Cobble areas for (int i = 0; i < numSources; i++) { PlanarImage source = getSource(i); Rectangle srcBounds = source.getBounds(); Rectangle srcRect = mapDestRect(destRect, i); if (srcRect == null) { // Set to source bounds. srcRect = srcBounds; } else { if(extender == null && !srcBounds.contains(srcRect)) { // Clip to source bounds. srcRect = srcBounds.intersection(srcRect); } if(!srcRect.intersects(srcBounds)) { // Outside of source bounds. if (setBackground) { ImageUtil.fillBackground(dest, destRect, backgroundValues); } return dest; } } rasterSources[i] = extender != null ? source.getExtendedData(srcRect, extender) : source.getData(srcRect); } computeRect(rasterSources, dest, destRect); for (int i = 0; i < numSources; i++) { Raster sourceData = rasterSources[i]; if(sourceData != null) { PlanarImage source = getSourceImage(i); // Recycle the source tile if(source.overlapsMultipleTiles(sourceData.getBounds())) { recycleTile(sourceData); } } } } else { PlanarImage[] imageSources = new PlanarImage[numSources]; for (int i = 0; i < numSources; i++) { imageSources[i] = getSource(i); } computeRect(imageSources, dest, destRect); } return dest; } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/0000755000175000017500000000000011633360404023174 5ustar mathieumathieujai-core-1.1.4/src/share/classes/javax/media/jai/operator/IDCTDescriptor.java0000644000175000017500000001123510203035544026617 0ustar mathieumathieu/* * $RCSfile: IDCTDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:36 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "IDCT" operation. * *

      The "IDCT" operation computes the inverse even discrete cosine transform * (DCT) of an image. Each band of the destination image is derived by * performing a two-dimensional inverse DCT on the corresponding band of the * source image. * *

      * * * * * * * * *
      Resource List
      Name Value
      GlobalName IDCT
      LocalName IDCT
      Vendor com.sun.media.jai
      Description Computes the inverse discrete cosine transform * of an image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/IDCTDescriptor.html
      Version 1.0

      * *

      No parameters are needed for the "IDCT" operation. * * @see javax.media.jai.OperationDescriptor */ public class IDCTDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "IDCT"}, {"LocalName", "IDCT"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("IDCTDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/IDCTDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")} }; /** Constructor. */ public IDCTDescriptor() { super(resources, 1, null, null, null); } /** Returns true since renderable operation is supported. */ public boolean isRenderableSupported() { return true; } /** * Computes the inverse discrete cosine transform of an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("IDCT", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); return JAI.create("IDCT", pb, hints); } /** * Computes the inverse discrete cosine transform of an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("IDCT", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); return JAI.createRenderable("IDCT", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/RenderableDescriptor.java0000644000175000017500000002703510203035544030144 0ustar mathieumathieu/* * $RCSfile: RenderableDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:43 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.BorderExtender; import javax.media.jai.Interpolation; import javax.media.jai.JAI; import javax.media.jai.KernelJAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; /** * An OperationDescriptor describing the "Renderable" operation. * *

      In renderable image mode the "Renderable" operation produces from a * RenderedImage source a RenderableImage * consisting of a "pyramid" of RenderedImages at progressively * lower resolutions. This operation does not support rendered image mode. * *

      Lower resolution images are produced by invoking the chain of * operations specified via the "downSampler" parameter on the image at the * next higher resolution level of the pyramid. The "downSampler" operation * chain must adhere to the specifications described for the constructors of * the ImageMIPMap class which accept this type of parameter. * The "downSampler" operation chain must reduce the image width and height at * each level of the pyramid. The default operation chain for "downSampler" * is a low pass filtering implemented using a 5x5 separable kernel derived * from the one-dimensional kernel * *

       * [0.05 0.25 0.40 0.25 0.05]
       * 
      * * followed by downsampling by 2. * *

      The number of levels in the pyramid will be such that the maximum of * the width and height of the lowest resolution pyramid level is less than or * equal to the value of the "maxLowResDim" parameter which must be positive. * *

      The minimum X and Y coordinates and height in rendering-independent * coordinates are supplied by the parameters "minX", "minY", and "height", * respectively. The value of "height" must be positive. It is not * necessary to supply a value for the rendering-independent width as this * is derived by multiplying the supplied height by the aspect ratio (width * divided by height) of the source RenderedImage. * *

      * * * * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName Renderable
      LocalName Renderable
      Vendor com.sun.media.jai
      Description Produces a RenderableImage from a RenderedImage.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/RenderableDescriptor.html
      Version 1.0
      arg0Desc The operation chain used to derive the lower resolution images.
      arg1Desc The maximum dimension of the lowest resolution pyramid level.
      arg2Desc The minimum rendering-independent X coordinate of the destination.
      arg3Desc The minimum rendering-independent Y coordinate of the destination.
      arg4Desc The rendering-independent height.

      * *

      * * * * * * * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      downSampler RenderedOpnull
      maxLowResDim Integer64
      minX Float0.0F
      minY Float0.0F
      height Float1.0F

      * * @see javax.media.jai.ImageMIPMap * @see javax.media.jai.OperationDescriptor */ public class RenderableDescriptor extends OperationDescriptorImpl { // One-dimensional kernel from which to create the default separable // two-dimensional low-pass filter in the downsampler chain. private static final float[] DEFAULT_KERNEL_1D = {0.05F, 0.25F, 0.4F, 0.25F, 0.05F}; /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Renderable"}, {"LocalName", "Renderable"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("RenderableDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/RenderableDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("RenderableDescriptor1")}, {"arg1Desc", JaiI18N.getString("RenderableDescriptor2")}, {"arg2Desc", JaiI18N.getString("RenderableDescriptor3")}, {"arg3Desc", JaiI18N.getString("RenderableDescriptor4")}, {"arg4Desc", JaiI18N.getString("RenderableDescriptor5")} }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { RenderedOp.class, Integer.class, Float.class, Float.class, Float.class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "downSampler", "maxLowResDim", "minX", "minY", "height" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { null, new Integer(64), new Float(0.0F), new Float(0.0F), new Float(1.0F) }; /** Constructor. */ public RenderableDescriptor() { super(resources, null, // rendered mode not supported -> null new Class[] {RenderedImage.class}, // renderable mode paramClasses, paramNames, paramDefaults); } /** Indicates that rendered operation is supported. */ public boolean isRenderedSupported() { return false; } /** Indicates that renderable operation is supported. */ public boolean isRenderableSupported() { return true; } /** Validates input parameters in the renderable layer. */ protected boolean validateParameters(ParameterBlock args, StringBuffer msg) { // Set the default down sampler if necessary. if(args.getNumParameters() == 0 || args.getObjectParameter(0) == null) { // Create the down-sampler operation chain consisting of a 5x5 // Gaussian filter followed by a subsampling by 2. Use a kernel // which satisfies the description in P. J. Burt and // E. H. Adelson, "The Laplacian pyramid as a compact image code", // IEEE Transactions on Communications., pp. 532-540, 1983. // Add the filtering operation. ParameterBlock pb = new ParameterBlock(); KernelJAI kernel = new KernelJAI(DEFAULT_KERNEL_1D.length, DEFAULT_KERNEL_1D.length, DEFAULT_KERNEL_1D.length/2, DEFAULT_KERNEL_1D.length/2, DEFAULT_KERNEL_1D, DEFAULT_KERNEL_1D); pb.add(kernel); BorderExtender extender = BorderExtender.createInstance(BorderExtender.BORDER_COPY); RenderingHints hints = JAI.getDefaultInstance().getRenderingHints(); if(hints == null) { hints = new RenderingHints(JAI.KEY_BORDER_EXTENDER, extender); } else { hints.put(JAI.KEY_BORDER_EXTENDER, extender); } RenderedOp filter = new RenderedOp("convolve", pb, hints); // Add the subsampling operation. pb = new ParameterBlock(); pb.addSource(filter); pb.add(new Float(0.5F)).add(new Float(0.5F)); pb.add(new Float(0.0F)).add(new Float(0.0F)); pb.add(Interpolation.getInstance(Interpolation.INTERP_NEAREST)); RenderedOp downSampler = new RenderedOp("scale", pb, null); args.set(downSampler, 0); } // Verify the generic integrity of the arguments and set defaults. if(!super.validateParameters(args, msg)) { return false; } // Make sure the maximum dimension and the height are both positive. if(args.getIntParameter(1) <= 0) { msg.append(getName() + " " + JaiI18N.getString("RenderableDescriptor6")); return false; } else if(args.getFloatParameter(4) <= 0.0F) { msg.append(getName() + " " + JaiI18N.getString("RenderableDescriptor7")); return false; } return true; } /** * Produces a RenderableImage from a RenderedImage. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderedImage source 0. * @param downSampler The operation chain used to derive the lower resolution images. * May be null. * @param maxLowResDim The maximum dimension of the lowest resolution pyramid level. * May be null. * @param minX The minimum rendering-independent X coordinate of the destination. * May be null. * @param minY The minimum rendering-independent Y coordinate of the destination. * May be null. * @param height The rendering-independent height. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderedImage source0, RenderedOp downSampler, Integer maxLowResDim, Float minX, Float minY, Float height, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Renderable", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("downSampler", downSampler); pb.setParameter("maxLowResDim", maxLowResDim); pb.setParameter("minX", minX); pb.setParameter("minY", minY); pb.setParameter("height", height); return JAI.createRenderable("Renderable", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/BinarizeDescriptor.java0000644000175000017500000001614010203035544027637 0ustar mathieumathieu/* * $RCSfile: BinarizeDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:30 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Binarize" operation. * *

      The "Binarize" operation takes one rendered or renderable single-banded * source image and a threshold value and applies a thresholding operation to * the produce a bilevel image. * *

      By default the destination image bounds are equal to those of the * source image. The SampleModel of the destination image is * an instance of MultiPixelPackedSampleModel. * *

      The pseudocode for "Binarize" is as follows: *

       
       *      dst(x, y) = src(x, y) >= threshold ? 1 : 0;
       * 
      * *

      * * * * * * * * * *
      Resource List
      Name Value
      GlobalName Binarize
      LocalName Binarize
      Vendor com.sun.media.jai
      Description Thresholds an image into a bilevel image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/BinarizeDescriptor.html
      Version 1.1
      arg0Desc The threshold value.

      * *

      * * * * * *
      Parameter List
      Name Class TypeDefault Value
      threshold java.lang.DoubleNO_PARAMETER_DEFAULT

      * * @see javax.media.jai.OperationDescriptor * * @since JAI 1.1 */ public class BinarizeDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Binarize"}, {"LocalName", "Binarize"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("BinarizeDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/BinarizeDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("BinarizeDescriptor1")} }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "threshold" }; /** * The parameter class list for this operation. * The number of threshold value provided should be 1. */ private static final Class[] paramClasses = { java.lang.Double.class }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT }; private static final String[] supportedModes = { "rendered", "renderable" }; /** Constructor. */ public BinarizeDescriptor() { super(resources, supportedModes, 1, paramNames, paramClasses, paramDefaults, null); } /** * Validates the input source. * *

      In addition to the standard checks performed by the * superclass method, this method checks that the source image * is single-banded. */ protected boolean validateSources(String modeName, ParameterBlock args, StringBuffer msg) { if (!super.validateSources(modeName, args, msg)) { return false; } if (!modeName.equalsIgnoreCase("rendered")) return true; RenderedImage source = (RenderedImage)(args.getSource(0)); int numBands = source.getSampleModel().getNumBands(); if (numBands != 1){ msg.append(getName() + " " + JaiI18N.getString("BinarizeDescriptor2")); return false; } return true; } /** * Binarize an image from a threshold value. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param threshold Argment must be of type java.lang.Double. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if threshold is null. */ public static RenderedOp create(RenderedImage source0, Double threshold, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Binarize", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("threshold", threshold); return JAI.create("Binarize", pb, hints); } /** * Binarize an image from a threshold value. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param threshold Argment must be of type java.lang.Double. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if threshold is null. */ public static RenderableOp createRenderable(RenderableImage source0, Double threshold, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Binarize", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("threshold", threshold); return JAI.createRenderable("Binarize", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/WarpDescriptor.java0000644000175000017500000003007710203035544027012 0ustar mathieumathieu/* * $RCSfile: WarpDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:46 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.util.PropertyGeneratorImpl; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.GeometricOpImage; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationNearest; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PlanarImage; import javax.media.jai.PropertyGenerator; import javax.media.jai.ROI; import javax.media.jai.ROIShape; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.Warp; import javax.media.jai.registry.RenderedRegistryMode; /** * This property generator computes the properties for the operation * "Warp" dynamically. */ class WarpPropertyGenerator extends PropertyGeneratorImpl { /** Constructor. */ public WarpPropertyGenerator() { super(new String[] {"ROI"}, new Class[] {ROI.class}, new Class[] {RenderedOp.class}); } /** * Returns the specified property. * * @param name Property name. * @param opNode Operation node. */ public Object getProperty(String name, Object opNode) { validate(name, opNode); if(opNode instanceof RenderedOp && name.equalsIgnoreCase("roi")) { RenderedOp op = (RenderedOp)opNode; ParameterBlock pb = op.getParameterBlock(); // Retrieve the rendered source image and its ROI. RenderedImage src = (RenderedImage)pb.getRenderedSource(0); Object property = src.getProperty("ROI"); if (property == null || property.equals(java.awt.Image.UndefinedProperty) || !(property instanceof ROI)) { return java.awt.Image.UndefinedProperty; } // Return undefined also if source ROI is empty. ROI srcROI = (ROI)property; if (srcROI.getBounds().isEmpty()) { return java.awt.Image.UndefinedProperty; } // Retrieve the Interpolation object. Interpolation interp = (Interpolation)pb.getObjectParameter(1); // Determine the effective source bounds. Rectangle srcBounds = null; PlanarImage dst = op.getRendering(); if (dst instanceof GeometricOpImage && ((GeometricOpImage)dst).getBorderExtender() == null) { srcBounds = new Rectangle(src.getMinX() + interp.getLeftPadding(), src.getMinY() + interp.getTopPadding(), src.getWidth() - interp.getWidth() + 1, src.getHeight() - interp.getHeight() + 1); } else { srcBounds = new Rectangle(src.getMinX(), src.getMinY(), src.getWidth(), src.getHeight()); } // If necessary, clip the ROI to the effective source bounds. if(!srcBounds.contains(srcROI.getBounds())) { srcROI = srcROI.intersect(new ROIShape(srcBounds)); } // Set the nearest neighbor interpolation object. Interpolation interpNN = interp instanceof InterpolationNearest ? interp : Interpolation.getInstance(Interpolation.INTERP_NEAREST); // Retrieve the Warp object. Warp warp = (Warp)pb.getObjectParameter(0); // Create the warped ROI. ROI dstROI = new ROI(JAI.create("warp", srcROI.getAsImage(), warp, interpNN)); // Retrieve the destination bounds. Rectangle dstBounds = op.getBounds(); // If necessary, clip the warped ROI to the destination bounds. if(!dstBounds.contains(dstROI.getBounds())) { dstROI = dstROI.intersect(new ROIShape(dstBounds)); } // Return the warped and possibly clipped ROI. return dstROI; } return java.awt.Image.UndefinedProperty; } } /** * An OperationDescriptor describing the "Warp" operation. * *

      The "Warp" operation performs (possibly filtered) general * warping on an image. * *

      The destination bounds may be specified by an {@link ImageLayout} * hint provided via a {@link RenderingHints} supplied to the operation. If * no bounds are so specified, then the destination bounds will be set to * the minimum bounding rectangle of the forward mapped source bounds * calculated using {@link Warp#mapSourceRect(Rectangle)} or, failing that, * {@link Warp#mapSourcePoint(Point2D)} applied to the vertices of the * source bounds. If forward mapping by both methods is not viable, then * an approximate affine mapping will be created and used to determine the * destination bounds by forward mapping the source bounds. If this approach * also fails, then the destination bounds will be set to the source bounds. * *

      "Warp" defines a PropertyGenerator that * performs an identical transformation on the "ROI" property of the * source image, which can be retrieved by calling the * getProperty method with "ROI" as the property name. * *

      The parameter, "backgroundValues", is defined to * fill the background with the user-specified background * values. These background values will be translated into background * colors by the ColorModel when the image is displayed. * With the default value, {0.0}, of this parameter, * the background pixels are filled with 0s. If the provided array * length is smaller than the number of bands, the first element of * the provided array is used for all the bands. If the provided values * are out of the data range of the destination image, they will be clamped * into the proper range. * *

      It should be noted that this operation automatically adds a * value of Boolean.TRUE for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL to the given * configuration so that the operation is performed * on the pixel values instead of being performed on the indices into * the color map if the source(s) have an IndexColorModel. * This addition will take place only if a value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL has not already been * provided by the user. Note that the configuration Map * is cloned before the new hint is added to it. The operation can be * smart about the value of the JAI.KEY_REPLACE_INDEX_COLOR_MODEL * RenderingHints, i.e. while the default value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL is * Boolean.TRUE, in some cases the operator could set the * default. * *

      * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName Warp
      LocalName Warp
      Vendor com.sun.media.jai
      Description Warps an image according * to a specified Warp object.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/WarpDescriptor.html
      Version 1.0
      arg0Desc The Warp object.
      arg1Desc The interpolation method.

      * *

      * * * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      warp javax.media.jai.WarpNO_PARAMETER_DEFAULT
      interpolation javax.media.jai.InterpolationInterpolationNearest
      backgroundValues double[]{0.0}

      * * @see javax.media.jai.Interpolation * @see javax.media.jai.Warp * @see javax.media.jai.OperationDescriptor */ public class WarpDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation and * specify the parameter list for the "Warp" operation. */ private static final String[][] resources = { {"GlobalName", "Warp"}, {"LocalName", "Warp"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("WarpDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/WarpDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("WarpDescriptor1")}, {"arg1Desc", JaiI18N.getString("WarpDescriptor2")}, {"arg2Desc", JaiI18N.getString("WarpDescriptor3")} }; /** The parameter names for the "Warp" operation. */ private static final String[] paramNames = { "warp", "interpolation", "backgroundValues" }; /** The parameter class types for the "Warp" operation. */ private static final Class[] paramClasses = { javax.media.jai.Warp.class, javax.media.jai.Interpolation.class, double[].class }; /** The parameter default values for the "Warp" operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT, Interpolation.getInstance(Interpolation.INTERP_NEAREST), new double[] {0.0} }; /** Constructor. */ public WarpDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** * Returns an array of PropertyGenerators implementing * property inheritance for the "Warp" operation. * * @return An array of property generators. */ public PropertyGenerator[] getPropertyGenerators() { PropertyGenerator[] pg = new PropertyGenerator[1]; pg[0] = new WarpPropertyGenerator(); return pg; } /** * Warps an image according to a specified Warp object. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param warp The warp object. * @param interpolation The interpolation method. * May be null. * @param backgroundValues The user-specified background values. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if warp is null. */ public static RenderedOp create(RenderedImage source0, Warp warp, Interpolation interpolation, double[] backgroundValues, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Warp", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("warp", warp); pb.setParameter("interpolation", interpolation); pb.setParameter("backgroundValues", backgroundValues); return JAI.create("Warp", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/HistogramDescriptor.java0000644000175000017500000002651110203035544030034 0ustar mathieumathieu/* * $RCSfile: HistogramDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:36 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.ROI; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; /** * This OperationDescriptor defines the "Histogram" * operation. * *

      A histogram of an image is represented by a list of "bins" * where each bin is the total number of pixel samples of the image * whose values lie within a given range. This data are encapulated * in the javax.media.jai.Histogram object, and may be * retrieved by calling the getProperty method on this * operator with "histogram" as the property name. * *

      At a request for the histogram property, this operator scans * the specific region of the source image, generates the pixel count * data, and returns an instance of the Histogram class * where the data are stored. The source image's pixels are unchanged * by this operator. * *

      The region-of-interest (ROI), within which the pixels are counted, * does not have to be a rectangle. It may be null, in * which case the entire image is scanned to accumulate the histogram. * *

      The set of pixels scanned may be further reduced by specifying * the "xPeriod" and "yPeriod" parameters that represent the sampling * rate along the two axis. These variables may not be less than 1. * If they are not set, the default value of 1 is used so that every * pixel within the ROI is counted. * *

      The three arguments, numBins, lowValue, * and highValue, define the type of the histogram to be * generated. Please see the Histogram specification for * their detailed descriptions. The three arrays must either have an * array length of 1, in which case the same value is applied to all * bands of the source image, or an array length that equals to the * number of bands of the source image, in which case each value is * applied to its corresponding band. The numBins must * all be greater than 0, and each lowValue must be less * than its corresponding highValue. Note that the default * values of these three parameters are specific to the case wherein * the image data are of type byte. For other image data * types the values of these parameters should be supplied explicitely. * *

      * * * * * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName Histogram
      LocalName Histogram
      Vendor com.sun.media.jai
      Description Generates a histogram based on the pixel values * within a specific region of an image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/HistogramDescriptor.html
      Version 1.1
      arg0Desc The region of the image to be scanned.
      arg1Desc The horizontal sampling rate; * may not be less than 1.
      arg2Desc The vertical sampling rate; * may not be less than 1.
      arg3Desc The number of bins for each band.
      arg4Desc The lowest inclusive pixel value to be * checked for each band.
      arg5Desc The highest exclusive pixel value to be * checked for each band.

      * *

      * * * * * * * * * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      roi javax.media.jai.ROInull
      xPeriod java.lang.Integer1
      yPeriod java.lang.Integer1
      numBins int[]{256}
      lowValue double[]{0.0}
      highValue double[]{256.0}

      * * @see javax.media.jai.Histogram * @see javax.media.jai.ROI * @see javax.media.jai.OperationDescriptor */ public class HistogramDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Histogram"}, {"LocalName", "Histogram"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("HistogramDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/HistogramDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion2")}, {"arg0Desc", JaiI18N.getString("HistogramDescriptor1")}, {"arg1Desc", JaiI18N.getString("HistogramDescriptor2")}, {"arg2Desc", JaiI18N.getString("HistogramDescriptor3")}, {"arg3Desc", JaiI18N.getString("HistogramDescriptor4")}, {"arg4Desc", JaiI18N.getString("HistogramDescriptor5")}, {"arg5Desc", JaiI18N.getString("HistogramDescriptor6")} }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "roi", "xPeriod", "yPeriod", "numBins", "lowValue", "highValue" }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { javax.media.jai.ROI.class, java.lang.Integer.class, java.lang.Integer.class, int[].class, double[].class, double[].class }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { null, new Integer(1), new Integer(1), new int[] {256}, new double[] {0.0}, new double[] {256.0} }; /** Constructor. */ public HistogramDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** * Returns the minimum legal value of a specified numeric parameter * for this operation. */ public Number getParamMinValue(int index) { switch (index) { case 1: case 2: return new Integer(1); case 0: case 3: case 4: case 5: return null; default: throw new ArrayIndexOutOfBoundsException(); } } /** * Returns true if this operation is capable of handling * the input parameters. * *

      In addition to the default validations done in the super class, * this method verifies that each element of numBins is * greater than 0, and each lowValue is less than its * corresponding highValue. * * @throws IllegalArgumentException If args is null. * @throws IllegalArgumentException If msg is null * and the validation fails. */ protected boolean validateParameters(ParameterBlock args, StringBuffer msg) { if ( args == null || msg == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (!super.validateParameters(args, msg)) { return false; } int[] numBins = (int[])args.getObjectParameter(3); double[] lowValue = (double[])args.getObjectParameter(4); double[] highValue = (double[])args.getObjectParameter(5); int l1 = numBins.length; int l2 = lowValue.length; int l3 = highValue.length; int length = Math.max(l1, Math.max(l2, l3)); for (int i = 0; i < length; i++) { if (i < l1 && numBins[i] <= 0) { msg.append(getName() + " " + JaiI18N.getString("HistogramDescriptor7")); return false; } double l = i < l2 ? lowValue[i] : lowValue[0]; double h = i < l3 ? highValue[i] : highValue[0]; if (l >= h) { msg.append(getName() + " " + JaiI18N.getString("HistogramDescriptor8")); return false; } } return true; } /** * Generates a histogram based on the pixel values within a specific region of an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param roi The region of the image to be scanned. * May be null. * @param xPeriod The horizontal sampling rate; may not be less than 1. * May be null. * @param yPeriod The vertical sampling rate; may not be less than 1. * May be null. * @param numBins The number of bins for each band. * May be null. * @param lowValue The lowest inclusive pixel value to be checked for each band. * May be null. * @param highValue The highest exclusive pixel value to be checked for each band. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, ROI roi, Integer xPeriod, Integer yPeriod, int[] numBins, double[] lowValue, double[] highValue, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Histogram", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("roi", roi); pb.setParameter("xPeriod", xPeriod); pb.setParameter("yPeriod", yPeriod); pb.setParameter("numBins", numBins); pb.setParameter("lowValue", lowValue); pb.setParameter("highValue", highValue); return JAI.create("Histogram", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/AndDescriptor.java0000644000175000017500000001626710203035544026610 0ustar mathieumathieu/* * $RCSfile: AndDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:29 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "And" operation. * *

      The And operation takes two source images, and performs a bit-wise * logical "and" on every pair of pixels, one from each source image, * of the corresponding position and band. No additional parameters are * required. * *

      Both source images must have integral data types. The two * data types may be different. * *

      Unless altered by an ImageLayout hint, the * destination image bound is the intersection of the two source image * bounds. If the two sources don't intersect, the destination will * have a width and height of 0. The number of bands of the * destination image is equal to the lesser number of bands of the * sources, and the data type is the smallest data type with * sufficient range to cover the range of both source data types. * *

      The following matrix defines the logical "and" operation. *

      * * * * * * *
      Logical "and"
      src1 src2 Result
      1 1 1
      1 0 0
      0 1 0
      0 0 0

      * *

      The destination pixel values are defined by the pseudocode: *

       * dst[x][y][b] = srcs[0][x][y][b] & srcs[1][x][y][b];
       * 
      * *

      * * * * * * * * *
      Resource List
      Name Value
      GlobalName And
      LocalName And
      Vendor com.sun.media.jai
      Description Logically "ands" two images.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/AndDescriptor.html
      Version 1.0

      * *

      No parameters are needed for this operation. * * @see javax.media.jai.OperationDescriptor */ public class AndDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "And"}, {"LocalName", "And"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("AndDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/AndDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")} }; private static final String[] supportedModes = { "rendered", "renderable" }; /** Constructor. */ public AndDescriptor() { super(resources, supportedModes, 2, null, null, null, null); } /** * Validates the input sources. * *

      In addition to the standard checks performed by the * superclass method, this method checks that the source images * are of integral data type. */ protected boolean validateSources(String modeName, ParameterBlock args, StringBuffer msg) { if (!super.validateSources(modeName, args, msg)) { return false; } if (!modeName.equalsIgnoreCase("rendered")) return true; for (int i = 0; i < 2; i++) { RenderedImage src = args.getRenderedSource(0); int dtype = src.getSampleModel().getDataType(); if (dtype != DataBuffer.TYPE_BYTE && dtype != DataBuffer.TYPE_USHORT && dtype != DataBuffer.TYPE_SHORT && dtype != DataBuffer.TYPE_INT) { msg.append(getName() + " " + JaiI18N.getString("AndDescriptor1")); return false; } } return true; } /** * Logically "ands" two images. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param source1 RenderedImage source 1. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if source1 is null. */ public static RenderedOp create(RenderedImage source0, RenderedImage source1, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("And", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setSource("source1", source1); return JAI.create("And", pb, hints); } /** * Logically "ands" two images. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param source1 RenderableImage source 1. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if source1 is null. */ public static RenderableOp createRenderable(RenderableImage source0, RenderableImage source1, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("And", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setSource("source1", source1); return JAI.createRenderable("And", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/CropDescriptor.java0000644000175000017500000003061310203035544027000 0ustar mathieumathieu/* * $RCSfile: CropDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:33 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.Rectangle2D; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PlanarImage; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Crop" operation. * *

      The Crop operation takes one rendered or renderable image and * crops the image to a specified rectangular area. The rectangular * area must not be empty, and must be fully contained with the source * image bounds. * *

      For rendered images the supplied origin and dimensions are used to * determine the smallest rectangle with integral origin and dimensions which * encloses the rectangular area requested. * *

      For renderable images the rectangular area is specified in * rendering-independent coordinates. When the image is rendered this area * will be mapped to rendered image coordinates using the affine transform * supplied for the rendering. The crop bounds in rendered coordinates are * defined to be the minimum bounding box of the rectangular area mapped to * rendered image coordinates. * *

      * * * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName Crop
      LocalName Crop
      Vendor com.sun.media.jai
      Description Crops the pixel values of a rendered image * to a specified rectangle.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/CropDescriptor.html
      Version 1.0
      arg0Desc The x origin for each band.
      arg1Desc The y origin for each band.
      arg2Desc The width for each band.
      arg3Desc The height for each band.

      * *

      * * * * * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      x FloatNO_PARAMETER_DEFAULT
      y FloatNO_PARAMETER_DEFAULT
      width FloatNO_PARAMETER_DEFAULT
      height FloatNO_PARAMETER_DEFAULT

      * * @see javax.media.jai.OperationDescriptor */ public class CropDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Crop"}, {"LocalName", "Crop"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("CropDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/CropDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("CropDescriptor1")}, {"arg1Desc", JaiI18N.getString("CropDescriptor2")}, {"arg2Desc", JaiI18N.getString("CropDescriptor3")}, {"arg3Desc", JaiI18N.getString("CropDescriptor4")} }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { Float.class, Float.class, Float.class, Float.class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "x", "y", "width", "height" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT, NO_PARAMETER_DEFAULT, NO_PARAMETER_DEFAULT, NO_PARAMETER_DEFAULT }; private static final String[] supportedModes = { "rendered", "renderable" }; /** Constructor. */ public CropDescriptor() { super(resources, supportedModes, 1, paramNames, paramClasses, paramDefaults, null); } /** * Validates the input source and parameters. * *

      In addition to the standard checks performed by the * superclass method, this method checks that "x", "y", "width", * and "height" form a rectangle that is not empty and that * is fully contained within the bounds of the source image. */ public boolean validateArguments(String modeName, ParameterBlock args, StringBuffer msg) { if (!super.validateArguments(modeName, args, msg)) { return false; } if (modeName.equalsIgnoreCase("rendered")) return validateRenderedArgs(args, msg); if (modeName.equalsIgnoreCase("renderable")) return validateRenderableArgs(args, msg); return true; } /** * Validates the input source and parameters in the rendered mode. * *

      In addition to the standard checks performed by the * superclass method, this method checks that "x", "y", "width", * and "height" form a rectangle that is not empty and that * is fully contained within the bounds of the source image. */ private boolean validateRenderedArgs(ParameterBlock args, StringBuffer msg) { // Get parameters. float x_req = args.getFloatParameter(0); float y_req = args.getFloatParameter(1); float w_req = args.getFloatParameter(2); float h_req = args.getFloatParameter(3); // Create required rectangle. Rectangle rect_req = (new Rectangle2D.Float(x_req, y_req, w_req, h_req)).getBounds(); // Check for an empty rectangle. if(rect_req.isEmpty()) { msg.append(getName() + " " + JaiI18N.getString("CropDescriptor5")); return false; } // Check for out-of-bounds RenderedImage src = (RenderedImage)args.getSource(0); Rectangle srcBounds = new Rectangle(src.getMinX(), src.getMinY(), src.getWidth(), src.getHeight()); if (!srcBounds.contains(rect_req)) { msg.append(getName() + " " + JaiI18N.getString("CropDescriptor6")); return false; } return true; } /** * Validates the input source and parameters in the renderable mode. * *

      In addition to the standard checks performed by the * superclass method, this method checks that "x", "y", "width", * and "height" form a rectangle that is not empty and that * is fully contained within the bounds of the source image. */ private boolean validateRenderableArgs(ParameterBlock args, StringBuffer msg) { // Get parameters. float x_req = args.getFloatParameter(0); float y_req = args.getFloatParameter(1); float w_req = args.getFloatParameter(2); float h_req = args.getFloatParameter(3); // Create required rectangle. Rectangle2D rect_req = new Rectangle2D.Float(x_req, y_req, w_req, h_req); // Check for an empty rectangle. if(rect_req.isEmpty()) { msg.append(getName() + " " + JaiI18N.getString("CropDescriptor5")); return false; } // Check for out-of-bounds RenderableImage src = (RenderableImage)args.getSource(0); Rectangle2D rect_src = new Rectangle2D.Float(src.getMinX(), src.getMinY(), src.getWidth(), src.getHeight()); if (!rect_src.contains(rect_req)) { msg.append(getName() + " " + JaiI18N.getString("CropDescriptor6")); return false; } return true; } /** * Performs cropping to a specified bounding box. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param x The x origin of the cropping operation. * @param y The y origin of the cropping operation. * @param width The width of the cropping operation. * @param height The height of the cropping operation. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if x is null. * @throws IllegalArgumentException if y is null. * @throws IllegalArgumentException if width is null. * @throws IllegalArgumentException if height is null. */ public static RenderedOp create(RenderedImage source0, Float x, Float y, Float width, Float height, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Crop", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("x", x); pb.setParameter("y", y); pb.setParameter("width", width); pb.setParameter("height", height); return JAI.create("Crop", pb, hints); } /** * Performs cropping to a specified bounding box. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param x The x origin of the cropping operation. * @param y The y origin of the cropping operation. * @param width The width of the cropping operation. * @param height The height of the cropping operation. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if x is null. * @throws IllegalArgumentException if y is null. * @throws IllegalArgumentException if width is null. * @throws IllegalArgumentException if height is null. */ public static RenderableOp createRenderable(RenderableImage source0, Float x, Float y, Float width, Float height, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Crop", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("x", x); pb.setParameter("y", y); pb.setParameter("width", width); pb.setParameter("height", height); return JAI.createRenderable("Crop", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/OrDescriptor.java0000644000175000017500000001626210203035544026461 0ustar mathieumathieu/* * $RCSfile: OrDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:41 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Or" operation. * *

      The Or operation takes two rendered or renderable images, and * performs bit-wise logical "or" on every pair of pixels, one from * each source image of the corresponding position and band. No * additional parameters are required. * *

      Both source images must have integral data types. The two * data types may be different. * *

      Unless altered by an ImageLayout hint, the * destination image bound is the intersection of the two source image * bounds. If the two sources don't intersect, the destination will * have a width and height of 0. The number of bands of the * destination image is equal to the lesser number of bands of the * sources, and the data type is the smallest data type with * sufficient range to cover the range of both source data types. * *

      The following matrix defines the logical "or" operation. *

      * * * * * * *
      Logical "or"
      src1 src2 Result
      0 0 0
      0 1 1
      1 0 1
      1 1 1

      * *

      The destination pixel values are defined by the pseudocode: *

       * dst[x][y][b] = srcs[0][x][y][b] | srcs[1][x][y][b];
       * 
      * *

      * * * * * * * * *
      Resource List
      Name Value
      GlobalName Or
      LocalName Or
      Vendor com.sun.media.jai
      Description Logically "ors" two images.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/OrDescriptor.html
      Version 1.0

      * *

      No parameters are needed for this operation. * * @see javax.media.jai.OperationDescriptor */ public class OrDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Or"}, {"LocalName", "Or"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("OrDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/OrDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")} }; private static final String[] supportedModes = { "rendered", "renderable" }; /** Constructor. */ public OrDescriptor() { super(resources, supportedModes, 2, null, null, null, null); } /** * Validates the input sources. * *

      In addition to the standard checks performed by the * superclass method, this method checks that the source images * are of integral data type. */ protected boolean validateSources(String modeName, ParameterBlock args, StringBuffer msg) { if (!super.validateSources(modeName, args, msg)) { return false; } if (!modeName.equalsIgnoreCase("rendered")) return true; for (int i = 0; i < 2; i++) { RenderedImage src = args.getRenderedSource(i); int dtype = src.getSampleModel().getDataType(); if (dtype != DataBuffer.TYPE_BYTE && dtype != DataBuffer.TYPE_USHORT && dtype != DataBuffer.TYPE_SHORT && dtype != DataBuffer.TYPE_INT) { msg.append(getName() + " " + JaiI18N.getString("OrDescriptor1")); return false; } } return true; } /** * Logically "ors" two images. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param source1 RenderedImage source 1. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if source1 is null. */ public static RenderedOp create(RenderedImage source0, RenderedImage source1, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Or", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setSource("source1", source1); return JAI.create("Or", pb, hints); } /** * Logically "ors" two images. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param source1 RenderableImage source 1. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if source1 is null. */ public static RenderableOp createRenderable(RenderableImage source0, RenderableImage source1, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Or", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setSource("source1", source1); return JAI.createRenderable("Or", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/MinFilterShape.java0000644000175000017500000000151610203035544026710 0ustar mathieumathieu/* * $RCSfile: MinFilterShape.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:40 $ * $State: Exp $ */ package javax.media.jai.operator; import javax.media.jai.EnumeratedParameter; /** * Class used to represent the acceptable values of the "maskShape" * parameter of the "MinFilter" operation. Acceptable values for the * "maskShape" parameter are defined in the MinFilterDescriptor * by the constants MIN_MASK_SQUARE, MIN_MASK_PLUS, * MIN_MASK_X, and * MIN_MASK_SQUARE_SEPARABLE. * * @since JAI 1.1 */ public final class MinFilterShape extends EnumeratedParameter { MinFilterShape(String name, int value) { super(name, value); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/AffineDescriptor.java0000644000175000017500000003712110203035544027266 0ustar mathieumathieu/* * $RCSfile: AffineDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:29 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.util.PropertyGeneratorImpl; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.GeometricOpImage; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationNearest; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PlanarImage; import javax.media.jai.PropertyGenerator; import javax.media.jai.ROI; import javax.media.jai.ROIShape; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * This property generator computes the properties for the operation * "Affine" dynamically. */ class AffinePropertyGenerator extends PropertyGeneratorImpl { /** Constructor. */ public AffinePropertyGenerator() { super(new String[] {"ROI"}, new Class[] {ROI.class}, new Class[] {RenderedOp.class}); } /** * Returns the specified property in the rendered layer. * * @param name Property name. * @param opNode Operation node. */ public Object getProperty(String name, Object opNode) { validate(name, opNode); if(opNode instanceof RenderedOp && name.equalsIgnoreCase("roi")) { RenderedOp op = (RenderedOp)opNode; ParameterBlock pb = op.getParameterBlock(); // Retrieve the rendered source image and its ROI. RenderedImage src = pb.getRenderedSource(0); Object property = src.getProperty("ROI"); if (property == null || property.equals(java.awt.Image.UndefinedProperty) || !(property instanceof ROI)) { return java.awt.Image.UndefinedProperty; } ROI srcROI = (ROI)property; // Retrieve the Interpolation object. Interpolation interp = (Interpolation)pb.getObjectParameter(1); // Determine the effective source bounds. Rectangle srcBounds = null; PlanarImage dst = op.getRendering(); if (dst instanceof GeometricOpImage && ((GeometricOpImage)dst).getBorderExtender() == null) { srcBounds = new Rectangle(src.getMinX() + interp.getLeftPadding(), src.getMinY() + interp.getTopPadding(), src.getWidth() - interp.getWidth() + 1, src.getHeight() - interp.getHeight() + 1); } else { srcBounds = new Rectangle(src.getMinX(), src.getMinY(), src.getWidth(), src.getHeight()); } // If necessary, clip the ROI to the effective source bounds. if (!srcBounds.contains(srcROI.getBounds())) { srcROI = srcROI.intersect(new ROIShape(srcBounds)); } // Retrieve the AffineTransform object. AffineTransform transform = (AffineTransform)pb.getObjectParameter(0); // Create the transformed ROI. ROI dstROI = srcROI.transform((AffineTransform)transform); // Retrieve the destination bounds. Rectangle dstBounds = op.getBounds(); // If necessary, clip the transformed ROI to the // destination bounds. if (!dstBounds.contains(dstROI.getBounds())) { dstROI = dstROI.intersect(new ROIShape(dstBounds)); } // Return the transformed and possibly clipped ROI. return dstROI; } return java.awt.Image.UndefinedProperty; } } /** * An OperationDescriptor describing the "Affine" operation. * *

      The Affine operation performs (possibly filtered) affine * mapping on a rendered or renderable source image. * *

      The relationship between the source and the destination pixels * is defined as follows. For each pixel (x, y) of the destination, * the source value at the fractional subpixel position (x', y') is * constructed by means of an Interpolation object and written to the * destination. * * The mapping between the destination pixel (x, y) and the source * position (x', y') is given by: * *

       *    x' = m[0][0] * x + m[0][1] * y + m[0][2]
       *    y' = m[1][0] * x + m[1][1] * y + m[1][2]
       * 
      * * where m is a 3x2 transform matrix that inverts the matrix supplied * as the "transform" argument. * *

      When interpolations which require padding the source such as Bilinear * or Bicubic interpolation are specified, the source needs to be extended * such that it has the extra pixels needed to compute all the destination * pixels. This extension is performed via the BorderExtender * class. The type of Border Extension can be specified as a * RenderingHint to the JAI.create method. * *

      The parameter, "backgroundValues", is defined to * fill the background with the user-specified background * values. These background values will be translated into background * colors by the ColorModel when the image is displayed. * With the default value, {0.0}, of this parameter, * the background pixels are filled with 0s. If the provided array * length is smaller than the number of bands, the first element of * the provided array is used for all the bands. If the provided values * are out of the data range of the destination image, they will be clamped * into the proper range. * *

      If no BorderExtender is specified (is null), the source will * not be extended. The transformed image size is still the same as if * the source had been extended. However, since there is insufficient * source to compute all the destination pixels, only that subset of * the destination image's pixels which can be computed will be * written in the destination. The rest of the destination will be * set to the user-specified background values. * *

      It may be noted that the minX, minY, width and height hints as * specified through the JAI.KEY_IMAGE_LAYOUT hint in the * RenderingHints object are not honored, as this operator * calculates the destination image bounds itself. The other * ImageLayout hints, like tileWidth and tileHeight, * however are honored. * *

      It should be noted that this operation automatically adds a * value of Boolean.TRUE for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL to the given * configuration so that the operation is performed * on the pixel values instead of being performed on the indices into * the color map if the source(s) have an IndexColorModel. * This addition will take place only if a value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL has not already been * provided by the user. Note that the configuration Map * is cloned before the new hint is added to it. The operation can be * smart about the value of the JAI.KEY_REPLACE_INDEX_COLOR_MODEL * RenderingHints, i.e. while the default value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL is * Boolean.TRUE, in some cases the operator could set the * default. * *

      "Affine" defines a PropertyGenerator that performs an identical * transformation on the "ROI" property of the source image, which can * be retrieved by calling the getProperty method with * "ROI" as the property name. * *

      * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName Affine
      LocalName Affine
      Vendor com.sun.media.jai
      Description Performs interpolated affine transform on * an image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/AffineDescriptor.html
      Version 1.0
      arg0Desc The affine transform matrix.
      arg1Desc The interpolation method.

      * *

      * * * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      transform java.awt.geom.AffineTransformidentity transform
      interpolation javax.media.jai.InterpolationInterpolationNearest
      backgroundValues double[]{0.0}

      * * @see java.awt.geom.AffineTransform * @see javax.media.jai.Interpolation * @see javax.media.jai.OperationDescriptor */ public class AffineDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Affine"}, {"LocalName", "Affine"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("AffineDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/AffineDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("AffineDescriptor1")}, {"arg1Desc", JaiI18N.getString("AffineDescriptor2")}, {"arg2Desc", JaiI18N.getString("AffineDescriptor3")}, }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { java.awt.geom.AffineTransform.class, javax.media.jai.Interpolation.class, double[].class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "transform", "interpolation", "backgroundValues" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { new AffineTransform(), Interpolation.getInstance(Interpolation.INTERP_NEAREST), new double[] {0.0} }; /** Constructor. */ public AffineDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** Returns true since renderable operation is supported. */ public boolean isRenderableSupported() { return true; } /** * Returns an array of PropertyGenerators implementing * property inheritance for the "Affine" operation. * * @return An array of property generators. */ public PropertyGenerator[] getPropertyGenerators() { PropertyGenerator[] pg = new PropertyGenerator[1]; pg[0] = new AffinePropertyGenerator(); return pg; } /** * Validates the input parameters. * *

      In addition to the standard checks performed by the * superclass method, this method checks that "transform" is * invertible. */ protected boolean validateParameters(ParameterBlock args, StringBuffer message) { if (!super.validateParameters(args, message)) { return false; } AffineTransform transform = (AffineTransform)args.getObjectParameter(0); try { AffineTransform itransform = transform.createInverse(); } catch (java.awt.geom.NoninvertibleTransformException e) { message.append(getName() + " " + JaiI18N.getString("AffineDescriptor4")); return false; } return true; } /** * Performs interpolated affine transform on an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param transform The affine transform matrix. * May be null. * @param interpolation The interpolation method. * May be null. * @param backgroundValues The user-specified background values. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, AffineTransform transform, Interpolation interpolation, double[] backgroundValues, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Affine", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("transform", transform); pb.setParameter("interpolation", interpolation); pb.setParameter("backgroundValues", backgroundValues); return JAI.create("Affine", pb, hints); } /** * Performs interpolated affine transform on an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param transform The affine transform matrix. * May be null. * @param interpolation The interpolation method. * May be null. * @param backgroundValues The user-specified background values. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, AffineTransform transform, Interpolation interpolation, double[] backgroundValues, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Affine", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("transform", transform); pb.setParameter("interpolation", interpolation); pb.setParameter("backgroundValues", backgroundValues); return JAI.createRenderable("Affine", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/MinDescriptor.java0000644000175000017500000001331010203035544026613 0ustar mathieumathieu/* * $RCSfile: MinDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:40 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Min" operation. * *

      The Min operation takes two rendered or renderable images, and for * every pair of pixels, one from each source image of the corresponding * position and band, finds the minimum pixel value. No additional parameters * are required. * *

      The two sources may have different number of bands and/or data * types. By default, the destination image bound is the intersection * of the two source image bounds. If the two sources don't intersect, * the destination will have a width and a height of 0. The number of * bands of the destination image is the same as the least number of * bands of the sources, and the data type is the biggest data type * of the sources. * *

      The destination pixel values are defined by the pseudocode: *

       * if (srcs[0][x][y][b] < srcs[1][x][y][b]) {
       *     dst[x][y][b] = srcs[0][x][y][b];
       * } else {
       *     dst[x][y][b] = srcs[1][x][y][b];
       * }
       * 
      * *

      * * * * * * * * *
      Resource List
      Name Value
      GlobalName Min
      LocalName Min
      Vendor com.sun.media.jai
      Description Computes the pixel-wise minimum of two * images.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/MinDescriptor.html
      Version 1.0

      * *

      No parameters are needed for this operation. * * @see javax.media.jai.OperationDescriptor */ public class MinDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Min"}, {"LocalName", "Min"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("MinDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/MinDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")} }; /** Constructor. */ public MinDescriptor() { super(resources, 2, null, null, null); } /** Returns true since renderable operation is supported. */ public boolean isRenderableSupported() { return true; } /** * Computes the pixel-wise minimum of two images. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param source1 RenderedImage source 1. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if source1 is null. */ public static RenderedOp create(RenderedImage source0, RenderedImage source1, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Min", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setSource("source1", source1); return JAI.create("Min", pb, hints); } /** * Computes the pixel-wise minimum of two images. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param source1 RenderableImage source 1. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if source1 is null. */ public static RenderableOp createRenderable(RenderableImage source0, RenderableImage source1, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Min", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setSource("source1", source1); return JAI.createRenderable("Min", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/ExpDescriptor.java0000644000175000017500000001124010203035544026624 0ustar mathieumathieu/* * $RCSfile: ExpDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:35 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Exp" operation. * *

      The "Exp" operation takes the exponential of the pixel values * of an image. The pixel values of the destination image are defined * by the pseudocode: * *

      dst[x][y][b] = java.lang.Math.exp(src[x][y][b])
      * *

      For integral image datatypes, the result will be rounded and clamped * as needed. * *

      * * * * * * * * *
      Resource List
      Name Value
      GlobalName Exp
      LocalName Exp
      Vendor com.sun.media.jai
      Description Computes the exponential of the pixel values * of an image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ExpDescriptor.html
      Version 1.0

      * *

      No parameters are needed for the "Exp" operation. * * @see javax.media.jai.OperationDescriptor */ public class ExpDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Exp"}, {"LocalName", "Exp"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("ExpDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ExpDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")} }; /** Constructor. */ public ExpDescriptor() { super(resources, 1, null, null, null); } /** Returns true since renderable operation is supported. */ public boolean isRenderableSupported() { return true; } /** * Computes the exponential of the pixel values of an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Exp", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); return JAI.create("Exp", pb, hints); } /** * Computes the exponential of the pixel values of an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Exp", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); return JAI.createRenderable("Exp", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/DCTDescriptor.java0000644000175000017500000001115510203035544026507 0ustar mathieumathieu/* * $RCSfile: DCTDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:33 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "DCT" operation. * *

      The "DCT" operation computes the even discrete cosine transform * (DCT) of an image. Each band of the destination image is derived by * performing a two-dimensional DCT on the corresponding band of the * source image. * *

      * * * * * * * * *
      Resource List
      Name Value
      GlobalName DCT
      LocalName DCT
      Vendor com.sun.media.jai
      Description Computes the discrete cosine transform of * an image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/DCTDescriptor.html
      Version 1.0

      * *

      No parameters are needed for the "DCT" operation. * * @see javax.media.jai.OperationDescriptor */ public class DCTDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "DCT"}, {"LocalName", "DCT"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("DCTDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/DCTDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")} }; /** Constructor. */ public DCTDescriptor() { super(resources, 1, null, null, null); } /** Returns true since renderable operation is supported. */ public boolean isRenderableSupported() { return true; } /** * Computes the discrete cosine transform of an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("DCT", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); return JAI.create("DCT", pb, hints); } /** * Computes the discrete cosine transform of an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("DCT", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); return JAI.createRenderable("DCT", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/MatchCDFDescriptor.java0000644000175000017500000002350110203035544027444 0ustar mathieumathieu/* * $RCSfile: MatchCDFDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:38 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.Image; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.Histogram; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "MatchCDF" operation. * *

      The "MatchCDF" operation performs a piecewise linear mapping of the * pixel values of an image such that the Cumulative Distribution Function * (CDF) of the destination image matches as closely as possible a specified * Cumulative Distribution Function. The desired CDF is described by an * array of the form

      float CDF[numBands][numBins[b]]
      where *
      numBins[b]
      denotes the number of bins in the histogram of the * source image for band b. Each element in the array *
      CDF[b]
      must be non-negative, the array must represent a non- * decreasing sequence, and the last element of the array must be 1.0F. * The source image must have a Histogram object available via * its getProperty() method. * *

      * * * * * * * * * *
      Resource List
      Name Value
      GlobalName MatchCDF
      LocalName MatchCDF
      Vendor com.sun.media.jai
      Description Matches pixel values to a supplied CDF.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/MatchCDFDescriptor.html
      Version 1.0
      arg0Desc The desired Cumulative Distribution Function.

      * *

      * * * * * *
      Parameter List
      Name Class TypeDefault Value
      CDF float[][]CDF for histogram equalization

      * * @see java.awt.image.DataBuffer * @see javax.media.jai.ImageLayout * @see javax.media.jai.OperationDescriptor */ public class MatchCDFDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "MatchCDF"}, {"LocalName", "MatchCDF"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("MatchCDFDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/MatchCDFDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", "The desired Cumulative Distribution Function."}, }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { float[][].class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "CDF" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { null }; private static final String[] supportedModes = { "rendered", "renderable" }; /** Constructor. */ public MatchCDFDescriptor() { super(resources, supportedModes, 1, paramNames, paramClasses, paramDefaults, null); } /** * Validates the input sources and parameter. * *

      In addition to the standard checks performed by the * superclass method, this method checks that the source image * contains a "histogram" property and that the "CDF" array * is appropriate for it. */ public boolean validateArguments(String modeName, ParameterBlock args, StringBuffer msg) { if (!super.validateArguments(modeName, args, msg)) { return false; } if (!modeName.equalsIgnoreCase("rendered")) return true; // Get the source and the CDF array. RenderedImage src = args.getRenderedSource(0); float[][] CDF = (float[][])args.getObjectParameter(0); // Ensure that the Histogram is available and that the CDF array // is appropriate for it. Object prop = src.getProperty("histogram"); if(prop == null || prop.equals(Image.UndefinedProperty)) { // Property is null or undefined. msg.append(getName() + " " + JaiI18N.getString("MatchCDFDescriptor1")); return false; } else if(!(prop instanceof Histogram)) { // Property is not a Histogram. msg.append(getName() + " " + JaiI18N.getString("MatchCDFDescriptor2")); return false; } else { Histogram hist = (Histogram)prop; int numBands = hist.getNumBands(); if (CDF == null) { int[] numBins = hist.getNumBins(); CDF = new float[numBands][]; for (int b = 0; b < numBands; b++) { CDF[b] = new float[numBins[b]]; for (int i = 0; i < numBins[b]; i++) CDF[b][i] = (i + 1)/numBins[b]; } } if(CDF.length != numBands) { // CDF length does not match Histogram. msg.append(getName() + " " + JaiI18N.getString("MatchCDFDescriptor3")); return false; } for(int b = 0; b < numBands; b++) { if(CDF[b].length != hist.getNumBins(b)) { // Check that CDF length for this band matches Histogram. msg.append(getName() + " " + JaiI18N.getString("MatchCDFDescriptor4")); return false; } } for(int b = 0; b < numBands; b++) { float[] CDFband = CDF[b]; int length = CDFband.length; if(CDFband[length-1] != 1.0) { // Last CDF array element value is not 1.0. msg.append(getName() + " " + JaiI18N.getString("MatchCDFDescriptor7")); return false; } for(int i = 0; i < length; i++) { if(CDFband[i] < 0.0F) { // Negative CDF value. msg.append(getName() + " " + JaiI18N.getString("MatchCDFDescriptor5")); return false; } else if(i != 0) { if(CDFband[i] < CDFband[i-1]) { // Decreasing sequence. msg.append(getName() + " " + JaiI18N.getString("MatchCDFDescriptor6")); return false; } } } } return true; } } /** * Matches pixel values to a supplied CDF. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param CDF The desired Cumulative Distribution Function. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, float[][] CDF, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("MatchCDF", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("CDF", CDF); return JAI.create("MatchCDF", pb, hints); } /** * Matches pixel values to a supplied CDF. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param CDF The desired Cumulative Distribution Function. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, float[][] CDF, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("MatchCDF", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("CDF", CDF); return JAI.createRenderable("MatchCDF", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/DFTDataNature.java0000644000175000017500000000151710203035544026425 0ustar mathieumathieu/* * $RCSfile: DFTDataNature.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:33 $ * $State: Exp $ */ package javax.media.jai.operator; import javax.media.jai.EnumeratedParameter; /** * Class used to represent the acceptable values of the "dataNature" * parameter of the "DFT" and "IDFT" operations. Acceptable values for the * "dataNature" parameter are defined in the DFTDescriptor * and IDFTDescriptor by the constants * REAL_TO_COMPLEX, COMPLEX_TO_COMPLEX, and * COMPLEX_TO_REAL. * * @since JAI 1.1 */ public final class DFTDataNature extends EnumeratedParameter { DFTDataNature(String name, int value) { super(name, value); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/EncodeDescriptor.java0000644000175000017500000001771010203035544027275 0ustar mathieumathieu/* * $RCSfile: EncodeDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:34 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.ImageEncodeParam; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.io.File; import java.io.IOException; import java.io.OutputStream; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Encode" operation. * * The "Encode" operation writes an image to a given OutputStream * in a specified format using the supplied encoding parameters. * *

      The third parameter contains an instance of * ImageEncodeParam to be used during the decoding. It * may be set to null in order to perform default * encoding, or equivalently may be omitted. If * non-null, it must be of the correct class type for the * selected format. * *

      The classes in the com.sun.media.jai.codec * package are not a committed part of the JAI API. Future releases * of JAI will make use of new classes in their place. This * class will change accordingly. * *

      * * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName encode
      LocalName encode
      Vendor com.sun.media.jai
      Description Stores an image to an OutputStream.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/EncodeDescriptor.html
      Version 1.0
      arg0Desc The OutputStream to write to.
      arg1Desc The format of the created file.
      arg2Desc The encoding parameters.

      * *

      * * * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      stream java.io.OutputStreamNO_PARAMETER_DEFAULT
      format java.lang.String"tiff"
      param com.sun.media.jai.codec.ImageEncodeParamnull

      * * @see javax.media.jai.OperationDescriptor */ public class EncodeDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation and * specify the parameter list for the "Encode" operation. */ private static final String[][] resources = { {"GlobalName", "Encode"}, {"LocalName", "Encode"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("EncodeDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/EncodeDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("EncodeDescriptor1")}, {"arg1Desc", JaiI18N.getString("EncodeDescriptor2")}, {"arg2Desc", JaiI18N.getString("EncodeDescriptor3")} }; /** The parameter names for the "Encode" operation. */ private static final String[] paramNames = { "stream", "format", "param" }; /** The parameter class types for the "Encode" operation. */ private static final Class[] paramClasses = { java.io.OutputStream.class, java.lang.String.class, com.sun.media.jai.codec.ImageEncodeParam.class }; /** The parameter default values for the "Encode" operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT, "tiff", null }; private static final String[] supportedModes = { "rendered" }; /** Constructor. */ public EncodeDescriptor() { super(resources, supportedModes, 1, paramNames, paramClasses, paramDefaults, null); } /** * Validates the input source and parameters. * *

      In addition to the standard checks performed by the * superclass method, this method checks that the format name is * recognized and is capable of encoding the source image using * the encoding parameter "param", if non-null. */ public boolean validateArguments(String modeName, ParameterBlock args, StringBuffer msg) { if (!modeName.equalsIgnoreCase("rendered")) return true; // Fool the superclass method if length < 3 if (args.getNumParameters() < 3) { args = (ParameterBlock)args.clone(); args.set(null, 2); } if (!super.validateArguments(modeName, args, msg)) { return false; } // Retrieve the format. String format = (String)args.getObjectParameter(1); // Retrieve the associated ImageCodec. ImageCodec codec = ImageCodec.getCodec(format); // Check for null codec. if (codec == null) { msg.append(getName() + " " + JaiI18N.getString("EncodeDescriptor4")); return false; } // Retrieve the ImageEncodeParam object. ImageEncodeParam param = (ImageEncodeParam)args.getObjectParameter(2); RenderedImage src = args.getRenderedSource(0); // Verify that the image can be encoded with the given parameters. if (!codec.canEncodeImage(src, param)) { msg.append(getName() + " " + JaiI18N.getString("EncodeDescriptor5")); return false; } return true; } /** * Returns true indicating that the operation should be rendered * immediately during a call to JAI.create(). * * @see javax.media.jai.OperationDescriptor */ public boolean isImmediate() { return true; } /** * Stores an image to an OutputStream. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param stream The OutputStream to write to. * @param format The format of the created file. * May be null. * @param param The encoding parameters. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if stream is null. */ public static RenderedOp create(RenderedImage source0, OutputStream stream, String format, ImageEncodeParam param, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Encode", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("stream", stream); pb.setParameter("format", format); pb.setParameter("param", param); return JAI.create("Encode", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/FilteredSubsampleDescriptor.java0000644000175000017500000005300010203035544031502 0ustar mathieumathieu/* * $RCSfile: FilteredSubsampleDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:36 $ * $State: Exp $ */package javax.media.jai.operator; import com.sun.media.jai.util.PropertyGeneratorImpl; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationBicubic2; import javax.media.jai.InterpolationBicubic; import javax.media.jai.InterpolationBilinear; import javax.media.jai.InterpolationNearest; import javax.media.jai.JAI; import javax.media.jai.OpImage; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PlanarImage; import javax.media.jai.PropertyGenerator; import javax.media.jai.ROI; import javax.media.jai.ROIShape; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.WarpOpImage; import javax.media.jai.registry.RenderedRegistryMode; /** *

      This property generator computes the properties for the operation * "FilteredSubsample" dynamically. */ class FilteredSubsamplePropertyGenerator extends PropertyGeneratorImpl { /** Constructor. */ public FilteredSubsamplePropertyGenerator() { super(new String[] {"FilteredSubsample"}, new Class[] {boolean.class}, new Class[] {RenderedOp.class, RenderableOp.class}); } /** * Returns the specified property. * * @param name Property name. * @param opNode Operation node. */ public Object getProperty(String name, Object opNode) { validate(name, opNode); if(opNode instanceof RenderedOp && name.equalsIgnoreCase("roi")) { RenderedOp op = (RenderedOp)opNode; ParameterBlock pb = op.getParameterBlock(); // Retrieve the rendered source image and its ROI. RenderedImage src = pb.getRenderedSource(0); Object property = src.getProperty("ROI"); if (property == null || property.equals(java.awt.Image.UndefinedProperty) || !(property instanceof ROI)) { return null; } ROI srcROI = (ROI)property; // Determine the effective source bounds. Rectangle srcBounds = null; PlanarImage dst = op.getRendering(); if(dst instanceof WarpOpImage && !((OpImage)dst).hasExtender(0)) { WarpOpImage warpIm = (WarpOpImage)dst; srcBounds = new Rectangle(src.getMinX() + warpIm.getLeftPadding(), src.getMinY() + warpIm.getTopPadding(), src.getWidth() - warpIm.getWidth() + 1, src.getHeight() - warpIm.getHeight() + 1); } else { srcBounds = new Rectangle(src.getMinX(), src.getMinY(), src.getWidth(), src.getHeight()); } // If necessary, clip the ROI to the effective source bounds. if(!srcBounds.contains(srcROI.getBounds())) { srcROI = srcROI.intersect(new ROIShape(srcBounds)); } // Retrieve the scale factors float sx = 1.0F/pb.getIntParameter(1); float sy = 1.0F/pb.getIntParameter(2); // Create an equivalent transform. AffineTransform transform = new AffineTransform(sx, 0.0, 0.0, sy, 0, 0); // Create the scaled ROI. ROI dstROI = srcROI.transform(transform); // Retrieve the destination bounds. Rectangle dstBounds = op.getBounds(); // If necessary, clip the warped ROI to the destination bounds. if(!dstBounds.contains(dstROI.getBounds())) { dstROI = dstROI.intersect(new ROIShape(dstBounds)); } // Return the warped and possibly clipped ROI. return dstROI; } else { return null; } } /** Returns the valid property names for the operation "FilteredSubsample". */ public String[] getPropertyNames() { String[] properties = new String[1]; properties[0] = "roi"; return(properties); } } /** * An OperationDescriptor describing the "FilteredSubsample" * operation. * *

      The "FilteredSubsample" operation subsamples an image by integral * factors. The furnished scale factors express the ratio of the * source dimensions to the destination dimensions. The input filter is * symmetric about the center pixel and is specified by values from the * center outward. Both filter axes use the same input filter values. *

      When applying scale factors of scaleX, scaleY to a source image * with width of src_width and height of src_height, the resulting image * is defined to have the following bounds: * * * dst_minX = round(src_minX / scaleX)
      * dst_minY = round(src_minY / scaleY)
      * dst_width = round(src_width / scaleX)
      * dst_height = round(src_height / scaleY)
      *
      * *

      The input filter is quadrant symmetric (typically antialias). The * filter is product-separable, quadrant symmetric, and is defined by half of its * span. For example, if the input filter, qsFilter, was of size 3, it would have * width and height 5 and have the symmetric form:
      * qs[2] qs[1] qs[0] qs[1] qs[2]
      * *

      A fully expanded 5 by 5 kernel has the following format (25 entries * defined by only 3 entries): * * *

      qs[2]*qs[2] qs[2]*qs[1] qs[2]*qs[0] qs[2]*qs[1] qs[2]*qs[2]
      * * qs[1]*qs[2] qs[1]*qs[1] qs[1]*qs[0] qs[1]*qs[1] qs[1]*qs[2]
      * * qs[0]*qs[2] qs[0]*qs[1] qs[0]*qs[0] qs[0]*qs[1] qs[0]*qs[2]
      * * qs[1]*qs[2] qs[1]*qs[1] qs[1]*qs[0] qs[1]*qs[1] qs[1]*qs[2]
      * * qs[2]*qs[2] qs[2]*qs[1] qs[2]*qs[0] qs[2]*qs[1] qs[2]*qs[2] *

      * *

      This operator is similar to the image scale operator. Important * differences are described here. The coordinate transformation differences * between the FilteredDownsampleOpImage and the ScaleOpImage operators can be * understood by comparing their mapping equations directly. * *

      For the scale operator, the destination (D) to source (S) mapping * equations are given by * * *

      xS = (xD - xTrans)/xScale
      * yS = (yD - yTrans)/yScale *
      * *

      The scale and translation terms are floating point values in D-frame * pixel units. For scale this means that one S pixel maps to xScale * by yScale D-frame pixels. The translation vector, (xTrans, yTrans), * is in D-frame pixel units. * *

      The FilteredSubsample operator mapping equations are given by * * *

      xS = xD*scaleX
      * yS = yD*scaleY *
      * *

      The scale terms for this operation are integral values in the * S-Frame; there are no translation terms for this operation. * *

      The downsample terms are restricted to positive integral values. * Geometrically, one D-frame pixel maps to scaleX * scaleY S-frame * pixels. The combination of downsampling and filtering has performance * benefits over sequential operator usage in part due to the symmetry * constraints imposed by only allowing integer parameters for scaling and * only allowing separable symmetric filters. With odd scale factors, D-frame * pixels map directly onto S-frame pixel centers. With even scale factors, * D-frame pixels map squarely between S-frame pixel centers. Below are * examples of even, odd, and combination cases. * *

      s = S-frame pixel centers
      * d = D-frame pixel centers mapped to S-frame *

      * *
       s   s   s   s   s   s           s   s   s   s   s   s  
      *
         d       d       d  
      *
       s   s   s   s   s   s           s   d   s   s   d   s  
      *
        
      *
       s   s   s   s   s   s           s   s   s   s   s   s  
      *
         d       d       d  
      *
       s   s   s   s   s   s           s   s   s   s   s   s  
      *
        
      *
       s   s   s   s   s   s           s   d   s   s   d   s  
      *
         d       d       d  
      *
       s   s   s   s   s   s           s   s   s   s   s   s  
      *
        
      *
       Even scaleX/Y factors            Odd scaleX/Y factors  
      *
         
      *
       s   s   s   s   s   s           s   s   s   s   s   s  
      *
           d           d    
      *
       s   s   s   s   s   s           s d s   s d s   s d s  
      *
         
      *
       s   s   s   s   s   s           s   s   s   s   s   s  
      *
           d           d    
      *
       s   s   s   s   s   s           s   s   s   s   s   s  
      *
         
      *
       s   s   s   s   s   s           s d s   s d s   s d s  
      *
           d           d    
      *
       s   s   s   s   s   s           s   s   s   s   s   s  
      *
         
      *
        Odd/even scaleX/Y factors      Even/odd scaleX/Y factors  

      *
      * *

      The convolution kernel is restricted to have quadrant symmetry (qs). This * type of symmetry is also product separable. The qsFilter is specified by * a floating array. If qsFilter[0], qsFilter[1], ... , * qsFilter[qsFilter.length - 1] * is the filter input, then the entire separable kernel is given by
      * qsFilter[qsFilter.length - 1], ... , qsFilter[0], ... , * qsFilter[qsFilter.length - 1]
      * *

      The restriction of integer parameter constraints allows full product * separablity and symmetry when applying the combined resample and filter * convolution operations. * *

      If Bilinear or Bicubic interpolation is specified, the source needs * to be extended such that it has the extra pixels needed to compute all * the destination pixels. This extension is performed via the * BorderExtender class. The type of border extension can be * specified as a RenderingHint to the JAI.create * method. * *

      If no BorderExtender is specified, the source will * not be extended. The output image size is still calculated * according to the formula specified above. However since there is not * enough source to compute all the destination pixels, only that * subset of the destination image's pixels which can be computed, * will be written in the destination. The rest of the destination * will be set to zeros. * *

      It should be noted that this operation automatically adds a * value of Boolean.TRUE for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL to the given * configuration so that the operation is performed * on the pixel values instead of being performed on the indices into * the color map if the source(s) have an IndexColorModel. * This addition will take place only if a value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL has not already been * provided by the user. Note that the configuration Map * is cloned before the new hint is added to it. The operation can be * smart about the value of the JAI.KEY_REPLACE_INDEX_COLOR_MODEL * RenderingHints, i.e. while the default value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL is * Boolean.TRUE, in some cases the operator could set the * default. * *

      "FilteredSubsample" defines a PropertyGenerator that performs an identical * transformation on the "ROI" property of the source image, which can * be retrieved by calling the getProperty method with * "ROI" as the property name. * *

      One design purpose of this operation is anti-aliasing when * downsampling. The technique of anti-aliasing applies a good * filter to the area of rendering to generate better results. Generally, * this filter is required to be (quadrant) symmetric and separable * to obtain good performance. The widely-used Gaussian filter * satisfies all these requirements. Thus, the default value for the * parameter "qsFilter" is generated from a Gaussian kernel * based on the following procedure: * *

      Define the Gaussian function G(x) as *

      G(x) = e-x2/(2s2)/( * (2pi)½s), *

      where s is the standard deviation, and pi * is the ratio of the circumference of a circle to its diameter. * *

      For a one-dimensional Gaussian kernel with a size of 2N+1, * the standard deviation of the Gaussian function to generate this kernel * is chosen as N/3. The * one-dimensional Gaussian kernel KN(1:2N+1) is *

      (G(-N)/S, G(-N+1)/S, ..., G(0),..., G(N-1)/S, G(N)/S), *

      where S is the summation of G(-N), G(-N+1), ...,G(0), * ..., G(N-1), and G(N). A two-dimensional Gaussian * kernel with a size of (2N+1) x (2N+1) is constructed as the * outer product of two KNs: * the (i, j)th element is * KN(i)KN(j). * The quadrant symmetric filter corresponding to the * (2N+1) x (2N+1) Gaussian kernel is simply *

      (G(0)/S, G(1)/S, ..., G(N)/S), or *

      (KN(N+1), ..., KN(2N+1). * *

      Denote the maximum of the X and Y subsample factors as M. * If M is even, the default "qsFilter" is the quadrant symmetric * filter derived from the two-dimensional (M+1) x (M+1) * Gaussian kernel. If M is odd, the default * "qsFilter" is the quadrant symmetric filter derived from the * two-dimensional M x M Gaussian kernel. * *

      * * * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName FilteredSubsample
      LocalName FilteredSubsample
      Vendor com.sun.media.jai
      Description Filters and subsamples an image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/FilteredSubsample.html
      Version 1.1
      arg0Desc The X subsample factor.
      arg1Desc The Y subsample factor.
      arg2Desc Symmetric filter coefficients.
      arg3Desc The interpolation object for * resampling.

      * *

      * * * * * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      scaleX java.lang.Integer2
      scaleY java.lang.Integer2
      qsFilter java.lang.Float []A quadrant symmetric filter * generated from a Gaussian kernel * as described above.
      interpolation javax.media.jai.InterpolationInterpolationNearest

      * * @see javax.media.jai.Interpolation * @see javax.media.jai.BorderExtender * @see javax.media.jai.OperationDescriptor * * @since JAI 1.1 */ public class FilteredSubsampleDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "FilteredSubsample"}, {"LocalName", "FilteredSubsample"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("FilteredSubsampleDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/FilteredSubsampleDescriptor.html"}, {"Version", "1.0"}, {"arg0Desc", "The X subsample factor."}, {"arg1Desc", "The Y subsample factor."}, {"arg2Desc", "Symmetric filter coefficients."}, {"arg3Desc", "Interpolation object."} }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { java.lang.Integer.class, java.lang.Integer.class, float[].class, Interpolation.class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "scaleX", "scaleY", "qsFilterArray", "interpolation", }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { new Integer(2), new Integer(2), null, Interpolation.getInstance(Interpolation.INTERP_NEAREST) }; private static final String[] supportedModes = { "rendered" }; /**

      Constructor. */ public FilteredSubsampleDescriptor() { super(resources, supportedModes, 1, paramNames, paramClasses, paramDefaults, null); } /** * Validates the input parameters. * *

      In addition to the standard checks performed by the * superclass method, this method checks that "scaleX" and "scaleY" * are both greater than 0 and that the interpolation type * is one of 4 standard types:
      *

      * javax.media.jai.InterpolationNearest
      * javax.media.jai.InterpolationBilinear
      * javax.media.jai.InterpolationBicubic
      * javax.media.jai.InterpolationBicubic2 *
      */ protected boolean validateParameters(String modeName, ParameterBlock args, StringBuffer msg) { if (!super.validateParameters(modeName, args, msg)) return false; int scaleX = args.getIntParameter(0); int scaleY = args.getIntParameter(1); if (scaleX < 1 || scaleY < 1) { msg.append(getName() + " " + JaiI18N.getString("FilteredSubsampleDescriptor1")); return false; } float[] filter = (float[])args.getObjectParameter(2); // if this parameter is null, generate the kernel based on the // procedure described above. if (filter == null) { int m = scaleX > scaleY ? scaleX: scaleY; if ((m & 1) == 0) m++; double sigma = (m - 1) / 6.0; // when m is 1, sigma is 0; will generate NaN. Give any number // to sigma will generate the correct kernel {1.0} if (m == 1) sigma = 1.0; filter = new float[m/2 + 1]; float sum = 0; for (int i = 0; i < filter.length; i++) { filter[i] = (float)gaussian((double)i, sigma); if (i == 0) sum += filter[i]; else sum += filter[i] * 2; } for (int i = 0; i < filter.length; i++) { filter[i] /= sum; } args.set(filter, 2); } Interpolation interp = (Interpolation)args.getObjectParameter(3); // Determine the interpolation type, if not supported throw exception if (!((interp instanceof InterpolationNearest) || (interp instanceof InterpolationBilinear) || (interp instanceof InterpolationBicubic) || (interp instanceof InterpolationBicubic2))) { msg.append(getName() + " " + JaiI18N.getString("FilteredSubsampleDescriptor2")); return false; } return true; } // validateParameters /** Computes the value of Gaussian function at x. * @param x The coordinate at where the value is computed. * @param sigma The standard deviation for the Gaussian function. */ private double gaussian(double x, double sigma) { return Math.exp(-x*x/(2 * sigma * sigma)) / sigma / Math.sqrt(2 * Math.PI); } /** * Filters and subsamples an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param scaleX The X subsample factor. * May be null. * @param scaleY The Y subsample factor. * May be null. * @param qsFilterArray Symmetric filter coefficients. * May be null. * @param interpolation Interpolation object. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, Integer scaleX, Integer scaleY, float[] qsFilterArray, Interpolation interpolation, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("FilteredSubsample", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("scaleX", scaleX); pb.setParameter("scaleY", scaleY); pb.setParameter("qsFilterArray", qsFilterArray); pb.setParameter("interpolation", interpolation); return JAI.create("FilteredSubsample", pb, hints); } } // FilteredSubsampleDescriptor jai-core-1.1.4/src/share/classes/javax/media/jai/operator/ClampDescriptor.java0000644000175000017500000002066210203035544027134 0ustar mathieumathieu/* * $RCSfile: ClampDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:31 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Clamp" operation. * *

      The Clamp operation takes one rendered or renderable source * image, and sets all the pixels whose value is below a "low" value * to that low value and all the pixels whose value is above a "high" * value to that high value. The pixels whose value is between the * "low" value and the "high" value are left unchanged. * *

      A different set of "low" and "high" values may be applied to each * band of the source image, or the same set of "low" and "high" values * may be applied to all bands of the source. If the number of "low" and * "high" values supplied is less than the number of bands of the source, * then the values from entry 0 are applied to all the bands. Each "low" * value must be less than or equal to its corresponding "high" value. * *

      The destination pixel values are defined by the pseudocode: *

       * lowVal = (low.length < dstNumBands) ?
       *          low[0] : low[b];
       * highVal = (high.length < dstNumBands) ?
       *           high[0] : high[b];
       *
       * if (src[x][y][b] < lowVal) {
       *     dst[x][y][b] = lowVal;
       * } else if (src[x][y][b] > highVal) {
       *     dst[x][y][b] = highVal;
       * } else {
       *     dst[x][y][b] = src[x][y][b];
       * }
       * 
      * *

      * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName Clamp
      LocalName Clamp
      Vendor com.sun.media.jai
      Description Clamps the pixel values of an image * to a specified range.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ClampDescriptor.html
      Version 1.0
      arg0Desc The lower boundary for each band.
      arg1Desc The upper boundary for each band.

      * *

      * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      low double[]{0.0}
      high double[]{255.0}

      * * @see javax.media.jai.OperationDescriptor */ public class ClampDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Clamp"}, {"LocalName", "Clamp"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("ClampDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ClampDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("ClampDescriptor1")}, {"arg1Desc", JaiI18N.getString("ClampDescriptor2")} }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { double[].class, double[].class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "low", "high" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { new double[] {0.0}, new double[] {255.0} }; /** Constructor. */ public ClampDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** Returns true since renderable operation is supported. */ public boolean isRenderableSupported() { return true; } /** * Validates the input parameters. * *

      In addition to the standard checks performed by the * superclass method, this method checks that "low" and "high" * have length at least 1 and that each "low" value is less than * or equal to the corresponding "high" value. */ protected boolean validateParameters(ParameterBlock args, StringBuffer msg) { if (!super.validateParameters(args, msg)) { return false; } double[] low = (double[])args.getObjectParameter(0); double[] high = (double[])args.getObjectParameter(1); if (low.length < 1 || high.length < 1) { msg.append(getName() + " " + JaiI18N.getString("ClampDescriptor3")); return false; } int length = Math.min(low.length, high.length); for (int i = 0; i < length; i++) { if (low[i] > high[i]) { msg.append(getName() + " " + JaiI18N.getString("ClampDescriptor4")); return false; } } return true; } /** * Clamps the pixel values of an image to a specified range. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param low The lower boundary for each band. * May be null. * @param high The upper boundary for each band. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, double[] low, double[] high, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Clamp", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("low", low); pb.setParameter("high", high); return JAI.create("Clamp", pb, hints); } /** * Clamps the pixel values of an image to a specified range. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param low The lower boundary for each band. * May be null. * @param high The upper boundary for each band. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, double[] low, double[] high, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Clamp", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("low", low); pb.setParameter("high", high); return JAI.createRenderable("Clamp", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/SubtractDescriptor.java0000644000175000017500000001471510203035544027671 0ustar mathieumathieu/* * $RCSfile: SubtractDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:45 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Subtract" * operation. * *

      The Subtract operation takes two rendered or renderable images, * and for every * pair of pixels, one from each source image of the corresponding * position and band, subtracts the pixel from the second source from * the pixel from the first source. No additional parameters are * required. * *

      The two source images may have different numbers of bands and * data types. By default, the destination image bounds are the * intersection of the two source image bounds. If the sources don't * intersect, the destination will have a width and height of 0. * *

      The default number of bands of the destination image is equal * to the smallest number of bands of the sources, and the data type * is the smallest data type with sufficient range to cover the range * of both source data types (not necessarily the range of their * sums). * *

      As a special case, if one of the source images has N bands (N > * 1), the other source has 1 band, and an ImageLayout * hint is provided containing a destination SampleModel * with K bands (1 < K <= N), then the single band of the 1-banded * source is subtracted from or into each of the first K bands of the * N-band source. * *

      If the result of the operation underflows/overflows the * minimum/maximum value supported by the destination data type, then * it will be clamped to the minimum/maximum value respectively. * *

      The destination pixel values are defined by the pseudocode: *

       * dst[x][y][dstBand] = clamp(srcs[0][x][y][src0Band] -
       *                            srcs[1][x][y][src1Band]);
       * 
      * *

      * * * * * * * * *
      Resource List
      Name Value
      GlobalName Subtract
      LocalName Subtract
      Vendor com.sun.media.jai
      Description Subtracts one image from * another image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/SubtractDescriptor.html
      Version 1.0

      * *

      No parameters are needed for this operation. * * @see javax.media.jai.OperationDescriptor */ public class SubtractDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Subtract"}, {"LocalName", "Subtract"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("SubtractDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/SubtractDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")} }; /** Constructor. */ public SubtractDescriptor() { super(resources, 2, null, null, null); } /** Returns true since renderable operation is supported. */ public boolean isRenderableSupported() { return true; } /** * Subtracts one image from another image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param source1 RenderedImage source 1. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if source1 is null. */ public static RenderedOp create(RenderedImage source0, RenderedImage source1, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Subtract", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setSource("source1", source1); return JAI.create("Subtract", pb, hints); } /** * Subtracts one image from another image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param source1 RenderableImage source 1. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if source1 is null. */ public static RenderableOp createRenderable(RenderableImage source0, RenderableImage source1, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Subtract", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setSource("source1", source1); return JAI.createRenderable("Subtract", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/ColorQuantizerType.java0000644000175000017500000000157010203035544027661 0ustar mathieumathieu/* * $RCSfile: ColorQuantizerType.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:31 $ * $State: Exp $ */ package javax.media.jai.operator; import javax.media.jai.EnumeratedParameter; /** *

      Class used to represent the acceptable values of the "quantizationAlgorithm" * parameter of the "ColorQuantizer" operation. Acceptable values for the * "quantizationAlgorithm" parameter are defined in the * ColorQuantizerDescriptor by the constants * MEDIANCUT, * NEUQUANT, and * OCTTREE.

      * * @see ColorQuantizerDescriptor * * @since JAI 1.1.2 */ public final class ColorQuantizerType extends EnumeratedParameter { ColorQuantizerType(String name, int value) { super(name, value); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/PNMDescriptor.java0000644000175000017500000001054110203035544026525 0ustar mathieumathieu/* * $RCSfile: PNMDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:42 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.codec.SeekableStream; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "PNM" operation. * *

      The "PNM" operation reads a standard PNM file, including PBM, * PGM, and PPM images of both ASCII and raw formats. It stores the * image data into an appropriate SampleModel, * *

      The classes in the com.sun.media.jai.codec * package are not a committed part of the JAI API. Future releases * of JAI will make use of new classes in their place. This * class will change accordingly. * *

      * * * * * * * * * *
      Resource List
      Name Value
      GlobalName PNM
      LocalName PNM
      Vendor com.sun.media.jai
      Description Reads a standard PNM file.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/PNMDescriptor.html
      Version 1.0
      arg0Desc A SeekableStream representing the PNM file.

      * *

      * * * * * *
      Parameter List
      Name Class TypeDefault Value
      stream com.sun.media.jai.codec.SeekableStreamNO_PARAMETER_DEFAULT

      * * @see com.sun.media.jai.codec.SeekableStream * @see javax.media.jai.OperationDescriptor */ public class PNMDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation and * specify the parameter list for the "PNM" operation. */ private static final String[][] resources = { {"GlobalName", "PNM"}, {"LocalName", "PNM"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("PNMDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/PNMDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("PNMDescriptor1")} }; /** The parameter names for the "PNM" operation. */ private static final String[] paramNames = { "stream" }; /** The parameter class types for the "PNM" operation. */ private static final Class[] paramClasses = { com.sun.media.jai.codec.SeekableStream.class }; /** The parameter default values for the "PNM" operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT }; /** Constructor. */ public PNMDescriptor() { super(resources, 0, paramClasses, paramNames, paramDefaults); } /** * Reads a standard PNM file. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param stream A SeekableStream representing the PNM file. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if stream is null. */ public static RenderedOp create(SeekableStream stream, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("PNM", RenderedRegistryMode.MODE_NAME); pb.setParameter("stream", stream); return JAI.create("PNM", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/MinFilterDescriptor.java0000644000175000017500000002162710203035544027773 0ustar mathieumathieu/* * $RCSfile: MinFilterDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:40 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.util.AreaOpPropertyGenerator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PropertyGenerator; import javax.media.jai.RenderedOp; import javax.media.jai.operator.MinFilterShape; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "MinFilter" operation. * *

      The "MinFilter" operation is a non-linear filter which is * useful for removing isolated lines or pixels while preserving the * overall appearance of an image. The filter is implemented by moving * a mask over the image. For each position of the mask, the * center pixel is replaced by the min of the pixel values covered * by the mask. * *

      There are several shapes possible for the mask. The * MinFilter operation supports three shapes, as follows: * *

      Square Mask: *

       *                       x x x
       *                       x x x
       *                       x x x
       * 
      * *

      Plus Mask: *

       *                         x
       *                       x x x
       *                         x
       * 
      * *

      X Mask: *

       *                       x   x
       *                         x
       *                       x   x
       * 
      * *

      Example: *

       * 	SeekableStream s = new FileSeekableStream(new File(imagefilename); 
       *	ParameterBlock pb = new ParameterBlock();
       *      pb.add(s);
       *      RenderedImage src = (RenderedImage)JAI.create("stream", pb);
       *
       *      pb = new ParameterBlock();
       *      pb.addSource(src);
       *      pb.add(MaxFilterDescriptor.MIN_MASK_PLUS);    // mask Type
       *      pb.add(new Integer(5));                       // mask size
       *      
       *      RenderedImage dst = (RenderedImage)JAI.create("minfilter", pb);
       * 
      *

      A RenderingHints can also be added to the above. * * It should be noted that this operation automatically adds a * value of Boolean.TRUE for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL to the given * configuration so that the operation is performed * on the pixel values instead of being performed on the indices into * the color map if the source(s) have an IndexColorModel. * This addition will take place only if a value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL has not already been * provided by the user. Note that the configuration Map * is cloned before the new hint is added to it. The operation can be * smart about the value of the JAI.KEY_REPLACE_INDEX_COLOR_MODEL * RenderingHints, i.e. while the default value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL is * Boolean.TRUE, in some cases the operator could set the * default. * *

      * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName MinFilter
      LocallName MinFilter
      Vendor com.sun.media.jai
      Description Performs min filtering on an image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jaiapi/javax.media.jai.operator.MinFilterDescriptor.html
      Version 1.0
      arg0Desc The shape of the mask to be used for Min Filtering.
      arg1Desc The size (width/height) of the mask to be used in Min Filtering.

      * *

      * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      maskShape javax.media.jai.operator.MinFilterShapeMIN_MASK_SQUARE
      maskSize java.lang.Integer3

      * * @see javax.media.jai.OperationDescriptor * @see MinFilterShape * * @since JAI 1.1 */ public class MinFilterDescriptor extends OperationDescriptorImpl { /** * Default 3x3 Windows */ /** Square shaped mask. */ public static final MinFilterShape MIN_MASK_SQUARE = new MinFilterShape("MIN_MASK_SQUARE", 1); /** Plus shaped mask. */ public static final MinFilterShape MIN_MASK_PLUS = new MinFilterShape("MIN_MASK_PLUS", 2); /** X shaped mask. */ public static final MinFilterShape MIN_MASK_X = new MinFilterShape("MIN_MASK_X", 3); /** Separable square mask. */ public static final MinFilterShape MIN_MASK_SQUARE_SEPARABLE = new MinFilterShape("MIN_MASK_SQUARE_SEPARABLE", 4); /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "MinFilter"}, {"LocalName", "MinFilter"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("MinFilterDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jaiapi/javax.media.jai.operator.MinFilterDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion2")}, {"arg0Desc", JaiI18N.getString("MinFilterDescriptor1")}, {"arg1Desc", JaiI18N.getString("MinFilterDescriptor2")} }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { MinFilterShape.class, java.lang.Integer.class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "maskShape","maskSize" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { MIN_MASK_SQUARE, new Integer(3) }; /** Constructor for the MinFilterDescriptor. */ public MinFilterDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** * Returns the minimum legal value of a specified numeric parameter * for this operation. */ public Number getParamMinValue(int index) { if (index == 0) { return null; } else if (index == 1){ return new Integer(1); } else { throw new ArrayIndexOutOfBoundsException(); } } /** * Returns the maximum legal value of a specified numeric parameter * for this operation. */ public Number getParamMaxValue(int index) { if (index == 0) { return null; } else if (index == 1){ return new Integer(Integer.MAX_VALUE); } else { throw new ArrayIndexOutOfBoundsException(); } } /** * Returns an array of PropertyGenerators implementing * property inheritance for the "MinFilter" operation. * * @return An array of property generators. */ public PropertyGenerator[] getPropertyGenerators() { PropertyGenerator[] pg = new PropertyGenerator[1]; pg[0] = new AreaOpPropertyGenerator(); return pg; } /** * Performs min filtering on an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param maskShape The shape of the mask to be used for Min Filtering. * May be null. * @param maskSize The size (width/height) of the mask to be used in Min Filtering. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, MinFilterShape maskShape, Integer maskSize, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("MinFilter", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("maskShape", maskShape); pb.setParameter("maskSize", maskSize); return JAI.create("MinFilter", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/MultiplyDescriptor.java0000644000175000017500000001443610203035544027721 0ustar mathieumathieu/* * $RCSfile: MultiplyDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:41 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Multiply" operation. * *

      The Multiply operation takes two rendered or renderable source * images, and multiplies every pair of pixels, one from each source * image of the corresponding position and band. No additional * parameters are required. * *

      The two source images may have different numbers of bands and * data types. By default, the destination image bounds are the * intersection of the two source image bounds. If the sources don't * intersect, the destination will have a width and height of 0. * *

      The default number of bands of the destination image is equal * to the smallest number of bands of the sources, and the data type * is the smallest data type with sufficient range to cover the range * of both source data types (not necessarily the range of their * sums). * *

      As a special case, if one of the source images has N bands (N > * 1), the other source has 1 band, and an ImageLayout * hint is provided containing a destination SampleModel * with K bands (1 < K <= N), then the single band of the 1-banded * source is added to each of the first K bands of the N-band source. * *

      If the result of the operation underflows/overflows the * minimum/maximum value supported by the destination data type, then * it will be clamped to the minimum/maximum value respectively. * *

      The destination pixel values are defined by the pseudocode: *

       * dst[x][y][dstBand] = clamp(srcs[0][x][y][src0Band] *
       *                            srcs[1][x][y][src1Band]);
       * 
      * *

      * * * * * * * * *
      Resource List
      Name Value
      GlobalName Multiply
      LocalName Multiply
      Vendor com.sun.media.jai
      Description Multiplies two images.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/MultiplyDescriptor.html
      Version 1.0

      * *

      No parameters are needed for this operation. * * @see javax.media.jai.OperationDescriptor */ public class MultiplyDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Multiply"}, {"LocalName", "Multiply"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("MultiplyDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/MultiplyDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")} }; /** Constructor. */ public MultiplyDescriptor() { super(resources, 2, null, null, null); } /** Returns true since renderable operation is supported. */ public boolean isRenderableSupported() { return true; } /** * Multiplies two images. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param source1 RenderedImage source 1. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if source1 is null. */ public static RenderedOp create(RenderedImage source0, RenderedImage source1, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Multiply", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setSource("source1", source1); return JAI.create("Multiply", pb, hints); } /** * Multiplies two images. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param source1 RenderableImage source 1. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if source1 is null. */ public static RenderableOp createRenderable(RenderableImage source0, RenderableImage source1, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Multiply", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setSource("source1", source1); return JAI.createRenderable("Multiply", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/MultiplyConstDescriptor.java0000644000175000017500000001711010203035544030720 0ustar mathieumathieu/* * $RCSfile: MultiplyConstDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:40 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the * "MultiplyConst" operation. * *

      The MultiplyConst operation takes one rendered or renderable * image and an array of double constants, and multiplies every pixel * of the same band of the source by the constant from the * corresponding array entry. If the number of constants supplied is * less than the number of bands of the destination, then the constant * from entry 0 is applied to all the bands. Otherwise, a constant * from a different entry is applied to each band. * *

      By default, the destination image bound, data type, and number of * bands are the same as the source image. If the result of the operation * underflows/overflows the minimum/maximum value supported by the * destination data type, then it will be clamped to the minimum/maximum * value respectively. * *

      The destination pixel values are calculated as: * if (constants.length < dstNumBands) { * dst[x][y][b] = srcs[x][y][b]*constants[0]; * } else { * dst[x][y][b] = srcs[x][y][b]*constants[b]; * } * * *

      * * * * * * * * * *
      Resource List
      Name Value
      GlobalName MultiplyConst
      LocalName MultiplyConst
      Vendor com.sun.media.jai
      Description Multiplies an image by * constants.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/MultiplyConstDescriptor.html
      Version 1.0
      arg0Desc The constants to be multiplied.

      * *

      * * * * * *
      Parameter List
      Name Class TypeDefault Value
      constants double[]{1.0}

      * * @see javax.media.jai.OperationDescriptor */ public class MultiplyConstDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "MultiplyConst"}, {"LocalName", "MultiplyConst"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("MultiplyConstDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/MultiplyConstDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("MultiplyConstDescriptor1")} }; /** * The parameter class list for this operation. * The number of constants provided should be either 1, in which case * this same constant is applied to all the source bands; or the same * number as the source bands, in which case one contant is applied * to each band. */ private static final Class[] paramClasses = { double[].class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "constants" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { new double[] {1.0} }; /** Constructor. */ public MultiplyConstDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** Returns true since renderable operation is supported. */ public boolean isRenderableSupported() { return true; } /** * Validates the input parameter. * *

      In addition to the standard checks performed by the * superclass method, this method checks that the length of the * "constants" array is at least 1. */ protected boolean validateParameters(ParameterBlock args, StringBuffer message) { if (!super.validateParameters(args, message)) { return false; } int length = ((double[])args.getObjectParameter(0)).length; if (length < 1) { message.append(getName() + " " + JaiI18N.getString("MultiplyConstDescriptor2")); return false; } return true; } /** * Multiplies an image by constants. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param constants The constants to be multiplied. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, double[] constants, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("MultiplyConst", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("constants", constants); return JAI.create("MultiplyConst", pb, hints); } /** * Multiplies an image by constants. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param constants The constants to be multiplied. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, double[] constants, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("MultiplyConst", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("constants", constants); return JAI.createRenderable("MultiplyConst", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/UnsharpMaskDescriptor.java0000644000175000017500000001542210203035544030332 0ustar mathieumathieu/* * $RCSfile: UnsharpMaskDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:46 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.util.AreaOpPropertyGenerator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import javax.media.jai.JAI; import javax.media.jai.KernelJAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PropertyGenerator; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; /** *

      An OperationDescriptor describing the "UnsharpMask" operation. * *

      Unsharp masking is derived from a photographic technique for * improving the sharpness of images. In its digital form it is implemented * using convolution to create a low-pass filtered version of a source image. * The low-pass image is then subtracted from the original image, * creating a high-pass image. The high pass image is then added back * to the original image, creating enhanced edge contrast. By adjusting * a scaling factor, the degree of high pass add-back can be controlled. * *

      The operation is implemented algorithmically as follows. At each * original pixel location x,y: * *

       *    result = original + (original - lowpass) * gainFactor
       *
       *     where
       *      original = value at position x,y of source image
       *      lowpass  = result of convolution with lowpass filter
       *                 centered at pixel x,y
       *      gain     = controlling parameter for degree of sharpness
       *                   gain = 0 : no effect
       *                   gain > 0 : sharpening
       *                   -1 < gain < 0 : smoothing
       * 
      *

      In general gain factors should be restricted to a range of * [-1, 2], as higher magnitude values are likely to cause * overflows or underflows which must be clamped to the image * data type's range. * *

      The default gain factor is set to 1,0F. * *

      This operation is widely applied to scanned image enhancement. * The typical gain factor for scanned images takes values in the range * of [1/4, 2] (page 278 in Digital Image Processing by William * K. Pratt, 3rd). * *

      * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName UnsharpMask
      LocalName UnsharpMask
      Vendor com.sun.media.jai
      Description Performs unsharp masking to sharpen or smooth * an image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/UnsharpMaskDescriptor.html
      Version 1.1
      arg0Desc The convolution kernel.
      arg1Desc The gain factor.

      * *

      * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      kernel javax.media.jai.KernelJAI3 X 3 average
      gain java.lang.Float1.0F

      * * @see javax.media.jai.OperationDescriptor * @see javax.media.jai.KernelJAI * @see javax.media.jai.operator.ConvolveDescriptor * * @since JAI 1.1 */ public class UnsharpMaskDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation and * specify the parameter list for a UnsharpMask operation. */ private static final String[][] resources = { {"GlobalName", "UnsharpMask"}, {"LocalName", "UnsharpMask"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("UnsharpMaskDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/UnsharpMaskDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("UnsharpMaskDescriptor1")}, {"arg1Desc", JaiI18N.getString("UnsharpMaskDescriptor2")} }; /** The parameter names for the UnsharpMask operation. */ private static final String[] paramNames = { "kernel", "gain" }; /** The parameter class types for the UnsharpMask operation. */ private static final Class[] paramClasses = { javax.media.jai.KernelJAI.class, java.lang.Float.class }; /** The parameter default values for the UnsharpMask operation. */ private static final Object[] paramDefaults = { new KernelJAI(3, 3, 1, 1, new float[]{1/9F, 1/9F, 1/9F, 1/9F, 1/9F, 1/9F, 1/9F, 1/9F, 1/9F}), new Float(1.0F) }; /** Constructor. */ public UnsharpMaskDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** * Returns an array of PropertyGenerators implementing * property inheritance for the "UnsharpMask" operation. * * @return An array of property generators. */ public PropertyGenerator[] getPropertyGenerators() { PropertyGenerator[] pg = new PropertyGenerator[1]; pg[0] = new AreaOpPropertyGenerator(); return pg; } /** * Performs UnsharpMask operation on the image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param kernel The low-pass convolution kernel. * May be null. * @param gain The sharpening value. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, KernelJAI kernel, Float gain, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("UnsharpMask", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("kernel", kernel); pb.setParameter("gain", gain); return JAI.create("UnsharpMask", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/NotDescriptor.java0000644000175000017500000001373310203035544026641 0ustar mathieumathieu/* * $RCSfile: NotDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:41 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Not" operation. * *

      The Not operation takes one rendered or renderable image, and * performs bit-wise logical "not" on every pixel from every band of * the source image. No additional parameters are required. * *

      The source image must have an integral data type. By default, * the destination image bound, data type, and number of bands are the * same as the source image. * *

      The following matrix defines the logical "not" operation. *

      * * * * *
      Logical "not"
      src Result
      1 0
      0 1

      * *

      The destination pixel values are defined by the pseudocode: *

       * dst[x][y][b] = ~src[x][y][b];
       * 
      * *

      * * * * * * * * *
      Resource List
      Name Value
      GlobalName Not
      LocalName Not
      Vendor com.sun.media.jai
      Description Logically "nots" an image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/NotDescriptor.html
      Version 1.0

      * *

      No parameters are needed for this operation. * * @see javax.media.jai.OperationDescriptor */ public class NotDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Not"}, {"LocalName", "Not"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("NotDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/NotDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")} }; private static final String[] supportedModes = { "rendered", "renderable" }; /** Constructor. */ public NotDescriptor() { super(resources, supportedModes, 1, null, null, null, null); } /** * Validates the input source. * *

      In addition to the standard checks performed by the * superclass method, this method checks that the source image * is of integral data type. */ protected boolean validateSources(String modeName, ParameterBlock args, StringBuffer msg) { if (!super.validateSources(modeName, args, msg)) { return false; } if (!modeName.equalsIgnoreCase("rendered")) return true; RenderedImage src = args.getRenderedSource(0); int dtype = src.getSampleModel().getDataType(); if (dtype != DataBuffer.TYPE_BYTE && dtype != DataBuffer.TYPE_USHORT && dtype != DataBuffer.TYPE_SHORT && dtype != DataBuffer.TYPE_INT) { msg.append(getName() + " " + JaiI18N.getString("NotDescriptor1")); return false; } return true; } /** * Logically "nots" an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Not", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); return JAI.create("Not", pb, hints); } /** * Logically "nots" an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Not", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); return JAI.createRenderable("Not", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/AWTImageDescriptor.java0000644000175000017500000001026410203035544027473 0ustar mathieumathieu/* * $RCSfile: AWTImageDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:28 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.Image; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "AWTImage" operation. * *

      The AWTImage operation converts a standard * java.awt.Image into a rendered image. By default, the * width and height of the image are the same as the original AWT * image. The sample model and color model are set according to the * AWT image data. * *

      * * * * * * * * * *
      Resource List
      Name Value
      GlobalName AWTImage
      LocalName AWTImage
      Vendor com.sun.media.jai
      Description Converts a java.awt.Image * into a rendered image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/AWTImageDescriptor.html
      Version 1.0
      arg0Desc The AWT image to be converted.

      * *

      * * * * * *
      Parameter List
      Name Class TypeDefault Value
      awtImage java.awt.ImageNO_PARAMETER_DEFAULT

      * * @see java.awt.Image * @see javax.media.jai.OperationDescriptor */ public class AWTImageDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "AWTImage"}, {"LocalName", "AWTImage"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("AWTImageDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/AWTImageDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("AWTImageDescriptor1")} }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { java.awt.Image.class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "awtImage" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT }; /** Constructor. */ public AWTImageDescriptor() { super(resources, 0, paramClasses, paramNames, paramDefaults); } /** * Converts a java.awt.Image into a rendered image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param awtImage The AWT image to be converted. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if awtImage is null. */ public static RenderedOp create(Image awtImage, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("AWTImage", RenderedRegistryMode.MODE_NAME); pb.setParameter("awtImage", awtImage); return JAI.create("AWTImage", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/OrConstDescriptor.java0000644000175000017500000002063510203035544027467 0ustar mathieumathieu/* * $RCSfile: OrConstDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:41 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "OrConst" operation. * *

      The Or operation takes one rendered or renderable image and an * array of integer constants, and performs a bit-wise logical "or" * between every pixel in the same band of the source and the constant * from the corresponding array entry. If the number of constants * supplied is less than the number of bands of the destination, then * the constant from entry 0 is applied to all the bands. Otherwise, a * constant from a different entry is applied to each band. * *

      The source image must have an integral data type. By default, * the destination image bound, data type, and number of bands are the * same as the source image. * *

      The following matrix defines the logical "or" operation. *

      * * * * * * *
      Logical "or"
      src const Result
      0 0 0
      0 1 1
      1 0 1
      1 1 1

      * *

      The destination pixel values are defined by the pseudocode: *

       * if (constants.length < dstNumBands) {
       *     dst[x][y][b] = src[x][y][b] | constants[0];
       * } else {
       *     dst[x][y][b] = src[x][y][b] | constants[b];
       * }
       * 
      * *

      * * * * * * * * * *
      Resource List
      Name Value
      GlobalName OrConst
      LocalName OrConst
      Vendor com.sun.media.jai
      Description Logically "ors" an image * with constants.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/OrConstDescriptor.html
      Version 1.0
      arg0Desc The constants to logically "or" with.

      * *

      * * * * * *
      Parameter List
      Name Class TypeDefault Value
      constants int[]{0}

      * * @see javax.media.jai.OperationDescriptor */ public class OrConstDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "OrConst"}, {"LocalName", "OrConst"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("OrConstDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/OrConstDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("OrConstDescriptor1")} }; /** * The parameter class list for this operation. * The number of constants provided should be either 1, in which case * this same constant is applied to all the source bands; or the same * number as the source bands, in which case one contant is applied * to each band. */ private static final Class[] paramClasses = { int[].class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "constants" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { new int[]{0} }; private static final String[] supportedModes = { "rendered", "renderable" }; /** Constructor. */ public OrConstDescriptor() { super(resources, supportedModes, 1, paramNames, paramClasses, paramDefaults, null); } /** * Validates the input source and parameter. * *

      In addition to the standard checks performed by the * superclass method, this method checks that the source image has * an integral data type and that "constants" has length at least 1. */ public boolean validateArguments(String modeName, ParameterBlock args, StringBuffer message) { if (!super.validateArguments(modeName, args, message)) { return false; } if (!modeName.equalsIgnoreCase("rendered")) return true; RenderedImage src = args.getRenderedSource(0); int dtype = src.getSampleModel().getDataType(); if (dtype != DataBuffer.TYPE_BYTE && dtype != DataBuffer.TYPE_USHORT && dtype != DataBuffer.TYPE_SHORT && dtype != DataBuffer.TYPE_INT) { message.append(getName() + " " + JaiI18N.getString("OrConstDescriptor2")); return false; } int length = ((int[])args.getObjectParameter(0)).length; if (length < 1) { message.append(getName() + " " + JaiI18N.getString("OrConstDescriptor3")); return false; } return true; } /** * Logically "ors" an image with constants. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param constants The constants to logically "or" with. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, int[] constants, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("OrConst", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("constants", constants); return JAI.create("OrConst", pb, hints); } /** * Logically "ors" an image with constants. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param constants The constants to logically "or" with. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, int[] constants, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("OrConst", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("constants", constants); return JAI.createRenderable("OrConst", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/GradientMagnitudeDescriptor.java0000644000175000017500000001763410203035544031500 0ustar mathieumathieu/* * $RCSfile: GradientMagnitudeDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:36 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.util.AreaOpPropertyGenerator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.JAI; import javax.media.jai.KernelJAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PropertyGenerator; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "GradientMagnitude" * operation. * *

      The "GradientMagnitude" operation is an edge detector which computes * the magnitude of the image gradient vector in two orthogonal directions. * *

      The result of the "GradientMagnitude" operation may be defined as: *

       * dst[x][y][b] = ((SH(x,y,b))^2 + (SV(x,y,b))^2 )^0.5
       * 
      * * where SH(x,y,b) and SV(x,y,b) are the horizontal and vertical gradient * images generated from band b of the source image by correlating it * with the supplied orthogonal (horizontal and vertical) gradient masks. * * Origins set on the kernels will be ignored. The origins are assumed to be * width/2 & height/2. * * It should be noted that this operation automatically adds a * value of Boolean.TRUE for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL to the given * configuration so that the operation is performed * on the pixel values instead of being performed on the indices into * the color map if the source(s) have an IndexColorModel. * This addition will take place only if a value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL has not already been * provided by the user. Note that the configuration Map * is cloned before the new hint is added to it. The operation can be * smart about the value of the JAI.KEY_REPLACE_INDEX_COLOR_MODEL * RenderingHints, i.e. while the default value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL is * Boolean.TRUE, in some cases the operator could set the * default. * *

      * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName GradientMagnitude
      LocallName GradientMagnitude
      Vendor com.sun.media.jai
      Description Performs gradient magnitude edge detection * on an image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jaiapi/javax.media.jai.operator.GradientMagnitudeDescriptor.html
      Version 1.0
      arg0Desc A gradient mask
      arg1Desc A gradient mask orthogonal to the first one.

      * *

      * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      mask1 javax.media.jai.KernelJAIKernalJAI.GRADIENT_MASK_SOBEL_HORIZONTAL
      mask2 javax.media.jai.KernelJAIKernalJAI.GRADIENT_MASK_SOBEL_VERTICAL

      * * @see javax.media.jai.OperationDescriptor * @see javax.media.jai.KernelJAI */ public class GradientMagnitudeDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for the GradientMagnitude operation. */ private static final String[][] resources = { {"GlobalName", "GradientMagnitude"}, {"LocalName", "GradientMagnitude"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("GradientMagnitudeDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jaiapi/javax.media.jai.operator.GradientMagnitudeDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", "A gradient mask."}, {"arg1Desc", "A gradient mask orthogonal to the first one."} }; /** The parameter names for the GradientMagnitude operation. */ private static final String[] paramNames = { "mask1", "mask2" }; /** The parameter class types for the GradientMagnitude operation. */ private static final Class[] paramClasses = { javax.media.jai.KernelJAI.class, javax.media.jai.KernelJAI.class }; /** The parameter default values for the GradientMagnitude operation. */ private static final Object[] paramDefaults = { KernelJAI.GRADIENT_MASK_SOBEL_HORIZONTAL, KernelJAI.GRADIENT_MASK_SOBEL_VERTICAL }; /** Constructor for the GradientMagnitudeDescriptor. */ public GradientMagnitudeDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** * Validates the input parameters. * *

      In addition to the standard checks performed by the * superclass method, this method checks that "mask1" and "mask2" * have the same dimensions. */ protected boolean validateParameters(ParameterBlock args, StringBuffer msg) { if (!super.validateParameters(args, msg)) { return false; } KernelJAI h_kernel = (KernelJAI)args.getObjectParameter(0); KernelJAI v_kernel = (KernelJAI)args.getObjectParameter(1); /* Check if both kernels are equivalent in terms of dimensions. */ if ((h_kernel.getWidth() != v_kernel.getWidth()) || (h_kernel.getHeight() != v_kernel.getHeight())) { msg.append(getName() + " " + JaiI18N.getString("GradientMagnitudeDescriptor1")); return false; } return true; } /** * Returns an array of PropertyGenerators implementing * property inheritance for the "GradientMagnitude" operation. * * @return An array of property generators. */ public PropertyGenerator[] getPropertyGenerators() { PropertyGenerator[] pg = new PropertyGenerator[1]; pg[0] = new AreaOpPropertyGenerator(); return pg; } /** * Computes the gradient of an image * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param mask1 A gradient mask. * May be null. * @param mask2 A gradient mask orthogonal to the first one. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, KernelJAI mask1, KernelJAI mask2, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("GradientMagnitude", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("mask1", mask1); pb.setParameter("mask2", mask2); return JAI.create("GradientMagnitude", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/BoxFilterDescriptor.java0000644000175000017500000002213610664641443030010 0ustar mathieumathieu/* * $RCSfile: BoxFilterDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2007-08-27 21:33:23 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.util.AreaOpPropertyGenerator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PropertyGenerator; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "BoxFilter" operation. * *

      The "BoxFilter" operation determines the intensity of a pixel * in an image by averaging the source pixels within a rectangular * area around the pixel. This is a special case of the convolution * operation, in which each source pixel contributes the same weight * to the destination pixel. The pixel values of the destination image * are defined by the pseudocode: * *

       *     int count = width * height; // # of pixels in the box
       *     for (int b = 0; b < numBands; b++) {
       *         int total = 0;
       *         for (int j = -yKey; j < -yKey + height; j++) {
       *             for (int i = -xKey; i < -xKey + width; i++) {
       *                 total += src[x+i][y+j][b];
       *             }
       *         }
       *         dst[x][y][b] = (total + count/2) / count; // round
       *     }
       * 
      * *

      Convolution, like any neighborhood operation, leaves a band of * pixels around the edges undefined. For example, for a 3x3 kernel * only four kernel elements and four source pixels contribute to the * convolution pixel at the corners of the source image. Pixels that * do not allow the full kernel to be applied to the source are not * included in the destination image. A "Border" operation may be used * to add an appropriate border to the source image in order to avoid * shrinkage of the image boundaries. * *

      The kernel may not be bigger in any dimension than the image data. * *

      * * * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName BoxFilter
      LocalName BoxFilter
      Vendor com.sun.media.jai
      Description Performs special case convolution where each * source pixel contributes equally to the * intensity of the destination pixel.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/BoxFilterDescriptor.html
      Version 1.0
      arg0Desc The width of the box.
      arg1Desc The height of the box.
      arg2Desc The X position of the key element.
      arg3Desc The Y position of the key element.

      * *

      * * * * * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      width java.lang.Integer3
      height java.lang.Integerwidth
      xKey java.lang.Integerwidth/2
      yKey java.lang.Integerheight/2

      * * @see javax.media.jai.OperationDescriptor */ public class BoxFilterDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "BoxFilter"}, {"LocalName", "BoxFilter"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("BoxFilterDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/BoxFilterDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("BoxFilterDescriptor1")}, {"arg1Desc", JaiI18N.getString("BoxFilterDescriptor2")}, {"arg2Desc", JaiI18N.getString("BoxFilterDescriptor3")}, {"arg3Desc", JaiI18N.getString("BoxFilterDescriptor4")} }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { java.lang.Integer.class, java.lang.Integer.class, java.lang.Integer.class, java.lang.Integer.class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "width", "height", "xKey", "yKey" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { new Integer(3), null, null, null }; /** Constructor. */ public BoxFilterDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** * Returns the minimum legal value of a specified numeric parameter * for this operation. */ public Number getParamMinValue(int index) { if (index == 0 || index == 1) { return new Integer(1); } else if (index == 2 || index == 3) { return new Integer(Integer.MIN_VALUE); } else { throw new ArrayIndexOutOfBoundsException(); } } protected boolean validateParameters(ParameterBlock args, StringBuffer msg) { // The number of parameters supplied. int argNumParams = args.getNumParameters(); if(argNumParams == 0) { // set width to default args.add(paramDefaults[0]); argNumParams++; } if(argNumParams > 0 && args.getObjectParameter(0) instanceof Integer) { Object obj; if(argNumParams < 2) { obj = args.getObjectParameter(0); if(obj instanceof Integer) { // set height to width args.add(obj); } } if(argNumParams < 3) { obj = args.getObjectParameter(0); if(obj instanceof Integer) { // set xKey to width/2 args.add(((Integer)obj).intValue()/2); } } if(argNumParams < 4) { obj = args.getObjectParameter(1); if(obj instanceof Integer) { // set yKey to height/2 args.add(((Integer)obj).intValue()/2); } } } return super.validateParameters(args, msg); } /** * Returns an array of PropertyGenerators implementing * property inheritance for the "BoxFilter" operation. * * @return An array of property generators. */ public PropertyGenerator[] getPropertyGenerators() { PropertyGenerator[] pg = new PropertyGenerator[1]; pg[0] = new AreaOpPropertyGenerator(); return pg; } /** * Performs special case convolution where each source pixel contributes equally to the intensity of the destination pixel. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param width The width of the box. * May be null. * @param height The height of the box. * May be null. * @param xKey The X position of the key element. * May be null. * @param yKey The Y position of the key element. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, Integer width, Integer height, Integer xKey, Integer yKey, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("BoxFilter", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("width", width); pb.setParameter("height", height); pb.setParameter("xKey", xKey); pb.setParameter("yKey", yKey); return JAI.create("BoxFilter", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/ConjugateDescriptor.java0000644000175000017500000001511610203035544030015 0ustar mathieumathieu/* * $RCSfile: ConjugateDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:32 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PropertyGenerator; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Conjugate" operation. * *

      The Conjugate operation negates the imaginary components of * pixel values of a rendered or renderable source image containing * complex data. The source image must contain an even number of * bands with the even-indexed bands (0, 2, ...) representing the * real and the odd-indexed bands (1, 3, ...) the imaginary parts of * each pixel. The destination image similarly contains an even * number of bands with the same interpretation and with contents * defined by: * *

       * dst[x][y][2*k]   =  src[x][y][2*k];
       * dst[x][y][2*k+1] = -src[x][y][2*k+1];
       * 
      * * where the index k varies from zero to one less than the number * of complex components in the destination image. * *

      "Conjugate" defines a PropertyGenerator that sets the "COMPLEX" * property of the image to java.lang.Boolean.TRUE, which may * be retrieved by calling the getProperty() method with * "COMPLEX" as the property name. * *

      * * * * * * * * *
      Resource List
      Name Value
      GlobalName Conjugate
      LocalName Conjugate
      Vendor com.sun.media.jai
      Description Computes the complex conjugate of a complex image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ConjugateDescriptor.html
      Version 1.0

      * *

      No parameters are needed for the "Conjugate" operation. * * @see javax.media.jai.OperationDescriptor */ public class ConjugateDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Conjugate"}, {"LocalName", "Conjugate"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("ConjugateDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ConjugateDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")} }; private static final String[] supportedModes = { "rendered", "renderable" }; /** Constructor. */ public ConjugateDescriptor() { super(resources, supportedModes, 1, null, null, null, null); } /** * Returns an array of PropertyGenerators implementing * property inheritance for the "Conjugate" operation. * * @return An array of property generators. */ public PropertyGenerator[] getPropertyGenerators(String modeName) { PropertyGenerator[] pg = new PropertyGenerator[1]; pg[0] = new ComplexPropertyGenerator(); return pg; } /** * Validates the input sources. * *

      In addition to the standard checks performed by the * superclass method, this method checks that the source image * has an even number of bands. */ protected boolean validateSources(String modeName, ParameterBlock args, StringBuffer msg) { if (!super.validateSources(modeName, args, msg)) { return false; } if (!modeName.equalsIgnoreCase("rendered")) return true; RenderedImage src = args.getRenderedSource(0); if (src.getSampleModel().getNumBands() % 2 != 0) { msg.append(getName() + " " + JaiI18N.getString("ConjugateDescriptor1")); return false; } return true; } /** * Computes the complex conjugate of a complex image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Conjugate", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); return JAI.create("Conjugate", pb, hints); } /** * Computes the complex conjugate of a complex image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Conjugate", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); return JAI.createRenderable("Conjugate", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/InvertDescriptor.java0000644000175000017500000001143410203035544027344 0ustar mathieumathieu/* * $RCSfile: InvertDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:37 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Invert" operation. * *

      The "Invert" operation inverts the pixel values of an image. * For source images with signed data types, the pixel values of the * destination image are defined by the pseudocode: * *

      dst[x][y][b] = -src[x][y][b]
      * *

      For unsigned data types, the destination values are defined by: * *

      dst[x][y][b] = MAX_VALUE - src[x][y][b]
      * * where MAX_VALUE is the maximum value supported by the * system of the data type of the source pixel. * *

      * * * * * * * * *
      Resource List
      Name Value
      GlobalName Invert
      LocalName Invert
      Vendor com.sun.media.jai
      Description Invert the pixel values of an image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/InvertDescriptor.html
      Version 1.0

      * *

      No parameters are needed for the "Invert" operation. * * @see javax.media.jai.OperationDescriptor */ public class InvertDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Invert"}, {"LocalName", "Invert"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("InvertDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/InvertDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")} }; /** Constructor. */ public InvertDescriptor() { super(resources, 1, null, null, null); } /** Returns true since renderable operation is supported. */ public boolean isRenderableSupported() { return true; } /** * Inverts the pixel values of an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Invert", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); return JAI.create("Invert", pb, hints); } /** * Inverts the pixel values of an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Invert", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); return JAI.createRenderable("Invert", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/JPEGDescriptor.java0000644000175000017500000001037010203035544026620 0ustar mathieumathieu/* * $RCSfile: JPEGDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:37 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.codec.SeekableStream; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "JPEG" operation. * *

      The "JPEG" operation reads an image from a JPEG (JFIF) stream. * *

      The classes in the com.sun.media.jai.codec * package are not a committed part of the JAI API. Future releases * of JAI will make use of new classes in their place. This * class will change accordingly. * *

      * * * * * * * * * *
      Resource List
      Name Value
      GlobalName JPEG
      LocalName JPEG
      Vendor com.sun.media.jai
      Description Reads an image from a JFIF (JPEG) stream.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/JPEGDescriptor.html
      Version 1.0
      arg0Desc The SeekableStream to read from.

      * *

      * * * * * *
      Parameter List
      Name Class TypeDefault Value
      stream com.sun.media.jai.codec.SeekableStreamNO_PARAMETER_DEFAULT

      * * @see com.sun.media.jai.codec.SeekableStream * @see javax.media.jai.OperationDescriptor */ public class JPEGDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation and * specify the parameter list for the "JPEG" operation. */ private static final String[][] resources = { {"GlobalName", "JPEG"}, {"LocalName", "JPEG"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("JPEGDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/JPEGDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("JPEGDescriptor1")}, }; /** The parameter names for the "JPEG" operation. */ private static final String[] paramNames = { "stream" }; /** The parameter class types for the "JPEG" operation. */ private static final Class[] paramClasses = { com.sun.media.jai.codec.SeekableStream.class }; /** The parameter default values for the "JPEG" operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT }; /** Constructor. */ public JPEGDescriptor() { super(resources, 0, paramClasses, paramNames, paramDefaults); } /** * Reads a standard JFIF (JPEG) stream. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param stream The SeekableStream to read from. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if stream is null. */ public static RenderedOp create(SeekableStream stream, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("JPEG", RenderedRegistryMode.MODE_NAME); pb.setParameter("stream", stream); return JAI.create("JPEG", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/CompositeDescriptor.java0000644000175000017500000004122410203035544030037 0ustar mathieumathieu/* * $RCSfile: CompositeDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:32 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.EnumeratedParameter; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.operator.CompositeDestAlpha; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Composite" operation. * *

      The "Composite" operation combines two images based on their alpha * values at each pixel. It is done on a per-band basis, and the two source * images are expected to have the same number of bands and the same data * type. The destination image has the same data type as the two sources. * *

      The destAlpha parameter indicates if the destination * image should have an extra alpha channel. If this parameter is set to * NO_DESTINATION_ALPHA, then the destination image does not * include an alpha band, and it should have the same number of bands as * the two source images. If it is set to DESTINATION_ALPHA_FIRST, * then the destination image has one extra band than the source images, * which represents the result alpha channel, and this band is the first * band (band 0) of the destination. If it is set to * DESTINATION_ALPHA_LAST, then the destination image also * has the extra alpha channel, but this band is the last band of the * destination. * *

      The destination pixel values may be viewed as representing a fractional * pixel coverage or transparency factor. Specifically, Composite implements * the Porter-Duff "over" rule (see Computer Graphics, July 1984 pp. * 253-259), in which the output color of a pixel with source value/alpha * tuples (A, a) and (B, b) is given by * a*A + (1 - a)*(b*B). The output alpha value is given * by a + (1 - a)*b. For premultiplied sources tuples * (a*A, a) and (b*B, b), the premultiplied output * value is simply (a*A) + (1 - a)*(b*B). * *

      The color channels of the two source images are supplied via * source1 and source2. The two sources must * be either both pre-multiplied by alpha or not. Alpha channel should * not be included in source1 and source2. * *

      The alpha channel of the first source images must be supplied * via the source1Alpha parameter. This parameter may not * be null. The alpha channel of the second source image may be supplied * via the source2Alpha parameter. This parameter may be * null, in which case the second source is considered completely opaque. * The alpha images should be single-banded, and have the same data type * as well as dimensions as their corresponding source images. * *

      The alphaPremultiplied parameter indicates whether * or not the supplied alpha image is premultiplied to both the source * images. It also indicates whether the destination image color channels * have the alpha values multiplied to the pixel color values. * *

      It should be noted that the source1Alpha and * source1Alpha parameters are RenderedImages in * the "rendered" mode and are RenderableImages in the * "renderable" mode. * *

      The destination image is the combination of the two source images. * It has the color channels, and if specified, one additional alpha channel * (the band index depends on the value of the destAlpha * parameter). Whether alpha value is pre-multiplied to the color channels * also depend on the value of alphaPremultiplied (pre-multiplied * if true). * *

      * * * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName composite
      LocallName composite
      Vendor com.sun.media.jai
      Description Composites two images based on an alpha mask.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jaiapi/javax.media.jai.operator.CompositeDescriptor.html
      Version 1.0
      arg0Desc The alpha image for the first source.
      arg1Desc The alpha image for the second source.
      arg2Desc True if alpha has been premultiplied to both * sources and the destination.
      arg3Desc Indicates if the destination image should * include an extra alpha channel, and if so, * should it be the first or last band.

      * *

      * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
      Parameter List
      NameClass TypeDefault Value
      source1AlphaRendered modejava.awt.image.RenderedImageNO_PARAMETER_DEFAULT
      Renderable modejava.awt.image.renderable.RenderableImage
      source2AlphaRendered modejava.awt.image.RenderedImagenull
      Renderable modejava.awt.image.renderable.RenderableImage
      alphaPremultipliedjava.lang.Booleanfalse
      destAlphajavax.media.jai.operator.CompositeDestAlphaNO_DESTINATION_ALPHA

      * * @see CompositeDestAlpha * @see java.awt.image.ColorModel * @see javax.media.jai.OperationDescriptor */ public class CompositeDescriptor extends OperationDescriptorImpl { /** The destination image does not have the alpha channel. */ public static final CompositeDestAlpha NO_DESTINATION_ALPHA = new CompositeDestAlpha("NO_DESTINATION_ALPHA", 0); /** The destination image has the channel, and it is the first band. */ public static final CompositeDestAlpha DESTINATION_ALPHA_FIRST = new CompositeDestAlpha("DESTINATION_ALPHA_FIRST", 1); /** The destination image has the channel, and it is the last band. */ public static final CompositeDestAlpha DESTINATION_ALPHA_LAST = new CompositeDestAlpha("DESTINATION_ALPHA_LAST", 2); /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ protected static final String[][] resources = { {"GlobalName", "Composite"}, {"LocalName", "Composite"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("CompositeDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/CompositeDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion2")}, {"arg0Desc", JaiI18N.getString("CompositeDescriptor1")}, {"arg1Desc", JaiI18N.getString("CompositeDescriptor2")}, {"arg2Desc", JaiI18N.getString("CompositeDescriptor3")}, {"arg3Desc", JaiI18N.getString("CompositeDescriptor4")} }; private static final Class[][] sourceClasses = { { java.awt.image.RenderedImage.class, java.awt.image.RenderedImage.class }, { java.awt.image.renderable.RenderableImage.class, java.awt.image.renderable.RenderableImage.class } }; /** The parameter class list for this operation. */ private static final Class[][] paramClasses = { { java.awt.image.RenderedImage.class, java.awt.image.RenderedImage.class, java.lang.Boolean.class, CompositeDestAlpha.class }, { java.awt.image.renderable.RenderableImage.class, java.awt.image.renderable.RenderableImage.class, java.lang.Boolean.class, CompositeDestAlpha.class } }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "source1Alpha", "source2Alpha", "alphaPremultiplied", "destAlpha" }; /** The parameter default value list for this operation. */ private static final Object[][] paramDefaults = { { NO_PARAMETER_DEFAULT, null, Boolean.FALSE, NO_DESTINATION_ALPHA }, { NO_PARAMETER_DEFAULT, null, Boolean.FALSE, NO_DESTINATION_ALPHA } }; private static final String[] supportedModes = { "rendered", "renderable" }; /** Constructor. */ public CompositeDescriptor() { super(resources, supportedModes, null, sourceClasses, paramNames, paramClasses, paramDefaults, null); } /** * Validates the input sources and parameters. * *

      In addition to the standard checks performed by the * superclass method, this method checks that the source image * samplemodels have the same number of bands and * transfer type, and that the alpha images have the same bounds * as the corresponding sources and the correct transfer type. */ public boolean validateArguments(String modeName, ParameterBlock args, StringBuffer msg) { if (!super.validateArguments(modeName, args, msg)) { return false; } if (!modeName.equalsIgnoreCase("rendered")) return true; RenderedImage src1 = args.getRenderedSource(0); RenderedImage src2 = args.getRenderedSource(1); SampleModel s1sm = src1.getSampleModel(); SampleModel s2sm = src2.getSampleModel(); if (s1sm.getNumBands() != s2sm.getNumBands() || s1sm.getTransferType() != s2sm.getTransferType()) { msg.append(getName() + " " + JaiI18N.getString("CompositeDescriptor8")); return false; } /* Validate Parameters. */ RenderedImage afa1 = (RenderedImage)args.getObjectParameter(0); if (src1.getMinX() != afa1.getMinX() || src1.getMinY() != afa1.getMinY() || src1.getWidth() != afa1.getWidth() || src1.getHeight() != afa1.getHeight()) { msg.append(getName() + " " + JaiI18N.getString("CompositeDescriptor12")); return false; } SampleModel a1sm = afa1.getSampleModel(); if (s1sm.getTransferType() != a1sm.getTransferType()) { msg.append(getName() + " " + JaiI18N.getString("CompositeDescriptor13")); return false; } RenderedImage afa2 = (RenderedImage)args.getObjectParameter(1); if (afa2 != null) { if (src2.getMinX() != afa2.getMinX() || src2.getMinY() != afa2.getMinY() || src2.getWidth() != afa2.getWidth() || src2.getHeight() != afa2.getHeight()) { msg.append(getName() + " " + JaiI18N.getString("CompositeDescriptor15")); return false; } SampleModel a2sm = afa2.getSampleModel(); if (s2sm.getTransferType() != a2sm.getTransferType()) { msg.append(getName() + " " + JaiI18N.getString("CompositeDescriptor16")); return false; } } return true; } /** * Composites two images based on an alpha mask. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param source1 RenderedImage source 1. * @param source1Alpha The alpha image for the first source. * @param source2Alpha The alpha image for the second source. * May be null. * @param alphaPremultiplied True if alpha has been premultiplied to both sources and the destination. * May be null. * @param destAlpha Indicates if the destination image should include an extra alpha channel, and if so, should it be the first or last band. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if source1 is null. * @throws IllegalArgumentException if source1Alpha is null. */ public static RenderedOp create(RenderedImage source0, RenderedImage source1, RenderedImage source1Alpha, RenderedImage source2Alpha, Boolean alphaPremultiplied, CompositeDestAlpha destAlpha, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Composite", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setSource("source1", source1); pb.setParameter("source1Alpha", source1Alpha); pb.setParameter("source2Alpha", source2Alpha); pb.setParameter("alphaPremultiplied", alphaPremultiplied); pb.setParameter("destAlpha", destAlpha); return JAI.create("Composite", pb, hints); } /** * Composites two images based on an alpha mask. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param source1 RenderableImage source 1. * @param source1Alpha The alpha image for the first source. * @param source2Alpha The alpha image for the second source. * May be null. * @param alphaPremultiplied True if alpha has been premultiplied to both sources and the destination. * May be null. * @param destAlpha Indicates if the destination image should include an extra alpha channel, and if so, should it be the first or last band. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if source1 is null. * @throws IllegalArgumentException if source1Alpha is null. */ public static RenderableOp createRenderable(RenderableImage source0, RenderableImage source1, RenderableImage source1Alpha, RenderableImage source2Alpha, Boolean alphaPremultiplied, CompositeDestAlpha destAlpha, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Composite", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setSource("source1", source1); pb.setParameter("source1Alpha", source1Alpha); pb.setParameter("source2Alpha", source2Alpha); pb.setParameter("alphaPremultiplied", alphaPremultiplied); pb.setParameter("destAlpha", destAlpha); return JAI.createRenderable("Composite", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/ErodeDescriptor.java0000644000175000017500000002161410203035544027134 0ustar mathieumathieu/* * $RCSfile: ErodeDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:35 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.util.AreaOpPropertyGenerator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import javax.media.jai.JAI; import javax.media.jai.KernelJAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PropertyGenerator; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; /** * * An OperationDescriptor describing the "Erode" operation. * *

      Gray Scale Erosion * is a spatial operation that computes * each output sample by subtracting elements of a kernel from the samples * surrounding a particular source sample. * The mathematical formulation for erosion operation is: * *

      For a kernel K with a key position (xKey, yKey), the erosion * of image I at (x,y) is given by: *

       *     max{ f:  f + K(xKey+i, yKey+j) <= I(x+i,y+j): all (i,j)}
       *
       *      "all" possible (i,j) means that both I(x+i,y+j) and K(xKey+i, yKey+j)
       *      are in bounds. Otherwise, the value is set to 0.
       *      "f" represents all possible floats satisfying the restriction.
       *
       * 
      *

      Intuitively, the kernel is like an unbrella and the key point * is the handle. At every point, you try to push the umbrella up as high * as possible but still underneath the image surface. The final height * of the handle is the value after erosion. Thus if you want the image * to erode from the upper right to bottom left, the following would do. * *

      * * * * *
      00X
      0X0
      X00
      * *

      Note that even if every entry of a kernel is zero, * the erosion changes the image. Different key positions * will also lead to different erosion results for such zero kernels. * *

      Pseudo code for the erosion operation is as follows. * Assuming the kernel K is of size M rows x N cols * and the key position is (xKey, yKey). * *

       * 
       * // erosion
       * for every dst pixel location (x,y){
       *    tmp = infinity;
       *    for (i = -xKey; i < M - xKey; i++){
       *       for (j = -yKey; j < N - yKey; j++){
       *          if((x+i, y+j) are in bounds of src){
       *             tmp = min{tmp, src[x + i][y + j] - K[xKey + i][yKey + j]};
       *          }
       *       }
       *    }
       *    dst[x][y] = tmp;
       *    if (dst[x][y] == infinity)
       *        dst[x][y] = 0;
       * }
       * 
      * *

      The kernel cannot be bigger in any dimension than the image data. * *

      Binary Image Erosion * requires the kernel to be binary, that is, to have values 0 and 1 * for each kernel entry. * Intuitively, binary erosion slides the kernel * key position and place it at every point (x,y) in the src image. * The dst value at this position is set to 1 if the entire kernel lies * within the image bounds and the src image value is 1 * wherever the corresponding kernel value is 1." * Otherwise, the value after erosion at (x,y) is set to 0. * Erosion usually shrinks images, but it can fill holes * with kernels like *

       [1 0 1] 
      * and the key position at the center. * *

      Pseudo code for the binary erosion operation is as follows. * *

       * // erosion
       * for every dst pixel location (x,y){
       *    dst[x][y] = 1;
       *    for (i = -xKey; i < M - xKey; i++){
       *       for (j = -yKey; j < N - yKey; j++){
       *         if((x+i,y+j) is out of bounds of src ||
       *             src(x+i, y+j)==0 && Key(xKey+i, yKey+j)==1){
       *            dst[x][y] = 0; break;
       *          }
       *       }
       *    }
       * }
       *
       * The following can be used as references for the underlying
       * connection between these two algorithms.
       *
       * 

      Reference: An Introduction to Nonlinear Image Processing, * by Edward R. Bougherty and Jaakko Astola, * Spie Optical Engineering Press, 1994. * * It should be noted that this operation automatically adds a * value of Boolean.TRUE for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL to the given * configuration so that the operation is performed * on the pixel values instead of being performed on the indices into * the color map if the source(s) have an IndexColorModel. * This addition will take place only if a value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL has not already been * provided by the user. Note that the configuration Map * is cloned before the new hint is added to it. The operation can be * smart about the value of the JAI.KEY_REPLACE_INDEX_COLOR_MODEL * RenderingHints, i.e. while the default value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL is * Boolean.TRUE, in some cases the operator could set the * default. * *

      * * * * * * * * * *
      Resource List
      Name Value
      GlobalName Erode
      LocalName Erode
      Vendor com.sun.media.jai
      Description Performs kernel based Erode on * an image.
      DocURL http://java.sun.com/products/java-media/jai/forD evelopers/jai-apidocs/javax/media/jai/operator/ErodeDescriptor.html
      Version 1.1
      arg0Desc The erode kernel.

      * *

      * * * * * *
      Parameter List
      Name Class TypeDefault Value
      kernel javax.media.jai.KernelJAINO_PARAMETER_DEFAULT

      * *
      * * * @see javax.media.jai.KernelJAI * * @since JAI 1.1 */ public class ErodeDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation and * specify the parameter list for a Erode operation. */ private static final String[][] resources = { {"GlobalName", "Erode"}, {"LocalName", "Erode"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("ErodeDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jaiapi/
      javax.media.jai.operator.ErodeDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("ErodeDescriptor1")} }; /** The parameter names for the Erode operation. */ private static final String[] paramNames = { "kernel" }; /** The parameter class types for the Erode operation. */ private static final Class[] paramClasses = { javax.media.jai.KernelJAI.class }; /** The parameter default values for the Erode operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT }; /** Constructor. */ public ErodeDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** * Returns an array of PropertyGenerators implementing * property inheritance for the "Erode" operation. * * @return An array of property generators. */ public PropertyGenerator[] getPropertyGenerators() { PropertyGenerator[] pg = new PropertyGenerator[1]; pg[0] = new AreaOpPropertyGenerator(); return pg; } /** * Performs binary kernel based Erode operation on the image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param kernel The binary convolution kernel. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if kernel is null. */ public static RenderedOp create(RenderedImage source0, KernelJAI kernel, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Erode", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("kernel", kernel); return JAI.create("Erode", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/AddConstDescriptor.java0000644000175000017500000001664710203035544027607 0ustar mathieumathieu/* * $RCSfile: AddConstDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:29 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "AddConst" operation. * *

      The AddConst operation takes one rendered or renderable source * image and an array of double constants, and adds a constant to * every pixel of its corresponding band of the source. If the number * of constants supplied is less than the number of bands of the * destination, then the constant from entry 0 is applied to all the * bands. Otherwise, a constant from a different entry is applied to * each band. * *

      By default, the destination image bound, data type, and number of * bands are the same as the source image. If the result of the operation * underflows/overflows the minimum/maximum value supported by the * destination data type, then it will be clamped to the minimum/maximum * value respectively. * *

      The destination pixel values are defined by the pseudocode: *

       * if (constants.length < dstNumBands) {
       *     dst[x][y][b] = src[x][y][b] + constants[0];
       * } else {
       *     dst[x][y][b] = src[x][y][b] + constants[b];
       * }
       * 
      * *

      * * * * * * * * * *
      Resource List
      Name Value
      GlobalName AddConst
      LocalName AddConst
      Vendor com.sun.media.jai
      Description Adds constants to an image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/AddConstDescriptor.html
      Version 1.0
      arg0Desc The constants to be added.

      * *

      * * * * * *
      Parameter List
      Name Class TypeDefault Value
      constants double[]{0.0}

      * * @see javax.media.jai.OperationDescriptor */ public class AddConstDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "AddConst"}, {"LocalName", "AddConst"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("AddConstDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/AddConstDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("AddConstDescriptor1")} }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "constants" }; /** * The parameter class list for this operation. * The number of constants provided should be either 1, in which case * this same constant is applied to all the source bands; or the same * number as the source bands, in which case one contant is applied * to each band. */ private static final Class[] paramClasses = { double[].class }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { new double[] {0.0} }; /** Constructor. */ public AddConstDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** Returns true since renderable operation is supported. */ public boolean isRenderableSupported() { return true; } /** * Validates the input parameter. * *

      In addition to the standard checks performed by the * superclass method, this method checks that the length of the * "constants" array is at least 1. */ protected boolean validateParameters(ParameterBlock args, StringBuffer message) { if (!super.validateParameters(args, message)) { return false; } int length = ((double[])args.getObjectParameter(0)).length; if (length < 1) { message.append(getName() + " " + JaiI18N.getString("AddConstDescriptor2")); return false; } return true; } /** * Adds constants to an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param constants The constants to be added. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, double[] constants, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("AddConst", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("constants", constants); return JAI.create("AddConst", pb, hints); } /** * Adds constants to an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param constants The constants to be added. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, double[] constants, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("AddConst", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("constants", constants); return JAI.createRenderable("AddConst", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/PolarToComplexDescriptor.java0000644000175000017500000001746710203035544031021 0ustar mathieumathieu/* * $RCSfile: PolarToComplexDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:43 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PropertyGenerator; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "PolarToComplex" * operation. * *

      The "PolarToComplex" operation creates an image with complex-valued * pixels from two images the respective pixel values of which represent the * magnitude (modulus) and phase of the corresponding complex pixel in the * destination image. The source images should have the same number of bands. * The first source image contains the magnitude values and the second source * image the phase values. The destination will have twice as many bands with * the even-indexed bands (0, 2, ...) representing the real and the * odd-indexed bands (1, 3, ...) the imaginary parts of each pixel. The * pixel values of the destination image are defined for a given complex * sample by the pseudocode: * *

       * dst[x][y][2*b]   = src0[x][y][b]*Math.cos(src1[x][y][b])
       * dst[x][y][2*b+1] = src0[x][y][b]*Math.sin(src1[x][y][b])
       * 
      * * where the index b varies from zero to one less than the number * of bands in the source images. * *

      For phase images with integral data type, it is assumed that the actual * phase angle is scaled from the range [-PI, PI] to the range [0, MAX_VALUE] * where MAX_VALUE is the maximum value of the data type in question. * *

      "PolarToComplex" defines a PropertyGenerator that sets the "COMPLEX" * property of the image to java.lang.Boolean.TRUE, which may * be retrieved by calling the getProperty() method with * "COMPLEX" as the property name. * *

      * * * * * * * * *
      Resource List
      Name Value
      GlobalName PolarToComplex
      LocalName PolarToComplex
      Vendor com.sun.media.jai
      Description Computes a complex image from a magnitude and a phase image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/PolarToComplexDescriptor.html
      Version 1.0

      * *

      No parameters are needed for the "PolarToComplex" operation. * * @see javax.media.jai.OperationDescriptor * @see javax.media.jai.operator.PhaseDescriptor */ public class PolarToComplexDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "PolarToComplex"}, {"LocalName", "PolarToComplex"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("PolarToComplexDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/PolarToComplexDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")} }; private static final String[] supportedModes = { "rendered", "renderable" }; /** Constructor. */ public PolarToComplexDescriptor() { super(resources, supportedModes, 2, null, null, null, null); } /** * Validates the input sources. * *

      In addition to the standard checks performed by the * superclass method, this method checks that the source images * have the same number of bands. */ protected boolean validateSources(String modeName, ParameterBlock args, StringBuffer msg) { if (!super.validateSources(modeName, args, msg)) { return false; } if (!modeName.equalsIgnoreCase("rendered")) return true; RenderedImage src1 = args.getRenderedSource(0); RenderedImage src2 = args.getRenderedSource(1); if (src1.getSampleModel().getNumBands() != src2.getSampleModel().getNumBands()) { msg.append(getName() + " " + JaiI18N.getString("PolarToComplexDescriptor1")); return false; } return true; } /** * Returns an array of PropertyGenerators implementing * property inheritance for the "Conjugate" operation. * * @return An array of property generators. */ public PropertyGenerator[] getPropertyGenerators(String modeName) { PropertyGenerator[] pg = new PropertyGenerator[1]; pg[0] = new ComplexPropertyGenerator(); return pg; } /** * Computes a complex image from a magnitude and a phase image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param source1 RenderedImage source 1. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if source1 is null. */ public static RenderedOp create(RenderedImage source0, RenderedImage source1, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("PolarToComplex", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setSource("source1", source1); return JAI.create("PolarToComplex", pb, hints); } /** * Computes a complex image from a magnitude and a phase image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param source1 RenderableImage source 1. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if source1 is null. */ public static RenderableOp createRenderable(RenderableImage source0, RenderableImage source1, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("PolarToComplex", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setSource("source1", source1); return JAI.createRenderable("PolarToComplex", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/ShearDir.java0000644000175000017500000000135710203035544025542 0ustar mathieumathieu/* * $RCSfile: ShearDir.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:44 $ * $State: Exp $ */ package javax.media.jai.operator; import javax.media.jai.EnumeratedParameter; /** * Class used to represent the acceptable values of the "shearDir" * parameter of the "Shear" operation. Acceptable values for the * "shearDir" parameter are defined in the ShearDescriptor * by the constants SHEAR_HORIZONTAL and * SHEAR_VERTICAL. * * @since JAI 1.1 */ public final class ShearDir extends EnumeratedParameter { ShearDir(String name, int value) { super(name, value); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/LogDescriptor.java0000644000175000017500000001215710203035544026621 0ustar mathieumathieu/* * $RCSfile: LogDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:38 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Log" operation. * *

      The "Log" operation takes the natural logarithm of the pixel * values of an image. The operation is done on a per-pixel, per-band * basis. For integral data types, the result will be rounded and * clamped as needed. The pixel values of the destination image are * defined as: *

       * dst[x][y][b] = java.lang.Math.log(src[x][y][b])
       * 
      * *

      For all integral data types, the log of 0 is set to 0. For * signed integral data types (short and int), * the log of a negative pixel value is set to -1. * *

      For all floating point data types ((float and * double), the log of 0 is set to -Infinity, * and the log of a negative pixel value is set to NaN. * *

      * * * * * * * * *
      Resource List
      Name Value
      GlobalName Log
      LocalName Log
      Vendor com.sun.media.jai
      Description Computes the natural logarithm of the pixel values * of an image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/LogDescriptor.html
      Version 1.0

      * *

      No parameters are needed for the "Log" operation. * * @see javax.media.jai.OperationDescriptor */ public class LogDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Log"}, {"LocalName", "Log"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("LogDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/LogDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")} }; /** Constructor. */ public LogDescriptor() { super(resources, 1, null, null, null); } /** Returns true since renderable operation is supported. */ public boolean isRenderableSupported() { return true; } /** * Computes the natural logarithm of the pixel values of an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Log", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); return JAI.create("Log", pb, hints); } /** * Computes the natural logarithm of the pixel values of an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Log", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); return JAI.createRenderable("Log", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/TranslateDescriptor.java0000644000175000017500000003200510203035544030027 0ustar mathieumathieu/* * $RCSfile: TranslateDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:45 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.util.PropertyGeneratorImpl; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.GeometricOpImage; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationNearest; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PlanarImage; import javax.media.jai.PropertyGenerator; import javax.media.jai.ROI; import javax.media.jai.ROIShape; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * This property generator computes the properties for the operation * "Translate" dynamically. */ class TranslatePropertyGenerator extends PropertyGeneratorImpl { /** Constructor. */ public TranslatePropertyGenerator() { super(new String[] {"ROI"}, new Class[] {ROI.class}, new Class[] {RenderedOp.class}); } /** * Returns the specified property. * * @param name Property name. * @param opNode Operation node. */ public Object getProperty(String name, Object opNode) { validate(name, opNode); if(opNode instanceof RenderedOp && name.equalsIgnoreCase("roi")) { RenderedOp op = (RenderedOp)opNode; ParameterBlock pb = op.getParameterBlock(); // Retrieve the rendered source image and its ROI. RenderedImage src = pb.getRenderedSource(0); Object property = src.getProperty("ROI"); if (property == null || property.equals(java.awt.Image.UndefinedProperty) || !(property instanceof ROI)) { return java.awt.Image.UndefinedProperty; } ROI srcROI = (ROI)property; // Retrieve the Interpolation object. Interpolation interp = (Interpolation)pb.getObjectParameter(2); // Determine the effective source bounds. Rectangle srcBounds = null; PlanarImage dst = op.getRendering(); if (dst instanceof GeometricOpImage && ((GeometricOpImage)dst).getBorderExtender() == null) { srcBounds = new Rectangle(src.getMinX() + interp.getLeftPadding(), src.getMinY() + interp.getTopPadding(), src.getWidth() - interp.getWidth() + 1, src.getHeight() - interp.getHeight() + 1); } else { srcBounds = new Rectangle(src.getMinX(), src.getMinY(), src.getWidth(), src.getHeight()); } // If necessary, clip the ROI to the effective source bounds. if(!srcBounds.contains(srcROI.getBounds())) { srcROI = srcROI.intersect(new ROIShape(srcBounds)); } // Retrieve the translation values. float tx = pb.getFloatParameter(0); float ty = pb.getFloatParameter(1); // Create a transform representing the translation. AffineTransform transform = AffineTransform.getTranslateInstance((double) tx, (double) ty); // Create the translated ROI. ROI dstROI = srcROI.transform(transform); // Retrieve the destination bounds. Rectangle dstBounds = op.getBounds(); // If necessary, clip the warped ROI to the destination bounds. if(!dstBounds.contains(dstROI.getBounds())) { dstROI = dstROI.intersect(new ROIShape(dstBounds)); } // Return the warped and possibly clipped ROI. return dstROI; } return java.awt.Image.UndefinedProperty; } } /** * An OperationDescriptor describing the "Translate" operation. * *

      The "Translate" operation copies an image to a new location * in the plane. * *

      For each pixel (x, y) of the destination, the source value at * the fractional subpixel position (x - xTrans, y - yTrans) is * constructed by means of an Interpolation object and written to the * destination. If both xTrans and yTrans are integral, the operation * simply "wraps" its source image to change the image's position in * the coordinate plane. * *

      It may be noted that the minX, minY, width and height hints as * specified through the JAI.KEY_IMAGE_LAYOUT hint in the * RenderingHints object are not honored, as this operator * calculates the destination image bounds itself. The other * ImageLayout hints, like tileWidth and tileHeight, * however are honored. * *

      It should be noted that this operation automatically adds a * value of Boolean.TRUE for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL to the given * configuration so that the operation is performed * on the pixel values instead of being performed on the indices into * the color map if the source(s) have an IndexColorModel. * This addition will take place only if a value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL has not already been * provided by the user. Note that the configuration Map * is cloned before the new hint is added to it. The operation can be * smart about the value of the JAI.KEY_REPLACE_INDEX_COLOR_MODEL * RenderingHints, i.e. while the default value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL is * Boolean.TRUE, in some cases the operator could set the * default. * *

      "Translate" defines a PropertyGenerator that performs an * identical transformation on the "ROI" property of the source image, * which can be retrieved by calling the getProperty * method with "ROI" as the property name. * *

      * * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName Translate
      LocalName Translate
      Vendor com.sun.media.jai
      Description Moves an image to a new location.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/TranslateDescriptor.html
      Version 1.0
      arg0Desc The displacement in X direction.
      arg1Desc The displacement in Y direction.
      arg2Desc The interpolation method.

      * *

      * * * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      xTrans java.lang.Float0.0F
      yTrans java.lang.Float0.0F
      interpolation javax.media.jai.InterpolationInterpolationNearest

      * * @see javax.media.jai.Interpolation * @see javax.media.jai.OperationDescriptor */ public class TranslateDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation and * specify the parameter list for the "Translate" operation. */ private static final String[][] resources = { {"GlobalName", "Translate"}, {"LocalName", "Translate"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("TranslateDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/TranslateDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("TranslateDescriptor1")}, {"arg1Desc", JaiI18N.getString("TranslateDescriptor2")}, {"arg2Desc", JaiI18N.getString("TranslateDescriptor3")} }; /** The parameter names for the "Translate" operation. */ private static final String[] paramNames = { "xTrans", "yTrans", "interpolation" }; /** The parameter class types for the "Translate" operation. */ private static final Class[] paramClasses = { java.lang.Float.class, java.lang.Float.class, javax.media.jai.Interpolation.class }; /** The parameter default values for the "Translate" operation. */ private static final Object[] paramDefaults = { new Float(0.0F), new Float(0.0F), Interpolation.getInstance(Interpolation.INTERP_NEAREST) }; /** Constructor. */ public TranslateDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** Returns true since renderable operation is supported. */ public boolean isRenderableSupported() { return true; } /** * Returns an array of PropertyGenerators implementing * property inheritance for the "Translate" operation * * @return An array of property generators. */ public PropertyGenerator[] getPropertyGenerators() { PropertyGenerator[] pg = new PropertyGenerator[1]; pg[0] = new TranslatePropertyGenerator(); return pg; } /** * Moves an image to a new location. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param xTrans The displacement in X direction. * May be null. * @param yTrans The displacement in Y direction. * May be null. * @param interpolation The interpolation method. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, Float xTrans, Float yTrans, Interpolation interpolation, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Translate", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("xTrans", xTrans); pb.setParameter("yTrans", yTrans); pb.setParameter("interpolation", interpolation); return JAI.create("Translate", pb, hints); } /** * Moves an image to a new location. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param xTrans The displacement in X direction. * May be null. * @param yTrans The displacement in Y direction. * May be null. * @param interpolation The interpolation method. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, Float xTrans, Float yTrans, Interpolation interpolation, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Translate", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("xTrans", xTrans); pb.setParameter("yTrans", yTrans); pb.setParameter("interpolation", interpolation); return JAI.createRenderable("Translate", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/SubsampleAverageDescriptor.java0000644000175000017500000003743710203035544031336 0ustar mathieumathieu/* * $RCSfile: SubsampleAverageDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:44 $ * $State: Exp $ */package javax.media.jai.operator; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.GeometricOpImage; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationNearest; import javax.media.jai.JAI; import javax.media.jai.OpImage; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PlanarImage; import javax.media.jai.PropertyGenerator; import javax.media.jai.ROI; import javax.media.jai.ROIShape; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; import javax.media.jai.util.Range; /** * This property generator computes the properties for the operation * "SubsampleAverage" dynamically. * * @since JAI 1.1.2 */ class SubsampleAveragePropertyGenerator implements PropertyGenerator { /** Constructor. */ public SubsampleAveragePropertyGenerator() {} /** * Returns the valid property names for the operation "SubsampleAverage". * This is equal to the array {"ROI"}. */ public String[] getPropertyNames() { String[] properties = new String[1]; properties[0] = "ROI"; return(properties); } /** * Returns the expected class which is ROI.class if * propertyName and null otherwise. */ public Class getClass(String propertyName) { if(propertyName == null) { throw new IllegalArgumentException (JaiI18N.getString("SubsampleAveragePropertyGenerator0")); } else if(propertyName.equalsIgnoreCase("roi")) { return ROI.class; } return null; } /** * Determines whether properties can be generated from the supplied node. */ public boolean canGenerateProperties(Object opNode) { if(opNode == null) { throw new IllegalArgumentException (JaiI18N.getString("SubsampleAveragePropertyGenerator1")); } return opNode instanceof RenderedOp; } /** * Returns the specified property for the supplied node. * * @param name Property name. * @param opNode Operation node. */ public Object getProperty(String name, Object opNode) { if(name == null || opNode == null) { throw new IllegalArgumentException (JaiI18N.getString("SubsampleAveragePropertyGenerator2")); } else if(!canGenerateProperties(opNode)) { throw new IllegalArgumentException (opNode.getClass().getName()+ JaiI18N.getString("SubsampleAveragePropertyGenerator3")); } return opNode instanceof RenderedOp ? getProperty(name, (RenderedOp)opNode) : null; } /** * Returns the specified property. * * @param name Property name. * @param op Operation node. */ public Object getProperty(String name, RenderedOp op) { if(name == null || op == null) { throw new IllegalArgumentException (JaiI18N.getString("SubsampleAveragePropertyGenerator4")); } if (name.equals("roi")) { ParameterBlock pb = op.getParameterBlock(); // Retrieve the rendered source image and its ROI. PlanarImage src = (PlanarImage)pb.getRenderedSource(0); Object property = src.getProperty("ROI"); if (property == null || property.equals(java.awt.Image.UndefinedProperty) || !(property instanceof ROI)) { return null; } ROI srcROI = (ROI)property; // Determine the effective source bounds. Rectangle srcBounds = null; PlanarImage dst = op.getRendering(); if(dst instanceof GeometricOpImage && ((GeometricOpImage)dst).getBorderExtender() == null) { GeometricOpImage geomIm = (GeometricOpImage)dst; Interpolation interp = geomIm.getInterpolation(); srcBounds = new Rectangle(src.getMinX() + interp.getLeftPadding(), src.getMinY() + interp.getTopPadding(), src.getWidth() - interp.getWidth() + 1, src.getHeight() - interp.getHeight() + 1); } else { srcBounds = src.getBounds(); } // If necessary, clip the ROI to the effective source bounds. if(!srcBounds.contains(srcROI.getBounds())) { srcROI = srcROI.intersect(new ROIShape(srcBounds)); } // Retrieve the scale factors and translation values. double sx = pb.getDoubleParameter(0); double sy = pb.getDoubleParameter(1); // Create an equivalent transform. AffineTransform transform = new AffineTransform(sx, 0.0, 0.0, sy, 0, 0); // Create the scaled ROI. ROI dstROI = srcROI.transform(transform); // Retrieve the destination bounds. Rectangle dstBounds = op.getBounds(); // If necessary, clip the warped ROI to the destination bounds. if(!dstBounds.contains(dstROI.getBounds())) { dstROI = dstROI.intersect(new ROIShape(dstBounds)); } // Return the warped and possibly clipped ROI. return dstROI; } else { return null; } } /** * Returns null. * * @param name Property name. * @param op Operation node. */ public Object getProperty(String name, RenderableOp op) { if(name == null || op == null) { throw new IllegalArgumentException (JaiI18N.getString("SubsampleAveragePropertyGenerator2")); } return null; } } /** * An OperationDescriptor describing the "SubsampleAverage" * operation. "SubsampleAverage" supports the rendered and renderable modes. * *

      * The "SubsampleAverage" operation subsamples an image by averaging * over a moving window. The scale factors supplied to the operation are * forward mapping coefficients representing the geometric transformation * from source to destination image coordinates. For example, if both * scale factors were equal to 0.5, the operation would produce an output * image of half the size of the input image in both dimensions. Both * scale factors must be in the range (0.0, 1.0] or an * exception will be thrown when the operation is created. The default * value of the vertical scale factor is the value of the horizontal scale * factor. If both scale factors equal 1.0, the source * image is returned directly. *

      * *

      * The size of the moving window or block over which source pixels are * averaged varies as a function of the scale factors and is defined as *

       *     int blockX = (int)Math.ceil(1.0/scaleX);
       *     int blockY = (int)Math.ceil(1.0/scaleY);
       * 
      *

      * *

      * For a given destination pixel (dstX, dstY), the upper * left corner (srcX, srcY) of the source block over which * averaging occurs is defined as *

       *     int srcX = (int)Math.floor((dstX - dstMinX)/scaleX + 0.5) + srcMinX;
       *     int srcY = (int)Math.floor((dstY - dstMinY)/scaleY + 0.5) + srcMinY;
       * 
      * where (srcMinX, srcMinY) are the image coordinates of the * upper left pixel of the source and (dstMinX, dstMinY) are * the image coordinates of the upper left pixel of the destination. *

      * *

      * The destination image bounds are defined as *

       *    int dstMinX = (int)Math.floor(srcMinX*scaleX);
       *    int dstMinY = (int)Math.floor(srcMinY*scaleY);
       *    int dstWidth = (int)(srcWidth*scaleX);
       *    int dstHeight = (int)(srcHeight*scaleY);
       * 
      * where (srcWidth, srcHeight) and * (dstWidth, dstHeight) are the source and destination * image dimensions, respectively. *

      * *

      * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName SubsampleAverage
      LocalName SubsampleAverage
      Vendor com.sun.media.jai
      Description Subsamples an image by averaging over a moving window.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/SubsampleAverageDescriptor.html
      Version 1.0
      arg0Desc The X scale factor.
      arg1Desc The Y scale factor.

      * *

      * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      scaleX java.lang.Double0.5
      scaleY java.lang.DoublescaleX

      * * @see FilteredSubsampleDescriptor * @see SubsampleBinaryToGrayDescriptor * @see ScaleDescriptor * * @since JAI 1.1.2 */ public class SubsampleAverageDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "SubsampleAverage"}, {"LocalName", "SubsampleAverage"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("SubsampleAverageDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/SubsampleAverageDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("SubsampleAverageDescriptor1")}, {"arg1Desc", JaiI18N.getString("SubsampleAverageDescriptor2")} }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { java.lang.Double.class, java.lang.Double.class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "scaleX", "scaleY" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { new Double(0.5), null }; /** The allowable Ranges of parameter values. */ private static final Object[] validParamValues = { new Range(Double.class, new Double(Double.MIN_VALUE), new Double(1.0)), new Range(Double.class, new Double(Double.MIN_VALUE), new Double(1.0)) }; /** Constructor. */ public SubsampleAverageDescriptor() { super(resources, new String[] {RenderedRegistryMode.MODE_NAME, RenderableRegistryMode.MODE_NAME}, 1, paramNames, paramClasses, paramDefaults, validParamValues); } /** * Returns an array of PropertyGenerators implementing * property inheritance for the "SubsampleAverage" operation. */ public PropertyGenerator[] getPropertyGenerators(String modeName) { if(modeName == null) { throw new IllegalArgumentException (JaiI18N.getString("SubsampleAverageDescriptor3")); } if(!RenderedRegistryMode.MODE_NAME.equalsIgnoreCase(modeName)) { PropertyGenerator[] pg = new PropertyGenerator[1]; pg[0] = new SubsampleAveragePropertyGenerator(); return pg; } return null; } /** * Validates the input parameters. * *

      In addition to the standard checks performed by the * superclass method, this method sets "scaleX" to "scaleY" * if the latter is not provided in args. */ protected boolean validateParameters(String modeName, ParameterBlock args, StringBuffer msg) { if (!super.validateParameters(modeName, args, msg)) { return false; } if(args.getNumParameters() < 2 || args.getObjectParameter(1) == null) { args.set(args.getObjectParameter(0), 1); } return true; } /** * Subsamples an image by averaging over a moving window. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param scaleX The X scale factor. * May be null. * @param scaleY The Y scale factor. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, Double scaleX, Double scaleY, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("SubsampleAverage", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("scaleX", scaleX); pb.setParameter("scaleY", scaleY); return JAI.create("SubsampleAverage", pb, hints); } /** * Subsamples an image by averaging over a moving window. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param scaleX The X scale factor. * May be null. * @param scaleY The Y scale factor. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, Double scaleX, Double scaleY, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("SubsampleAverage", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("scaleX", scaleX); pb.setParameter("scaleY", scaleY); return JAI.createRenderable("SubsampleAverage", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/DilateDescriptor.java0000644000175000017500000002303610203035544027300 0ustar mathieumathieu/* * $RCSfile: DilateDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:34 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.util.AreaOpPropertyGenerator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import javax.media.jai.JAI; import javax.media.jai.KernelJAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PropertyGenerator; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Dilate" operation. * * Dilation for gray scale images can be charaterized by "slide, add and max", * while for binary images by "slide and set". As always, the kernel * is expected to come with a key position. * *

      Dilation, unlike convolution and most neighborhood operations, * actually can grow the image region. But to conform with other * image neighborhood operations, the border pixels are set to 0. * For a 3 x 3 kernel with the key point at the center, there will * be a pixel wide 0 stripe around the border. * *

      When applied to multi-band images the dilation operator processes * each band independently using the methodology which would be applied * to single band images of the same data type. * *

      Gray scale dilation is a spatial operation that computes * each output sample by adding elements of a kernel to the samples * surrounding a particular source sample and taking the maximum. * A mathematical expression is: * *

      For a kernel K with a key position (xKey,yKey), the dilation * of image I at (x,y) is given by: *

       *     max{ I(x-i, y-j) + K(xKey+i, yKey+j): some (i,j) restriction }
       *  
       *      where the (i,j) restriction means:
       *      all possible (i,j) so that both I(x-i,y-j) and K(xKey+i, yKey+j)
       *      are defined, that is, these indices are in bounds.
       *
       * 
      *

      Intuitively in 2D, the kernel is like * an umbrella and the key point is the handle. When the handle moves * all over the image surface, the upper outbounds of all the umbrella * positions is the dilation. Thus if you want the image to dilate in * the upper right direction, the following kernel would do with * the bold face key position. * *

      * * * * *
      0050
      0500
      000
      * *

      Note also that zero kernel have effects on the dilation! * That is because of the "max" in the add and max process. Thus * a 3 x 1 zero kernel with the key position at the bottom of the kernel * dilates the image upwards. * *

      * After the kernel is rotated 180 degrees, Pseudo code for dilation operation * is as follows. Of course, you should provide the kernel in its * (unrotated) original form. Assuming the kernel K is of size M rows x N cols * and the key position is (xKey, yKey). * *

       * // gray-scale dilation:
       * for every dst pixel location (x,y){
       *    dst[x][y] = -infinity;
       *    for (i = -xKey; i < M - xKey; i++){
       *       for (j = -yKey; j < N - yKey; j++){
       *          if((x+i, y+j) are in bounds of src &&
       *	      (xKey+i, yKey+j) are in bounds of K){
       *             tmp = src[x + i][y + j]+ K[xKey + i][yKey + j];
       *	       dst[x][y] = max{tmp, dst[x][y]};
       *          }
       *       }
       *    }
       * }
       * 
      * *

      The kernel cannot be bigger in any dimension than the image data. * *

      Binary Image Dilation * requires the kernel K to be binary, that is, have values 0 or 1 * for all kernel entries. * Intuitively, starting from dst image being a duplicate of src, * binary dilation slides the kernel K to place the key position * at every non-zero point (x,y) in src image and set dst positions * under ones of K to 1. * *

      After the kernel is rotated 180 degrees, the pseudo code for * dilation operation is as follows. (Of course, you should provide * the kernel in its original unrotated form.) * *

       * 
       * // binary dilation
       * for every dst pixel location (x,y){
       *    dst[x][y] = src[x][y];
       *    for (i = -xKey; i < M - xKey; i++){
       *       for (j = -yKey; j < N - yKey; j++){
       *         if(src[x+i,y+i]==1 && Key(xKey+i, yKey+j)==1){
       *            dst[x][y] = 1; break;
       *          }
       *       }
       *    }
       * }
       *
       * It should be noted that this operation automatically adds a
       * value of Boolean.TRUE for the
       * JAI.KEY_REPLACE_INDEX_COLOR_MODEL to the given
       * configuration so that the operation is performed
       * on the pixel values instead of being performed on the indices into
       * the color map if the source(s) have an IndexColorModel.
       * This addition will take place only if a value for the 
       * JAI.KEY_REPLACE_INDEX_COLOR_MODEL has not already been
       * provided by the user. Note that the configuration Map
       * is cloned before the new hint is added to it. The operation can be 
       * smart about the value of the JAI.KEY_REPLACE_INDEX_COLOR_MODEL
       * RenderingHints, i.e. while the default value for the
       * JAI.KEY_REPLACE_INDEX_COLOR_MODEL is
       * Boolean.TRUE, in some cases the operator could set the
       * default. 
       *
       * 

      * * * * * * * * * *
      Resource List
      Name Value
      GlobalName Dilate
      LocalName Dilate
      Vendor com.sun.media.jai
      Description Performs kernel based Dilate on * an image.
      DocURL http://java.sun.com/products/java-media/jai/forD evelopers/jai-apidocs/javax/media/jai/operator/DilateDescriptor.html
      Version 1.1
      arg0Desc The dilate kernel.

      * *

      * * * * * *
      Parameter List
      Name Class TypeDefault Value
      kernel javax.media.jai.KernelJAINO_PARAMETER_DEFAULT

      * *
      *

      Reference: An Introduction to Nonlinear Image Processing, * by Edward R. Bougherty and Jaakko Astola, * Spie Optical Engineering Press, 1994. * * @see javax.media.jai.OperationDescriptor * @see javax.media.jai.KernelJAI * @see javax.media.jai.operator.ErodeDescriptor * * @since JAI 1.1 */ public class DilateDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation and * specify the parameter list for a Dilate operation. */ private static final String[][] resources = { {"GlobalName", "Dilate"}, {"LocalName", "Dilate"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("DilateDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jaiapi/
      javax.media.jai.operator.DilateDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("DilateDescriptor1")} }; /** The parameter names for the Dilate operation. */ private static final String[] paramNames = { "kernel" }; /** The parameter class types for the Dilate operation. */ private static final Class[] paramClasses = { javax.media.jai.KernelJAI.class }; /** The parameter default values for the Dilate operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT }; /** Constructor. */ public DilateDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** * Returns an array of PropertyGenerators implementing * property inheritance for the "Dilate" operation. * * @return An array of property generators. */ public PropertyGenerator[] getPropertyGenerators() { PropertyGenerator[] pg = new PropertyGenerator[1]; pg[0] = new AreaOpPropertyGenerator(); return pg; } /** * Performs binary kernel based Dilate operation on the image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param kernel The binary convolution kernel. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if kernel is null. */ public static RenderedOp create(RenderedImage source0, KernelJAI kernel, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Dilate", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("kernel", kernel); return JAI.create("Dilate", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/XorConstDescriptor.java0000644000175000017500000002067110203035544027657 0ustar mathieumathieu/* * $RCSfile: XorConstDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:46 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "XorConst" operation. * *

      The XorConst operation takes one rendered or renderable image * and an array of integer constants, and performs a bit-wise logical * "xor" between every pixel in the same band of the source and the * constant from the corresponding array entry. If the number of * constants supplied is less than the number of bands of the * destination, then the constant from entry 0 is applied to all the * bands. Otherwise, a constant from a different entry is applied to * each band. * *

      The source image must have an integral data type. By default, * the destination image bound, data type, and number of bands are the * same as the source image. * *

      The following matrix defines the "xor" operation. *

      * * * * * * *
      Logical "xor"
      src const Result
      0 0 0
      0 1 1
      1 0 1
      1 1 0

      * *

      The destination pixel values are defined by the pseudocode: *

       * if (constants.length < dstNumBands) {
       *     dst[x][y][b] = src[x][y][b] ^ constants[0];
       * } else {
       *     dst[x][y][b] = src[x][y][b] ^ constants[b];
       * }
       * 
      * *

      * * * * * * * * * *
      Resource List
      Name Value
      GlobalName XorConst
      LocalName XorConst
      Vendor com.sun.media.jai
      Description Logically "xors" an image * with constants.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/XorConstDescriptor.html
      Version 1.0
      arg0Desc The constants to logically "xor" with.

      * *

      * * * * * *
      Parameter List
      Name Class TypeDefault Value
      constants int[]{0}

      * * @see javax.media.jai.OperationDescriptor */ public class XorConstDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "XorConst"}, {"LocalName", "XorConst"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("XorConstDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/XorConstDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("XorConstDescriptor1")} }; /** * The parameter class list for this operation. * The number of constants provided should be either 1, in which case * this same constant is applied to all the source bands; or the same * number as the source bands, in which case one contant is applied * to each band. */ private static final Class[] paramClasses = { int[].class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "constants" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { new int[] {0} }; private static final String[] supportedModes = { "rendered", "renderable" }; /** Constructor. */ public XorConstDescriptor() { super(resources, supportedModes, 1, paramNames, paramClasses, paramDefaults, null); } /** * Validates the input source and parameter. * *

      In addition to the standard checks performed by the * superclass method, this method checks that the source image has * an integral data type and that "constants" has length at least 1. */ public boolean validateArguments(String modeName, ParameterBlock args, StringBuffer message) { if (!super.validateArguments(modeName, args, message)) { return false; } if (!modeName.equalsIgnoreCase("rendered")) return true; RenderedImage src = args.getRenderedSource(0); int dtype = src.getSampleModel().getDataType(); if (dtype != DataBuffer.TYPE_BYTE && dtype != DataBuffer.TYPE_USHORT && dtype != DataBuffer.TYPE_SHORT && dtype != DataBuffer.TYPE_INT) { message.append(getName() + " " + JaiI18N.getString("XorConstDescriptor2")); return false; } int length = ((int[])args.getObjectParameter(0)).length; if (length < 1) { message.append(getName() + " " + JaiI18N.getString("XorConstDescriptor3")); return false; } return true; } /** * Logically "xors" an image with constants. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param constants The constants to logically "xor" with. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, int[] constants, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("XorConst", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("constants", constants); return JAI.create("XorConst", pb, hints); } /** * Logically "xors" an image with constants. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param constants The constants to logically "xor" with. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, int[] constants, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("XorConst", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("constants", constants); return JAI.createRenderable("XorConst", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/DivideIntoConstDescriptor.java0000644000175000017500000001766210203035544031153 0ustar mathieumathieu/* * $RCSfile: DivideIntoConstDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:34 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the * "DivideIntoConst" operation. * *

      The DivideIntoConst operation takes one rendered or renderable * image and an array of double constants, and divides every pixel of * the same band of the source into the constant from the * corresponding array entry. If the number of constants supplied is * less than the number of bands of the destination, then the constant * from entry 0 is applied to all the bands. Otherwise, a constant * from a different entry is applied to each band. * *

      In case of division by 0, if the numerator is 0, then the result * is set to 0; otherwise, the result is set to the maximum value * supported by the destination data type. * *

      By default, the destination image bound, data type, and number of * bands are the same as the source image. If the result of the operation * underflows/overflows the minimum/maximum value supported by the * destination data type, then it will be clamped to the minimum/maximum * value respectively. * *

      The destination pixel values are defined by the pseudocode: * if (constants.length < dstNumBands) { * dst[x][y][b] = constants[0]/src[x][y][b]; * } else { * dst[x][y][b] = constants[b]/src[x][y][b]; * } * * *

      * * * * * * * * * *
      Resource List
      Name Value
      GlobalName DivideIntoConst
      LocalName DivideIntoConst
      Vendor com.sun.media.jai
      Description Divides an image into * constants.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/DivideIntoConstDescriptor.html
      Version 1.0
      arg0Desc The constants to be divided into.

      * *

      * * * * * *
      Parameter List
      Name Class TypeDefault Value
      constants double[]NO_PARAMETER_DEFAULT

      * * @see javax.media.jai.OperationDescriptor */ public class DivideIntoConstDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "DivideIntoConst"}, {"LocalName", "DivideIntoConst"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("DivideIntoConstDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/DivideIntoConstDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("DivideIntoConstDescriptor1")} }; /** * The parameter class list for this operation. * The number of constants provided should be either 1, in which case * this same constant is applied to all the source bands; or the same * number as the source bands, in which case one contant is applied * to each band. */ private static final Class[] paramClasses = { double[].class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "constants" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT }; /** Constructor. */ public DivideIntoConstDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** Returns true since renderable operation is supported. */ public boolean isRenderableSupported() { return true; } /** * Validates the input parameters. * *

      In addition to the standard checks performed by the * superclass method, this method checks that the length of the * "constants" array is at least 1. */ protected boolean validateParameters(ParameterBlock args, StringBuffer message) { if (!super.validateParameters(args, message)) { return false; } int length = ((double[])args.getObjectParameter(0)).length; if (length < 1) { message.append(getName() + " " + JaiI18N.getString("DivideIntoConstDescriptor2")); return false; } return true; } /** * Divides an image into constants. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param constants The constants to be divided into. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if constants is null. */ public static RenderedOp create(RenderedImage source0, double[] constants, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("DivideIntoConst", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("constants", constants); return JAI.create("DivideIntoConst", pb, hints); } /** * Divides an image into constants. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param constants The constants to be divided into. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if constants is null. */ public static RenderableOp createRenderable(RenderableImage source0, double[] constants, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("DivideIntoConst", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("constants", constants); return JAI.createRenderable("DivideIntoConst", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/FormatDescriptor.java0000644000175000017500000002760710203035544027336 0ustar mathieumathieu/* * $RCSfile: FormatDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:36 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Format" operation. * *

      The "Format" operation performs reformatting on an image. It * is capable of casting the pixel values of an image to a given data * type, replacing the SampleModel and ColorModel of an image, and * restructuring the image's tile grid layout. The pixel values of * the destination image are defined by the pseudocode: * *

      dst[x][y][b] = cast(src[x][y][b], dataType)
      * * where "dataType" is one of the constants TYPE_BYTE, TYPE_SHORT, * TYPE_USHORT, TYPE_INT, TYPE_FLOAT, or TYPE_DOUBLE from * java.awt.image.DataBuffer. * *

      The output SampleModel, ColorModel and tile grid layout are * specified by passing an ImageLayout object as a RenderingHint named * "ImageLayout". The output image will have a SampleModel compatible * with the one specified in the layout hint wherever possible; * however, for output data types of float and * double a ComponentSampleModel will be used * regardless of the value of the hint parameter. * *

      One of the common uses of the format operator is to cast the * pixel values of an image to a given data type. In such a case, if * the source image provided has an IndexColorModel, a * RenderingHints object for * JAI.KEY_REPLACE_INDEX_COLOR_MODEL with the value of * Boolean.TRUE will automatically be added to the * configuration Map for the operation. This addition * will only take place if a value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL has not already been * provided by the user. Note that the configuration Map * is cloned before the new hint is added to it. Due to the addition * of this new RenderingHint, using the "format" operation * with source(s) that have an IndexColorModel will cause * the destination to have an expanded non-IndexColorModel * ColorModel. This expansion ensures that the conversion * to a different data type, ColorModel or * SampleModel happens correctly such that the indices * into the color map (for IndexColorModel images) are * not treated as pixel data. If the format operator is not being used * to cast the pixel values of an image to a given data type, the * expansion will not take place, the resultant image will still have * an IndexColorModel. * *

      The ImageLayout may also specify a tile grid origin and size * which will be respected. * *

      The typecasting performed by the Format function * is defined by the following set of expressions, dependent on the * data types of the source and destination. Casting an image to its * current data type is a no-op. See The * Java Language Specification for the definition of type * conversions between primitive types. * *

      In most cases, it is not necessary to explictly perform widening * typecasts since they will be performed automatically by image * operators when handed source images having different datatypes. * *

      * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
      Source Type Destination Type Action
      BYTE SHORT (short)(x & 0xff)
      BYTE USHORT (short)(x & 0xff)
      BYTE INT (int)(x & 0xff)
      BYTE FLOAT (float)(x & 0xff)
      BYTE DOUBLE (double)(x & 0xff)
      SHORT BYTE (byte)clamp((int)x, 0, 255)
      SHORT USHORT (short)clamp((int)x, 0, 32767)
      SHORT INT (int)x
      SHORT FLOAT (float)x
      SHORT DOUBLE (double)x
      USHORT BYTE (byte)clamp((int)x & 0xffff, 0, 255)
      USHORT SHORT (short)clamp((int)x & 0xffff, 0, 32767)
      USHORT INT (int)(x & 0xffff)
      USHORT FLOAT (float)(x & 0xffff)
      USHORT DOUBLE (double)(x & 0xffff)
      INT BYTE (byte)clamp(x, 0, 255)
      INT SHORT (short)clamp(x, -32768, 32767)
      INT USHORT (short)clamp(x, 0, 65535)
      INT FLOAT (float)x
      INT DOUBLE (double)x
      FLOAT BYTE (byte)clamp((int)x, 0, 255)
      FLOAT SHORT (short)clamp((int)x, -32768, 32767)
      FLOAT USHORT (short)clamp((int)x, 0, 65535)
      FLOAT INT (int)x
      FLOAT DOUBLE (double)x
      DOUBLE BYTE (byte)clamp((int)x, 0, 255)
      DOUBLE SHORT (short)clamp((int)x, -32768, 32767)
      DOUBLE USHORT (short)clamp((int)x, 0, 65535)
      DOUBLE INT (int)x
      DOUBLE FLOAT (float)x

      * * The clamp function may be defined as: *
       * int clamp(int x, int low, int high) {
       *     return (x < low) ? low : ((x > high) ? high : x);
       * }
       * 
      * *

      * * * * * * * * * *
      Resource List
      Name Value
      GlobalName Format
      LocalName Format
      Vendor com.sun.media.jai
      Description Reformats an image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/FormatDescriptor.html
      Version 1.0
      arg0Desc The output data type (from java.awt.image.DataBuffer).

      * *

      * * * * * *
      Parameter List
      Name Class TypeDefault Value
      dataType java.lang.IntegerDataBuffer.TYPE_BYTE

      * * @see java.awt.image.DataBuffer * @see javax.media.jai.ImageLayout * @see javax.media.jai.OperationDescriptor */ public class FormatDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Format"}, {"LocalName", "Format"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("FormatDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/FormatDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", "The output data type (from java.awt.image.DataBuffer)."} }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { java.lang.Integer.class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "dataType" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { new Integer(DataBuffer.TYPE_BYTE) }; /** Constructor. */ public FormatDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** Returns true since renderable operation is supported. */ public boolean isRenderableSupported() { return true; } /** * Returns the minimum legal value of a specified numeric parameter * for this operation. */ public Number getParamMinValue(int index) { if (index == 0) { return new Integer(DataBuffer.TYPE_BYTE); } else { throw new ArrayIndexOutOfBoundsException(); } } /** * Returns the maximum legal value of a specified numeric parameter * for this operation. */ public Number getParamMaxValue(int index) { if (index == 0) { return new Integer(DataBuffer.TYPE_DOUBLE); } else { throw new ArrayIndexOutOfBoundsException(); } } /** * Reformats an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param dataType The output data type (from java.awt.image.DataBuffer). * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, Integer dataType, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Format", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("dataType", dataType); return JAI.create("Format", pb, hints); } /** * Reformats an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param dataType The output data type (from java.awt.image.DataBuffer). * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, Integer dataType, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Format", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("dataType", dataType); return JAI.createRenderable("Format", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/TransposeType.java0000644000175000017500000000161110203035544026652 0ustar mathieumathieu/* * $RCSfile: TransposeType.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:46 $ * $State: Exp $ */ package javax.media.jai.operator; import javax.media.jai.EnumeratedParameter; /** * Class used to represent the acceptable values of the "type" * parameter of the "Transpose" operation. Acceptable values for the * "type" parameter are defined in the TransposeDescriptor * by the constants FLIP_VERTICAL, FLIP_HORIZONTAL, * FLIP_DIAGONAL, FLIP_ANTIDIAGONAL, * ROTATE_90, ROTATE_180, and * ROTATE_270. * * @since JAI 1.1 */ public final class TransposeType extends EnumeratedParameter { TransposeType(String name, int value) { super(name, value); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/IIPResolutionDescriptor.java0000644000175000017500000002606010203035544030603 0ustar mathieumathieu/* * $RCSfile: IIPResolutionDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:37 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.net.URL; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "IIPResolution" * operation. * *

      This operation provides client-side support of the Internet Imaging * Protocol (IIP) in the rendered mode. It is resolution-specific. It * requests from the IIP server an image at a particular resolution level, * and creates a java.awt.image.RenderedImage based on the * data received from the server. Once the RenderedImage * is created, the resolution level cannot be changed. * *

      The layout of the created RenderedImage is set as * follows: *

        *
      • minX, minY, tileGridXOffset, * and tileGridYOffset are set to 0; *
      • width and height are determined based * on the specified resolution level; *
      • tileWidth and tileHeight are set to 64; *
      • sampleModel is of the type * java.awt.image.PixelInterleavedSampleModel with byte * data type and the appropriate number of bands; *
      • colorModel is of the type * java.awt.image.ComponentColorModel, with the * ColorSpace set to sRGB, PhotoYCC, or Grayscale, depending * on the color space of the remote image; if an alpha channel is * present, it will be premultiplied. *
      * *

      The "URL" parameter specifies the URL of the IIP image as a * java.lang.String. It must represent a valid URL, and * include any required FIF or SDS commands. It cannot be null. * *

      The "resolution" parameter specifies the resolution level of the * requested IIP image from the server. The lowest resolution level is * 0, with larger integers representing higher resolution levels. If the * requested resolution level does not exist, the nearest resolution level * is used. If this parameter is not specified, it is set to the default * value IIPResolutionDescriptor.MAX_RESOLUTION which indicates * the highest resolution level. * *

      The "subImage" parameter indicates the sub-image to be used by the * server to get the image at the specified resolution level. This parameter * cannot be negative. If this parameter is not specified, it is set to * the default value 0. * *

      There is no source image associated with this operation. * *

      If available from the IIP server certain properties may be set on the * RenderedImage. The names of properties and the class types * of their associated values are listed in the following table. * *

      * * * * * * * * * * * * * * * * * * * * * * * * * * *
      Property List
      Property Name Property Value Class Type
      affine-transform java.awt.geom.AffineTransform
      app-name java.lang.String
      aspect-ratio java.lang.Float
      author java.lang.String
      colorspace int[]
      color-twist float[16]
      comment java.lang.String
      contrast-adjust java.lang.Float
      copyright java.lang.String
      create-dtm java.lang.String
      edit-time java.lang.String
      filtering-value java.lang.Float
      iip java.lang.String
      iip-server java.lang.String
      keywords java.lang.String
      last-author java.lang.String
      last-printed java.lang.String
      last-save-dtm java.lang.String
      max-size int[2]
      resolution-number java.lang.Integer
      rev-number java.lang.String
      roi-iip java.awt.geom.Rectangle2D.Float
      subject java.lang.String
      title java.lang.String

      * * For information on the significance of each of the above properties please * refer to the IIP specification. * *

      * * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName IIPResolution
      LocalName IIPResolution
      Vendor com.sun.media.jai
      Description Provides client-side support of the Internet * Imaging Protocol in the rendered mode.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/IIPResolutionDescriptor.html
      Version 1.0
      arg0Desc The URL of the IIP image.
      arg1Desc The resolution level to request.
      arg2Desc The sub-image to be used by the * server.

      * *

      * * * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      URL java.lang.StringNO_PARAMETER_DEFAULT
      resolution java.lang.IntegerIIPResolutionDescriptor.MAX_RESOLUTION
      subImage java.lang.Integer0

      * * @see Digital Imaging Group * @see java.awt.image.RenderedImage * @see IIPDescriptor */ public class IIPResolutionDescriptor extends OperationDescriptorImpl { /** Convenience name for Max Resolution of an image on an IIP server. */ public static final Integer MAX_RESOLUTION = new Integer(Integer.MAX_VALUE); /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "IIPResolution"}, {"LocalName", "IIPResolution"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("IIPResolutionDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/IIPResolutionDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("IIPResolutionDescriptor1")}, {"arg1Desc", JaiI18N.getString("IIPResolutionDescriptor2")}, {"arg2Desc", JaiI18N.getString("IIPResolutionDescriptor3")} }; /** The parameter class types for this operation. */ private static final Class[] paramClasses = { java.lang.String.class, java.lang.Integer.class, java.lang.Integer.class }; /** The parameter names for this operation. */ private static final String[] paramNames = { "URL", "resolution", "subImage" }; /** The parameter default values for this operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT, MAX_RESOLUTION, new Integer(0) }; /** Constructor. */ public IIPResolutionDescriptor() { super(resources, 0, paramClasses, paramNames, paramDefaults); } /** * Returns the minimum legal value of a specified numeric parameter * for this operation. If the supplied index does not * correspond to a numeric parameter, this method returns * null. * * @return An Integer of value 0 if index * is 1 or 2, or null if index is 0. * * @throws ArrayIndexOutOfBoundsException if index is less * than 0 or greater than 2. */ public Number getParamMinValue(int index) { if (index == 0) { return null; } else if (index == 1 || index == 2) { return new Integer(0); } else { throw new ArrayIndexOutOfBoundsException(); } } /** * Validates the input parameters. * *

      In addition to the standard checks performed by the * superclass method, this method checks that the supplied URL * string specifies a valid protocol. */ protected boolean validateParameters(ParameterBlock args, StringBuffer msg) { if (!super.validateParameters(args, msg)) { return false; } try { new URL((String)args.getObjectParameter(0)); } catch (Exception e) { /* Use the same error message as IIPDescriptor. */ msg.append(getName() + " " + JaiI18N.getString("IIPDescriptor15")); return false; } return true; } /** * Provides client support of the Internet Imaging Protocol in the rendered mode. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param URL The URL of the IIP image. * @param resolution The resolution level to request. * May be null. * @param subImage The sub-image to be used by the server. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if URL is null. */ public static RenderedOp create(String URL, Integer resolution, Integer subImage, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("IIPResolution", RenderedRegistryMode.MODE_NAME); pb.setParameter("URL", URL); pb.setParameter("resolution", resolution); pb.setParameter("subImage", subImage); return JAI.create("IIPResolution", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/PatternDescriptor.java0000644000175000017500000001227110203035544027512 0ustar mathieumathieu/* * $RCSfile: PatternDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:42 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Pattern" operation. * *

      The "Pattern" operation defines a tiled image consisting of a * repeated pattern. The width and height of the destination image * must be specified. The tileWidth and tileHeight are equal to pattern's * width and height. Each tile of the destination image will be defined * by a reference to a shared instance of the pattern. * *

      * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName pattern
      LocalName pattern
      Vendor com.sun.media.jai
      Description Defines an image with a repeated * pattern.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/PatternDescriptor.html
      Version 1.0
      arg0Desc The width of the image in pixels.
      arg1Desc The height of the image in pixels.

      * *

      * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      width java.lang.IntegerNO_PARAMETER_DEFAULT
      height java.lang.IntegerNO_PARAMETER_DEFAULT

      * * @see javax.media.jai.OperationDescriptor */ public class PatternDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * for the "Pattern" operation. */ private static final String[][] resources = { {"GlobalName", "Pattern"}, {"LocalName", "Pattern"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("PatternDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/PatternDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("PatternDescriptor1")}, {"arg1Desc", JaiI18N.getString("PatternDescriptor2")} }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { java.lang.Integer.class, java.lang.Integer.class, }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "width", "height" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT, NO_PARAMETER_DEFAULT }; /** Constructor. */ public PatternDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } public Number getParamMinValue(int index) { if (index == 0 || index == 1) { return new Integer(1); } else { throw new ArrayIndexOutOfBoundsException(); } } /** * Defines an image with a repeated pattern. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param width The width of the image in pixels. * @param height The height of the image in pixels. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if width is null. * @throws IllegalArgumentException if height is null. */ public static RenderedOp create(RenderedImage source0, Integer width, Integer height, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Pattern", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("width", width); pb.setParameter("height", height); return JAI.create("Pattern", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/BorderDescriptor.java0000644000175000017500000003354410203035544027320 0ustar mathieumathieu/* * $RCSfile: BorderDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:30 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.geom.Area; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.BorderExtender; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.OperationNode; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Border" operation. * *

      The Border operation adds a border around a rendered image. The * size of the border is specified in pixels by the left, right, top, * and bottom padding parameters, corresponding to the four sides of * the source image. These paddings may not be less than 0. * *

      The pixel values of the added border area will be set according to * the algorithm of the BorderExtender passed as a parameter. * The BorderExtenders provide the ability to extend the * border by: *

        *
      • filling it with zeros (BorderExtenderZero); *
      • filling it with constants (BorderExtenderConstant); *
      • copying the edge and corner pixels (BorderExtenderCopy); *
      • reflecting about the edges of the image * (BorderExtenderReflect); or, *
      • "wrapping" the image plane toroidally, that is, joining opposite * edges of the image (BorderExtenderWrap). *
      * *

      * * * * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName Border
      LocalName Border
      Vendor com.sun.media.jai
      Description Adds a border around an image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/BorderDescriptor.html
      Version 1.0
      arg0Desc The image's left padding.
      arg1Desc The image's right padding.
      arg2Desc The image's top padding.
      arg3Desc The image's bottom padding.
      arg4Desc The border extender.

      * *

      * * * * * * * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      leftPad java.lang.Integer0
      rightPad java.lang.Integer0
      topPad java.lang.Integer0
      bottomPad java.lang.Integer0
      type javax.media.jai.BorderExtenderjavax.media.jai.BorderExtenderZero

      * * @see javax.media.jai.OperationDescriptor */ public class BorderDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Border"}, {"LocalName", "Border"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("BorderDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/BorderDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion2")}, {"arg0Desc", JaiI18N.getString("BorderDescriptor1")}, {"arg1Desc", JaiI18N.getString("BorderDescriptor2")}, {"arg2Desc", JaiI18N.getString("BorderDescriptor3")}, {"arg3Desc", JaiI18N.getString("BorderDescriptor4")}, {"arg4Desc", JaiI18N.getString("BorderDescriptor5")}, }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "leftPad", "rightPad", "topPad", "bottomPad", "type" }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { java.lang.Integer.class, java.lang.Integer.class, java.lang.Integer.class, java.lang.Integer.class, javax.media.jai.BorderExtender.class }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { new Integer(0), new Integer(0), new Integer(0), new Integer(0), BorderExtender.createInstance(BorderExtender.BORDER_ZERO) }; /** Constructor. */ public BorderDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** * Calculates the region over which two distinct renderings * of the "Border" operation may be expected to differ. * *

      The operation returns a Shape or null * in the rendered mode and null in all other modes. * * @param modeName The name of the mode. * @param oldParamBlock The previous sources and parameters. * @param oldHints The previous hints. * @param newParamBlock The current sources and parameters. * @param newHints The current hints. * @param node The affected node in the processing chain (ignored). * * @return The region over which the data of two renderings of this * operation may be expected to be invalid or null * if there is no common region of validity. * A non-null empty region indicates that the * operation would produce identical data over the bounds of the * old rendering although perhaps not over the area occupied by * the tiles of the old rendering. * * @throws IllegalArgumentException if modeName * is null or if the operation requires either * sources or parameters and either oldParamBlock * or newParamBlock is null. * @throws IllegalArgumentException if oldParamBlock or * newParamBlock do not contain sufficient sources * or parameters for the operation in question. */ public Object getInvalidRegion(String modeName, ParameterBlock oldParamBlock, RenderingHints oldHints, ParameterBlock newParamBlock, RenderingHints newHints, OperationNode node) { if ((modeName == null) || ((getNumSources() > 0 || getNumParameters() > 0) && (oldParamBlock == null || newParamBlock == null))) { throw new IllegalArgumentException(JaiI18N.getString("BorderDescriptor6")); } int numSources = getNumSources(); if ((numSources > 0) && (oldParamBlock.getNumSources() != numSources || newParamBlock.getNumSources() != numSources)) { throw new IllegalArgumentException(JaiI18N.getString("BorderDescriptor7")); } int numParams = getParameterListDescriptor(modeName).getNumParameters(); if ((numParams > 0) && (oldParamBlock.getNumParameters() != numParams || newParamBlock.getNumParameters() != numParams)) { throw new IllegalArgumentException(JaiI18N.getString("BorderDescriptor8")); } // Return null if the RenderingHints, source, left padding, or // top padding changed. if(!modeName.equalsIgnoreCase(RenderedRegistryMode.MODE_NAME) || (oldHints == null && newHints != null) || (oldHints != null && newHints == null) || (oldHints != null && !oldHints.equals(newHints)) || !oldParamBlock.getSource(0).equals(newParamBlock.getSource(0)) || oldParamBlock.getIntParameter(0) != // left pad newParamBlock.getIntParameter(0) || oldParamBlock.getIntParameter(2) != // top pad newParamBlock.getIntParameter(2)) { return null; } Shape invalidRegion = null; if(!oldParamBlock.getObjectParameter(4).equals( newParamBlock.getObjectParameter(4))) { // BorderExtender changed. // Get source and the left and top padding. RenderedImage src = oldParamBlock.getRenderedSource(0); int leftPad = oldParamBlock.getIntParameter(0); int topPad = oldParamBlock.getIntParameter(2); // Get source bounds. Rectangle srcBounds = new Rectangle(src.getMinX(), src.getMinY(), src.getWidth(), src.getHeight()); // Get destination bounds. Rectangle dstBounds = new Rectangle(srcBounds.x - leftPad, srcBounds.y - topPad, srcBounds.width + leftPad + oldParamBlock.getIntParameter(1), srcBounds.height + topPad + oldParamBlock.getIntParameter(3)); // Determine invalid area by subtracting source bounds. Area invalidArea = new Area(dstBounds); invalidArea.subtract(new Area(srcBounds)); invalidRegion = invalidArea; } else if((newParamBlock.getIntParameter(1) < // new R < old R oldParamBlock.getIntParameter(1) && newParamBlock.getIntParameter(3) <= // new B <= old B oldParamBlock.getIntParameter(3)) || (newParamBlock.getIntParameter(3) < // new B < old B oldParamBlock.getIntParameter(3) && newParamBlock.getIntParameter(1) <= // new R <= old R oldParamBlock.getIntParameter(1))) { // One or both right and bottom padding decreased. // Get source and the left and top padding. RenderedImage src = oldParamBlock.getRenderedSource(0); int leftPad = oldParamBlock.getIntParameter(0); int topPad = oldParamBlock.getIntParameter(2); // Get source bounds. Rectangle srcBounds = new Rectangle(src.getMinX(), src.getMinY(), src.getWidth(), src.getHeight()); // Get old destination bounds. Rectangle oldBounds = new Rectangle(srcBounds.x - leftPad, srcBounds.y - topPad, srcBounds.width + leftPad + oldParamBlock.getIntParameter(1), srcBounds.height + topPad + oldParamBlock.getIntParameter(3)); // Get new destination bounds. Rectangle newBounds = new Rectangle(srcBounds.x - leftPad, srcBounds.y - topPad, srcBounds.width + leftPad + newParamBlock.getIntParameter(1), srcBounds.height + topPad + newParamBlock.getIntParameter(3)); // Determine invalid area by subtracting new from old bounds. Area invalidArea = new Area(oldBounds); invalidArea.subtract(new Area(newBounds)); invalidRegion = invalidArea; } else { // Either nothing changed or one or both of the right and bottom // padding was increased. invalidRegion = new Rectangle(); } return invalidRegion; } /** * Adds a border around an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param leftPad The image's left padding. * May be null. * @param rightPad The image's right padding. * May be null. * @param topPad The image's top padding. * May be null. * @param bottomPad The image's bottom padding. * May be null. * @param type The border type. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, Integer leftPad, Integer rightPad, Integer topPad, Integer bottomPad, BorderExtender type, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Border", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("leftPad", leftPad); pb.setParameter("rightPad", rightPad); pb.setParameter("topPad", topPad); pb.setParameter("bottomPad", bottomPad); pb.setParameter("type", type); return JAI.create("Border", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/AbsoluteDescriptor.java0000644000175000017500000001174510203035544027660 0ustar mathieumathieu/* * $RCSfile: AbsoluteDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:28 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Absolute" operation. * *

      The "Absolute" operation takes a single rendered or renderable * source image, and computes the mathematical absolute value of each pixel: * *

       * if (src[x][y][b] < 0) {
       *     dst[x][y][b] = -src[x][y][b];
       * } else {
       *     dst[x][y][b] =  src[x][y][b];
       * }
       * 
      * *

      For signed integral data types, the smallest value of the data * type does not have a positive counterpart; such values will be left * unchanged. This behavior parallels that of the Java unary minus * operator (see The Java Language Specification, section * 15.14.4). * *

      * * * * * * * * *
      Resource List
      Name Value
      GlobalName Absolute
      LocalName Absolute
      Vendor com.sun.media.jai
      Description Replaces the pixel values of an image by their absolute values.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/AbsoluteDescriptor.html
      Version 1.0

      * *

      No parameters are needed for the "Absolute" operation. * * @see javax.media.jai.OperationDescriptor */ public class AbsoluteDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Absolute"}, {"LocalName", "Absolute"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("AbsoluteDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/AbsoluteDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")} }; /** Constructor. */ public AbsoluteDescriptor() { super(resources, 1, null, null, null); } /** Returns true since renderable operation is supported. */ public boolean isRenderableSupported() { return true; } /** * Replaces the pixel values of an image by their absolute values. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Absolute", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); return JAI.create("Absolute", pb, hints); } /** * Replaces the pixel values of an image by their absolute values. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Absolute", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); return JAI.createRenderable("Absolute", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/RescaleDescriptor.java0000644000175000017500000002073310203035544027455 0ustar mathieumathieu/* * $RCSfile: RescaleDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:43 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Rescale" operation. * *

      The "Rescale" operation takes a rendered or renderable source * image and maps the pixel values of an image from one range to * another range by multiplying each pixel value by one of a set of * constants and then adding another constant to the result of the * multiplication. If the number of constants supplied is less than * the number of bands of the destination, then the constant from * entry 0 is applied to all the bands. Otherwise, a constant from a * different entry is applied to each band. There must be at least one * entry in each of the contants and offsets arrays. * *

      The destination pixel values are defined by the pseudocode: *

       * constant = (constants.length < dstNumBands) ?
       *            constants[0] : constants[b];
       * offset = (offsets.length < dstNumBands) ?
       *          offsets[0] : offsets[b];
       *
       * dst[x][y][b] = src[x][y][b]*constant + offset;
       * 
      * *

      The pixel arithmetic is performed using the data type of the * destination image. By default, the destination will have the same * data type as the source image unless an ImageLayout * containing a SampleModel with a different data type * is supplied as a rendering hint. * *

      * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName Rescale
      LocalName Rescale
      Vendor com.sun.media.jai
      Description Maps the pixels values of an image from * one range to another range.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/RescaleDescriptor.html
      Version 1.0
      arg0Desc The per-band constants to multiply by.
      arg1Desc The per-band offsets to be added.

      * *

      * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      constants double[]{1.0}
      offsets double[]{0.0}

      * * @see javax.media.jai.OperationDescriptor */ public class RescaleDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Rescale"}, {"LocalName", "Rescale"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("RescaleDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/RescaleDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("RescaleDescriptor1")}, {"arg1Desc", JaiI18N.getString("RescaleDescriptor2")} }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { double[].class, double[].class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "constants", "offsets" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { new double[] {1.0}, new double[] {0.0} }; /** Constructor. */ public RescaleDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** Returns true since renderable operation is supported. */ public boolean isRenderableSupported() { return true; } /** * Validates the input parameters. * *

      In addition to the standard checks performed by the * superclass method, this method checks that the length of the * "constants" and "offsets" arrays are each at least 1. */ protected boolean validateParameters(ParameterBlock args, StringBuffer msg) { if (!super.validateParameters(args, msg)) { return false; } int constantsLength = ((double[])args.getObjectParameter(0)).length; int offsetsLength = ((double[])args.getObjectParameter(1)).length; if (constantsLength < 1) { msg.append(getName() + " " + JaiI18N.getString("RescaleDescriptor3")); return false; } if (offsetsLength < 1) { msg.append(getName() + ": " + JaiI18N.getString("RescaleDescriptor4")); return false; } return true; } /** * Maps the pixels values of an image from one range to another range. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param constants The per-band constants to multiply by. * May be null. * @param offsets The per-band offsets to be added. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, double[] constants, double[] offsets, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Rescale", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("constants", constants); pb.setParameter("offsets", offsets); return JAI.create("Rescale", pb, hints); } /** * Maps the pixels values of an image from one range to another range. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param constants The per-band constants to multiply by. * May be null. * @param offsets The per-band offsets to be added. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, double[] constants, double[] offsets, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Rescale", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("constants", constants); pb.setParameter("offsets", offsets); return JAI.createRenderable("Rescale", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/IDFTDescriptor.java0000644000175000017500000003202310203035544026620 0ustar mathieumathieu/* * $RCSfile: IDFTDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:37 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.util.PropertyGeneratorImpl; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.EnumeratedParameter; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PropertyGenerator; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.operator.DFTDataNature; import javax.media.jai.operator.DFTScalingType; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * This property generator computes the properties for the operation * "IDFT" dynamically. */ class IDFTPropertyGenerator extends PropertyGeneratorImpl { /** Constructor. */ public IDFTPropertyGenerator() { super(new String[] {"COMPLEX"}, new Class[] {Boolean.class}, new Class[] {RenderedOp.class, RenderableOp.class}); } /** * Returns the specified property. * * @param name Property name. * @param opNode Operation node. */ public Object getProperty(String name, Object opNode) { validate(name, opNode); if (name.equalsIgnoreCase("complex")) { if(opNode instanceof RenderedOp) { RenderedOp op = (RenderedOp)opNode; ParameterBlock pb = op.getParameterBlock(); DFTDataNature dataNature = (DFTDataNature)pb.getObjectParameter(1); return dataNature.equals(IDFTDescriptor.COMPLEX_TO_REAL) ? Boolean.FALSE : Boolean.TRUE; } else if(opNode instanceof RenderableOp) { RenderableOp op = (RenderableOp)opNode; ParameterBlock pb = op.getParameterBlock(); DFTDataNature dataNature = (DFTDataNature)pb.getObjectParameter(1); return dataNature.equals(IDFTDescriptor.COMPLEX_TO_REAL) ? Boolean.FALSE : Boolean.TRUE; } } return java.awt.Image.UndefinedProperty; } } /** * An OperationDescriptor describing the "IDFT" operation. * *

      The "IDFT" operation computes the inverse discrete Fourier transform of * an image. A positive exponential is used as the basis function for the * transform. The operation supports real-to-complex, complex-to-complex, and * complex-to-real transforms. A complex image must have an even number of * bands, with the even bands (0, 2, ...) representing the real parts and the * odd bands (1, 3, ...) the imaginary parts of each complex pixel. * *

      The nature of the source and destination data is specified by the * "dataNature" operation parameter. If the source data are complex then * the number of bands in the source image must be a multiple of 2. The * number of bands in the destination must match that which would be expected * given the number of bands in the source image and the specified nature * of the source and destination data. If the source image is real then the * number of bands in the destination will be twice that in the source. If * the destination image is real than the number of bands in the destination * will be half that in the source. Otherwise the number of bands in the * source and destination must be equal. * *

      If an underlying fast Fourier transform (FFT) implementation is used * which requires that the image dimensions be powers of 2, then the width * and height may each be increased to the power of 2 greater than or equal * to the original width and height, respectively. * *

      "IDFT" defines a PropertyGenerator that sets the "COMPLEX" property of * the image to java.lang.Boolean.FALSE if the "dataNature" * operation parameter is equal to IDFTDescriptor.COMPLEX_TO_REAL and to * java.lang.Boolean.TRUE otherwise. The value of this property * may be retrieved by calling the getProperty() method with * "COMPLEX" as the property name. * *

      * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName IDFT
      LocalName IDFT
      Vendor com.sun.media.jai
      Description Computes the discrete Fourier transform of * an image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/IDFTDescriptor.html
      Version 1.0
      arg0Desc The type of scaling to be used.
      arg1Desc The nature of the data.

      * *

      * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      scalingType javax.media.jai.operator.DFTScalingTypeIDFTDescriptor.SCALING_DIMENSIONS
      dataNature javax.media.jai.operator.DFTDataNatureIDFTDescriptor.COMPLEX_TO_REAL

      * * @see javax.media.jai.OperationDescriptor * @see DFTDescriptor * @see DFTDataNature * @see DFTScalingType */ public class IDFTDescriptor extends OperationDescriptorImpl { /** * A flag indicating that the transform is not to be scaled. */ public static final DFTScalingType SCALING_NONE = DFTDescriptor.SCALING_NONE; /** * A flag indicating that the transform is to be scaled by the square * root of the product of its dimensions. */ public static final DFTScalingType SCALING_UNITARY = DFTDescriptor.SCALING_UNITARY; /** * A flag indicating that the transform is to be scaled by the product * of its dimensions. */ public static final DFTScalingType SCALING_DIMENSIONS = DFTDescriptor.SCALING_DIMENSIONS; /** * A flag indicating that the source data are real and the destination * data complex. */ public static final DFTDataNature REAL_TO_COMPLEX = DFTDescriptor.REAL_TO_COMPLEX; /** * A flag indicating that the source and destination data are both complex. */ public static final DFTDataNature COMPLEX_TO_COMPLEX = DFTDescriptor.COMPLEX_TO_COMPLEX; /** * A flag indicating that the source data are complex and the destination * data real. */ public static final DFTDataNature COMPLEX_TO_REAL = DFTDescriptor.COMPLEX_TO_REAL; /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "IDFT"}, {"LocalName", "IDFT"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("IDFTDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/IDFTDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion2")}, {"arg0Desc", JaiI18N.getString("IDFTDescriptor1")}, {"arg1Desc", JaiI18N.getString("IDFTDescriptor2")} }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { DFTScalingType.class, DFTDataNature.class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "scalingType", "dataNature" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { IDFTDescriptor.SCALING_DIMENSIONS, IDFTDescriptor.COMPLEX_TO_REAL }; private static final String[] supportedModes = { "rendered", "renderable" }; /** Constructor. */ public IDFTDescriptor() { super(resources, supportedModes, 1, paramNames, paramClasses, paramDefaults, null); } /** * Validates the input source and parameters. * *

      In addition to the standard checks performed by the * superclass method, this method checks that "scalingType" is one * of SCALING_NONE, SCALING_UNITARY, or * SCALING_DIMENSIONS, and that "dataNature" is one * of REAL_TO_COMPLEX, * COMPLEX_TO_COMPLEX, or * COMPLEX_TO_REAL. Also, if "dataNature" is * COMPLEX_TO_COMPLEX or COMPLEX_TO_REAL * the number of source bands must be even. */ public boolean validateArguments(String modeName, ParameterBlock args, StringBuffer msg) { if (!super.validateArguments(modeName, args, msg)) { return false; } if (!modeName.equalsIgnoreCase("rendered")) return true; // Check source band count: must be even for a complex source. EnumeratedParameter dataNature = (EnumeratedParameter)args.getObjectParameter(1); if (!dataNature.equals(IDFTDescriptor.REAL_TO_COMPLEX)) { RenderedImage src = args.getRenderedSource(0); if (src.getSampleModel().getNumBands() % 2 != 0) { msg.append(getName() + " " + JaiI18N.getString("IDFTDescriptor5")); return false; } } return true; } /** * Returns an array of PropertyGenerators implementing * property inheritance for the "IDFT" operation. * * @return An array of property generators. */ public PropertyGenerator[] getPropertyGenerators(String modeName) { PropertyGenerator[] pg = new PropertyGenerator[1]; pg[0] = new IDFTPropertyGenerator(); return pg; } /** * Computes the inverse discrete Fourier transform of an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param scalingType The type of scaling to perform. * May be null. * @param dataNature The nature of the data. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, DFTScalingType scalingType, DFTDataNature dataNature, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("IDFT", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("scalingType", scalingType); pb.setParameter("dataNature", dataNature); return JAI.create("IDFT", pb, hints); } /** * Computes the inverse discrete Fourier transform of an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param scalingType The type of scaling to perform. * May be null. * @param dataNature The nature of the data. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, DFTScalingType scalingType, DFTDataNature dataNature, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("IDFT", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("scalingType", scalingType); pb.setParameter("dataNature", dataNature); return JAI.createRenderable("IDFT", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/BandMergeDescriptor.java0000644000175000017500000001334510203035544027724 0ustar mathieumathieu/* * $RCSfile: BandMergeDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:30 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "BandMerge" operation. * *

      The BandMerge operation takes two or more rendered or renderable images, * and * performs "BandMerge" on every pixel. Each source image should be non-null. * The number of bands of the destination image is the sum of those of * all source images. * *

      Intuitively, "BandMerge" stacks all the bands of source images * in the order given in a ParameterBlock. If image1 has three bands * and image2 has one band, the bandmerged image has 4 bands. * Three grayscale images can be used to merge into an RGB image in * this way. * * The destination image bound is the intersection of the source image * bounds. If the sources don't intersect, the destination will * have a width and height of 0. * *

      * * * * * * * * *
      Resource List
      Name Value
      GlobalName BandMerge
      LocalName BandMerge
      Vendor com.sun.media.jai
      Description "bandmerges" rendered images.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/BandMergeDescriptor.html
      Version 1.0

      * *

      No parameters are needed for this operation. * * @see javax.media.jai.OperationDescriptor * * @since JAI 1.1 */ public class BandMergeDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "BandMerge"}, {"LocalName", "BandMerge"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("BandMergeDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/BandMergeDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")} }; /** Constructor. */ // XXX will make this arbitrary number of bands public BandMergeDescriptor() { super(resources, 2, null, null, null); } /** Returns true since renderable operation is supported. */ public boolean isRenderableSupported() { return true; } /** * Merge (possibly multi-banded)images into a multibanded image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param source1 RenderedImage source 1. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if source1 is null. */ public static RenderedOp create(RenderedImage source0, RenderedImage source1, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("BandMerge", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setSource("source1", source1); return JAI.create("BandMerge", pb, hints); } /** * Merge (possibly multi-banded)images into a multibanded image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param source1 RenderableImage source 1. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if source1 is null. */ public static RenderableOp createRenderable(RenderableImage source0, RenderableImage source1, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("BandMerge", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setSource("source1", source1); return JAI.createRenderable("BandMerge", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/MagnitudeSquaredDescriptor.java0000644000175000017500000001601210203035544031334 0ustar mathieumathieu/* * $RCSfile: MagnitudeSquaredDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:38 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PropertyGenerator; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the * "MagnitudeSquared" operation. * *

      The "MagnitudeSquared" operation computes the squared magnitude or * of each pixel of a complex image. The source image must have an even * number of bands, with the even bands (0, 2, ...) representing the real * parts and the odd bands (1, 3, ...) the imaginary parts of each complex * pixel. The destination image has at most half the number of bands of the * source image with each sample in a pixel representing the magnitude of the * corresponding complex source sample. The magnitude squared values of the * destination image are defined for a given sample by the pseudocode: * *

      dstPixel[x][y][b] = src[x][y][2*b]^2 + src[x][y][2*b + 1]^2
      * * where the number of bands b varies from zero to one less than the * number of bands in the destination image. * *

      For integral image datatypes, the result will be rounded and clamped * as needed. * *

      "MagnitudeSquared" defines a PropertyGenerator that sets the "COMPLEX" * property of the image to java.lang.Boolean.FALSE, which may * be retrieved by calling the getProperty() method with * "COMPLEX" as the property name. * *

      * * * * * * * * *
      Resource List
      Name Value
      GlobalName MagnitudeSquared
      LocalName MagnitudeSquared
      Vendor com.sun.media.jai
      Description Computes the squared magnitude of each * pixel of a complex image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/MagnitudeSquaredDescriptor.html
      Version 1.0

      * *

      No parameters are needed for the "MagnitudeSquared" operation. * * @see javax.media.jai.OperationDescriptor */ public class MagnitudeSquaredDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "MagnitudeSquared"}, {"LocalName", "MagnitudeSquared"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("MagnitudeSquaredDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/MagnitudeSquaredDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")} }; private static final String[] supportedModes = { "rendered", "renderable" }; /** Constructor. */ public MagnitudeSquaredDescriptor() { super(resources, supportedModes, 1, null, null, null, null); } /** * Returns an array of PropertyGenerators implementing * property inheritance for the "MagnitudeSquared" operation. * * @return An array of property generators. */ public PropertyGenerator[] getPropertyGenerators(String modeName) { PropertyGenerator[] pg = new PropertyGenerator[1]; pg[0] = new ComplexPropertyGenerator(); return pg; } /** * Validates the input source. * *

      In addition to the standard checks performed by the * superclass method, this method checks that the source image * has an even number of bands. */ protected boolean validateSources(String modeName, ParameterBlock args, StringBuffer msg) { if (!super.validateSources(modeName, args, msg)) { return false; } if (!modeName.equalsIgnoreCase("rendered")) return true; RenderedImage src = args.getRenderedSource(0); int bands = src.getSampleModel().getNumBands(); if (bands % 2 != 0) { msg.append(getName() + " " + JaiI18N.getString("MagnitudeSquaredDescriptor1")); return false; } return true; } /** * Computes the squared magnitude of each pixel of a complex image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("MagnitudeSquared", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); return JAI.create("MagnitudeSquared", pb, hints); } /** * Computes the squared magnitude of each pixel of a complex image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("MagnitudeSquared", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); return JAI.createRenderable("MagnitudeSquared", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/ScaleDescriptor.java0000644000175000017500000004351110203035544027125 0ustar mathieumathieu/* * $RCSfile: ScaleDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:43 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.util.PropertyGeneratorImpl; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.GeometricOpImage; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationNearest; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PlanarImage; import javax.media.jai.PropertyGenerator; import javax.media.jai.ROI; import javax.media.jai.ROIShape; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * This property generator computes the properties for the operation * "Scale" dynamically. */ class ScalePropertyGenerator extends PropertyGeneratorImpl { /** Constructor. */ public ScalePropertyGenerator() { super(new String[] {"ROI"}, new Class[] {ROI.class}, new Class[] {RenderedOp.class}); } /** * Returns the specified property. * * @param name Property name. * @param opNode Operation node. */ public Object getProperty(String name, Object opNode) { validate(name, opNode); if(opNode instanceof RenderedOp && name.equalsIgnoreCase("roi")) { RenderedOp op = (RenderedOp)opNode; ParameterBlock pb = op.getParameterBlock(); // Retrieve the rendered source image and its ROI. RenderedImage src = pb.getRenderedSource(0); Object property = src.getProperty("ROI"); if (property == null || property.equals(java.awt.Image.UndefinedProperty) || !(property instanceof ROI)) { return java.awt.Image.UndefinedProperty; } ROI srcROI = (ROI)property; // Retrieve the Interpolation object. Interpolation interp = (Interpolation)pb.getObjectParameter(4); // Determine the effective source bounds. Rectangle srcBounds = null; PlanarImage dst = op.getRendering(); if (dst instanceof GeometricOpImage && ((GeometricOpImage)dst).getBorderExtender() == null) { srcBounds = new Rectangle(src.getMinX() + interp.getLeftPadding(), src.getMinY() + interp.getTopPadding(), src.getWidth() - interp.getWidth() + 1, src.getHeight() - interp.getHeight() + 1); } else { srcBounds = new Rectangle(src.getMinX(), src.getMinY(), src.getWidth(), src.getHeight()); } // If necessary, clip the ROI to the effective source bounds. if(!srcBounds.contains(srcROI.getBounds())) { srcROI = srcROI.intersect(new ROIShape(srcBounds)); } // Retrieve the scale factors and translation values. float sx = pb.getFloatParameter(0); float sy = pb.getFloatParameter(1); float tx = pb.getFloatParameter(2); float ty = pb.getFloatParameter(3); // Create an equivalent transform. AffineTransform transform = new AffineTransform(sx, 0.0, 0.0, sy, tx, ty); // Create the scaled ROI. ROI dstROI = srcROI.transform(transform); // Retrieve the destination bounds. Rectangle dstBounds = op.getBounds(); // If necessary, clip the warped ROI to the destination bounds. if(!dstBounds.contains(dstROI.getBounds())) { dstROI = dstROI.intersect(new ROIShape(dstBounds)); } // Return the warped and possibly clipped ROI. return dstROI; } return java.awt.Image.UndefinedProperty; } } /** * An OperationDescriptor describing the "Scale" operation. * *

      The "Scale" operation translates and resizes an image. For each * pixel (x, y) of the destination, the source value at the fractional * subpixel position ((x - xTrans)/xScale, (y - yTrans)/yScale) is * constructed by means of an Interpolation object and written to the * destination. * *

      When applying scale factors of scaleX, scaleY to a source image * with the upper left pixel at (srcMinX, srcMinY) and width of srcWidth * and height of srcHeight, the resulting image is defined to have the * following bounds: * * * dstMinX = ceil(A), where A = srcMinX * scaleX - 0.5 + transX, * dstMinY = ceil(B), where B = srcMinY * scaleY - 0.5 + transY, * dstMaxX = ceil(C), where C = (srcMaxX + 1) * scaleX - 1.5 + transX * and srcMaxX = srcMinX + srcWidth - 1 * dstMaxY = ceil(D), where D = (srcMaxY + 1) * scaleY - 1.5 + transY * and srcMaxY = srcMinY + srcHeight - 1 * dstWidth = dstMaxX - dstMinX + 1 * dstHeight = dstMaxY - dstMinY + 1 * * *

      In the case where source's upper left pixel is located is (0, 0), * the formulae simplify to * * * dstMinX = 0 * dstMinY = 0 * dstWidth = ceil (srcWidth * scaleX - 0.5 + transX) * dstHeight = ceil (srcHeight * scaleY - 0.5 + transY) * * *

      In the case where the source's upper left pixel is located at (0, 0) * and the scaling factors are integers, the formulae further simplify to * * * dstMinX = 0 * dstMinY = 0 * dstWidth = ceil (srcWidth * scaleX + transX) * dstWidth = ceil (srcHeight * scaleY + transY) * * *

      When interpolations which require padding the source such as Bilinear * or Bicubic interpolation are specified, the source needs to be extended * such that it has the extra pixels needed to compute all the destination * pixels. This extension is performed via the BorderExtender * class. The type of Border Extension can be specified as a * RenderingHint to the JAI.create method. * *

      If no Border Extension is specified, the source will not be extended. * The scaled image size is still calculated according to the formula * specified above. However since there isn't enough source to compute all the * destination pixels, only that subset of the destination image's pixels, * which can be computed, will be written in the destination. The rest of the * destination will not be written. * *

      Specifying a scale factor of greater than 1 increases the size * of the image, specifying a scale factor between 0 and 1 (non-inclusive) * decreases the size of an image. An IllegalArgumentException will be thrown * if the specified scale factors are negative or equal to zero. * *

      It may be noted that the minX, minY, width and height hints as * specified through the JAI.KEY_IMAGE_LAYOUT hint in the * RenderingHints object are not honored, as this operator * calculates the destination image bounds itself. The other * ImageLayout hints, like tileWidth and tileHeight, * however are honored. * *

      It should be noted that this operation automatically adds a * value of Boolean.TRUE for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL to the given * configuration so that the operation is performed * on the pixel values instead of being performed on the indices into * the color map if the source(s) have an IndexColorModel. * This addition will take place only if a value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL has not already been * provided by the user. Note that the configuration Map * is cloned before the new hint is added to it. The operation can be * smart about the value of the JAI.KEY_REPLACE_INDEX_COLOR_MODEL * RenderingHints, i.e. while the default value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL is * Boolean.TRUE, in some cases the operator could set the * default. * *

      "Scale" defines a PropertyGenerator that performs an identical * transformation on the "ROI" property of the source image, which can * be retrieved by calling the getProperty method with * "ROI" as the property name. * *

      * * * * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName Scale
      LocalName Scale
      Vendor com.sun.media.jai
      Description Resizes an image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ScaleDescriptor.html
      Version 1.0
      arg0Desc The X scale factor.
      arg1Desc The Y scale factor.
      arg2Desc The X translation.
      arg3Desc The Y translation.
      arg4Desc The interpolation method for * resampling.

      * *

      * * * * * * * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      xScale java.lang.Float1.0F
      yScale java.lang.Float1.0F
      xTrans java.lang.Float0.0F
      yTrans java.lang.Float0.0F
      interpolation javax.media.jai.InterpolationInterpolationNearest

      * * @see javax.media.jai.Interpolation * @see javax.media.jai.BorderExtender * @see javax.media.jai.OperationDescriptor */ public class ScaleDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Scale"}, {"LocalName", "Scale"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("ScaleDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ScaleDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("ScaleDescriptor1")}, {"arg1Desc", JaiI18N.getString("ScaleDescriptor2")}, {"arg2Desc", JaiI18N.getString("ScaleDescriptor3")}, {"arg3Desc", JaiI18N.getString("ScaleDescriptor4")}, {"arg4Desc", JaiI18N.getString("ScaleDescriptor5")} }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { java.lang.Float.class, java.lang.Float.class, java.lang.Float.class, java.lang.Float.class, javax.media.jai.Interpolation.class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "xScale", "yScale", "xTrans", "yTrans", "interpolation" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { new Float(1.0F), new Float(1.0F), new Float(0.0F), new Float(0.0F), Interpolation.getInstance(Interpolation.INTERP_NEAREST) }; /** Constructor. */ public ScaleDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** Returns true since renderable operation is supported. */ public boolean isRenderableSupported() { return true; } /** * Returns an array of PropertyGenerators implementing * property inheritance for the "Scale" operation. * * @return An array of property generators. */ public PropertyGenerator[] getPropertyGenerators() { PropertyGenerator[] pg = new PropertyGenerator[1]; pg[0] = new ScalePropertyGenerator(); return pg; } /** * Validates the input parameters. * *

      In addition to the standard checks performed by the * superclass method, this method checks that "xScale" and "yScale" * are both greater than 0. */ protected boolean validateParameters(ParameterBlock args, StringBuffer msg) { if (!super.validateParameters(args, msg)) { return false; } float xScale = args.getFloatParameter(0); float yScale = args.getFloatParameter(1); if (xScale <= 0 || yScale <= 0) { msg.append(getName() + " " + JaiI18N.getString("ScaleDescriptor6")); return false; } return true; } /** * Returns the minimum legal value of a specified numeric parameter * for this operation. * *

      For the minimum value of "xScale" and "yScale", this method * returns 0. However, the scale factors must be a positive floating * number and can not be 0. */ public Number getParamMinValue(int index) { if (index == 0 || index == 1) { return new Float(0.0F); } else if (index == 2 || index == 3) { return new Float(-Float.MAX_VALUE); } else if (index == 4) { return null; } else { throw new ArrayIndexOutOfBoundsException(); } } /** * Resizes an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param xScale The X scale factor. * May be null. * @param yScale The Y scale factor. * May be null. * @param xTrans The X translation. * May be null. * @param yTrans The Y translation. * May be null. * @param interpolation The interpolation method for resampling. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, Float xScale, Float yScale, Float xTrans, Float yTrans, Interpolation interpolation, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Scale", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("xScale", xScale); pb.setParameter("yScale", yScale); pb.setParameter("xTrans", xTrans); pb.setParameter("yTrans", yTrans); pb.setParameter("interpolation", interpolation); return JAI.create("Scale", pb, hints); } /** * Resizes an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param xScale The X scale factor. * May be null. * @param yScale The Y scale factor. * May be null. * @param xTrans The X translation. * May be null. * @param yTrans The Y translation. * May be null. * @param interpolation The interpolation method for resampling. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, Float xScale, Float yScale, Float xTrans, Float yTrans, Interpolation interpolation, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Scale", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("xScale", xScale); pb.setParameter("yScale", yScale); pb.setParameter("xTrans", xTrans); pb.setParameter("yTrans", yTrans); pb.setParameter("interpolation", interpolation); return JAI.createRenderable("Scale", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/MosaicDescriptor.java0000644000175000017500000003507010203035544027312 0ustar mathieumathieu/* * $RCSfile: MosaicDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:40 $ * $State: Exp $ */package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.Transparency; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.ImageLayout; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PlanarImage; import javax.media.jai.ROI; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Mosaic" operation * in the rendered mode. * *

      * The "Mosaic" operation creates a mosaic of two or more source images. * This operation could be used for example to assemble a set of * overlapping geospatially rectified images into a contiguous * image. It could also be used to create a montage of photographs such * as a panorama. *

      * *

      * All source images are assumed to have been geometrically mapped into * a common coordinate space. The origin (minX, minY) of * each image is therefore taken to represent the location of the respective * image in the common coordinate system of the source images. This * coordinate space will also be that of the destination image. *

      * *

      * All source images must have the same data type and sample size for all * bands. The destination will have the same data type, sample size, and * number of bands and color components as the sources. *

      * *

      * The destination layout may be specified by an {@link ImageLayout} hint * provided via a {@link RenderingHints} supplied to the operation. If this * hint contains a setting for the image bounds (origin and dimensions), it * will be used even if it does not intersect the union of the bounds of * the sources; otherwise the image bounds will be set to the union of all * source image bounds. If the data type or sample size specified by the layout * hint do not match those of the sources, then this portion of the hint will be * ignored. *

      * *

      * It is permissible that the number of source images be initially zero. In * this case a non-null ImageLayout must be * supplied with valid width and height and a non-null * SampleModel. The destination data type, sample size, number * of bands, and image bounds will all be determined by the * ImageLayout. *

      * *

      * If sourceAlpha is non-null, then any non- * null elements of the array must be single-band images * with the same data type and sample size as the sources. *

      * *

      * The source threshold array parameter has maximum dimensions as * double[NUM_SOURCES][NUM_BANDS]. Default values of the * thresholds actually used are defined as follows: *

        *
      • The default value of sourceThreshold[0][0] is * 1.0.
      • *
      • If sourceThreshold[i] != null and * sourceThreshold[i].length < NUM_BANDS, then set * sourceThreshold[i][j] = sourceThreshold[i][0] for all * 1 <= j < NUM_BANDS.
      • *
      • If sourceThreshold[i] == null then set * sourceThreshold[i] = sourceThreshold[0].
      • *
      *

      * *

      * The background value array parameter has maximum dimensions as * double[NUM_BANDS]. Default values of the * background actually used are defined as follows: *

        *
      • The default value of backgroundValues[0] is * 0.0.
      • *
      • If backgroundValues.length < NUM_BANDS, then set * backgroundValues[j] = backgroundValues[0] for all * 1 <= j < NUM_BANDS.
      • *
      * The default behavior therefore is to set the background to zero. *

      * *

      * If a given destination position (x, y) is within the bounds * of M source images, then the destination pixel value * D(x, y) is computed using an algorithm selected on the * basis of the mosaicType parameter value. If the destination * position is not within any source image, then the destination pixel value * is set to the specified background value. *

      * *

      * If the mosaicType parameter value is * MOSAIC_TYPE_BLEND, then the destination pixel value * is computed as: *

       * double[][][] s; // source pixel values
       * double[][][] w; // derived source weight values
       * double[][] d;   // destination pixel values
       *
       * double weightSum = 0.0;
       * for(int i = 0; i < M; i++) {
       *     weightSum += w[i][x][y];
       * }
       *
       * if(weightSum != 0.0) {
       *     double sourceSum = 0.0;
       *     for(int i = 0; i < M; i++) {
       *         sourceSum += s[i][x][y]*w[i][x][y];
       *     }
       *     d[x][y] = sourceSum / weightSum;
       * } else {
       *     d[x][y] = background;
       * }
       * 
      * where the index i is over the sources which contain * (x, y). The destination pixel value is therefore a * blend of the source pixel values at the same position. *

      * *

      * If the mosaicType parameter value is * MOSAIC_TYPE_OVERLAY, then the destination pixel value * is computed as: *

       * d[x][y] = background;
       * for(int i = 0; i < M; i++) {
       *     if(w[i][x][y] != 0.0) {
       *         d[x][y] = s[i][x][y];
       *         break;
       *     }
       * }
       * 
      * The destination pixel value is therefore the value of the first source * pixel at the same position for which the derived weight value at the same * position is non-zero. *

      * *

      * The derived weight values for the ith source are determined from * the corresponding sourceAlpha, sourceROI, and * sourceThreshold parameters as follows where for * illustration purposes it is assumed that any alpha values range over * [0.0, 1.0] with 1.0 being opaque: *

       * // Set flag indicating whether to interpret alpha values as bilevel.
       * boolean isAlphaBitmask =
       *     !(mosaicType.equals(MOSAIC_TYPE_BLEND) &&
       *       sourceAlpha != null &&
       *       !(sourceAlpha.length < NUM_SOURCES));
       * if(!isAlphaBitmask) {
       *     for(int i = 0; i < NUM_SOURCES; i++) {
       *         if(sourceAlpha[i] == null) {
       *             isAlphaBitmask = true;
       *             break;
       *         }
       *     }
       * }
       *
       * // Derive source weights from the supplied parameters.
       * w[i][x][y] = 0.0;
       * if(sourceAlpha != null && sourceAlpha[i] != null) {
       *     w[i][x][y] = sourceAlpha[i][x][y];
       *     if(isAlphaBitmask && w[i][x][y] > 0.0) {
       *         w[i][x][y] = 1.0;
       *     }
       * } else if(sourceROI != null && sourceROI[i] != null &&
       *           sourceROI[i].contains(x,y)) {
       *     w[i][x][y] = 1.0;
       * } else if(s[i][x][y] >= sourceThreshold[i]) { // s[i][x][y] = source value
       *     w[i][x][y] = 1.0;
       * }
       * 
      *

      * *

      * As illustrated above, the interpretation of the alpha values will vary * depending on the values of the parameters supplied to the operation. If * and only if mosaicType equals MOSAIC_TYPE_BLEND * and an alpha mask is available for each source will the alpha values be * treated as arbitrary values as for {@link Transparency#TRANSLUCENT}. In * all other cases the alpha values will be treated as bilevel values * as for {@link Transparency#BITMASK}. *

      * *

      * It should be remarked that the MOSAIC_TYPE_BLEND algorithm * applied when the weights are treated as bilevel values is equivalent to * averaging all non-transparent source pixels at a given position. This * in effect intrinsically provides a third category of mosaicking. The * available categories are summarized in the following table. * * * * * * * * * * * * * * *
      Mosaic Categories
      Mosaic TypeTransparency TypeCategory
      MOSAIC_TYPE_BLENDBITMASKAverage
      MOSAIC_TYPE_BLENDTRANSLUCENTAlpha Blend
      MOSAIC_TYPE_OVERLAYBITMASK || TRANSLUCENTSuperposition
      *

      * *

      * * * * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName Mosaic
      LocalName Mosaic
      Vendor com.sun.media.jai
      Description Creates a mosaic of two or more rendered images.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/MosaicDescriptor.html
      Version 1.0
      arg0Desc Mosaicking type.
      arg1Desc Source alpha masks.
      arg2Desc Source region of interest masks.
      arg3Desc Source threshold values.
      arg4Desc Destination background value.

      * *

      * * * * * * * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      mosaicType javax.media.jai.operator.MosaicTypeMOSAIC_TYPE_OVERLAY
      sourceAlpha javax.media.jai.PlanarImage[]null
      sourceROI javax.media.jai.ROI[]null
      sourceThreshold double[][]double[][] {{1.0}}
      backgroundValues double[]double[] {0.0}

      * * @since JAI 1.1.2 */ public class MosaicDescriptor extends OperationDescriptorImpl { /** * Destination pixel equals alpha blend of source pixels. */ public static final MosaicType MOSAIC_TYPE_BLEND = new MosaicType("MOSAIC_TYPE_BLEND", 1); /** * Destination pixel equals first opaque source pixel. */ public static final MosaicType MOSAIC_TYPE_OVERLAY = new MosaicType("MOSAIC_TYPE_OVERLAY", 0); /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Mosaic"}, {"LocalName", "Mosaic"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("MosaicDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/MosaicDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("MosaicDescriptor1")}, {"arg1Desc", JaiI18N.getString("MosaicDescriptor2")}, {"arg2Desc", JaiI18N.getString("MosaicDescriptor3")}, {"arg3Desc", JaiI18N.getString("MosaicDescriptor4")}, {"arg4Desc", JaiI18N.getString("MosaicDescriptor5")} }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { javax.media.jai.operator.MosaicType.class, javax.media.jai.PlanarImage[].class, javax.media.jai.ROI[].class, double[][].class, double[].class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "mosaicType", "sourceAlpha", "sourceROI", "sourceThreshold", "backgroundValues" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { MOSAIC_TYPE_OVERLAY, null, null, new double[][] {{1.0}}, new double[] {0.0} }; /** Constructor. */ public MosaicDescriptor() { super(resources, new String[] {RenderedRegistryMode.MODE_NAME}, 0, paramNames, paramClasses, paramDefaults, null); } /** * Creates a mosaic of two or more rendered images. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param sources RenderedImage sources. * @param mosaicType Mosaicking type. * May be null. * @param sourceAlpha * May be null. * @param sourceAlpha Source alpha masks. * May be null. * @param sourceROI Source region of interest masks. * May be null. * @param sourceThreshold Source threshold values. * May be null. * @param backgroundValues Destination background value. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if any source is null. */ public static RenderedOp create(RenderedImage[] sources, MosaicType mosaicType, PlanarImage[] sourceAlpha, ROI[] sourceROI, double[][] sourceThreshold, double[] backgroundValues, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Mosaic", RenderedRegistryMode.MODE_NAME); int numSources = sources.length; for(int i = 0; i < numSources; i++) { pb.addSource(sources[i]); } pb.setParameter("mosaicType", mosaicType); pb.setParameter("sourceAlpha", sourceAlpha); pb.setParameter("sourceROI", sourceROI); pb.setParameter("sourceThreshold", sourceThreshold); pb.setParameter("backgroundValues", backgroundValues); return JAI.create("Mosaic", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/IIPDescriptor.java0000644000175000017500000007067110203035544026526 0ustar mathieumathieu/* * $RCSfile: IIPDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:37 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.color.ICC_Profile; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import java.net.URL; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "IIP" operation. * *

      This operation provides client-side support of the Internet Imaging * Protocol (IIP) in both the rendered and renderable modes. It creates * a java.awt.image.RenderedImage or a * java.awt.image.renderable.RenderableImage based on the * data received from the IIP server, and optionally applies a sequence * of operations to the created image. * *

      The operations that may be applied and the order in which they are * applied are defined in section 2.2.1.1 of the Internet Imaging Protocol * Specification version 1.0.5. Some or all of the requested operations * may be executed on the IIP server if it is determined that the server * supports such operations. Any of the requested operations not supported * by the server will be executed on the host on which the operation chain * is rendered. * *

      The processing sequence for the supplied operations is as follows: *

        *
      • filtering (blur or sharpen); *
      • tone and color correction ("color twist"); *
      • contrast adjustment; *
      • selection of source rectangle of interest; *
      • spatial orientation (rendering-independent affine transformation); *
      • selection of destination rectangle of interest; *
      • rendering transformation (renderable mode only); *
      • transposition (rotation and/or mirroring). *
      * *

      As indicated, the rendering transformation is performed only in * renderable mode processing. This transformation is derived from the * AffineTransform supplied in the RenderContext * when rendering actually occurs. Rendered mode processing creates * a RenderedImage which is the default rendering of the * RenderableImage created in renderable mode processing. * *

      The "URL" parameter specifies the URL of the IIP image as a * java.lang.String. It must represent a valid URL, and * include any required FIF or SDS commands. It cannot be null. * *

      The "subImages" parameter optionally indicates the sub-images to * be used by the server to get the images at each resolution level. The * values in this int array cannot be negative. If this * parameter is not specified, or if the array is too short (length is 0), * or if a negative value is specified, then this operation will use the * zeroth sub-image of the resolution level actually processed. * *

      The "filter" parameter specifies a blur or sharpen operation: a * positive value indicates sharpen and a negative value blur. A unit * step should produce a perceptible change in the image. The default * value is 0 which signifies that no filtering will occur. * *

      The "colorTwist" parameter represents a 4x4 matrix stored in row-major * order and should have an array length of at least 16. If an array of * length greater than 16 is specified, all elements from index 16 and beyond * are ignored. Elements 12, 13 and 14 must be 0. This matrix will be * applied to the (possibly padded) data in an intermediate normalized * PhotoYCC color space with a premultiplied alpha channel. This operation * will force an alpha channel to be added to the image if the last column * of the last row of the color twist matrix is not 1.0F. Also, if the image * originally has a grayscale color space it will be cast up to RGB if * casting the data back to grayscale after applying the color twist matrix * would result in any loss of data. * *

      The "contrast" parameter specifies a contrast enhancement operation * with increasing contrast for larger value. It must be greater than or equal * to 1.0F. A value of 1.0F indicates no contrast adjustment. * *

      The "sourceROI" parameter specifies the rectangle of interest in the * source image in rendering-independent coordinates. The intersection of * this rectangle with the rendering-independent bounds of the source image * must equal itself. The rendering-independent bounds of the source image * are defined to be (0.0F, 0.0F, r, 1.0F) where r is the aspect ratio * (width/height) of the source image. Note that the source image will not * in fact be cropped to these limits but values outside of this rectangle * will be suppressed. * *

      The "transform" parameter represents an affine backward mapping to be * applied in rendering-independent coordinates. Note that the direction * of transformation is opposite to that of the AffineTransform * supplied in the RenderContext which is a forward mapping. The * default value of this transform is the identity mapping. The supplied * AffineTransform must be invertible. * *

      The "aspectRatio" parameter specifies the rendering-independent width * of the destination image and must be positive. The rendering-independent * bounds of the destination image are (0.0F, 0.0F, aspectRatio, 1.0F). If * this parameter is not provided the destination aspect ratio defaults to * that of the source. * *

      The "destROI" parameter specifies the rectangle of interest in the * destination image in rendering-independent coordinates. This rectangle must * have a non-empty intersection with the rendering-independent bounds of the * destination image but is not constrained to the destination image bounds. * *

      A counterclockwise rotation may be applied to the destination image. * However, the angle is limited to 0, 90, 180, or 270 degrees. By default, * the destination image is not rotated. * *

      The "mirrorAxis" parameter may be null, in which case * no flipping is applied, or a String of "x", "X", "y", or * "Y". * *

      The "ICCProfile" parameter may only be used with client-side processing * or with server-side processing if the connection protocol supports the * ability to transfer a profile. * *

      The "JPEGQuality" and "JPEGTable" parameters are only used with * server-side processing. If provided, JPEGQuality must be in the range * [0,100] and JPEGTable in [1,255]. * *

      There is no source image associated with this operation. * *

      * * * * * * * * * * * * * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName IIP
      LocalName IIP
      Vendor com.sun.media.jai
      Description Provides client support of the Internet * Imaging Protocol in the rendered and * renderable modes.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/IIPDescriptor.html
      Version 1.0
      arg0Desc The URL of the IIP image.
      arg1Desc The sub-images to be used by the server * for images at each resolution level.
      arg2Desc The filtering value.
      arg3Desc The color twist matrix.
      arg4Desc The contrast value.
      arg5Desc The source rectangle of interest in * rendering-independent coordinates.
      arg6Desc The rendering-independent spatial orientation * transform.
      arg7Desc The aspect ratio of the destination * image.
      arg8Desc The destination rectangle of interest in * rendering-independent coordinates.
      arg9Desc The counterclockwise rotation angle to be * applied to the destination.
      arg10Desc The mirror axis.
      arg11Desc The ICC profile used to represent the color * space of the source image.
      arg12Desc The JPEG quality factor.
      arg13Desc The JPEG compression group index * number.

      * *

      * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      URL java.lang.StringNO_PARAMETER_DEFAULT
      subImages int[]{ 0 }
      filter java.lang.Float0.0F
      colorTwist float[]null
      contrast java.lang.Float1.0F
      sourceROI java.awt.geom.Rectangle2D.Floatnull
      transform java.awt.geom.AffineTransformidentity transform
      aspectRatio java.lang.Floatnull
      destROI java.awt.geom.Rectangle2D.Floatnull
      rotation java.lang.Integer0
      mirrorAxis java.lang.Stringnull
      ICCProfile java.awt.color.ICC_Profilenull
      JPEGQuality java.lang.Integernull
      JPEGTable java.lang.Integernull

      * * @see Digital Imaging Group * @see java.awt.image.RenderedImage * @see java.awt.image.renderable.RenderableImage * @see IIPResolutionDescriptor */ public class IIPDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "IIP"}, {"LocalName", "IIP"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("IIPDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/IIPDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("IIPDescriptor1")}, {"arg1Desc", JaiI18N.getString("IIPDescriptor2")}, {"arg2Desc", JaiI18N.getString("IIPDescriptor3")}, {"arg3Desc", JaiI18N.getString("IIPDescriptor4")}, {"arg4Desc", JaiI18N.getString("IIPDescriptor5")}, {"arg5Desc", JaiI18N.getString("IIPDescriptor6")}, {"arg6Desc", JaiI18N.getString("IIPDescriptor7")}, {"arg7Desc", JaiI18N.getString("IIPDescriptor8")}, {"arg8Desc", JaiI18N.getString("IIPDescriptor9")}, {"arg9Desc", JaiI18N.getString("IIPDescriptor10")}, {"arg10Desc", JaiI18N.getString("IIPDescriptor11")}, {"arg11Desc", JaiI18N.getString("IIPDescriptor12")}, {"arg12Desc", JaiI18N.getString("IIPDescriptor13")}, {"arg13Desc", JaiI18N.getString("IIPDescriptor14")} }; /** The parameter class types for this operation. */ private static final Class[] paramClasses = { java.lang.String.class, // arg0 int[].class, // arg1 java.lang.Float.class, // arg2 float[].class, // arg3 java.lang.Float.class, // arg4 java.awt.geom.Rectangle2D.Float.class, // arg5 java.awt.geom.AffineTransform.class, // arg6 java.lang.Float.class, // arg7 java.awt.geom.Rectangle2D.Float.class, // arg8 java.lang.Integer.class, // arg9 java.lang.String.class, // arg10 java.awt.color.ICC_Profile.class, // arg11 java.lang.Integer.class, // arg12 java.lang.Integer.class // arg13 }; /** The parameter names for this operation. */ private static final String[] paramNames = { "URL", "subImages", "filter", "colorTwist", "contrast", "sourceROI", "transform", "aspectRatio", "destROI", "rotation", "mirrorAxis", "ICCProfile", "JPEGQuality", "JPEGTable" }; /** * The parameter default values for this operation. For those parameters * whose default value is null, an appropriate value is * chosen by the individual implementation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT, new int[] { 0 }, new java.lang.Float(0.0F), null, new java.lang.Float(1.0F), null, new AffineTransform(), null, null, new Integer(0), null, null, null, null }; /** Constructor. */ public IIPDescriptor() { super(resources, 0, paramClasses, paramNames, paramDefaults); } /** * Overrides super class's default implementation to return * true because this operation supports renderable mode. */ public boolean isRenderableSupported() { return true; } /** * Returns the minimum legal value of a specified numeric parameter * for this operation. If the supplied index does not * correspond to a numeric parameter, this method returns * null. * * @throws ArrayIndexOutOfBoundsException if index is less * than 0 or greater than 13. */ public Number getParamMinValue(int index) { if (index == 0 || index == 1 || index == 3 || index == 5 || index == 6 || index == 8 || index == 10 || index == 11) { return null; } else if (index == 2) { return new java.lang.Float(-java.lang.Float.MAX_VALUE); } else if(index == 7) { return new java.lang.Float(0.0F); } else if (index == 4) { return new java.lang.Float(1.0F); } else if (index == 12 || index == 9) { return new Integer(0); } else if (index == 13) { return new Integer(1); } else { throw new ArrayIndexOutOfBoundsException(); } } /** * Returns the maximum legal value of a specified numeric parameter * for this operation. If the supplied index does not * correspond to a numeric parameter, this method returns * null. * * @throws ArrayIndexOutOfBoundsException if index is less * than 0 or greater than 13. */ public Number getParamMaxValue(int index) { if (index == 0 || index == 1 || index == 3 || index == 5 || index == 6 || index == 8 || index == 10 || index == 11) { return null; } else if (index == 2 || index == 4 || index == 7) { return new java.lang.Float(java.lang.Float.MAX_VALUE); } else if (index == 9) { return new Integer(270); } else if (index == 12) { return new Integer(100); } else if (index == 13) { return new Integer(255); } else { throw new ArrayIndexOutOfBoundsException(); } } /** * Validates the input parameters. * *

      In addition to the standard checks performed by the * superclass method, this method checks that: *

        *
      • the supplied URL string specifies a valid protocol; *
      • the color twist, if not null, has an array * length of at least 16 (all elements from index 16 and beyond are * ignored and elements 12, 13, and 14 are set to 0); *
      • both the source and dest ROI, if not null, has * a width and height greater than 0; *
      • the mirror axis, if not null, has a * String of "x", "X", "y", or "Y"; *
      • the destination rotation is one of the valid degrees * (0, 90. 180, 270). *
      */ protected boolean validateParameters(ParameterBlock args, StringBuffer msg) { if (!super.validateParameters(args, msg)) { return false; } try { new URL((String)args.getObjectParameter(0)); } catch (Exception e) { msg.append(getName() + " " + JaiI18N.getString("IIPDescriptor15")); return false; } int[] subImages = (int[]) args.getObjectParameter(1); if (subImages.length < 1) { args.set(paramDefaults[1], 1); } float[] colorTwist = (float[])args.getObjectParameter(3); if (colorTwist != null) { if (colorTwist.length < 16) { msg.append(getName() + " " + JaiI18N.getString("IIPDescriptor16")); return false; } /* Make sure elements 12, 13, and 14 are 0. */ colorTwist[12] = 0; colorTwist[13] = 0; colorTwist[14] = 0; args.set(colorTwist, 3); } float contrast = args.getFloatParameter(4); if(contrast < 1.0F) { msg.append(getName() + " " + JaiI18N.getString("IIPDescriptor20")); return false; } java.awt.geom.Rectangle2D.Float sourceROI = (java.awt.geom.Rectangle2D.Float)args.getObjectParameter(5); if (sourceROI != null && (sourceROI.getWidth() < 0.0 || sourceROI.getHeight() < 0.0)) { msg.append(getName() + " " + JaiI18N.getString("IIPDescriptor17")); return false; } AffineTransform tf = (AffineTransform)args.getObjectParameter(6); if(tf.getDeterminant() == 0.0) { msg.append(getName() + " " + JaiI18N.getString("IIPDescriptor24")); return false; } if(args.getObjectParameter(7) != null) { float aspectRatio = args.getFloatParameter(7); if(aspectRatio < 0.0F) { msg.append(getName() + " " + JaiI18N.getString("IIPDescriptor21")); return false; } } java.awt.geom.Rectangle2D.Float destROI = (java.awt.geom.Rectangle2D.Float)args.getObjectParameter(8); if (destROI != null && (destROI.getWidth() < 0.0 || destROI.getHeight() < 0.0)) { msg.append(getName() + " " + JaiI18N.getString("IIPDescriptor17")); return false; } int rotation = args.getIntParameter(9); if (rotation != 0 && rotation != 90 && rotation != 180 && rotation != 270) { msg.append(getName() + " " + JaiI18N.getString("IIPDescriptor18")); return false; } String mirrorAxis = (String)args.getObjectParameter(10); if (mirrorAxis != null && !mirrorAxis.equalsIgnoreCase("x") && !mirrorAxis.equalsIgnoreCase("y")) { msg.append(getName() + " " + JaiI18N.getString("IIPDescriptor19")); return false; } if(args.getObjectParameter(12) != null) { int JPEGQuality = args.getIntParameter(12); if(JPEGQuality < 0 || JPEGQuality > 100) { msg.append(getName() + " " + JaiI18N.getString("IIPDescriptor22")); return false; } } if(args.getObjectParameter(13) != null) { int JPEGIndex = args.getIntParameter(13); if(JPEGIndex < 1 || JPEGIndex > 255) { msg.append(getName() + " " + JaiI18N.getString("IIPDescriptor23")); return false; } } return true; } /** * Provides client support of the Internet Imaging Protocol in the rendered and renderable mode. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param URL The URL of the IIP image. * @param subImages The sub-images to be used by the server for images at each resolution level. * May be null. * @param filter The filtering value. * May be null. * @param colorTwist The color twist matrix. * May be null. * @param contrast The contrast value. * May be null. * @param sourceROI The source rectangle of interest in rendering-independent coordinates. * May be null. * @param transform The rendering-independent spatial orientation transform. * May be null. * @param aspectRatio The aspect ratio of the destination image. * May be null. * @param destROI The destination rectangle of interest in rendering-independent coordinates. * May be null. * @param rotation The counterclockwise rotation angle to be applied to the destination. * May be null. * @param mirrorAxis The mirror axis. * May be null. * @param ICCProfile The ICC profile used to represent the color space of the source image. * May be null. * @param JPEGQuality The JPEG quality factor. * May be null. * @param JPEGTable The JPEG compression group index number. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if URL is null. */ public static RenderedOp create(String URL, int[] subImages, Float filter, float[] colorTwist, Float contrast, Rectangle2D.Float sourceROI, AffineTransform transform, Float aspectRatio, Rectangle2D.Float destROI, Integer rotation, String mirrorAxis, ICC_Profile ICCProfile, Integer JPEGQuality, Integer JPEGTable, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("IIP", RenderedRegistryMode.MODE_NAME); pb.setParameter("URL", URL); pb.setParameter("subImages", subImages); pb.setParameter("filter", filter); pb.setParameter("colorTwist", colorTwist); pb.setParameter("contrast", contrast); pb.setParameter("sourceROI", sourceROI); pb.setParameter("transform", transform); pb.setParameter("aspectRatio", aspectRatio); pb.setParameter("destROI", destROI); pb.setParameter("rotation", rotation); pb.setParameter("mirrorAxis", mirrorAxis); pb.setParameter("ICCProfile", ICCProfile); pb.setParameter("JPEGQuality", JPEGQuality); pb.setParameter("JPEGTable", JPEGTable); return JAI.create("IIP", pb, hints); } /** * Provides client support of the Internet Imaging Protocol in the rendered and renderable mode. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param URL The URL of the IIP image. * @param subImages The sub-images to be used by the server for images at each resolution level. * May be null. * @param filter The filtering value. * May be null. * @param colorTwist The color twist matrix. * May be null. * @param contrast The contrast value. * May be null. * @param sourceROI The source rectangle of interest in rendering-independent coordinates. * May be null. * @param transform The rendering-independent spatial orientation transform. * May be null. * @param aspectRatio The aspect ratio of the destination image. * May be null. * @param destROI The destination rectangle of interest in rendering-independent coordinates. * May be null. * @param rotation The counterclockwise rotation angle to be applied to the destination. * May be null. * @param mirrorAxis The mirror axis. * May be null. * @param ICCProfile The ICC profile used to represent the color space of the source image. * May be null. * @param JPEGQuality The JPEG quality factor. * May be null. * @param JPEGTable The JPEG compression group index number. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if URL is null. */ public static RenderableOp createRenderable(String URL, int[] subImages, Float filter, float[] colorTwist, Float contrast, Rectangle2D.Float sourceROI, AffineTransform transform, Float aspectRatio, Rectangle2D.Float destROI, Integer rotation, String mirrorAxis, ICC_Profile ICCProfile, Integer JPEGQuality, Integer JPEGTable, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("IIP", RenderableRegistryMode.MODE_NAME); pb.setParameter("URL", URL); pb.setParameter("subImages", subImages); pb.setParameter("filter", filter); pb.setParameter("colorTwist", colorTwist); pb.setParameter("contrast", contrast); pb.setParameter("sourceROI", sourceROI); pb.setParameter("transform", transform); pb.setParameter("aspectRatio", aspectRatio); pb.setParameter("destROI", destROI); pb.setParameter("rotation", rotation); pb.setParameter("mirrorAxis", mirrorAxis); pb.setParameter("ICCProfile", ICCProfile); pb.setParameter("JPEGQuality", JPEGQuality); pb.setParameter("JPEGTable", JPEGTable); return JAI.createRenderable("IIP", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/AddConstToCollectionDescriptor.java0000644000175000017500000001463410203035544032120 0ustar mathieumathieu/* * $RCSfile: AddConstToCollectionDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:29 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.util.Collection; import java.util.Iterator; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.registry.CollectionRegistryMode; /** * An OperationDescriptor describing the * "AddConstToCollection" operation. * *

      The AddConstToCollection operation takes a collection of * rendered images and an array of double constants, and for each * rendered image in the collection adds a constant to every pixel of * its corresponding band. If the number of constants supplied is less * than the number of bands of a source image then the same constant * from entry 0 is applied to all the bands. Otherwise, a constant * from a different entry is applied to each band. * *

      The operation will attempt to store the result images in the same * collection class as that of the source images. If a new instance of * the source collection class can not be created, then the operation * will store the result images in a java.util.Vector. There will be the * same number of images in the output collection as in the source * collection. * *

      * * * * * * * * * *
      Resource List
      Name Value
      GlobalName AddConstToCollection
      LocalName AddConstToCollection
      Vendor com.sun.media.jai
      Description Adds constants to a collection * of rendered images.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/AddConstToCollectionDescriptor.html
      Version 1.0
      arg0Desc The constants to be added.

      * *

      * * * * * *
      Parameter List
      Name Class TypeDefault Value
      constants double[]{0.0}

      * * @see javax.media.jai.CollectionImage * @see java.util.Collection * @see javax.media.jai.OperationDescriptor */ public class AddConstToCollectionDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "AddConstToCollection"}, {"LocalName", "AddConstToCollection"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("AddConstToCollectionDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/AddConstToCollectionDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("AddConstToCollectionDescriptor1")} }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "constants" }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { double[].class }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { new double[]{0.0} }; private static final String[] supportedModes = { "collection" }; /** Constructor. */ public AddConstToCollectionDescriptor() { super(resources, supportedModes, 1, paramNames, paramClasses, paramDefaults, null); } /** Validates input source and parameter. */ public boolean validateArguments(String modeName, ParameterBlock args, StringBuffer msg) { if (!super.validateArguments(modeName, args, msg)) { return false; } Collection col = (Collection)args.getSource(0); if (col.size() < 1) { msg.append(getName() + " " + JaiI18N.getString("AddConstToCollectionDescriptor2")); return false; } Iterator iter = col.iterator(); while (iter.hasNext()) { Object o = iter.next(); if (!(o instanceof RenderedImage)) { msg.append(getName() + " " + JaiI18N.getString("AddConstToCollectionDescriptor3")); return false; } } int length = ((double[])args.getObjectParameter(0)).length; if (length < 1) { msg.append(getName() + " " + JaiI18N.getString("AddConstToCollectionDescriptor4")); return false; } return true; } /** * Adds constants to a collection of rendered images. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createCollection(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see Collection * * @param source0 Collection source 0. * @param constants The constants to be added. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The Collection destination. * @throws IllegalArgumentException if source0 is null. */ public static Collection createCollection(Collection source0, double[] constants, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("AddConstToCollection", CollectionRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("constants", constants); return JAI.createCollection("AddConstToCollection", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/ExtremaDescriptor.java0000644000175000017500000002457210203035544027511 0ustar mathieumathieu/* * $RCSfile: ExtremaDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:35 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.ROI; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Extrema" operation. * *

      The Extrema operation scans a specific region of a rendered * image and finds the maximum and minimum pixel values for each band * within that region of the image. The image data pass through this * operation unchanged. * *

      The region-wise maximum and minimum pixel values may be obtained * as properties. Calling the getProperty method on this * operation with "extrema" as the property name retrieves both the * region-wise maximum and minimum pixel values. Calling it with * "maximum" as the property name retrieves the region-wise maximum * pixel value, and with "minimum" as the property name retrieves the * region-wise minimum pixel value. The return value for "extrema" has * type double[2][#bands], and those for "maximum" * and "minimum" have type double[#bands]. * *

      The region of interest (ROI) does not have to be a rectangle. * It may be null, in which case the entire image is * scanned to find the image-wise maximum and minimum pixel values * for each band. * *

      The set of pixels scanned may be further reduced by * specifying the "xPeriod" and "yPeriod" parameters that represent * the sampling rate along each axis. These variables may not be * less than 1. However, they may be null, in which * case the sampling rate is set to 1; that is, every pixel in the * ROI is processed. * *

      The Boolean parameter "saveLocations" indicates whether * the locations of the extrema will be computed. If TRUE, the * locations are computed and stored in the properties "minLocations" and * "maxLocations" in the form of lists of run length codes. Each run length * code is stored as a three-entry integer array (xStart, yStart, * length). Because the statistics are implemented on the * low-resolution image, this length is defined on the image * coordinate system of the low-resolution image. Thus, the run length code * above means the pixels (xStart, yStart), (xStart + xPeriod, yStart), * ..., (xStart + (length - 1) * xPeriod, yStart) of the original * image have a value of the maximum or minimum, depending on whether this * run exists in the property "maxLocations" or "minLocations". The run length * code is row-based, thus the run doesn't wrap on the image boundaries. * Runs are not guaranteed to be maximal, e.g. a run that crosses tile * boundaries might be broken at the boundary into multiple runs. The order * of the runs is not guaranteed. * *

      The value objects of the properties "minLocations" and "maxLocations" * are arrays of java.util.List. Each array entry contains * the minimum/maximum locations of one band. The elements in * a list can be accessed using the iterator of * the java.util.List. For example, the sample code below * demonstrates how to retrieve the minimum locations of the first band: *

       *	List minLocations = ((List[])extremaOp.getProperty("minLocations"))[0];
       *	Iterator iter = minLocations.iterator();
       *	while(iter.hasNext()) {
       *	    int[] runLength = (int[])iter.next();
       *	    int xStart = runLength[0];
       *          int yStart = runLength[1];
       *          int length = runLength[2];
       *      }
       * 
      * *

      In the implementation of this operator, the proper use of the parameter * "saveLocations" also helps to keep the efficiency of the operator in the * common case: when only the extremal values are computed. * *

      The parameter "maxRuns" is the maximum number of run length codes that * the user would like to store. It is defined to reduce memory * consumption on very large images. If the parameter "saveLocations" is * FALSE, this parameter will be ignored. If this parameter is * equal to Integer.MAX_VALUE, all the locations will be saved. * *

      * * * * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName Extrema
      LocalName Extrema
      Vendor com.sun.media.jai
      Description Finds the maximum and minimum pixel value * in each band of an image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ExtremaDescriptor.html
      Version 1.0
      arg0Desc The region of the image to scan.
      arg1Desc The horizontal sampling rate, * may not be less than 1.
      arg2Desc The vertical sampling rate, * may not be less than 1.
      arg3Desc Whether to store extrema locations.
      arg4Desc Maximum number of run length codes to store.

      * *

      * * * * * * * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      roi javax.media.jai.ROInull
      xPeriod java.lang.Integer1
      yPeriod java.lang.Integer1
      saveLocations java.lang.BooleanBoolean.FALSE
      maxRuns java.lang.Integer1

      * * @see javax.media.jai.OperationDescriptor */ public class ExtremaDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Extrema"}, {"LocalName", "Extrema"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("ExtremaDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ExtremaDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("ExtremaDescriptor1")}, {"arg1Desc", JaiI18N.getString("ExtremaDescriptor2")}, {"arg2Desc", JaiI18N.getString("ExtremaDescriptor3")}, {"arg3Desc", JaiI18N.getString("ExtremaDescriptor4")}, {"arg4Desc", JaiI18N.getString("ExtremaDescriptor5")} }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "roi", "xPeriod", "yPeriod", "saveLocations", "maxRuns" }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { javax.media.jai.ROI.class, java.lang.Integer.class, java.lang.Integer.class, java.lang.Boolean.class, java.lang.Integer.class }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { null, new Integer(1), new Integer(1), Boolean.FALSE, new Integer(1) }; /** Constructor. */ public ExtremaDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** * Returns the minimum legal value of a specified numeric parameter * for this operation. */ public Number getParamMinValue(int index) { if (index == 0 || index == 3) { return null; } else if (index == 1 || index == 2 || index == 4) { return new Integer(1); } else { throw new ArrayIndexOutOfBoundsException(); } } /** * Finds the maximum and minimum pixel value in each band of an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param roi The region of the image to scan. * May be null. * @param xPeriod The horizontal sampling rate, may not be less than 1. * May be null. * @param yPeriod The vertical sampling rate, may not be less than 1. * May be null. * @param saveLocations Whether to store extrema locations. * May be null. * @param maxRuns Maximum number of run length codes to store. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, ROI roi, Integer xPeriod, Integer yPeriod, Boolean saveLocations, Integer maxRuns, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Extrema", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("roi", roi); pb.setParameter("xPeriod", xPeriod); pb.setParameter("yPeriod", yPeriod); pb.setParameter("saveLocations", saveLocations); pb.setParameter("maxRuns", maxRuns); return JAI.create("Extrema", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/BandSelectDescriptor.java0000644000175000017500000001773510203035544030113 0ustar mathieumathieu/* * $RCSfile: BandSelectDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:30 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "BandSelect" operation. * *

      The BandSelect operation chooses N bands from a * rendered or renderable source image and copies the pixel data of * these bands to the destination image in the order specified. The * bandIndices parameter specifies the source band * indices, and its size (bandIndices.length) determines * the number of bands of the destination image. The destination * image may have ay number of bands, and a particular band of the * source image may be repeated in the destination image by specifying * it multiple times in the bandIndices parameter. * *

      Each of the bandIndices value should be a valid * band index number of the source image. For example, if the source * only has two bands, then 1 is a valid band index, but 3 is not. The * first band is numbered 0. * *

      The destination pixel values are defined by the pseudocode: *

       * dst[x][y][b] = src[x][y][bandIndices[b]];
       * 
      * *

      * * * * * * * * * *
      Resource List
      Name Value
      GlobalName BandSelect
      LocalName BandSelect
      Vendor com.sun.media.jai
      Description Selects n number of bands from * an image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/BandSelectDescriptor.html
      Version 1.0
      arg0Desc The indices of the selected bands.

      * *

      * * * * * *
      Parameter List
      Name Class TypeDefault Value
      bandIndices int[]NO_PARAMETER_DEFAULT

      * * @see javax.media.jai.OperationDescriptor */ public class BandSelectDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "BandSelect"}, {"LocalName", "BandSelect"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("BandSelectDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/BandSelectDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("BandSelectDescriptor1")} }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { int[].class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "bandIndices" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT }; private static final String[] supportedModes = { "rendered", "renderable" }; /** Constructor. */ public BandSelectDescriptor() { super(resources, supportedModes, 1, paramNames, paramClasses, paramDefaults, null); } /** * Validates the input source and parameters. * *

      In addition to the standard checks performed by the * superclass method, this method checks that "bandIndices" has a * length of at least 1 and does not contain any values less than * 0 or greater than the number of source bands minus 1. */ public boolean validateArguments(String modeName, ParameterBlock args, StringBuffer message) { if (!super.validateArguments(modeName, args, message)) { return false; } if (!modeName.equalsIgnoreCase("rendered")) return true; int[] indices = (int[])args.getObjectParameter(0); if (indices.length < 1) { message.append(getName() + " " + JaiI18N.getString("BandSelectDescriptor2")); return false; } RenderedImage src = args.getRenderedSource(0); int bands = src.getSampleModel().getNumBands(); for (int i = 0; i < indices.length; i++) { if (indices[i] < 0 || indices[i] >= bands) { message.append(getName() + " " + JaiI18N.getString("BandSelectDescriptor3")); return false; } } return true; } /** * Selects n number of bands from an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param bandIndices The indices of the selected bands. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if bandIndices is null. */ public static RenderedOp create(RenderedImage source0, int[] bandIndices, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("BandSelect", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("bandIndices", bandIndices); return JAI.create("BandSelect", pb, hints); } /** * Selects n number of bands from an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param bandIndices The indices of the selected bands. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if bandIndices is null. */ public static RenderableOp createRenderable(RenderableImage source0, int[] bandIndices, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("BandSelect", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("bandIndices", bandIndices); return JAI.createRenderable("BandSelect", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/TransposeDescriptor.java0000644000175000017500000002705010203035544030054 0ustar mathieumathieu/* * $RCSfile: TransposeDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:46 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.util.PropertyGeneratorImpl; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.EnumeratedParameter; import javax.media.jai.Interpolation; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PlanarImage; import javax.media.jai.PropertyGenerator; import javax.media.jai.ROI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.operator.TransposeType; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * This property generator computes the properties for the operation * "Transpose" dynamically. */ class TransposePropertyGenerator extends PropertyGeneratorImpl { /** Constructor. */ public TransposePropertyGenerator() { super(new String[] {"ROI"}, new Class[] {ROI.class}, new Class[] {RenderedOp.class}); } /** * Returns the specified property. * * @param name Property name. * @param opNode Operation node. */ public Object getProperty(String name, Object opNode) { validate(name, opNode); if(opNode instanceof RenderedOp && name.equalsIgnoreCase("roi")) { RenderedOp op = (RenderedOp)opNode; ParameterBlock pb = op.getParameterBlock(); // Retrieve the rendered source image and its ROI. PlanarImage src = (PlanarImage)pb.getRenderedSource(0); Object property = src.getProperty("ROI"); if (property == null || property.equals(java.awt.Image.UndefinedProperty) || !(property instanceof ROI)) { return java.awt.Image.UndefinedProperty; } // Return undefined also if source ROI is empty. ROI srcROI = (ROI)property; if (srcROI.getBounds().isEmpty()) { return java.awt.Image.UndefinedProperty; } /// This should really create a proper AffineTransform /// and transform the ROI with it to avoid forcing /// ROI.getAsImage to be called. // Retrieve the transpose type and create a nearest neighbor // Interpolation object. TransposeType transposeType = (TransposeType)pb.getObjectParameter(0); Interpolation interp = Interpolation.getInstance(Interpolation.INTERP_NEAREST); // Return the transposed ROI. return new ROI(JAI.create("transpose", srcROI.getAsImage(), transposeType)); } return java.awt.Image.UndefinedProperty; } } /** * An OperationDescriptor describing the "Transpose" operation. * *

      The "Transpose" operation performs the following operations: * *

        * *
      • Flip an image across an imaginary horizontal line that runs * through the center of the image (FLIP_VERTICAL).
      • * *
      • Flip an image across an imaginary vertical line that runs * through the center of the image (FLIP_HORIZONTAL).
      • * *
      • Flip an image across its main diagonal that runs from the upper * left to the lower right corner (FLIP_DIAGONAL).
      • * *
      • Flip an image across its main antidiagonal that runs from the * upper right to the lower left corner(FLIP_ANTIDIAGONAL).
      • * *
      • Rotate an image clockwise by 90, 180, or 270 degrees * (ROTATE_90, ROTATE_180, ROTATE_270).
      • * *
      * *

      In all cases, the resulting image will have the same origin (as * defined by the return values of its getMinX() and * getMinY() methods) as the source image. * *

      It should be noted that this operation automatically adds a * value of Boolean.TRUE for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL to the given * configuration so that the operation is performed * on the pixel values instead of being performed on the indices into * the color map if the source(s) have an IndexColorModel. * This addition will take place only if a value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL has not already been * provided by the user. Note that the configuration Map * is cloned before the new hint is added to it. The operation can be * smart about the value of the JAI.KEY_REPLACE_INDEX_COLOR_MODEL * RenderingHints, i.e. while the default value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL is * Boolean.TRUE, in some cases the operator could set the * default. * *

      "Transpose" defines a PropertyGenerator that * performs an identical transformation on the "ROI" property of the * source image, which can be retrieved by calling the * getProperty method with "ROI" as the property name. * *

      * * * * * * * * * *
      Resource List
      Name Value
      GlobalName transpose
      LocalName transpose
      Vendor com.sun.media.jai
      Description Reflects an image in a specified direction * or rotates an image in multiples of 90 * degrees.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/TransposeDescriptor.html
      Version 1.0
      arg0Desc The type of flip operation * to be performed.

      * *

      * * * * * *
      Parameter List
      Name Class TypeDefault Value
      type javax.media.jai.operator.TransposeTypeNO_PARAMETER_DEFAULT

      * * @see javax.media.jai.OperationDescriptor * @see TransposeType */ public class TransposeDescriptor extends OperationDescriptorImpl { public static final TransposeType FLIP_VERTICAL = new TransposeType("FLIP_VERTICAL", 0); public static final TransposeType FLIP_HORIZONTAL = new TransposeType("FLIP_HORIZONTAL", 1); public static final TransposeType FLIP_DIAGONAL = new TransposeType("FLIP_DIAGONAL", 2); public static final TransposeType FLIP_ANTIDIAGONAL = new TransposeType("FLIP_ANTIDIAGONAL", 3); public static final TransposeType ROTATE_90 = new TransposeType("ROTATE_90", 4); public static final TransposeType ROTATE_180 = new TransposeType("ROTATE_180", 5); public static final TransposeType ROTATE_270 = new TransposeType("ROTATE_270", 6); /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Transpose"}, {"LocalName", "Transpose"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("TransposeDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/TransposeDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion2")}, {"arg0Desc", JaiI18N.getString("TransposeDescriptor1")} }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { TransposeType.class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "type" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT }; /** Constructor. */ public TransposeDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** Returns true since renderable operation is supported. */ public boolean isRenderableSupported() { return true; } /** * Returns an array of PropertyGenerators implementing * property inheritance for the "Transpose" operation. * * @return An array of property generators. */ public PropertyGenerator[] getPropertyGenerators() { PropertyGenerator[] pg = new PropertyGenerator[1]; pg[0] = new TransposePropertyGenerator(); return pg; } /** * Reflects an image in a specified direction or rotates an image in multiples of 90 degrees. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param type The The type of flip operation to be performed. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if type is null. */ public static RenderedOp create(RenderedImage source0, TransposeType type, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Transpose", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("type", type); return JAI.create("Transpose", pb, hints); } /** * Reflects an image in a specified direction or rotates an image in multiples of 90 degrees. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param type The The type of flip operation to be performed. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if type is null. */ public static RenderableOp createRenderable(RenderableImage source0, TransposeType type, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Transpose", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("type", type); return JAI.createRenderable("Transpose", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/DFTScalingType.java0000644000175000017500000000151610203035544026616 0ustar mathieumathieu/* * $RCSfile: DFTScalingType.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:34 $ * $State: Exp $ */ package javax.media.jai.operator; import javax.media.jai.EnumeratedParameter; /** * Class used to represent the acceptable values of the "scalingType" * parameter of the "DFT" and "IDFT" operations. Acceptable values for the * "scalingType" parameter are defined in the DFTDescriptor * and IDFTDescriptor by the constants SCALING_NONE, * SCALING_UNITARY, and SCALING_DIMENSIONS. * * @since JAI 1.1 */ public final class DFTScalingType extends EnumeratedParameter { DFTScalingType(String name, int value) { super(name, value); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/SubsampleBinaryToGrayDescriptor.java0000644000175000017500000003320310203035544032321 0ustar mathieumathieu/* * $RCSfile: SubsampleBinaryToGrayDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:44 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.util.PropertyGeneratorImpl; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OpImage; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PixelAccessor; import javax.media.jai.PropertyGenerator; import javax.media.jai.ROI; import javax.media.jai.ROIShape; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * This property generator computes the properties for the operation * "SubsampleBinaryToGray" dynamically. */ class SubsampleBinaryToGrayPropertyGenerator extends PropertyGeneratorImpl { /** Constructor. */ public SubsampleBinaryToGrayPropertyGenerator() { super(new String[] {"ROI"}, new Class[] {ROI.class}, new Class[] {RenderedOp.class}); } /** * Returns the specified property. * * @param name Property name. * @param opNode Operation node. */ public Object getProperty(String name, Object opNode) { validate(name, opNode); if(opNode instanceof RenderedOp && name.equalsIgnoreCase("roi")) { RenderedOp op = (RenderedOp)opNode; ParameterBlock pb = op.getParameterBlock(); // Retrieve the rendered source image and its ROI. RenderedImage src = pb.getRenderedSource(0); Object property = src.getProperty("ROI"); if (property == null || property.equals(java.awt.Image.UndefinedProperty) || !(property instanceof ROI)) { return java.awt.Image.UndefinedProperty; } ROI srcROI = (ROI)property; // Determine the effective source bounds. Rectangle srcBounds = new Rectangle(src.getMinX(), src.getMinY(), src.getWidth(), src.getHeight()); // If necessary, clip the ROI to the effective source bounds. if(!srcBounds.contains(srcROI.getBounds())) { srcROI = srcROI.intersect(new ROIShape(srcBounds)); } // Retrieve the scale factors and translation values. float sx = pb.getFloatParameter(0); float sy = pb.getFloatParameter(1); // Create an equivalent transform. AffineTransform transform = new AffineTransform(sx, 0.0, 0.0, sy, 0.0, 0.0); // Create the scaled ROI. ROI dstROI = srcROI.transform(transform); // Retrieve the destination bounds. Rectangle dstBounds = op.getBounds(); // If necessary, clip the warped ROI to the destination bounds. if(!dstBounds.contains(dstROI.getBounds())) { dstROI = dstROI.intersect(new ROIShape(dstBounds)); } // Return the warped and possibly clipped ROI. return dstROI; } return java.awt.Image.UndefinedProperty; } } /** * An OperationDescriptor describing the "SubsampleBinaryToGray" * operation. * *

      The "SubsampleBinaryToGray" operation collects a Binary image pixels * into a gray scale image. Roughly speaking, each pixel (x, y) of * the destination, is the sum of the source pixel values of the source * pixel matrix of size 1/xScale by 1/yScale. In the noninteger case, the * next bigger inter is used. Thus when xScale = yScale = 1/2.2, * a 3 x 3 pixel matrix into a single destination pixel, resulting in 10 * levels [0..9]. The resulting gray values are then normalized to have * gray values in [0..255]. * *

      The source is a Binary image and the result of the operation is * a grayscale image. The scale factors xScale * and yScale must be between (0, 1] and strictly bigger * than 0. * *

      The destination image is a byte image whose * dimensions are: * *

       *       dstWidth  = floor(srcWidth * xScale)
       *       dstHeight = floor(srcHeight * yScale)
       * 
      * *

      It may be noted that the minX, minY, width and height hints as * specified through the JAI.KEY_IMAGE_LAYOUT hint in the * RenderingHints object are not honored, as this operator * calculates the destination image bounds itself. The other * ImageLayout hints, like tileWidth and tileHeight, * however, are honored. * *

      It should be noted that this operation automatically adds a * value of Boolean.FALSE for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL to the given * configuration since this operation is capable of * dealing correctly with a source that has an IndexColorModel, * without having to expand the IndexColorModel. * This addition will take place only if a value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL has not already been * provided by the user. Note that the configuration Map * is cloned before the new hint is added to it. * *

      Specifying a scale factor of greater than 1 or less than 0 * will cause an exception. To scale image sizes up or down, * see Scale operator. * *

      * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName SubsampleBinaryToGray
      LocalName SubsampleBinaryToGray
      Vendor com.sun.media.jai
      Description Subsamples a binary image into a gray scale image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/SubsampleBinaryToGrayDescriptor.html
      Version 1.0
      arg0Desc The X scale factor.
      arg1Desc The Y scale factor.

      * *

      * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      xScale java.lang.Float1.0F
      yScale java.lang.Float1.0F

      * * @see javax.media.jai.OperationDescriptor * * @since JAI 1.1 */ public class SubsampleBinaryToGrayDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "SubsampleBinaryToGray"}, {"LocalName", "SubsampleBinaryToGray"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("SubsampleBinaryToGray0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/SubsampleBinaryToGrayDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("SubsampleBinaryToGray1")}, {"arg1Desc", JaiI18N.getString("SubsampleBinaryToGray2")} }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { java.lang.Float.class, java.lang.Float.class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "xScale", "yScale" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { new Float(1.0F), new Float(1.0F) }; /** Constructor. */ public SubsampleBinaryToGrayDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** Returns true since renderable operation is supported. */ public boolean isRenderableSupported() { return true; } /** * Returns an array of PropertyGenerators implementing * property inheritance for the "SubsampleBinaryToGray" operation. * * @return An array of property generators. */ public PropertyGenerator[] getPropertyGenerators() { PropertyGenerator[] pg = new PropertyGenerator[1]; pg[0] = new SubsampleBinaryToGrayPropertyGenerator(); return pg; } /** * Validates the input parameters. * *

      In addition to the standard checks performed by the * superclass method, this method checks that "xScale" and "yScale" * are both greater than 0 and less than or equal to 1. * *

      The src image must be a binary, a one band, one bit * per pixel image with a MultiPixelPackedSampleModel. */ protected boolean validateParameters(ParameterBlock args, StringBuffer msg) { if (!super.validateParameters(args, msg)) { return false; } // Checks for source to be Binary and Dest Grayscale // to be in the RIF RenderedImage src = (RenderedImage)args.getSource(0); PixelAccessor srcPA = new PixelAccessor(src); if (!srcPA.isPacked || !srcPA.isMultiPixelPackedSM){ msg.append(getName() + " " + JaiI18N.getString("SubsampleBinaryToGray3")); return false; } float xScale = args.getFloatParameter(0); float yScale = args.getFloatParameter(1); if (xScale <= 0.0F || yScale <= 0.0F || xScale > 1.0F || yScale > 1.0F) { msg.append(getName() + " " + JaiI18N.getString("SubsampleBinaryToGray1") + " or " + JaiI18N.getString("SubsampleBinaryToGray2")); return false; } return true; } /** * Returns the minimum legal value of a specified numeric parameter * for this operation. * *

      For the minimum value of "xScale" and "yScale", this method * returns 0. However, the scale factors must be a positive floating * number and can not be 0. */ public Number getParamMinValue(int index) { if (index == 0 || index == 1) { return new Float(0.0F); } else { throw new ArrayIndexOutOfBoundsException(); } } /** * To subsamples binary image to gray; reverse of dithering. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param xScale scaleX must be between 0 and 1, excluding 0. * May be null. * @param yScale scaleY must be between 0 and 1, excluding 0. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, Float xScale, Float yScale, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("SubsampleBinaryToGray", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("xScale", xScale); pb.setParameter("yScale", yScale); return JAI.create("SubsampleBinaryToGray", pb, hints); } /** * To subsamples binary image to gray; reverse of dithering. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param xScale scaleX must be between 0 and 1, excluding 0. * May be null. * @param yScale scaleY must be between 0 and 1, excluding 0. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, Float xScale, Float yScale, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("SubsampleBinaryToGray", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("xScale", xScale); pb.setParameter("yScale", yScale); return JAI.createRenderable("SubsampleBinaryToGray", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/AndConstDescriptor.java0000644000175000017500000002073410203035544027611 0ustar mathieumathieu/* * $RCSfile: AndConstDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:29 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "AndConst" * operation. * *

      This operation takes one rendered or renderable image and an * array of integer constants, and performs a bit-wise logical "and" * between every pixel in the same band of the source and the constant * from the corresponding array entry. If the number of constants * supplied is less than the number of bands of the destination, then * the constant from entry 0 is applied to all the bands. Otherwise, a * constant from a different entry is applied to each band. * *

      The source image must have an integral data type. By default, * the destination image bound, data type, and number of bands are the * same as the source image. * *

      The following matrix defines the logical "and" operation. *

      * * * * * * *
      Logical "and"
      src const Result
      0 0 0
      0 1 0
      1 0 0
      1 1 1

      * *

      The destination pixel values are defined by the pseudocode: *

       * if (constants.length < dstNumBands) {
       *     dst[x][y][b] = srcs[x][y][b] & constants[0];
       * } else {
       *     dst[x][y][b] = srcs[x][y][b] & constants[b];
       * }
       * 
      * *

      * * * * * * * * * *
      Resource List
      Name Value
      GlobalName AndConst
      LocalName AndConst
      Vendor com.sun.media.jai
      Description Logically "ands" an image * with constants.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/AndConstDescriptor.html
      Version 1.0
      arg0Desc The constants to logically "and" with.

      * *

      * * * * * *
      Parameter List
      Name Class TypeDefault Value
      constants int[]{0xFFFFFFFF}

      * * @see javax.media.jai.OperationDescriptor */ public class AndConstDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "AndConst"}, {"LocalName", "AndConst"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("AndConstDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/AndConstDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("AndConstDescriptor1")} }; /** * The parameter class list for this operation. * The number of constants provided should be either 1, in which case * this same constant is applied to all the source bands; or the same * number as the source bands, in which case one contant is applied * to each band. */ private static final Class[] paramClasses = { int[].class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "constants" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { new int[] {0xFFFFFFFF} }; private static final String[] supportedModes = { "rendered", "renderable" }; /** Constructor. */ public AndConstDescriptor() { super(resources, supportedModes, 1, paramNames, paramClasses, paramDefaults, null); } /** * Validates the input source and parameter. * *

      In addition to the standard checks performed by the * superclass method, this method checks that the source image has * an integral data type and that "constants" has length at least 1. */ public boolean validateArguments(String modeName, ParameterBlock args, StringBuffer message) { if (!super.validateArguments(modeName, args, message)) { return false; } if (!modeName.equalsIgnoreCase("rendered")) return true; RenderedImage src = args.getRenderedSource(0); int dtype = src.getSampleModel().getDataType(); if ((dtype != DataBuffer.TYPE_BYTE ) && (dtype != DataBuffer.TYPE_USHORT) && (dtype != DataBuffer.TYPE_SHORT ) && (dtype != DataBuffer.TYPE_INT )) { message.append(getName() + " " + JaiI18N.getString("AndConstDescriptor1")); return false; } int length = ((int[])args.getObjectParameter(0)).length; if (length < 1) { message.append(getName() + " " + JaiI18N.getString("AndConstDescriptor2")); return false; } return true; } /** * Logically "ands" an image with constants. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param constants The constants to logically "and" with. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, int[] constants, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("AndConst", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("constants", constants); return JAI.create("AndConst", pb, hints); } /** * Logically "ands" an image with constants. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param constants The constants to logically "and" with. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, int[] constants, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("AndConst", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("constants", constants); return JAI.createRenderable("AndConst", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/SubtractFromConstDescriptor.java0000644000175000017500000001747210203035544031527 0ustar mathieumathieu/* * $RCSfile: SubtractFromConstDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:45 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the * "SubtractFromConst" operation. * *

      The SubtractFromConst operation takes one rendered or * renderable image and an array of double constants, and subtracts * every pixel of the same band of the source from the constant from * the corresponding array entry. If the number of constants supplied * is less than the number of bands of the destination, then the * constant from entry 0 is applied to all the bands. Otherwise, a * constant from a different entry is applied to each band. * *

      By default, the destination image bound, data type, and number of * bands are the same as the source image. If the result of the operation * underflows/overflows the minimum/maximum value supported by the * destination data type, then it will be clamped to the minimum/maximum * value respectively. * *

      The destination pixel values are defined by the pseudocode: *

       * if (constants.length < dstNumBands) {
       *     dst[x][y][b] = constants[0] - src[x][y][b];
       * } else {
       *     dst[x][y][b] = constants[b] - src[x][y][b];
       * }
       * 
      * *

      * * * * * * * * * *
      Resource List
      Name Value
      GlobalName SubtractFromConst
      LocalName SubtractFromConst
      Vendor com.sun.media.jai
      Description Subtracts an image from * constants.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/SubtractFromConstDescriptor.html
      Version 1.0
      arg0Desc The constants to be subtracted from.

      * *

      * * * * * *
      Parameter List
      Name Class TypeDefault Value
      constants double[]NO_PARAMETER_DEFAULT

      * * @see javax.media.jai.OperationDescriptor */ public class SubtractFromConstDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "SubtractFromConst"}, {"LocalName", "SubtractFromConst"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("SubtractFromConstDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/SubtractFromConstDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("SubtractFromConstDescriptor1")} }; /** * The parameter class list for this operation. * The number of constants provided should be either 1, in which case * this same constant is applied to all the source bands; or the same * number as the source bands, in which case one contant is applied * to each band. */ private static final Class[] paramClasses = { double[].class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "constants" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT }; /** Constructor. */ public SubtractFromConstDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** Returns true since renderable operation is supported. */ public boolean isRenderableSupported() { return true; } /** * Validates the input parameter. * *

      In addition to the standard checks performed by the * superclass method, this method checks that the length of the * "constants" array is at least 1. */ protected boolean validateParameters(ParameterBlock args, StringBuffer message) { if (!super.validateParameters(args, message)) { return false; } int length = ((double[])args.getObjectParameter(0)).length; if (length < 1) { message.append(getName() + " " + JaiI18N.getString("SubtractFromConstDescriptor2")); return false; } return true; } /** * Subtracts an image from constants. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param constants The constants to be subtracted from. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if constants is null. */ public static RenderedOp create(RenderedImage source0, double[] constants, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("SubtractFromConst", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("constants", constants); return JAI.create("SubtractFromConst", pb, hints); } /** * Subtracts an image from constants. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param constants The constants to be subtracted from. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if constants is null. */ public static RenderableOp createRenderable(RenderableImage source0, double[] constants, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("SubtractFromConst", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("constants", constants); return JAI.createRenderable("SubtractFromConst", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/XorDescriptor.java0000644000175000017500000001627110203035544026651 0ustar mathieumathieu/* * $RCSfile: XorDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:47 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Xor" operation. * *

      The Xor operation takes two rendered or renderable images, and * performs bit-wise logical "xor" on every pair of pixels, one from * each source image of the corresponding position and band. No * additional parameters are required. * *

      Both source images must have integral data types. The two * data types may be different. * *

      Unless altered by an ImageLayout hint, the * destination image bound is the intersection of the two source image * bounds. If the two sources don't intersect, the destination will * have a width and height of 0. The number of bands of the * destination image is equal to the lesser number of bands of the * sources, and the data type is the smallest data type with * sufficient range to cover the range of both source data types. * *

      The following matrix defines the "xor" operation. *

      * * * * * * *
      Logical "xor"
      src1 src2 Result
      0 0 0
      0 1 1
      1 0 1
      1 1 0

      * *

      The destination pixel values are defined by the pseudocode: *

       * dst[x][y][b] = srcs[0][x][y][b] ^ srcs[0][x][y][b];
       * 
      * *

      * * * * * * * * *
      Resource List
      Name Value
      GlobalName Xor
      LocalName Xor
      Vendor com.sun.media.jai
      Description Logically "xors" two images.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/XorDescriptor.html
      Version 1.0

      * *

      No parameters are needed for this operation. * * @see javax.media.jai.OperationDescriptor */ public class XorDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Xor"}, {"LocalName", "Xor"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("XorDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/XorDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")} }; private static final String[] supportedModes = { "rendered", "renderable" }; /** Constructor. */ public XorDescriptor() { super(resources, supportedModes, 2, null, null, null, null); } /** * Validates the input sources. * *

      In addition to the standard checks performed by the * superclass method, this method checks that the source images * are of integral data type. */ protected boolean validateSources(String modeName, ParameterBlock args, StringBuffer msg) { if (!super.validateSources(modeName, args, msg)) { return false; } if (!modeName.equalsIgnoreCase("rendered")) return true; for (int i = 0; i < 2; i++) { RenderedImage src = args.getRenderedSource(0); int dtype = src.getSampleModel().getDataType(); if (dtype != DataBuffer.TYPE_BYTE && dtype != DataBuffer.TYPE_USHORT && dtype != DataBuffer.TYPE_SHORT && dtype != DataBuffer.TYPE_INT) { msg.append(getName() + " " + JaiI18N.getString("XorDescriptor1")); return false; } } return true; } /** * Logically "xors" two images. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param source1 RenderedImage source 1. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if source1 is null. */ public static RenderedOp create(RenderedImage source0, RenderedImage source1, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Xor", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setSource("source1", source1); return JAI.create("Xor", pb, hints); } /** * Logically "xors" two images. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param source1 RenderableImage source 1. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if source1 is null. */ public static RenderableOp createRenderable(RenderableImage source0, RenderableImage source1, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Xor", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setSource("source1", source1); return JAI.createRenderable("Xor", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/MaxDescriptor.java0000644000175000017500000001331010203035544026615 0ustar mathieumathieu/* * $RCSfile: MaxDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:39 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Max" operation. * *

      The Max operation takes two rendered or renderable images, and for * every pair of pixels, one from each source image of the corresponding * position and band, finds the maximum pixel value. No additional parameters * are required. * *

      The two sources may have different number of bands and/or data * types. By default, the destination image bound is the intersection * of the two source image bounds. If the two sources don't intersect, * the destination will have a width and a height of 0. The number of * bands of the destination image is the same as the least number of * bands of the sources, and the data type is the biggest data type * of the sources. * *

      The destination pixel values are defined by the pseudocode: *

       * if (srcs[0][x][y][b] > srcs[1][x][y][b]) {
       *     dst[x][y][b] = srcs[0][x][y][b];
       * } else {
       *     dst[x][y][b] = srcs[1][x][y][b];
       * }
       * 
      * *

      * * * * * * * * *
      Resource List
      Name Value
      GlobalName Max
      LocalName Max
      Vendor com.sun.media.jai
      Description Computes the pixel-wise maximum of two * images.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/MaxDescriptor.html
      Version 1.0

      * *

      No parameters are needed for this operation. * * @see javax.media.jai.OperationDescriptor */ public class MaxDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Max"}, {"LocalName", "Max"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("MaxDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/MaxDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")} }; /** Constructor. */ public MaxDescriptor() { super(resources, 2, null, null, null); } /** Returns true since renderable operation is supported. */ public boolean isRenderableSupported() { return true; } /** * Computes the pixel-wise maximum of two images. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param source1 RenderedImage source 1. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if source1 is null. */ public static RenderedOp create(RenderedImage source0, RenderedImage source1, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Max", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setSource("source1", source1); return JAI.create("Max", pb, hints); } /** * Computes the pixel-wise maximum of two images. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param source1 RenderableImage source 1. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if source1 is null. */ public static RenderableOp createRenderable(RenderableImage source0, RenderableImage source1, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Max", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setSource("source1", source1); return JAI.createRenderable("Max", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/MosaicType.java0000644000175000017500000000142510203035544026112 0ustar mathieumathieu/* * $RCSfile: MosaicType.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:40 $ * $State: Exp $ */package javax.media.jai.operator; import javax.media.jai.EnumeratedParameter; /** * Class used to represent the acceptable values of the "mosaicType" * parameter of the "Mosaic" operation. Acceptable values for the * "maskShape" parameter are defined in the {@link MosaicDescriptor} * by the constants {@link MosaicDescriptor#MOSAIC_TYPE_BLEND} and * {@link MosaicDescriptor#MOSAIC_TYPE_OVERLAY}. * * @since JAI 1.1.2 */ public final class MosaicType extends EnumeratedParameter { MosaicType(String name, int value) { super(name, value); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/MaxFilterDescriptor.java0000644000175000017500000002170310203035544027770 0ustar mathieumathieu/* * $RCSfile: MaxFilterDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:39 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.util.AreaOpPropertyGenerator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.EnumeratedParameter; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PropertyGenerator; import javax.media.jai.RenderedOp; import javax.media.jai.operator.MaxFilterShape; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "MaxFilter" operation. * *

      The "MaxFilter" operation is a non-linear filter which is * useful for removing isolated lines or pixels while preserving the * overall appearance of an image. The filter is implemented by moving * a mask over the image. For each position of the mask, the * center pixel is replaced by the max of the pixel values covered * by the mask. * *

      There are several shapes possible for the mask. The * MaxFilter operation supports three shapes, as follows: * *

      Square Mask: *

       *                       x x x
       *                       x x x
       *                       x x x
       * 
      * *

      Plus Mask: *

       *                         x
       *                       x x x
       *                         x
       * 
      * *

      X Mask: *

       *                       x   x
       *                         x
       *                       x   x
       * 
      * *

      Example: *

       * 	SeekableStream s = new FileSeekableStream(new File(imagefilename); 
       *	ParameterBlock pb = new ParameterBlock();
       *      pb.add(s);
       *      RenderedImage src = (RenderedImage)JAI.create("stream", pb);
       *
       *      pb = new ParameterBlock();
       *      pb.addSource(src);
       *      pb.add(MaxFilterDescriptor.MAX_MASK_PLUS);    // mask Type
       *      pb.add(new Integer(5));                       // mask size
       *      
       *      RenderedImage dst = (RenderedImage)JAI.create("maxfilter", pb);
       * 
      *

      A RenderingHints can also be added to the above. * * It should be noted that this operation automatically adds a * value of Boolean.TRUE for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL to the given * configuration so that the operation is performed * on the pixel values instead of being performed on the indices into * the color map if the source(s) have an IndexColorModel. * This addition will take place only if a value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL has not already been * provided by the user. Note that the configuration Map * is cloned before the new hint is added to it. The operation can be * smart about the value of the JAI.KEY_REPLACE_INDEX_COLOR_MODEL * RenderingHints, i.e. while the default value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL is * Boolean.TRUE, in some cases the operator could set the * default. * *

      * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName MaxFilter
      LocallName MaxFilter
      Vendor com.sun.media.jai
      Description Performs max filtering on an image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jaiapi/javax.media.jai.operator.MaxFilterDescriptor.html
      Version 1.0
      arg0Desc The shape of the mask to be used for Max Filtering.
      arg1Desc The size (width/height) of the mask to be used in Max Filtering.

      * *

      * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      maskShape javax.media.jai.operator.MaxFilterShapeMAX_MASK_SQUARE
      maskSize java.lang.Integer3

      * * @see javax.media.jai.OperationDescriptor * @see MaxFilterShape * * @since JAI 1.1 */ public class MaxFilterDescriptor extends OperationDescriptorImpl { /** * Default 3x3 Windows */ /** Square shaped mask. */ public static final MaxFilterShape MAX_MASK_SQUARE = new MaxFilterShape("MAX_MASK_SQUARE", 1); /** Plus shaped mask. */ public static final MaxFilterShape MAX_MASK_PLUS = new MaxFilterShape("MAX_MASK_PLUS", 2); /** X shaped mask. */ public static final MaxFilterShape MAX_MASK_X = new MaxFilterShape("MAX_MASK_X", 3); /** Separable square mask. */ public static final MaxFilterShape MAX_MASK_SQUARE_SEPARABLE = new MaxFilterShape("MAX_MASK_SQUARE_SEPARABLE", 4); /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "MaxFilter"}, {"LocalName", "MaxFilter"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("MaxFilterDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jaiapi/javax.media.jai.operator.MaxFilterDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion2")}, {"arg0Desc", JaiI18N.getString("MaxFilterDescriptor1")}, {"arg1Desc", JaiI18N.getString("MaxFilterDescriptor2")} }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { MaxFilterShape.class, java.lang.Integer.class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "maskShape","maskSize" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { MAX_MASK_SQUARE, new Integer(3) }; /** Constructor for the MaxFilterDescriptor. */ public MaxFilterDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** * Returns the minimum legal value of a specified numeric parameter * for this operation. */ public Number getParamMinValue(int index) { if (index == 0) { return null; } else if (index == 1){ return new Integer(1); } else { throw new ArrayIndexOutOfBoundsException(); } } /** * Returns the maximum legal value of a specified numeric parameter * for this operation. */ public Number getParamMaxValue(int index) { if (index == 0) { return null; } else if (index == 1){ return new Integer(Integer.MAX_VALUE); } else { throw new ArrayIndexOutOfBoundsException(); } } /** * Returns an array of PropertyGenerators implementing * property inheritance for the "MaxFilter" operation. * * @return An array of property generators. */ public PropertyGenerator[] getPropertyGenerators() { PropertyGenerator[] pg = new PropertyGenerator[1]; pg[0] = new AreaOpPropertyGenerator(); return pg; } /** * Performs max filtering on an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param maskShape The shape of the mask to be used for Max Filtering. * May be null. * @param maskSize The size (width/height) of the mask to be used in Max Filtering. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, MaxFilterShape maskShape, Integer maskSize, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("MaxFilter", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("maskShape", maskShape); pb.setParameter("maskSize", maskSize); return JAI.create("MaxFilter", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/TIFFDescriptor.java0000644000175000017500000002222410203035544026624 0ustar mathieumathieu/* * $RCSfile: TIFFDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:45 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.codec.SeekableStream; import com.sun.media.jai.codec.TIFFDecodeParam; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "TIFF" operation. * *

      The "TIFF" operation reads TIFF data from a SeekableStream. * *

      * TIFF version 6.0 was finalized in June, 1992. Since that time * there have been two technical notes extending the specification. * There are also a number of * * TIFF Extensions including * * GeoTIFF. * *

      The TIFF format consists of a short header that points to a * linked list of Image File Directories (IFDs). An IFD is * essentially a list of fields. The TIFFDirectory class * encapsulates a set of common operations performed on an IFD; an instance * of the TIFFField class corresponds to a field in an IFD. * Each field has numeric value or tag, a data type, and a byte offset * at which the field's data may be found. This mechanism allows TIFF * files to contain multiple images, each with its own IFD, and to order * its contents flexibly since (apart from the header) nothing is required to * appear at a fixed offset. * *

      An image generated by the "TIFF" operation will have a property * named "tiff_directory" the value of which will be a TIFFDirectory * corresponding to the IFD of the image. JAI's property inheritance mechanism * provides a mechanism by which the field information of the IFD may be * made available to applications in a straightforward way. This mechanism * may be utilized by setting a PropertyGenerator on this * OperationDescriptor which extracts TIFFFields * from the TIFFDirectory-valued property and returns them either * as TIFFField instances or as some user-defined object * initialized from a TIFFField. In the latter case application * code would be insulated from the uncommitted TIFFField class. * *

      The second parameter contains an instance of * TIFFDecodeParam to be used during the decoding. * It may be set to null in order to perform default * decoding, or equivalently may be omitted. * *

      Some TIFF extensions make use of a mechanism known as "private * IFDs." A private IFD is one that is not referenced by the standard * linked list of IFDs that starts in the file header. To a standard * TIFF reader, it appears as an unreferenced area in the file. * However, the byte offset of the private IFD is stored as the value * of a private tag, allowing readers that understand the tag to * locate and interpret the IFD. The "TIFF" operation may read the data * at an arbitrary IFD by supplying the offset of the IFD via the * setIFDOffset() method of the TIFFDecodeParam * parameter. * *

      The third parameter specifies which page of the TIFF data to * read. This permits loading of multi-page TIFF files. The value * provided is zero-relative and so may be interpreted as the index * of the IFD after the first IFD in the stream with zero of course * indicating the first IFD. The default value is zero. * *

      All pages of a multi-page TIFF stream may also be read by doing the * following: *

       * SeekableStream s; // initialization omitted
       * ParameterBlock pb = new ParameterBlock();
       * pb.add(s);
       *
       * TIFFDecodeParam param = new TIFFDecodeParam();
       * pb.add(param);
       *
       * java.util.ArrayList images = new ArrayList();
       * long nextOffset = 0;
       * do {
       *     RenderedOp op = JAI.create("tiff", pb);
       *     images.add(op);
       *     TIFFDirectory dir = (TIFFDirectory)op.getProperty("tiff_directory");
       *     nextOffset = dir.getNextIFDOffset();
       *     if(nextOffset != 0) {
       *         param.setIFDOffset(nextOffset);
       *     }
       * } while(nextOffset != 0);
       * 
      * *

      The classes in the com.sun.media.jai.codec * package are not a committed part of the JAI API. Future releases * of JAI will make use of new classes in their place. This * class will change accordingly. * *

      * * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName TIFF
      LocalName TIFF
      Vendor com.sun.media.jai
      Description Reads a TIFF 6.0 file.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/TIFFDescriptor.html
      Version 1.0
      arg0Desc The SeekableStream to read from.
      arg1Desc The TIFFDecodeParam to use.
      arg2Desc The page to be decoded.

      *

      * * * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      stream com.sun.media.jai.codec.SeekableStreamNO_PARAMETER_DEFAULT
      param com.sun.media.jai.codec.TIFFDecodeParamnull
      page java.lang.Integer0

      * * @see com.sun.media.jai.codec.SeekableStream * @see com.sun.media.jai.codec.TIFFDecodeParam * @see com.sun.media.jai.codec.TIFFDirectory * @see com.sun.media.jai.codec.TIFFEncodeParam * @see com.sun.media.jai.codec.TIFFField * @see javax.media.jai.OperationDescriptor * @see javax.media.jai.operator.EncodeDescriptor */ public class TIFFDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation and * specify the parameter list for the "TIFF" operation. */ private static final String[][] resources = { {"GlobalName", "TIFF"}, {"LocalName", "TIFF"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("TIFFDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/TIFFDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("TIFFDescriptor1")}, {"arg1Desc", JaiI18N.getString("TIFFDescriptor2")}, {"arg2Desc", JaiI18N.getString("TIFFDescriptor3")} }; /** The parameter names for the "TIFF" operation. */ private static final String[] paramNames = { "stream", "param", "page" }; /** The parameter class types for the "TIFF" operation. */ private static final Class[] paramClasses = { com.sun.media.jai.codec.SeekableStream.class, com.sun.media.jai.codec.TIFFDecodeParam.class, java.lang.Integer.class }; /** The parameter default values for the "TIFF" operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT, null, new Integer(0) }; /** Constructor. */ public TIFFDescriptor() { super(resources, 0, paramClasses, paramNames, paramDefaults); } /** * Reads TIFF 6.0 data from an SeekableStream. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param stream The SeekableStream to read from. * @param param The TIFFDecodeParam to use. * May be null. * @param page The page to be decoded. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if stream is null. */ public static RenderedOp create(SeekableStream stream, TIFFDecodeParam param, Integer page, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("TIFF", RenderedRegistryMode.MODE_NAME); pb.setParameter("stream", stream); pb.setParameter("param", param); pb.setParameter("page", page); return JAI.create("TIFF", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/PiecewiseDescriptor.java0000644000175000017500000002303410203035544030011 0ustar mathieumathieu/* * $RCSfile: PiecewiseDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:43 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Piecewise" operation. * *

      The "Piecewise" operation performs a piecewise linear mapping of the * pixel values of an image. The piecewise linear mapping is described by a * set of breakpoints which are provided as an array of the form *

      float breakPoints[N][2][numBreakPoints]
      where the value of * N may be either unity or the number of bands in the source image. * If N is unity then the same set of breakpoints will be applied to * all bands in the image. The abscissas of the supplied breakpoints must * be monotonically increasing. * *

      The pixel values of the destination image are defined by the pseudocode: * *

       * if (src[x][y][b] < breakPoints[b][0][0]) {
       *     dst[x][y][b] = breakPoints[b][1][0]);
       * } else if (src[x][y][b] > breakPoints[b][0][numBreakPoints-1]) {
       *     dst[x][y][b] = breakPoints[b][1][numBreakPoints-1]);
       * } else {
       *     int i = 0;
       *     while(breakPoints[b][0][i+1] < src[x][y][b]) {
       *         i++;
       *     }
       *     dst[x][y][b] = breakPoints[b][1][i] +
       *                        (src[x][y][b] - breakPoints[b][0][i])*
       *                        (breakPoints[b][1][i+1] - breakPoints[b][1][i])/
       *                        (breakPoints[b][0][i+1] - breakPoints[b][0][i]);
       * }
       * 
      * *

      * * * * * * * * * *
      Resource List
      Name Value
      GlobalName Piecewise
      LocalName Piecewise
      Vendor com.sun.media.jai
      Description Applies a piecewise pixel value mapping.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/PiecewiseDescriptor.html
      Version 1.0
      arg0Desc The breakpoint array.

      * *

      * * * * * *
      Parameter List
      Name Class TypeDefault Value
      breakPoints float[][][]identity mapping on [0, 255]

      * * @see java.awt.image.DataBuffer * @see javax.media.jai.ImageLayout * @see javax.media.jai.OperationDescriptor */ public class PiecewiseDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Piecewise"}, {"LocalName", "Piecewise"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("PiecewiseDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/PiecewiseDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", "The breakpoint array."} }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { float[][][].class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "breakPoints" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { new float[][][]{{{0.0f, 255.0f}, {0.0f, 255.0f}}} }; private static final String[] supportedModes = { "rendered", "renderable" }; /** Constructor. */ public PiecewiseDescriptor() { super(resources, supportedModes, 1, paramNames, paramClasses, paramDefaults, null); } /** * Validates the input source and parameter. * *

      In addition to the standard checks performed by the * superclass method, this method checks that the number of bands * in "breakPoints" is either 1 or the number of bands in the * source image, the second breakpoint array dimension is 2, * the third dimension is the same for abscissas and ordinates, * and that the absicssas are monotonically increasing. */ public boolean validateArguments(String modeName, ParameterBlock args, StringBuffer msg) { if (!super.validateArguments(modeName, args, msg)) { return false; } if (!modeName.equalsIgnoreCase("rendered")) return true; // Get the source and the breakpoint array. RenderedImage src = args.getRenderedSource(0); float[][][] breakPoints = (float[][][])args.getObjectParameter(0); // Ensure that the number of breakpoint bands is either 1 or // the number of bands in the source image, the second // breakpoint array dimension is 2, the third dimension is // the same for abscissas and ordinates, and that the absicssas // are monotonically increasing. if (breakPoints.length != 1 && breakPoints.length != src.getSampleModel().getNumBands()) { // Number of breakpoints not 1 nor numBands. msg.append(getName() + " " + JaiI18N.getString("PiecewiseDescriptor1")); return false; } else { int numBands = breakPoints.length; for (int b = 0; b < numBands; b++) { if (breakPoints[b].length != 2) { // Second breakpoint dimension not 2. msg.append(getName() + " " + JaiI18N.getString("PiecewiseDescriptor2")); return false; } else if (breakPoints[b][0].length != breakPoints[b][1].length) { // Differing numbers of abscissas and ordinates. msg.append(getName() + " " + JaiI18N.getString("PiecewiseDescriptor3")); return false; } } for (int b = 0; b < numBands; b++) { int count = breakPoints[b][0].length - 1; float[] x = breakPoints[b][0]; for (int i = 0; i < count; i++) { if (x[i] >= x[i+1]) { // Abscissas not monotonically increasing. msg.append(getName() + " " + JaiI18N.getString("PiecewiseDescriptor4")); return false; } } } } return true; } /** * Applies a piecewise pixel value mapping. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param breakPoints The breakpoint array. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, float[][][] breakPoints, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Piecewise", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("breakPoints", breakPoints); return JAI.create("Piecewise", pb, hints); } /** * Applies a piecewise pixel value mapping. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param breakPoints The breakpoint array. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, float[][][] breakPoints, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Piecewise", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("breakPoints", breakPoints); return JAI.createRenderable("Piecewise", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/MaxFilterShape.java0000644000175000017500000000151610203035544026712 0ustar mathieumathieu/* * $RCSfile: MaxFilterShape.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:39 $ * $State: Exp $ */ package javax.media.jai.operator; import javax.media.jai.EnumeratedParameter; /** * Class used to represent the acceptable values of the "maskShape" * parameter of the "MaxFilter" operation. Acceptable values for the * "maskShape" parameter are defined in the MaxFilterDescriptor * by the constants MAX_MASK_SQUARE, MAX_MASK_PLUS, * MAX_MASK_X, and * MAX_MASK_SQUARE_SEPARABLE. * * @since JAI 1.1 */ public final class MaxFilterShape extends EnumeratedParameter { MaxFilterShape(String name, int value) { super(name, value); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/StreamDescriptor.java0000644000175000017500000001245410203035544027333 0ustar mathieumathieu/* * $RCSfile: StreamDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:44 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.codec.ImageDecodeParam; import com.sun.media.jai.codec.SeekableStream; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Stream" operation. * *

      The Stream operation produces an image by decoding data from a * SeekableStream. The allowable formats are those * registered with the com.sun.media.jai.codec.ImageCodec * class. * *

      The allowable formats are those registered with the * com.sun.media.jai.codec.ImageCodec class. * *

      The second parameter contains an instance of * ImageDecodeParam to be used during the decoding. * It may be set to null in order to perform default * decoding, or equivalently may be omitted. * *

      The classes in the com.sun.media.jai.codec * package are not a committed part of the JAI API. Future releases * of JAI will make use of new classes in their place. This * class will change accordingly. * *

      * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName stream
      LocalName stream
      Vendor com.sun.media.jai
      Description Reads an image from a SeekableStream.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/StreamDescriptor.html
      Version 1.0
      arg0Desc The SeekableStream to read from.
      arg1Desc The ImageDecodeParam to use.

      * *

      * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      stream com.sun.media.jai.codec.SeekableStreamNO_PARAMETER_DEFAULT
      param com.sun.media.jai.codec.ImageDecodeParamnull

      * * @see javax.media.jai.OperationDescriptor */ public class StreamDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation and * specify the parameter list for the "Stream" operation. */ private static final String[][] resources = { {"GlobalName", "Stream"}, {"LocalName", "Stream"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("StreamDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/StreamDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("StreamDescriptor1")}, {"arg1Desc", JaiI18N.getString("StreamDescriptor2")} }; /** The parameter names for the "Stream" operation. */ private static final String[] paramNames = { "stream", "param" }; /** The parameter class types for the "Stream" operation. */ private static final Class[] paramClasses = { com.sun.media.jai.codec.SeekableStream.class, com.sun.media.jai.codec.ImageDecodeParam.class }; /** The parameter default values for the "Stream" operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT, null }; /** Constructor. */ public StreamDescriptor() { super(resources, 0, paramClasses, paramNames, paramDefaults); } /** * Reads an image from a SeekableStream. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param stream The SeekableStream to read from. * @param param The ImageDecodeParam to use. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if stream is null. */ public static RenderedOp create(SeekableStream stream, ImageDecodeParam param, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Stream", RenderedRegistryMode.MODE_NAME); pb.setParameter("stream", stream); pb.setParameter("param", param); return JAI.create("Stream", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/ConvolveDescriptor.java0000644000175000017500000001627410203035544027677 0ustar mathieumathieu/* * $RCSfile: ConvolveDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:32 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.util.AreaOpPropertyGenerator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import javax.media.jai.JAI; import javax.media.jai.KernelJAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PropertyGenerator; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Convolve" operation. * *

      Convolution is a spatial operation that computes each output * sample by multiplying elements of a kernel with the samples * surrounding a particular source sample. * *

      For each destination sample, the kernel is rotated 180 degrees * and its "key element," or origin, is placed over the source pixel * corresponding with the destination pixel. The kernel elements are * multiplied with the source pixels beneath them, and the resulting * products are summed together to produce the destination sample * value. * *

      Pseudocode for the convolution operation on a single sample * dst[x][y] is as follows, assuming the kernel is of size width x height * and has already been rotated through 180 degrees. The kernel's Origin * element is located at position (xOrigin, yOrigin): * *

       * dst[x][y] = 0;
       * for (int i = -xOrigin; i < -xOrigin + width; i++) {
       *     for (int j = -yOrigin; j < -yOrigin + height; j++) {
       *         dst[x][y] += src[x + i][y + j]*kernel[xOrigin + i][yOrigin + j];
       *     }
       * }
       * 
      * *

      Convolution, like any neighborhood operation, leaves a band of * pixels around the edges undefined. For example, for a 3x3 kernel * only four kernel elements and four source pixels contribute to the * convolution pixel at the corners of the source image. Pixels that * do not allow the full kernel to be applied to the source are not * included in the destination image. A "Border" operation may be used * to add an appropriate border to the source image in order to avoid * shrinkage of the image boundaries. * *

      The kernel may not be bigger in any dimension than the image data. * * It should be noted that this operation automatically adds a * value of Boolean.TRUE for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL to the given * configuration so that the operation is performed * on the pixel values instead of being performed on the indices into * the color map if the source(s) have an IndexColorModel. * This addition will take place only if a value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL has not already been * provided by the user. Note that the configuration Map * is cloned before the new hint is added to it. The operation can be * smart about the value of the JAI.KEY_REPLACE_INDEX_COLOR_MODEL * RenderingHints, i.e. while the default value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL is * Boolean.TRUE, in some cases the operator could set the * default. * *

      * * * * * * * * * *
      Resource List
      Name Value
      GlobalName Convolve
      LocalName Convolve
      Vendor com.sun.media.jai
      Description Performs kernel-based convolution * on an image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ConvolveDescriptor.html
      Version 1.0
      arg0Desc The convolution kernel.

      * *

      * * * * * *
      Parameter List
      Name Class TypeDefault Value
      kernel javax.media.jai.KernelJAINO_PARAMETER_DEFAULT

      * * @see javax.media.jai.OperationDescriptor * @see javax.media.jai.KernelJAI */ public class ConvolveDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation and * specify the parameter list for a Convolve operation. */ private static final String[][] resources = { {"GlobalName", "Convolve"}, {"LocalName", "Convolve"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("ConvolveDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ConvolveDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("ConvolveDescriptor1")} }; /** The parameter names for the Convolve operation. */ private static final String[] paramNames = { "kernel" }; /** The parameter class types for the Convolve operation. */ private static final Class[] paramClasses = { javax.media.jai.KernelJAI.class }; /** The parameter default values for the Convolve operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT }; /** Constructor. */ public ConvolveDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** * Returns an array of PropertyGenerators implementing * property inheritance for the "Convolve" operation. * * @return An array of property generators. */ public PropertyGenerator[] getPropertyGenerators() { PropertyGenerator[] pg = new PropertyGenerator[1]; pg[0] = new AreaOpPropertyGenerator(); return pg; } /** * Performs kernel-based convolution on an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param kernel The convolution kernel. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if kernel is null. */ public static RenderedOp create(RenderedImage source0, KernelJAI kernel, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Convolve", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("kernel", kernel); return JAI.create("Convolve", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/AddDescriptor.java0000644000175000017500000001426310203035544026570 0ustar mathieumathieu/* * $RCSfile: AddDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:29 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Add" operation. * *

      The Add operation takes two rendered or renderable source * images, and adds every pair of pixels, one from each source image * of the corresponding position and band. No additional parameters * are required. * *

      The two source images may have different numbers of bands and * data types. By default, the destination image bounds are the * intersection of the two source image bounds. If the sources don't * intersect, the destination will have a width and height of 0. * *

      The default number of bands of the destination image is equal * to the smallest number of bands of the sources, and the data type * is the smallest data type with sufficient range to cover the range * of both source data types (not necessarily the range of their * sums). * *

      As a special case, if one of the source images has N bands (N > * 1), the other source has 1 band, and an ImageLayout * hint is provided containing a destination SampleModel * with K bands (1 < K <= N), then the single band of the 1-banded * source is added to each of the first K bands of the N-band source. * *

      If the result of the operation underflows/overflows the * minimum/maximum value supported by the destination data type, then * it will be clamped to the minimum/maximum value respectively. * *

      The destination pixel values are defined by the pseudocode: *

       * dst[x][y][dstBand] = clamp(srcs[0][x][y][src0Band] +
       *                            srcs[1][x][y][src1Band]);
       * 
      * *

      * * * * * * * * *
      Resource List
      Name Value
      GlobalName Add
      LocalName Add
      Vendor com.sun.media.jai
      Description Adds two images.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/AddDescriptor.html
      Version 1.0

      * *

      No parameters are needed for this operation. * * @see javax.media.jai.OperationDescriptor */ public class AddDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Add"}, {"LocalName", "Add"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("AddDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/AddDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")} }; /** Constructor. */ public AddDescriptor() { super(resources, 2, null, null, null); } /** Returns true since renderable operation is supported. */ public boolean isRenderableSupported() { return true; } /** * Adds two images. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param source1 RenderedImage source 1. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if source1 is null. */ public static RenderedOp create(RenderedImage source0, RenderedImage source1, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Add", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setSource("source1", source1); return JAI.create("Add", pb, hints); } /** * Adds two images. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param source1 RenderableImage source 1. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if source1 is null. */ public static RenderableOp createRenderable(RenderableImage source0, RenderableImage source1, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Add", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setSource("source1", source1); return JAI.createRenderable("Add", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/BandCombineDescriptor.java0000644000175000017500000002113010203035544030230 0ustar mathieumathieu/* * $RCSfile: BandCombineDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:30 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.ColorModel; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OpImage; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "BandCombine" operation. * *

      The BandCombing operation computes a set of arbitrary linear * combinations of the bands of a rendered or renderable source image, * using a specified matrix. The matrix must a number of rows equal to * the number of desired destination bands and a number of columns equal to the * number of source bands plus one. In other words, the array may be * constructed using the syntax: * *

       * double[][] matrix = new double[destBands][sourceBands + 1];
       * 
      * *

      The number of source bands used to determine the matrix dimensions * is given by source.getSampleModel().getNumBands() regardless * of the type of ColorModel the source has. * *

      The extra column in the matrix contains constant values each of which * is added to the respective band of the destination. The transformation is * therefore defined by the pseudocode: * *

       * // s = source pixel
       * // d = destination pixel
       * for(int i = 0; i < destBands; i++) {
       *     d[i] = matrix[i][sourceBands];
       *     for(int j = 0; j < sourceBands; j++) {
       *         d[i] += matrix[i][j]*s[j];
       *     }
       * }
       * 
      * *

      If the result of the computation underflows/overflows the * minimum/maximum value supported by the destination image, then it * will be clamped to the minimum/maximum value respectively. * *

      * * * * * * * * * *
      Resource List
      Name Value
      GlobalName BandCombine
      LocalName BandCombine
      Vendor com.sun.media.jai
      Description Performs arbitrary interband linear combination * using a specified matrix.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/BandCombineDescriptor.html
      Version 1.0
      arg0Desc The matrix specifying the band combination.

      * *

      * * * * * *
      Parameter List
      Name Class TypeDefault Value
      matrix double[][]NO_PARAMETER_DEFAULT

      * * @see javax.media.jai.OperationDescriptor */ public class BandCombineDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "BandCombine"}, {"LocalName", "BandCombine"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("BandCombineDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/BandCombineDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("BandCombineDescriptor1")} }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { double[][].class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "matrix" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT }; private static final String[] supportedModes = { "rendered", "renderable" }; /** Constructor. */ public BandCombineDescriptor() { super(resources, supportedModes, 1, paramNames, paramClasses, paramDefaults, null); } /** * Validates the input source and parameters. * *

      In addition to the standard checks performed by the * superclass method, this method checks that "matrix" has at * least 1 row and (source bands + 1) columns. * *

      The number of source bands is considered to be equal to * source.getSampleModel().getNumBands(). */ public boolean validateArguments(String modeName, ParameterBlock args, StringBuffer message) { if (!super.validateArguments(modeName, args, message)) { return false; } if (!modeName.equalsIgnoreCase("rendered")) return true; RenderedImage src = args.getRenderedSource(0); double[][] matrix = (double[][])args.getObjectParameter(0); SampleModel sm = src.getSampleModel(); int rowLength = sm.getNumBands() + 1; if (matrix.length < 1) { message.append(getName() + ": " + JaiI18N.getString("BandCombineDescriptor2")); return false; } for (int i = 0; i < matrix.length; i++) { if (matrix[i].length != rowLength) { message.append(getName() + ": " + JaiI18N.getString("BandCombineDescriptor2")); return false; } } return true; } /** * Performs arbitrary interband linear combination using a specified matrix. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param matrix The matrix specifying the band combination. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if matrix is null. */ public static RenderedOp create(RenderedImage source0, double[][] matrix, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("BandCombine", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("matrix", matrix); return JAI.create("BandCombine", pb, hints); } /** * Performs arbitrary interband linear combination using a specified matrix. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param matrix The matrix specifying the band combination. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if matrix is null. */ public static RenderableOp createRenderable(RenderableImage source0, double[][] matrix, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("BandCombine", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("matrix", matrix); return JAI.createRenderable("BandCombine", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/ColorConvertDescriptor.java0000644000175000017500000001671710203035544030525 0ustar mathieumathieu/* * $RCSfile: ColorConvertDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:31 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.ColorModel; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "ColorConvert" operation. * *

      The "ColorConvert" operation performs a pixel-by-pixel color * conversion of the data in a rendered or renderable source image. * *

      The data are treated as having no alpha channel, i.e., all bands are * color bands. The color space of the source image is specified by the * ColorSpace object of the source image ColorModel * which must not be null. The color space of the destination * image is specified by the ColorSpace of the "colorModel" * parameter which must be a ColorModel. If a * ColorModel is suggested via the RenderingHints * it is ignored. * *

      The calculation pathway is selected to optimize performance and * accuracy based on which ColorSpace subclasses are used to * represent the source and destination color spaces. The subclass * categories are ICC_ColorSpace, ColorSpaceJAI, * and generic ColorSpace, i.e., one which is not an instance * of either the two aforementioned subclasses. Note that in the Sun * Microsystems implementation, an ICC_ColorSpace instance * is what is returned by ColorSpace.getInstance(). * *

      Integral data are assumed to occupy the full range of the respective * data type; floating point data are assumed to be normalized to the range * [0.0,1.0]. * *

      By default, the destination image bounds, data type, and number of * bands are the same as those of the source image. * *

      * * * * * * * * * *
      Resource List
      Name Value
      GlobalName ColorConvert
      LocalName ColorConvert
      Vendor com.sun.media.jai
      Description Convert the color space of an image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ColorConvertDescriptor.html
      Version 1.0
      arg0Desc The destination ColorModel.

      * *

      * * * * * *
      Parameter List
      Name Class TypeDefault Value
      colorModel java.awt.image.ColorModelNO_PARAMETER_DEFAULT

      * * @see javax.media.jai.OperationDescriptor * @see java.awt.color.ColorSpace * @see java.awt.color.ICC_ColorSpace * @see java.awt.image.ColorModel * @see javax.media.jai.ColorSpaceJAI * @see javax.media.jai.IHSColorSpace */ public class ColorConvertDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "ColorConvert"}, {"LocalName", "ColorConvert"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("ColorConvertDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ColorConvertDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion2")}, {"arg0Desc", JaiI18N.getString("ColorConvertDescriptor1")} }; /** * The parameter class list for this operation. */ private static final Class[] paramClasses = { java.awt.image.ColorModel.class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "colorModel" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT }; /** Constructor. */ public ColorConvertDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** Returns true since renderable operation is supported. */ public boolean isRenderableSupported() { return true; } /** * Convert the color space of an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param colorModel The destination color space. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if colorModel is null. */ public static RenderedOp create(RenderedImage source0, ColorModel colorModel, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("ColorConvert", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("colorModel", colorModel); return JAI.create("ColorConvert", pb, hints); } /** * Convert the color space of an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param colorModel The destination color space. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if colorModel is null. */ public static RenderableOp createRenderable(RenderableImage source0, ColorModel colorModel, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("ColorConvert", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("colorModel", colorModel); return JAI.createRenderable("ColorConvert", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/GIFDescriptor.java0000644000175000017500000001032210203035544026475 0ustar mathieumathieu/* * $RCSfile: GIFDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:36 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.codec.SeekableStream; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "GIF" operation. * *

      The "GIF" operation reads an image from a GIF stream. * *

      The classes in the com.sun.media.jai.codec * package are not a committed part of the JAI API. Future releases * of JAI will make use of new classes in their place. This * class will change accordingly. * *

      * * * * * * * * * *
      Resource List
      Name Value
      GlobalName GIF
      LocalName GIF
      Vendor com.sun.media.jai
      Description Reads an image from a GIF stream.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/GIFDescriptor.html
      Version 1.0
      arg0Desc The SeekableStream to read from.

      * *

      * * * * * *
      Parameter List
      Name Class TypeDefault Value
      stream com.sun.media.jai.codec.SeekableStreamNO_PARAMETER_DEFAULT

      * * @see com.sun.media.jai.codec.SeekableStream * @see javax.media.jai.OperationDescriptor */ public class GIFDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation and * specify the parameter list for the "GIF" operation. */ private static final String[][] resources = { {"GlobalName", "GIF"}, {"LocalName", "GIF"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("GIFDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/GIFDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("GIFDescriptor1")}, }; /** The parameter names for the "GIF" operation. */ private static final String[] paramNames = { "stream" }; /** The parameter class types for the "GIF" operation. */ private static final Class[] paramClasses = { com.sun.media.jai.codec.SeekableStream.class }; /** The parameter default values for the "GIF" operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT }; /** Constructor. */ public GIFDescriptor() { super(resources, 0, paramClasses, paramNames, paramDefaults); } /** * Reads an image from a GIF stream. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param stream The SeekableStream to read from. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if stream is null. */ public static RenderedOp create(SeekableStream stream, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("GIF", RenderedRegistryMode.MODE_NAME); pb.setParameter("stream", stream); return JAI.create("GIF", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/OverlayDescriptor.java0000644000175000017500000001545010203035544027520 0ustar mathieumathieu/* * $RCSfile: OverlayDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:42 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Overlay" operation. * *

      The Overlay operation takes two rendered or renderable source * images, and overlays the second source image on top of the first * source image. No additional parameters are required. * *

      The two source images must have the same data type and number * of bands. However, their SampleModel types may * differ. The destination image will always have the same bounding * rectangle as the first source image, that is, the image on the * bottom, and the same data type and number of bands as the two * sources. In case the two sources don't intersect, the destination * will be the same as the first source. * *

      The destination pixel values are defined by the pseudocode: *

       * if (srcs[1] contains the point (x, y)) {
       *     dst[x][y][b] = srcs[1][x][y][b];
       * } else {
       *     dst[x][y][b] = srcs[0][x][y][b];
       * }
       * 
      * *

      * * * * * * * * *
      Resource List
      Name Value
      GlobalName Overlay
      LocalName Overlay
      Vendor com.sun.media.jai
      Description Overlays one image on top of * another.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/OverlayDescriptor.html
      Version 1.0

      * *

      No parameters are needed for this operation. * * @see javax.media.jai.OperationDescriptor */ public class OverlayDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Overlay"}, {"LocalName", "Overlay"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("OverlayDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/OverlayDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")} }; private static final String[] supportedModes = { "rendered", "renderable" }; /** Constructor. */ public OverlayDescriptor() { super(resources, supportedModes, 2, null, null, null, null); } /** * Validates the input sources. * *

      In addition to the standard checks performed by the * superclass method, this method checks that the source image * SampleModels have the same number of bands and * transfer types. */ protected boolean validateSources(String modeName, ParameterBlock args, StringBuffer msg) { if (!super.validateSources(modeName, args, msg)) { return false; } if (!modeName.equalsIgnoreCase("rendered")) return true; RenderedImage src1 = args.getRenderedSource(0); RenderedImage src2 = args.getRenderedSource(1); SampleModel s1sm = src1.getSampleModel(); SampleModel s2sm = src2.getSampleModel(); if (s1sm.getNumBands() != s2sm.getNumBands() || s1sm.getTransferType() != s2sm.getTransferType()) { msg.append(getName() + " " + JaiI18N.getString("OverlayDescriptor1")); return false; } return true; } /** * Overlays one image on top of another. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param source1 RenderedImage source 1. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if source1 is null. */ public static RenderedOp create(RenderedImage source0, RenderedImage source1, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Overlay", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setSource("source1", source1); return JAI.create("Overlay", pb, hints); } /** * Overlays one image on top of another. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param source1 RenderableImage source 1. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if source1 is null. */ public static RenderableOp createRenderable(RenderableImage source0, RenderableImage source1, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Overlay", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setSource("source1", source1); return JAI.createRenderable("Overlay", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/LookupDescriptor.java0000644000175000017500000002256410203035544027354 0ustar mathieumathieu/* * $RCSfile: LookupDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:38 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.LookupTableJAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Lookup" operation. * *

      The Lookup operation takes a rendered or renderable image and a * lookup table, and performs general table lookup by passing the * source image through the table. * *

      The source may be a single- or multi-banded image of data types * byte, ushort, short, or * int. The lookup table may be single- or multi-banded * and of any JAI supported data types. The destination image must have * the same data type as the lookup table, and its number of bands is * determined based on the number of bands of the source and the table. * If the source is single-banded, the destination has the same number * of bands as the lookup table; otherwise, the destination has the * same number of bands as the source. * *

      If either the source or the table is single-banded and the other * one is multi-banded, then the single band is applied to every band * of the multi-banded object. If both are multi-banded, then their * corresponding bands are matched up. * *

      The table may have a set of offset values, one for each band. This * value is subtracted from the source pixel values before indexing into * the table data array. * *

      It is the user's responsibility to make certain the lookup table * supplied is suitable for the source image. Specifically, the table * data covers the entire range of the source data. Otherwise, the result * of this operation is undefined. * *

      By the nature of this operation, the destination may have a * different number of bands and/or data type from the source. The * SampleModel of the destination is created in accordance * with the actual lookup table used in a specific case. * *

      The destination pixel values are defined by the pseudocode: *

        *
      • If the source image is single-banded and the lookup table is * single- or multi-banded, then the destination image has the same * number of bands as the lookup table: *
         * dst[x][y][b] = table[b][src[x][y][0] - offsets[b]]
         * 
        *
      • * *
      • If the source image is multi-banded and the lookup table is * single-banded, then the destination image has the same number of * bands as the source image: *
         * dst[x][y][b] = table[0][src[x][y][b] - offsets[0]]
         * 
        *
      • * *
      • If the source image is multi-banded and the lookup table is * multi-banded, with the same number of bands as the source image, * then the destination image will have the same number of bands as the * source image: *
         * dst[x][y][b] = table[b][src[x][y][b] - offsets[b]]
         * 
        *
      • *
      * *

      * * * * * * * * * *
      Resource List
      Name Value
      GlobalName Lookup
      LocalName Lookup
      Vendor com.sun.media.jai
      Description Performs general table lookup on an * image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/LookupDescriptor.html
      Version 1.0
      arg0Desc The lookup table the source image * is passed through.

      * *

      * * * * * *
      Parameter List
      Name Class TypeDefault Value
      table javax.media.jai.LookupTableJAINO_PARAMETER_DEFAULT

      * * @see javax.media.jai.LookupTableJAI * @see javax.media.jai.OperationDescriptor */ public class LookupDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Lookup"}, {"LocalName", "Lookup"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("LookupDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/LookupDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("LookupDescriptor1")} }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { javax.media.jai.LookupTableJAI.class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "table" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT }; private static final String[] supportedModes = { "rendered", "renderable" }; /** Constructor. */ public LookupDescriptor() { super(resources, supportedModes, 1, paramNames, paramClasses, paramDefaults, null); } /** * Validates the input source. * *

      In addition to the standard checks performed by the * superclass method, this method checks that the source image * is of integral data type. */ protected boolean validateSources(String modeName, ParameterBlock args, StringBuffer msg) { if (!super.validateSources(modeName, args, msg)) { return false; } if (!modeName.equalsIgnoreCase("rendered")) return true; RenderedImage src = args.getRenderedSource(0); int dtype = src.getSampleModel().getDataType(); if (dtype != DataBuffer.TYPE_BYTE && dtype != DataBuffer.TYPE_USHORT && dtype != DataBuffer.TYPE_SHORT && dtype != DataBuffer.TYPE_INT) { msg.append(getName() + " " + JaiI18N.getString("LookupDescriptor2")); return false; } return true; } /** * Performs general table lookup on an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param table The lookup table the source image is passed through. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if table is null. */ public static RenderedOp create(RenderedImage source0, LookupTableJAI table, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Lookup", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("table", table); return JAI.create("Lookup", pb, hints); } /** * Performs general table lookup on an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param table The lookup table the source image is passed through. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if table is null. */ public static RenderableOp createRenderable(RenderableImage source0, LookupTableJAI table, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Lookup", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("table", table); return JAI.createRenderable("Lookup", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/FileLoadDescriptor.java0000644000175000017500000001713410342716030027557 0ustar mathieumathieu/* * $RCSfile: FileLoadDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.3 $ * $Date: 2005-11-29 00:08:56 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.codec.ImageDecodeParam; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.io.File; import java.io.InputStream; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "FileLoad" operation. * *

      In the default instance the validateParameters() method * checks that the named file exists and is readable. If not, it will return * false, causing JAI.createNS() to throw an * IllegalArgumentException. * * In special cases like when an image is loaded from a Remote system, * the above check for existence of a file on the local system could be bypassed. * This is done by setting the Boolean variable checkFileLocally to FALSE in the ParameterBlock * *

      The allowable formats are those registered with the * com.sun.media.jai.codec.ImageCodec class. * *

      The second parameter contains an instance of * ImageDecodeParam to be used during the decoding. * It may be set to null in order to perform default * decoding, or equivalently may be omitted. * *

      The classes in the com.sun.media.jai.codec * package are not a committed part of the JAI API. Future releases * of JAI will make use of new classes in their place. This * class will change accordingly. * *

      * * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName fileload
      LocalName fileload
      Vendor com.sun.media.jai
      Description Reads an image from a file.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/FileLoadDescriptor.html
      Version 1.0
      arg0Desc The path of the file to read from.
      arg1Desc The ImageDecodeParam to use.
      arg2Desc Boolean specifying if File existence should be checked locally.

      * *

      * * * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      filename java.lang.StringNO_PARAMETER_DEFAULT
      param com.sun.media.jai.codec.ImageDecodeParamnull
      checkFileLocally java.lang.BooleanTRUE

      * * @see javax.media.jai.OperationDescriptor */ public class FileLoadDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation and * specify the parameter list for the "FileLoad" operation. */ private static final String[][] resources = { {"GlobalName", "FileLoad"}, {"LocalName", "FileLoad"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("FileLoadDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/FileLoadDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("FileLoadDescriptor1")}, {"arg1Desc", JaiI18N.getString("FileLoadDescriptor4")}, {"arg2Desc", JaiI18N.getString("FileLoadDescriptor5")} }; /** The parameter names for the "FileLoad" operation. */ private static final String[] paramNames = { "filename", "param", "checkFileLocally" }; /** The parameter class types for the "FileLoad" operation. */ private static final Class[] paramClasses = { java.lang.String.class, com.sun.media.jai.codec.ImageDecodeParam.class, java.lang.Boolean.class }; /** The parameter default values for the "FileLoad" operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT, null, Boolean.TRUE }; /** Constructor. */ public FileLoadDescriptor() { super(resources, 0, paramClasses, paramNames, paramDefaults); } /** * Validates the input parameters. * *

      In addition to the standard checks performed by the * superclass method, this method by default checks that the source file * exists and is readable. This check may be bypassed by setting the * checkFileLocally parameter to FALSE */ protected boolean validateParameters(ParameterBlock args, StringBuffer msg) { if (!super.validateParameters(args, msg)) { return false; } Boolean checkFile = (Boolean)args.getObjectParameter(2); if (checkFile.booleanValue()){ String filename = (String)args.getObjectParameter(0); File f = new File(filename); boolean fileExists = f.exists(); if (!fileExists) { // Check if the file is accessible as an InputStream resource. // This would be the case if the application and the image file // are packaged in a JAR file InputStream is = this.getClass().getClassLoader().getResourceAsStream(filename); if(is == null) { msg.append("\"" + filename + "\": " + JaiI18N.getString("FileLoadDescriptor2")); return false; } } else { // file exists if (!f.canRead()) { msg.append("\"" + filename + "\": " + JaiI18N.getString("FileLoadDescriptor3")); return false; } } } return true; } /** * Reads an image from a file. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param filename The path of the file to read from. * @param param The ImageDecodeParam to use. * May be null. * @param checkFileLocally Boolean specifying if File existence should be checked locally * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if filename is null. */ public static RenderedOp create(String filename, ImageDecodeParam param, Boolean checkFileLocally, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("FileLoad", RenderedRegistryMode.MODE_NAME); pb.setParameter("filename", filename); pb.setParameter("param", param); pb.setParameter("checkFileLocally", checkFileLocally); return JAI.create("FileLoad", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/ComplexPropertyGenerator.java0000644000175000017500000000223610203035544031061 0ustar mathieumathieu/* * $RCSfile: ComplexPropertyGenerator.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:31 $ * $State: Exp $ */ package javax.media.jai.operator; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import com.sun.media.jai.util.PropertyGeneratorImpl; /** * This property generator returns Boolean.TRUE for the * "COMPLEX" property for the rendered and renderable modes. */ class ComplexPropertyGenerator extends PropertyGeneratorImpl { /** Constructor. */ public ComplexPropertyGenerator() { super(new String[] {"COMPLEX"}, new Class[] {Boolean.class}, new Class[] {RenderedOp.class, RenderableOp.class}); } /** * Returns the specified property. * * @param name Property name. * @param op Operation node. */ public Object getProperty(String name, Object op) { validate(name, op); return name.equalsIgnoreCase("complex") ? Boolean.TRUE : java.awt.Image.UndefinedProperty; } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/MedianFilterDescriptor.java0000644000175000017500000002212310203035544030435 0ustar mathieumathieu/* * $RCSfile: MedianFilterDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:39 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.util.AreaOpPropertyGenerator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PropertyGenerator; import javax.media.jai.RenderedOp; import javax.media.jai.operator.MedianFilterShape; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "MedianFilter" operation. * *

      The "MedianFilter" operation is a non-linear filter which is * useful for removing isolated lines or pixels while preserving the * overall appearance of an image. The filter is implemented by moving * a mask over the image. For each position of the mask, the * center pixel is replaced by the median of the pixel values covered * by the mask. * *

      There are several shapes possible for the mask. The * MedianFilter operation supports three shapes, as follows: * *

      Square Mask: *

       *                       x x x
       *                       x x x
       *                       x x x
       * 
      * *

      Plus Mask: *

       *                         x
       *                       x x x
       *                         x
       * 
      * *

      X Mask: *

       *                       x   x
       *                         x
       *                       x   x
       * 
      * *

      The Median operation may also be used to compute the "separable * median" of a 3x3 or 5x5 region of pixels. The separable median is * defined as the median of the medians of each row. For example, if * the pixel values in a 3x3 window are equal to: * *

       * [ 1 2 3 ]
       * [ 5 6 7 ]
       * [ 4 8 9 ]
       * 
      * * then the overall (non-separable) median value is 5, while the * separable median is equal to the median of the three row medians: * median(1, 2, 3) = 2, median(5, 6, 7) = 6, and median(4, 8, 9) = 8, * yielding an overall median of 6. The separable median may be * obtained by specifying a mask of type MEDIAN_MASK_SQUARE_SEPARABLE. * * It should be noted that this operation automatically adds a * value of Boolean.TRUE for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL to the given * configuration so that the operation is performed * on the pixel values instead of being performed on the indices into * the color map if the source(s) have an IndexColorModel. * This addition will take place only if a value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL has not already been * provided by the user. Note that the configuration Map * is cloned before the new hint is added to it. The operation can be * smart about the value of the JAI.KEY_REPLACE_INDEX_COLOR_MODEL * RenderingHints, i.e. while the default value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL is * Boolean.TRUE, in some cases the operator could set the * default. * *

      * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName MedianFilter
      LocallName MedianFilter
      Vendor com.sun.media.jai
      Description Performs median filtering on an image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jaiapi/javax.media.jai.operator.MedianFilterDescriptor.html
      Version 1.0
      arg0Desc The shape of the mask to be used for Median Filtering.
      arg1Desc The size (width/height) of the mask to be used in Median Filtering.

      * *

      * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      maskShape javax.media.jai.operator.MedianFilterShapeMEDIAN_MASK_SQUARE
      maskSize java.lang.Integer3

      * * @see javax.media.jai.OperationDescriptor * @see MedianFilterShape */ public class MedianFilterDescriptor extends OperationDescriptorImpl { /** * Default 3x3 Windows */ /** Square shaped mask. */ public static final MedianFilterShape MEDIAN_MASK_SQUARE = new MedianFilterShape("MEDIAN_MASK_SQUARE", 1); /** Plus shaped mask. */ public static final MedianFilterShape MEDIAN_MASK_PLUS = new MedianFilterShape("MEDIAN_MASK_PLUS", 2); /** X shaped mask. */ public static final MedianFilterShape MEDIAN_MASK_X = new MedianFilterShape("MEDIAN_MASK_X", 3); /** Separable square mask. */ public static final MedianFilterShape MEDIAN_MASK_SQUARE_SEPARABLE = new MedianFilterShape("MEDIAN_MASK_SQUARE_SEPARABLE", 4); /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "MedianFilter"}, {"LocalName", "MedianFilter"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("MedianFilterDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jaiapi/javax.media.jai.operator.MedianFilterDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion2")}, {"arg0Desc", JaiI18N.getString("MedianFilterDescriptor1")}, {"arg1Desc", JaiI18N.getString("MedianFilterDescriptor2")} }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { MedianFilterShape.class, java.lang.Integer.class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "maskShape","maskSize" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { MEDIAN_MASK_SQUARE, new Integer(3) }; /** Constructor for the MedianFilterDescriptor. */ public MedianFilterDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** * Returns the minimum legal value of a specified numeric parameter * for this operation. */ public Number getParamMinValue(int index) { if (index == 0) { return null; } else if (index == 1){ return new Integer(1); } else { throw new ArrayIndexOutOfBoundsException(); } } /** * Returns the maximum legal value of a specified numeric parameter * for this operation. */ public Number getParamMaxValue(int index) { if (index == 0) { return null; } else if (index == 1){ return new Integer(Integer.MAX_VALUE); } else { throw new ArrayIndexOutOfBoundsException(); } } /** * Returns an array of PropertyGenerators implementing * property inheritance for the "MedianFilter" operation. * * @return An array of property generators. */ public PropertyGenerator[] getPropertyGenerators() { PropertyGenerator[] pg = new PropertyGenerator[1]; pg[0] = new AreaOpPropertyGenerator(); return pg; } /** * Performs median filtering on an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param maskShape The mask shape to be used for Median Filtering. * May be null. * @param maskSize The mask size to be used for Median Filtering. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, MedianFilterShape maskShape, Integer maskSize, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("MedianFilter", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("maskShape", maskShape); pb.setParameter("maskSize", maskSize); return JAI.create("MedianFilter", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/CompositeDestAlpha.java0000644000175000017500000000151510203035544027565 0ustar mathieumathieu/* * $RCSfile: CompositeDestAlpha.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:32 $ * $State: Exp $ */ package javax.media.jai.operator; import javax.media.jai.EnumeratedParameter; /** * Class used to represent the acceptable values of the "destAlpha" * parameter of the "Composite" operation. Acceptable values for the * "destAlpha" parameter are defined in the CompositeDescriptor * by the constants NO_DESTINATION_ALPHA, * DESTINATION_ALPHA_FIRST, and * DESTINATION_ALPHA_LAST. * * @since JAI 1.1 */ public final class CompositeDestAlpha extends EnumeratedParameter { CompositeDestAlpha(String name, int value) { super(name, value); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/BMPDescriptor.java0000644000175000017500000001106110203035544026507 0ustar mathieumathieu/* * $RCSfile: BMPDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:29 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.codec.SeekableStream; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "BMP" operation. * *

      The "BMP" operation reads a standard BMP input stream. * The "BMP" operation currently reads Version2, Version3 and * some of the Version 4 images, as defined in the Microsoft * Windows BMP file format. * *

      Version 4 of the BMP format allows for the specification of alpha * values, gamma values and CIE colorspaces. These are not * currently handled, but the relevant properties are emitted, if * they are available from the BMP image file. * *

      The classes in the com.sun.media.jai.codec * package are not a committed part of the JAI API. Future releases * of JAI will make use of new classes in their place. This * class will change accordingly. * *

      * * * * * * * * * *
      Resource List
      Name Value
      GlobalName BMP
      LocalName BMP
      Vendor com.sun.media.jai
      Description Reads an image from a BMP stream.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/BMPDescriptor.html
      Version 1.0
      arg0Desc The SeekableStream to read from.

      * *

      * * * * * *
      Parameter List
      Name Class TypeDefault Value
      stream com.sun.media.jai.codec.SeekableStreamNO_PARAMETER_DEFAULT

      * * @see com.sun.media.jai.codec.SeekableStream * @see javax.media.jai.OperationDescriptor */ public class BMPDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation and * specify the parameter list for the "BMP" operation. */ private static final String[][] resources = { {"GlobalName", "BMP"}, {"LocalName", "BMP"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("BMPDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/BMPDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("BMPDescriptor1")}, }; /** The parameter names for the "BMP" operation. */ private static final String[] paramNames = { "stream" }; /** The parameter class types for the "BMP" operation. */ private static final Class[] paramClasses = { com.sun.media.jai.codec.SeekableStream.class }; /** The parameter default values for the "BMP" operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT }; /** Constructor. */ public BMPDescriptor() { super(resources, 0, paramClasses, paramNames, paramDefaults); } /** * Reads an image from a BMP stream. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param stream The SeekableStream to read from. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if stream is null. */ public static RenderedOp create(SeekableStream stream, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("BMP", RenderedRegistryMode.MODE_NAME); pb.setParameter("stream", stream); return JAI.create("BMP", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/FileStoreDescriptor.java0000644000175000017500000002473510203035544030001 0ustar mathieumathieu/* * $RCSfile: FileStoreDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:35 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.ImageEncodeParam; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.io.File; import java.io.IOException; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "FileStore" operation. * * The "FileStore" operation writes an image to a given file in a specified * format using the supplied encoding parameters. * *

      In the default instance the validateParameters() method * checks for the named file to be writable if it already exists, else that * it can be created. If not, it will return false, causing * JAI.createNS() to throw an * IllegalArgumentException. * *

      In special cases such as an image being written to a remote system, * the above check for existence of a file on the local system should be * bypassed. This can be accomplished by setting the Boolean * variable checkFileLocally to FALSE in the * ParameterBlock. *

      The third parameter contains an instance of * ImageEncodeParam to be used during the decoding. It * may be set to null in order to perform default * encoding, or equivalently may be omitted. If * non-null, it must be of the correct class type for the * selected format. * *

      The requested file path must be writable. * *

      The classes in the com.sun.media.jai.codec * package are not a committed part of the JAI API. Future releases * of JAI will make use of new classes in their place. This * class will change accordingly. * *

      * * * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName filestore
      LocalName filestore
      Vendor com.sun.media.jai
      Description Stores an image to a file.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/FileStoreDescriptor.html
      Version 1.0
      arg0Desc The path of the file to write to.
      arg1Desc The format of the file.
      arg2Desc The encoding parameters.
      arg3Desc Boolean specifying whether check for file creation / writing locally should be done.

      * *

      * * * * * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      filename java.lang.StringNO_PARAMETER_DEFAULT
      format java.lang.String"tiff"
      param com.sun.media.jai.codec.ImageEncodeParamnull
      checkFileLocally java.lang.BooleanTRUE

      * * @see javax.media.jai.OperationDescriptor */ public class FileStoreDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation and * specify the parameter list for the "FileStore" operation. */ private static final String[][] resources = { {"GlobalName", "FileStore"}, {"LocalName", "FileStore"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("FileStoreDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/FileStoreDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("FileStoreDescriptor1")}, {"arg1Desc", JaiI18N.getString("FileStoreDescriptor2")}, {"arg2Desc", JaiI18N.getString("FileStoreDescriptor3")}, {"arg3Desc", JaiI18N.getString("FileStoreDescriptor11")} }; /** The parameter names for the "FileStore" operation. */ private static final String[] paramNames = { "filename", "format", "param", "checkFileLocally" }; /** The parameter class types for the "FileStore" operation. */ private static final Class[] paramClasses = { java.lang.String.class, java.lang.String.class, com.sun.media.jai.codec.ImageEncodeParam.class, java.lang.Boolean.class }; /** The parameter default values for the "FileStore" operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT, "tiff", null, Boolean.TRUE }; private static final String[] supportedModes = { "rendered" }; /** Constructor. */ public FileStoreDescriptor() { super(resources, supportedModes, 1, paramNames, paramClasses, paramDefaults, null); } /** * Validates the input source and parameters. * *

      In addition to the standard checks performed by the * superclass method, this method checks that the format name is * recognized and is capable of encoding the source image using * the encoding parameter "param", if non-null, * ans that the output file path "filename" is writable. */ public boolean validateArguments(String modeName, ParameterBlock args, StringBuffer msg) { if (!super.validateArguments(modeName, args, msg)) { return false; } if (!modeName.equalsIgnoreCase("rendered")) return true; // Retrieve the format. String format = (String)args.getObjectParameter(1); // Retrieve the associated ImageCodec. ImageCodec codec = ImageCodec.getCodec(format); // Check for null codec. if (codec == null) { msg.append(getName() + " " + JaiI18N.getString("FileStoreDescriptor4")); return false; } // Retrieve the ImageEncodeParam object. ImageEncodeParam param = (ImageEncodeParam)args.getObjectParameter(2); RenderedImage src = args.getRenderedSource(0); // Verify that the image can be encoded with null parameters. if (!codec.canEncodeImage(src, param)) { msg.append(getName() + " " + JaiI18N.getString("FileStoreDescriptor5")); return false; } // Retrieve the file path. String pathName = (String)args.getObjectParameter(0); if (pathName == null) { msg.append(getName() + " " + JaiI18N.getString("FileStoreDescriptor6")); return false; } // Perform non-destructive test that the file // may be created and written. Boolean checkFile = (Boolean)args.getObjectParameter(3); if (checkFile.booleanValue()) { try { File f = new File(pathName); if (f.exists()) { if (!f.canWrite()) { // Cannot write to existing file. msg.append(getName() + " " + JaiI18N.getString("FileStoreDescriptor7")); return false; } } else { if (!f.createNewFile()) { // Cannot create file. msg.append(getName() + " " + JaiI18N.getString("FileStoreDescriptor8")); return false; } f.delete(); } } catch (IOException ioe) { // I/O exception during createNewFile(). msg.append(getName() + " " + JaiI18N.getString("FileStoreDescriptor9") + " " + ioe.getMessage()); return false; } catch (SecurityException se) { // Security exception during exists(), canWrite(), // createNewFile(), or delete(). msg.append(getName() + " " + JaiI18N.getString("FileStoreDescriptor10") + " " + se.getMessage()); return false; } } return true; } /** * Returns true indicating that the operation should be rendered * immediately during a call to JAI.create(). * * @see javax.media.jai.OperationDescriptor */ public boolean isImmediate() { return true; } /** * Stores an image to a file. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param filename The path of the file to write to. * @param format The format of the file. * May be null. * @param param The encoding parameters. * May be null. * @param checkFileLocally Boolean specifying whether check for file * creation / writing locally should be done. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if filename is null. */ public static RenderedOp create(RenderedImage source0, String filename, String format, ImageEncodeParam param, Boolean checkFileLocally, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("FileStore", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("filename", filename); pb.setParameter("format", format); pb.setParameter("param", param); pb.setParameter("checkFileLocally", checkFileLocally); return JAI.create("FileStore", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/ColorQuantizerDescriptor.java0000644000175000017500000003533110203035544031060 0ustar mathieumathieu/* * $RCSfile: ColorQuantizerDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:31 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderedOp; import javax.media.jai.ROI; import javax.media.jai.util.Range; import javax.media.jai.registry.RenderedRegistryMode; /** * This OperationDescriptor defines the "ColorQuantizer" * operation. * *

      This operation generates an optimal lookup table (LUT) based on * the provided 3-band RGB source image by executing a color * quantization algorithm. This LUT is stored in the property * "JAI.LookupTable" that has a type of LookupTableJAI. * Thus, it can be retrieved by means of getProperty. * This LUT can be further utilized in other operations such as * "errordiffusion" to convert the 3-band RGB image into a high-quality * color-indexed image. The computation of the LUT can be deferred by * defining a DeferredProperty from the property * "JAI.LookupTable" and providing that as the parameter value for * "errordiffusion". This operation also creates a color-indexed * destination image based on the nearest distance classification (without * dithering). However, the quality of this classification result may * not be as good as the result of "errordiffusion". * *

      The supported source image data type is implementation-dependent. * For example, the Sun implementation will support only the byte type. * *

      The data set used in the color quantization can be defined by * the optional parameters xPeriod, yPeriod * and ROI. If these parameters are provided, the pixels in * the subsampled image (and in the ROI) will be used to compute the * LUT. * *

      Three built-in color quantization algorithms are supported by * this operation: Paul Heckbert's median-cut algorithm, Anthony Dekker's * NeuQuant algorithm, and the Oct-Tree color quantization algorithm of * Gervautz and Purgathofer. * *

      The median-cut color quantization computes the 3D color histogram * first, then chooses and divides the largest color cube (in number of pixels) * along the median, until the required number of clusters is obtained * or all the cubes are not separable. The NeuQuant algorithm creates * the cluster centers using Kohonen's self-organizing neural network. * The Oct-Tree color quantization constructs an oct-tree of the * color histogram, then repeatedly merges the offspring into the parent * if they contain a number of pixels smaller than a threshold. With the * equivalent parameters, the median-cut algorithm is the fastest, and the * NeuQuant algorithm is the slowest. However, NeuQuant algorithm can * still generate a good result with a relatively high subsample rate, which * is useful for large images. * In these three algorithms, the Oct-Tree algorithm is the most space * consuming one. For further details of these algorithms, * please refer to the following references: * * * * * * * * * * * * * * * * *
      AlgorithmReferences
      Median-CutColor Image Quantization for Frame Buffer * Display, Paul Heckbert, SIGGRAPH proceedings, 1982, pp. 297-307 *
      NeuQuantKohonen Neural Networks for Optimal Colour Quantization, * Anthony Dekker, In Network: Computation in Neural Systems, * Volume 5, Institute of Physics Publishing, 1994, pp 351-367. *
      Oct-TreeInteractive Computer Graphics: Functional, Procedural, and * Device-Level Methods by Peter Burger and Duncan Gillis, * Addison-Wesley, 1989, pp 345. *
      * *

      The generated LUT may have fewer entries than expected. For * example, the source image might not have as many colors as expected. * In the oct-tree algorithm, all the offspring of a node are merged * if they contain a number of pixels smaller than a threshold. This * may result in slightly fewer colors than expected. * *

      The learning procedure of the NeuQuant algorithm randomly goes * through all the pixels in the training data set. To simplify and * speed up the implementation, the bounding rectangle of the * provided ROI may be used (by the implementation) to define the * training data set instead of the ROI itself. * *

      * * * * * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName ColorQuantizer
      LocalName ColorQuantizer
      Vendor com.sun.media.jai
      Description Generates an optimal LUT by executing a * color quantization algorithm, and a * color-indexed image by the nearest distance * classification.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ColorQuantizerDescriptor.html
      Version 1.1
      arg0Desc The color quantization algorithm name. One of * ColorQuantizerDescriptor.MEDIANCUT, * ColorQuantizerDescriptor.NEUQUANT, or * ColorQuantizerDescriptor.OCTTREE
      arg1Desc The maximum color number, that is, the expected * number of colors in the result image.
      arg2Desc This is an algorithm-dependent parameter. For * the median-cut color quantization, it is the * maximum size of the three-dimensional * histogram. * For the neuquant color quantization, it is the * number of cycles. For the oct-tree color * quantization, it is the maximum size of the * oct-tree.
      arg3Desc The ROI in which the pixels are involved into * the color quantization.
      arg4Desc The subsample rate in x direction.
      arg4Desc The subsample rate in y direction.

      * *

      * * * * * * * * * * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      quantizationAlgorithmjavax.media.jai.operator.ColorQuantizerTypeColorQuantizerDescriptor.MEDIANCUT
      maxColorNum java.lang.Integer256
      upperBound java.lang.Integer32768 for median-cut, 100 for neuquant, * 65536 for oct-tree
      roi javax.media.jai.ROInull
      xPeriod java.lang.Integer1
      yPeriod java.lang.Integer1

      * * @see javax.media.jai.ROI * @see javax.media.jai.OperationDescriptor * * @since JAI 1.1.2 */ public class ColorQuantizerDescriptor extends OperationDescriptorImpl { /** The predefined color quantization algorithms. */ /** The pre-defined median-cut color quantization algorithm. */ public static final ColorQuantizerType MEDIANCUT = new ColorQuantizerType("MEDIANCUT", 1); /** The pre-defined NeuQuant color quantization algorithm. */ public static final ColorQuantizerType NEUQUANT = new ColorQuantizerType("NEUQUANT", 2); /** The pre-defined Oct-Tree color quantization algorithm. */ public static final ColorQuantizerType OCTTREE = new ColorQuantizerType("OCTTREE", 3); /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "ColorQuantizer"}, {"LocalName", "ColorQuantizer"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("ColorQuantizerDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ColorQuantizerDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion2")}, {"arg0Desc", JaiI18N.getString("ColorQuantizerDescriptor1")}, {"arg1Desc", JaiI18N.getString("ColorQuantizerDescriptor2")}, {"arg2Desc", JaiI18N.getString("ColorQuantizerDescriptor3")}, {"arg3Desc", JaiI18N.getString("ColorQuantizerDescriptor4")}, {"arg4Desc", JaiI18N.getString("ColorQuantizerDescriptor5")}, {"arg5Desc", JaiI18N.getString("ColorQuantizerDescriptor6")}, }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "quantizationAlgorithm", "maxColorNum", "upperBound", "roi", "xPeriod", "yPeriod" }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { javax.media.jai.operator.ColorQuantizerType.class, java.lang.Integer.class, java.lang.Integer.class, javax.media.jai.ROI.class, java.lang.Integer.class, java.lang.Integer.class }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { MEDIANCUT, new Integer(256), null, null, new Integer(1), new Integer(1) }; private static final String[] supportedModes = { "rendered" }; /** Constructor. */ public ColorQuantizerDescriptor() { super(resources, supportedModes, 1, paramNames, paramClasses, paramDefaults, null); } /** * Returns the minimum legal value of a specified numeric parameter * for this operation. */ public Range getParamValueRange(int index) { switch (index) { case 1: case 2: case 4: case 5: return new Range(Integer.class, new Integer(1), null); } return null; } /** * Returns true if this operation is capable of handling * the input parameters. * *

      In addition to the default validations done in the super class, * this method verifies that the provided quantization algorithm is one of * the three predefined algorithms in this class. * * @throws IllegalArgumentException If args is null. * @throws IllegalArgumentException If msg is null * and the validation fails. */ protected boolean validateParameters(String modeName, ParameterBlock args, StringBuffer msg) { if ( args == null || msg == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if (!super.validateParameters(modeName, args, msg)) return false; ColorQuantizerType algorithm = (ColorQuantizerType)args.getObjectParameter(0); if (algorithm != MEDIANCUT && algorithm != NEUQUANT && algorithm != OCTTREE) { msg.append(getName() + " " + JaiI18N.getString("ColorQuantizerDescriptor7")); return false; } Integer secondOne = (Integer)args.getObjectParameter(2); if (secondOne == null) { int upperBound = 0; if (algorithm.equals(MEDIANCUT)) upperBound = 32768; else if (algorithm.equals(NEUQUANT)) // set the cycle for train to 100 upperBound = 100; else if (algorithm.equals(OCTTREE)) // set the maximum tree size to 65536 upperBound = 65536; args.set(upperBound, 2); } return true; } /** * Color quantization on the provided image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param algorithm The algorithm to be chosen. May be null. * @param maxColorNum The maximum color number. May be null. * @param upperBound An algorithm-dependent parameter. See the parameter * table above. May be null. * @param roi The region of interest. May be null. * @param xPeriod The X subsample rate. May be null. * @param yPeriod The Y subsample rate. May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, ColorQuantizerType algorithm, Integer maxColorNum, Integer upperBound, ROI roi, Integer xPeriod, Integer yPeriod, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("ColorQuantizer", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("quantizationAlgorithm", algorithm); pb.setParameter("maxColorNum", maxColorNum); pb.setParameter("upperBound", upperBound); pb.setParameter("roi", roi); pb.setParameter("xPeriod", xPeriod); pb.setParameter("yPeriod", yPeriod); return JAI.create("ColorQuantizer", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/ErrorDiffusionDescriptor.java0000644000175000017500000001275710203035544031046 0ustar mathieumathieu/* * $RCSfile: ErrorDiffusionDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:35 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.JAI; import javax.media.jai.KernelJAI; import javax.media.jai.LookupTableJAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.ROI; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "ErrorDiffusion" * operation. * *

      The "ErrorDiffusion" operation performs color quantization by * finding the nearest color to each pixel in a supplied color map * and "diffusing" the color quantization error below and to the right * of the pixel. * *

      * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName ErrorDiffusion
      LocalName ErrorDiffusion
      Vendor com.sun.media.jai
      Description Performs error diffusion color quantization * using a specified color map and * error filter.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ErrorDiffusionDescriptor.html
      Version 1.0
      arg0Desc The color map.
      arg1Desc The error filter kernel.

      * *

      * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      colorMap javax.media.jai.LookupTableJAINO_PARAMETER_DEFAULT
      errorKernel javax.media.jai.KernelJAIjavax.media.jai.KernelJAI.ERROR_FILTER_FLOYD_STEINBERG

      * * @see javax.media.jai.LookupTableJAI * @see javax.media.jai.KernelJAI * @see javax.media.jai.ColorCube * @see javax.media.jai.OperationDescriptor */ public class ErrorDiffusionDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation and * specify the parameter list for the "ErrorDiffusion" operation. */ private static final String[][] resources = { {"GlobalName", "ErrorDiffusion"}, {"LocalName", "ErrorDiffusion"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("ErrorDiffusionDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ErrorDiffusionDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("ErrorDiffusionDescriptor1")}, {"arg1Desc", JaiI18N.getString("ErrorDiffusionDescriptor2")} }; /** The parameter names for the "ErrorDiffusion" operation. */ private static final String[] paramNames = { "colorMap", "errorKernel" }; /** The parameter class types for the "ErrorDiffusion" operation. */ private static final Class[] paramClasses = { javax.media.jai.LookupTableJAI.class, javax.media.jai.KernelJAI.class }; /** The parameter default values for the "ErrorDiffusion" operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT, // Default error filter to Floyd-Steinberg. KernelJAI.ERROR_FILTER_FLOYD_STEINBERG }; /** Constructor. */ public ErrorDiffusionDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** * Performs error diffusion color quantization using a specified color map and error filter. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param colorMap The color map. * @param errorKernel The error filter kernel. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if colorMap is null. */ public static RenderedOp create(RenderedImage source0, LookupTableJAI colorMap, KernelJAI errorKernel, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("ErrorDiffusion", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("colorMap", colorMap); pb.setParameter("errorKernel", errorKernel); return JAI.create("ErrorDiffusion", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/ThresholdDescriptor.java0000644000175000017500000002242410203035544030032 0ustar mathieumathieu/* * $RCSfile: ThresholdDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:45 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Threshold" operation. * *

      The Threshold operation takes one rendered image, and maps all * the pixels of this image whose value falls within a specified range * to a specified constant. The range is specified by a low value and * a high value. * *

      If the number of elements supplied via the "high", "low", and * "constants" arrays are less than the number of bands of the source * image, then the element from entry 0 is applied to all the bands. * Otherwise, the element from a different entry is applied to its * corresponding band. * *

      The destination pixel values are defined by the pseudocode: *

       * lowVal = (low.length < dstNumBands) ?
       *          low[0] : low[b];
       * highVal = (high.length < dstNumBands) ?
       *           high[0] : high[b];
       * const = (constants.length < dstNumBands) ?
       *           constants[0] : constants[b];
       *
       * if (src[x][y][b] >= lowVal && src[x][y][b] <= highVal) {
       *     dst[x][y][b] = const;
       * } else {
       *     dst[x][y][b] = src[x][y][b];
       * }
       * 
      * *

      * * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName Threshold
      LocalName Threshold
      Vendor com.sun.media.jai
      Description Maps the pixels whose value falls between a low * value and a high value to a constant.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ThresholdDescriptor.html
      Version 1.0
      arg0Desc The low value.
      arg1Desc The high value.
      arg2Desc The constant the pixels are mapped to.

      * *

      * * * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      low double[]NO_PARAMETER_DEFAULT
      high double[]NO_PARAMETER_DEFAULT
      constants double[]NO_PARAMETER_DEFAULT

      * * @see javax.media.jai.OperationDescriptor */ public class ThresholdDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Threshold"}, {"LocalName", "Threshold"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("ThresholdDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ThresholdDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("ThresholdDescriptor1")}, {"arg1Desc", JaiI18N.getString("ThresholdDescriptor2")}, {"arg2Desc", JaiI18N.getString("ThresholdDescriptor3")} }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "low", "high", "constants" }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { double[].class, double[].class, double[].class }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT, NO_PARAMETER_DEFAULT, NO_PARAMETER_DEFAULT }; /** Constructor. */ public ThresholdDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** Returns true since renderable operation is supported. */ public boolean isRenderableSupported() { return true; } /** Validates input parameters. */ protected boolean validateParameters(ParameterBlock args, StringBuffer msg) { int numParams = args.getNumParameters(); if (numParams < 3) { msg.append(getName() + " " + JaiI18N.getString("ThresholdDescriptor4")); return false; } for (int i = 0; i < 3; i++) { Object p = args.getObjectParameter(i); if (p == null) { msg.append(getName() + " " + JaiI18N.getString("ThresholdDescriptor5")); return false; } if (!(p instanceof double[])) { msg.append(getName() + " " + JaiI18N.getString("ThresholdDescriptor6")); return false; } if (((double[])p).length < 1) { msg.append(getName() + " " + JaiI18N.getString("ThresholdDescriptor7")); return false; } } return true; } /** * Maps the pixels whose value falls between a low value and a high value to a constant. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param low The low value. * @param high The high value. * @param constants The constant the pixels are mapped to. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if low is null. * @throws IllegalArgumentException if high is null. * @throws IllegalArgumentException if constants is null. */ public static RenderedOp create(RenderedImage source0, double[] low, double[] high, double[] constants, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Threshold", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("low", low); pb.setParameter("high", high); pb.setParameter("constants", constants); return JAI.create("Threshold", pb, hints); } /** * Maps the pixels whose value falls between a low value and a high value to a constant. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param low The low value. * @param high The high value. * @param constants The constant the pixels are mapped to. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if low is null. * @throws IllegalArgumentException if high is null. * @throws IllegalArgumentException if constants is null. */ public static RenderableOp createRenderable(RenderableImage source0, double[] low, double[] high, double[] constants, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Threshold", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("low", low); pb.setParameter("high", high); pb.setParameter("constants", constants); return JAI.createRenderable("Threshold", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/MultiplyComplexDescriptor.java0000644000175000017500000002001710203035544031241 0ustar mathieumathieu/* * $RCSfile: MultiplyComplexDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:40 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PropertyGenerator; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "MultiplyComplex" * operation. * *

      The "MultiplyComplex" operation multiplies two images representing * complex data. The source images must each contain an even number of bands * with the even-indexed bands (0, 2, ...) representing the real and the * odd-indexed bands (1, 3, ...) the imaginary parts of each pixel. The * destination image similarly contains an even number of bands with the * same interpretation and with contents defined by: * *

       * a = src0[x][y][2*k];
       * b = src0[x][y][2*k+1];
       * c = src1[x][y][2*k];
       * d = src1[x][y][2*k+1];
       *
       * dst[x][y][2*k]   = a*c - b*d;
       * dst[x][y][2*k+1] = a*d + b*c;
       * 
      * * where 0 <= k < numBands/2. * * By default, the number of bands of the destination image is the * the minimum of the number of bands of the two sources, and the * data type is the biggest data type of the sources. * However, the number of destination bands can be specified to be * M = 2*L through an ImageLayout hint, when * one source image has 2 bands and the other has N = 2*K bands * where K > 1, with a natural restriction 1 <= L <= K. * In such a special case each of the first L complex components * in the N-band source will be multiplied by the single complex * component in the 1-band source. * *

      If the result of the operation underflows/overflows the * minimum/maximum value supported by the destination data type, then it will * be clamped to the minimum/maximum value respectively. * *

      "MultiplyComplex" defines a PropertyGenerator that sets the "COMPLEX" * property of the image to java.lang.Boolean.TRUE, which may * be retrieved by calling the getProperty() method with * "COMPLEX" as the property name. * *

      * * * * * * * * *
      Resource List
      Name Value
      GlobalName MultiplyComplex
      LocalName MultiplyComplex
      Vendor com.sun.media.jai
      Description Computes the complex product of two images.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/MultiplyComplexDescriptor.html
      Version 1.0

      * *

      No parameters are needed for the "MultiplyComplex" operation. * * @see javax.media.jai.OperationDescriptor */ public class MultiplyComplexDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "MultiplyComplex"}, {"LocalName", "MultiplyComplex"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("MultiplyComplexDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/MultiplyComplexDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")} }; private static final String[] supportedModes = { "rendered", "renderable" }; /** Constructor. */ public MultiplyComplexDescriptor() { super(resources, supportedModes, 2, null, null, null, null); } /** * Validates the input sources. * *

      In addition to the standard checks performed by the * superclass method, this method checks that the source images * each have an even number of bands. */ protected boolean validateSources(String modeName, ParameterBlock args, StringBuffer msg) { if (!super.validateSources(modeName, args, msg)) { return false; } if (!modeName.equalsIgnoreCase("rendered")) return true; RenderedImage src1 = args.getRenderedSource(0); RenderedImage src2 = args.getRenderedSource(1); if (src1.getSampleModel().getNumBands() % 2 != 0 || src2.getSampleModel().getNumBands() % 2 != 0) { msg.append(getName() + " " + JaiI18N.getString("MultiplyComplexDescriptor1")); return false; } return true; } /** * Returns an array of PropertyGenerators implementing * property inheritance for the "MultiplyComplex" operation. * * @return An array of property generators. */ public PropertyGenerator[] getPropertyGenerators(String modeName) { PropertyGenerator[] pg = new PropertyGenerator[1]; pg[0] = new ComplexPropertyGenerator(); return pg; } /** * Computes the complex product of two images. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param source1 RenderedImage source 1. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if source1 is null. */ public static RenderedOp create(RenderedImage source0, RenderedImage source1, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("MultiplyComplex", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setSource("source1", source1); return JAI.create("MultiplyComplex", pb, hints); } /** * Computes the complex product of two images. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param source1 RenderableImage source 1. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if source1 is null. */ public static RenderableOp createRenderable(RenderableImage source0, RenderableImage source1, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("MultiplyComplex", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setSource("source1", source1); return JAI.createRenderable("MultiplyComplex", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/DFTDescriptor.java0000644000175000017500000003167710203035544026525 0ustar mathieumathieu/* * $RCSfile: DFTDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:33 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.util.PropertyGeneratorImpl; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.EnumeratedParameter; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PropertyGenerator; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.operator.DFTDataNature; import javax.media.jai.operator.DFTScalingType; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * This property generator computes the properties for the operation * "DFT" dynamically. */ class DFTPropertyGenerator extends PropertyGeneratorImpl { /** Constructor. */ public DFTPropertyGenerator() { super(new String[] {"COMPLEX"}, new Class[] {Boolean.class}, new Class[] {RenderedOp.class, RenderableOp.class}); } /** * Returns the specified property. * * @param name Property name. * @param opNode Operation node. */ public Object getProperty(String name, Object opNode) { validate(name, opNode); if (name.equalsIgnoreCase("complex")) { if(opNode instanceof RenderedOp) { RenderedOp op = (RenderedOp)opNode; ParameterBlock pb = op.getParameterBlock(); DFTDataNature dataNature = (DFTDataNature)pb.getObjectParameter(1); return dataNature.equals(DFTDescriptor.COMPLEX_TO_REAL) ? Boolean.FALSE : Boolean.TRUE; } else if(opNode instanceof RenderableOp) { RenderableOp op = (RenderableOp)opNode; ParameterBlock pb = op.getParameterBlock(); DFTDataNature dataNature = (DFTDataNature)pb.getObjectParameter(1); return dataNature.equals(DFTDescriptor.COMPLEX_TO_REAL) ? Boolean.FALSE : Boolean.TRUE; } } return java.awt.Image.UndefinedProperty; } } /** * An OperationDescriptor describing the "DFT" operation. * *

      The "DFT" operation computes the discrete Fourier transform of an * image. A negative exponential is used as the basis function for the * transform. The operation supports real-to-complex, complex-to-complex, and * complex-to-real transforms. A complex image must have an even number of * bands, with the even bands (0, 2, ...) representing the real parts and the * odd bands (1, 3, ...) the imaginary parts of each complex pixel. * *

      The nature of the source and destination data is specified by the * "dataNature" operation parameter. If the source data are complex then * the number of bands in the source image must be a multiple of 2. The * number of bands in the destination must match that which would be expected * given the number of bands in the source image and the specified nature * of the source and destination data. If the source image is real then the * number of bands in the destination will be twice that in the source. If * the destination image is real than the number of bands in the destination * will be half that in the source. Otherwise the number of bands in the * source and destination must be equal. * *

      If an underlying fast Fourier transform (FFT) implementation is used * which requires that the image dimensions be powers of 2, then the width * and height may each be increased to the power of 2 greater than or equal * to the original width and height, respectively. * *

      "DFT" defines a PropertyGenerator that sets the "COMPLEX" property of * the image to java.lang.Boolean.FALSE if the "dataNature" * operation parameter is equal to COMPLEX_TO_REAL and to * java.lang.Boolean.TRUE otherwise. The value of this property * may be retrieved by calling the getProperty() method with * "COMPLEX" as the property name. * *

      * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName DFT
      LocalName DFT
      Vendor com.sun.media.jai
      Description Computes the discrete Fourier transform of * an image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/DFTDescriptor.html
      Version 1.0
      arg0Desc The type of scaling to be used.
      arg1Desc The nature of the data.

      * *

      * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      scalingType javax.media.jai.operator.DFTScalingTypeDFTDescriptor.SCALING_NONE
      dataNature javax.media.jai.operator.DFTDataNatureDFTDescriptor.REAL_TO_COMPLEX

      * * @see DFTDataNature * @see DFTScalingType * @see javax.media.jai.OperationDescriptor */ public class DFTDescriptor extends OperationDescriptorImpl { /** * A flag indicating that the transform is not to be scaled. */ public static final DFTScalingType SCALING_NONE = new DFTScalingType("SCALING_NONE", 1); /** * A flag indicating that the transform is to be scaled by the square * root of the product of its dimensions. */ public static final DFTScalingType SCALING_UNITARY = new DFTScalingType("SCALING_UNITARY", 2); /** * A flag indicating that the transform is to be scaled by the product * of its dimensions. */ public static final DFTScalingType SCALING_DIMENSIONS = new DFTScalingType("SCALING_DIMENSIONS", 3); /** * A flag indicating that the source data are real and the destination * data complex. */ public static final DFTDataNature REAL_TO_COMPLEX = new DFTDataNature("REAL_TO_COMPLEX", 1); /** * A flag indicating that the source and destination data are both complex. */ public static final DFTDataNature COMPLEX_TO_COMPLEX = new DFTDataNature("COMPLEX_TO_COMPLEX", 2); /** * A flag indicating that the source data are complex and the destination * data real. */ public static final DFTDataNature COMPLEX_TO_REAL = new DFTDataNature("COMPLEX_TO_REAL", 3); /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "DFT"}, {"LocalName", "DFT"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("DFTDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/DFTDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion2")}, {"arg0Desc", JaiI18N.getString("DFTDescriptor1")}, {"arg1Desc", JaiI18N.getString("DFTDescriptor2")} }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { DFTScalingType.class, DFTDataNature.class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "scalingType", "dataNature" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { SCALING_NONE, REAL_TO_COMPLEX }; private static final String[] supportedModes = { "rendered", "renderable" }; /** Constructor. */ public DFTDescriptor() { super(resources, supportedModes, 1, paramNames, paramClasses, paramDefaults, null); } /** * Validates the input source and parameters. * *

      In addition to the standard checks performed by the * superclass method, this method checks that "scalingType" is one * of SCALING_NONE, SCALING_UNITARY, or * SCALING_DIMENSIONS, and that "dataNature" is one * of REAL_TO_COMPLEX, * COMPLEX_TO_COMPLEX, or * COMPLEX_TO_REAL. Also, if "dataNature" is * COMPLEX_TO_COMPLEX or COMPLEX_TO_REAL * the number of source bands must be even. */ public boolean validateArguments(String modeName, ParameterBlock args, StringBuffer msg) { if (!super.validateArguments(modeName, args, msg)) { return false; } if (!modeName.equalsIgnoreCase("rendered")) return true; // Check source band count: must be even for a complex source. EnumeratedParameter dataNature = (EnumeratedParameter)args.getObjectParameter(1); if (!dataNature.equals(REAL_TO_COMPLEX)) { RenderedImage src = args.getRenderedSource(0); if (src.getSampleModel().getNumBands() % 2 != 0) { msg.append(getName() + " " + JaiI18N.getString("DFTDescriptor5")); return false; } } return true; } /** * Returns an array of PropertyGenerators implementing * property inheritance for the "DFT" operation. * * @return An array of property generators. */ public PropertyGenerator[] getPropertyGenerators(String modeName) { PropertyGenerator[] pg = new PropertyGenerator[1]; pg[0] = new DFTPropertyGenerator(); return pg; } /** * Computes the discrete Fourier transform of an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param scalingType The type of scaling to perform. * May be null. * @param dataNature The nature of the data. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, DFTScalingType scalingType, DFTDataNature dataNature, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("DFT", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("scalingType", scalingType); pb.setParameter("dataNature", dataNature); return JAI.create("DFT", pb, hints); } /** * Computes the discrete Fourier transform of an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param scalingType The type of scaling to perform. * May be null. * @param dataNature The nature of the data. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, DFTScalingType scalingType, DFTDataNature dataNature, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("DFT", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("scalingType", scalingType); pb.setParameter("dataNature", dataNature); return JAI.createRenderable("DFT", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/JaiI18N.java0000644000175000017500000000074710203035544025146 0ustar mathieumathieu/* * $RCSfile: JaiI18N.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:38 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.util.PropertyUtil; class JaiI18N { static String packageName = "javax.media.jai.operator"; public static String getString(String key) { return PropertyUtil.getString(packageName, key); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/AddCollectionDescriptor.java0000644000175000017500000001707610203035544030611 0ustar mathieumathieu/* * $RCSfile: AddCollectionDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:28 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import java.util.Collection; import java.util.Iterator; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.ParameterListDescriptor; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the * "AddCollection" operation. * *

      The AddCollection operation takes a collection of rendered * or renderable source images, and adds every set of pixels, one from * each source image of the corresponding position and band. No * additional parameters are required. * *

      There is no restriction on the actual class type used to * represent the source collection, but all elements of the collection * must be instances of RenderedImage or * RenderableImage depending on the mode. The number of * images in the collection may vary from 2 to n. The source images * may have different numbers of bands and data types. * *

      By default, the destination image bounds are the intersection * of all of the source image bounds. If any of the two sources are * completely disjoint, the destination will have a width and a height * of 0. The number of bands of the destination image is equal to * the minimum number of bands of all the sources, and the data type is * the biggest data type of all the sources. If the result of the * operation underflows/overflows the minimum/maximum value supported * by the destination data type, then it will be clamped to the * minimum/maximum value respectively. * *

      The destination pixel values are defined by the pseudocode: *

       * dst[x][y][b] = 0;
       * for (int i = 0; i < numSources; i++) {
       *     dst[x][y][b] += srcs[i][x][y][b];
       * }
       * 
      * *

      * * * * * * * * *
      Resource List
      Name Value
      GlobalName AddCollection
      LocalName AddCollection
      Vendor com.sun.media.jai
      Description Adds a collection of rendered images.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/AddCollectionDescriptor.html
      Version 1.0

      * *

      No parameters are needed for this operation. * * @see java.awt.image.RenderedImage * @see java.awt.image.renderable.RenderableImage * @see java.util.Collection * @see javax.media.jai.OperationDescriptor */ public class AddCollectionDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "AddCollection"}, {"LocalName", "AddCollection"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("AddCollectionDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/AddCollectionDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")} }; /** The source class list for this operation. */ private static final Class[][] sourceClasses = { { java.util.Collection.class }, { java.util.Collection.class } }; private static final String[] supportedModes = { "rendered", "renderable" }; /** Constructor. */ public AddCollectionDescriptor() { super(resources, supportedModes, null, sourceClasses, (ParameterListDescriptor)null); } /** Validates input source collection. */ protected boolean validateSources(String modeName, ParameterBlock args, StringBuffer msg) { if (!super.validateSources(modeName, args, msg)) { return false; } Collection col = (Collection)args.getSource(0); if (col.size() < 2) { msg.append(getName() + " " + JaiI18N.getString("AddCollectionDescriptor1")); return false; } Iterator iter = col.iterator(); if (modeName.equalsIgnoreCase(RenderedRegistryMode.MODE_NAME)) { while (iter.hasNext()) { Object o = iter.next(); if (!(o instanceof RenderedImage)) { msg.append(getName() + " " + JaiI18N.getString("AddCollectionDescriptor2")); return false; } } } else if (modeName.equalsIgnoreCase(RenderableRegistryMode.MODE_NAME)) { while (iter.hasNext()) { Object o = iter.next(); if (!(o instanceof RenderableImage)) { msg.append(getName() + " " + JaiI18N.getString("AddCollectionDescriptor3")); return false; } } } return true; } /** * Adds a collection of images. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 Collection source 0. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(Collection source0, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("AddCollection", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); return JAI.create("AddCollection", pb, hints); } /** * Adds a collection of images. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 Collection source 0. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(Collection source0, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("AddCollection", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); return JAI.createRenderable("AddCollection", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/OrderedDitherDescriptor.java0000644000175000017500000002402410203035544030620 0ustar mathieumathieu/* * $RCSfile: OrderedDitherDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:41 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.ColorCube; import javax.media.jai.JAI; import javax.media.jai.KernelJAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.ROI; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "OrderedDither" * operation. * *

      The "OrderedDither" operation performs color quantization by * finding the nearest color to each pixel in a supplied color cube * and "shifting" the resulting index value by a pseudo-random amount * determined by the values of a supplied dither mask. * *

      The dither mask is supplied as an array of KernelJAI * objects the length of which must equal the number of bands in the * image. Each element of the array is a KernelJAI object * which represents the dither mask matrix for the corresponding band. * All KernelJAI objects in the array must have the same * dimensions and contain floating point values greater than or equal * to 0.0 and less than or equal to 1.0. * *

      For all integral data types, the source image samples are presumed * to occupy the full range of the respective types. For floating point data * types it is assumed that the data samples have been scaled to the range * [0.0, 1.0]. * *

      * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName OrderedDither
      LocalName OrderedDither
      Vendor com.sun.media.jai
      Description Performs ordered dither color quantization * using a specified color cube and * dither mask.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/OrderedDitherDescriptor.html
      Version 1.0
      arg0Desc The color cube.
      arg1Desc The dither mask.

      * *

      * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      colorMap javax.media.jai.ColorCubeColorCube.BYTE_496
      ditherMask javax.media.jai.KernelJAI[]KernelJAI.DITHER_MASK_443

      * * @see javax.media.jai.KernelJAI * @see javax.media.jai.ColorCube * @see javax.media.jai.OperationDescriptor */ public class OrderedDitherDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "OrderedDither"}, {"LocalName", "OrderedDither"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("OrderedDitherDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/OrderedDitherDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("OrderedDitherDescriptor1")}, {"arg1Desc", JaiI18N.getString("OrderedDitherDescriptor2")} }; /** The parameter names for the "OrderedDither" operation. */ private static final String[] paramNames = { "colorMap", "ditherMask" }; /** The parameter class types for the "OrderedDither" operation. */ private static final Class[] paramClasses = { javax.media.jai.ColorCube.class, javax.media.jai.KernelJAI[].class }; /** The parameter default values for the "OrderedDither" operation. */ private static final Object[] paramDefaults = { ColorCube.BYTE_496, KernelJAI.DITHER_MASK_443 }; private static final String[] supportedModes = { "rendered" }; /** * Method to check the validity of the color map parameter. The supplied * color cube must have the same data type and number of bands as the * source image. * * @param sourceImage The source image of the operation. * @param colorMap The color cube. * @param msg The buffer to which messages should be appended. * * @return Whether the color map is valid. */ private static boolean isValidColorMap(RenderedImage sourceImage, ColorCube colorMap, StringBuffer msg) { SampleModel srcSampleModel = sourceImage.getSampleModel(); if(colorMap.getDataType() != srcSampleModel.getTransferType()) { msg.append(JaiI18N.getString("OrderedDitherDescriptor3")); return false; } else if (colorMap.getNumBands() != srcSampleModel.getNumBands()) { msg.append(JaiI18N.getString("OrderedDitherDescriptor4")); return false; } return true; } /** * Method to check the validity of the dither mask parameter. The dither * mask is an array of KernelJAI objects wherein the number * of elements in the array must equal the number of bands in the source * image. Furthermore all kernels in the array must have the same width * and height. Finally all data elements of all kernels must be greater * than or equal to zero and less than or equal to unity. * * @param sourceImage The source image of the operation. * @param ditherMask The dither mask. * @param msg The buffer to which messages should be appended. * * @return Whether the dither mask is valid. */ private static boolean isValidDitherMask(RenderedImage sourceImage, KernelJAI[] ditherMask, StringBuffer msg) { if(ditherMask.length != sourceImage.getSampleModel().getNumBands()) { msg.append(JaiI18N.getString("OrderedDitherDescriptor5")); return false; } int maskWidth = ditherMask[0].getWidth(); int maskHeight = ditherMask[0].getHeight(); for(int band = 0; band < ditherMask.length; band++) { if(ditherMask[band].getWidth() != maskWidth || ditherMask[band].getHeight() != maskHeight) { msg.append(JaiI18N.getString("OrderedDitherDescriptor6")); return false; } float[] kernelData = ditherMask[band].getKernelData(); for(int i = 0; i < kernelData.length; i++) { if(kernelData[i] < 0.0F || kernelData[i] > 1.0) { msg.append(JaiI18N.getString("OrderedDitherDescriptor7")); return false; } } } return true; } /** Constructor. */ public OrderedDitherDescriptor() { super(resources, supportedModes, 1, paramNames, paramClasses, paramDefaults, null); } /** * Validates the input source and parameters. * *

      In addition to the standard checks performed by the * superclass method, this method checks that "colorMap" * and "ditherMask" are valid for the given source image. */ public boolean validateArguments(String modeName, ParameterBlock args, StringBuffer msg) { if (!super.validateArguments(modeName, args, msg)) { return false; } if (!modeName.equalsIgnoreCase("rendered")) return true; // Retrieve the operation source and parameters. RenderedImage src = args.getRenderedSource(0); ColorCube colorMap = (ColorCube)args.getObjectParameter(0); KernelJAI[] ditherMask = (KernelJAI[])args.getObjectParameter(1); // Check color map validity. if (!isValidColorMap(src, colorMap, msg)) { return false; } // Check dither mask validity. if (!isValidDitherMask(src, ditherMask, msg)) { return false; } return true; } /** * Performs ordered dither color quantization using a specified color cube and dither mask. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param colorMap The color cube. * May be null. * @param ditherMask The dither mask. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, ColorCube colorMap, KernelJAI[] ditherMask, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("OrderedDither", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("colorMap", colorMap); pb.setParameter("ditherMask", ditherMask); return JAI.create("OrderedDither", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/MagnitudeDescriptor.java0000644000175000017500000001546510203035544030022 0ustar mathieumathieu/* * $RCSfile: MagnitudeDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:38 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PropertyGenerator; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Magnitude" operation. * *

      The "Magnitude" operation computes the magnitude of each pixel of a * complex image. The source image must have an even number of bands, with * the even bands (0, 2, ...) representing the real parts and the odd bands * (1, 3, ...) the imaginary parts of each complex pixel. The destination * image has at most half the number of bands of the source image with each * sample in a pixel representing the magnitude of the corresponding complex * source sample. The magnitude values of the destination image are defined * for a given sample by the pseudocode: * *

      dstPixel[x][y][b] = sqrt(src[x][y][2*b]^2 + src[x][y][2*b + 1]^2)
      * * where the number of bands b varies from zero to one less than the * number of bands in the destination image. * *

      For integral image datatypes, the result will be rounded and clamped * as needed. * *

      "Magnitude" defines a PropertyGenerator that sets the "COMPLEX" * property of the image to java.lang.Boolean.FALSE, which may * be retrieved by calling the getProperty() method with * "COMPLEX" as the property name. * *

      * * * * * * * * *
      Resource List
      Name Value
      GlobalName Magnitude
      LocalName Magnitude
      Vendor com.sun.media.jai
      Description Find the magnitude of each pixel of * an image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/MagnitudeDescriptor.html
      Version 1.0

      * *

      No parameters are needed for the "Magnitude" operation. * * @see javax.media.jai.OperationDescriptor */ public class MagnitudeDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Magnitude"}, {"LocalName", "Magnitude"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("MagnitudeDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/MagnitudeDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")} }; private static final String[] supportedModes = { "rendered", "renderable" }; /** Constructor. */ public MagnitudeDescriptor() { super(resources, supportedModes, 1, null, null, null, null); } /** * Validates the input source. * *

      In addition to the standard checks performed by the * superclass method, this method checks that the source image * has an even number of bands. */ protected boolean validateSources(String modeName, ParameterBlock args, StringBuffer msg) { if (!super.validateSources(modeName, args, msg)) { return false; } if (!modeName.equalsIgnoreCase("rendered")) return true; RenderedImage src = args.getRenderedSource(0); int bands = src.getSampleModel().getNumBands(); if (bands % 2 != 0) { msg.append(getName() + " " + JaiI18N.getString("MagnitudeDescriptor1")); return false; } return true; } /** * Returns an array of PropertyGenerators implementing * property inheritance for the "Magnitude" operation. * * @return An array of property generators. */ public PropertyGenerator[] getPropertyGenerators(String modeName) { PropertyGenerator[] pg = new PropertyGenerator[1]; pg[0] = new ComplexPropertyGenerator(); return pg; } /** * Find the magnitude of each pixel of an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Magnitude", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); return JAI.create("Magnitude", pb, hints); } /** * Find the magnitude of each pixel of an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Magnitude", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); return JAI.createRenderable("Magnitude", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/ShearDescriptor.java0000644000175000017500000003355110203035544027143 0ustar mathieumathieu/* * $RCSfile: ShearDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:44 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.util.PropertyGeneratorImpl; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.EnumeratedParameter; import javax.media.jai.GeometricOpImage; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationNearest; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PlanarImage; import javax.media.jai.PropertyGenerator; import javax.media.jai.ROI; import javax.media.jai.ROIShape; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.operator.ShearDir; import javax.media.jai.registry.RenderedRegistryMode; /** * This property generator computes the properties for the operation * "Shear" dynamically. */ class ShearPropertyGenerator extends PropertyGeneratorImpl { /** Constructor. */ public ShearPropertyGenerator() { super(new String[] {"ROI"}, new Class[] {ROI.class}, new Class[] {RenderedOp.class}); } /** * Returns the specified property. * * @param name Property name. * @param opNode Operation node. */ public Object getProperty(String name, Object opNode) { validate(name, opNode); if(opNode instanceof RenderedOp && name.equalsIgnoreCase("roi")) { RenderedOp op = (RenderedOp)opNode; ParameterBlock pb = op.getParameterBlock(); // Retrieve the rendered source image and its ROI. RenderedImage src = (RenderedImage)pb.getRenderedSource(0); Object property = src.getProperty("ROI"); if (property == null || property.equals(java.awt.Image.UndefinedProperty) || !(property instanceof ROI)) { return java.awt.Image.UndefinedProperty; } ROI srcROI = (ROI)property; // Retrieve the Interpolation object. Interpolation interp = (Interpolation)pb.getObjectParameter(4); // Determine the effective source bounds. Rectangle srcBounds = null; PlanarImage dst = op.getRendering(); if (dst instanceof GeometricOpImage && ((GeometricOpImage)dst).getBorderExtender() == null) { srcBounds = new Rectangle(src.getMinX() + interp.getLeftPadding(), src.getMinY() + interp.getTopPadding(), src.getWidth() - interp.getWidth() + 1, src.getHeight() - interp.getHeight() + 1); } else { srcBounds = new Rectangle(src.getMinX(), src.getMinY(), src.getWidth(), src.getHeight()); } // Set the nearest neighbor interpolation object. Interpolation interpNN = interp instanceof InterpolationNearest ? interp : Interpolation.getInstance(Interpolation.INTERP_NEAREST); // Retrieve the operation parameters. float sv = pb.getFloatParameter(0); EnumeratedParameter shearDir = (EnumeratedParameter)pb.getObjectParameter(1); float tx = pb.getFloatParameter(2); float ty = pb.getFloatParameter(3); // Create an equivalent transform. AffineTransform transform = new AffineTransform(1.0, shearDir == ShearDescriptor.SHEAR_VERTICAL ? sv : 0, shearDir == ShearDescriptor.SHEAR_HORIZONTAL ? sv : 0, 1.0, shearDir == ShearDescriptor.SHEAR_HORIZONTAL ? tx : 0, shearDir == ShearDescriptor.SHEAR_VERTICAL ? ty : 0); // Create the sheared ROI. ROI dstROI = srcROI.transform(transform); // Retrieve the destination bounds. Rectangle dstBounds = op.getBounds(); // If necessary, clip the sheared ROI to the destination bounds. if(!dstBounds.contains(dstROI.getBounds())) { dstROI = dstROI.intersect(new ROIShape(dstBounds)); } // Return the sheared and possibly clipped ROI. return dstROI; } return java.awt.Image.UndefinedProperty; } } /** * An OperationDescriptor describing the "Shear" operation. * *

      The "Shear" operation shears an image either horizontally or * vertically. For each pixel (x, y) of the destination, the source * value at the fractional subpixel position (x', y') is constructed by * means of an Interpolation object and written to the destination. * *

      If the "shearDir" parameter is equal to SHEAR_HORIZONTAL * then x' = (x - xTrans - y*shear) and y' = y. * If the "shearDir" parameter is equal to SHEAR_VERTICAL * then x' = x and y' = (y - yTrans - x*shear). * *

      The parameter, "backgroundValues", is defined to * fill the background with the user-specified background * values. These background values will be translated into background * colors by the ColorModel when the image is displayed. * With the default value, {0.0}, of this parameter, * the background pixels are filled with 0s. If the provided array * length is smaller than the number of bands, the first element of * the provided array is used for all the bands. If the provided values * are out of the data range of the destination image, they will be clamped * into the proper range. * *

      It may be noted that the minX, minY, width and height hints as * specified through the JAI.KEY_IMAGE_LAYOUT hint in the * RenderingHints object are not honored, as this operator * calculates the destination image bounds itself. The other * ImageLayout hints, like tileWidth and tileHeight, * however are honored. * *

      It should be noted that this operation automatically adds a * value of Boolean.TRUE for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL to the given * configuration so that the operation is performed * on the pixel values instead of being performed on the indices into * the color map if the source(s) have an IndexColorModel. * This addition will take place only if a value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL has not already been * provided by the user. Note that the configuration Map * is cloned before the new hint is added to it. The operation can be * smart about the value of the JAI.KEY_REPLACE_INDEX_COLOR_MODEL * RenderingHints, i.e. while the default value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL is * Boolean.TRUE, in some cases the operator could set the * default. * *

      "Shear" defines a PropertyGenerator that performs an identical * transformation on the "ROI" property of the source image, which can * be retrieved by calling the getProperty method with * "ROI" as the property name. * *

      * * * * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName shear
      LocalName shear
      Vendor com.sun.media.jai
      Description Shears an image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ShearDescriptor.html
      Version 1.0
      arg0Desc The shear value.
      arg1Desc The shear direction.
      arg2Desc The X translation.
      arg3Desc The Y translation.
      arg4Desc The interpolation method for * resampling.

      * *

      * * * * * * * * * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      shear java.lang.Float0.0F
      shearDir javax.media.jai.operator.ShearDirSHEAR_HORIZONTAL
      xTrans java.lang.Float0.0F
      yTrans java.lang.Float0.0F
      interpolation javax.media.jai.InterpolationInterpolationNearest
      backgroundValues double[]{0.0}

      * * @see javax.media.jai.Interpolation * @see javax.media.jai.OperationDescriptor * @see ShearDir */ public class ShearDescriptor extends OperationDescriptorImpl { public static final ShearDir SHEAR_HORIZONTAL = new ShearDir("SHEAR_HORIZONTAL", 0); public static final ShearDir SHEAR_VERTICAL = new ShearDir("SHEAR_VERTICAL", 1); /** * The resource strings that provide the general documentation and * specify the parameter list for the "Shear" operation. */ private static final String[][] resources = { {"GlobalName", "Shear"}, {"LocalName", "Shear"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("ShearDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ShearDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion2")}, {"arg0Desc", JaiI18N.getString("ShearDescriptor1")}, {"arg1Desc", JaiI18N.getString("ShearDescriptor2")}, {"arg2Desc", JaiI18N.getString("ShearDescriptor3")}, {"arg3Desc", JaiI18N.getString("ShearDescriptor4")}, {"arg4Desc", JaiI18N.getString("ShearDescriptor5")}, {"arg5Desc", JaiI18N.getString("ShearDescriptor6")} }; /** The parameter names for the "Shear" operation. */ private static final String[] paramNames = { "shear", "shearDir", "xTrans", "yTrans", "interpolation", "backgroundValues" }; /** The parameter class types for the "Shear" operation. */ private static final Class[] paramClasses = { java.lang.Float.class, javax.media.jai.operator.ShearDir.class, java.lang.Float.class, java.lang.Float.class, javax.media.jai.Interpolation.class, double[].class }; /** The parameter default values for the "Shear" operation. */ private static final Object[] paramDefaults = { new Float(0.0F), SHEAR_HORIZONTAL, new Float(0.0F), new Float(0.0F), Interpolation.getInstance(Interpolation.INTERP_NEAREST), new double[] {0.0} }; /** Constructor. */ public ShearDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** * Returns an array of PropertyGenerators implementing * property inheritance for the "Shear" operation. * * @return An array of property generators. */ public PropertyGenerator[] getPropertyGenerators() { PropertyGenerator[] pg = new PropertyGenerator[1]; pg[0] = new ShearPropertyGenerator(); return pg; } /** * Shears an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param shear The shear value. * May be null. * @param shearDir The shear direction. * May be null. * @param xTrans The X translation. * May be null. * @param yTrans The Y translation. * May be null. * @param interpolation The interpolation method for resampling. * May be null. * @param backgroundValues The user-specified background values. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, Float shear, ShearDir shearDir, Float xTrans, Float yTrans, Interpolation interpolation, double[] backgroundValues, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Shear", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("shear", shear); pb.setParameter("shearDir", shearDir); pb.setParameter("xTrans", xTrans); pb.setParameter("yTrans", yTrans); pb.setParameter("interpolation", interpolation); pb.setParameter("backgroundValues", backgroundValues); return JAI.create("Shear", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/DivideDescriptor.java0000644000175000017500000001473410203035544027307 0ustar mathieumathieu/* * $RCSfile: DivideDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:34 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Divide" operation. * *

      The Divide operation takes two rendered or renderable images, * and for every pair of pixels, one from each source image of the * corresponding position and band, divides the pixel from the first * source by the pixel from the second source. No additional parameters * are required for this operation. * *

      In case of division by 0, if the numerator is 0, then the result * is set to 0; otherwise, the result is set to the maximum value * supported by the destination data type. * *

      The two source images may have different number of bands and data * types. By default, the destination image bound is the intersection * of the two source image bounds. If the two sources don't intersect, * the destination will have a width and a height of 0. * *

      The default number of bands of the destination image is the same * as the least number of bands of the sources, and the data type is the * biggest data type of the sources. * *

      As a special case, if one of the source images has N bands (N > * 1), the other source has 1 band, and an ImageLayout * hint is provided containing a destination SampleModel * with K bands (1 < K <= N), then the single band of the 1-banded * source will be divided by or into to each of the first K bands of * the N-band source. * *

      If the result of the operation underflows/overflows the * minimum/maximum value supported by the destination data type, then * it will be clamped to the minimum/maximum value respectively. * *

      The destination pixel values are defined by the pseudocode: *

       * dst[x][y][dstBand] = srcs[0][x][y][src0Band]/srcs[1][x][y][src1Band];
       * 
      * *

      * * * * * * * * *
      Resource List
      Name Value
      GlobalName divide
      LocalName divide
      Vendor com.sun.media.jai
      Description Dividies one image by * another image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/DivideDescriptor.html
      Version 1.0

      * *

      No parameters are needed for this operation. * * @see javax.media.jai.OperationDescriptor */ public class DivideDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Divide"}, {"LocalName", "Divide"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("DivideDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/DivideDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")} }; /** Constructor. */ public DivideDescriptor() { super(resources, 2, null, null, null); } /** Returns true since renderable operation is supported. */ public boolean isRenderableSupported() { return true; } /** * Divides one image by another image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param source1 RenderedImage source 1. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if source1 is null. */ public static RenderedOp create(RenderedImage source0, RenderedImage source1, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Divide", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setSource("source1", source1); return JAI.create("Divide", pb, hints); } /** * Divides one image by another image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param source1 RenderableImage source 1. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if source1 is null. */ public static RenderableOp createRenderable(RenderableImage source0, RenderableImage source1, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Divide", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setSource("source1", source1); return JAI.createRenderable("Divide", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/DivideComplexDescriptor.java0000644000175000017500000002020010203035544030620 0ustar mathieumathieu/* * $RCSfile: DivideComplexDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:34 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PropertyGenerator; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "DivideComplex" * operation. * *

      The "DivideComplex" operation divides two images representing * complex data. The source images must each contain an even number of bands * with the even-indexed bands (0, 2, ...) representing the real and the * odd-indexed bands (1, 3, ...) the imaginary parts of each pixel. The * destination image similarly contains an even number of bands with the * same interpretation and with contents defined by: * *

       * a = src0[x][y][2*k];
       * b = src0[x][y][2*k+1];
       * c = src1[x][y][2*k];
       * d = src1[x][y][2*k+1];
       *
       * dst[x][y][2*k] = (a*c + b*d)/(c^2 + d^2)
       * dst[x][y][2*k+1] = (b*c - a*d)/(c^2 + d^2)
       * 
      * * where 0 <= k < numBands/2. * * By default, the number of bands of the destination image is the * the minimum of the number of bands of the two sources, and the * data type is the biggest data type of the sources. * However, the number of destination bands can be specified to be * M = 2*L through an ImageLayout hint, when * one source image has 2 bands and the other has N = 2*K bands * where K > 1, with a natural restriction 1 <= L <= K. * In such a special case, * if the first source has 2 bands its single complex component * will be divided by each of the first L complex components of the second * source; if the second source has 2 bands its single complex component will * divide each of the L complex components of the first source. * *

      If the result of the operation underflows/overflows the * minimum/maximum value supported by the destination data type, then it will * be clamped to the minimum/maximum value respectively. * *

      "DivideComplex" defines a PropertyGenerator that sets the "COMPLEX" * property of the image to java.lang.Boolean.TRUE, which may * be retrieved by calling the getProperty() method with * "COMPLEX" as the property name. * *

      * * * * * * * * *
      Resource List
      Name Value
      GlobalName DivideComplex
      LocalName DivideComplex
      Vendor com.sun.media.jai
      Description Compute the complex quotient of two images.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/DivideComplexDescriptor.html
      Version 1.0

      * *

      No parameters are needed for the "DivideComplex" operation. * * @see javax.media.jai.OperationDescriptor */ public class DivideComplexDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "DivideComplex"}, {"LocalName", "DivideComplex"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("DivideComplexDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/DivideComplexDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")} }; private static final String[] supportedModes = { "rendered", "renderable" }; /** Constructor. */ public DivideComplexDescriptor() { super(resources, supportedModes, 2, null, null, null, null); } /** * Validates the input sources. * *

      In addition to the standard checks performed by the * superclass method, this method checks that both sources have * an even number of bands. */ protected boolean validateSources(String modeName, ParameterBlock args, StringBuffer msg) { if (!super.validateSources(modeName, args, msg)) { return false; } if (!modeName.equalsIgnoreCase("rendered")) return true; RenderedImage src1 = args.getRenderedSource(0); RenderedImage src2 = args.getRenderedSource(1); if (src1.getSampleModel().getNumBands() % 2 != 0 || src2.getSampleModel().getNumBands() % 2 != 0) { msg.append(getName() + " " + JaiI18N.getString("DivideComplexDescriptor1")); return false; } return true; } /** * Returns an array of PropertyGenerators implementing * property inheritance for the "DivideComplex" operation. * * @return An array of property generators. */ public PropertyGenerator[] getPropertyGenerators(String modeName) { PropertyGenerator[] pg = new PropertyGenerator[1]; pg[0] = new ComplexPropertyGenerator(); return pg; } /** * Compute the complex quotient of two images. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param source1 RenderedImage source 1. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if source1 is null. */ public static RenderedOp create(RenderedImage source0, RenderedImage source1, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("DivideComplex", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setSource("source1", source1); return JAI.create("DivideComplex", pb, hints); } /** * Compute the complex quotient of two images. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param source1 RenderableImage source 1. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if source1 is null. */ public static RenderableOp createRenderable(RenderableImage source0, RenderableImage source1, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("DivideComplex", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setSource("source1", source1); return JAI.createRenderable("DivideComplex", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/PeriodicShiftDescriptor.java0000644000175000017500000002101510203035544030625 0ustar mathieumathieu/* * $RCSfile: PeriodicShiftDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:42 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "PeriodicShift" * operation. * *

      The destination image of the "PeriodicShift" operation is the * infinite periodic extension of the source image with horizontal and * vertical periods equal to the image width and height, respectively, * shifted by a specified amount along each axis and clipped to the * bounds of the source image. Thus for each band b the destination * image sample at location (x,y) is defined by: * *

       * if(x < width - shiftX) {
       *     if(y < height - shiftY) {
       *         dst[x][y][b] = src[x + shiftX][y + shiftY][b];
       *     } else {
       *         dst[x][y][b] = src[x + shiftX][y - height + shiftY][b];
       *     }
       * } else {
       *     if(y < height - shiftY) {
       *         dst[x][y][b] = src[x - width + shiftX][y + shiftY][b];
       *     } else {
       *         dst[x][y][b] = src[x - width + shiftX][y - height + shiftY][b];
       *     }
       * }
       * 
      * * where shiftX and shiftY denote the translation factors * along the X and Y axes, respectively. * *

      * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName PeriodicShift
      LocalName PeriodicShift
      Vendor com.sun.media.jai
      Description Computes the periodic translation of an image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/PeriodicShiftDescriptor.html
      Version 1.0
      arg0Desc The displacement in the X direction.
      arg1Desc The displacement in the Y direction.

      * *

      * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      shiftX java.lang.IntegersourceWidth/2
      shiftY java.lang.IntegersourceHeight/2

      * * @see javax.media.jai.OperationDescriptor */ public class PeriodicShiftDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "PeriodicShift"}, {"LocalName", "PeriodicShift"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("PeriodicShiftDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/PeriodicShiftDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("PeriodicShiftDescriptor1")}, {"arg1Desc", JaiI18N.getString("PeriodicShiftDescriptor2")} }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { java.lang.Integer.class, java.lang.Integer.class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "shiftX", "shiftY" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { null, null }; private static final String[] supportedModes = { "rendered", "renderable" }; /** Constructor. */ public PeriodicShiftDescriptor() { super(resources, supportedModes, 1, paramNames, paramClasses, paramDefaults, null); } /** * Validates the input parameters. * *

      In addition to the standard checks performed by the * superclass method, this method checks that "shiftX" and * "shiftY" are between 0 and the source image width and * height, respectively. */ public boolean validateArguments(String modeName, ParameterBlock args, StringBuffer msg) { if (!super.validateArguments(modeName, args, msg)) { return false; } if (!modeName.equalsIgnoreCase("rendered")) return true; RenderedImage src = args.getRenderedSource(0); // Set non-static default values based on source if (args.getObjectParameter(0) == null) { args.set(new Integer(src.getWidth()/2), 0); } if (args.getObjectParameter(1) == null) { args.set(new Integer(src.getHeight()/2), 1); } int shiftX = args.getIntParameter(0); int shiftY = args.getIntParameter(1); if (shiftX < 0 || shiftX >= src.getWidth() || shiftY < 0 || shiftY >= src.getHeight()) { msg.append(getName() + " " + JaiI18N.getString("PeriodicShiftDescriptor3")); return false; } return true; } /** * Computes the periodic translation of an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param shiftX The displacement in the X direction. * May be null. * @param shiftY The displacement in the Y direction. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, Integer shiftX, Integer shiftY, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("PeriodicShift", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("shiftX", shiftX); pb.setParameter("shiftY", shiftY); return JAI.create("PeriodicShift", pb, hints); } /** * Computes the periodic translation of an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param shiftX The displacement in the X direction. * May be null. * @param shiftY The displacement in the Y direction. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, Integer shiftX, Integer shiftY, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("PeriodicShift", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("shiftX", shiftX); pb.setParameter("shiftY", shiftY); return JAI.createRenderable("PeriodicShift", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/PNGDescriptor.java0000644000175000017500000002022310203035544026515 0ustar mathieumathieu/* * $RCSfile: PNGDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:42 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.codec.PNGDecodeParam; import com.sun.media.jai.codec.SeekableStream; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "PNG" operation. * *

      The "PNG" operation reads a standard PNG version 1.1 input stream. * The PNG (Portable Network Graphics) specification may be * found at * http://www.cdrom.com/pub/png/spec. * *

      The "PNG" operation implements the entire PNG specification, * but provides access only to the final, high-resolution version of * interlaced images. * *

      The second parameter contains an instance of * PNGDecodeParam to be used during the decoding. * It may be set to null in order to perform default * decoding, or equivalently may be omitted. * *

      The documentation for PNGDecodeParam describes the * possible output formats of PNG images after decoding. * *

      The classes in the com.sun.media.jai.codec * package are not a committed part of the JAI API. Future releases * of JAI will make use of new classes in their place. This * class will change accordingly. * *

      * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName PNG
      LocalName PNG
      Vendor com.sun.media.jai
      Description Reads an image from a PNG stream.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/PNGDescriptor.html
      Version 1.0
      arg0Desc The SeekableStream to read from.
      arg1Desc The PNGDecodeParam to use.

      * *

      * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      stream com.sun.media.jai.codec.SeekableStreamNO_PARAMETER_DEFAULT
      param com.sun.media.jai.codec.PNGDecodeParamnull

      * *

      * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
      Properties
      Property Name Class Comment
      file_type String"PNG v. 1.0"
      background_color java.awt.ColorThe suggested background color.
      significant_bits int[]The number of significant bits stored in the file.
      bit_depth IntegerThe bit depth of the file
      color_type StringOne of "Grayscale", "Truecolor", "Index", * "Grayscale with alpha" or "Truecolor with alpha"
      interlace_method String"None" or "Adam7"
      white_point_x FloatThe CIE X coordinate of the white point, if known.
      white_point_y FloatThe CIE Y coordinate of the white point, if known.
      red_x FloatThe CIE X coordinate of the red primary, if known.
      red_y FloatThe CIE Y coordinate of the red primary, if known.
      green_x FloatThe CIE X coordinate of the green primary, if known.
      green_y FloatThe CIE Y coordinate of the green primary, if known.
      blue_x FloatThe CIE X coordinate of the blue primary, if known.
      blue_y FloatThe CIE Y coordinate of the blue primary, if known.
      gamma FloatThe image gamma, if known.
      x_pixels_per_unit IntegerThe number of horizontal pixels per unit.
      y_pixels_per_unit IntegerThe number of vertical pixels per unit.
      pixel_aspect_ratio FloatThe width of a pixel divided by its height.
      pixel_units String"Meters" or null
      timestamp java.util.DateThe creation or modification time of the image.
      text:* StringThe value of a tEXt chunk.
      ztext:* StringThe value of a zTXt chunk (not yet implemented).
      chunk:* byte[]The contents of any non-standard chunks.

      * * @see com.sun.media.jai.codec.PNGDecodeParam * @see com.sun.media.jai.codec.SeekableStream * @see javax.media.jai.OperationDescriptor */ public class PNGDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation and * specify the parameter list for the "PNG" operation. */ private static final String[][] resources = { {"GlobalName", "PNG"}, {"LocalName", "PNG"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("PNGDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/PNGDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("PNGDescriptor1")}, {"arg1Desc", JaiI18N.getString("PNGDescriptor2")}, }; /** The parameter names for the "PNG" operation. */ private static final String[] paramNames = { "stream", "param" }; /** The parameter class types for the "PNG" operation. */ private static final Class[] paramClasses = { com.sun.media.jai.codec.SeekableStream.class, com.sun.media.jai.codec.PNGDecodeParam.class }; /** The parameter default values for the "PNG" operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT, null }; /** Constructor. */ public PNGDescriptor() { super(resources, 0, paramClasses, paramNames, paramDefaults); } /** * Reads a standard JFIF (PNG) file. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param stream The SeekableStream to read from. * @param param The PNGDecodeParam to use. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if stream is null. */ public static RenderedOp create(SeekableStream stream, PNGDecodeParam param, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("PNG", RenderedRegistryMode.MODE_NAME); pb.setParameter("stream", stream); pb.setParameter("param", param); return JAI.create("PNG", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/DivideByConstDescriptor.java0000644000175000017500000001736310203035544030612 0ustar mathieumathieu/* * $RCSfile: DivideByConstDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:34 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the * "DivideByConst" operation. * *

      The DivideByConst operation takes one rendered or renderable * source image and an array of double constants, and divides every * pixel of the same band of the source by the constant from the * corresponding array entry. If the number of constants supplied is * less than the number of bands of the destination, then the constant * from entry 0 is applied to all the bands. Otherwise, a constant * from a different entry is applied to each band. * *

      In case of division by 0, if the numerator is 0, then the * result is set to 0; otherwise, the result is set to the maximum * value supported by the destination data type. * *

      By default, the destination image bound, data type, and number * of bands are the same as the source image. If the result of the * operation underflows/overflows the minimum/maximum value supported * by the destination data type, then it will be clamped to the * minimum/maximum value respectively. * *

      The destination pixel values are defined by the pseudocode: *

       * if (constants.length < dstNumBands) {
       *     dst[x][y][b] = srcs[x][y][b]/constants[0];
       * } else {
       *     dst[x][y][b] = srcs[x][y][b]/constants[b];
       * }
       * 
      * *

      * * * * * * * * * *
      Resource List
      Name Value
      GlobalName DivideByConst
      LocalName DivideByConst
      Vendor com.sun.media.jai
      Description Divides an image by constants.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/DivideByConstDescriptor.html
      Version 1.0
      arg0Desc The constants to be divided by.

      * *

      * * * * * *
      Parameter List
      Name Class TypeDefault Value
      constants double[]{1.0}

      * * @see javax.media.jai.OperationDescriptor */ public class DivideByConstDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "DivideByConst"}, {"LocalName", "DivideByConst"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("DivideByConstDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/DivideByConstDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("DivideByConstDescriptor1")} }; /** * The parameter class list for this operation. * The number of constants provided should be either 1, in which case * this same constant is applied to all the source bands; or the same * number as the source bands, in which case one contant is applied * to each band. */ private static final Class[] paramClasses = { double[].class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "constants" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { new double[] {1.0} }; /** Constructor. */ public DivideByConstDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** Returns true since renderable operation is supported. */ public boolean isRenderableSupported() { return true; } /** * Validates the input parameters. * *

      In addition to the standard checks performed by the * superclass method, this method checks that the length of the * "constants" array is at least 1. */ protected boolean validateParameters(ParameterBlock args, StringBuffer message) { if (!super.validateParameters(args, message)) { return false; } int length = ((double[])args.getObjectParameter(0)).length; if (length < 1) { message.append(getName() + " " + JaiI18N.getString("DivideByConstDescriptor2")); return false; } return true; } /** * Divides an image by constants. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param constants The constants to be divided by. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, double[] constants, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("DivideByConst", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("constants", constants); return JAI.create("DivideByConst", pb, hints); } /** * Divides an image by constants. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param constants The constants to be divided by. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, double[] constants, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("DivideByConst", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("constants", constants); return JAI.createRenderable("DivideByConst", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/RotateDescriptor.java0000644000175000017500000003501310203035544027332 0ustar mathieumathieu/* * $RCSfile: RotateDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:43 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.util.PropertyGeneratorImpl; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.GeometricOpImage; import javax.media.jai.Interpolation; import javax.media.jai.InterpolationNearest; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PlanarImage; import javax.media.jai.PropertyGenerator; import javax.media.jai.ROI; import javax.media.jai.ROIShape; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * This property generator computes the properties for the operation * "Rotate" dynamically. */ class RotatePropertyGenerator extends PropertyGeneratorImpl { /** Constructor. */ public RotatePropertyGenerator() { super(new String[] {"ROI"}, new Class[] {ROI.class}, new Class[] {RenderedOp.class}); } /** * Returns the specified property. * * @param name Property name. * @param opNode Operation node. */ public Object getProperty(String name, Object opNode) { validate(name, opNode); if(opNode instanceof RenderedOp && name.equalsIgnoreCase("roi")) { RenderedOp op = (RenderedOp)opNode; ParameterBlock pb = op.getParameterBlock(); // Retrieve the rendered source image and its ROI. RenderedImage src = pb.getRenderedSource(0); Object property = src.getProperty("ROI"); if (property == null || property.equals(java.awt.Image.UndefinedProperty) || !(property instanceof ROI)) { return java.awt.Image.UndefinedProperty; } ROI srcROI = (ROI)property; // Retrieve the Interpolation object. Interpolation interp = (Interpolation)pb.getObjectParameter(3); // Determine the effective source bounds. Rectangle srcBounds = null; PlanarImage dst = op.getRendering(); if (dst instanceof GeometricOpImage && ((GeometricOpImage)dst).getBorderExtender() == null) { srcBounds = new Rectangle(src.getMinX() + interp.getLeftPadding(), src.getMinY() + interp.getTopPadding(), src.getWidth() - interp.getWidth() + 1, src.getHeight() - interp.getHeight() + 1); } else { srcBounds = new Rectangle(src.getMinX(), src.getMinY(), src.getWidth(), src.getHeight()); } // If necessary, clip the ROI to the effective source bounds. if(!srcBounds.contains(srcROI.getBounds())) { srcROI = srcROI.intersect(new ROIShape(srcBounds)); } // Retrieve the translation and rotation angle. double xorig = (double)pb.getFloatParameter(0); double yorig = (double)pb.getFloatParameter(1); double angle = (double)pb.getFloatParameter(2); // Create an transform representing the rotation. AffineTransform transform = AffineTransform.getRotateInstance(angle, xorig, yorig); // Create the rotated/translated ROI. ROI dstROI = srcROI.transform(transform); // Retrieve the destination bounds. Rectangle dstBounds = op.getBounds(); // If necessary, clip the rotated ROI to the destination bounds. if(!dstBounds.contains(dstROI.getBounds())) { dstROI = dstROI.intersect(new ROIShape(dstBounds)); } // Return the rotated and possibly clipped ROI. return dstROI; } return java.awt.Image.UndefinedProperty; } } /** * An OperationDescriptor describing the "Rotate" operation. * *

      The "Rotate" operation rotates an image about a given point by * a given angle, specified in radians. The origin defaults to (0, 0). * *

      The parameter, "backgroundValues", is defined to * fill the background with the user-specified background * values. These background values will be translated into background * colors by the ColorModel when the image is displayed. * With the default value, {0.0}, of this parameter, * the background pixels are filled with 0s. If the provided array * length is smaller than the number of bands, the first element of * the provided array is used for all the bands. If the provided values * are out of the data range of the destination image, they will be clamped * into the proper range. * *

      It may be noted that the minX, minY, width and height hints as * specified through the JAI.KEY_IMAGE_LAYOUT hint in the * RenderingHints object are not honored, as this operator * calculates the destination image bounds itself. The other * ImageLayout hints, like tileWidth and tileHeight, * however are honored. * *

      It should be noted that this operation automatically adds a * value of Boolean.TRUE for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL to the given * configuration so that the operation is performed * on the pixel values instead of being performed on the indices into * the color map if the source(s) have an IndexColorModel. * This addition will take place only if a value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL has not already been * provided by the user. Note that the configuration Map * is cloned before the new hint is added to it. The operation can be * smart about the value of the JAI.KEY_REPLACE_INDEX_COLOR_MODEL * RenderingHints, i.e. while the default value for the * JAI.KEY_REPLACE_INDEX_COLOR_MODEL is * Boolean.TRUE, in some cases the operator could set the * default. * *

      "Rotate" defines a PropertyGenerator that performs an * identical transformation on the "ROI" property of the source image, * which can be retrieved by calling the getProperty * method with "ROI" as the property name. * *

      * * * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName Rotate
      LocalName Rotate
      Vendor com.sun.media.jai
      Description Rotate an image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/RotateDescriptor.html
      Version 1.0
      arg0Desc The X origin to rotate about.
      arg1Desc The Y origin to rotate about.
      arg2Desc The rotation angle in radians.
      arg3Desc The interpolation method.

      * *

      * * * * * * * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      xOrigin java.lang.Float0.0F
      yOrigin java.lang.Float0.0F
      angle java.lang.Float0.0F
      interpolation javax.media.jai.InterpolationInterpolationNearest
      backgroundValues double[]{0.0}

      * * @see javax.media.jai.Interpolation * @see javax.media.jai.OperationDescriptor */ public class RotateDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation and * specify the parameter list for the "Rotate" operation. */ private static final String[][] resources = { {"GlobalName", "Rotate"}, {"LocalName", "Rotate"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("RotateDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/RotateDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("RotateDescriptor1")}, {"arg1Desc", JaiI18N.getString("RotateDescriptor2")}, {"arg2Desc", JaiI18N.getString("RotateDescriptor3")}, {"arg3Desc", JaiI18N.getString("RotateDescriptor4")}, {"arg4Desc", JaiI18N.getString("RotateDescriptor5")} }; /** The parameter names for the "Rotate" operation. */ private static final String[] paramNames = { "xOrigin", "yOrigin", "angle", "interpolation", "backgroundValues" }; /** The parameter class types for the "Rotate" operation. */ private static final Class[] paramClasses = { java.lang.Float.class, java.lang.Float.class, java.lang.Float.class, javax.media.jai.Interpolation.class, double[].class }; /** The parameter default values for the "Rotate" operation. */ private static final Object[] paramDefaults = { new Float(0.0F), new Float(0.0F), new Float(0.0F), Interpolation.getInstance(Interpolation.INTERP_NEAREST), new double[] {0.0} }; /** Constructor. */ public RotateDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** Returns true since renderable operation is supported. */ public boolean isRenderableSupported() { return true; } /** * Returns an array of PropertyGenerators implementing * property inheritance for the "Rotate" operation. * * @return An array of property generators. */ public PropertyGenerator[] getPropertyGenerators() { PropertyGenerator[] pg = new PropertyGenerator[1]; pg[0] = new RotatePropertyGenerator(); return pg; } /** * Rotates an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param xOrigin The X origin to rotate about. * May be null. * @param yOrigin The Y origin to rotate about. * May be null. * @param angle The rotation angle in radians. * May be null. * @param interpolation The interpolation method. * May be null. * @param backgroundValues The user-specified background values. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, Float xOrigin, Float yOrigin, Float angle, Interpolation interpolation, double[] backgroundValues, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Rotate", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("xOrigin", xOrigin); pb.setParameter("yOrigin", yOrigin); pb.setParameter("angle", angle); pb.setParameter("interpolation", interpolation); pb.setParameter("backgroundValues", backgroundValues); return JAI.create("Rotate", pb, hints); } /** * Rotates an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param xOrigin The X origin to rotate about. * May be null. * @param yOrigin The Y origin to rotate about. * May be null. * @param angle The rotation angle in radians. * May be null. * @param interpolation The interpolation method. * May be null. * @param backgroundValues The user-specified background values. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, Float xOrigin, Float yOrigin, Float angle, Interpolation interpolation, double[] backgroundValues, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Rotate", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("xOrigin", xOrigin); pb.setParameter("yOrigin", yOrigin); pb.setParameter("angle", angle); pb.setParameter("interpolation", interpolation); pb.setParameter("backgroundValues", backgroundValues); return JAI.createRenderable("Rotate", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/FPXDescriptor.java0000644000175000017500000001221210203035544026525 0ustar mathieumathieu/* * $RCSfile: FPXDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:35 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.codec.FPXDecodeParam; import com.sun.media.jai.codec.SeekableStream; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "FPX" operation. * * The "FPX" operation reads an image from a FlashPix stream. * *

      The second parameter contains an instance of * FPXDecodeParam to be used during the decoding. * It may be set to null in order to perform default * decoding, or equivalently may be omitted. * *

      The classes in the com.sun.media.jai.codec * package are not a committed part of the JAI API. Future releases * of JAI will make use of new classes in their place. This * class will change accordingly. * *

      * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName FPX
      LocalName FPX
      Vendor com.sun.media.jai
      Description Reads an image from a FlashPix stream.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/FPXDescriptor.html
      Version 1.0
      arg0Desc The SeekableStream to read from.
      arg1Desc The FPXDecodeParam to use.

      * *

      * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      stream com.sun.media.jai.codec.SeekableStreamNO_PARAMETER_DEFAULT
      param com.sun.media.jai.codec.FPXDecodeParamnull

      * * @see com.sun.media.jai.codec.SeekableStream * @see javax.media.jai.OperationDescriptor */ public class FPXDescriptor extends OperationDescriptorImpl { /** Convenience name for the Max Resolution of an FPX image */ public static final Integer MAX_RESOLUTION = new Integer(-1); /** * The resource strings that provide the general documentation and * specify the parameter list for the "FPX" operation. */ private static final String[][] resources = { {"GlobalName", "FPX"}, {"LocalName", "FPX"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("FPXDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/FPXDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("FPXDescriptor1")}, {"arg1Desc", JaiI18N.getString("FPXDescriptor2")} }; /** The parameter names for the "FPX" operation. */ private static final String[] paramNames = { "stream", "param" }; /** The parameter class types for the "FPX" operation. */ private static final Class[] paramClasses = { com.sun.media.jai.codec.SeekableStream.class, com.sun.media.jai.codec.FPXDecodeParam.class }; /** The parameter default values for the "FPX" operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT, null }; /** Constructor. */ public FPXDescriptor() { super(resources, 0, paramClasses, paramNames, paramDefaults); } /** * Reads an image from a FlashPix stream. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param stream The SeekableStream to read from. * @param param The FPXDecodeParam to use. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if stream is null. */ public static RenderedOp create(SeekableStream stream, FPXDecodeParam param, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("FPX", RenderedRegistryMode.MODE_NAME); pb.setParameter("stream", stream); pb.setParameter("param", param); return JAI.create("FPX", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/ImageFunctionDescriptor.java0000644000175000017500000002660410203035544030632 0ustar mathieumathieu/* * $RCSfile: ImageFunctionDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:37 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.util.PropertyGeneratorImpl; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.ImageFunction; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PropertyGenerator; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; /** * This property generator computes the properties for the operation * "ImageFunction" dynamically. */ class ImageFunctionPropertyGenerator extends PropertyGeneratorImpl { /** Constructor. */ public ImageFunctionPropertyGenerator() { super(new String[] {"COMPLEX"}, new Class[] {Boolean.class}, new Class[] {RenderedOp.class, RenderableOp.class}); } /** * Returns the specified property. * * @param name Property name. * @param opNode Operation node. */ public Object getProperty(String name, Object opNode) { validate(name, opNode); if (name.equalsIgnoreCase("complex")) { if(opNode instanceof RenderedOp) { RenderedOp op = (RenderedOp)opNode; ParameterBlock pb = op.getParameterBlock(); ImageFunction imFunc = (ImageFunction)pb.getObjectParameter(0); return imFunc.isComplex() ? Boolean.TRUE : Boolean.FALSE; } else if(opNode instanceof RenderableOp) { RenderableOp op = (RenderableOp)opNode; ParameterBlock pb = op.getParameterBlock(); ImageFunction imFunc = (ImageFunction)pb.getObjectParameter(0); return imFunc.isComplex() ? Boolean.TRUE : Boolean.FALSE; } } return java.awt.Image.UndefinedProperty; } } /** * An OperationDescriptor describing the "ImageFunction" * operation. * *

      The "ImageFunction" operation generates an image on the basis of * a functional description provided by an object which is an instance of * a class which implements the ImageFunction interface. * The (x,y) coordinates passed to the getElements() * methods of the ImageFunction object are derived by applying * an optional translation and scaling to the X- and Y-coordinates of the * image. The image X- and Y-coordinates as usual depend on the values of * the minimum X- and Y- coordinates of the image which need not be zero. * Specifically, the function coordinates passed to getElements() * are calculated from the image coordinates as: * *

       * functionX = xScale*(imageX - xTrans);
       * functionY = yScale*(imageY - yTrans);
       * 
      * * This implies that the pixel at coordinates (xTrans,yTrans) will * be assigned the value of the function at (0,0). * *

      The number of bands in the destination image must be equal to the * value returned by the getNumElements() method of the * ImageFunction unless the isComplex() method * of the ImageFunction returns true in which * case it will be twice that. The data type of the destination image is * determined by the SampleModel specified by an * ImageLayout object provided via a hint. If no layout hint * is provided, the data type will default to single-precision floating point. * The double precision floating point form of the getElements() * method of the ImageFunction will be invoked if and only if * the data type is specified to be double. For all other data * types the single precision form of getElements() will be * invoked and the destination sample values will be clamped to the data type * of the image. * *

      The width and height of the image are provided explicitely as * parameters. These values override the width and height specified via * an ImageLayout if such is provided. * *

      "ImageFunction" defines a PropertyGenerator that sets the "COMPLEX" * property of the image to java.lang.Boolean.TRUE or * java.lang.Boolean.FALSE depending on whether the * isComplex() method of the ImageFunction * parameter returns true or false, respectively. * This property may be retrieved by calling the getProperty() * method with "COMPLEX" as the property name. * *

      * * * * * * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName ImageFunction
      LocalName ImageFunction
      Vendor com.sun.media.jai
      Description Generates an image from a functional description.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ImageFunctionDescriptor.html
      Version 1.0
      arg0Desc The functional description.
      arg1Desc The image width.
      arg2Desc The image height.
      arg3Desc The X scale factor.
      arg4Desc The Y scale factor.
      arg5Desc The X translation.
      arg6Desc The Y translation.

      * *

      * * * * * * * * * * * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      function javax.media.jai.ImageFunctionNO_PARAMETER_DEFAULT
      width java.lang.IntegerNO_PARAMETER_DEFAULT
      height java.lang.IntegerNO_PARAMETER_DEFAULT
      xScale java.lang.Float1.0F
      yScale java.lang.Float1.0F
      xTrans java.lang.Float0.0F
      yTrans java.lang.Float0.0F

      * * @see java.awt.geom.AffineTransform * @see javax.media.jai.OperationDescriptor * @see javax.media.jai.ImageFunction */ public class ImageFunctionDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "ImageFunction"}, {"LocalName", "ImageFunction"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("ImageFunctionDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ImageFunctionDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("ImageFunctionDescriptor1")}, {"arg1Desc", JaiI18N.getString("ImageFunctionDescriptor2")}, {"arg2Desc", JaiI18N.getString("ImageFunctionDescriptor3")}, {"arg3Desc", JaiI18N.getString("ImageFunctionDescriptor4")}, {"arg4Desc", JaiI18N.getString("ImageFunctionDescriptor5")}, {"arg5Desc", JaiI18N.getString("ImageFunctionDescriptor6")}, {"arg6Desc", JaiI18N.getString("ImageFunctionDescriptor7")} }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { javax.media.jai.ImageFunction.class, java.lang.Integer.class, java.lang.Integer.class, java.lang.Float.class, java.lang.Float.class, java.lang.Float.class, java.lang.Float.class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "function", "width", "height", "xScale", "yScale", "xTrans", "yTrans" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT, NO_PARAMETER_DEFAULT, NO_PARAMETER_DEFAULT, new Float(1.0F), new Float(1.0F), // unity scale new Float(0.0F), new Float(0.0F) // zero translation }; /** Constructor. */ public ImageFunctionDescriptor() { super(resources, 0, paramClasses, paramNames, paramDefaults); } /** * Returns an array of PropertyGenerators implementing * property inheritance for the "ImageFunction" operation. * * @return An array of property generators. */ public PropertyGenerator[] getPropertyGenerators() { PropertyGenerator[] pg = new PropertyGenerator[1]; pg[0] = new ImageFunctionPropertyGenerator(); return pg; } /** * Generates an image from a functional description. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param function The functional description. * @param width The image width. * @param height The image height. * @param xScale The X scale factor. * May be null. * @param yScale The Y scale factor. * May be null. * @param xTrans The X translation. * May be null. * @param yTrans The Y translation. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if function is null. * @throws IllegalArgumentException if width is null. * @throws IllegalArgumentException if height is null. */ public static RenderedOp create(ImageFunction function, Integer width, Integer height, Float xScale, Float yScale, Float xTrans, Float yTrans, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("ImageFunction", RenderedRegistryMode.MODE_NAME); pb.setParameter("function", function); pb.setParameter("width", width); pb.setParameter("height", height); pb.setParameter("xScale", xScale); pb.setParameter("yScale", yScale); pb.setParameter("xTrans", xTrans); pb.setParameter("yTrans", yTrans); return JAI.create("ImageFunction", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/NullDescriptor.java0000644000175000017500000002162710203035544027014 0ustar mathieumathieu/* * $RCSfile: NullDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:41 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import java.util.Vector; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.OperationNode; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Null" operation. * *

      The "Null" operation performs no processing. It merely propagates its * first source along the operation chain unmodified. There may be an * arbitrary number of sources but only the first one is passed along * so it must have the appropriate class type for the operation mode. * *

      This operation may be useful as a placeholder in operation chains * and in creating nodes to which PropertyGenerators may be * attached. This would enable non-image data nodes to be present in chains * without requiring that specific OperationDescriptors be * implemented for these operations. The PropertyGenerators * required would in this case be added locally to the nodes using the * addPropertyGenerator() method of the node. * *

      * * * * * * * * *
      Resource List
      Name Value
      GlobalName Null
      LocalName Null
      Vendor com.sun.media.jai
      Description An operation which does no processing.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/NullDescriptor.html
      Version 1.0

      * *

      No parameters are needed for this operation. * * @see javax.media.jai.OperationDescriptor * * @since JAI 1.1 */ public class NullDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Null"}, {"LocalName", "Null"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("NullDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/NullDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, }; private static final String[] supportedModes = { "rendered", "renderable" }; /** Constructor. */ public NullDescriptor() { super(resources, supportedModes, 1, null, null, null, null); } /** * If the PB has more than one source, replace it with a new PB which * has only one source equal to the first source of the input. * We want to support an arbitrary number of sources but only care that * there is at least one of the appropriate class. */ private static ParameterBlock foolSourceValidation(ParameterBlock args) { if(args.getNumSources() > 1) { Vector singleSource = new Vector(); singleSource.add(args.getSource(0)); args = new ParameterBlock(singleSource, args.getParameters()); } return args; } /** * Returns true if there is at least one source * and the first source is a RenderedImage or * RenderableImage. * * @throws IllegalArgumentException if args is null. * @throws IllegalArgumentException if msg is null * and the validation fails. */ protected boolean validateSources(String modeName, ParameterBlock args, StringBuffer msg) { if ( args == null || msg == null ) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } return super.validateSources(modeName, foolSourceValidation(args), msg); } /** * Calculates the region over which two distinct renderings * of the "Null" operation may be expected to differ. * *

      The operation returns an empty Shape if the first * source in each of the two ParameterBlocks are equal * according to the equals() method of the old source or * null for all other cases. * * @param modeName The name of the mode. * @param oldParamBlock The previous sources and parameters. * @param oldHints The previous hints. * @param newParamBlock The current sources and parameters. * @param newHints The current hints. * @param node The affected node in the processing chain (ignored). * * @return The region over which the data of two renderings of this * operation may be expected to be invalid or null * if there is no common region of validity. * A non-null empty region indicates that the * operation would produce identical data over the bounds of the * old rendering although perhaps not over the area occupied by * the tiles of the old rendering. * * @throws IllegalArgumentException if modeName * is null or if either oldParamBlock * or newParamBlock is null. * @throws IllegalArgumentException if oldParamBlock or * newParamBlock does not contain at least one source. */ public Object getInvalidRegion(String modeName, ParameterBlock oldParamBlock, RenderingHints oldHints, ParameterBlock newParamBlock, RenderingHints newHints, OperationNode node) { if (modeName == null || oldParamBlock == null || newParamBlock == null) { throw new IllegalArgumentException(JaiI18N.getString("NullDescriptor1")); } if (oldParamBlock.getNumSources() < 1 || newParamBlock.getNumSources() < 1) { throw new IllegalArgumentException(JaiI18N.getString("NullDescriptor2")); } return oldParamBlock.getSource(0).equals(newParamBlock.getSource(0)) ? new Rectangle() : null; } /** * An operation which does no processing. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Null", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); return JAI.create("Null", pb, hints); } /** * An operation which does no processing. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Null", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); return JAI.createRenderable("Null", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/PhaseDescriptor.java0000644000175000017500000001564310203035544027143 0ustar mathieumathieu/* * $RCSfile: PhaseDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:42 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.PropertyGenerator; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Phase" operation. * *

      The "Phase" operation computes the phase angle of each pixel of a * complex image. The source image must have an even number of bands, with * the even bands (0, 2, ...) representing the real parts and the odd bands * (1, 3, ...) the imaginary parts of each complex pixel. The destination * image has at most half the number of bands of the source image with each * sample in a pixel representing the phase angle of the corresponding complex * source sample. The angular values of the destination image are defined * for a given sample by the pseudocode: * *

      dst[x][y][b] = Math.atan2(src[x][y][2*b+1], src[x][y][2*b])
      * * where the number of bands b varies from zero to one less than the * number of bands in the destination image. * *

      For integral image datatypes, the result will be rounded and * scaled so the the "natural" arctangent range [-PI, PI) is remapped into * the range [0, MAX_VALUE); the result for floating point image datatypes * is the value returned by the atan2() method. * *

      "Phase" defines a PropertyGenerator that sets the "COMPLEX" * property of the image to java.lang.Boolean.FALSE, which may * be retrieved by calling the getProperty() method with * "COMPLEX" as the property name. * *

      * * * * * * * * *
      Resource List
      Name Value
      GlobalName Phase
      LocalName Phase
      Vendor com.sun.media.jai
      Description Computes the phase angle of each pixel of * an image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/PhaseDescriptor.html
      Version 1.0

      * *

      No parameters are needed for the "Phase" operation. * * @see javax.media.jai.OperationDescriptor */ public class PhaseDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Phase"}, {"LocalName", "Phase"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("PhaseDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/PhaseDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")} }; private static final String[] supportedModes = { "rendered", "renderable" }; /** Constructor. */ public PhaseDescriptor() { super(resources, supportedModes, 1, null, null, null, null); } /** * Validates the input source. * *

      In addition to the standard checks performed by the * superclass method, this method checks that the source image * has an even number of bands. */ protected boolean validateSources(String modeName, ParameterBlock args, StringBuffer msg) { if (!super.validateSources(modeName, args, msg)) { return false; } if (!modeName.equalsIgnoreCase("rendered")) return true; RenderedImage src = args.getRenderedSource(0); int bands = src.getSampleModel().getNumBands(); if (bands % 2 != 0) { msg.append(getName() + " " + JaiI18N.getString("PhaseDescriptor1")); return false; } return true; } /** * Returns an array of PropertyGenerators implementing * property inheritance for the "Phase" operation. * * @return An array of property generators. */ public PropertyGenerator[] getPropertyGenerators(String modeName) { PropertyGenerator[] pg = new PropertyGenerator[1]; pg[0] = new ComplexPropertyGenerator(); return pg; } /** * Computes the phase angle of each pixel of an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Phase", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); return JAI.create("Phase", pb, hints); } /** * Computes the phase angle of each pixel of an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderableOp createRenderable(RenderableImage source0, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Phase", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); return JAI.createRenderable("Phase", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/SubtractConstDescriptor.java0000644000175000017500000001731610203035544030700 0ustar mathieumathieu/* * $RCSfile: SubtractConstDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:45 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "SubtractConst" * operation. * *

      The SubtractConst operation takes one rendered or renderable * source image and an array of double constants, and subtracts a * constant from every pixel of its corresponding band of the * source. If the number of constants supplied is less than the number * of bands of the destination, then the constant from entry 0 is * applied to all the bands. Otherwise, a constant from a different * entry is applied to each band. * *

      By default, the destination image bounds, data type, and number * of bands are the same as the source image. If the result of the * operation underflows/overflows the minimum/maximum value supported * by the destination data type, then it will be clamped to the * minimum/maximum value respectively. * *

      The destination pixel values are defined by the pseudocode: *

       * if (constants.length < dstNumBands) {
       *     dst[x][y][b] = src[x][y][b] - constants[0];
       * } else {
       *     dst[x][y][b] = src[x][y][b] - constants[b];
       * }
       * 
      * *

      * * * * * * * * * *
      Resource List
      Name Value
      GlobalName SubtractConst
      LocalName SubtractConst
      Vendor com.sun.media.jai
      Description Subtracts constants from an * image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/SubtractConstDescriptor.html
      Version 1.0
      arg0Desc The constants to be subtracted.

      * *

      * * * * * *
      Parameter List
      Name Class TypeDefault Value
      constants double[]NO_PARAMETER_DEFAULT

      * * @see javax.media.jai.OperationDescriptor */ public class SubtractConstDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "SubtractConst"}, {"LocalName", "SubtractConst"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("SubtractConstDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/SubtractConstDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("SubtractConstDescriptor1")} }; /** * The parameter class list for this operation. * The number of constants provided should be either 1, in which case * this same constant is applied to all the source bands; or the same * number as the source bands, in which case one contant is applied * to each band. */ private static final Class[] paramClasses = { double[].class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "constants" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT }; /** Constructor. */ public SubtractConstDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** Returns true since renderable operation is supported. */ public boolean isRenderableSupported() { return true; } /** * Validates the input parameter. * *

      In addition to the standard checks performed by the * superclass method, this method checks that the length of the * "constants" array is at least 1. */ protected boolean validateParameters(ParameterBlock args, StringBuffer message) { if (!super.validateParameters(args, message)) { return false; } int length = ((double[])args.getObjectParameter(0)).length; if (length < 1) { message.append(getName() + " " + JaiI18N.getString("SubtractConstDescriptor2")); return false; } return true; } /** * Subtracts constants from an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param constants The constants to be subtracted. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if constants is null. */ public static RenderedOp create(RenderedImage source0, double[] constants, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("SubtractConst", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("constants", constants); return JAI.create("SubtractConst", pb, hints); } /** * Subtracts constants from an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param source0 RenderableImage source 0. * @param constants The constants to be subtracted. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if source0 is null. * @throws IllegalArgumentException if constants is null. */ public static RenderableOp createRenderable(RenderableImage source0, double[] constants, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("SubtractConst", RenderableRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("constants", constants); return JAI.createRenderable("SubtractConst", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/MeanDescriptor.java0000644000175000017500000001504710203035544026761 0ustar mathieumathieu/* * $RCSfile: MeanDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:39 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.ROI; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "Mean" operation. * *

      The Mean operation scans a specific region of a rendered image * and computes the mean pixel value for each band within that region * of the image. The image data pass through this operation unchanged. * *

      The region-wise mean pixel value for each band may be * retrieved by calling the getProperty method on this * operation with "mean" as the property name. The return value has * type double[#bands]. * *

      The region of interest (ROI) does not have to be a rectangle. * It may be null, in which case the entire image is * scanned to find the image-wise mean pixel value for each band. * *

      The set of pixels scanned may be further reduced by * specifying the "xPeriod" and "yPeriod" parameters that represent * the sampling rate along each axis. These variables may not be * less than 1. However, they may be null, in which * case the sampling rate is set to 1; that is, every pixel in the * ROI is processed. * *

      * * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName Mean
      LocalName Mean
      Vendor com.sun.media.jai
      Description Calculates the region-wise mean pixel value * for each band of an image.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/MeanDescriptor.html
      Version 1.0
      arg0Desc The region of the image to scan.
      arg1Desc The horizontal sampling rate, * may not be less than 1.
      arg2Desc The vertical sampling rate, * may not be less than 1.

      * *

      * * * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      roi javax.media.jai.ROInull
      xPeriod java.lang.Integer1
      yPeriod java.lang.Integer1

      * * @see javax.media.jai.ROI * @see javax.media.jai.OperationDescriptor */ public class MeanDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Mean"}, {"LocalName", "Mean"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("MeanDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/MeanDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("MeanDescriptor1")}, {"arg1Desc", JaiI18N.getString("MeanDescriptor2")}, {"arg2Desc", JaiI18N.getString("MeanDescriptor3")} }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "roi", "xPeriod", "yPeriod" }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { javax.media.jai.ROI.class, java.lang.Integer.class, java.lang.Integer.class }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { null, new Integer(1), new Integer(1) }; /** Constructor. */ public MeanDescriptor() { super(resources, 1, paramClasses, paramNames, paramDefaults); } /** * Returns the minimum legal value of a specified numeric parameter * for this operation. */ public Number getParamMinValue(int index) { if (index == 0) { return null; } else if (index == 1 || index == 2) { return new Integer(1); } else { throw new ArrayIndexOutOfBoundsException(); } } /** * Calculates the image-wise mean pixel value for each band of an image. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param source0 RenderedImage source 0. * @param roi The region of the image to scan. * May be null. * @param xPeriod The horizontal sampling rate, may not be less than 1. * May be null. * @param yPeriod The vertical sampling rate, may not be less than 1. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if source0 is null. */ public static RenderedOp create(RenderedImage source0, ROI roi, Integer xPeriod, Integer yPeriod, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Mean", RenderedRegistryMode.MODE_NAME); pb.setSource("source0", source0); pb.setParameter("roi", roi); pb.setParameter("xPeriod", xPeriod); pb.setParameter("yPeriod", yPeriod); return JAI.create("Mean", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/URLDescriptor.java0000644000175000017500000001201710203035544026535 0ustar mathieumathieu/* * $RCSfile: URLDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:46 $ * $State: Exp $ */ package javax.media.jai.operator; import com.sun.media.jai.codec.ImageDecodeParam; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.net.URL; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderedRegistryMode; /** * An OperationDescriptor describing the "URL" operation. * *

      The URL operation creates an output image whose source is * specified by a Uniform Resource Locator (URL). * *

      The allowable formats are those registered with the * com.sun.media.jai.codec.ImageCodec class. * *

      The second parameter contains an instance of * ImageDecodeParam to be used during the decoding. * It may be set to null in order to perform default * decoding, or equivalently may be omitted. * *

      The classes in the com.sun.media.jai.codec * package are not a committed part of the JAI API. Future releases * of JAI will make use of new classes in their place. This * class will change accordingly. * *

      * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName fileload
      LocalName fileload
      Vendor com.sun.media.jai
      Description Reads an image from a file.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/URLDescriptor.html
      Version 1.0
      arg0Desc The path of the file to read from.
      arg1Desc The ImageDecodeParam to use.

      * *

      * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      URL java.net.URLNO_PARAMETER_DEFAULT
      param com.sun.media.jai.codec.ImageDecodeParamnull

      * * @see javax.media.jai.OperationDescriptor */ public class URLDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation and * specify the parameter list for the "URL" operation. */ private static final String[][] resources = { {"GlobalName", "URL"}, {"LocalName", "URL"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("URLDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/URLDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("URLDescriptor1")}, {"arg1Desc", JaiI18N.getString("URLDescriptor2")} }; /** The parameter names for the "URL" operation. */ private static final String[] paramNames = { "URL", "param" }; /** The parameter class types for the "URL" operation. */ private static final Class[] paramClasses = { java.net.URL.class, com.sun.media.jai.codec.ImageDecodeParam.class }; /** The parameter default values for the "URL" operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT, null }; /** Constructor. */ public URLDescriptor() { super(resources, 0, paramClasses, paramNames, paramDefaults); } /** * Reads an image from a URL. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param URL The URL to read from. * @param param The ImageDecodeParam to use. * May be null. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if URL is null. */ public static RenderedOp create(URL URL, ImageDecodeParam param, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("URL", RenderedRegistryMode.MODE_NAME); pb.setParameter("URL", URL); pb.setParameter("param", param); return JAI.create("URL", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/javax.media.jai.operator.properties0000644000175000017500000007020010203035544032070 0ustar mathieumathieu# # $RCSfile: javax.media.jai.operator.properties,v $ # # Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. # # Use is subject to license terms. # # $Revision: 1.1 $ # $Date: 2005-02-11 04:57:47 $ # $State: Exp $ # DescriptorVersion=1.1 DescriptorVersion2=1.2 AWTImageDescriptor0=Converts a java.awt.Image into a rendered image. AWTImageDescriptor1=The AWT image to be converted. AbsoluteDescriptor0=Replaces the pixel values of an image by their absolute values. AddCollectionDescriptor0=Adds a collection of images. AddCollectionDescriptor1= operation requires the source collection to have at least 2 elements. AddCollectionDescriptor2= operation requires all elements in the source collection to be of class java.awt.image.RenderedImage. AddCollectionDescriptor3= operation requires all elements in the source collection to be of class java.awt.image.renderable.RenderableImage. AddConstDescriptor0=Adds constants to an image. AddConstDescriptor1=The constants to be added. AddConstDescriptor2= operation requires its parameter to have at least 1 array element. AddConstToCollectionDescriptor0=Adds constants to a collection of rendered images. AddConstToCollectionDescriptor1=The constants to be added. AddConstToCollectionDescriptor2=operation requires the source collection to have at least 1 element. AddConstToCollectionDescriptor3=operation requires all elements in the source collection to be of class java.awt.image.RenderedImage. AddConstToCollectionDescriptor4=operation requires its parameter to have at least 1 array element. AddDescriptor0=Adds two images. AffineDescriptor0=Performs interpolated affine transform on an image. AffineDescriptor1=The affine transform matrix. AffineDescriptor2=The interpolation method. AffineDescriptor3=The user-specified background values. AffineDescriptor4= operation can not invert the supplied transform parameter. AndConstDescriptor0=Logically \"ands\" an image with constants. AndConstDescriptor1=The constants to logically \"and\" with. AndConstDescriptor2= operation requires its source to have an integral data type. AndConstDescriptor3= operation requires its parameter to have at least 1 array element. AndDescriptor0=Logically \"ands\" two images. AndDescriptor1= operation requires 2 sources. AndDescriptor2= operation requires both sources to be a valid input; null is supplied. BinarizeDescriptor0 = Binarize an image from a threshold value. BinarizeDescriptor1 = Argment must be of type java.lang.Double. BinarizeDescriptor2 = source image must be single-banded. BMPDescriptor0=Reads an image from a BMP stream. BMPDescriptor1=The SeekableStream to read from. BandCombineDescriptor0=Performs arbitrary interband linear combination using a specified matrix. BandCombineDescriptor1=The matrix specifying the band combination. BandCombineDescriptor2=a row of the matrix does not have the correct number of entries, should be OpImage.getExpandedNumBands(source0.getSampleModel(), source0.getColorModel()) + 1. BandMergeDescriptor0=Merge (possibly multi-banded)images into a multibanded image. BandMergeDescriptor1=later. BandMergeDescriptor2=later. BandSelectDescriptor0=Selects n number of bands from an image. BandSelectDescriptor1=The indices of the selected bands. BandSelectDescriptor2= operation requires the band indices to have at least 1 array element. BandSelectDescriptor3= operation requires band indices to be less than the number of bands of the source image. BorderDescriptor0=Adds a border around an image. BorderDescriptor1=The image's left padding. BorderDescriptor2=The image's right padding. BorderDescriptor3=The image's top padding. BorderDescriptor4=The image's bottom padding. BorderDescriptor5=The border type. BorderDescriptor6=The mode name and ParameterBlocks may not be null. BorderDescriptor7=Insufficient number of sources in old or new ParameterBlock. BorderDescriptor8=Insufficient number of parameters in old or new ParameterBlock. BoxFilterDescriptor0=Performs special case convolution where each source pixel contributes equally to the intensity of the destination pixel. BoxFilterDescriptor1=The width of the box. BoxFilterDescriptor2=The height of the box. BoxFilterDescriptor3=The X position of the key element. BoxFilterDescriptor4=The Y position of the key element. ClampDescriptor0=Clamps the pixel values of an image to a specified range. ClampDescriptor1=The lower boundary for each band. ClampDescriptor2=The upper boundary for each band. ClampDescriptor3= operation requires each parameter to have at least 1 array element. ClampDescriptor4= operation requires each lower boundary to be less than or equal to its corresponding upper boundary. ColorConvertDescriptor0=Convert the color space of an image. ColorConvertDescriptor1=The destination color space. ColorConvertDescriptor2=The source image ColorModel may not be null. ColorQuantizerDescriptor0=Generates optimal lookup table and result image of nearest distance classification ColorQuantizerDescriptor1=The quantization algorithm to be used. ColorQuantizerDescriptor2=The expected maximum color number. ColorQuantizerDescriptor3=The histogram size for median-cut; The train cycle for NeuQuant; The maximum tree size for Oct-Tree. ColorQuantizerDescriptor4=The ROI in which the pixels are involved into the color quantization. ColorQuantizerDescriptor5=The subsampling rate in x-direction. ColorQuantizerDescriptor6=The subsampling rate in y-direction. ColorQuantizerDescriptor7=Invalid color quantization algorithm. CompositeDescriptor0=Composites two images based on an alpha mask. CompositeDescriptor1=The alpha image for the first source. CompositeDescriptor2=The alpha image for the second source. CompositeDescriptor3=True if alpha has been premultiplied to both sources and the destination. CompositeDescriptor4=Indicates if the destination image should include an extra alpha channel, and if so, should it be the first or last band. CompositeDescriptor8=operation requires the 2 sources to match in number of bands and data type. CompositeDescriptor9=operation requires 4 parameters. CompositeDescriptor10=operation requires source1Alpha parameter to be a valid input. CompositeDescriptor11=operation requires source1Alpha parameter to be of class javax.media.jai.PlanarImage. CompositeDescriptor12=operation requires its first source and alpha image to match in dimensions. CompositeDescriptor13=operation requires its first source and alpha image to match in data type. CompositeDescriptor14=operation requires source2Alpha parameter to be of class javax.media.jai.PlanarImage. CompositeDescriptor15=operation requires its second source and alpha image to match in dimensions. CompositeDescriptor16=operation requires its second source and alpha image to match in data type. CompositeDescriptor17=operation requires alphaPremultiplied parameter to be of class java.lang.Boolean. CompositeDescriptor18=operation requires alphaFirst parameter to be of class java.lang.Boolean. ConjugateDescriptor0=Computes the complex conjugate of a complex image. ConjugateDescriptor1=The source image must have an even number of bands. ConstantDescriptor0=Creates an image with constant pixel values. ConstantDescriptor1=Image width in pixels. ConstantDescriptor2=Image height in pixels. ConstantDescriptor3=The constant pixel band values. ConstantDescriptor4= operation requires parameter 2 to have at least 1 array element. ConstantDescriptor5=The width and height supplied in rendered mode must round to at least unity. ConstantDescriptor6=The width and height supplied in renderable mode must be positive. ConvolveDescriptor0=Performs kernel-based convolution on an image. ConvolveDescriptor1=The convolution kernel. CropDescriptor0=Performs cropping to a specified bounding box. CropDescriptor1=The x origin of the cropping operation. CropDescriptor2=The y origin of the cropping operation. CropDescriptor3=The width of the cropping operation. CropDescriptor4=The height of the cropping operation. CropDescriptor5=The rectangular crop area must not be empty. CropDescriptor6=The rectangular crop area must not be outside the image. DCTDescriptor0=Computes the discrete cosine transform of an image. DFTDescriptor0=Computes the discrete Fourier transform of an image. DFTDescriptor1=The type of scaling to perform. DFTDescriptor2=The nature of the data. DFTDescriptor3= operation does not support supplied scaling type. DFTDescriptor4= operation does not support supplied data nature. DFTDescriptor5= operation requires complex sources to have an even number of bands. DilateDescriptor0=Performs binary kernel based Dilate operation on the image. DilateDescriptor1=The binary convolution kernel. DivideByConstDescriptor0=Divides an image by constants. DivideByConstDescriptor1=The constants to be divided by. DivideByConstDescriptor2= operation requires its parameter to have at least 1 array element. DivideComplexDescriptor0=Compute the complex quotient of two images. DivideComplexDescriptor1= operation requires its sources to have an even number of bands. DivideDescriptor0=Divides one image by another image. DivideIntoConstDescriptor0=Divides an image into constants. DivideIntoConstDescriptor1=The constants to be divided into. DivideIntoConstDescriptor2= operation requires its parameter to have at least 1 array element. EncodeDescriptor0=Stores an image to an OutputStream. EncodeDescriptor1=The OutputStream to write to. EncodeDescriptor2=The format of the created file. EncodeDescriptor3=The encoding parameters. EncodeDescriptor4=The specified format has no associated registered ImageCodec. EncodeDescriptor5=No ImageEncoder is available for this format or this image cannot be encoded with the given encoding parameters. ErodeDescriptor0=Performs binary kernel based Erode operation on the image. ErodeDescriptor1=The binary convolution kernel. ErrorDiffusionDescriptor0=Performs error diffusion color quantization using a specified color map and error filter. ErrorDiffusionDescriptor1=The color map. ErrorDiffusionDescriptor2=The error filter kernel. ErrorDiffusionDescriptor3=Color map and source data types do not match. ErrorDiffusionDescriptor4=Color map and source band counts do not match. ErrorDiffusionDescriptor5=All color map bands must have the same offset. ErrorDiffusionDescriptor6=A kernel element which is not ignored has a value outside the range [0.0,1.0]. ErrorDiffusionDescriptor7=The sum of the values of kernel elements which are not ignored is not 1.0. ExpDescriptor0=Computes the exponential of the pixel values of an image. ExtremaDescriptor0=Finds the maximum and minimum pixel value in each band of an image. ExtremaDescriptor1=The region of the image to scan. ExtremaDescriptor2=The horizontal sampling rate, may not be less than 1. ExtremaDescriptor3=The vertical sampling rate, may not be less than 1. ExtremaDescriptor4=Whether to store extrema locations. ExtremaDescriptor5=Maximum number of run length codes to store. FPXDescriptor0=Reads an image from a FlashPix stream. FPXDescriptor1=The SeekableStream to read from. FPXDescriptor2=The FPXDecodeParam to use. FPXFileDescriptor0=Reads an image from a FlashPix file. FPXFileDescriptor1=The File to read from. FPXFileDescriptor2=The resolution number to read. FileLoadDescriptor0=Reads an image from a file. FileLoadDescriptor1=The path of the file to read from. FileLoadDescriptor2=File not found. FileLoadDescriptor3=File is not readable. FileLoadDescriptor4=The ImageDecodeParam to use. FileLoadDescriptor5=Boolean specifying if File existence should be checked locally FileStoreDescriptor0=Stores an image to a file. FileStoreDescriptor1=The path of the file to write to. FileStoreDescriptor2=The format of the file. FileStoreDescriptor3=The encoding parameters. FileStoreDescriptor4=The specified format has no associated registered ImageCodec. FileStoreDescriptor5=No ImageEncoder is available for this format or this image cannot be encoded with the given encoding parameters. FileStoreDescriptor6=The file name parameter is null. FileStoreDescriptor7=The specified file already exists with permissions that disallow overwriting. FileStoreDescriptor8=Cannot create image file. FileStoreDescriptor9=IOException encountered in file creation test: FileStoreDescriptor10=SecurityException encountered in file creation test: FileStoreDescriptor11=Boolean specifying whether check for file creation / writing locally should be done. FilteredSubsampleDescriptor0=Filters and subsamples an image. FilteredSubsampleDescriptor1=Subsample scale factors must be positive. FilteredSubsampleDescriptor2=Interpolation must be one of Nearest, Bilinear, Bicubic, or Bicubic2. FormatDescriptor0=Reformats an image. FormatDescriptor1= operation does not support supplied data type. Generic0=The input argument(s) may not be null. GIFDescriptor0=Reads an image from a GIF stream. GIFDescriptor1=The SeekableStream to read from. GradientMagnitudeDescriptor0=Computes the gradient of an image GradientMagnitudeDescriptor1=kernels not of same dimensions GradientMagnitudeDescriptor2=operation requires 2 parameters HistogramDescriptor0=Generates a histogram based on the pixel values within a specific region of an image. HistogramDescriptor1=The region of the image to be scanned. HistogramDescriptor2=The horizontal sampling rate; may not be less than 1. HistogramDescriptor3=The vertical sampling rate; may not be less than 1. HistogramDescriptor4=The number of bins for each band. HistogramDescriptor5=The lowest inclusive pixel value to be checked for each band. HistogramDescriptor6=The highest exclusive pixel value to be checked for each band. HistogramDescriptor7=The numBins must be greater than 0. HistogramDescriptor8=The lowValue must be less than its corresponding highValue. IDCTDescriptor0=Computes the inverse discrete cosine transform of an image. IDFTDescriptor0=Computes the inverse discrete Fourier transform of an image. IDFTDescriptor1=The type of scaling to perform. IDFTDescriptor2=The nature of the data. IDFTDescriptor3= operation does not support supplied scaling type. IDFTDescriptor4= operation does not support supplied data nature. IDFTDescriptor5= operation requires complex sources to have an even number of bands. IIPDescriptor0=Provides client support of the Internet Imaging Protocol in the rendered and renderable mode. IIPDescriptor1=The URL of the IIP image. IIPDescriptor2=The sub-images to be used by the server for images at each resolution level. IIPDescriptor3=The filtering value. IIPDescriptor4=The color twist matrix. IIPDescriptor5=The contrast value. IIPDescriptor6=The source rectangle of interest in rendering-independent coordinates. IIPDescriptor7=The rendering-independent spatial orientation transform. IIPDescriptor8=The aspect ratio of the destination image. IIPDescriptor9=The destination rectangle of interest in rendering-independent coordinates. IIPDescriptor10=The counterclockwise rotation angle to be applied to the destination. IIPDescriptor11=The mirror axis. IIPDescriptor12=The ICC profile used to represent the color space of the source image. IIPDescriptor13=The JPEG quality factor. IIPDescriptor14=The JPEG compression group index number. IIPDescriptor15=requires the URL string to specify a valid protocol. IIPDescriptor16=requires the colorTwist array to have at least 16 elements. IIPDescriptor17=requires both the width and height of the rectangle that marks the region of interest to be greater than 0. IIPDescriptor18=requires the rotation angle for the destination image to be one of 0, 90, 180, or 270 degrees. IIPDescriptor19=requires the mirror axis to be either X or Y. IIPDescriptor20=requires the contrast value to be greater than or equal to 1.0F. IIPDescriptor21=requires the aspect ratio to be positive. IIPDescriptor22=requires the JPEG quality factor to be in the range [0,100]. IIPDescriptor23=requires the JPEG table index to be in the range [1,255]. IIPDescriptor24=requires the transform parameter to be invertible. IIPResolutionDescriptor0=Provides client support of the Internet Imaging Protocol in the rendered mode. IIPResolutionDescriptor1=The URL of the IIP image. IIPResolutionDescriptor2=The resolution level to request. IIPResolutionDescriptor3=The sub-image to be used by the server. ImageFunctionDescriptor0=Generates an image from a functional description. ImageFunctionDescriptor1=The functional description. ImageFunctionDescriptor2=The image width. ImageFunctionDescriptor3=The image height. ImageFunctionDescriptor4=The X scale factor. ImageFunctionDescriptor5=The Y scale factor. ImageFunctionDescriptor6=The X translation. ImageFunctionDescriptor7=The Y translation. InvertDescriptor0=Inverts the pixel values of an image. JPEGDescriptor0=Reads a standard JFIF (JPEG) stream. JPEGDescriptor1=The SeekableStream to read from. LogDescriptor0=Computes the natural logarithm of the pixel values of an image. LookupDescriptor0=Performs general table lookup on an image. LookupDescriptor1=The lookup table the source image is passed through. LookupDescriptor2= operation requires its source to have an integral data type. MagnitudeDescriptor0=Find the magnitude of each pixel of an image. MagnitudeDescriptor1= operation requires its source to have an even number of bands. MagnitudeSquaredDescriptor0=Computes the squared magnitude of each pixel of a complex image. MagnitudeSquaredDescriptor1= operation requires its source to have an even number of bands. MatchCDFDescriptor0=Matches pixel values to a supplied CDF. MatchCDFDescriptor1=The source histogram property is undefined. MatchCDFDescriptor2=The source histogram property returns an object which is not a Histogram. MatchCDFDescriptor3=The length of the CDF array must equal the number of bands in the source Histogram. MatchCDFDescriptor4=The length of each component array of the CDF must equal the number of bins in the corresponding band of the source Histogram. MatchCDFDescriptor5=All CDF values must be non-negative. MatchCDFDescriptor6=The CDF array must represent a non-decreasing sequence. MatchCDFDescriptor7=The ultimate CDF array value in each band must be 1. MaxDescriptor0=Computes the pixel-wise maximum of two images. MaxFilterDescriptor0=Performs max filtering on an image. MaxFilterDescriptor1=The shape of the mask to be used for Max Filtering. MaxFilterDescriptor2=The size (width/height) of the mask to be used in Max Filtering. MeanDescriptor0=Calculates the image-wise mean pixel value for each band of an image. MeanDescriptor1=The region of the image to scan. MeanDescriptor2=The horizontal sampling rate, may not be less than 1. MeanDescriptor3=The vertical sampling rate, may not be less than 1. MeanDescriptor4=operation requires at least 3 parameters. MeanDescriptor5=operation requires the first parameter to be of javax.media.jai.ROI class. MeanDescriptor6=operation requires the second and third parameter to be of java.lang.Integer class. MeanDescriptor7=operation requires xPeriod and yPeriod parameters to be greater than or equal to 1. MedianFilterDescriptor0=Performs median filtering on an image. MedianFilterDescriptor1=The mask shape to be used for Median Filtering. MedianFilterDescriptor2=The mask size to be used for Median Filtering. MedianFilterDescriptor3= operation does not support supplied mask shape. MedianFilterDescriptor4= operation requires its mask size to be greater than 0. MinDescriptor0=Computes the pixel-wise minimum of two images. MinFilterDescriptor0=Performs min filtering on an image. MinFilterDescriptor1=The shape of the mask to be used for Min Filtering. MinFilterDescriptor2=The size (width/height) of the mask to be used in Min Filtering. MosaicDescriptor0=Creates a mosaic of two or more rendered images. MosaicDescriptor1=Mosaicking type. MosaicDescriptor2=Source alpha masks. MosaicDescriptor3=Source region of interest masks. MosaicDescriptor4=Source threshold values. MosaicDescriptor5=Destination background value. MultiplyComplexDescriptor0=Computes the complex product of two images. MultiplyComplexDescriptor1= operation requires its sources to have an even number of bands. MultiplyConstDescriptor0=Multiplies an image by constants. MultiplyConstDescriptor1=The constants to be multiplied. MultiplyConstDescriptor2= operation requires its parameter to have at least 1 array element. MultiplyDescriptor0=Multiplies two images. NotDescriptor0=Logically \"nots\" an image. NotDescriptor1= operation requires its source to have an integral data type. NullDescriptor0=An operation which does no processing. NullDescriptor1=The mode name and ParameterBlocks may not be null. NullDescriptor2=The old and new ParameterBlocks must each contain at least one source. OrConstDescriptor0=Logically \"ors\" an image with constants. OrConstDescriptor1=The constants to logically \"or\" with. OrConstDescriptor2= operation requires its source to have an integral data type. OrConstDescriptor3= operation requires its parameter to have at least 1 array element. OrDescriptor0=Logically \"ors\" two images. OrDescriptor1= operation requires 2 sources. OrderedDitherDescriptor0=Performs ordered dither color quantization using a specified color cube and dither mask. OrderedDitherDescriptor1=The color cube. OrderedDitherDescriptor2=The dither mask. OrderedDitherDescriptor3=The data type of the color cube does not match that of the source image. OrderedDitherDescriptor4=The number of bands of the color cube does not match that of the source image. OrderedDitherDescriptor5=The dither mask array length does not match the number of bands in the source image. OrderedDitherDescriptor6=Not all dither mask array elements have the same width and height. OrderedDitherDescriptor7=A dither mask matrix value is outside the allowable range [0.0, 1.0]. OverlayDescriptor0=Overlays one image on top of another. OverlayDescriptor1=operation requires the two sources to match in number of bands and data type. PNGDescriptor0=Reads a standard JFIF (PNG) file. PNGDescriptor1=The SeekableStream to read from. PNGDescriptor2=The PNGDecodeParam to use. PNMDescriptor0=Reads a standard PNM file. PNMDescriptor1=A SeekableStream representing the PNM file. PatternDescriptor0=Defines an image with a repeated pattern. PatternDescriptor1=The width of the image in pixels. PatternDescriptor2=The height of the image in pixels. PeriodicShiftDescriptor0=Computes the periodic translation of an image. PeriodicShiftDescriptor1=The displacement in the X direction. PeriodicShiftDescriptor2=The displacement in the Y direction. PeriodicShiftDescriptor3=Shift values must be non-negative and less than the corresponding image dimension. PhaseDescriptor0=Computes the phase angle of each pixel of an image. PhaseDescriptor1= operation requires its source to have an even number of bands. PiecewiseDescriptor0=Applies a piecewise pixel value mapping. PiecewiseDescriptor1=The breakpoint array must represent 1 band or the number of bands in the source image. PiecewiseDescriptor2=The second dimension of the breakpoint array must be of length 2. PiecewiseDescriptor3=The number of breakpoint abscissas must equal the number of breakpoint ordinates in each band. PiecewiseDescriptor4=The breakpoint abscissas must be monotonically increasing. PolarToComplexDescriptor0=Computes a complex image from a magnitude and a phase image. PolarToComplexDescriptor1=operation requires its sources to have an equal number of bands. RenderableDescriptor0=Produces a RenderableImage from a RenderedImage. RenderableDescriptor1=The operation chain used to derive the lower resolution images. RenderableDescriptor2=The maximum dimension of the lowest resolution pyramid level. RenderableDescriptor3=The minimum rendering-independent X coordinate of the destination. RenderableDescriptor4=The minimum rendering-independent Y coordinate of the destination. RenderableDescriptor5=The rendering-independent height. RenderableDescriptor6=The maximum dimension of the lowest level must be positive. RenderableDescriptor7=The rendering-independent height must be positive. RescaleDescriptor0=Maps the pixels values of an image from one range to another range. RescaleDescriptor1=The per-band constants to multiply by. RescaleDescriptor2=The per-band offsets to be added. RescaleDescriptor3=constants.length must be at least 1. RescaleDescriptor4=offsets.length must be at least 1. RGBToIHSDescriptor0=Convert an image from RGB to IHS color space. RotateDescriptor0=Rotates an image. RotateDescriptor1=The X origin to rotate about. RotateDescriptor2=The Y origin to rotate about. RotateDescriptor3=The rotation angle in radians. RotateDescriptor4=The interpolation method. RotateDescriptor5=The user-specified background values. ScaleDescriptor0=Resizes an image. ScaleDescriptor1=The X scale factor. ScaleDescriptor2=The Y scale factor. ScaleDescriptor3=The X translation. ScaleDescriptor4=The Y translation. ScaleDescriptor5=The interpolation method for resampling. ScaleDescriptor6= operation requires both scale factors be greater than 0. ShearDescriptor0=Shears an image. ShearDescriptor1=The shear value. ShearDescriptor2=The shear direction. ShearDescriptor3=The X translation. ShearDescriptor4=The Y translation. ShearDescriptor5=The interpolation method for resampling. ShearDescriptor6=The user-specified background values. SobelDescriptor0=Performs Sobel edge detection on the image. StreamDescriptor0=Reads an image from a SeekableStream. StreamDescriptor1=The SeekableStream to read from. StreamDescriptor2=The ImageDecodeParam to use. SubsampleBinaryToGray0=To subsamples binary image to gray; reverse of dithering. SubsampleBinaryToGray1=scaleX must be between 0 and 1, excluding 0. SubsampleBinaryToGray2=scaleY must be between 0 and 1, excluding 0. SubsampleBinaryToGray3=src image must have MultiPixelPackedSampleModel. SubsampleAverageDescriptor0=Subsamples an image by averaging over a moving window. SubsampleAverageDescriptor1=The X scale factor. SubsampleAverageDescriptor2=The Y scale factor. SubsampleAverageDescriptor3=modeName may not be null. SubsampleAveragePropertyGenerator0=propertyName may not be null SubsampleAveragePropertyGenerator1=opNode may not be null SubsampleAveragePropertyGenerator2=Neither parameter may be null. SubsampleAveragePropertyGenerator3= is not a supported class. SubtractConstDescriptor0=Subtracts constants from an image. SubtractConstDescriptor1=The constants to be subtracted. SubtractConstDescriptor2= operation requires its parameter to have at least 1 array element. SubtractDescriptor0=Subtracts one image from another image. SubtractFromConstDescriptor0=Subtracts an image from constants. SubtractFromConstDescriptor1=The constants to be subtracted from. SubtractFromConstDescriptor2= operation requires its parameter to have at least 1 array element. TIFFDescriptor0=Reads TIFF 6.0 data from an SeekableStream. TIFFDescriptor1=The SeekableStream to read from. TIFFDescriptor2=The TIFFDecodeParam to use. TIFFDescriptor3=The page to be decoded. ThresholdDescriptor0=Maps the pixels whose value falls between a low value and a high value to a constant. ThresholdDescriptor1=The low value. ThresholdDescriptor2=The high value. ThresholdDescriptor3=The constant the pixels are mapped to. ThresholdDescriptor4=operation requires 3 input parameters. ThresholdDescriptor5=operation requires all parameters to be valid input; a null is supplied. ThresholdDescriptor6=operation requires all parameters to be of type double[]. ThresholdDescriptor7=operation requires all parameters to have at least 1 array element. TransposeDescriptor0=Reflects an image in a specified direction or rotates an image in multiples of 90 degrees. TransposeDescriptor1=The The type of flip operation to be performed. TranslateDescriptor0=Moves an image to a new location. TranslateDescriptor1=The displacement in X direction. TranslateDescriptor2=The displacement in Y direction. TranslateDescriptor3=The interpolation method. UnsharpMaskDescriptor0=Performs UnsharpMask operation on the image. UnsharpMaskDescriptor1=The low-pass convolution kernel. UnsharpMaskDescriptor2=The sharpening value. URLDescriptor0=Reads an image from a URL. URLDescriptor1=The URL to read from. URLDescriptor2=The ImageDecodeParam to use. WarpDescriptor0=Warps an image according to a specified Warp object. WarpDescriptor1=The warp object. WarpDescriptor2=The interpolation method. WarpDescriptor3=The user-specified background values. XorConstDescriptor0=Logically \"xors\" an image with constants. XorConstDescriptor1=The constants to logically \"xor\" with. XorConstDescriptor2= Operation requires its source to have an integral data type. XorConstDescriptor3= Operation requires its parameter to have at least 1 array element. XorDescriptor0=Logically \"xors\" two images. XorDescriptor1= operation requires 2 sources. jai-core-1.1.4/src/share/classes/javax/media/jai/operator/MedianFilterShape.java0000644000175000017500000000155410203035544027364 0ustar mathieumathieu/* * $RCSfile: MedianFilterShape.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:39 $ * $State: Exp $ */ package javax.media.jai.operator; import javax.media.jai.EnumeratedParameter; /** * Class used to represent the acceptable values of the "maskShape" * parameter of the "MedianFilter" operation. Acceptable values for the * "maskShape" parameter are defined in the MedianFilterDescriptor * by the constants MEDIAN_MASK_SQUARE, * MEDIAN_MASK_PLUS, * MEDIAN_MASK_X, and * MEDIAN_MASK_SQUARE_SEPARABLE. * * @since JAI 1.1 */ public final class MedianFilterShape extends EnumeratedParameter { MedianFilterShape(String name, int value) { super(name, value); } } jai-core-1.1.4/src/share/classes/javax/media/jai/operator/ConstantDescriptor.java0000644000175000017500000002213410203035544027665 0ustar mathieumathieu/* * $RCSfile: ConstantDescriptor.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:32 $ * $State: Exp $ */ package javax.media.jai.operator; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderableImage; import javax.media.jai.JAI; import javax.media.jai.OperationDescriptorImpl; import javax.media.jai.ParameterBlockJAI; import javax.media.jai.RenderableOp; import javax.media.jai.RenderedOp; import javax.media.jai.registry.RenderableRegistryMode; import javax.media.jai.registry.RenderedRegistryMode; import javax.media.jai.util.Range; /** * An OperationDescriptor describing the "Constant" * operation. * *

      The Constant operation creates a multi-banded, tiled rendered * image, where all the pixels from the same band have a constant * value. The width and height of the image must be specified and * greater than 0. At least one constant must be supplied. The number * of bands of the image is determined by the number of constant pixel * values supplied in the "bandValues" parameter. The data type is * determined by the type of the constants; this means all elements of * the bandValues array must be of the same type. * *

      If the bandValues array is a Short * array, then TYPE_USHORT is used if all values are * non-negative; otherwise TYPE_SHORT is used. * *

      * * * * * * * * * * * *
      Resource List
      Name Value
      GlobalName Constant
      LocalName Constant
      Vendor com.sun.media.jai
      Description Creates an image with * constant pixel values.
      DocURL http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ConstantDescriptor.html
      Version 1.0
      arg0Desc Image width in pixels.
      arg1Desc Image height in pixels.
      arg2Desc The constant pixel band values.

      * *

      * * * * * * * * * *
      Parameter List
      Name Class TypeDefault Value
      width java.lang.FloatNO_PARAMETER_DEFAULT
      height java.lang.FloatNO_PARAMETER_DEFAULT
      bandValues java.lang.Number[]NO_PARAMETER_DEFAULT

      * * @see javax.media.jai.OperationDescriptor */ public class ConstantDescriptor extends OperationDescriptorImpl { /** * The resource strings that provide the general documentation * and specify the parameter list for this operation. */ private static final String[][] resources = { {"GlobalName", "Constant"}, {"LocalName", "Constant"}, {"Vendor", "com.sun.media.jai"}, {"Description", JaiI18N.getString("ConstantDescriptor0")}, {"DocURL", "http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/operator/ConstantDescriptor.html"}, {"Version", JaiI18N.getString("DescriptorVersion")}, {"arg0Desc", JaiI18N.getString("ConstantDescriptor1")}, {"arg1Desc", JaiI18N.getString("ConstantDescriptor2")}, {"arg2Desc", JaiI18N.getString("ConstantDescriptor3")} }; /** The parameter class list for this operation. */ private static final Class[] paramClasses = { java.lang.Float.class, java.lang.Float.class, java.lang.Number[].class }; /** The parameter name list for this operation. */ private static final String[] paramNames = { "width", "height", "bandValues" }; /** The parameter default value list for this operation. */ private static final Object[] paramDefaults = { NO_PARAMETER_DEFAULT, NO_PARAMETER_DEFAULT, NO_PARAMETER_DEFAULT }; private static final String[] supportedModes = { "rendered", "renderable" }; private static final Object[] validParamValues = { new Range(Float.class, new Float(0.0F), false, null, false), new Range(Float.class, new Float(0.0F), false, null, false), null }; /** Constructor. */ public ConstantDescriptor() { super(resources, supportedModes, 0, paramNames, paramClasses, paramDefaults, validParamValues); } /** * Validates the input parameters. * *

      In addition to the standard checks performed by the * superclass method, this method checks that "width" and "height" * are greater than 0 (for rendered mode) and that "bandValues" has * length at least 1. */ protected boolean validateParameters(String modeName, ParameterBlock args, StringBuffer message) { if (!super.validateParameters(modeName, args, message)) { return false; } int length = ((Number[])args.getObjectParameter(2)).length; if (length < 1) { message.append(getName() + " " + JaiI18N.getString("ConstantDescriptor4")); return false; } if (modeName.equalsIgnoreCase("rendered")) { int width = Math.round(args.getFloatParameter(0)); int height = Math.round(args.getFloatParameter(1)); if ((width < 1) || (height < 1)) { message.append(getName() + " " + JaiI18N.getString("ConstantDescriptor5")); return false; } } else if (modeName.equalsIgnoreCase("renderable")) { float width = args.getFloatParameter(0); float height = args.getFloatParameter(1); if ((width <= 0.0F) || (height <= 0.0F)) { message.append(getName() + " " + JaiI18N.getString("ConstantDescriptor6")); return false; } } return true; } /** * Creates an image with constant pixel values. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#create(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderedOp * * @param width Image width in pixels. * @param height Image height in pixels. * @param bandValues The constant pixel band values. * @param hints The RenderingHints to use. * May be null. * @return The RenderedOp destination. * @throws IllegalArgumentException if width is null. * @throws IllegalArgumentException if height is null. * @throws IllegalArgumentException if bandValues is null. */ public static RenderedOp create(Float width, Float height, Number[] bandValues, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Constant", RenderedRegistryMode.MODE_NAME); pb.setParameter("width", width); pb.setParameter("height", height); pb.setParameter("bandValues", bandValues); return JAI.create("Constant", pb, hints); } /** * Creates an image with constant pixel values. * *

      Creates a ParameterBlockJAI from all * supplied arguments except hints and invokes * {@link JAI#createRenderable(String,ParameterBlock,RenderingHints)}. * * @see JAI * @see ParameterBlockJAI * @see RenderableOp * * @param width Image width in pixels. * @param height Image height in pixels. * @param bandValues The constant pixel band values. * @param hints The RenderingHints to use. * May be null. * @return The RenderableOp destination. * @throws IllegalArgumentException if width is null. * @throws IllegalArgumentException if height is null. * @throws IllegalArgumentException if bandValues is null. */ public static RenderableOp createRenderable(Float width, Float height, Number[] bandValues, RenderingHints hints) { ParameterBlockJAI pb = new ParameterBlockJAI("Constant", RenderableRegistryMode.MODE_NAME); pb.setParameter("width", width); pb.setParameter("height", height); pb.setParameter("bandValues", bandValues); return JAI.createRenderable("Constant", pb, hints); } } jai-core-1.1.4/src/share/classes/javax/media/jai/CollectionOp.java0000644000175000017500000016633710444633025024617 0ustar mathieumathieu/* * $RCSfile: CollectionOp.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2006-06-16 22:52:05 $ * $State: Exp $ */ package javax.media.jai; import java.awt.RenderingHints; import java.awt.image.RenderedImage; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderContext; import java.awt.image.renderable.RenderableImage; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.Locale; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.Vector; import javax.media.jai.registry.CIFRegistry; import javax.media.jai.registry.RCIFRegistry; import javax.media.jai.registry.CollectionRegistryMode; import javax.media.jai.registry.RenderableCollectionRegistryMode; import com.sun.media.jai.util.ImageUtil; import com.sun.media.jai.util.PropertyUtil; /** * A node in a CollectionImage chain. A CollectionOp * stores an operation name, a ParameterBlock containing sources * and parameters, and a RenderingHints containing hints which * may be used in rendering the node. A set of nodes may be joined together * via the source Vectors within their respective * ParameterBlocks to form a directed acyclic * graph (DAG). The topology, i.e., connectivity, of the graph may be * altered by changing the node's sources. The operation name, parameters, * and rendering hints may also be changed. A CollectionOp may * be used in either the rendered or the renderable mode for * Collections, i.e., "collection" or "renderableCollection" * mode, respectively. * *

      A CollectionOp may be constructed directly as, for example, *

       * 
       * Collection srcCol;
       * double[] constants;
       * ParameterBlock pb =
       *     (new ParameterBlock()).addSource(srcCol).add(constants);
       * CollectionOp node =
       *     new CollectionOp("addConstToCollection", pb, null);
       * 
       * 
      * or by the createCollection or createCollectionNS() * "collection" mode methods or the createRenderableCollection() * or createRenderableCollectionNS() "renderableCollection" mode * methods defined in the JAI class. The difference between * direct construction of a node and creation via a convenience method is that * in the latter case: * *
        *
      1. It is verified that the operation supports the appropriate mode, * i.e., "collection" or "renderableCollection".
      2. *
      3. It is verified that the operation generates a * CollectionImage, a RenderedImage * ("collection" mode only), or a RenderableImage * ("renderableCollection" mode only).
      4. *
      5. Global RenderingHints maintained by the JAI * instance are merged with the local RenderingHints with the * local hints taking precedence.
      6. *
      7. Using the validateArguments() method of the associated * OperationDescriptor, the arguments (sources and parameters) * are validated as being compatible with the specified operation in * the appropriate mode.
      8. *
      9. If the arguments are valid, then the CollectionOp is * created; otherwise any source Collections are * "unwrapped" until a valid argument list is obtained or it is * determined that such is impossible. *
      10. If the operation is in the rendered mode and is defined to be * "immediate" (the isImmediate() method of the corresponding * OperationDescriptor returns true) * then the node is rendered.
      11. *
      * *

      When a chain of nodes is rendered by any means a "parallel" chain of * CollectionImages is created. Each node in the chain of * CollectionOps corresponds to a node in the chain of * CollectionImages. Collection methods invoked * on the CollectionOp are in general forwarded to the associated * CollectionImage which is referred to as the rendering * of the node. The rendering of the node may be a rendered or renderable * CollectionImage, i.e., eventually contain * RenderedImages or RenderableImages, respectively, * depending on the mode in which the node is used. * *

      The translation between CollectionOp chains and * CollectionImage chains makes use of two levels of * indirection provided by the OperationRegistry and either the * CollectionImageFactory (CIF) or the * RenderableCollectionImageFactory (RCIF) facilities. * First, the local OperationRegistry is used to map the * operation name into a CIF or RCIF. This factory then constructs * a CollectionImage. The local * OperationRegistry is used in order to take advantage * of the best possible implementation of the operation. * *

      A node may be rendered explicitly by invoking the method * getCollection() which also returns the rendering of the * node. A node may be rendered implicitly by invoking any method * defined in the Collection interface. A rendering of a * node may also be obtained by means of the createInstance() * method. This method returns a Collection rendering without * marking the node as having been rendered. If the node is not * marked as rendered then it will not fire * CollectionChangeEvents as described below. * *

      CollectionOp nodes may participate in Java Bean-style * events. The PropertyChangeEmitter methods may be used * to register and unregister PropertyChangeListeners. * CollectionOps are also PropertyChangeListeners * so that they may be registered as listeners of other * PropertyChangeEmitters or the equivalent. Each * CollectionOp also automatically receives any * CollectionChangeEvents emitted by any of its sources which * are also CollectionOps and RenderingChangeEvents * from any RenderedOp sources. * *

      Certain PropertyChangeEvents may be emitted by the * CollectionOp. These include the * PropertyChangeEventJAIs and * PropertySourceChangeEvents required by virtue of implementing * the OperationNode interface. Additionally a * CollectionChangeEvent may be emitted if the node is * operating in the "collection" mode, has already been rendered, and one of * the following conditions is satisfied: *

        *
      • any of the critical attributes is changed (edited), i.e., the * operation name, operation registry, node sources, parameters, or rendering * hints; or
      • *
      • the node receives a CollectionChangeEvent from one of * its CollectionOp sources or a RenderingChangeEvent * from one if its RenderedOp.
      • *
      * In either case the following sequence of actions should occur: *
        *
      1. A. If the operation name or the registry has changed, a new * CollectionImage will be generated by the * OperationRegistry for the new operation. *
        B. If the operation name has not changed, an attempt will be made to * re-use some elements of the previously generated * CollectionImage by invoking update() on the * CollectionImageFactory which generated it. If this attempt * fails, a new CollectionImage for this operation will be * requested from the OperationRegistry.
      2. *
      3. A CollectionChangeEvent will be fired to all registered * listeners of the "Collection" PropertyChangeEvent and to all * sinks which are PropertyChangeListeners. The new and old * values set on the event object correspond to the previous and current * CollectionImages, respectively, associated with this node.
      4. *
      * *

      CollectionOp nodes are WritablePropertySources * and so manage a name-value database of image meta-data also known as image * properties. Properties may be set on and requested from a node. The * value of a property not explicitly set on the node (via * setProperty()) is obtained from the property environment of * the node. When a property is derived from the property environment it is * cached locally to ensure synchronization, i.e., that properties do not * change spontaneously if for example the same property is modified upstream. * *

      The property environment of a CollectionOp is initially * derived from that of the corresponding OperationDescriptor * as maintained by the OperationRegistry. It may be modified * locally by adding PropertyGenerators, directives to copy * certain properties from specific sources, or requests to suppress certain * properties. These modifications per se cannot be undone directly but * may be eliminated as a side effect of other changes to the node as * described below. * *

      When a property value is requested an attempt will be made to derive * it from the several entities in the following order of precedence: *

        *
      1. local properties;
      2. *
      3. the rendering of the node if it is a PropertySource;
      4. *
      5. any registered PropertyGenerators, or *
        a source specified via a copy-from-source directive;
      6. *
      7. the first source which defines the property.
      8. *
      * Local properties are those which have been cached locally either by virtue * of direct invocation of setProperty() or due to caching of a * property derived from the property environment. * *

      All dynamically computed properties of a CollectionOp which * have been cached locally, i.e., those cached properties which were not set * by an explicit call to setProperty(), will be cleared when any * of the critical attributes of the node is edited. By implication these * properties will also be cleared when a CollectionChangeEvent * is received from any node source. The property environment or the cached * properties may also be cleared by invoking resetProperties(). * * @see CollectionImage * @see OperationRegistry * @see RenderableOp * @see RenderedOp * */ public class CollectionOp extends CollectionImage implements OperationNode, PropertyChangeListener { /** * An object to assist in implementing OperationNode. * * @since JAI 1.1 */ protected OperationNodeSupport nodeSupport; /** * The PropertySource containing the combined properties * of all of the node's sources. * * @since JAI 1.1 */ protected PropertySource thePropertySource; /** * Flag indicating whether the operation is being instantiated in * renderable mode. * * @since JAI 1.1 */ protected boolean isRenderable = false; /** * The RenderingHints when the node was last rendered, i.e., when * "theImage" was set to its current value. */ private transient RenderingHints oldHints; /** Node event names. */ private static Set nodeEventNames = null; static { nodeEventNames = new HashSet(); nodeEventNames.add("operationname"); nodeEventNames.add("operationregistry"); nodeEventNames.add("parameterblock"); nodeEventNames.add("sources"); nodeEventNames.add("parameters"); nodeEventNames.add("renderinghints"); } /** * Constructs a CollectionOp that will be used to * instantiate a particular Collection operation from a given * operation registry, an operation name, a ParameterBlock, * and a set of rendering hints. * *

      This method does not validate the contents of the supplied * ParameterBlock. The caller should ensure that * the sources and parameters in the ParameterBlock * are suitable for the operation this node represents; otherwise * some form of error or exception may occur at the time of rendering. * *

      The ParameterBlock may include * DeferredData parameters. These will not be evaluated * until their values are actually required, i.e., when a collection * rendering is requested. * *

      The node is added automatically as a sink of any * PlanarImage or CollectionImage sources. * * @param registry The OperationRegistry to be used for * instantiation. if null, the default registry * is used. Saved by reference. * @param opName The operation name. Saved by reference. * @param pb The sources and other parameters. If null, * it is assumed that this node has no sources and parameters. * This parameter is cloned. * @param hints The rendering hints. If null, it is assumed * that no hints are associated with the rendering. * This parameter is cloned. * @param isRenderable Whether the operation is being executed in * renderable mode. * * @throws IllegalArgumentException if opName * is null. * * @since JAI 1.1 */ public CollectionOp(OperationRegistry registry, String opName, ParameterBlock pb, RenderingHints hints, boolean isRenderable) { if (opName == null) { throw new IllegalArgumentException(JaiI18N.getString("Generic0")); } if(pb == null) { // Ensure that the PB is non-null. pb = new ParameterBlock(); } else { // Clone the PB per the doc. pb = (ParameterBlock)pb.clone(); } if(hints != null) { // Clone the hints per the doc. hints = (RenderingHints)hints.clone(); } // Initialize the various helper objects. eventManager = new PropertyChangeSupportJAI(this); properties = new WritablePropertySourceImpl(null, null, eventManager); nodeSupport = new OperationNodeSupport(getRegistryModeName(), opName, registry, pb, hints, eventManager); this.isRenderable = isRenderable; // Add the node as a PropertyChangeListener of itself for // the critical attributes of the node. Case is ignored // in the property names but infix caps are used here anyway. addPropertyChangeListener("OperationName", this); addPropertyChangeListener("OperationRegistry", this); addPropertyChangeListener("ParameterBlock", this); addPropertyChangeListener("Sources", this); addPropertyChangeListener("Parameters", this); addPropertyChangeListener("RenderingHints", this); // Add self as a sink of any CollectionImage or PlanarImage sources. Vector nodeSources = pb.getSources(); if(nodeSources != null) { Iterator it = nodeSources.iterator(); while(it.hasNext()) { Object src = it.next(); if(src instanceof CollectionImage) { ((CollectionImage)src).addSink(this); } else if(src instanceof PlanarImage) { ((PlanarImage)src).addSink(this); } } } } /** * Constructs a CollectionOp that will be used to * instantiate a particular Collection operation from a given * operation registry, an operation name, a ParameterBlock, * and a set of rendering hints. The operation will use the rendered mode. * *

      This method does not validate the contents of the supplied * ParameterBlock. The caller should ensure that * the sources and parameters in the ParameterBlock * are suitable for the operation this node represents; otherwise * some form of error or exception may occur at the time of rendering. * *

      The ParameterBlock may include * DeferredData parameters. These will not be evaluated * until their values are actually required, i.e., when a collection * rendering is requested. * * @param registry The OperationRegistry to be used for * instantiation. if null, the default registry * is used. Saved by reference. * @param opName The operation name. Saved by reference. * @param pb The sources and other parameters. If null, * it is assumed that this node has no sources and parameters. * This parameter is cloned. * @param hints The rendering hints. If null, it is assumed * that no hints are associated with the rendering. * This parameter is cloned. * * @throws IllegalArgumentException if opName is * null. */ public CollectionOp(OperationRegistry registry, String opName, ParameterBlock pb, RenderingHints hints) { this(registry, opName, pb, hints, false); } /** * Constructs a CollectionOp that will be used to * instantiate a particular Collection operation from a given * operation name, a ParameterBlock, and a set of * rendering hints. The default operation registry is used. * *

      This method does not validate the contents of the supplied * ParameterBlock. The caller should ensure that * the sources and parameters in the ParameterBlock * are suitable for the operation this node represents; otherwise * some form of error or exception may occur at the time of rendering. * *

      The ParameterBlock may include * DeferredData parameters. These will not be evaluated * until their values are actually required, i.e., when a collection * rendering is requested. * * @param opName The operation name. Saved by reference. * @param pb The sources and other parameters. If null, * it is assumed that this node has no sources and parameters. * This parameter is cloned. * @param hints The rendering hints. If null, it is assumed * that no hints are associated with the rendering. * This parameter is cloned. * * @throws IllegalArgumentException if opName is * null. */ public CollectionOp(String opName, ParameterBlock pb, RenderingHints hints) { this(null, opName, pb, hints); } /** * Constructs a CollectionOp that will be used to * instantiate a particular Collection operation from a given * operation registry, an operation name, and a * ParameterBlock There are no rendering hints * associated with this operation. * The operation will use the rendered mode. * *

      This method does not validate the contents of the supplied * ParameterBlock. The caller should ensure that * the sources and parameters in the ParameterBlock * are suitable for the operation this node represents; otherwise * some form of error or exception may occur at the time of rendering. * *

      The ParameterBlock may include * DeferredData parameters. These will not be evaluated * until their values are actually required, i.e., when a collection * rendering is requested. * * @param registry The OperationRegistry to be used for * instantiation. if null, the default registry * is used. Saved by reference. * @param opName The operation name. Saved by reference. * @param pb The sources and other parameters. If null, * it is assumed that this node has no sources and parameters. * This parameter is cloned. * * @throws IllegalArgumentException if opName is * null. * * @deprecated as of JAI 1.1. * @see #CollectionOp(OperationRegistry,String,ParameterBlock,RenderingHints) */ public CollectionOp(OperationRegistry registry, String opName, ParameterBlock pb) { this(registry, opName, pb, null); } /** * Returns whether the operation is being instantiated in renderable mode. * * @since JAI 1.1 */ public boolean isRenderable() { return isRenderable; } /** * Returns the name of the RegistryMode corresponding to * this CollectionOp. * * @since JAI 1.1 */ public String getRegistryModeName() { return isRenderable ? RenderableCollectionRegistryMode.MODE_NAME : CollectionRegistryMode.MODE_NAME; } /* ----- Critical attribute accessors and mutators. ----- */ /** * Returns the OperationRegistry that is used * by this node. If the registry had not been set, the default * registry is returned. */ public synchronized OperationRegistry getRegistry() { return nodeSupport.getRegistry(); } /** * Sets the OperationRegistry that is used by * this node. If the specified registry is null, the * default registry is used. The parameter is saved by reference. * *

      If the supplied registry does not equal the current registry, a * PropertyChangeEventJAI named "OperationRegistry" * will be fired and a CollectionChangeEvent may be * fired if the node has already been rendered. * * @param registry The new OperationRegistry to be set; * it may be null. */ public synchronized void setRegistry(OperationRegistry registry) { nodeSupport.setRegistry(registry); } /** * Returns the name of the operation this node represents as * a String. */ public String getOperationName() { return nodeSupport.getOperationName(); } /** * Sets the name of the operation this node represents. * The parameter is saved by reference. * *

      If the supplied name does not equal the current operation name, a * PropertyChangeEventJAI named "OperationName" * will be fired and a CollectionChangeEvent may be * fired if the node has already been rendered. * * @param opName The new operation name to be set. * * @throws IllegalArgumentException if opName is * null. */ public synchronized void setOperationName(String opName) { nodeSupport.setOperationName(opName); } /** Returns a clone of the ParameterBlock of this node. */ public ParameterBlock getParameterBlock() { return (ParameterBlock)nodeSupport.getParameterBlock().clone(); } /** * Sets the ParameterBlock of this node. * If the specified new ParameterBlock is null, * it is assumed that this node has no input sources and parameters. * The supplied parameter is cloned. * *

      This method does not validate the contents of the supplied * ParameterBlock. The caller should ensure that * the sources and parameters in the ParameterBlock * are suitable for the operation this node represents; otherwise * some form of error or exception may occur at the time of rendering. * *

      If the supplied ParameterBlock does not equal the * current ParameterBlock, a * PropertyChangeEventJAI named "ParameterBlock", "Sources", * or "Parameters" will be fired. A CollectionChangeEvent * may also be fired if the node has already been rendered. * *

      The ParameterBlock may include * DeferredData parameters. These will not be evaluated * until their values are actually required, i.e., when a collection * rendering is requested. * *

      The node is registered as a sink of any PlanarImage * or CollectionImage sources contained in the supplied * ParameterBlock. The node is also removed as a sink of * any previous PlanarImage or CollectionImage * sources if these are not in the new ParameterBlock. * * @param pb The new ParameterBlock to be set; * it may be null. */ public synchronized void setParameterBlock(ParameterBlock pb) { Vector nodeSources = nodeSupport.getParameterBlock().getSources(); if(nodeSources != null && nodeSources.size() > 0) { Iterator it = nodeSources.iterator(); while(it.hasNext()) { Object src = it.next(); if(src instanceof PlanarImage) { ((PlanarImage)src).removeSink(this); } else if(src instanceof CollectionImage) { ((CollectionImage)src).removeSink(this); } } } if(pb != null) { Vector newSources = pb.getSources();; if(newSources != null && newSources.size() > 0) { Iterator it = newSources.iterator(); while(it.hasNext()) { Object src = it.next(); if(src instanceof PlanarImage) { ((PlanarImage)src).addSink(this); } else if(src instanceof CollectionImage) { ((CollectionImage)src).addSink(this); } } } } nodeSupport.setParameterBlock(pb == null ? new ParameterBlock() : (ParameterBlock)pb.clone()); } /** * Returns a clone of the RenderingHints of this node or * null. */ public RenderingHints getRenderingHints() { RenderingHints hints = nodeSupport.getRenderingHints(); return hints == null ? null : (RenderingHints)hints.clone(); } /** * Sets the RenderingHints of this node. * The supplied parameter is cloned if non-null. * *

      If the supplied RenderingHints does not equal the * current RenderingHints, a * PropertyChangeEventJAI named "RenderingHints" * will be fired and a CollectionChangeEvent may be * fired if the node has already been rendered. * * @param hints The new RenderingHints to be set; * it may be null. */ public synchronized void setRenderingHints(RenderingHints hints) { if(hints != null) { hints = (RenderingHints)hints.clone(); } nodeSupport.setRenderingHints(hints); } /* ----- Collection generation methods. ----- */ /** * Returns the Collection rendering associated with * this operation. * *

      This method does not validate the sources and parameters * stored in the ParameterBlock against the specification * of the operation this node represents. It is the responsibility * of the caller to ensure that the data in the * ParameterBlock are suitable for this operation. * Otherwise, some kind of exception or error will occur. Invoking * this method will cause any DeferredData parameters * in the ParameterBlock to be evaluated. * *

      Invoking this method will cause any source RenderedOp * nodes to be rendered using getRendering() and any * source CollectionOp nodes to be rendered using * getCollection(). Any DeferredData parameters * in the ParameterBlock will also be evaluated. * * @throws RuntimeException if the image factory charged with rendering * the node is unable to create a rendering. */ public Collection getCollection() { createCollection(); return imageCollection; } /** Creates a Collection rendering if none exists. */ private synchronized void createCollection() { if (imageCollection == null) { imageCollection = createInstance(true); } } /** * Instantiates a Collection operator that computes * the result of this CollectionOp. * *

      This method does not validate the sources and parameters * stored in the ParameterBlock against the specification * of the operation this node represents. It is the responsibility * of the caller to ensure that the data in the * ParameterBlock are suitable for this operation. * Otherwise, some kind of exception or error will occur. * *

      Invoking this method will cause any source RenderedOp * or CollectionOp nodes to be rendered using their * respective createInstance() methods. Any * DeferredData parameters in the ParameterBlock * will also be evaluated. * * @throws RuntimeException if the image factory charged with rendering * the node is unable to create a rendering. */ public synchronized Collection createInstance() { return createInstance(false); } /** * This method performs the actions described by the documentation of * createInstance(). The parameter value selects the method * used to render the source(s). * * @throws RuntimeException if the image factory charged with rendering * the node is unable to create a rendering. */ private synchronized Collection createInstance(boolean isChainFrozen) { // Get the PB evaluating any DeferredData objects in the process. ParameterBlock args = ImageUtil.evaluateParameters(nodeSupport.getParameterBlock()); ParameterBlock pb = new ParameterBlock(); pb.setParameters(args.getParameters()); int numSources = args.getNumSources(); for (int i = 0; i < numSources; i++) { Object source = args.getSource(i); Object src = null; if (source instanceof RenderedOp) { src = isChainFrozen ? ((RenderedOp)source).getRendering() : ((RenderedOp)source).createInstance(); } else if (source instanceof CollectionOp) { CollectionOp co = (CollectionOp)source; src = isChainFrozen ? co.getCollection() : co.createInstance(); } else if (source instanceof RenderedImage || source instanceof RenderableImage || source instanceof Collection) { src = source; } else { // Source is some other type. Pass on (for now). src = source; } pb.addSource(src); } Collection instance = null; if(isRenderable) { instance = RCIFRegistry.create(nodeSupport.getRegistry(), nodeSupport.getOperationName(), pb); } else { CollectionImageFactory cif = CIFRegistry.get(nodeSupport.getRegistry(), nodeSupport.getOperationName()); instance = cif.create(pb, nodeSupport.getRenderingHints()); // Set the CollectionImageFactory on the result. if(instance != null) { ((CollectionImage)instance).setImageFactory(cif); } } // Throw an error if the rendering is null. if (instance == null) { throw new RuntimeException(JaiI18N.getString("CollectionOp0")); } // Save the RenderingHints. oldHints = nodeSupport.getRenderingHints() == null ? null : (RenderingHints)nodeSupport.getRenderingHints().clone(); return instance; } /** * Returns the Collection rendering associated with this * operation with any contained RenderableImages rendered * using the supplied RenderContext parameter. If the * operation is being executed in rendered mode * (isRenderable() returns false), invoking * this method is equivalent to invoking getCollection(), * i.e., the parameter is ignored. If the operation is being * executed in renderable mode, the Collection will differ * from that returned by getCollection() due to any contained * RenderableImages having been rendered. If the * Collection contains any nested Collections, * these will be unwrapped recursively such that a rendering is created * for all RenderableImages encountered. Any * RenderingHints in the RenderContext are * merged with those set on the node with the argument hints taking * precedence. * * @since JAI 1.1 */ public Collection createRendering(RenderContext renderContext) { if(!isRenderable) { return this; } // Merge argument hints with node hints. RenderingHints mergedHints = JAI.mergeRenderingHints(nodeSupport.getRenderingHints(), renderContext.getRenderingHints()); if(mergedHints != renderContext.getRenderingHints()) { renderContext = (RenderContext)renderContext.clone(); renderContext.setRenderingHints(mergedHints); } return renderCollection(imageCollection, renderContext); } /** * Returns a new Collection with any * RenderableImages rendered using the supplied * RenderContext. This method is re-entrant and * invokes itself if there is a nested Collection. */ private Collection renderCollection(Collection cIn, RenderContext rc) { if(cIn == null || rc == null) { throw new IllegalArgumentException(); // no message. } Collection cOut; if(cIn instanceof Set) { cOut = Collections.synchronizedSet(new HashSet(cIn.size())); } else if(cIn instanceof SortedSet) { Comparator comparator = ((SortedSet)cIn).comparator(); cOut = Collections.synchronizedSortedSet(new TreeSet(comparator)); } else { cOut = new Vector(cIn.size()); } Iterator it = cIn.iterator(); while(it.hasNext()) { Object element = it.next(); if(element instanceof RenderableImage) { cOut.add(((RenderableImage)cIn).createRendering(rc)); } else if(element instanceof Collection) { cOut.add(renderCollection((Collection)element, rc)); } else { cOut.add(element); } } return cOut; } /* ----- PropertyChangeListener method. ----- */ /** * Implementation of PropertyChangeListener. * *

      When invoked with an event which is an instance of either * CollectionChangeEvent or * RenderingChangeEvent emitted by a * CollectionOp or RenderedOp source, * respectively, the node will respond by * re-rendering itself while retaining any data possible. * * @since JAI 1.1 */ public synchronized void propertyChange(PropertyChangeEvent evt) { // If this is a renderable node just return as CollectionChangeEvents // should not be emitted for "renderablecollection" mode. if(isRenderable()) return; // // React if and only if the node has been rendered and // A: a non-PropertySourceChangeEvent PropertyChangeEventJAI // was received from this node, or // B: a CollectionChangeEvent was received from a source node, or // C: a RenderingChangeEvent was received from a source node. // // Cache event and node sources. Object evtSrc = evt.getSource(); Vector nodeSources = nodeSupport.getParameterBlock().getSources(); // Get the name of the bean property and convert it to lower // case now for efficiency later. String propName = evt.getPropertyName().toLowerCase(Locale.ENGLISH); if(imageCollection != null && ((evt instanceof PropertyChangeEventJAI && evtSrc == this && !(evt instanceof PropertySourceChangeEvent) && nodeEventNames.contains(propName)) || ((evt instanceof CollectionChangeEvent || evt instanceof RenderingChangeEvent) && nodeSources.contains(evtSrc)))) { // Save the previous rendering. Collection theOldCollection = imageCollection; // Initialize the event flag. boolean fireEvent = false; if(!(imageCollection instanceof CollectionImage)) { // Collection is not a CollectionImage so no update possible; // invalidate the entire rendering. fireEvent = true; imageCollection = null; } else if(evtSrc == this && (propName.equals("operationname") || propName.equals("operationregistry"))) { // Operation name or OperationRegistry changed: // invalidate the entire rendering. fireEvent = true; imageCollection = null; } else if(evt instanceof CollectionChangeEvent) { // Set the event flag. fireEvent = true; // Save the previous image factory. We know that the old // Collection is a CollectionImage or the first branch of // the if-block would have been entered above. CollectionImageFactory oldCIF = ((CollectionImage)theOldCollection).getImageFactory(); if(oldCIF == null) { // The factory is null: no update possible. imageCollection = null; } else { // CollectionChangeEvent from a source CollectionOp. CollectionChangeEvent ccEvent = (CollectionChangeEvent)evt; // Construct old and new ParameterBlocks. Vector parameters = nodeSupport.getParameterBlock().getParameters(); parameters = ImageUtil.evaluateParameters(parameters); ParameterBlock oldPB = new ParameterBlock((Vector)nodeSources.clone(), parameters); ParameterBlock newPB = new ParameterBlock((Vector)nodeSources.clone(), parameters); int sourceIndex = nodeSources.indexOf(ccEvent.getSource()); oldPB.setSource(ccEvent.getOldValue(), sourceIndex); newPB.setSource(ccEvent.getNewValue(), sourceIndex); // Update the collection. imageCollection = oldCIF.update(oldPB, oldHints, newPB, oldHints, (CollectionImage)theOldCollection, this); } } else { // not op name, registry change, nor CollectionChangeEvent // Save the previous image factory. CollectionImageFactory oldCIF = ((CollectionImage)theOldCollection).getImageFactory(); if(oldCIF == null || oldCIF != CIFRegistry.get(nodeSupport.getRegistry(), nodeSupport.getOperationName())) { // Impossible to update unless the old and new CIFs // are equal and non-null. imageCollection = null; // Set event flag. fireEvent = true; } else { // Attempt to update the Collection rendering. ParameterBlock oldPB = null; ParameterBlock newPB = null; boolean updateCollection = false; if(propName.equals("parameterblock")) { oldPB = (ParameterBlock)evt.getOldValue(); newPB = (ParameterBlock)evt.getNewValue(); updateCollection = true; } else if(propName.equals("sources")) { // Replace source(s) Vector params = nodeSupport.getParameterBlock().getParameters(); oldPB = new ParameterBlock((Vector)evt.getOldValue(), params); newPB = new ParameterBlock((Vector)evt.getNewValue(), params); updateCollection = true; } else if(propName.equals("parameters")) { // Replace parameter(s) oldPB = new ParameterBlock(nodeSources, (Vector)evt.getOldValue()); newPB = new ParameterBlock(nodeSources, (Vector)evt.getNewValue()); updateCollection = true; } else if(propName.equals("renderinghints")) { oldPB = newPB = nodeSupport.getParameterBlock(); updateCollection = true; } else if(evt instanceof RenderingChangeEvent) { // Event from a RenderedOp source. // Replace appropriate source. int renderingIndex = nodeSources.indexOf(evt.getSource()); Vector oldSources = (Vector)nodeSources.clone(); Vector newSources = (Vector)nodeSources.clone(); oldSources.set(renderingIndex, evt.getOldValue()); newSources.set(renderingIndex, evt.getNewValue()); Vector params = nodeSupport.getParameterBlock().getParameters(); oldPB = new ParameterBlock(oldSources, params); newPB = new ParameterBlock(newSources, params); updateCollection = true; } if(updateCollection) { // Set event flag. fireEvent = true; // Evaluate any DeferredData parameters. oldPB = ImageUtil.evaluateParameters(oldPB); newPB = ImageUtil.evaluateParameters(newPB); // Update the collection. RenderingHints newHints = nodeSupport.getRenderingHints(); if((imageCollection = oldCIF.update(oldPB, oldHints, newPB, newHints, (CollectionImage)theOldCollection, this)) != null) { oldHints = newHints; } } } } // Re-render the node. This will only occur if imageCollection // has been set to null above. getCollection(); // Fire an event if the flag was set. if(fireEvent) { // Clear the synthetic and cached properties and reset the // property source. resetProperties(true); // Create the event object. CollectionChangeEvent ccEvent = new CollectionChangeEvent(this, theOldCollection, imageCollection); // Fire to all registered listeners. eventManager.firePropertyChange(ccEvent); // Fire to all PropertyChangeListener sinks. Set sinks = getSinks(); if(sinks != null) { Iterator it = sinks.iterator(); while(it.hasNext()) { Object sink = it.next(); if(sink instanceof PropertyChangeListener) { ((PropertyChangeListener)sink).propertyChange(ccEvent); } } } } } } /* ----- Property-related methods. ----- */ /** Creates a PropertySource if none exists. */ private synchronized void createPropertySource() { if (thePropertySource == null) { getCollection(); PropertySource defaultPS = null; if(imageCollection instanceof PropertySource) { // Create a PropertySource wrapper of the rendering. defaultPS = new PropertySource() { /** * Retrieve the names from an instance of the node. */ public String[] getPropertyNames() { return ((PropertySource)imageCollection).getPropertyNames(); } public String[] getPropertyNames(String prefix) { return PropertyUtil.getPropertyNames( getPropertyNames(), prefix); } public Class getPropertyClass(String name) { return null; } /** * Retrieve the actual property values from a * rendering of the node. */ public Object getProperty(String name) { return ((PropertySource)imageCollection).getProperty(name); } }; } // Create a PropertySource encapsulating the // property environment of the node. thePropertySource = nodeSupport.getPropertySource(this, defaultPS); // Add the PropertySource to the helper object. properties.addProperties(thePropertySource); } } /** * Resets the PropertySource. If the parameter is * true then the property environment is completely * reset; if false then only cached properties are * cleared, i.e., those which were derived from the property * environment and are now stored in the local cache. * * @since JAI 1.1 */ protected synchronized void resetProperties(boolean resetPropertySource) { properties.clearCachedProperties(); if (resetPropertySource && thePropertySource != null) { properties.removePropertySource(thePropertySource); thePropertySource = null; } } /** * Returns the names of properties available from this node. * These properties are a combination of those derived * from prior nodes in the operation chain and those set locally. * * @return An array of Strings containing valid * property names or null if there are none. * * @since JAI 1.1 */ public synchronized String[] getPropertyNames() { createPropertySource(); return properties.getPropertyNames(); } /** * Returns the class expected to be returned by a request for * the property with the specified name. If this information * is unavailable, null will be returned. * * @return The Class expected to be return by a * request for the value of this property or null. * @exception IllegalArgumentException if name * is null. * * @since JAI 1.1 */ public Class getPropertyClass(String name) { createPropertySource(); return properties.getPropertyClass(name); } /** * Gets a property from the property set of this Collection. * 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. * @exception IllegalArgumentException if name * is null. * * @since JAI 1.1 */ public Object getProperty(String name) { createPropertySource(); return properties.getProperty(name); } /** * Sets a local property on a node. Local property settings override * properties derived from prior nodes in the operation chain. * * @param name a String representing the property name. * @param value the property's value, as an Object. * @exception IllegalArgumentException if name * or value * is null. * * @since JAI 1.1 */ public void setProperty(String name, Object value) { createPropertySource(); properties.setProperty(name, value); } /** * Removes the named property from the local property * set of the CollectionOp as well as from its property * environment. * * @exception IllegalArgumentException if name * is null. * * @since JAI 1.1 */ public void removeProperty(String name) { createPropertySource(); properties.removeProperty(name); } /** * Returns the property associated with the specified property name, * or java.awt.Image.UndefinedProperty if the specified * property is not set on the image. This method is dynamic in the * sense that subsequent invocations of this method on the same object * may return different values as a function of changes in the property * environment of the node, e.g., a change in which * PropertyGenerators are registered or in the values * associated with properties of node sources. The case of the property * name passed to this method is ignored. * * @param name A String naming the property. * * @throws IllegalArgumentException if * name is null. * * @since JAI 1.1 */ public synchronized Object getDynamicProperty(String name) { createPropertySource(); return thePropertySource.getProperty(name); } /** * Adds a PropertyGenerator to the node. The property values * emitted by this property generator override any previous * definitions. * * @param pg a PropertyGenerator to be added to this node's * property environment. * * @since JAI 1.1 */ public void addPropertyGenerator(PropertyGenerator pg) { nodeSupport.addPropertyGenerator(pg); } /** * Forces a property to be copied from the specified source node. * By default, a property is copied from the first source node * that emits it. The result of specifying an invalid source is * undefined. * * @param propertyName the name of the property to be copied. * @param sourceIndex the index of the from which to copy the property. * @throws IllegalArgumentException if propertyName is * null. * * @since JAI 1.1 */ public synchronized void copyPropertyFromSource(String propertyName, int sourceIndex) { nodeSupport.copyPropertyFromSource(propertyName, sourceIndex); } /** * Removes a named property from the property environment of this * node. Unless the property is stored locally either due * to having been set explicitly via setProperty() * or to having been cached for property * synchronization purposes, subsequent calls to * getProperty(name) will return * java.awt.Image.UndefinedProperty, and name * will not appear on the list of properties emitted by * getPropertyNames(). To delete the property from the * local property set of the node, removeProperty() should * be used. * * @param name a String naming the property to be suppressed. * @throws IllegalArgumentException if * name is null. * * @since JAI 1.1 */ public void suppressProperty(String name) { nodeSupport.suppressProperty(name); } /***************************************************************** * The following methods override public or protected methods in * * CollectionImage thus causing the Collection to be created. * *****************************************************************/ /** * Creates the Collection rendering if none yet exists, and * returns the number of elements in this Collection. */ public int size() { createCollection(); return imageCollection.size(); } /** * Creates the Collection rendering if none yet exists, and * returns true if this Collection contains * no element. */ public boolean isEmpty() { createCollection(); return imageCollection.isEmpty(); } /** * Creates the Collection rendering if none yet exists, and * returns true if this Collection contains * the specified object. */ public boolean contains(Object o) { createCollection(); return imageCollection.contains(o); } /** * Creates the Collection rendering if none yet exists, and * returns an Iterator over the elements in this * Collection. */ public Iterator iterator() { createCollection(); return imageCollection.iterator(); } /** * Creates the Collection rendering if none yet exists, and * returns an array containing all of the elements in this * Collection. */ public Object[] toArray() { createCollection(); return imageCollection.toArray(); } /** * Creates the Collection rendering if none yet exists, and * returns an array containing all of the elements in this * Collection whose runtime type is that of the specified * array. * * @throws ArrayStoreException if the runtime type of the * specified array is not a supertype of the runtime type of * every element in this Collection. */ public Object[] toArray(Object[] a) { createCollection(); return imageCollection.toArray(a); } /** * Creates the Collection rendering if none yet exists, and * adds the specified object to this Collection. */ public boolean add(Object o) { createCollection(); return imageCollection.add(o); } /** * Creates the Collection rendering if none yet exists, and * removes the specified object from this Collection. */ public boolean remove(Object o) { createCollection(); return imageCollection.remove(o); } /** * Creates the Collection rendering if none yet exists, and * returns true if this Collection contains * all of the elements in the specified Collection. */ public boolean containsAll(Collection c) { createCollection(); return imageCollection.containsAll(c); } /** * Creates the Collection rendering if none yet exists, and * adds all of the elements in the specified Collection * to this Collection. */ public boolean addAll(Collection c) { createCollection(); return imageCollection.addAll(c); } /** * Creates the Collection rendering if none yet exists, and * removes all this Collection's elements that are also * contained in the specified Collection. */ public boolean removeAll(Collection c) { createCollection(); return imageCollection.removeAll(c); } /** * Creates the Collection rendering if none yet exists, and * retains only the elements in this Collection that are * contained in the specified Collection. */ public boolean retainAll(Collection c) { createCollection(); return imageCollection.retainAll(c); } /** * Creates the Collection rendering if none yet exists, and * removes all of the elements from this Collection. */ public void clear() { createCollection(); imageCollection.clear(); } } jai-core-1.1.4/src/share/classes/javax/media/jai/IHSColorSpace.java0000644000175000017500000015721710336734610024622 0ustar mathieumathieu/* * $RCSfile: IHSColorSpace.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.2 $ * $Date: 2005-11-16 22:58:16 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Point; import java.awt.color.ColorSpace; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferShort; import java.awt.image.DataBufferInt; import java.awt.image.SampleModel; import java.lang.ref.SoftReference; /** * Singleton class representing the IHS (Intensity, Hue, * Saturation) color space (also known as HSI or HIS). * *

      The RGB-to-IHS transformation is defined by the equations: *

        *
      • I = (R+G+B)/3
      • *
      • S = 1-min(R, G, B)/I
      • *
      • H = G > B ? h : 2*PI - h, where
        * h = cos-1{[(R-G)+(R-B)]/2[(R-G)2+ * (R-G)(G-B)]1/2}
      • *
      * where the R, G, B values have been normalized to the range [0.0, 1.0]. * Refer to Practical Algorithms for Image Analysis, Seul, et. al., * (Cambridge, 2000), pages 50-55, for more information. * * The inverse transformation (IHS-to-RGB) is defined as: * *

      When H is in [2PI/3, 4PI/3], R * should be the smallest. Then, there exists *

      R = (1-S)I *

      and

      • G = (c1 + c2)/2
      • *
      • B = (c1 - c2)/2
      *

      where c1 = 3I-R and c2 = sqrt(3)(R-I)tg(H) * *

      when H is in [4PI/3, 2PI], * *

      G = (1-S)I *

      and

      • B = (c1 + c2)/2
      • *
      • R = (c1 - c2)/2
      *

      where c1 = 3I-G and c2 = sqrt(3)(G-I)tg(H-2PI/3) * *

      when H is in [0, 2PI/3], * *

      B = (1-S)I *

      and

      • R = (c1 + c2)/2
      • *
      • G = (c1 - c2)/2
      * *

      where c1 = 3I-B and c2 = sqrt(3)(B-I)tg(H-4PI/3) *

      Methods defined in the superclasses are not commented extensively. * * @see ColorSpaceJAI * * @since JAI 1.1 */ // Old RGB-to-IHS equations ("Practical Algorithms for Image Analysis", // Seul, et. al., Cambridge, 2000): // //

        //
      • I = (R+G+B)/3
      • //
      • S = 1-min(R, G, B)/I
      • //
      • H = G > B ? h : 2*PI - h, where
        // h = cos-1{[(R-G)+(R-B)]/2[(R-G)2+ // (R-G)(G-B)]1/2}
      • //
      // where the R, G, B values have been normalized to the range [0.0, 1.0]. //

      The RGB-to-IHS transformation is defined by the equations: //

        //
      • // [ I ]   [  1/3        1/3       1/3       ] [ R ]
        // [ U ] = [ -1/sqrt(6) -1/sqrt(6) 2/sqrt(6) ] [ G ]
        // [ V ]   [  1/sqrt(6) -2/sqrt(6) 0         ] [ B ]
        // 
      • //
      • H = atan(V/U)
      • //
      • S = sqrt(U2 + V2)
      • //
      // // The inverse IHS-to-RGB transformation is defined by: //
        //
      • U = S*cos(H)
      • //
      • V = S*sin(H)
      • //
      • // [ R ] = [ 4/3 -2*sqrt(6)/9  sqrt(6)/3 ] [ I ]
        // [ G ] = [ 2/3    sqrt(6)/9 -sqrt(6)/3 ] [ U ]
        // [ B ]   [ 1      sqrt(6)/3  0         ] [ V ]
        // 
      • //
      // // Refer to Digital Image Processing, Second Edition, William K. Pratt // (Wiley, 1991), pages 71-72, for more information. public final class IHSColorSpace extends ColorSpaceJAI { // Constant for rgb-to-ihs private static final double PI2 = Math.PI*2.0 ; // Constants for ihs-to-rgb private static final double PI23 = PI2/3.0 ; private static final double PI43 = PI23*2.0 ; private static final double SQRT3 = Math.sqrt(3.0) ; private final static double BYTESCALE = 127.5/Math.PI ; private static SoftReference reference = new SoftReference(null); /* tables and their softrefrences */ private static byte[] acosTable = null ; private static double[] sqrtTable = null ; private static double[] tanTable = null ; private static SoftReference acosSoftRef ; private static SoftReference sqrtSoftRef ; private static SoftReference tanSoftRef ; /** * Retrieves the unique instance of this class the construction of * which is deferred until the first invocation of this method. */ public static IHSColorSpace getInstance() { synchronized(reference) { Object referent = reference.get(); IHSColorSpace cs; if (referent == null) { // First invocation or SoftReference has been cleared. reference = new SoftReference(cs = new IHSColorSpace()); } else { // SoftReference has not been cleared. cs = (IHSColorSpace)referent; } return cs; } } /** * Constructs an instance of this class with type * ColorSpace.TYPE_HSV, 3 components, and preferred * intermediary space sRGB. */ protected IHSColorSpace() { super(ColorSpace.TYPE_HSV, 3, true); } // generate the cos table used in RGBtoIHS for byte type private synchronized void generateACosTable() { if ((acosSoftRef == null) || (acosSoftRef.get() == null)) { acosTable = new byte[1001] ; acosSoftRef = new SoftReference(acosTable); for (int i=0; i<=1000; i++) acosTable[i] = (byte)(BYTESCALE *Math.acos((i - 500) * 0.002) + 0.5) ; } } // generate the square root table used in RGBtoIHS for byte type private synchronized void generateSqrtTable() { if ((sqrtSoftRef == null) || (sqrtSoftRef.get() == null)) { sqrtTable = new double[1001] ; sqrtSoftRef = new SoftReference(sqrtTable); for (int i = 0; i <= 1000; i++) sqrtTable[i] = Math.sqrt(i / 1000.0) ; } } // generate the tangent table used in IHStoRGB for byte type private synchronized void generateTanTable() { if ((tanSoftRef == null) || (tanSoftRef.get() == null)) { tanTable = new double[256] ; tanSoftRef = new SoftReference(tanTable); for (int i = 0; i < 256; i++) tanTable[i] = Math.tan(i * PI2 / 255.0) ; } } /** * Converts a single color value from CIEXYZ to IHS. */ public float[] fromCIEXYZ(float[] colorValue) { float[] rgb = new float[3] ; XYZ2RGB(colorValue, rgb); float r = rgb[0] ; float g = rgb[1] ; float b = rgb[2] ; float[] ihs = new float[3] ; ihs[0] = (r + g + b)/3.0f ; float drg = r - g ; float drb = r - b ; float temp = (float) Math.sqrt(drg * (double)drg +drb * (double)(drb - drg)) ; // when temp is zero, R=G=B. Hue should be NaN. To make // numerically consistent, set it to 2PI if (temp != 0.0f) { temp = (float) Math.acos((drg + drb) / (double)temp / 2) ; if (g < b) ihs[1] = (float) (PI2 - temp) ; else ihs[1] = temp ; } else ihs[1] = (float) PI2 ; float min = (r < g) ? r : g ; min = (min < b) ? min : b ; // when intensity is 0, means R=G=B=0. S can be set to 0 to indicate // R=G=B. if (ihs[0] == 0.0f) ihs[2] = 0.0f ; else ihs[2] = 1.0f - min / ihs[0] ; return ihs ; } /** * Converts a single color value from sRGB to IHS. */ public float[] fromRGB(float[] rgbValue) { float r = rgbValue[0] ; float g = rgbValue[1] ; float b = rgbValue[2] ; r = (r < 0.0f)? 0.0f : ((r > 1.0f) ? 1.0f: r) ; g = (g < 0.0f)? 0.0f : ((g > 1.0f) ? 1.0f: g) ; b = (b < 0.0f)? 0.0f : ((b > 1.0f) ? 1.0f: b) ; float[] ihs = new float[3] ; ihs[0] = (r + g + b)/3.0f ; float drg = r - g ; float drb = r - b ; float temp = (float) Math.sqrt(drg * (double)drg + drb * (double)(drb - drg)) ; // when temp is zero, R=G=B. Hue should be NaN. To make // numerically consistent, set it to 2PI if (temp != 0.0f) { temp = (float) Math.acos((drg + drb) / (double)temp / 2) ; if (g < b) ihs[1] = (float) (PI2 - temp) ; else ihs[1] = temp ; } else ihs[1] = (float)PI2 ; float min = (r < g) ? r : g ; min = (min < b) ? min : b ; // when intensity is 0, means R=G=B=0. S can be set to 0 to indicate // R=G=B. if (ihs[0] == 0.0f) ihs[2] = 0.0f ; else ihs[2] = 1.0f - min / ihs[0] ; return ihs ; } /** * Converts a single color value from IHS to CIEXYZ. */ public float[] toCIEXYZ(float[] colorValue) { float i = colorValue[0] ; float h = colorValue[1] ; float s = colorValue[2] ; i = (i < 0.0f)? 0.0f : ((i > 1.0f) ? 1.0f: i) ; h = (h < 0.0f)? 0.0f : ((h > (float)PI2) ? (float)PI2: h) ; s = (s < 0.0f)? 0.0f : ((s > 1.0f) ? 1.0f: s) ; float r = 0.0f, g = 0.0f, b = 0.0f ; // when the saturation is zero, means this color is grey color. // so R=G=B. if (s == 0.0f) { r = g = b = i ; } else { if (h >= PI23 && h < PI43) { r = (1 - s) * i ; float c1 = 3 * i - r ; float c2 = (float) (SQRT3 * (r - i) * Math.tan(h)) ; g = (c1 + c2) / 2 ; b = (c1 - c2) / 2 ; } else if (h > PI43) { g = (1 - s) * i ; float c1 = 3 * i - g ; float c2 = (float) (SQRT3 * (g - i) * Math.tan(h - PI23)) ; b = (c1 + c2) / 2 ; r = (c1 - c2) / 2 ; } else if (h < PI23) { b = (1 - s)* i ; float c1 = 3 * i - b ; float c2 = (float)(SQRT3 * (b - i) * Math.tan(h - PI43)) ; r = (c1 + c2) / 2 ; g = (c1 - c2) / 2 ; } } float[] xyz = new float[3] ; float[] rgb = new float[3] ; rgb[0] = r; rgb[1] = g; rgb[2] = b; RGB2XYZ(rgb, xyz); return xyz ; } /** * Converts a single color value from IHS to sRGB. */ public float[] toRGB(float[] colorValue) { float i = colorValue[0] ; float h = colorValue[1] ; float s = colorValue[2] ; i = (i < 0.0f) ? 0.0f : ((i > 1.0f) ? 1.0f : i) ; h = (h < 0.0f) ? 0.0f : ((h > (float)PI2) ? (float)PI2 : h) ; s = (s < 0.0f) ? 0.0f : ((s > 1.0f) ? 1.0f : s) ; float[] rgb = new float[3] ; // when the saturation is 0, the color is grey. so R=G=B=I. if (s == 0.0f) { rgb[0] = rgb[1] = rgb[2] = i ; } else { if (h >= PI23 && h <= PI43) { float r = (1 - s) * i ; float c1 = 3 * i - r ; float c2 = (float) (SQRT3 * (r - i) * Math.tan(h)) ; rgb[0] = r ; rgb[1] = (c1 + c2) / 2 ; rgb[2] = (c1 - c2) / 2 ; } else if (h >PI43) { float g = (1 - s) * i ; float c1 = 3 * i - g ; float c2 = (float) (SQRT3 * (g - i) * Math.tan(h - PI23)) ; rgb[0] = (c1 - c2) / 2 ; rgb[1] = g ; rgb[2] = (c1 + c2) / 2 ; } else if (h < PI23) { float b = (1 - s) * i ; float c1 = 3 * i - b ; float c2 = (float) (SQRT3 * (b - i) * Math.tan(h - PI43)) ; rgb[0] = (c1 + c2) / 2 ; rgb[1] = (c1 - c2) / 2 ; rgb[2] = b ; } } return rgb ; } /** * Converts a Raster of colors represented as pixels * from CIEXYZ to IHS. */ public WritableRaster fromCIEXYZ(Raster src, int[] srcComponentSize, WritableRaster dest, int[] destComponentSize) { WritableRaster tempRas = CIEXYZToRGB(src, srcComponentSize, null, null) ; return fromRGB(tempRas, tempRas.getSampleModel().getSampleSize(), dest, destComponentSize) ; } /** * Converts a Raster of colors represented as pixels * from sRGB to IHS. */ public WritableRaster fromRGB(Raster src, int[] srcComponentSize, WritableRaster dest, int[] destComponentSize) { //Validate the parameters checkParameters(src, srcComponentSize, dest, destComponentSize) ; SampleModel srcSampleModel = src.getSampleModel() ; // When the parameter is not provided by the caller, set it as the // sample sizes of the source if (srcComponentSize == null) srcComponentSize = srcSampleModel.getSampleSize() ; //when the destination ratser is not provided, create a new one if (dest == null) { Point origin = new Point(src.getMinX(), src.getMinY()) ; dest = RasterFactory.createWritableRaster(srcSampleModel, origin) ; } SampleModel dstSampleModel = dest.getSampleModel() ; if (destComponentSize == null) destComponentSize = dstSampleModel.getSampleSize() ; PixelAccessor srcAcc = new PixelAccessor(srcSampleModel, null) ; UnpackedImageData srcUid = srcAcc.getPixels(src, src.getBounds(), srcSampleModel.getDataType(), false) ; switch (srcSampleModel.getDataType()) { case DataBuffer.TYPE_BYTE: fromRGBByte(srcUid, srcComponentSize, dest, destComponentSize) ; break ; case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: fromRGBShort(srcUid, srcComponentSize, dest, destComponentSize) ; break ; case DataBuffer.TYPE_INT: fromRGBInt(srcUid, srcComponentSize, dest, destComponentSize) ; break ; case DataBuffer.TYPE_FLOAT: fromRGBFloat(srcUid, srcComponentSize, dest, destComponentSize) ; break ; case DataBuffer.TYPE_DOUBLE: fromRGBDouble(srcUid, srcComponentSize, dest, destComponentSize) ; break ; } return dest ; } // convert a byte type raster from RGB to IHS private void fromRGBByte(UnpackedImageData src, int[] srcComponentSize, WritableRaster dest, int[] destComponentSize) { byte[] rBuf = src.getByteData(0) ; byte[] gBuf = src.getByteData(1) ; byte[] bBuf = src.getByteData(2) ; int normr = 8 - srcComponentSize[0] ; int normg = 8 - srcComponentSize[1] ; int normb = 8 - srcComponentSize[2] ; double normi = 1.0 / 255.0, normh = 1.0, norms = 1.0 ; int bnormi = 0, bnormh = 0, bnorms = 0 ; int dstType = dest.getSampleModel().getDataType() ; boolean isByte = (dstType == DataBuffer.TYPE_BYTE) ; if (isByte) { bnormi = 8 - destComponentSize[0] ; bnormh = 8 - destComponentSize[1] ; bnorms = 8 - destComponentSize[2] ; generateACosTable() ; generateSqrtTable() ; } else if (dstType < DataBuffer.TYPE_FLOAT) { normi = ((1l << destComponentSize[0]) - 1) / 255.0 ; normh = ((1l << destComponentSize[1]) - 1) / PI2 ; norms = ((1l << destComponentSize[2]) - 1) ; } int height = dest.getHeight() ; int width = dest.getWidth() ; double[] dstPixels = null ; int[] dstIntPixels = null ; if (isByte) dstIntPixels = new int[3 * height * width] ; else dstPixels = new double[3 * height * width] ; int rStart = src.bandOffsets[0] ; int gStart = src.bandOffsets[1] ; int bStart = src.bandOffsets[2] ; int srcPixelStride = src.pixelStride; int srcLineStride = src.lineStride; int dIndex = 0 ; for (int j = 0 ; j < height; j++, rStart += srcLineStride, gStart += srcLineStride, bStart += srcLineStride) { for (int i = 0, rIndex = rStart, gIndex = gStart, bIndex = bStart ; i < width; i++, rIndex += srcPixelStride, gIndex += srcPixelStride, bIndex += srcPixelStride) { short R = (short)((rBuf[rIndex] & 0xFF) << normr) ; short G = (short)((gBuf[gIndex] & 0xFF) << normg) ; short B = (short)((bBuf[bIndex] & 0xFF) << normb) ; if (isByte) { float intensity = (R + G + B) / 3.0f ; dstIntPixels[ dIndex++ ] = ((short)(intensity + 0.5f)) >> bnormi ; short drg = (short)(R - G) ; short drb = (short)(R - B) ; int tint = drg * drg + drb * (drb - drg) ; short sum = (short)(drg + drb) ; double temp ; if (tint!= 0) temp = sqrtTable[(int)(250.0 * sum * sum / tint + 0.5)] ; else temp = -1.0 ; int hue ; if (sum>0) hue = acosTable[(int)(500 * temp + 0.5) + 500] ; else hue = acosTable[(int)(-500 * temp - 0.5) + 500] ; if (B >= G) dstIntPixels[ dIndex++ ] = (255 - hue) >> bnormh ; else dstIntPixels[ dIndex++ ] = hue >> bnormh ; short min = (G > B) ? B : G ; min = (R > min) ? min : R ; dstIntPixels[ dIndex++ ] = (255 - (int)(255 * min / intensity + 0.5f)) >> bnorms ; } else { float intensity = (R + G + B) /3.0f ; dstPixels[ dIndex++ ] = normi * intensity ; double drg = R - G ; double drb = R - B ; double temp = Math.sqrt(drg * drg + drb * (drb - drg)) ; if (temp != 0) { temp = Math.acos((drg + drb) / temp / 2) ; if (B >= G) temp = PI2 - temp ; } else temp = PI2 ; dstPixels[ dIndex++ ] = normh * temp ; double min = (G > B) ? B : G ; min = (R > min) ? min : R ; dstPixels[ dIndex++ ] = norms * (1 - min / intensity) ; } } } if (isByte) dest.setPixels(dest.getMinX(), dest.getMinY(), width, height, dstIntPixels) ; else { convertToSigned(dstPixels, dstType) ; dest.setPixels(dest.getMinX(), dest.getMinY(), width, height, dstPixels) ; } } // convert a short type ratser from RGB to IHS private void fromRGBShort(UnpackedImageData src, int[] srcComponentSize, WritableRaster dest, int[] destComponentSize) { short[] rBuf = src.getShortData(0) ; short[] gBuf = src.getShortData(1) ; short[] bBuf = src.getShortData(2) ; // used to left-shift the input to fill all the bits int normr = 16 - srcComponentSize[0] ; int normg = 16 - srcComponentSize[1] ; int normb = 16 - srcComponentSize[2] ; //map the output to the desired range double normi = 1.0 / 65535.0, normh = 1.0, norms = 1.0 ; //right-shift the output values to the desired range int bnormi = 0, bnormh = 0, bnorms = 0 ; int dstType = dest.getSampleModel().getDataType() ; boolean isByte = (dstType == DataBuffer.TYPE_BYTE) ; if (isByte) { bnormi = 16 - destComponentSize[0] ; bnormh = 8 - destComponentSize[1] ; bnorms = 8 - destComponentSize[2] ; generateACosTable() ; generateSqrtTable() ; } else if (dstType < DataBuffer.TYPE_FLOAT) { normi = ((1l << destComponentSize[0]) - 1)/65535.0 ; normh = ((1l << destComponentSize[1]) - 1)/PI2 ; norms = ((1l << destComponentSize[2]) - 1) ; } int height = dest.getHeight() ; int width = dest.getWidth() ; double[] dstPixels = null ; int[] dstIntPixels = null ; if (isByte) dstIntPixels = new int[3 * height * width] ; else dstPixels = new double[3 * height * width] ; int rStart = src.bandOffsets[0] ; int gStart = src.bandOffsets[1] ; int bStart = src.bandOffsets[2] ; int srcPixelStride = src.pixelStride; int srcLineStride = src.lineStride; int dIndex = 0 ; for (int j = 0 ; j> bnormi ; int drg = R - G ; int drb = R - B ; double tint = drg * (double)drg + drb *(double)(drb - drg) ; double sum = drg + drb ; double temp ; if (tint != 0) temp = sqrtTable[(int)(250.0 * sum * sum / tint + 0.5)] ; else temp = -1.0 ; int hue ; if (sum > 0) hue = acosTable[(int)(500 * temp + 0.5) + 500] ; else hue = acosTable[(int)(-500 * temp - 0.5) + 500] ; if (B >= G) dstIntPixels[dIndex++] = (255 - hue) >> bnormh ; else dstIntPixels[dIndex++] = hue >> bnormh ; int min = (G > B) ? B : G ; min = (R > min) ? min : R ; dstIntPixels[dIndex++] = (255 - (int)(255 * min / intensity + 0.5f)) >> bnorms ; } else { float intensity = (R + G + B) /3.0f ; dstPixels[dIndex++] = normi * intensity ; double drg = R - G ; double drb = R - B ; double temp = Math.sqrt(drg * drg + drb * (drb - drg)) ; if (temp != 0) { temp = Math.acos((drg + drb) / temp / 2) ; if (B >= G) temp = PI2 - temp ; } else temp = PI2 ; dstPixels[dIndex++] =normh * temp ; double min = (G > B) ? B : G ; min = (R > min) ? min : R ; dstPixels[dIndex++] = norms * (1 - min / intensity) ; } } } if (isByte) dest.setPixels(dest.getMinX(), dest.getMinY(), width, height, dstIntPixels) ; else { convertToSigned(dstPixels, dstType) ; dest.setPixels(dest.getMinX(), dest.getMinY(), width, height, dstPixels) ; } } // convert an int type raster from RGB to IHS private void fromRGBInt(UnpackedImageData src, int[] srcComponentSize, WritableRaster dest, int[] destComponentSize) { int[] rBuf = src.getIntData(0) ; int[] gBuf = src.getIntData(1) ; int[] bBuf = src.getIntData(2) ; int normr = 32 - srcComponentSize[0] ; int normg = 32 - srcComponentSize[1] ; int normb = 32 - srcComponentSize[2] ; double range = Integer.MAX_VALUE - (double)Integer.MIN_VALUE ; double normi = 1.0 / range, normh = 1.0, norms = 1.0 ; int bnormi = 0, bnormh = 0, bnorms = 0 ; int dstType = dest.getSampleModel().getDataType() ; boolean isByte = (dstType == DataBuffer.TYPE_BYTE) ; if (isByte) { bnormi = 32 - destComponentSize[0] ; bnormh = 8 - destComponentSize[1] ; bnorms = 8 - destComponentSize[2] ; generateACosTable() ; generateSqrtTable() ; } else if (dstType < DataBuffer.TYPE_FLOAT) { normi = ((1l << destComponentSize[0]) - 1) / range ; normh = ((1l << destComponentSize[1]) - 1) / PI2 ; norms = ((1l << destComponentSize[2]) - 1) ; } int height = dest.getHeight() ; int width = dest.getWidth() ; double[] dstPixels = null ; int[] dstIntPixels = null ; if (isByte) dstIntPixels = new int[3 * height * width] ; else dstPixels = new double[3 * height * width] ; int rStart = src.bandOffsets[0] ; int gStart = src.bandOffsets[1] ; int bStart = src.bandOffsets[2] ; int srcPixelStride = src.pixelStride; int srcLineStride = src.lineStride; int dIndex = 0 ; for (int j = 0 ; j < height; j++, rStart += srcLineStride, gStart += srcLineStride, bStart += srcLineStride) { for (int i = 0, rIndex = rStart, gIndex = gStart, bIndex = bStart ; i < width; i++, rIndex += srcPixelStride, gIndex += srcPixelStride, bIndex += srcPixelStride) { long R = (rBuf[rIndex] & 0xFFFFFFFFl) << normr ; long G = (gBuf[gIndex] & 0xFFFFFFFFl) << normg ; long B = (bBuf[bIndex] & 0xFFFFFFFFl) << normb ; if (isByte) { float intensity = (R + G + B) /3.0f ; dstIntPixels[ dIndex++ ] = (int)(((long)(intensity + 0.5f))>>bnormi); long drg = R - G ; long drb = R - B ; double tint = drg * (double)drg + drb * (double)(drb -drg) ; double sum = drg + drb ; double temp ; if (tint != 0) temp = sqrtTable[(int)(250.0 * sum * sum / tint + 0.5)] ; else temp = -1.0 ; int hue ; if (sum>0) hue = acosTable[(int)(500 * temp + 0.5) + 500] ; else hue = acosTable[(int)(-500 * temp - 0.5) + 500] ; if (B >= G) dstIntPixels[dIndex++] = (255 - hue) >> bnormh ; else dstIntPixels[dIndex++] = hue >> bnormh ; long min = (G > B) ? B : G ; min = (R > min) ? min : R ; dstIntPixels[dIndex++] = (255 - (int)(255 * min / intensity + 0.5f)) >> bnorms ; } else { float intensity = (R + G + B) /3.0f ; dstPixels[dIndex++] = normi * intensity ; double drg = R - G ; double drb = R - B ; double temp = Math.sqrt(drg * drg + drb * (drb - drg)) ; if (temp != 0) { temp = Math.acos((drg + drb) / temp / 2) ; if (B >= G) temp = PI2 - temp ; } else temp = PI2 ; dstPixels[dIndex++] = normh * temp ; double min = (G > B) ? B : G ; min = (R > min) ? min : R ; dstPixels[dIndex++] = norms * (1 - min / intensity) ; } } } if (isByte) dest.setPixels(dest.getMinX(), dest.getMinY(), width, height, dstIntPixels) ; else { convertToSigned(dstPixels, dstType) ; dest.setPixels(dest.getMinX(), dest.getMinY(), width, height, dstPixels) ; } } // convert a float type raster from RGB to IHS private void fromRGBFloat(UnpackedImageData src, int[] srcComponentSize, WritableRaster dest, int[] destComponentSize) { float[] rBuf = src.getFloatData(0) ; float[] gBuf = src.getFloatData(1) ; float[] bBuf = src.getFloatData(2) ; double normi = 1.0, normh = 1.0, norms = 1.0 ; int bnormi = 0, bnormh = 0, bnorms = 0 ; int dstType = dest.getSampleModel().getDataType() ; boolean isByte = (dstType == DataBuffer.TYPE_BYTE) ; if (isByte) { bnormi = (1 << destComponentSize[0]) - 1; bnormh = 8 - destComponentSize[1] ; bnorms = 8 - destComponentSize[2] ; generateACosTable() ; generateSqrtTable() ; } else if (dstType < DataBuffer.TYPE_FLOAT) { normi = (1l << destComponentSize[0]) - 1 ; normh = ((1l << destComponentSize[1]) - 1) / PI2 ; norms = (1l << destComponentSize[2]) - 1 ; } int height = dest.getHeight() ; int width = dest.getWidth() ; double[] dstPixels = null ; int[] dstIntPixels = null ; if (isByte) dstIntPixels = new int[3 * height * width] ; else dstPixels = new double[3 * height * width] ; int rStart = src.bandOffsets[0] ; int gStart = src.bandOffsets[1] ; int bStart = src.bandOffsets[2] ; int srcPixelStride = src.pixelStride; int srcLineStride = src.lineStride; int dIndex = 0 ; for (int j = 0 ; j0) hue = acosTable[(int)(500 * temp + 0.5) + 500] ; else hue = acosTable[(int)(-500 * temp - 0.5) + 500] ; if (B >= G) dstIntPixels[ dIndex++ ] = (255 - hue) >> bnormh ; else dstIntPixels[ dIndex++ ] = hue >> bnormh ; float min = (G > B) ? B : G ; min = (R > min) ? min : R ; dstIntPixels[ dIndex++ ] = (255 - (int)(255 * min / intensity + 0.5f)) >> bnorms ; } else { float intensity = (R + G + B) /3.0f ; dstPixels[dIndex++] = normi * intensity ; double drg = R - G ; double drb = R - B ; double temp = Math.sqrt(drg * drg + drb * (drb - drg)) ; if (temp != 0) { temp = Math.acos((drg + drb) / temp / 2) ; if (B >= G) temp = PI2 - temp ; } else temp = PI2 ; dstPixels[dIndex++] = normh * temp ; double min = (G > B) ? B : G ; min = (R > min) ? min : R ; dstPixels[dIndex++] = norms * (1 - min / intensity) ; } } } if (isByte) dest.setPixels(dest.getMinX(), dest.getMinY(), width, height, dstIntPixels) ; else { convertToSigned(dstPixels, dstType) ; dest.setPixels(dest.getMinX(), dest.getMinY(), width, height, dstPixels) ; } } // convert a double type raster from RGB to IHS private void fromRGBDouble(UnpackedImageData src, int[] srcComponentSize, WritableRaster dest, int[] destComponentSize) { double[] rBuf = src.getDoubleData(0) ; double[] gBuf = src.getDoubleData(1) ; double[] bBuf = src.getDoubleData(2) ; double normi = 1.0, normh = 1.0, norms = 1.0 ; int bnormi = 0, bnormh = 0, bnorms = 0 ; int dstType = dest.getSampleModel().getDataType() ; boolean isByte = (dstType == DataBuffer.TYPE_BYTE) ; if (isByte) { bnormi = (1 << destComponentSize[0]) - 1 ; bnormh = 8 - destComponentSize[1] ; bnorms = 8 - destComponentSize[2] ; generateACosTable() ; generateSqrtTable() ; } else if (dstType < DataBuffer.TYPE_FLOAT) { normi = (1l << destComponentSize[0]) - 1 ; normh = ((1l << destComponentSize[1]) - 1) / PI2 ; norms = (1l << destComponentSize[2]) - 1 ; } int height = dest.getHeight() ; int width = dest.getWidth() ; double[] dstPixels = null ; int[] dstIntPixels = null ; if (isByte) dstIntPixels = new int[3 * height * width] ; else dstPixels = new double[3 * height * width] ; int rStart = src.bandOffsets[0] ; int gStart = src.bandOffsets[1] ; int bStart = src.bandOffsets[2] ; int srcPixelStride = src.pixelStride; int srcLineStride = src.lineStride; int dIndex = 0 ; for (int j = 0 ; j 0) hue = acosTable[(int)(500 * temp + 0.5) + 500] ; else hue = acosTable[(int)(-500 * temp - 0.5) + 500] ; if (B >= G) dstIntPixels[dIndex++] = (255 - hue) >> bnormh ; else dstIntPixels[dIndex++] = hue >> bnormh ; double min = (G > B) ? B : G ; min = (R > min) ? min : R ; dstIntPixels[dIndex++] = (255 - (int)(255 * min / intensity + 0.5f)) >> bnorms ; } else { double intensity = (R + G + B) / 3.0f ; dstPixels[dIndex++] = normi * intensity ; double drg = R - G ; double drb = R - B ; double temp = Math.sqrt(drg * drg + drb * (drb - drg)) ; if (temp != 0) { temp = Math.acos((drg + drb) / temp / 2) ; if (B >= G) temp = PI2 - temp ; } else temp = PI2 ; dstPixels[dIndex++] = normh * temp ; double min = (G > B) ? B : G ; min = (R > min) ? min : R ; dstPixels[dIndex++] = norms * (1 - min / intensity) ; } } } if (isByte) dest.setPixels(dest.getMinX(), dest.getMinY(), width, height, dstIntPixels) ; else { convertToSigned(dstPixels, dstType) ; dest.setPixels(dest.getMinX(), dest.getMinY(), width, height, dstPixels) ; } } /** * Converts a Raster of colors represented as pixels * from IHS to CIEXYZ. */ public WritableRaster toCIEXYZ(Raster src, int[] srcComponentSize, WritableRaster dest, int[] destComponentSize) { WritableRaster tempRas = toRGB(src, srcComponentSize, null, null) ; return RGBToCIEXYZ(tempRas, tempRas.getSampleModel().getSampleSize(), dest, destComponentSize) ; } /** * Converts a Raster of colors represented as pixels * from IHS to sRGB. */ public WritableRaster toRGB(Raster src, int[] srcComponentSize, WritableRaster dest, int[] destComponentSize) { checkParameters(src, srcComponentSize, dest,destComponentSize) ; SampleModel srcSampleModel = src.getSampleModel() ; if (srcComponentSize == null) srcComponentSize = srcSampleModel.getSampleSize() ; if (dest == null) { Point origin = new Point(src.getMinX(), src.getMinY()) ; dest = RasterFactory.createWritableRaster(srcSampleModel, origin) ; } SampleModel dstSampleModel = dest.getSampleModel() ; if (destComponentSize == null) destComponentSize = dstSampleModel.getSampleSize() ; PixelAccessor srcAcc = new PixelAccessor(srcSampleModel, null) ; UnpackedImageData srcUid = srcAcc.getPixels(src, src.getBounds(), srcSampleModel.getDataType(), false) ; switch (srcSampleModel.getDataType()) { case DataBuffer.TYPE_BYTE: toRGBByte(srcUid, srcComponentSize, dest, destComponentSize) ; break ; case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: toRGBShort(srcUid, srcComponentSize, dest, destComponentSize) ; break ; case DataBuffer.TYPE_INT: toRGBInt(srcUid, srcComponentSize, dest, destComponentSize) ; break ; case DataBuffer.TYPE_FLOAT: toRGBFloat(srcUid, srcComponentSize, dest, destComponentSize) ; break ; case DataBuffer.TYPE_DOUBLE: toRGBDouble(srcUid, srcComponentSize, dest, destComponentSize) ; break ; } return dest ; } // convert a byte type raster from IHS to RGB private void toRGBByte(UnpackedImageData src, int[] srcComponentSize, WritableRaster dest, int[] destComponentSize) { byte[] iBuf = src.getByteData(0) ; byte[] hBuf = src.getByteData(1) ; byte[] sBuf = src.getByteData(2) ; double normi = 1.0 / ((1 << srcComponentSize[0]) - 1) ; double normh = 1.0 / ((1 << srcComponentSize[1]) - 1) * PI2 ; double norms = 1.0 / ((1 << srcComponentSize[2]) - 1) ; double normr = 1.0, normg = 1.0, normb = 1.0 ; int dstType = dest.getSampleModel().getDataType() ; boolean isByte = (dstType == DataBuffer.TYPE_BYTE) ; if (isByte) { generateTanTable() ; } if (dstType < DataBuffer.TYPE_FLOAT) { normr = (1l << destComponentSize[0]) - 1 ; normg = (1l << destComponentSize[1]) - 1 ; normb = (1l << destComponentSize[2]) - 1 ; } int height = dest.getHeight() ; int width = dest.getWidth() ; double[] dstPixels = null ; int[] dstIntPixels = null ; if (isByte) dstIntPixels = new int[3 * height * width] ; else dstPixels = new double[3 * height * width] ; int iStart = src.bandOffsets[0] ; int hStart = src.bandOffsets[1] ; int sStart = src.bandOffsets[2] ; int srcPixelStride = src.pixelStride; int srcLineStride = src.lineStride; int dIndex = 0 ; for (int j = 0 ; j= 85 && h <= 170) { r = (float) ((1 - S) * I) ; float c1 = (float) (3 * I - r) ; float c2 = (float) (SQRT3 * (r - I) * tanTable[h]) ; g = (float)((c1 + c2) / 2) ; b = (float)((c1 - c2) / 2) ; } else if (h > 170) { g = (float)((1 - S) * I) ; float c1 = (float) (3 * I - g) ; float c2 = (float) (SQRT3 * (g -I)*tanTable[h-85]) ; b = (c1 + c2)/2 ; r = (c1 - c2)/2 ; } else if (h < 85) { b = (float)((1 - S) * I) ; float c1 = (float) (3 * I - b) ; float c2 = (float) (SQRT3 * (b -I)*tanTable[h+85]) ; r = (c1 + c2)/2 ; g = (c1 - c2)/2 ; } } dstIntPixels[dIndex++] = (int)(((r<0.0f) ? 0.0f:((r>1.0f) ? 1.0f: r))*normr+0.5) ; dstIntPixels[dIndex++] = (int)(((g<0.0f) ? 0.0f:((g>1.0f) ? 1.0f: g))*normg+0.5) ; dstIntPixels[dIndex++] = (int)(((b<0.0f) ? 0.0f:((b>1.0f) ? 1.0f: b))*normb+0.5) ; } else { double R, G, B ; R = G = B = I ; if (S != 0) { double H = h * normh ; if (H >= PI23 && H <= PI43) { R = (1 - S) * I ; double c1 = 3 * I - R ; double c2 = SQRT3 * (R - I) * Math.tan(H) ; G = (c1 + c2)/2 ; B = (c1 - c2)/2 ; } else if (H > PI43) { G = (1 - S) * I ; double c1 = 3 * I - G ; double c2 = SQRT3 * (G - I) * Math.tan(H - PI23) ; B = (c1 + c2)/2 ; R = (c1 - c2)/2 ; } else if (H < PI23) { B = (1 - S) * I ; double c1 = 3 * I - B ; double c2 = SQRT3 * (B - I) * Math.tan(H - PI43) ; R = (c1 + c2)/2 ; G = (c1 - c2)/2 ; } } dstPixels[dIndex++] = ((R<0) ? 0:((R>1.0) ?1.0:R))*normr ; dstPixels[dIndex++] = ((G<0) ? 0:((G>1.0) ?1.0:G))*normg ; dstPixels[dIndex++] = ((B<0) ? 0:((B>1.0) ?1.0:B))*normb ; } } } if (isByte) dest.setPixels(dest.getMinX(), dest.getMinY(), width, height, dstIntPixels) ; else { convertToSigned(dstPixels, dstType) ; dest.setPixels(dest.getMinX(), dest.getMinY(), width, height, dstPixels) ; } } // convert a short type ratser from IHS to RGB private void toRGBShort(UnpackedImageData src, int[] srcComponentSize, WritableRaster dest, int[] destComponentSize) { short[] iBuf = src.getShortData(0) ; short[] hBuf = src.getShortData(1) ; short[] sBuf = src.getShortData(2) ; double normi = 1.0 / ((1 << srcComponentSize[0]) - 1) ; double normh = 1.0 / ((1 << srcComponentSize[1]) - 1) * PI2 ; double norms = 1.0 / ((1 << srcComponentSize[2]) - 1) ; double normr = 1.0, normg = 1.0, normb = 1.0 ; int dstType = dest.getSampleModel().getDataType() ; if (dstType < DataBuffer.TYPE_FLOAT) { normr = (1l << destComponentSize[0]) - 1 ; normg = (1l << destComponentSize[1]) - 1 ; normb = (1l << destComponentSize[2]) - 1 ; } int height = dest.getHeight() ; int width = dest.getWidth() ; double[] dstPixels = new double[3 * height * width] ; int iStart = src.bandOffsets[0] ; int hStart = src.bandOffsets[1] ; int sStart = src.bandOffsets[2] ; int srcPixelStride = src.pixelStride; int srcLineStride = src.lineStride; int dIndex = 0 ; for (int j = 0 ; j= PI23 && H <= PI43) { R = (1 - S) * I ; double c1 = 3 * I - R ; double c2 = SQRT3 * (R - I) * Math.tan(H) ; G = (c1 + c2) / 2 ; B = (c1 - c2) / 2 ; } else if (H > PI43) { G = (1 - S) * I ; double c1 = 3 * I - G ; double c2 = SQRT3 * (G - I) * Math.tan(H - PI23) ; B = (c1 + c2) / 2 ; R = (c1 - c2) / 2 ; } else if (H < PI23) { B = (1 - S) * I ; double c1 = 3 * I - B ; double c2 = SQRT3 * (B - I) * Math.tan(H - PI43) ; R = (c1 + c2) / 2 ; G = (c1 - c2) / 2 ; } } dstPixels[dIndex++] =((R<0) ? 0:((R>1.0) ? 1.0:R)) * normr ; dstPixels[dIndex++] =((G<0) ? 0:((G>1.0) ? 1.0:G)) * normg ; dstPixels[dIndex++] =((B<0) ? 0:((B>1.0) ? 1.0:B)) * normb ; } } convertToSigned(dstPixels, dstType) ; dest.setPixels(dest.getMinX(), dest.getMinY(),width,height,dstPixels) ; } private void toRGBInt(UnpackedImageData src, int[] srcComponentSize, WritableRaster dest, int[] destComponentSize) { int[] iBuf = src.getIntData(0) ; int[] hBuf = src.getIntData(1) ; int[] sBuf = src.getIntData(2) ; double normi = 1.0 / ((1l << srcComponentSize[0]) - 1) ; double normh = 1.0 / ((1l << srcComponentSize[1]) - 1) * PI2 ; double norms = 1.0 / ((1l << srcComponentSize[2]) - 1) ; double normr = 1.0, normg = 1.0, normb = 1.0 ; int dstType = dest.getSampleModel().getDataType() ; if (dstType < DataBuffer.TYPE_FLOAT) { normr = (1l << destComponentSize[0]) - 1 ; normg = (1l << destComponentSize[1]) - 1 ; normb = (1l << destComponentSize[2]) - 1 ; } int height = dest.getHeight() ; int width = dest.getWidth() ; double[] dstPixels = new double[3 * height * width] ; int iStart = src.bandOffsets[0] ; int hStart = src.bandOffsets[1] ; int sStart = src.bandOffsets[2] ; int srcPixelStride = src.pixelStride; int srcLineStride = src.lineStride; int dIndex = 0 ; for (int j = 0 ; j < height; j++, iStart += srcLineStride, hStart += srcLineStride, sStart += srcLineStride) { for (int i = 0, iIndex = iStart, hIndex = hStart, sIndex = sStart ; i < width; i++, iIndex += srcPixelStride, hIndex += srcPixelStride, sIndex += srcPixelStride) { double I = (iBuf[iIndex] & 0xFFFFFFFFl) * normi ; double H = (hBuf[hIndex] & 0xFFFFFFFFl) * normh ; double S = (sBuf[sIndex] & 0xFFFFFFFFl) * norms ; double R, G, B ; R = G = B = I ; if (S != 0) { if (H >= PI23 && H <= PI43) { R = (1 - S) * I ; double c1 = 3 * I - R ; double c2 = SQRT3 * (R - I) * Math.tan(H) ; G = (c1 + c2) / 2 ; B = (c1 - c2) / 2 ; } else if (H > PI43) { G = (1 - S) * I ; double c1 = 3 * I - G ; double c2 = SQRT3 * (G - I) * Math.tan(H - PI23) ; B = (c1 + c2) / 2 ; R = (c1 - c2) / 2 ; } else if (H < PI23) { B = (1 - S) * I ; double c1 = 3 * I - B ; double c2 = SQRT3 * (B - I) * Math.tan(H - PI43) ; R = (c1 + c2) / 2 ; G = (c1 - c2) / 2 ; } } dstPixels[dIndex++] =((R<0) ? 0:((R>1.0) ? 1.0:R)) * normr ; dstPixels[dIndex++] =((G<0) ? 0:((G>1.0) ? 1.0:G)) * normg ; dstPixels[dIndex++] =((B<0) ? 0:((B>1.0) ? 1.0:B)) * normb ; } } convertToSigned(dstPixels, dstType) ; dest.setPixels(dest.getMinX(), dest.getMinY(),width,height,dstPixels) ; } private void toRGBFloat(UnpackedImageData src, int[] srcComponentSize, WritableRaster dest, int[] destComponentSize) { float[] iBuf = src.getFloatData(0) ; float[] hBuf = src.getFloatData(1) ; float[] sBuf = src.getFloatData(2) ; double normr = 1.0, normg = 1.0, normb = 1.0 ; int dstType = dest.getSampleModel().getDataType() ; if (dstType < DataBuffer.TYPE_FLOAT) { normr = (1l << destComponentSize[0]) - 1 ; normg = (1l << destComponentSize[1]) - 1 ; normb = (1l << destComponentSize[2]) - 1 ; } int height = dest.getHeight() ; int width = dest.getWidth() ; double[] dstPixels = new double[3 * height * width] ; int iStart = src.bandOffsets[0] ; int hStart = src.bandOffsets[1] ; int sStart = src.bandOffsets[2] ; int srcPixelStride = src.pixelStride; int srcLineStride = src.lineStride; int dIndex = 0 ; for (int j = 0 ; j= PI23 && H <= PI43) { R = (1 - S) * I ; double c1 = 3 * I - R ; double c2 = SQRT3 * (R - I) * Math.tan(H) ; G = (c1 + c2) / 2 ; B = (c1 - c2) / 2 ; } else if (H > PI43) { G = (1 - S) * I ; double c1 = 3 * I - G ; double c2 = SQRT3 * (G - I) * Math.tan(H - PI23) ; B = (c1 + c2) / 2 ; R = (c1 - c2) / 2 ; } else if (H < PI23) { B = (1 - S) * I ; double c1 = 3 * I - B ; double c2 = SQRT3 * (B - I) * Math.tan(H - PI43) ; R = (c1 + c2) / 2 ; G = (c1 - c2) / 2 ; } } dstPixels[dIndex++] =((R<0) ? 0:((R>1.0) ? 1.0:R)) * normr ; dstPixels[dIndex++] =((G<0) ? 0:((G>1.0) ? 1.0:G)) * normg ; dstPixels[dIndex++] =((B<0) ? 0:((B>1.0) ? 1.0:B)) * normb ; } } convertToSigned(dstPixels, dstType) ; dest.setPixels(dest.getMinX(), dest.getMinY(),width,height,dstPixels) ; } private void toRGBDouble(UnpackedImageData src, int[] srcComponentSize, WritableRaster dest, int[] destComponentSize) { double[] iBuf = src.getDoubleData(0) ; double[] hBuf = src.getDoubleData(1) ; double[] sBuf = src.getDoubleData(2) ; double normr = 1.0, normg = 1.0, normb = 1.0 ; int dstType = dest.getSampleModel().getDataType() ; if (dstType < DataBuffer.TYPE_FLOAT) { normr = (1l << destComponentSize[0]) - 1 ; normg = (1l << destComponentSize[1]) - 1 ; normb = (1l << destComponentSize[2]) - 1 ; } int height = dest.getHeight() ; int width = dest.getWidth() ; double[] dstPixels = new double[3 * height * width] ; int iStart = src.bandOffsets[0] ; int hStart = src.bandOffsets[1] ; int sStart = src.bandOffsets[2] ; int srcPixelStride = src.pixelStride; int srcLineStride = src.lineStride; int dIndex = 0 ; for (int j = 0 ; j < height; j++, iStart += srcLineStride, hStart += srcLineStride, sStart += srcLineStride) { for (int i = 0, iIndex = iStart, hIndex = hStart, sIndex = sStart ; i < width; i++, iIndex += srcPixelStride, hIndex += srcPixelStride, sIndex += srcPixelStride) { double I = iBuf[iIndex] ; double H = hBuf[hIndex] ; double S = sBuf[sIndex] ; double R, G, B ; R = G = B = I ; if (S != 0) { if (H >= PI23 && H <= PI43) { R = (1 - S) * I ; double c1 = 3 * I - R ; double c2 = SQRT3 * (R - I) * Math.tan(H) ; G = (c1 + c2) / 2 ; B = (c1 - c2) / 2 ; } else if (H > PI43) { G = (1 - S) * I ; double c1 = 3 * I - G ; double c2 = SQRT3 * (G - I) * Math.tan(H - PI23) ; B = (c1 + c2) / 2 ; R = (c1 - c2) / 2 ; } else if (H < PI23) { B = (1 - S) * I ; double c1 = 3 * I - B ; double c2 = SQRT3 * (B - I) * Math.tan(H - PI43) ; R = (c1 + c2) / 2 ; G = (c1 - c2) / 2 ; } } dstPixels[dIndex++] =((R<0) ? 0:((R>1.0) ? 1.0:R)) * normr ; dstPixels[dIndex++] =((G<0) ? 0:((G>1.0) ? 1.0:G)) * normg ; dstPixels[dIndex++] =((B<0) ? 0:((B>1.0) ? 1.0:B)) * normb ; } } convertToSigned(dstPixels, dstType) ; dest.setPixels(dest.getMinX(), dest.getMinY(),width,height,dstPixels) ; } } jai-core-1.1.4/src/share/classes/javax/media/jai/RecyclingTileFactory.java0000644000175000017500000004062110203035544026270 0ustar mathieumathieu/* * $RCSfile: RecyclingTileFactory.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:18 $ * $State: Exp $ */ package javax.media.jai; import java.awt.Point; import java.awt.image.ComponentSampleModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferInt; import java.awt.image.DataBufferShort; import java.awt.image.DataBufferUShort; import java.awt.image.MultiPixelPackedSampleModel; import java.awt.image.Raster; import java.awt.image.SampleModel; import java.awt.image.SinglePixelPackedSampleModel; import java.awt.image.WritableRaster; import java.lang.ref.SoftReference; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Observable; import com.sun.media.jai.util.DataBufferUtils; /** * A simple implementation of TileFactory wherein the tiles * returned from createTile() attempt to re-use primitive * arrays provided by the TileRecycler method * recycleTile(). * *

      * A simple example of the use of this class is as follows wherein * image files are read, each image is filtered, and each output * written to a file: *

       * String[] sourceFiles; // source file paths
       * KernelJAI kernel; // filtering kernel
       *
       * // Create a RenderingHints object and set hints.
       * RenderingHints rh = new RenderingHints(null);
       * RecyclingTileFactory rtf = new RecyclingTileFactory();
       * rh.put(JAI.KEY_TILE_RECYCLER, rtf);
       * rh.put(JAI.KEY_TILE_FACTORY, rtf);
       * rh.put(JAI.KEY_IMAGE_LAYOUT,
       *        new ImageLayout().setTileWidth(32).setTileHeight(32));
       *
       * int counter = 0;
       *
       * // Read each image, filter it, and save the output to a file.
       * for(int i = 0; i < sourceFiles.length; i++) {
       *     PlanarImage source = JAI.create("fileload", sourceFiles[i]);
       *     ParameterBlock pb =
       *         (new ParameterBlock()).addSource(source).add(kernel);
       *
       *     // The TileFactory hint will cause tiles to be created by 'rtf'.
       *     RenderedOp dest = JAI.create("convolve", pb, rh);
       *     String fileName = "image_"+(++counter)+".tif";
       *     JAI.create("filestore", dest, fileName);
       *
       *     // The TileRecycler hint will cause arrays to be reused by 'rtf'.
       *     dest.dispose();
       * }
       * 
      * In the above code, if the SampleModel of all source * images is identical, then data arrays should only be created in the * first iteration. *

      * * @since JAI 1.1.2 */ public class RecyclingTileFactory extends Observable implements TileFactory, TileRecycler { private static final boolean DEBUG = false; /* XXX public static void main(String[] args) throws Throwable { RecyclingTileFactory rtf = new RecyclingTileFactory(); WritableRaster original = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, 1024, 768, 1, null); rtf.recycleTile(Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, 1024, 768, 1, null)); rtf.recycleTile(Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, 1024, 768, 1, null)); rtf.createTile(original.getSampleModel(), new Point(original.getMinX(), original.getMinY())); rtf.createTile(original.getSampleModel(), new Point(original.getMinX(), original.getMinY())); rtf.createTile(original.getSampleModel(), new Point(original.getMinX(), original.getMinY())); //System.out.println(original.hashCode()+" "+original); //System.out.println(recycled.hashCode()+" "+recycled); System.exit(0); } */ /** * Cache of recycled arrays. The key in this mapping is a * Long which is formed for a given two-dimensional * array as * *
           * long type;     // DataBuffer.TYPE_*
           * long numBanks; // Number of banks
           * long size;     // Size of each bank
           * Long key = new Long((type << 56) | (numBanks << 32) | size);
           * 
      * * where the value of type is one of the constants * DataBuffer.TYPE_*. The value corresponding to each key * is an ArrayList of SoftReferences to the * internal data banks of DataBuffers of tiles wherein the * data bank array has the type and dimensions implied by the key. */ private HashMap recycledArrays = new HashMap(32); /** * The amount of memory currrently used for array storage. */ private long memoryUsed = 0L; // XXX Inline this method or make it public? private static long getBufferSizeCSM(ComponentSampleModel csm) { int[] bandOffsets = csm.getBandOffsets(); int maxBandOff=bandOffsets[0]; for (int i=1; i= 0) size += maxBandOff+1; int pixelStride = csm.getPixelStride(); if (pixelStride > 0) size += pixelStride * (csm.getWidth()-1); int scanlineStride = csm.getScanlineStride(); if (scanlineStride > 0) size += scanlineStride*(csm.getHeight()-1); return size; } // XXX Inline this method or make it public? private static long getNumBanksCSM(ComponentSampleModel csm) { int[] bankIndices = csm.getBankIndices(); int maxIndex = bankIndices[0]; for(int i = 1; i < bankIndices.length; i++) { int bankIndex = bankIndices[i]; if(bankIndex > maxIndex) { maxIndex = bankIndex; } } return maxIndex + 1; } /** * Returns a SoftReference to the internal bank * data of the DataBuffer. */ private static SoftReference getBankReference(DataBuffer db) { Object array = null; switch(db.getDataType()) { case DataBuffer.TYPE_BYTE: array = ((DataBufferByte)db).getBankData(); break; case DataBuffer.TYPE_USHORT: array = ((DataBufferUShort)db).getBankData(); break; case DataBuffer.TYPE_SHORT: array = ((DataBufferShort)db).getBankData(); break; case DataBuffer.TYPE_INT: array = ((DataBufferInt)db).getBankData(); break; case DataBuffer.TYPE_FLOAT: array = DataBufferUtils.getBankDataFloat(db); break; case DataBuffer.TYPE_DOUBLE: array = DataBufferUtils.getBankDataDouble(db); break; default: throw new UnsupportedOperationException (JaiI18N.getString("Generic3")); } return new SoftReference(array); } /** * Returns the amount of memory (in bytes) used by the supplied data * bank array. */ private static long getDataBankSize(int dataType, int numBanks, int size) { int bytesPerElement = 0; switch(dataType) { case DataBuffer.TYPE_BYTE: bytesPerElement = 1; break; case DataBuffer.TYPE_USHORT: case DataBuffer.TYPE_SHORT: bytesPerElement = 2; break; case DataBuffer.TYPE_INT: case DataBuffer.TYPE_FLOAT: bytesPerElement = 4; break; case DataBuffer.TYPE_DOUBLE: bytesPerElement = 8; break; default: throw new UnsupportedOperationException (JaiI18N.getString("Generic3")); } return numBanks*size*bytesPerElement; } /** * Constructs a RecyclingTileFactory. */ public RecyclingTileFactory() {} /** * Returns true. */ public boolean canReclaimMemory() { return true; } /** * Returns true. */ public boolean isMemoryCache() { return true; } public long getMemoryUsed() { return memoryUsed; } public void flush() { synchronized(recycledArrays) { recycledArrays.clear(); memoryUsed = 0L; } } public WritableRaster createTile(SampleModel sampleModel, Point location) { if(sampleModel == null) { throw new IllegalArgumentException("sampleModel == null!"); } if(location == null) { location = new Point(0,0); } DataBuffer db = null; int type = sampleModel.getTransferType(); long numBanks = 0; long size = 0; if(sampleModel instanceof ComponentSampleModel) { ComponentSampleModel csm = (ComponentSampleModel)sampleModel; numBanks = getNumBanksCSM(csm); size = getBufferSizeCSM(csm); } else if(sampleModel instanceof MultiPixelPackedSampleModel) { MultiPixelPackedSampleModel mppsm = (MultiPixelPackedSampleModel)sampleModel; numBanks = 1; int dataTypeSize = DataBuffer.getDataTypeSize(type); size = mppsm.getScanlineStride()*mppsm.getHeight() + (mppsm.getDataBitOffset() + dataTypeSize - 1)/dataTypeSize; } else if(sampleModel instanceof SinglePixelPackedSampleModel) { SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel)sampleModel; numBanks = 1; size = sppsm.getScanlineStride()*(sppsm.getHeight() - 1) + sppsm.getWidth(); } if(size != 0) { Object array = getRecycledArray(type, numBanks, size); if(array != null) { switch(type) { case DataBuffer.TYPE_BYTE: { byte[][] bankData = (byte[][])array; for(int i = 0; i < numBanks; i++) { Arrays.fill(bankData[i], (byte)0); } db = new DataBufferByte(bankData, (int)size); } break; case DataBuffer.TYPE_USHORT: { short[][] bankData = (short[][])array; for(int i = 0; i < numBanks; i++) { Arrays.fill(bankData[i], (short)0); } db = new DataBufferUShort(bankData, (int)size); } break; case DataBuffer.TYPE_SHORT: { short[][] bankData = (short[][])array; for(int i = 0; i < numBanks; i++) { Arrays.fill(bankData[i], (short)0); } db = new DataBufferShort(bankData, (int)size); } break; case DataBuffer.TYPE_INT: { int[][] bankData = (int[][])array; for(int i = 0; i < numBanks; i++) { Arrays.fill(bankData[i], 0); } db = new DataBufferInt(bankData, (int)size); } break; case DataBuffer.TYPE_FLOAT: { float[][] bankData = (float[][])array; for(int i = 0; i < numBanks; i++) { Arrays.fill(bankData[i], 0.0F); } db = DataBufferUtils.createDataBufferFloat(bankData, (int)size); } break; case DataBuffer.TYPE_DOUBLE: { double[][] bankData = (double[][])array; for(int i = 0; i < numBanks; i++) { Arrays.fill(bankData[i], 0.0); } db = DataBufferUtils.createDataBufferDouble(bankData, (int)size); } break; default: throw new IllegalArgumentException (JaiI18N.getString("Generic3")); } if(DEBUG) { System.out.println(getClass().getName()+ " Using a recycled array");// XXX //(new Throwable()).printStackTrace(); // XXX } } else if(DEBUG) { System.out.println(getClass().getName()+ " No type "+type+ " array["+numBanks+"]["+size+"] available"); } } else if(DEBUG) { System.out.println(getClass().getName()+ " Size is zero"); } if(db == null) { if(DEBUG) { System.out.println(getClass().getName()+ " Creating new DataBuffer");// XXX } //(new Throwable()).printStackTrace(); // XXX db = sampleModel.createDataBuffer(); } return Raster.createWritableRaster(sampleModel, db, location); } /** * Recycle the given tile. */ public void recycleTile(Raster tile) { DataBuffer db = tile.getDataBuffer(); Long key = new Long(((long)db.getDataType() << 56) | ((long)db.getNumBanks() << 32) | (long)db.getSize()); if(DEBUG) { System.out.println("Recycling array for: "+ db.getDataType()+" "+ db.getNumBanks()+" "+ db.getSize()); //System.out.println("recycleTile(); key = "+key); } synchronized(recycledArrays) { Object value = recycledArrays.get(key); ArrayList arrays = null; if(value != null) { arrays = (ArrayList)value; } else { arrays = new ArrayList(); } memoryUsed += getDataBankSize(db.getDataType(), db.getNumBanks(), db.getSize()); arrays.add(getBankReference(db)); if(value == null) { recycledArrays.put(key, arrays); } } } /** * Retrieve an array of the specified type and length. */ private Object getRecycledArray(int arrayType, long numBanks, long arrayLength) { Long key = new Long(((long)arrayType << 56) | numBanks << 32 | arrayLength); if(DEBUG) { System.out.println("Attempting to get array for: "+ arrayType+" "+numBanks+" "+arrayLength); //System.out.println("Attempting to get array for key "+key); } synchronized(recycledArrays) { Object value = recycledArrays.get(key); if(value != null) { ArrayList arrays = (ArrayList)value; for(int idx = arrays.size() - 1; idx >= 0; idx--) { SoftReference bankRef = (SoftReference)arrays.remove(idx); memoryUsed -= getDataBankSize(arrayType, (int)numBanks, (int)arrayLength); if(idx == 0) { recycledArrays.remove(key); } Object array = bankRef.get(); if(array != null) { return array; } if(DEBUG) System.out.println("null reference"); } } } //if(DEBUG) System.out.println("getRecycledArray() returning "+array); return null; } } jai-core-1.1.4/src/share/classes/javax/media/jai/ImageFunction.java0000644000175000017500000000636210203035544024737 0ustar mathieumathieu/* * $RCSfile: ImageFunction.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:09 $ * $State: Exp $ */ package javax.media.jai; /** * ImageFunction is a common interface for vector-valued functions which * are to be evaluated at positions in the X-Y coordinate system. At * each position the value of such a function may contain one or more * elements each of which may be complex. */ public interface ImageFunction { /** Returns whether or not each value's elements are complex. */ boolean isComplex(); /** Returns the number of elements per value at each position. */ int getNumElements(); /** * Returns all values of a given element for a specified set of * coordinates. An ArrayIndexOutOfBoundsException may be thrown if * the length of the supplied array(s) is insufficient. * * @param startX The X coordinate of the upper left location to evaluate. * @param startY The Y coordinate of the upper left location to evaluate. * @param deltaX The horizontal increment. * @param deltaY The vertical increment. * @param countX The number of points in the horizontal direction. * @param countY The number of points in the vertical direction. * @param real A pre-allocated float array of length at least countX*countY * in which the real parts of all elements will be returned. * @param imag A pre-allocated float array of length at least countX*countY * in which the imaginary parts of all elements will be returned; may be * null for real data, i.e., when isComplex() returns false. * * @throws ArrayIndexOutOfBoundsException if the length of the supplied * array(s) is insufficient. */ void getElements(float startX, float startY, float deltaX, float deltaY, int countX, int countY, int element, float[] real, float[] imag); /** * Returns all values of a given element for a specified set of * coordinates. An ArrayIndexOutOfBoundsException may be thrown if * the length of the supplied array(s) is insufficient. * * @param startX The X coordinate of the upper left location to evaluate. * @param startY The Y coordinate of the upper left location to evaluate. * @param deltaX The horizontal increment. * @param deltaY The vertical increment. * @param countX The number of points in the horizontal direction. * @param countY The number of points in the vertical direction. * @param real A pre-allocated double array of length at least * countX*countY in which the real parts of all elements will be returned. * @param imag A pre-allocated double array of length at least * countX*countY in which the imaginary parts of all elements will be * returned; may be null for real data, i.e., when * isComplex() returns false. * * @throws ArrayIndexOutOfBoundsException if the length of the supplied * array(s) is insufficient. */ void getElements(double startX, double startY, double deltaX, double deltaY, int countX, int countY, int element, double[] real, double[] imag); } jai-core-1.1.4/src/share/classes/javax/media/jai/PartialOrderNode.java0000644000175000017500000001024410203035544025377 0ustar mathieumathieu/* * $RCSfile: PartialOrderNode.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:15 $ * $State: Exp $ */ package javax.media.jai; import java.util.Enumeration; import java.util.Vector; /** * A node in a directed graph of operations. Each node maintains * three pieces of information, in addition to an arbitrary * Object containing user data associated with the node, * in order to allow topological sorting to be performed in linear time. * *

      First, the in-degree (number of other nodes pointing to this * node) is stored as an int. Nodes with in-degree equal to 0 are * "free" and may appear first in a topological sort. * *

      Second, a reference called zeroLink to another * PartialOrderNode is kept in order to allow construction * of a linked list of nodes with zero in-degree. * *

      Third, a Vector of neighboring nodes is maintained * (in no particular order). These are the nodes which are pointed to * by the current node. * *

      This class is used by the implementation of the * OperationRegistry class and is not intended to be part * of the API. * */ final class PartialOrderNode implements Cloneable, java.io.Serializable { /** The name of the object associated with this node. */ protected String name; /** The data associated with this node. */ protected Object nodeData; /** The in-degree of the node. */ protected int inDegree = 0; /** Copy of the inDegree of the node. */ protected int copyInDegree = 0; /** A link to another node with 0 in-degree, or null. */ protected PartialOrderNode zeroLink = null; /** A Vector of neighboring nodes. */ Vector neighbors = new Vector(); /** * Constructs an PartialOrderNode with given associated data. * * @param nodeData an Object to associate with this node. */ PartialOrderNode(Object nodeData, String name) { this.nodeData = nodeData; this.name = name; } /** Returns the Object represented by this node. */ Object getData() { return nodeData; } /** Returns the name of the Object represented by this node. */ String getName() { return name; } /** Returns the in-degree of this node. */ int getInDegree() { return inDegree; } /** Returns the copy in-degree of this node. */ int getCopyInDegree() { return copyInDegree; } /** Sets the copy in-degree of this node. */ void setCopyInDegree(int copyInDegree) { this.copyInDegree = copyInDegree; } /** Returns the next zero in-degree node in the linked list. */ PartialOrderNode getZeroLink() { return zeroLink; } /** Sets the next zero in-degree node in the linked list. */ void setZeroLink(PartialOrderNode poNode) { zeroLink = poNode; } /** Returns the neighbors of this node as an Enumeration. */ Enumeration getNeighbors() { return neighbors.elements(); } /** * Adds a directed edge to the graph. The neighbors list of this * node is updated and the in-degree of the other node is incremented. */ void addEdge(PartialOrderNode poNode) { neighbors.addElement(poNode); poNode.incrementInDegree(); } /** * Removes a directed edge from the graph. The neighbors list of this * node is updated and the in-degree of the other node is decremented. */ void removeEdge(PartialOrderNode poNode) { neighbors.removeElement(poNode); poNode.decrementInDegree(); } /** Increments the in-degree of a node. */ void incrementInDegree() { ++inDegree; } /** Increments the copy-in-degree of a node. */ void incrementCopyInDegree() { ++copyInDegree; } /** Decrements the in-degree of a node. */ void decrementInDegree() { --inDegree; } /** Decrements the copy in-degree of a node. */ void decrementCopyInDegree() { --copyInDegree; } } jai-core-1.1.4/src/share/classes/javax/media/jai/InterpolationBilinear.java0000644000175000017500000004107510203035544026504 0ustar mathieumathieu/* * $RCSfile: InterpolationBilinear.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005-02-11 04:57:10 $ * $State: Exp $ */ package javax.media.jai; /** * A class representing bilinear interpolation. The class is marked * 'final' so it may be either automatically or manually inlined. * *

      Bilinear interpolation requires a neighborhood extending one * pixel to the right and below the central sample. If the fractional * subsample position is given by (xfrac, yfrac), the resampled pixel value * will be: * *

       *    (1 - yfrac) * [(1 - xfrac)*s00 + xfrac*s01] + 
       *    yfrac * [(1 - xfrac)*s10 + xfrac*s11]
       * 
      * *

      A neighborhood extending one sample to the right of, and one * sample below the central sample is required to perform bilinear * interpolation. This implementation maintains equal subsampleBits in x and y. * *

      The diagrams below illustrate the pixels involved in one-dimensional * bilinear interpolation. Point s0 is the interpolation kernel key position. * xfrac and yfrac, indicated by the dots, represent the point of interpolation * between two pixels. This value lies between 0.0 and 1.0 exclusive for * floating point and 0 and 2subsampleBits exclusive for integer * interpolations. * *

       * 
       *         Horizontal              Vertical
       *
       *          s0 .  s1                  s0                             
       *             ^                       .< yfrac                      
       *            xfrac                   s1                        
       * 
       * 
      * *

      The diagram below illustrates the pixels involved in * two-dimensional bilinear interpolation. * *

       * 
       *                      s00    s01                                     
       *                                                                      
       *                          .      < yfrac                      
       *                                                                      
       *                      s10    s11                                     
       *                          ^                                           
       *                         xfrac                                        
       * 
       * 
      * *

      The class is marked 'final' so that it may be more easily inlined. */ public final class InterpolationBilinear extends Interpolation { /** The value of 1.0 scaled by 2^subsampleBits */ private int one; /** The value of 0.5 scaled by 2^subsampleBits */ private int round; /** The number of bits to shift integer pixels to account for * subsampleBits */ private int shift; /** Twice the value of 'shift'. Accounts for accumulated * scaling shifts in two-axis interpolation */ private int shift2; /** The value of 0.5 scaled by 2^shift2 */ private int round2; static final int DEFAULT_SUBSAMPLE_BITS = 8; /** * Constructs an InterpolationBilinear with a given subsample * precision, in bits. This precision is applied to both axes. * * @param subsampleBits the subsample precision. */ public InterpolationBilinear(int subsampleBits) { super(2, 2, 0, 1, 0, 1, subsampleBits, subsampleBits); shift = subsampleBits; one = 1 << shift; round = 1 << (shift - 1); shift2 = 2*subsampleBits; round2 = 1 << (shift2 - 1); } /** * Constructs an InterpolationBilinear with the default subsample * precision 0f 8 bits. */ public InterpolationBilinear() { this(DEFAULT_SUBSAMPLE_BITS); } /** * Performs horizontal interpolation on a one-dimensional array of integral samples. * * @param samples an array of ints. * @param xfrac the subsample position, multiplied by 2^(subsampleBits). * @return the interpolated value as an int. */ public final int interpolateH(int[] samples, int xfrac) { return interpolateH(samples[0], samples[1], xfrac); } /** * Performs vertical interpolation on a one-dimensional array of integral samples. * * @param samples an array of ints. * @param yfrac the Y subsample position, multiplied by 2^(subsampleBits). * @return the interpolated value as an int. */ public final int interpolateV(int[] samples, int yfrac) { return interpolateV(samples[0], samples[1], yfrac); } /** * Performs interpolation on a two-dimensional array of integral samples. * * @param samples a two-dimensional array of ints. * @param xfrac the X subsample position, multiplied by 2^(subsampleBits). * @param yfrac the Y subsample position, multiplied by 2^(subsampleBits). * @return the interpolated value as an int. */ public final int interpolate(int[][] samples, int xfrac, int yfrac) { return interpolate(samples[0][0], samples[0][1], samples[1][0], samples[1][1], xfrac, yfrac); } /** * Performs horizontal interpolation on a pair of integral samples. * This method may be used instead of the array version for speed. * * @param s0 the central sample. * @param s1 the sample to the right of the central sample. * @param xfrac the subsample position, multiplied by 2^(subsampleBits). * @return the interpolated value as an int. */ public final int interpolateH(int s0, int s1, int xfrac) { return ( (s1 - s0) * xfrac + (s0 << shift) + round) >> shift; } /** * Performs vertical interpolation on a pair of integral samples. * This method may be used instead of the array version for speed. * * @param s0 the central sample. * @param s1 the sample below the central sample. * @param yfrac the Y subsample position, multiplied by 2^(subsampleBits). * @return the interpolated value as an int. */ public final int interpolateV(int s0, int s1, int yfrac) { return ( (s1 - s0) * yfrac + (s0 << shift) + round) >> shift; } /** * Performs horizontal interpolation on a quadruple of integral samples. * The outlying samples are ignored. */ public final int interpolateH(int s_, int s0, int s1, int s2, int xfrac) { return interpolateH(s0, s1, xfrac); } /** * Performs vertical interpolation on a quadruple of integral samples. * The outlying samples are ignored. */ public final int interpolateV(int s_, int s0, int s1, int s2, int yfrac) { return interpolateV(s0, s1, yfrac); } /** * Performs interpolation on a 2x2 grid of integral samples. * * @param s00 the central sample. * @param s01 the sample to the right of the central sample. * @param s10 the sample below the central sample. * @param s11 the sample below and to the right of the central sample. * @param xfrac the X subsample position, multiplied by 2^(subsampleBits). * @param yfrac the Y subsample position, multiplied by 2^(subsampleBits). * @return the interpolated value as an int. */ public final int interpolate(int s00, int s01, int s10, int s11, int xfrac, int yfrac) { int s0 = (s01 - s00) * xfrac + (s00 << shift); int s1 = (s11 - s10) * xfrac + (s10 << shift); return ( (s1 - s0) * yfrac + (s0 << shift) + round2) >> shift2; } /** * Performs interpolation on a 4x4 grid of integral samples. * The outlying samples are ignored. */ public final int interpolate(int s__, int s_0, int s_1, int s_2, int s0_, int s00, int s01, int s02, int s1_, int s10, int s11, int s12, int s2_, int s20, int s21, int s22, int xfrac, int yfrac) { return interpolate(s00, s01, s10, s11, xfrac, yfrac); } /** * Performs horizontal interpolation on a one-dimensional array of * floating-point samples. * * @param samples an array of floats. * @param xfrac the X subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a float. */ public final float interpolateH(float[] samples, float xfrac) { return interpolateH(samples[0], samples[1], xfrac); } /** * Performs vertical interpolation on a one-dimensional array of * floating-point samples. * * @param samples an array of floats. * @param yfrac the Y subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a float. */ public final float interpolateV(float[] samples, float yfrac) { return interpolateV(samples[0], samples[1], yfrac); } /** * Performs interpolation on a two-dimensional array of * floating-point samples. * * @param samples an array of floats. * @param xfrac the X subsample position, in the range [0.0F, 1.0F). * @param yfrac the Y subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a float. */ public final float interpolate(float[][] samples, float xfrac, float yfrac) { return interpolate(samples[0][0], samples[0][1], samples[1][0], samples[1][1], xfrac, yfrac); } /** * Performs horizontal interpolation on a horizontal pair of floating-point * samples. This method may be used instead of the array version * for speed. * * @param s0 the central sample. * @param s1 the sample to the right of the central sample. * @param xfrac the subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a float. */ public final float interpolateH(float s0, float s1, float xfrac) { return (s1 - s0)*xfrac + s0; } /** * Performs vertical interpolation on a vertical pair of floating-point * samples. This method may be used instead of the array version * for speed. * * @param s0 the central sample. * @param s1 the sample below the central sample. * @param yfrac the Y subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a float. */ public final float interpolateV(float s0, float s1, float yfrac) { return (s1 - s0)*yfrac + s0; } /** * Performs horizontal interpolation on a horizontal quad of floating-point * samples. The outlying samples are ignored. */ public final float interpolateH(float s_, float s0, float s1, float s2, float frac) { return interpolateH(s0, s1, frac); } /** * Performs vertical interpolation on a horizontal quad of floating-point * samples. The outlying samples are ignored. */ public final float interpolateV(float s_, float s0, float s1, float s2, float frac) { return interpolateV(s0, s1, frac); } /** * Performs interpolation on a 2x2 grid of floating-point samples. * * @param s00 the central sample. * @param s01 the sample to the right of the central sample. * @param s10 the sample below the central sample. * @param s11 the sample below and to the right of the central sample. * @param xfrac the X subsample position, in the range [0.0F, 1.0F). * @param yfrac the Y subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a float. */ public final float interpolate(float s00, float s01, float s10, float s11, float xfrac, float yfrac) { float s0 = (s01 - s00)*xfrac + s00; float s1 = (s11 - s10)*xfrac + s10; return (s1 - s0)*yfrac + s0; } /** * Performs interpolation on a 4x4 grid. The outlying samples * are ignored. */ public final float interpolate(float s__, float s_0, float s_1, float s_2, float s0_, float s00, float s01, float s02, float s1_, float s10, float s11, float s12, float s2_, float s20, float s21, float s22, float xfrac, float yfrac) { return interpolate(s00, s01, s10, s11, xfrac, yfrac); } /** * Performs horizontal interpolation on a one-dimensional array of * double samples. * * @param samples an array of doubles. * @param xfrac the X subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a double. */ public final double interpolateH(double[] samples, float xfrac) { return interpolateH(samples[0], samples[1], xfrac); } /** * Performs vertical interpolation on a one-dimensional array of * double samples. * * @param samples an array of doubles. * @param yfrac the Y subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a double. */ public final double interpolateV(double[] samples, float yfrac) { return interpolateV(samples[0], samples[1], yfrac); } /** * Performs interpolation on a two-dimensional array of * double samples. * * @param samples an array of doubles. * @param xfrac the X subsample position, in the range [0.0F, 1.0F). * @param yfrac the Y subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a double. */ public final double interpolate(double[][] samples, float xfrac, float yfrac) { return interpolate(samples[0][0], samples[0][1], samples[1][0], samples[1][1], xfrac, yfrac); } /** * Performs horizontal interpolation on a horizontal pair of double * samples. This method may be used instead of the array version * for speed. * * @param s0 the central sample. * @param s1 the sample to the right of the central sample. * @param xfrac the subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a double. */ public final double interpolateH(double s0, double s1, float xfrac) { return (s1 - s0)*xfrac + s0; } /** * Performs vertical interpolation on a vertical pair of double * samples. This method may be used instead of the array version * for speed. * * @param s0 the central sample. * @param s1 the sample below the central sample. * @param yfrac the Y subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a double. */ public final double interpolateV(double s0, double s1, float yfrac) { return (s1 - s0)*yfrac + s0; } /** * Performs interpolation on a horizontal quad of double * samples. The outlying samples are ignored. */ public final double interpolateH(double s_, double s0, double s1, double s2, float xfrac) { return interpolateH(s0, s1, xfrac); } /** * Performs vertical interpolation on a vertical quad of double * samples. The outlying samples are ignored. */ public final double interpolateV(double s_, double s0, double s1, double s2, float yfrac) { return interpolateV(s0, s1, yfrac); } /** * Performs interpolation on a 2x2 grid of double samples. * * @param s00 the central sample. * @param s01 the sample to the right of the central sample. * @param s10 the sample below the central sample. * @param s11 the sample below and to the right of the central sample. * @param xfrac the X subsample position, in the range [0.0F, 1.0F). * @param yfrac the Y subsample position, in the range [0.0F, 1.0F). * @return the interpolated value as a double. */ public final double interpolate(double s00, double s01, double s10, double s11, float xfrac, float yfrac) { double s0 = (s01 - s00)*xfrac + s00; double s1 = (s11 - s10)*xfrac + s10; return (s1 - s0)*yfrac + s0; } /** * Performs interpolation on a 4x4 grid. The outlying samples * are ignored. */ public final double interpolate(double s__,double s_0,double s_1, double s_2, double s0_,double s00,double s01, double s02, double s1_,double s10,double s11, double s12, double s2_,double s20,double s21, double s22, float xfrac, float yfrac) { return interpolate(s00, s01, s10, s11, xfrac, yfrac); } } jai-core-1.1.4/README-build.html0000644000175000017500000000720410244451471016005 0ustar mathieumathieu README-build: build instructions for the jai-core project

      Building the Java Advanced Imaging Packages

      To build the Java Advanced Imaging packages, you must first checkout the jai-core CVS repository on java.net. For example, run the cvs checkout command as follows:

        cd <cvs-root-dir>
        cvs checkout jai-core

      System Requirements

      Any operating environment that supports J2SE should work. We have built jai-core on the following operating environments:

      • Solaris-sparc: Sparc (Ultra60 or better) running Solaris 9
      • Solaris-x86: i386/i586 running Solaris-x86 9
      • Linux: i386/i586 running SuSE 9 or RedHat 9.0
      • Windows: Windows/XP, Windows 2000
      The following software must be installed:

      Building Java Advanced Imaging

      Before you start building, your PATH must include the following directories:

      • <ant-root-dir>/bin
      • <jdk-root-dir>/bin
      • Setting JAVA_HOME or JAVACMD environment variable will supersede <jdk-root-dir>/bin for ant. For further details please refer to the ant manual.

      The default target, jar, creates both optimized and debug jar files.

      To build:
        cd <cvs-root-dir>/jai-core
        ant

      The above steps build both the Java code for javax.media.jai.* and com.sun.media.jai.* packages.

      The build will be placed in jai-core/build/<platform>/opt, where <platform> is determined from the ant echo command:

        ant echo
      Jar files are placed in jai-core/build/<platform>/opt/lib/ext. Binaries for native libraries are part of the project and are copied to the jai-core/build/<platform>/opt/<jrenativesubdir> directory as part of the build, where <jrenativesubdir> is determined from the ant echo command as above.

      To see other targets that are available, type "ant -projecthelp". Note that ant must be run from the top-level directory.

      Running Java Advanced Imaging

      To run Java Advanced Imaging, please checkout the jai-demos project and then refer to README-build.html in jai-demos for details on building and running Java Advanced Imaging demo programs. jai-core-1.1.4/LICENSE-jaispec.txt0000644000175000017500000001535310203035544016322 0ustar mathieumathieuSUN IS WILLING TO LICENSE THIS SPECIFICATION TO YOU ONLY UPON THE CONDITION THAT YOU ACCEPT ALL OF THE TERMS CONTAINED IN THIS LICENSE AGREEMENT ("AGREEMENT"). PLEASE READ THE TERMS AND CONDITIONS OF THIS LICENSE CAREFULLY. BY DOWNLOADING THIS SPECIFICATION, YOU ACCEPT THE TERMS AND CONDITIONS OF THIS LICENSE AGREEMENT. IF YOU ARE NOT WILLING TO BE BOUND BY ITS TERMS, SELECT THE "DECLINE" BUTTON AT THE BOTTOM OF THIS PAGE AND THE DOWNLOADING PROCESS WILL NOT CONTINUE. Java(TM) Advanced Imaging API Specification ("Specification") Version: 1.1 Status: MR Release: April, 2003 Copyright 2003 Sun Microsystems, Inc. 4150 Network Circle, Santa Clara, California 95054, U.S.A. All rights reserved. NOTICE The Specification is protected by copyright and the information described therein may be protected by one or more U.S. patents, foreign patents, or pending applications. Except as provided under the following license, no part of the Specification may be reproduced in any form by any means without the prior written authorization of Sun Microsystems, Inc. ("Sun") and its licensors, if any. Any use of the Specification and the information described therein will be governed by the terms and conditions of this license and the Export Control Guidelines as set forth in the Terms of Use on Sun's website. By viewing, downloading or otherwise copying the Specification, you agree that you have read, understood, and will comply with all of the terms and conditions set forth herein. Sun hereby grants you a fully-paid, non-exclusive, non-transferable, worldwide, limited license (without the right to sublicense), under Sun's intellectual property rights that are essential to practice the Specification, to internally practice the Specification for the purpose of designing and developing your Java applets and applications intended to run on the Java platform or creating a clean room implementation of the Specification that: (i) includes a complete implementation of the current version of the Specification, without subsetting or supersetting; (ii) implements all of the interfaces and functionality of the Specification without subsetting or supersetting; (iii) includes a complete implementation of any optional components (as defined by the Specification) which you choose to implement, without subsetting or supersetting; (iv) implements all of the interfaces and functionality of such optional components, without subsetting or supersetting; (v) does not add any additional packages, classes or interfaces to the "java.*" or "javax.*" packages or subpackages or other packages defined by the Specification; (vi) satisfies all testing requirements available from Sun relating to the most recently published version of the Specification six (6) months prior to any release of the clean room implementation or upgrade thereto; (vii) does not derive from any Sun source code or binary code materials; and (viii) does not include any Sun source code or binary code materials without an appropriate and separate license from Sun. The Specification contains the proprietary information of Sun and may only be used in accordance with the license terms set forth herein. This license will terminate immediately without notice from Sun if you fail to comply with any provision of this license. Upon termination or expiration of this license, you must cease use of or destroy the Specification. TRADEMARKS No right, title, or interest in or to any trademarks, service marks, or trade names of Sun or Sun's licensors is granted hereunder. Sun, Sun Microsystems, the Sun logo, Java, and the Java Coffee Cup logo are trademarks or registered trademarks of Sun Microsystems, Inc. in the U.S. and other countries. DISCLAIMER OF WARRANTIES THE SPECIFICATION IS PROVIDED "AS IS". SUN MAKES NO REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT THAT THE CONTENTS OF THE SPECIFICATION ARE SUITABLE FOR ANY PURPOSE OR THAT ANY PRACTICE OR IMPLEMENTATION OF SUCH CONTENTS WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADE SECRETS OR OTHER RIGHTS. This document does not represent any commitment to release or implement any portion of the Specification in any product. THE SPECIFICATION COULD INCLUDE TECHNICAL INACCURACIES OR TYPOGRAPHICAL ERRORS. CHANGES ARE PERIODICALLY ADDED TO THE INFORMATION THEREIN; THESE CHANGES WILL BE INCORPORATED INTO NEW VERSIONS OF THE SPECIFICATION, IF ANY. SUN MAY MAKE IMPROVEMENTS AND/OR CHANGES TO THE PRODUCT(S) AND/OR THE PROGRAM(S) DESCRIBED IN THE SPECIFICATION AT ANY TIME. Any use of such changes in the Specification will be governed by the then-current license for the applicable version of the Specification. LIMITATION OF LIABILITY TO THE EXTENT NOT PROHIBITED BY LAW, IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY DAMAGES, INCLUDING WITHOUT LIMITATION, LOST REVENUE, PROFITS OR DATA, OR FOR SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF OR RELATED TO ANY FURNISHING, PRACTICING, MODIFYING OR ANY USE OF THE SPECIFICATION, EVEN IF SUN AND/OR ITS LICENSORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. You will indemnify, hold harmless, and defend Sun and its licensors from any claims arising or resulting from: (i) your use of the Specification; (ii) the use or distribution of your Java application, applet and/or clean room implementation; and/or (iii) any claims that later versions or releases of any Specification furnished to you are incompatible with the Specification provided to you under this license. RESTRICTED RIGHTS LEGEND U.S. Government: If this Specification is being acquired by or on behalf of the U.S. Government or by a U.S. Government prime contractor or subcontractor (at any tier), then the Government's rights in the Software and accompanying documentation shall be only as set forth in this license; this is in accordance with 48 C.F.R. 227.7201 through 227.7202-4 (for Department of Defense (DoD) acquisitions) and with 48 C.F.R. 2.101 and 12.212 (for non-DoD acquisitions). REPORT You may wish to report any ambiguities, inconsistencies or inaccuracies you may find in connection with your use of the Specification ("Feedback"). To the extent that you provide Sun with any Feedback, you hereby: (i) agree that such Feedback is provided on a non-proprietary and non-confidential basis, and (ii) grant Sun a perpetual, non-exclusive, worldwide, fully paid-up, irrevocable license, with the right to sublicense through multiple levels of sublicensees, to incorporate, disclose, and use without limitation the Feedback for any purpose related to the Specification and future versions, implementations, and test suites thereof. (LFI#95629/Form ID#011801) jai-core-1.1.4/build.xml0000644000175000017500000010073210664641253014713 0ustar mathieumathieu This project builds Java Advanced Imaging. Normally the target platform will be determined automatically from the system on which the build is being run. This may be overridden by setting the "BUILD_TARGET" property to the desired target platform using the ant "-D" command line option. The supported BUILD_TARGET property values and their respective target platforms are: linux-amd64 64-bit Linux-AMD64 linux-i586 32-bit Linux-x86 solaris-amd64 64-bit Solaris-AMD64 solaris-i586 32-bit Solaris-x86 solaris-sparc 32-bit Solaris-SPARC solaris-sparcv9 64-bit Solaris-SPARC windows-i586 32-bit Windows-x86 It is legal to specify a target as only Java classes and not platform-specific native objects are built by this project. If the build and target operating system are both Solaris, the build target is determined automatically, and a 64-bit JVM is being used for the build, then 64-bit mode should be selected by including "-d64" in the list of values passed to the JVM in the "ANT_OPTS" environment variable. A build of JAI is one of the following four types: daily build, stable build, beta build (or release candidate) and fcs build. Daily builds are named as (dr refers to developer's release) jai-{version}-pre-dr-bNN-{os}-{arch}-DD_MMM_YYYY.zip Stable builds are named as jai-{version}-dr-bNN-{os}-{arch}.zip Beta builds are named as either jai-{version}-betaN-{os}-{arch}-.zip or jai-{version}-rcN-{os}-{arch}-.zip FCS builds are simply jai-{version}-{os}-{arch}-.zip The default is to do a daily build. This may be overriden by setting the BUILD_TYPE property on the command line to do a different kind of build: BUILD_TYPE=daily Do a daily build (default) BUILD_TYPE=stable Do a stable build : -bNN BUILD_TYPE=beta Do a beta or RC production build : -betaN or -rcN BUILD_TYPE=fcs Do an FCS production build : [no suffix] For example: ant -DBUILD_TYPE=stable ... The "MLIB_DIR" property may be used via the ant "-D" command line option to specify the directory containing mlibwrapper_jai.jar; the default value is src/share/mediaLib. Class files are compiled into build/${platform}/debug/classes or build/${platform}/opt/classes. Jar files are created in build/${platform}/debug/lib/ext or build/${platform}/opt/lib/ext. Native libraries are copied to build/${platform}/debug/bin or build/${platform}/opt/bin on Windows and build/${platform}/debug/lib/${arch} or build/${platform}/opt/lib/${arch} on all other platforms. Javadoc is created in build/${platform}/javadocs/docs-jcp, build/${platform}/javadocs/docs-private, or build/${platform}/javadocs/docs-public depending on the access type. The distribution lib bundle is created in dist/${platform}/lib. The distribution javadoc bundle is created in dist/${platform}/javadocs. Creating the distribution bundles requires specification of the BUILD_TYPE property, with the value being either "beta" or "fcs". For example, ant -DBUILD_TYPE=beta dist OR ant -DBUILD_TYPE=fcs dist jai-core-1.1.4/LICENSE-mediaLibJAI.txt0000644000175000017500000002420210203035544016727 0ustar mathieumathieuSun Microsystems, Inc. Binary Code License Agreement MEDIALIB FOR JAI READ THE TERMS OF THIS AGREEMENT AND ANY PROVIDED SUPPLEMENTAL LICENSE TERMS (COLLECTIVELY "AGREEMENT") CAREFULLY BEFORE OPENING THE SOFTWARE MEDIA PACKAGE. BY OPENING THE SOFTWARE MEDIA PACKAGE, YOU AGREE TO THE TERMS OF THIS AGREEMENT. IF YOU ARE ACCESSING THE SOFTWARE ELECTRONICALLY, INDICATE YOUR ACCEPTANCE OF THESE TERMS BY SELECTING THE "ACCEPT" BUTTON AT THE END OF THIS AGREEMENT. IF YOU DO NOT AGREE TO ALL THESE TERMS, PROMPTLY RETURN THE UNUSED SOFTWARE TO YOUR PLACE OF PURCHASE FOR A REFUND OR, IF THE SOFTWARE IS ACCESSED ELECTRONICALLY, SELECT THE "DECLINE" BUTTON AT THE END OF THIS AGREEMENT. 1. LICENSE TO USE. Sun grants you a non-exclusive and non-transferable license for the internal use only of the accompanying software and documentation,the Java Advanced and any error corrections provided by Sun (collectively "Software"), by the number of users and the class of computer hardware for which the corresponding fee has been paid. 2. RESTRICTIONS. Software is confidential and copyrighted. Title to Software and all associated intellectual property rights is retained by Sun and/or its licensors. Except as specifically authorized in any Supplemental License Terms, you may not make copies of Software, other than a single copy of Software for archival purposes. Unless enforcement is prohibited by applicable law, you may not modify, decompile, or reverse engineer Software. Licensee acknowledges that Software is not designed or intended for use in the design, construction, operation or maintenance of any nuclear facility. Sun Microsystems, Inc. disclaims any express or implied warranty of fitness for such uses. No right, title or interest in or to any trademark, service mark, logo or trade name of Sun or its licensors is granted under this Agreement. 3. LIMITED WARRANTY. Sun warrants to you that for a period of ninety (90) days from the date of purchase, as evidenced by a copy of the receipt, the media on which Software is furnished (if any) will be free of defects in materials and workmanship under normal use. Except for the foregoing, Software is provided "AS IS". Your exclusive remedy and Sun's entire liability under this limited warranty will be at Sun's option to replace Software media or refund the fee paid for Software. 4. DISCLAIMER OF WARRANTY. UNLESS SPECIFIED IN THIS AGREEMENT, ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT ARE DISCLAIMED, EXCEPT TO THE EXTENT THAT THESE DISCLAIMERS ARE HELD TO BE LEGALLY INVALID. 5. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF OR RELATED TO THE USE OF OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. In no event will Sun's liability to you, whether in contract, tort (including negligence), or otherwise, exceed the amount paid by you for Software under this Agreement. The foregoing limitations will apply even if the above stated warranty fails of its essential purpose. 6. Termination. This Agreement is effective until terminated. You may terminate this Agreement at any time by destroying all copies of Software. This Agreement will terminate immediately without notice from Sun if you fail to comply with any provision of this Agreement. Upon Termination, you must destroy all copies of Software. 7. Export Regulations. All Software and technical data delivered under this Agreement are subject to US export control laws and may be subject to export or import regulations in other countries. You agree to comply strictly with all such laws and regulations and acknowledge that you have the responsibility to obtain such licenses to export, re-export, or import as may be required after delivery to you. 8. U.S. Government Restricted Rights. If Software is being acquired by or on behalf of the U.S. Government or by a U.S. Government prime contractor or subcontractor (at any tier), then the Government's rights in Software and accompanying documentation will be only as set forth in this Agreement; this is in accordance with 48 CFR 227.7201 through 227.7202-4 (for Department of Defense (DOD) acquisitions) and with 48 CFR 2.101 and 12.212 (for non-DOD acquisitions). 9. Governing Law. Any action related to this Agreement will be governed by California law and controlling U.S. federal law. No choice of law rules of any jurisdiction will apply. 10. Severability. If any provision of this Agreement is held to be unenforceable, this Agreement will remain in effect with the provision omitted, unless omission would frustrate the intent of the parties, in which case this Agreement will immediately terminate. 11. Integration. This Agreement is the entire agreement between you and Sun relating to its subject matter. It supersedes all prior or contemporaneous oral or written communications, proposals, representations and warranties and prevails over any conflicting or additional terms of any quote, order, acknowledgment, or other communication between the parties relating to its subject matter during the term of this Agreement. No modification of this Agreement will be binding, unless in writing and signed by an authorized representative of each party. MEDIALIB FOR JAI SUPPLEMENTAL LICENSE TERMS These supplemental license terms ("Supplemental Terms") add to or modify the terms of the Binary Code License Agreement (collectively, the "Agreement"). Capitalized terms not defined in these Supplemental Terms shall have the same meanings ascribed to them in the Agreement. These Supplemental Terms shall supersede any inconsistent or conflicting terms in the Agreement, or in any license contained within the Software. 1. Software Internal Use and Development License Grant. Subject to the terms and conditions of this Agreement, including, but not limited to Section 3 (Java Technology Restrictions) of these Supplemental Terms, Sun grants you a non-exclusive, non-transferable, limited license to reproduce internally and use internally the binary form of the Software and the Java Advanced Imaging and the JAI Image I/O Open Source Project, complete and unmodified, for the sole purpose of designing, developing and testing your Java applets and applications ("Programs"). 2. License to Distribute Software. In addition to the license granted in Section 1 (Software Internal Use and Development License Grant) of these Supplemental Terms, subject to the terms and conditions of this Agreement, including but not limited to, Section 3 (Java Technology Restrictions) of these Supplemental Terms, Sun grants you a non-exclusive, non-transferable, limited license to reproduce and distribute the Software in binary code form only, provided that you (i) distribute the Software complete and unmodified and only bundled as part of your Programs, (ii) do not distribute additional software intended to replace any component(s) of the Software, (iii) do not remove or alter any proprietary legends or notices contained in the Software, (iv) only distribute the Software subject to a license agreement that protects Sun's interests consistent with the terms contained in this Agreement, (v) agree to defend and indemnify Sun and its licensors from and against any damages, costs, liabilities, settlement amounts and/or expenses (including attorneys' fees) incurred in connection with any claim, lawsuit or action by any third party that arises or results from the use or distribution of any and all Programs and/or Software, and (vi) bundle the Software with the implementation of the Java Advanced Imaging and the JAI Image I/O Open Source Project. 3. Java Technology Restrictions. You may not modify the Java Platform Interface ("JPI", identified as classes contained within the "java" package or any subpackages of the "java" package), by creating additional classes within the JPI or otherwise causing the addition to or modification of the classes in the JPI. In the event that you create an additional class and associated API(s) which (i) extends the functionality of the Java platform, and (ii) is exposed to third party software developers for the purpose of developing additional software which invokes such additional API, you must promptly publish broadly an accurate specification for such API for free use by all developers. You may not create, or authorize your licensees to create additional classes, interfaces, or subpackages that are in any way identified as "java", "javax", "sun" or similar convention as specified by Sun in any naming convention designation. 4. Java Runtime Availability. Refer to the appropriate version of the Java Runtime Environment binary code license (currently located at http://www.java.sun.com/jdk/index.html) for the availability of runtime code which may be distributed with Java applets and applications. 5. Trademarks and Logos. You acknowledge and agree as between you and Sun that Sun owns the SUN, SOLARIS, JAVA, JINI, FORTE, and iPLANET trademarks and all SUN, SOLARIS, JAVA, JINI, FORTE, and iPLANET-related trademarks, service marks, logos and other brand designations ("Sun Marks"), and you agree to comply with the Sun Trademark and Logo Usage Requirements currently located at http://www.sun.com/policies/trademarks. Any use you make of the Sun Marks inures to Sun's benefit. 6. Source Code. Software may contain source code that is provided solely for reference purposes pursuant to the terms of this Agreement. Source code may not be redistributed unless expressly provided for in this Agreement. 7. Termination for Infringement. Either party may terminate this Agreement immediately should any Software become, or in either party's opinion be likely to become, the subject of a claim of infringement of any intellectual property right. For inquiries please contact: Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, California 95054, U.S.A (LFI#143342/Form ID#011801) jai-core-1.1.4/COPYRIGHT.txt0000644000175000017500000000466010475132037015201 0ustar mathieumathieuCopyright 2006 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, California 95054, U.S.A. All rights reserved. U.S. Government Rights - Commercial software. Government users are subject to the Sun Microsystems, Inc. standard license agreement and applicable provisions of the FAR and its supplements. Use is subject to license terms. This distribution may include materials developed by third parties. Sun, Sun Microsystems, the Sun logo, Java and the Duke logo are trademarks or registered trademarks of Sun Microsystems, Inc. in the U.S. and other countries. This product is covered and controlled by U.S. Export Control laws and may be subject to the export or import laws in other countries. Nuclear, missile, chemical biological weapons or nuclear maritime end uses or end users, whether direct or indirect, are strictly prohibited. Export or reexport to countries subject to U.S. embargo or to entities identified on U.S. export exclusion lists, including, but not limited to, the denied persons and specially designated nationals lists is strictly prohibited. Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, California 95054, Etats-Unis. Tous droits rservs. L'utilisation est soumise aux termes de la Licence. Cette distribution peut comprendre des composants dvelopps par des tierces parties. Sun, Sun Microsystems, le logo Sun, Java et le logo Duke sont des marques de fabrique ou des marques dposes de Sun Microsystems, Inc. aux Etats-Unis et dans d'autres pays. Ce produit est soumis la lgislation amricaine en matire de contrle des exportations et peut tre soumis la rglementation en vigueur dans d'autres pays dans le domaine des exportations et importations. Les utilisations, ou utilisateurs finaux, pour des armes nuclaires,des missiles, des armes biologiques et chimiques ou du nuclaire maritime, directement ou indirectement, sont strictement interdites. Les exportations ou rexportations vers les pays sous embargo amricain, ou vers des entits figurant sur les listes d'exclusion d'exportation amricaines, y compris, mais de manire non exhaustive, la liste de personnes qui font objet d'un ordre de ne pas participer, d'une faon directe ou indirecte, aux exportations des produits ou des services qui sont rgis par la lgislation amricaine en matire de contrle des exportations et la liste de ressortissants spcifiquement dsigns, sont rigoureusement interdites. jai-core-1.1.4/www/0000755000175000017500000000000011633360232013703 5ustar mathieumathieujai-core-1.1.4/www/build-instr.html0000644000175000017500000000145410204223424017023 0ustar mathieumathieu jai-core download and build instructions

      Instructions for downloading and building jai-core

      To build the Java Advanced Imaging packages, you must checkout the jai-core CVS repository:

        cd <cvs-root-dir>
        cvs checkout jai-core

      Once this is done, read the README-FIRST.txt file, then read and follow the instructions in the README-build.html file in the <cvs-root-dir>/jai-core directory.

      jai-core-1.1.4/www/index.html0000644000175000017500000002400010503601411015664 0ustar mathieumathieu jai-core Project Home Page

      License | How to Contribute | Reporting Issues | Feedback and Discussion | Project Highlights | Project Suggestions | Demos

      This project contains the source code for the core Java Advanced Imaging API reference implementation containing the packages javax.media.jai.* and com.sun.media.jai.*. Refer to the download and build instructions for information on downloading and building jai-core.

      jai-demos (the Java Advanced Imaging demonstration programs) is a related project. 

      License

      The source code for the core Java Advanced Imaging API reference implementation is licensed under the Java Research License (JRL) for non-commercial use. The JRL allows users to download, build, and modify the source code in the jai-core project for research use, subject to the terms of the license.

      Java Advanced Imaging is also licensed for commercial use under a new no-fee Java Distribution License (JDL). The JDL allows commercial use of Java Advanced Imaging with or without modification, as long as compatibility with the entire API Specification is maintained. This includes passing the TCK tests for Java Advanced Imaging, and retaining the javax namespace as defined by the JCP.

      The API specification will continue to evolve as part of the Java Community Process (JCP). We welcome community participation, and encourage developers to contribute both bug fixes and new implementation code to the API. The combination of the JRL and the JDL was chosen to facilitate this, while ensuring the integrity and compatibility of the API.

      How to Contribute

      We invite developers to contribute to Java Advanced Imaging. Please refer to the Contributing to Java Advanced Imaging page in the parent jai project to learn how to contribute to this project or any other Java Advanced Imaging related project. See the JavaDesktop Community Governance Guidelines for general guidelines on participating in JavaDesktop community projects, such as jai-core.

      Reporting Issues

      We have set up the Issue Tracker in this project to track issues (bugs, feature requests, etc.) for Java Advanced Imaging reference implementation related issues.

      The following subcomponents exist in the jai-core project's Issue Tracker:

      • build: Issues with the jai-core build process
      • codec: Issues with the codec classes in the com.sun.media.jai.codec.* and com.sun.media.jai.codecimpl.* packages
      • implementation: Issues with the reference implementation for jai-core
      • medialib: Issues with the native medialib implementation for jai-core
      • specification: Issues with the jai-core specification
      • www: Issues with the jai-core web pages
      • other: Issues that cannot be categorized into any of the above

      Remember to take a little time when filing a bug report -- make sure you can reproduce the problem, and be very explicit in your description of the problem. If there's something you'd like to see and don't, or something you can imagine being improved, let us know by filing an enhancement request. Use the project's Issue Tracker and file an "Enhancement" rather than a "Defect".

      This project has its own issues mailing list. Click here to subscribe to the issues mailing list described below:

      • issues@jai-core.dev.java.net list is for automated issue tracking.

      Refer to the Reporting Issues section in the parent jai project for more information.

      Feedback and Discussion

      Through the JAI forum and mailing lists, people using the software can benefit from each others' expertise. To be really useful, the lists need your input! Please don't be afraid to post -- help out others when you can; post your questions, comments, and ideas as you see fit.

      We have created four Java Advanced Imaging mailing lists in the parent jai project:

      • announce@jai.dev.java.net
      • interest@jai.dev.java.net
      • issues@jai.dev.java.net
      • cvs@jai.dev.java.net

      Click here to subscribe to any of these lists.

      The announce list is a low-volume moderated mailing list for announcing Java Advanced Imaging related news and information. The interest list is an open mailing list for discussing all aspects of the Java Advanced Imaging API, the reference implementation for the Java Advanced Imaging core, demo programs etc. The issues list is for automated issues tracking (for issues in the parent jai web pages only), and the cvs list is for automated CVS change messages (for changes to the parent jai web pages only).

      For those who prefer a forum to a mailing list, we have created the Java Advanced Imaging API forum, hosted as part of the JavaDesktop Community. Messages posted on the forum are cross-posted to interest@jai.dev.java.net and vice-versa.

      Finally, in addition to the issues mailing list mentioned in the Reporting Issues section above, each sub-project also has its own cvs mailing list. Click here to subscribe to the cvs mailing list described below:

      • cvs@jai-core.dev.java.net list is for automated CVS change messages and discussion of those changes

      Project Highlights

      Release project Source code

      The current Java Advanced Imaging source code is version 1.1.4. It is available for download from the CVS repository of this project. The Java Advanced Imaging CVS tags page lists the existing CVS tags and describes the naming scheme used for these in the CVS repository. The root of this release is tagged as root-1_1_4 and the latest version is tagged jai-1_1_4 in the CVS repository. The source code can be retrieved either by a simple cvs checkout for the latest release or the tag can be used to retrieve a specific version from the CVS repository.

      Java Advanced Imaging 1.1.4* bug fixes

      We will work with the community to address bugs identified in Java Advanced Imaging 1.1.3, and will release new versions of Java Advanced Imaging 1.1.4* as needed. Bug fixes will be immediately available in the CVS source, or from the daily builds. We will release official 1.1.4* versions as needed. Version 1.1.3 has been released and we are currently working on 1.1.4-alpha as the next release.

      Java Advanced Imaging 2.0

      We intend to file a new JSR under the Java Community Process (JCP), and form an Expert Group of community members to design and implement the next version of the Java Advanced Imaging API. The scope of this release will be driven by the JSR Expert Group. Prospective features include image analysis and understanding capabilities, volumetric imaging, and any of the project suggestions below which affect the public API defined by the javax.media.jai package hierarchy. Suggestions for features to include in the next JSR are of course welcome.

      Project Suggestions

      Here are some project ideas for the jai-core project.

      • API to track image operation progress
      • Disk-based TileCache
      • Local adaptive histogram equalization operation
      • Programmatic mechanism to detect use of native operations
      • Base class for operations on unions of sources
      • Make OperationNodes sensitive to RenderingChangeEvents generated by operation parameters
      • Chain code extraction

      Demos

      The jai-demos project includes two sets of demonstration programs. The first category are simple demo programs that may demonstrate a single JAI feature. The second category are more comprehensive demo applications that may demonstrate multiple JAI features and provide for interactive user experience through the extensive use of GUI elements.

      jai-core-1.1.4/LICENSE-JRL.txt0000644000175000017500000001566710203035544015343 0ustar mathieumathieuJAVA RESEARCH LICENSE Version 1.5 I. DEFINITIONS. "Licensee " means You and any other party that has entered into and has in effect a version of this License. "Modifications" means any (a) change or addition to the Technology or (b) new source or object code implementing any portion of the Technology. "Sun" means Sun Microsystems, Inc. and its successors and assignees. "Research Use" means research, evaluation, or development for the purpose of advancing knowledge, teaching, learning, or customizing the Technology or Modifications for personal use. Research Use expressly excludes use or distribution for direct or indirect commercial (including strategic) gain or advantage. "Technology" means the source code, object code and specifications of the technology made available by Sun pursuant to this License. "Technology Site" means the website designated by Sun for accessing the Technology. "You" means the individual executing this License or the legal entity or entities represented by the individual executing this License. II. PURPOSE. Sun is licensing the Technology under this Java Research License (the "License") to promote research, education, innovation, and development using the Technology. COMMERCIAL USE AND DISTRIBUTION OF TECHNOLOGY AND MODIFICATIONS IS PERMITTED ONLY UNDER A SUN COMMERCIAL LICENSE. III. RESEARCH USE RIGHTS. A. License Grant. Subject to the conditions contained herein, Sun grants to You a non-exclusive, non-transferable, worldwide, and royalty-free license to do the following for Your Research Use only: 1. Reproduce, create Modifications of, and use the Technology alone, or with Modifications; 2. Share source code of the Technology alone, or with Modifications, with other Licensees; and 3. Distribute object code of the Technology, alone, or with Modifications, to any third parties for Research Use only, under a license of Your choice that is consistent with this License; and publish papers and books discussing the Technology which may include relevant excerpts that do not in the aggregate constitute a significant portion of the Technology. B. Residual Rights. You may use any information in intangible form that you remember after accessing the Technology, except when such use violates Sun's copyrights or patent rights. C. No Implied Licenses. Other than the rights granted herein, Sun retains all rights, title, and interest in Technology, and You retain all rights, title, and interest in Your Modifications and associated specifications, subject to the terms of this License. D. Open Source Licenses. Portions of the Technology may be provided with notices and open source licenses from open source communities and third parties that govern the use of those portions, and any licenses granted hereunder do not alter any rights and obligations you may have under such open source licenses, however, the disclaimer of warranty and limitation of liability provisions in this License will apply to all Technology in this distribution. IV. INTELLECTUAL PROPERTY REQUIREMENTS As a condition to Your License, You agree to comply with the following restrictions and responsibilities: A. License and Copyright Notices. You must include a copy of this Java Research License in a Readme file for any Technology or Modifications you distribute. You must also include the following statement, "Use and distribution of this technology is subject to the Java Research License included herein", (a) once prominently in the source code tree and/or specifications for Your source code distributions, and (b) once in the same file as Your copyright or proprietary notices for Your binary code distributions. You must cause any files containing Your Modification to carry prominent notice stating that You changed the files. You must not remove or alter any copyright or other proprietary notices in the Technology. B. Licensee Exchanges. Any Technology and Modifications You receive from any Licensee are governed by this License. V. GENERAL TERMS. A. Disclaimer Of Warranties. THE TECHNOLOGY IS PROVIDED "AS IS", WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE TECHNOLOGY IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING OF THIRD PARTY RIGHTS. YOU AGREE THAT YOU BEAR THE ENTIRE RISK IN CONNECTION WITH YOUR USE AND DISTRIBUTION OF ANY AND ALL TECHNOLOGY UNDER THIS LICENSE. B. Infringement; Limitation Of Liability. 1. If any portion of, or functionality implemented by, the Technology becomes the subject of a claim or threatened claim of infringement ("Affected Materials"), Sun may, in its unrestricted discretion, suspend Your rights to use and distribute the Affected Materials under this License. Such suspension of rights will be effective immediately upon Sun's posting of notice of suspension on the Technology Site. 2. IN NO EVENT WILL SUN BE LIABLE FOR ANY DIRECT, INDIRECT, PUNITIVE, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES IN CONNECTION WITH OR ARISING OUT OF THIS LICENSE (INCLUDING, WITHOUT LIMITATION, LOSS OF PROFITS, USE, DATA, OR ECONOMIC ADVANTAGE OF ANY SORT), HOWEVER IT ARISES AND ON ANY THEORY OF LIABILITY (including negligence), WHETHER OR NOT SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. LIABILITY UNDER THIS SECTION V.B.2 SHALL BE SO LIMITED AND EXCLUDED, NOTWITHSTANDING FAILURE OF THE ESSENTIAL PURPOSE OF ANY REMEDY. C. Termination. 1. You may terminate this License at any time by notifying Sun in writing. 2. All Your rights will terminate under this License if You fail to comply with any of its material terms or conditions and do not cure such failure within thirty (30) days after becoming aware of such noncompliance. 3. Upon termination, You must discontinue all uses and distribution of the Technology, and all provisions of this Section V ("General Terms") shall survive termination. D. Miscellaneous. 1. Trademark. You agree to comply with Sun's Trademark & Logo Usage Requirements, as modified from time to time, available at http://www.sun.com/policies/trademarks/. Except as expressly provided in this License, You are granted no rights in or to any Sun trademarks now or hereafter used or licensed by Sun. 2. Integration. This License represents the complete agreement of the parties concerning its subject matter. 3. Severability. If any provision of this License is held unenforceable, such provision shall be reformed to the extent necessary to make it enforceable unless to do so would defeat the intent of the parties, in which case, this License shall terminate. 4. Governing Law. This License is governed by the laws of the United States and the State of California, as applied to contracts entered into and performed in California between California residents. In no event shall this License be construed against the drafter. 5. Export Control. As further described at http://www.sun.com/its, you agree to comply with the U.S. export controls and trade laws of other countries that apply to Technology and Modifications.